improved UX in Event Log by preserving criteria when viewing Details, added RowClass and ColumnClass parameters to Pager component, added initial-scale=1.0 to viewport specification in _host, added default visitor tracking filter, fixed "The given key 'level' was not present in the dictionary" issue in Visitor Management - Details by ensuring data was fully loaded
This commit is contained in:
parent
ad090e62cc
commit
9d17804ac7
@ -9,86 +9,88 @@
|
||||
@inject IStringLocalizer<Detail> Localizer
|
||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="dateTime" HelpText="The date and time of this log" ResourceKey="DateTime">Date/Time: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="dateTime" class="form-control" @bind="@_logDate" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="level" HelpText="The level of this log" ResourceKey="Level">Level: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="level" class="form-control" @bind="@_level" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="feature" HelpText="The feature that was affected" ResourceKey="Feature">Feature: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="feature" class="form-control" @bind="@_feature" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="function" HelpText="The function that was performed" ResourceKey="Function">Function: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="function" class="form-control" @bind="@_function" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="category" HelpText="The categories that were affected" ResourceKey="Category">Category: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="category" class="form-control" @bind="@_category" readonly />
|
||||
</div>
|
||||
</div>
|
||||
@if (_pageName != string.Empty)
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="page" HelpText="The page that was affected" ResourceKey="Page">Page: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="page" class="form-control" @bind="@_pageName" readonly />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@if (_moduleTitle != string.Empty)
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="module" HelpText="The module that was affected" ResourceKey="Module">Module: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="module" class="form-control" @bind="@_moduleTitle" readonly />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@if (_username != string.Empty)
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="user" HelpText="The user that caused this log" ResourceKey="User">User: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="user" class="form-control" @bind="@_username" readonly />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="url" HelpText="The url the log comes from" ResourceKey="Url">Url: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="url" class="form-control" @bind="@_url" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="template" HelpText="What the log is about" ResourceKey="Template">Template: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="template" class="form-control" @bind="@_template" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="message" HelpText="The message that the system generated" ResourceKey="Message">Message: </Label>
|
||||
<div class="col-sm-9">
|
||||
<textarea id="message" class="form-control" @bind="@_message" rows="5" readonly></textarea>
|
||||
</div>
|
||||
</div>
|
||||
@if (!string.IsNullOrEmpty(_exception))
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="exception" HelpText="The exceptions generated by the system" ResourceKey="Exception">Exception: </Label>
|
||||
@if (_initialized)
|
||||
{
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="dateTime" HelpText="The date and time of this log" ResourceKey="DateTime">Date/Time: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="dateTime" class="form-control" @bind="@_logDate" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="level" HelpText="The level of this log" ResourceKey="Level">Level: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="level" class="form-control" @bind="@_level" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="feature" HelpText="The feature that was affected" ResourceKey="Feature">Feature: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="feature" class="form-control" @bind="@_feature" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="function" HelpText="The function that was performed" ResourceKey="Function">Function: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="function" class="form-control" @bind="@_function" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="category" HelpText="The categories that were affected" ResourceKey="Category">Category: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="category" class="form-control" @bind="@_category" readonly />
|
||||
</div>
|
||||
</div>
|
||||
@if (_pageName != string.Empty)
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="page" HelpText="The page that was affected" ResourceKey="Page">Page: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="page" class="form-control" @bind="@_pageName" readonly />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@if (_moduleTitle != string.Empty)
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="module" HelpText="The module that was affected" ResourceKey="Module">Module: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="module" class="form-control" @bind="@_moduleTitle" readonly />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@if (_username != string.Empty)
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="user" HelpText="The user that caused this log" ResourceKey="User">User: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="user" class="form-control" @bind="@_username" readonly />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="url" HelpText="The url the log comes from" ResourceKey="Url">Url: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="url" class="form-control" @bind="@_url" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="template" HelpText="What the log is about" ResourceKey="Template">Template: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="template" class="form-control" @bind="@_template" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="message" HelpText="The message that the system generated" ResourceKey="Message">Message: </Label>
|
||||
<div class="col-sm-9">
|
||||
<textarea id="message" class="form-control" @bind="@_message" rows="5" readonly></textarea>
|
||||
</div>
|
||||
</div>
|
||||
@if (!string.IsNullOrEmpty(_exception))
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="exception" HelpText="The exceptions generated by the system" ResourceKey="Exception">Exception: </Label>
|
||||
<div class="col-sm-9">
|
||||
<textarea id="exception" class="form-control" @bind="@_exception" rows="5" readonly></textarea>
|
||||
</div>
|
||||
@ -107,81 +109,83 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<NavLink class="btn btn-secondary" href="@NavigateUrl(PageState.Page.Path, "level=" + PageState.QueryString["level"] + "&function=" + PageState.QueryString["function"] + "&rows=" + PageState.QueryString["rows"] + "&page=" + PageState.QueryString["page"])">@SharedLocalizer["Cancel"]</NavLink>
|
||||
|
||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||
@code {
|
||||
private bool _initialized = false;
|
||||
private int _logId;
|
||||
private string _logDate = string.Empty;
|
||||
private string _level = string.Empty;
|
||||
private string _feature = string.Empty;
|
||||
private string _function = string.Empty;
|
||||
private string _category = string.Empty;
|
||||
private string _pageName = string.Empty;
|
||||
private string _moduleTitle = string.Empty;
|
||||
private string _username = string.Empty;
|
||||
private string _url = string.Empty;
|
||||
private string _template = string.Empty;
|
||||
private string _message = string.Empty;
|
||||
private string _exception = string.Empty;
|
||||
private string _properties = string.Empty;
|
||||
private string _server = string.Empty;
|
||||
|
||||
@code {
|
||||
private int _logId;
|
||||
private string _logDate = string.Empty;
|
||||
private string _level = string.Empty;
|
||||
private string _feature = string.Empty;
|
||||
private string _function = string.Empty;
|
||||
private string _category = string.Empty;
|
||||
private string _pageName = string.Empty;
|
||||
private string _moduleTitle = string.Empty;
|
||||
private string _username = string.Empty;
|
||||
private string _url = string.Empty;
|
||||
private string _template = string.Empty;
|
||||
private string _message = string.Empty;
|
||||
private string _exception = string.Empty;
|
||||
private string _properties = string.Empty;
|
||||
private string _server = string.Empty;
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
try
|
||||
_logId = Int32.Parse(PageState.QueryString["id"]);
|
||||
var log = await LogService.GetLogAsync(_logId);
|
||||
if (log != null)
|
||||
{
|
||||
_logId = Int32.Parse(PageState.QueryString["id"]);
|
||||
var log = await LogService.GetLogAsync(_logId);
|
||||
if (log != null)
|
||||
_logDate = log.LogDate.ToString(CultureInfo.CurrentCulture);
|
||||
_level = log.Level;
|
||||
_feature = log.Feature;
|
||||
_function = log.Function;
|
||||
_category = log.Category;
|
||||
|
||||
if (log.PageId != null)
|
||||
{
|
||||
_logDate = log.LogDate.ToString(CultureInfo.CurrentCulture);
|
||||
_level = log.Level;
|
||||
_feature = log.Feature;
|
||||
_function = log.Function;
|
||||
_category = log.Category;
|
||||
|
||||
if (log.PageId != null)
|
||||
var page = await PageService.GetPageAsync(log.PageId.Value);
|
||||
if (page != null)
|
||||
{
|
||||
var page = await PageService.GetPageAsync(log.PageId.Value);
|
||||
if (page != null)
|
||||
{
|
||||
_pageName = page.Name;
|
||||
}
|
||||
_pageName = page.Name;
|
||||
}
|
||||
|
||||
if (log.PageId != null && log.ModuleId != null)
|
||||
{
|
||||
var pagemodule = await PageModuleService.GetPageModuleAsync(log.PageId.Value, log.ModuleId.Value);
|
||||
if (pagemodule != null)
|
||||
{
|
||||
_moduleTitle = pagemodule.Title;
|
||||
}
|
||||
}
|
||||
|
||||
if (log.UserId != null)
|
||||
{
|
||||
var user = await UserService.GetUserAsync(log.UserId.Value, PageState.Site.SiteId);
|
||||
if (user != null)
|
||||
{
|
||||
_username = user.Username;
|
||||
}
|
||||
}
|
||||
|
||||
_url = log.Url;
|
||||
_template = log.MessageTemplate;
|
||||
_message = log.Message;
|
||||
_exception = log.Exception;
|
||||
_properties = log.Properties;
|
||||
_server = log.Server;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Loading Log {LogId} {Error}", _logId, ex.Message);
|
||||
AddModuleMessage(Localizer["Error.Log.Load"], MessageType.Error);
|
||||
|
||||
if (log.PageId != null && log.ModuleId != null)
|
||||
{
|
||||
var pagemodule = await PageModuleService.GetPageModuleAsync(log.PageId.Value, log.ModuleId.Value);
|
||||
if (pagemodule != null)
|
||||
{
|
||||
_moduleTitle = pagemodule.Title;
|
||||
}
|
||||
}
|
||||
|
||||
if (log.UserId != null)
|
||||
{
|
||||
var user = await UserService.GetUserAsync(log.UserId.Value, PageState.Site.SiteId);
|
||||
if (user != null)
|
||||
{
|
||||
_username = user.Username;
|
||||
}
|
||||
}
|
||||
|
||||
_url = log.Url;
|
||||
_template = log.MessageTemplate;
|
||||
_message = log.Message;
|
||||
_exception = log.Exception;
|
||||
_properties = log.Properties;
|
||||
_server = log.Server;
|
||||
_initialized = true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Loading Log {LogId} {Error}", _logId, ex.Message);
|
||||
AddModuleMessage(Localizer["Error.Log.Load"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ else
|
||||
<div class="row mb-1 align-items-center">
|
||||
<div class="col-sm-4">
|
||||
<Label For="level" HelpText="Select the log level for event log items" ResourceKey="Level">Level: </Label><br /><br />
|
||||
<select id="level" class="form-select" @onchange="(e => LevelChanged(e))">
|
||||
<select id="level" class="form-select" value="@_level" @onchange="(e => LevelChanged(e))">
|
||||
<option value="-"><@Localizer["AllLevels"]></option>
|
||||
<option value="Trace">@Localizer["Trace"]</option>
|
||||
<option value="Debug">@Localizer["Debug"]</option>
|
||||
@ -29,7 +29,7 @@ else
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<Label For="function" HelpText="Select the function for event log items" ResourceKey="Function">Function: </Label><br /><br />
|
||||
<select id="function" class="form-select" @onchange="(e => FunctionChanged(e))">
|
||||
<select id="function" class="form-select" value="@_function" @onchange="(e => FunctionChanged(e))">
|
||||
<option value="-"><@Localizer["AllFunctions"]></option>
|
||||
<option value="Create">@Localizer["Create"]</option>
|
||||
<option value="Read">@Localizer["Read"]</option>
|
||||
@ -41,7 +41,7 @@ else
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<Label For="rows" HelpText="Select the maximum number of event log items to review. Please note that if you choose more than 10 items the information will be split into pages." ResourceKey="Rows">Maximum Items: </Label><br /><br />
|
||||
<select id="rows" class="form-select" @onchange="(e => RowsChanged(e))">
|
||||
<select id="rows" class="form-select" value="@_rows" @onchange="(e => RowsChanged(e))">
|
||||
<option value="10">10</option>
|
||||
<option value="50">50</option>
|
||||
<option value="100">100</option>
|
||||
@ -53,7 +53,7 @@ else
|
||||
|
||||
@if (_logs.Any())
|
||||
{
|
||||
<Pager Items="@_logs">
|
||||
<Pager Items="@_logs" CurrentPage="@_page.ToString()" OnPageChange="OnPageChange">
|
||||
<Header>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th>@Localizer["Date"]</th>
|
||||
@ -62,7 +62,7 @@ else
|
||||
<th>@Localizer["Function"]</th>
|
||||
</Header>
|
||||
<Row>
|
||||
<td class="@GetClass(context.Function)"><ActionLink Action="Detail" Parameters="@($"id=" + context.LogId.ToString())" ResourceKey="LogDetails" /></td>
|
||||
<td class="@GetClass(context.Function)"><ActionLink Action="Detail" Parameters="@($"id=" + context.LogId.ToString() + "&level=" + _level + "&function=" + _function + "&rows=" + _rows + "&page=" + _page.ToString())" ResourceKey="LogDetails" /></td>
|
||||
<td class="@GetClass(context.Function)">@context.LogDate</td>
|
||||
<td class="@GetClass(context.Function)">@context.Level</td>
|
||||
<td class="@GetClass(context.Function)">@context.Feature</td>
|
||||
@ -94,6 +94,7 @@ else
|
||||
private string _level = "-";
|
||||
private string _function = "-";
|
||||
private string _rows = "10";
|
||||
private int _page = 1;
|
||||
private List<Log> _logs;
|
||||
private string _retention = "";
|
||||
|
||||
@ -103,8 +104,27 @@ else
|
||||
{
|
||||
try
|
||||
{
|
||||
if (PageState.QueryString.ContainsKey("level"))
|
||||
{
|
||||
_level = PageState.QueryString["level"];
|
||||
}
|
||||
if (PageState.QueryString.ContainsKey("function"))
|
||||
{
|
||||
_function = PageState.QueryString["function"];
|
||||
}
|
||||
if (PageState.QueryString.ContainsKey("rows"))
|
||||
{
|
||||
_rows = PageState.QueryString["rows"];
|
||||
}
|
||||
if (PageState.QueryString.ContainsKey("page") && int.TryParse(PageState.QueryString["page"], out int page))
|
||||
{
|
||||
_page = page;
|
||||
}
|
||||
|
||||
await GetLogs();
|
||||
_retention = SettingService.GetSetting(PageState.Site.Settings, "LogRetention", "30");
|
||||
|
||||
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||
_retention = SettingService.GetSetting(settings, "LogRetention", "30");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -208,4 +228,9 @@ else
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPageChange(int page)
|
||||
{
|
||||
_page = page;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -7,69 +7,73 @@
|
||||
@inject IStringLocalizer<Detail> Localizer
|
||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="ip" HelpText="The last recorded IP address for this visitor" ResourceKey="IP">IP Address: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="ip" class="form-control" @bind="@_ip" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="language" HelpText="The last recorded language for this visitor" ResourceKey="Language">Language: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="language" class="form-control" @bind="@_language" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="useragent" HelpText="The last recorded user agent for this visitor" ResourceKey="UserAgent">User Agent: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="useragent" class="form-control" @bind="@_useragent" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="url" HelpText="The last recorded url for this visitor" ResourceKey="Url">Url: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="url" class="form-control" @bind="@_url" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="referrer" HelpText="The last recorded referrer for this visitor" ResourceKey="Referrer">Referrer: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="referrer" class="form-control" @bind="@_referrer" readonly />
|
||||
</div>
|
||||
</div>
|
||||
@if (_user != string.Empty)
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="user" HelpText="The last recorded user associated with this visitor" ResourceKey="User">User: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="user" class="form-control" @bind="@_user" readonly />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="visits" HelpText="The total number of visits by this visitor all time" ResourceKey="Visits">Visits: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="visits" class="form-control" @bind="@_visits" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="visited" HelpText="The last recorded date/time when the visitor visited the site" ResourceKey="Visited">Visited: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="visited" class="form-control" @bind="@_visited" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="created" HelpText="The first recorded date/time when this visitor visited the site" ResourceKey="Created">Created: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="created" class="form-control" @bind="@_created" readonly />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@if (_initialized)
|
||||
{
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="ip" HelpText="The last recorded IP address for this visitor" ResourceKey="IP">IP Address: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="ip" class="form-control" @bind="@_ip" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="language" HelpText="The last recorded language for this visitor" ResourceKey="Language">Language: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="language" class="form-control" @bind="@_language" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="useragent" HelpText="The last recorded user agent for this visitor" ResourceKey="UserAgent">User Agent: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="useragent" class="form-control" @bind="@_useragent" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="url" HelpText="The last recorded url for this visitor" ResourceKey="Url">Url: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="url" class="form-control" @bind="@_url" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="referrer" HelpText="The last recorded referrer for this visitor" ResourceKey="Referrer">Referrer: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="referrer" class="form-control" @bind="@_referrer" readonly />
|
||||
</div>
|
||||
</div>
|
||||
@if (_user != string.Empty)
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="user" HelpText="The last recorded user associated with this visitor" ResourceKey="User">User: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="user" class="form-control" @bind="@_user" readonly />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="visits" HelpText="The total number of visits by this visitor all time" ResourceKey="Visits">Visits: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="visits" class="form-control" @bind="@_visits" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="visited" HelpText="The last recorded date/time when the visitor visited the site" ResourceKey="Visited">Visited: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="visited" class="form-control" @bind="@_visited" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="created" HelpText="The first recorded date/time when this visitor visited the site" ResourceKey="Created">Created: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="created" class="form-control" @bind="@_created" readonly />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<NavLink class="btn btn-secondary" href="@NavigateUrl(PageState.Page.Path, "type=" + PageState.QueryString["type"] + "&days=" + PageState.QueryString["days"] + "&page=" + PageState.QueryString["page"])">@SharedLocalizer["Cancel"]</NavLink>
|
||||
|
||||
@code {
|
||||
private bool _initialized = false;
|
||||
private int _visitorId;
|
||||
private string _ip = string.Empty;
|
||||
private string _language = string.Empty;
|
||||
@ -108,6 +112,7 @@
|
||||
_user = user.DisplayName;
|
||||
}
|
||||
}
|
||||
_initialized = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -120,4 +125,4 @@
|
||||
AddModuleMessage(Localizer["Error.LoadVisitor"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ else
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="filter" HelpText="Comma delimited list of terms which may exist in IP addresses, user agents, or languages which identify visitors which should not be tracked (ie. bots)" ResourceKey="Filter">Filter: </Label>
|
||||
<Label Class="col-sm-3" For="filter" HelpText="Comma delimited list of terms which may exist in IP addresses, user agents, or languages identifying visitors which should not be tracked" ResourceKey="Filter">Filter: </Label>
|
||||
<div class="col-sm-9">
|
||||
<textarea id="filter" class="form-control" @bind="@_filter" rows="3"></textarea>
|
||||
</div>
|
||||
@ -117,8 +117,9 @@ else
|
||||
await GetVisitors();
|
||||
|
||||
_tracking = PageState.Site.VisitorTracking.ToString();
|
||||
_filter = SettingService.GetSetting(PageState.Site.Settings, "VisitorFilter", "");
|
||||
_retention = SettingService.GetSetting(PageState.Site.Settings, "VisitorRetention", "30");
|
||||
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||
_filter = SettingService.GetSetting(settings, "VisitorFilter", Constants.DefaultVisitorFilter);
|
||||
_retention = SettingService.GetSetting(settings, "VisitorRetention", "30");
|
||||
}
|
||||
|
||||
private async void TypeChanged(ChangeEventArgs e)
|
||||
|
@ -57,12 +57,12 @@
|
||||
<div class="table-responsive">
|
||||
<table class="@Class">
|
||||
<thead>
|
||||
<tr>@Header</tr>
|
||||
<tr class="@RowClass">@Header</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var item in ItemList)
|
||||
{
|
||||
<tr>@Row(item)</tr>
|
||||
<tr class="@RowClass">@Row(item)</tr>
|
||||
@if (Detail != null)
|
||||
{
|
||||
<tr>@Detail(item)</tr>
|
||||
@ -93,23 +93,19 @@
|
||||
}
|
||||
}
|
||||
<div class="@Class">
|
||||
@if (Header != null)
|
||||
{
|
||||
<div class="row"><div class="col">@Header</div></div>
|
||||
}
|
||||
@for (int row = 0; row < rows; row++)
|
||||
{
|
||||
<div class="row">
|
||||
<div class="@RowClass">
|
||||
@for (int col = 0; col < cols; col++)
|
||||
{
|
||||
int index = (row * _columns) + col;
|
||||
if (index < ItemList.Count())
|
||||
{
|
||||
<div class="col">@Row(ItemList.ElementAt(index))</div>
|
||||
<div class="@ColumnClass">@Row(ItemList.ElementAt(index))</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="col"> </div>
|
||||
<div> </div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
@ -182,10 +178,10 @@
|
||||
public string Toolbar { get; set; } // Top, Bottom or Both
|
||||
|
||||
[Parameter]
|
||||
public RenderFragment Header { get; set; } = null;
|
||||
public RenderFragment Header { get; set; } = null; // only applicable to Table layouts
|
||||
|
||||
[Parameter]
|
||||
public RenderFragment<TableItem> Row { get; set; } = null;
|
||||
public RenderFragment<TableItem> Row { get; set; } = null; // required
|
||||
|
||||
[Parameter]
|
||||
public RenderFragment<TableItem> Detail { get; set; } = null; // only applicable to Table layouts
|
||||
@ -197,19 +193,25 @@
|
||||
public string PageSize { get; set; } // number of items to display on a page
|
||||
|
||||
[Parameter]
|
||||
public string Columns { get; set; } // only applicable to Grid layouts
|
||||
public string Columns { get; set; } // only applicable to Grid layouts - default is zero indicating use responsive behavior
|
||||
|
||||
[Parameter]
|
||||
public string CurrentPage { get; set; } // optional property to set the initial page to display
|
||||
public string CurrentPage { get; set; } // sets the initial page to display
|
||||
|
||||
[Parameter]
|
||||
public string DisplayPages { get; set; } // maximum number of page numbers to display for user selection
|
||||
|
||||
[Parameter]
|
||||
public string Class { get; set; }
|
||||
public string Class { get; set; } // class for the containing element - ie. <table> for Table or <div> for Grid
|
||||
|
||||
[Parameter]
|
||||
public Action<int> OnPageChange { get; set; } // optional - executes a method in the calling component when the page changes
|
||||
public string RowClass { get; set; } // class for row element - ie. <tr> for Table or <div> for Grid
|
||||
|
||||
[Parameter]
|
||||
public string ColumnClass { get; set; } // class for column element - only applicable to Grid format
|
||||
|
||||
[Parameter]
|
||||
public Action<int> OnPageChange { get; set; } // a method to be executed in the calling component when the page changes
|
||||
|
||||
private IEnumerable<TableItem> ItemList { get; set; }
|
||||
|
||||
@ -233,11 +235,35 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
Class = "container-fluid px-0";
|
||||
Class = "container-fluid";
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(PageSize))
|
||||
if (string.IsNullOrEmpty(RowClass))
|
||||
{
|
||||
if (Format == "Table")
|
||||
{
|
||||
RowClass = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
RowClass = "row";
|
||||
}
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(ColumnClass))
|
||||
{
|
||||
if (Format == "Table")
|
||||
{
|
||||
ColumnClass = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
ColumnClass = "col";
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(PageSize))
|
||||
{
|
||||
_maxItems = int.Parse(PageSize);
|
||||
}
|
||||
|
@ -175,7 +175,7 @@
|
||||
<value>Details</value>
|
||||
</data>
|
||||
<data name="Filter.HelpText" xml:space="preserve">
|
||||
<value>Comma delimited list of terms which may exist in IP addresses, user agents, or languages which identify visitors which should not be tracked (ie. bots)</value>
|
||||
<value>Comma delimited list of terms which may exist in IP addresses, user agents, or languages identifying visitors which should not be tracked</value>
|
||||
</data>
|
||||
<data name="Filter.Text" xml:space="preserve">
|
||||
<value>Filter:</value>
|
||||
|
@ -6,7 +6,7 @@
|
||||
<html lang="@Model.Language">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>@Model.Title</title>
|
||||
<base href="~/" />
|
||||
<link id="app-favicon" rel="shortcut icon" type="image/x-icon" href="@Model.FavIcon" />
|
||||
|
@ -5,7 +5,6 @@ using Oqtane.Modules;
|
||||
using Oqtane.Models;
|
||||
using Oqtane.Themes;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Oqtane.Repository;
|
||||
@ -20,6 +19,7 @@ using Microsoft.AspNetCore.Http;
|
||||
using System.Security.Claims;
|
||||
using System.Net;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Oqtane.Enums;
|
||||
|
||||
namespace Oqtane.Pages
|
||||
{
|
||||
@ -36,8 +36,9 @@ namespace Oqtane.Pages
|
||||
private readonly IVisitorRepository _visitors;
|
||||
private readonly IAliasRepository _aliases;
|
||||
private readonly ISettingRepository _settings;
|
||||
private readonly ILogManager _logger;
|
||||
|
||||
public HostModel(IConfiguration configuration, ITenantManager tenantManager, ILocalizationManager localizationManager, ILanguageRepository languages, IAntiforgery antiforgery, ISiteRepository sites, IPageRepository pages, IUrlMappingRepository urlMappings, IVisitorRepository visitors, IAliasRepository aliases, ISettingRepository settings)
|
||||
public HostModel(IConfiguration configuration, ITenantManager tenantManager, ILocalizationManager localizationManager, ILanguageRepository languages, IAntiforgery antiforgery, ISiteRepository sites, IPageRepository pages, IUrlMappingRepository urlMappings, IVisitorRepository visitors, IAliasRepository aliases, ISettingRepository settings, ILogManager logger)
|
||||
{
|
||||
_configuration = configuration;
|
||||
_tenantManager = tenantManager;
|
||||
@ -50,6 +51,7 @@ namespace Oqtane.Pages
|
||||
_visitors = visitors;
|
||||
_aliases = aliases;
|
||||
_settings = settings;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public string Language = "en";
|
||||
@ -206,86 +208,95 @@ namespace Oqtane.Pages
|
||||
|
||||
private void TrackVisitor(int SiteId)
|
||||
{
|
||||
// get request attributes
|
||||
string useragent = (Request.Headers[HeaderNames.UserAgent] != StringValues.Empty) ? Request.Headers[HeaderNames.UserAgent] : "(none)";
|
||||
string language = (Request.Headers[HeaderNames.AcceptLanguage] != StringValues.Empty) ? Request.Headers[HeaderNames.AcceptLanguage] : "";
|
||||
language = (language.Contains(",")) ? language.Substring(0, language.IndexOf(",")) : language;
|
||||
language = (language.Contains(";")) ? language.Substring(0, language.IndexOf(";")) : language;
|
||||
language = (language.Trim().Length == 0) ? "??" : language;
|
||||
|
||||
// filter
|
||||
var filter = _settings.GetSetting(EntityNames.Site, SiteId, "VisitorFilter");
|
||||
if (filter != null && !string.IsNullOrEmpty(filter.SettingValue))
|
||||
try
|
||||
{
|
||||
foreach (string term in filter.SettingValue.ToLower().Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(sValue => sValue.Trim()).ToArray())
|
||||
// get request attributes
|
||||
string useragent = (Request.Headers[HeaderNames.UserAgent] != StringValues.Empty) ? Request.Headers[HeaderNames.UserAgent] : "(none)";
|
||||
string language = (Request.Headers[HeaderNames.AcceptLanguage] != StringValues.Empty) ? Request.Headers[HeaderNames.AcceptLanguage] : "";
|
||||
language = (language.Contains(",")) ? language.Substring(0, language.IndexOf(",")) : language;
|
||||
language = (language.Contains(";")) ? language.Substring(0, language.IndexOf(";")) : language;
|
||||
language = (language.Trim().Length == 0) ? "??" : language;
|
||||
|
||||
// filter
|
||||
string filter = Constants.DefaultVisitorFilter;
|
||||
var setting = _settings.GetSetting(EntityNames.Site, SiteId, "VisitorFilter");
|
||||
if (setting != null)
|
||||
{
|
||||
filter = setting.SettingValue;
|
||||
}
|
||||
foreach (string term in filter.ToLower().Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(sValue => sValue.Trim()).ToArray())
|
||||
{
|
||||
if (RemoteIPAddress.ToLower().Contains(term) || useragent.ToLower().Contains(term) || language.ToLower().Contains(term))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string url = Request.GetEncodedUrl();
|
||||
string referrer = (Request.Headers[HeaderNames.Referer] != StringValues.Empty) ? Request.Headers[HeaderNames.Referer] : "";
|
||||
int? userid = null;
|
||||
if (User.HasClaim(item => item.Type == ClaimTypes.PrimarySid))
|
||||
{
|
||||
userid = int.Parse(User.Claims.First(item => item.Type == ClaimTypes.PrimarySid).Value);
|
||||
}
|
||||
|
||||
var VisitorCookie = "APP_VISITOR_" + SiteId.ToString();
|
||||
if (!int.TryParse(Request.Cookies[VisitorCookie], out VisitorId))
|
||||
{
|
||||
var visitor = new Visitor();
|
||||
visitor.SiteId = SiteId;
|
||||
visitor.IPAddress = RemoteIPAddress;
|
||||
visitor.UserAgent = useragent;
|
||||
visitor.Language = language;
|
||||
visitor.Url = url;
|
||||
visitor.Referrer = referrer;
|
||||
visitor.UserId = userid;
|
||||
visitor.Visits = 1;
|
||||
visitor.CreatedOn = DateTime.UtcNow;
|
||||
visitor.VisitedOn = DateTime.UtcNow;
|
||||
visitor = _visitors.AddVisitor(visitor);
|
||||
|
||||
Response.Cookies.Append(
|
||||
VisitorCookie,
|
||||
visitor.VisitorId.ToString(),
|
||||
new CookieOptions()
|
||||
{
|
||||
Expires = DateTimeOffset.UtcNow.AddYears(1),
|
||||
IsEssential = true
|
||||
}
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
var visitor = _visitors.GetVisitor(VisitorId);
|
||||
if (visitor != null)
|
||||
string url = Request.GetEncodedUrl();
|
||||
string referrer = (Request.Headers[HeaderNames.Referer] != StringValues.Empty) ? Request.Headers[HeaderNames.Referer] : "";
|
||||
int? userid = null;
|
||||
if (User.HasClaim(item => item.Type == ClaimTypes.PrimarySid))
|
||||
{
|
||||
userid = int.Parse(User.Claims.First(item => item.Type == ClaimTypes.PrimarySid).Value);
|
||||
}
|
||||
|
||||
var VisitorCookie = "APP_VISITOR_" + SiteId.ToString();
|
||||
if (!int.TryParse(Request.Cookies[VisitorCookie], out VisitorId))
|
||||
{
|
||||
var visitor = new Visitor();
|
||||
visitor.SiteId = SiteId;
|
||||
visitor.IPAddress = RemoteIPAddress;
|
||||
visitor.UserAgent = useragent;
|
||||
visitor.Language = language;
|
||||
visitor.Url = url;
|
||||
if (!string.IsNullOrEmpty(referrer))
|
||||
{
|
||||
visitor.Referrer = referrer;
|
||||
}
|
||||
if (userid != null)
|
||||
{
|
||||
visitor.UserId = userid;
|
||||
}
|
||||
visitor.Visits += 1;
|
||||
visitor.Referrer = referrer;
|
||||
visitor.UserId = userid;
|
||||
visitor.Visits = 1;
|
||||
visitor.CreatedOn = DateTime.UtcNow;
|
||||
visitor.VisitedOn = DateTime.UtcNow;
|
||||
_visitors.UpdateVisitor(visitor);
|
||||
visitor = _visitors.AddVisitor(visitor);
|
||||
|
||||
Response.Cookies.Append(
|
||||
VisitorCookie,
|
||||
visitor.VisitorId.ToString(),
|
||||
new CookieOptions()
|
||||
{
|
||||
Expires = DateTimeOffset.UtcNow.AddYears(1),
|
||||
IsEssential = true
|
||||
}
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
Response.Cookies.Delete(VisitorCookie);
|
||||
var visitor = _visitors.GetVisitor(VisitorId);
|
||||
if (visitor != null)
|
||||
{
|
||||
visitor.IPAddress = RemoteIPAddress;
|
||||
visitor.UserAgent = useragent;
|
||||
visitor.Language = language;
|
||||
visitor.Url = url;
|
||||
if (!string.IsNullOrEmpty(referrer))
|
||||
{
|
||||
visitor.Referrer = referrer;
|
||||
}
|
||||
if (userid != null)
|
||||
{
|
||||
visitor.UserId = userid;
|
||||
}
|
||||
visitor.Visits += 1;
|
||||
visitor.VisitedOn = DateTime.UtcNow;
|
||||
_visitors.UpdateVisitor(visitor);
|
||||
}
|
||||
else
|
||||
{
|
||||
Response.Cookies.Delete(VisitorCookie);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Other, "Error Tracking Visitor {Error}", ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private string CreatePWAScript(Alias alias, Site site, Route route)
|
||||
|
@ -83,5 +83,7 @@ namespace Oqtane.Shared {
|
||||
public static readonly string RequestVerificationToken = "__RequestVerificationToken";
|
||||
public static readonly string AntiForgeryTokenHeaderName = "X-XSRF-TOKEN-HEADER";
|
||||
public static readonly string AntiForgeryTokenCookieName = "X-XSRF-TOKEN-COOKIE";
|
||||
|
||||
public static readonly string DefaultVisitorFilter = "bot,crawler,slurp,spider,(none),??";
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user