commit
a1bff809f3
@ -10,100 +10,32 @@
|
|||||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row mb-1 align-items-center">
|
|
||||||
|
|
||||||
<div class="col-sm-9">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-1 align-items-center">
|
|
||||||
|
|
||||||
<div class="col-sm-9">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-1 align-items-center">
|
|
||||||
|
|
||||||
<div class="col-sm-9">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-1 align-items-center">
|
|
||||||
|
|
||||||
<div class="col-sm-9">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-1 align-items-center">
|
|
||||||
|
|
||||||
<div class="col-sm-9">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-1 align-items-center">
|
|
||||||
|
|
||||||
<div class="col-sm-9">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-1 align-items-center">
|
|
||||||
|
|
||||||
<div class="col-sm-9">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-1 align-items-center">
|
|
||||||
|
|
||||||
<div class="col-sm-9">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-1 align-items-center">
|
|
||||||
|
|
||||||
<div class="col-sm-9">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-1 align-items-center">
|
|
||||||
|
|
||||||
<div class="col-sm-9">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-1 align-items-center">
|
|
||||||
|
|
||||||
<div class="col-sm-9">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div class="row mb-1 align-items-center">
|
<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>
|
<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">
|
<div class="col-sm-9">
|
||||||
<input id="dateTime" class="form-control" @bind="@_logDate" readonly />
|
<input id="dateTime" class="form-control" @bind="@_logDate" readonly />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<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>
|
<Label Class="col-sm-3" For="level" HelpText="The level of this log" ResourceKey="Level">Level: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="level" class="form-control" @bind="@_level" readonly />
|
<input id="level" class="form-control" @bind="@_level" readonly />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<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>
|
<Label Class="col-sm-3" For="feature" HelpText="The feature that was affected" ResourceKey="Feature">Feature: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="feature" class="form-control" @bind="@_feature" readonly />
|
<input id="feature" class="form-control" @bind="@_feature" readonly />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<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>
|
<Label Class="col-sm-3" For="function" HelpText="The function that was performed" ResourceKey="Function">Function: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="function" class="form-control" @bind="@_function" readonly />
|
<input id="function" class="form-control" @bind="@_function" readonly />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<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>
|
<Label Class="col-sm-3" For="category" HelpText="The categories that were affected" ResourceKey="Category">Category: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="category" class="form-control" @bind="@_category" readonly />
|
<input id="category" class="form-control" @bind="@_category" readonly />
|
||||||
</div>
|
</div>
|
||||||
@ -111,7 +43,7 @@
|
|||||||
@if (_pageName != string.Empty)
|
@if (_pageName != string.Empty)
|
||||||
{
|
{
|
||||||
<div class="row mb-1 align-items-center">
|
<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>
|
<Label Class="col-sm-3" For="page" HelpText="The page that was affected" ResourceKey="Page">Page: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="page" class="form-control" @bind="@_pageName" readonly />
|
<input id="page" class="form-control" @bind="@_pageName" readonly />
|
||||||
</div>
|
</div>
|
||||||
@ -120,7 +52,7 @@
|
|||||||
@if (_moduleTitle != string.Empty)
|
@if (_moduleTitle != string.Empty)
|
||||||
{
|
{
|
||||||
<div class="row mb-1 align-items-center">
|
<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>
|
<Label Class="col-sm-3" For="module" HelpText="The module that was affected" ResourceKey="Module">Module: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="module" class="form-control" @bind="@_moduleTitle" readonly />
|
<input id="module" class="form-control" @bind="@_moduleTitle" readonly />
|
||||||
</div>
|
</div>
|
||||||
@ -129,26 +61,26 @@
|
|||||||
@if (_username != string.Empty)
|
@if (_username != string.Empty)
|
||||||
{
|
{
|
||||||
<div class="row mb-1 align-items-center">
|
<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>
|
<Label Class="col-sm-3" For="user" HelpText="The user that caused this log" ResourceKey="User">User: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="user" class="form-control" @bind="@_username" readonly />
|
<input id="user" class="form-control" @bind="@_username" readonly />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
<div class="row mb-1 align-items-center">
|
<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>
|
<Label Class="col-sm-3" For="url" HelpText="The url the log comes from" ResourceKey="Url">Url: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="url" class="form-control" @bind="@_url" readonly />
|
<input id="url" class="form-control" @bind="@_url" readonly />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<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>
|
<Label Class="col-sm-3" For="template" HelpText="What the log is about" ResourceKey="Template">Template: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="template" class="form-control" @bind="@_template" readonly />
|
<input id="template" class="form-control" @bind="@_template" readonly />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<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>
|
<Label Class="col-sm-3" For="message" HelpText="The message that the system generated" ResourceKey="Message">Message: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<textarea id="message" class="form-control" @bind="@_message" rows="5" readonly></textarea>
|
<textarea id="message" class="form-control" @bind="@_message" rows="5" readonly></textarea>
|
||||||
</div>
|
</div>
|
||||||
@ -156,24 +88,25 @@
|
|||||||
@if (!string.IsNullOrEmpty(_exception))
|
@if (!string.IsNullOrEmpty(_exception))
|
||||||
{
|
{
|
||||||
<div class="row mb-1 align-items-center">
|
<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>
|
<Label Class="col-sm-3" For="exception" HelpText="The exceptions generated by the system" ResourceKey="Exception">Exception: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<textarea id="exception" class="form-control" @bind="@_exception" rows="5" readonly></textarea>
|
<textarea id="exception" class="form-control" @bind="@_exception" rows="5" readonly></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="properties" HelpText="The properties that were affected" ResourceKey="Properties">Properties: </Label>
|
<Label Class="col-sm-3" For="properties" HelpText="The properties that were affected" ResourceKey="Properties">Properties: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<textarea id="properties" class="form-control" @bind="@_properties" rows="5" readonly></textarea>
|
<textarea id="properties" class="form-control" @bind="@_properties" rows="5" readonly></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="server" HelpText="The server that was affected" ResourceKey="Server">Server: </Label>
|
<Label Class="col-sm-3" For="server" HelpText="The server that was affected" ResourceKey="Server">Server: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="server" class="form-control" @bind="@_server" readonly />
|
<input id="server" class="form-control" @bind="@_server" readonly />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||||
|
|
||||||
|
@ -51,11 +51,11 @@ else
|
|||||||
{
|
{
|
||||||
<Pager Items="@_logs">
|
<Pager Items="@_logs">
|
||||||
<Header>
|
<Header>
|
||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
<th>@Localizer["Date"]</th>
|
<th>@Localizer["Date"]</th>
|
||||||
<th>@Localizer["Level"]</th>
|
<th>@Localizer["Level"]</th>
|
||||||
<th>@Localizer["Feature"]</th>
|
<th>@Localizer["Feature"]</th>
|
||||||
<th>@Localizer["Function"]</th>
|
<th>@Localizer["Function"]</th>
|
||||||
</Header>
|
</Header>
|
||||||
<Row>
|
<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())" ResourceKey="LogDetails" /></td>
|
||||||
|
123
Oqtane.Client/Modules/Admin/Visitors/Detail.razor
Normal file
123
Oqtane.Client/Modules/Admin/Visitors/Detail.razor
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
@namespace Oqtane.Modules.Admin.Visitors
|
||||||
|
@using System.Globalization
|
||||||
|
@inherits ModuleBase
|
||||||
|
@inject NavigationManager NavigationManager
|
||||||
|
@inject IVisitorService VisitorService
|
||||||
|
@inject IUserService UserService
|
||||||
|
@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>
|
||||||
|
|
||||||
|
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
private int _visitorId;
|
||||||
|
private string _ip = string.Empty;
|
||||||
|
private string _language = string.Empty;
|
||||||
|
private string _useragent = string.Empty;
|
||||||
|
private string _url = string.Empty;
|
||||||
|
private string _referrer = string.Empty;
|
||||||
|
private string _user = string.Empty;
|
||||||
|
private string _visits = string.Empty;
|
||||||
|
private string _visited = string.Empty;
|
||||||
|
private string _created = string.Empty;
|
||||||
|
|
||||||
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_visitorId = Int32.Parse(PageState.QueryString["id"]);
|
||||||
|
var visitor = await VisitorService.GetVisitorAsync(_visitorId);
|
||||||
|
if (visitor != null)
|
||||||
|
{
|
||||||
|
_ip = visitor.IPAddress;
|
||||||
|
_language = visitor.Language;
|
||||||
|
_useragent = visitor.UserAgent;
|
||||||
|
_url = visitor.Url;
|
||||||
|
_referrer = visitor.Referrer;
|
||||||
|
_visits = visitor.Visits.ToString();
|
||||||
|
_visited = visitor.VisitedOn.ToString(CultureInfo.CurrentCulture);
|
||||||
|
_created = visitor.CreatedOn.ToString(CultureInfo.CurrentCulture);
|
||||||
|
|
||||||
|
if (visitor.UserId != null)
|
||||||
|
{
|
||||||
|
var user = await UserService.GetUserAsync(visitor.UserId.Value, PageState.Site.SiteId);
|
||||||
|
if (user != null)
|
||||||
|
{
|
||||||
|
_user = user.DisplayName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AddModuleMessage(Localizer["Error.LoadVisitor"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Loading Visitor {VisitorId} {Error}", _visitorId, ex.Message);
|
||||||
|
AddModuleMessage(Localizer["Error.LoadVisitor"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -33,13 +33,16 @@ else
|
|||||||
<br/>
|
<br/>
|
||||||
<Pager Items="@_visitors">
|
<Pager Items="@_visitors">
|
||||||
<Header>
|
<Header>
|
||||||
|
<th style="width: 1px;"> </th>
|
||||||
<th>@Localizer["IP"]</th>
|
<th>@Localizer["IP"]</th>
|
||||||
<th>@Localizer["User"]</th>
|
<th>@Localizer["User"]</th>
|
||||||
<th>@Localizer["Language"]</th>
|
<th>@Localizer["Language"]</th>
|
||||||
<th>@Localizer["Visits"]</th>
|
<th>@Localizer["Visits"]</th>
|
||||||
<th>@Localizer["Visited"]</th>
|
<th>@Localizer["Visited"]</th>
|
||||||
|
<th>@Localizer["Created"]</th>
|
||||||
</Header>
|
</Header>
|
||||||
<Row>
|
<Row>
|
||||||
|
<td><ActionLink Action="Detail" Parameters="@($"id=" + context.VisitorId.ToString())" ResourceKey="Details" /></td>
|
||||||
<td>@context.IPAddress</td>
|
<td>@context.IPAddress</td>
|
||||||
<td>
|
<td>
|
||||||
@if (context.UserId != null)
|
@if (context.UserId != null)
|
||||||
@ -50,6 +53,7 @@ else
|
|||||||
<td>@context.Language</td>
|
<td>@context.Language</td>
|
||||||
<td>@context.Visits</td>
|
<td>@context.Visits</td>
|
||||||
<td>@context.VisitedOn</td>
|
<td>@context.VisitedOn</td>
|
||||||
|
<td>@context.CreatedOn</td>
|
||||||
</Row>
|
</Row>
|
||||||
</Pager>
|
</Pager>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
|
@ -34,10 +34,6 @@
|
|||||||
<ProjectReference Include="..\Oqtane.Shared\Oqtane.Shared.csproj" />
|
<ProjectReference Include="..\Oqtane.Shared\Oqtane.Shared.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Folder Include="Resources\" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<TrimmerRootAssembly Include="System.Runtime" />
|
<TrimmerRootAssembly Include="System.Runtime" />
|
||||||
<TrimmerRootAssembly Include="System.Linq.Parallel" />
|
<TrimmerRootAssembly Include="System.Linq.Parallel" />
|
||||||
|
@ -132,11 +132,11 @@
|
|||||||
<data name="User" xml:space="preserve">
|
<data name="User" xml:space="preserve">
|
||||||
<value>User</value>
|
<value>User</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Visited" xml:space="preserve">
|
<data name="Requested" xml:space="preserve">
|
||||||
<value>Visited</value>
|
<value>Requested</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Visits" xml:space="preserve">
|
<data name="Requests" xml:space="preserve">
|
||||||
<value>Visits</value>
|
<value>Requests</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Mapped" xml:space="preserve">
|
<data name="Mapped" xml:space="preserve">
|
||||||
<value>Mapped Urls</value>
|
<value>Mapped Urls</value>
|
||||||
|
177
Oqtane.Client/Resources/Modules/Admin/Visitors/Detail.resx
Normal file
177
Oqtane.Client/Resources/Modules/Admin/Visitors/Detail.resx
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<data name="IP.Text" xml:space="preserve">
|
||||||
|
<value>IP Address:</value>
|
||||||
|
</data>
|
||||||
|
<data name="IP.HelpText" xml:space="preserve">
|
||||||
|
<value>The last recorded IP address for this visitor</value>
|
||||||
|
</data>
|
||||||
|
<data name="Language.HelpText" xml:space="preserve">
|
||||||
|
<value>The last recorded language for this visitor</value>
|
||||||
|
</data>
|
||||||
|
<data name="Language.Text" xml:space="preserve">
|
||||||
|
<value>Language:</value>
|
||||||
|
</data>
|
||||||
|
<data name="Error.LoadVisitor" xml:space="preserve">
|
||||||
|
<value>Error Loading Visitor</value>
|
||||||
|
</data>
|
||||||
|
<data name="Created.HelpText" xml:space="preserve">
|
||||||
|
<value>The frst recorded date/time when the visitor visited the site</value>
|
||||||
|
</data>
|
||||||
|
<data name="Created.Text" xml:space="preserve">
|
||||||
|
<value>Created:</value>
|
||||||
|
</data>
|
||||||
|
<data name="Referrer.HelpText" xml:space="preserve">
|
||||||
|
<value>The last recorded referrer for this visitor</value>
|
||||||
|
</data>
|
||||||
|
<data name="Referrer.Text" xml:space="preserve">
|
||||||
|
<value>Referrer:</value>
|
||||||
|
</data>
|
||||||
|
<data name="Url.HelpText" xml:space="preserve">
|
||||||
|
<value>The last recorded Url for this visitor</value>
|
||||||
|
</data>
|
||||||
|
<data name="Url.Text" xml:space="preserve">
|
||||||
|
<value>Url:</value>
|
||||||
|
</data>
|
||||||
|
<data name="User.HelpText" xml:space="preserve">
|
||||||
|
<value>The last recorded user associated with this visitor</value>
|
||||||
|
</data>
|
||||||
|
<data name="User.Text" xml:space="preserve">
|
||||||
|
<value>User:</value>
|
||||||
|
</data>
|
||||||
|
<data name="UserAgent.HelpText" xml:space="preserve">
|
||||||
|
<value>The last recorded user agent for this visitor</value>
|
||||||
|
</data>
|
||||||
|
<data name="UserAgent.Text" xml:space="preserve">
|
||||||
|
<value>User Agent:</value>
|
||||||
|
</data>
|
||||||
|
<data name="Visited.HelpText" xml:space="preserve">
|
||||||
|
<value>The last recorded date/time when the visitor visited the site</value>
|
||||||
|
</data>
|
||||||
|
<data name="Visited.Text" xml:space="preserve">
|
||||||
|
<value>Visited:</value>
|
||||||
|
</data>
|
||||||
|
<data name="Visits.HelpText" xml:space="preserve">
|
||||||
|
<value>The total number of visits by this visitor all time</value>
|
||||||
|
</data>
|
||||||
|
<data name="Visits.Text" xml:space="preserve">
|
||||||
|
<value>Visits:</value>
|
||||||
|
</data>
|
||||||
|
</root>
|
@ -120,11 +120,11 @@
|
|||||||
<data name="Url" xml:space="preserve">
|
<data name="Url" xml:space="preserve">
|
||||||
<value>Url</value>
|
<value>Url</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Requested" xml:space="preserve">
|
<data name="Visited" xml:space="preserve">
|
||||||
<value>Requested</value>
|
<value>Visited</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Requests" xml:space="preserve">
|
<data name="Visits" xml:space="preserve">
|
||||||
<value>Requests</value>
|
<value>Visits</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="AllVisitors" xml:space="preserve">
|
<data name="AllVisitors" xml:space="preserve">
|
||||||
<value>All Visitors</value>
|
<value>All Visitors</value>
|
||||||
@ -162,4 +162,7 @@
|
|||||||
<data name="VisitorTracking.Text" xml:space="preserve">
|
<data name="VisitorTracking.Text" xml:space="preserve">
|
||||||
<value>Visitor Tracking Enabled?</value>
|
<value>Visitor Tracking Enabled?</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Created" xml:space="preserve">
|
||||||
|
<value>Created</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -17,5 +17,13 @@ namespace Oqtane.Services
|
|||||||
/// <param name="siteId">ID-reference of a <see cref="Site"/></param>
|
/// <param name="siteId">ID-reference of a <see cref="Site"/></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<List<Visitor>> GetVisitorsAsync(int siteId, DateTime fromDate);
|
Task<List<Visitor>> GetVisitorsAsync(int siteId, DateTime fromDate);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a specific <see cref="Visitor"/> of this <see cref="Site"/>.
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="visitorId">ID-reference of a <see cref="Visitor"/></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<Visitor> GetVisitorAsync(int visitorId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,5 +28,10 @@ namespace Oqtane.Services
|
|||||||
List<Visitor> visitors = await GetJsonAsync<List<Visitor>>($"{Apiurl}?siteid={siteId}&fromdate={fromDate.ToString("dd-MMM-yyyy")}");
|
List<Visitor> visitors = await GetJsonAsync<List<Visitor>>($"{Apiurl}?siteid={siteId}&fromdate={fromDate.ToString("dd-MMM-yyyy")}");
|
||||||
return visitors.OrderByDescending(item => item.VisitedOn).ToList();
|
return visitors.OrderByDescending(item => item.VisitedOn).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<Visitor> GetVisitorAsync(int visitorId)
|
||||||
|
{
|
||||||
|
return await GetJsonAsync<Visitor>($"{Apiurl}/{visitorId}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -204,12 +204,15 @@ namespace Oqtane.Controllers
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case EntityNames.Visitor:
|
case EntityNames.Visitor:
|
||||||
authorized = false;
|
|
||||||
var visitorCookie = "APP_VISITOR_" + _alias.SiteId.ToString();
|
var visitorCookie = "APP_VISITOR_" + _alias.SiteId.ToString();
|
||||||
if (int.TryParse(Request.Cookies[visitorCookie], out int visitorId))
|
if (int.TryParse(Request.Cookies[visitorCookie], out int visitorId))
|
||||||
{
|
{
|
||||||
authorized = (visitorId == entityId);
|
authorized = (visitorId == entityId);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
authorized = User.IsInRole(RoleNames.Admin);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return authorized;
|
return authorized;
|
||||||
|
@ -42,5 +42,33 @@ namespace Oqtane.Controllers
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GET api/<controller>/5
|
||||||
|
[HttpGet("{id}")]
|
||||||
|
public Visitor Get(int id)
|
||||||
|
{
|
||||||
|
bool authorized;
|
||||||
|
var visitorCookie = "APP_VISITOR_" + _alias.SiteId.ToString();
|
||||||
|
if (int.TryParse(Request.Cookies[visitorCookie], out int visitorId))
|
||||||
|
{
|
||||||
|
authorized = (visitorId == id);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
authorized = User.IsInRole(RoleNames.Admin);
|
||||||
|
}
|
||||||
|
|
||||||
|
var visitor = _visitors.GetVisitor(id);
|
||||||
|
if (authorized && visitor != null && visitor.SiteId == _alias.SiteId)
|
||||||
|
{
|
||||||
|
return visitor;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Visitor Get Attempt {VisitorId}", id);
|
||||||
|
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,33 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Oqtane.Databases.Interfaces;
|
||||||
|
using Oqtane.Migrations.EntityBuilders;
|
||||||
|
using Oqtane.Repository;
|
||||||
|
|
||||||
|
namespace Oqtane.Migrations.Tenant
|
||||||
|
{
|
||||||
|
[DbContext(typeof(TenantDBContext))]
|
||||||
|
[Migration("Tenant.03.00.01.05")]
|
||||||
|
public class AddVisitorReferrer : MultiDatabaseMigration
|
||||||
|
{
|
||||||
|
public AddVisitorReferrer(IDatabase database) : base(database)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
var visitorEntityBuilder = new VisitorEntityBuilder(migrationBuilder, ActiveDatabase);
|
||||||
|
|
||||||
|
visitorEntityBuilder.AddStringColumn("Referrer", 500, true);
|
||||||
|
visitorEntityBuilder.AddStringColumn("Url", 500, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
var visitorEntityBuilder = new VisitorEntityBuilder(migrationBuilder, ActiveDatabase);
|
||||||
|
|
||||||
|
visitorEntityBuilder.DropColumn("Referrer");
|
||||||
|
visitorEntityBuilder.DropColumn("Url");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -189,19 +189,33 @@ namespace Oqtane.Pages
|
|||||||
|
|
||||||
private void TrackVisitor(int SiteId)
|
private void TrackVisitor(int SiteId)
|
||||||
{
|
{
|
||||||
|
// get request attributes
|
||||||
|
string ip = HttpContext.Connection.RemoteIpAddress.ToString();
|
||||||
|
string useragent = Request.Headers[HeaderNames.UserAgent];
|
||||||
|
string language = Request.Headers[HeaderNames.AcceptLanguage];
|
||||||
|
if (language.Contains(","))
|
||||||
|
{
|
||||||
|
language = language.Substring(0, language.IndexOf(","));
|
||||||
|
}
|
||||||
|
string url = Request.GetEncodedUrl();
|
||||||
|
string referrer = 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();
|
var VisitorCookie = "APP_VISITOR_" + SiteId.ToString();
|
||||||
if (!int.TryParse(Request.Cookies[VisitorCookie], out VisitorId))
|
if (!int.TryParse(Request.Cookies[VisitorCookie], out VisitorId))
|
||||||
{
|
{
|
||||||
var visitor = new Visitor();
|
var visitor = new Visitor();
|
||||||
visitor.SiteId = SiteId;
|
visitor.SiteId = SiteId;
|
||||||
visitor.IPAddress = HttpContext.Connection.RemoteIpAddress.ToString();
|
visitor.IPAddress = ip;
|
||||||
visitor.UserAgent = Request.Headers[HeaderNames.UserAgent];
|
visitor.UserAgent = useragent;
|
||||||
visitor.Language = Request.Headers[HeaderNames.AcceptLanguage];
|
visitor.Language = language;
|
||||||
if (visitor.Language.Contains(","))
|
visitor.Url = url;
|
||||||
{
|
visitor.Referrer = referrer;
|
||||||
visitor.Language = visitor.Language.Substring(0, visitor.Language.IndexOf(","));
|
visitor.UserId = userid;
|
||||||
}
|
|
||||||
visitor.UserId = null;
|
|
||||||
visitor.Visits = 1;
|
visitor.Visits = 1;
|
||||||
visitor.CreatedOn = DateTime.UtcNow;
|
visitor.CreatedOn = DateTime.UtcNow;
|
||||||
visitor.VisitedOn = DateTime.UtcNow;
|
visitor.VisitedOn = DateTime.UtcNow;
|
||||||
@ -222,16 +236,17 @@ namespace Oqtane.Pages
|
|||||||
var visitor = _visitors.GetVisitor(VisitorId);
|
var visitor = _visitors.GetVisitor(VisitorId);
|
||||||
if (visitor != null)
|
if (visitor != null)
|
||||||
{
|
{
|
||||||
visitor.IPAddress = HttpContext.Connection.RemoteIpAddress.ToString();
|
visitor.IPAddress = ip;
|
||||||
visitor.UserAgent = Request.Headers[HeaderNames.UserAgent];
|
visitor.UserAgent = useragent;
|
||||||
visitor.Language = Request.Headers[HeaderNames.AcceptLanguage];
|
visitor.Language = language;
|
||||||
if (visitor.Language.Contains(","))
|
visitor.Url = url;
|
||||||
|
if (!string.IsNullOrEmpty(referrer))
|
||||||
{
|
{
|
||||||
visitor.Language = visitor.Language.Substring(0, visitor.Language.IndexOf(","));
|
visitor.Referrer = referrer;
|
||||||
}
|
}
|
||||||
if (User.HasClaim(item => item.Type == ClaimTypes.PrimarySid))
|
if (userid != null)
|
||||||
{
|
{
|
||||||
visitor.UserId = int.Parse(User.Claims.First(item => item.Type == ClaimTypes.PrimarySid).Value);
|
visitor.UserId = userid;
|
||||||
}
|
}
|
||||||
visitor.Visits += 1;
|
visitor.Visits += 1;
|
||||||
visitor.VisitedOn = DateTime.UtcNow;
|
visitor.VisitedOn = DateTime.UtcNow;
|
||||||
|
@ -29,20 +29,30 @@ namespace Oqtane.Models
|
|||||||
public int Visits { get; set; }
|
public int Visits { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// IP Address of visitor
|
/// Last recorded IP Address of visitor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string IPAddress { get; set; }
|
public string IPAddress { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// User agent of visitor
|
/// Last recorded user agent of visitor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string UserAgent { get; set; }
|
public string UserAgent { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Language of visitor
|
/// Last recorded language of visitor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Language { get; set; }
|
public string Language { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Last recorded Url of visitor
|
||||||
|
/// </summary>
|
||||||
|
public string Url { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Last recorded Referrer of visitor
|
||||||
|
/// </summary>
|
||||||
|
public string Referrer { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Date the visitor first visited the site
|
/// Date the visitor first visited the site
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
Reference in New Issue
Block a user