ui tweaks/improvements including recent players and ip info lookup
This commit is contained in:
parent
fd049edb3f
commit
284c2e9726
@ -10,5 +10,6 @@ namespace SharedLibraryCore.Dtos
|
|||||||
public int LinkId { get; set; }
|
public int LinkId { get; set; }
|
||||||
public EFClient.Permission Level { get; set; }
|
public EFClient.Permission Level { get; set; }
|
||||||
public DateTime LastConnection { get; set; }
|
public DateTime LastConnection { get; set; }
|
||||||
|
public bool IsMasked { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ namespace SharedLibraryCore.Dtos
|
|||||||
public int MaxClients { get; set; }
|
public int MaxClients { get; set; }
|
||||||
public List<ChatInfo> ChatHistory { get; set; }
|
public List<ChatInfo> ChatHistory { get; set; }
|
||||||
public List<PlayerInfo> Players { get; set; }
|
public List<PlayerInfo> Players { get; set; }
|
||||||
|
public List<Report> Reports { get; set; }
|
||||||
public ClientHistoryInfo ClientHistory { get; set; }
|
public ClientHistoryInfo ClientHistory { get; set; }
|
||||||
public long ID { get; set; }
|
public long ID { get; set; }
|
||||||
public bool Online { get; set; }
|
public bool Online { get; set; }
|
||||||
|
@ -47,13 +47,15 @@ namespace SharedLibraryCore.Services
|
|||||||
private readonly ApplicationConfiguration _appConfig;
|
private readonly ApplicationConfiguration _appConfig;
|
||||||
private readonly IDatabaseContextFactory _contextFactory;
|
private readonly IDatabaseContextFactory _contextFactory;
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
|
private readonly IGeoLocationService _geoLocationService;
|
||||||
|
|
||||||
public ClientService(ILogger<ClientService> logger, IDatabaseContextFactory databaseContextFactory,
|
public ClientService(ILogger<ClientService> logger, IDatabaseContextFactory databaseContextFactory,
|
||||||
ApplicationConfiguration appConfig)
|
ApplicationConfiguration appConfig, IGeoLocationService geoLocationService)
|
||||||
{
|
{
|
||||||
_contextFactory = databaseContextFactory;
|
_contextFactory = databaseContextFactory;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_appConfig = appConfig;
|
_appConfig = appConfig;
|
||||||
|
_geoLocationService = geoLocationService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<EFClient> Create(EFClient entity)
|
public async Task<EFClient> Create(EFClient entity)
|
||||||
@ -782,7 +784,8 @@ namespace SharedLibraryCore.Services
|
|||||||
Password = client.Password,
|
Password = client.Password,
|
||||||
PasswordSalt = client.PasswordSalt,
|
PasswordSalt = client.PasswordSalt,
|
||||||
NetworkId = client.NetworkId,
|
NetworkId = client.NetworkId,
|
||||||
LastConnection = client.LastConnection
|
LastConnection = client.LastConnection,
|
||||||
|
Masked = client.Masked
|
||||||
};
|
};
|
||||||
|
|
||||||
return await iqClients.ToListAsync();
|
return await iqClients.ToListAsync();
|
||||||
@ -901,18 +904,24 @@ namespace SharedLibraryCore.Services
|
|||||||
|
|
||||||
await using var context = _contextFactory.CreateContext(false);
|
await using var context = _contextFactory.CreateContext(false);
|
||||||
var iqClients = context.Clients
|
var iqClients = context.Clients
|
||||||
.Where(_client => _client.CurrentAlias.IPAddress != null)
|
.Where(client => client.CurrentAlias.IPAddress != null)
|
||||||
.Where(_client => _client.FirstConnection >= startOfPeriod)
|
.Where(client => client.FirstConnection >= startOfPeriod)
|
||||||
.OrderByDescending(_client => _client.FirstConnection)
|
.OrderByDescending(client => client.FirstConnection)
|
||||||
.Select(_client => new PlayerInfo
|
.Select(client => new PlayerInfo
|
||||||
{
|
{
|
||||||
ClientId = _client.ClientId,
|
ClientId = client.ClientId,
|
||||||
Name = _client.CurrentAlias.Name,
|
Name = client.CurrentAlias.Name,
|
||||||
IPAddress = _client.CurrentAlias.IPAddress.ConvertIPtoString(),
|
IPAddress = client.CurrentAlias.IPAddress.ConvertIPtoString(),
|
||||||
LastConnection = _client.FirstConnection
|
LastConnection = client.FirstConnection
|
||||||
});
|
});
|
||||||
|
|
||||||
return await iqClients.ToListAsync();
|
var clientList = await iqClients.ToListAsync();
|
||||||
|
foreach (var client in clientList)
|
||||||
|
{
|
||||||
|
client.GeoLocationInfo = await _geoLocationService.Locate(client.IPAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
return clientList;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Mvc;
|
|||||||
using SharedLibraryCore;
|
using SharedLibraryCore;
|
||||||
using SharedLibraryCore.Commands;
|
using SharedLibraryCore.Commands;
|
||||||
using SharedLibraryCore.Configuration;
|
using SharedLibraryCore.Configuration;
|
||||||
|
using SharedLibraryCore.Dtos;
|
||||||
using SharedLibraryCore.Interfaces;
|
using SharedLibraryCore.Interfaces;
|
||||||
using WebfrontCore.Permissions;
|
using WebfrontCore.Permissions;
|
||||||
using WebfrontCore.ViewModels;
|
using WebfrontCore.ViewModels;
|
||||||
@ -317,17 +318,21 @@ namespace WebfrontCore.Controllers
|
|||||||
public async Task<IActionResult> RecentClientsForm()
|
public async Task<IActionResult> RecentClientsForm()
|
||||||
{
|
{
|
||||||
var clients = await Manager.GetClientService().GetRecentClients();
|
var clients = await Manager.GetClientService().GetRecentClients();
|
||||||
foreach (var client in clients)
|
|
||||||
{
|
|
||||||
client.IPAddress =
|
|
||||||
_appConfig.HasPermission(Client.Level, WebfrontEntity.ClientIPAddress, WebfrontPermission.Read)
|
|
||||||
? client.IPAddress
|
|
||||||
: null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return View("~/Views/Shared/Components/Client/_RecentClients.cshtml", clients);
|
return View("~/Views/Shared/Components/Client/_RecentClients.cshtml", clients);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IActionResult RecentReportsForm()
|
||||||
|
{
|
||||||
|
var serverInfo = Manager.GetServers().Select(server =>
|
||||||
|
new ServerInfo
|
||||||
|
{
|
||||||
|
Name = server.Hostname,
|
||||||
|
Reports = server.Reports
|
||||||
|
});
|
||||||
|
|
||||||
|
return View("Partials/_Reports", serverInfo);
|
||||||
|
}
|
||||||
|
|
||||||
public IActionResult FlagForm()
|
public IActionResult FlagForm()
|
||||||
{
|
{
|
||||||
var info = new ActionInfo
|
var info = new ActionInfo
|
||||||
|
@ -176,11 +176,12 @@ namespace WebfrontCore.Controllers
|
|||||||
adminsDict.Add(admin.Level, new List<ClientInfo>());
|
adminsDict.Add(admin.Level, new List<ClientInfo>());
|
||||||
}
|
}
|
||||||
|
|
||||||
adminsDict[admin.Level].Add(new ClientInfo()
|
adminsDict[admin.Level].Add(new ClientInfo
|
||||||
{
|
{
|
||||||
Name = admin.Name,
|
Name = admin.Name,
|
||||||
ClientId = admin.ClientId,
|
ClientId = admin.ClientId,
|
||||||
LastConnection = admin.LastConnection
|
LastConnection = admin.LastConnection,
|
||||||
|
IsMasked = admin.Masked
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,8 @@
|
|||||||
|
|
||||||
<!-- desktop -->
|
<!-- desktop -->
|
||||||
<div class="content mt-0">
|
<div class="content mt-0">
|
||||||
<h2 class="content-title mt-20">@ViewBag.ResultCount result(s) for <span class="badge badge-primary font-size-18">@ViewBag.SearchTerm</span></h2>
|
<h2 class="content-title mt-20 mb-0">Search Results</h2>
|
||||||
|
<div class="text-muted mb-15"><span class="badge">@ViewBag.SearchTerm</span> returned <span class="text-primary">@ViewBag.ResultCount</span> matche(s)</div>
|
||||||
|
|
||||||
<table class="table d-none d-md-table">
|
<table class="table d-none d-md-table">
|
||||||
<thead>
|
<thead>
|
||||||
|
@ -9,7 +9,8 @@
|
|||||||
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<h2 class="content-title mt-20">@Html.Raw(Utilities.FormatExt(ViewBag.Localization["WEBFRONT_STATS_MESSAGES_FOUND"], $"<span class=\"badge badge-primary font-size-18\">{Model.TotalResultCount.ToString("N0")}</span>"))</h2>
|
<h2 class="content-title mt-20 mb-0">Search Results</h2>
|
||||||
|
<div class="text-muted mb-15">@Html.Raw(Utilities.FormatExt(ViewBag.Localization["WEBFRONT_STATS_MESSAGES_FOUND"], $"<span class=\"badge\">{Model.TotalResultCount.ToString("N0")}</span>"))</div>
|
||||||
|
|
||||||
<table class="table bg-dark-dm bg-light-lm rounded" style="table-layout: fixed">
|
<table class="table bg-dark-dm bg-light-lm rounded" style="table-layout: fixed">
|
||||||
<thead class="d-none d-lg-table-header-group">
|
<thead class="d-none d-lg-table-header-group">
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
@model Dictionary<SharedLibraryCore.Database.Models.EFClient.Permission, IList<SharedLibraryCore.Dtos.ClientInfo>>
|
@model Dictionary<Data.Models.Client.EFClient.Permission, IList<SharedLibraryCore.Dtos.ClientInfo>>
|
||||||
<div class="content mt-0">
|
<div class="content mt-0">
|
||||||
<h4 class="content-title mt-20">@ViewBag.Title</h4>
|
<h4 class="content-title mt-20">@ViewBag.Title</h4>
|
||||||
|
|
||||||
|
|
||||||
@foreach (var key in Model.Keys)
|
@foreach (var key in Model.Keys)
|
||||||
{
|
{
|
||||||
<table class="table mb-20">
|
<table class="table mb-20">
|
||||||
@ -12,20 +13,34 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@foreach (var client in Model[key].OrderByDescending(client => client.LastConnection))
|
<has-permission entity="ClientLevel" required-permission="Read">
|
||||||
{
|
@foreach (var client in Model[key].OrderByDescending(client => client.LastConnection))
|
||||||
<tr class="bg-dark-dm bg-light-lm">
|
{
|
||||||
<td>
|
if (!ViewBag.Authorized && client.IsMasked)
|
||||||
<a asp-controller="Client" asp-action="Profile" asp-route-id="@client.ClientId">
|
{
|
||||||
<color-code value="@client.Name"></color-code>
|
continue;
|
||||||
</a>
|
}
|
||||||
</td>
|
<tr class="bg-dark-dm bg-light-lm">
|
||||||
<td class="text-right">
|
<td>
|
||||||
@client.LastConnection.HumanizeForCurrentCulture()
|
@if (client.IsMasked)
|
||||||
</td>
|
{
|
||||||
</tr>
|
<span data-toggle="tooltip" data-title="Client is masked">
|
||||||
}
|
<span class="oi oi-shield mr-5 font-size-12"></span>
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
|
||||||
|
<a asp-controller="Client" asp-action="Profile" asp-route-id="@client.ClientId">
|
||||||
|
<color-code value="@client.Name"></color-code>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td class="text-right">
|
||||||
|
@client.LastConnection.HumanizeForCurrentCulture()
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
</has-permission>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
}
|
}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -33,205 +33,217 @@
|
|||||||
|
|
||||||
<div class="content row mt-20">
|
<div class="content row mt-20">
|
||||||
<div class="col-12 col-lg-9 col-xl-10">
|
<div class="col-12 col-lg-9 col-xl-10">
|
||||||
@if (Model.ActivePenalty != null)
|
@if (Model.ActivePenalty != null)
|
||||||
{
|
{
|
||||||
<has-permission entity="ClientLevel" required-permission="Read">
|
<has-permission entity="ClientLevel" required-permission="Read">
|
||||||
<div class="alert @ClassForPenaltyType(Model.ActivePenalty.Type) mt-10 mb-10" role="alert">
|
<div class="alert @ClassForPenaltyType(Model.ActivePenalty.Type) mt-10 mb-10" role="alert">
|
||||||
@foreach (var result in Utilities.SplitTranslationTokens(translationKey))
|
@foreach (var result in Utilities.SplitTranslationTokens(translationKey))
|
||||||
|
{
|
||||||
|
switch (result.MatchValue)
|
||||||
{
|
{
|
||||||
switch (result.MatchValue)
|
case "reason":
|
||||||
{
|
<span class="text-light-dm font-weight-lighter">@(ViewBag.Authorized ? !string.IsNullOrEmpty(Model.ActivePenalty.AutomatedOffense) && Model.ActivePenalty.Type != EFPenalty.PenaltyType.Warning ? Utilities.FormatExt(ViewBag.Localization["WEBFRONT_PROFILE_ANTICHEAT_DETECTION"], Model.ActivePenalty.AutomatedOffense) : Model.ActivePenalty.Offense.StripColors() : Model.ActivePenalty.Offense.StripColors())</span>
|
||||||
case "reason":
|
break;
|
||||||
<span class="text-light-dm font-weight-lighter">@(ViewBag.Authorized ? !string.IsNullOrEmpty(Model.ActivePenalty.AutomatedOffense) && Model.ActivePenalty.Type != EFPenalty.PenaltyType.Warning ? Utilities.FormatExt(ViewBag.Localization["WEBFRONT_PROFILE_ANTICHEAT_DETECTION"], Model.ActivePenalty.AutomatedOffense) : Model.ActivePenalty.Offense.StripColors() : Model.ActivePenalty.Offense.StripColors())</span>
|
case "time":
|
||||||
break;
|
<span class="text-light-dm font-weight-lighter">
|
||||||
case "time":
|
@((Model.ActivePenalty.Expires.Value - DateTime.UtcNow).HumanizeForCurrentCulture())
|
||||||
<span class="text-light-dm font-weight-lighter">
|
</span>
|
||||||
@((Model.ActivePenalty.Expires.Value - DateTime.UtcNow).HumanizeForCurrentCulture())
|
break;
|
||||||
</span>
|
default:
|
||||||
break;
|
<span>@result.MatchValue</span>
|
||||||
default:
|
break;
|
||||||
<span>@result.MatchValue</span>
|
}
|
||||||
break;
|
}
|
||||||
}
|
</div>
|
||||||
|
</has-permission>
|
||||||
|
}
|
||||||
|
|
||||||
|
<h2 class="content-title mb-10">Player Profile</h2>
|
||||||
|
|
||||||
|
<div id="profile_wrapper" class="mb-10 mt-10">
|
||||||
|
|
||||||
|
<!-- online status indicator -->
|
||||||
|
@if (Model.Online)
|
||||||
|
{
|
||||||
|
<div class="bg-success rounded-circle position-absolute status-indicator z-20 mt-10 ml-10" data-toggle="tooltip" data-placement="bottom" data-title="Client is online"></div>
|
||||||
|
<div class="bg-success rounded-circle position-absolute status-indicator with-ripple z-10 mt-10 ml-10"></div>
|
||||||
|
}
|
||||||
|
|
||||||
|
<!-- main profile row -->
|
||||||
|
<div class="card p-15 ml-0 mr-0 mt-0 mb-10 d-flex flex-fill flex-wrap flex-column flex-md-row justify-content-center">
|
||||||
|
<div class="pl-5 pr-5 d-flex flex-column flex-md-row">
|
||||||
|
<div id="profile_avatar" class="w-150 w-md-100 h-150 h-md-100 mt-5 mb-5 d-flex justify-content-center align-self-center rounded @ClassForProfileBackground() @(isTempBanned ? "penalties-bgcolor-tempban" : "")" style="background-image:url('@($"https://gravatar.com/avatar/{gravatarUrl}?size=168&default=blank&rating=pg")">
|
||||||
|
@if (string.IsNullOrEmpty(gravatarUrl))
|
||||||
|
{
|
||||||
|
<div class="profile-shortcode align-self-center text-dark-lm">@shortCode</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</has-permission>
|
<div class="d-flex flex-column align-self-center ml-20 mr-20 mt-10 mb-10 mt-md-0 mb-md-0 text-center text-md-left">
|
||||||
}
|
<!-- name -->
|
||||||
|
<div id="profile_name">
|
||||||
|
<span class="font-size-20 font-weight-medium">
|
||||||
|
<color-code value="@Model.Name"></color-code>
|
||||||
|
</span>
|
||||||
|
<has-permission entity="MetaAliasUpdate" required-permission="Read">
|
||||||
|
<div class="dropdown with-arrow">
|
||||||
|
<div data-toggle="dropdown" id="profileAliasHistory" aria-haspopup="true" aria-expanded="false">
|
||||||
|
@if (Model.Aliases.Any())
|
||||||
|
{
|
||||||
|
<i class="oi oi-caret-bottom font-size-12" aria-hidden="true"></i>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
<h2 class="content-title mb-10">Player Profile</h2>
|
<div class="dropdown-menu dropdown-menu-center @(Model.Aliases.Where(alias => !alias.Item1.Contains(" ")).Max(alias => (int?)alias.Item1.Length) >= 15 ? "w-250" : "")" aria-labelledby="profileAliasHistory">
|
||||||
|
@foreach (var (alias, dateAdded) in Model.Aliases.OrderByDescending(alias => alias.Item2).Take(15))
|
||||||
<div id="profile_wrapper" class="mb-10 mt-10">
|
{
|
||||||
|
<a asp-controller="Client" asp-action="Find" asp-route-clientName="@alias.StripColors()" class="dropdown-item" data-toggle="tooltip" data-title="@dateAdded.HumanizeForCurrentCulture()">
|
||||||
<!-- online status indicator -->
|
<i class="oi oi-magnifying-glass text-muted mr-5"></i>
|
||||||
@if (Model.Online)
|
<color-code value="@alias"></color-code>
|
||||||
{
|
</a>
|
||||||
<div class="bg-success rounded-circle position-absolute status-indicator z-20 mt-10 ml-10" data-toggle="tooltip" data-placement="bottom" data-title="Client is online"></div>
|
}
|
||||||
<div class="bg-success rounded-circle position-absolute status-indicator with-ripple z-10 mt-10 ml-10"></div>
|
@if (Model.Aliases.Count > 15)
|
||||||
}
|
{
|
||||||
|
<div class="dropdown-divider"></div>
|
||||||
<!-- main profile row -->
|
<span class="dropdown-item bg-dark-dm bg-light-lm">...and @(Model.Aliases.Count - 15) more</span>
|
||||||
<div class="card p-20 ml-0 mr-0 mt-0 mb-10 d-flex flex-fill flex-wrap flex-column flex-md-row justify-content-center">
|
}
|
||||||
<div class="d-flex flex-column flex-md-row">
|
</div>
|
||||||
<div id="profile_avatar" class="w-150 w-md-100 h-150 h-md-100 mt-10 mb-10 d-flex justify-content-center align-self-center rounded @ClassForProfileBackground() @(isTempBanned ? "penalties-bgcolor-tempban" : "")" style="background-image:url('@($"https://gravatar.com/avatar/{gravatarUrl}?size=168&default=blank&rating=pg")">
|
</div>
|
||||||
@if (string.IsNullOrEmpty(gravatarUrl))
|
</has-permission>
|
||||||
{
|
|
||||||
<div class="profile-shortcode align-self-center text-dark-lm">@shortCode</div>
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex flex-column align-self-center ml-20 mr-20 mt-10 mb-10 mt-md-0 mb-md-0 text-center text-md-left">
|
<!-- permission level -->
|
||||||
<!-- name -->
|
<has-permission entity="ClientLevel" required-permission="Read">
|
||||||
<div id="profile_name">
|
<div class="align-self-center align-self-md-start font-weight-bold font-size-16 level-color-@Model.LevelInt">
|
||||||
<span class="font-size-20 font-weight-medium">
|
<div class="d-flex flex-row">
|
||||||
<color-code value="@Model.Name"></color-code>
|
<span>@Model.Level</span>
|
||||||
</span>
|
</div>
|
||||||
<has-permission entity="MetaAliasUpdate" required-permission="Read">
|
|
||||||
<div class="dropdown dropright with-arrow">
|
|
||||||
<div data-toggle="dropdown" id="profileAliasHistory" aria-haspopup="true" aria-expanded="false">
|
|
||||||
@if (Model.Aliases.Any())
|
|
||||||
{
|
|
||||||
<i class="oi oi-caret-bottom font-size-12" aria-hidden="true"></i>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="dropdown-menu @(Model.Aliases.Where(alias => !alias.Item1.Contains(" ")).Max(alias => (int?)alias.Item1.Length) >= 15 ? "w-250" : "") " aria-labelledby="profileAliasHistory">
|
|
||||||
@foreach (var (alias, dateAdded) in Model.Aliases.OrderByDescending(alias => alias.Item2).Take(15))
|
|
||||||
{
|
|
||||||
<a asp-controller="Client" asp-action="Find" asp-route-clientName="@alias" class="dropdown-item" data-toggle="tooltip" data-title="@dateAdded.HumanizeForCurrentCulture()">
|
|
||||||
<i class="oi oi-magnifying-glass text-muted mr-5"></i>
|
|
||||||
<color-code value="@alias"></color-code>
|
|
||||||
</a>
|
|
||||||
}
|
|
||||||
@if (Model.Aliases.Count > 15)
|
|
||||||
{
|
|
||||||
<div class="dropdown-divider"></div>
|
|
||||||
<span class="dropdown-item bg-dark-dm bg-light-lm">...and @(Model.Aliases.Count - 15) more</span>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</has-permission>
|
|
||||||
</div>
|
</div>
|
||||||
<!-- permission level -->
|
</has-permission>
|
||||||
<has-permission entity="ClientLevel" required-permission="Read">
|
|
||||||
<div class="align-self-center align-self-md-start font-weight-bold font-size-16 level-color-@Model.LevelInt">
|
<!-- guid -->
|
||||||
<div class="d-flex flex-row">
|
<has-permission entity="ClientGuid" required-permission="Read">
|
||||||
<span>@Model.Level</span>
|
<div class="dropdown dropup with-arrow">
|
||||||
|
<div class="text-muted" data-toggle="dropdown" id="altGuidFormatsDropdown" aria-haspopup="true" aria-expanded="false">@Model.NetworkId.ToString("X")</div>
|
||||||
|
<div class="dropdown-menu" aria-labelledby="altGuidFormatsDropdown">
|
||||||
|
<div class="p-10 font-size-12">
|
||||||
|
<div class="">Alternative Formats</div>
|
||||||
|
<div class="dropdown-divider mt-5 mb-5"></div>
|
||||||
|
<div class="text-muted font-weight-lighter">@((ulong)Model.NetworkId)</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</has-permission>
|
</div>
|
||||||
|
</has-permission>
|
||||||
|
|
||||||
<!-- guid -->
|
<!-- ip address -->
|
||||||
<has-permission entity="ClientGuid" required-permission="Read">
|
<div class="align-self-center align-self-md-start d-flex flex-row">
|
||||||
<div class="dropdown dropup with-arrow">
|
<span class="text-muted mr-5">@Model.IPAddress</span>
|
||||||
<div class="text-muted" data-toggle="dropdown" id="altGuidFormatsDropdown" aria-haspopup="true" aria-expanded="false">@Model.NetworkId.ToString("X")</div>
|
<has-permission entity="MetaAliasUpdate" required-permission="Read">
|
||||||
<div class="dropdown-menu" aria-labelledby="altGuidFormatsDropdown">
|
<div class="dropdown with-arrow">
|
||||||
<div class="p-10 font-size-12">
|
<div data-toggle="dropdown" id="profileIPAddressHistory" aria-haspopup="true" aria-expanded="false">
|
||||||
<div class="">Alternative Formats</div>
|
@if (Model.IPs.Any(ip => !string.IsNullOrEmpty(ip.Item1)))
|
||||||
<div class="dropdown-divider mt-5 mb-5"></div>
|
{
|
||||||
<div class="text-muted font-weight-lighter">@((ulong)Model.NetworkId)</div>
|
<i class="oi oi-caret-bottom font-size-12 text-muted" aria-hidden="true"></i>
|
||||||
</div>
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="dropdown-menu dropdown-menu-center" aria-labelledby="profileAliasHistory">
|
||||||
</has-permission>
|
@foreach (var (ip, dateAdded) in Model.IPs.OrderByDescending(ip => ip.Item2).Take(15))
|
||||||
|
{
|
||||||
<!-- ip address -->
|
if (string.IsNullOrEmpty(ip))
|
||||||
<div class="align-self-center align-self-md-start d-flex flex-row">
|
|
||||||
<span class="text-muted mr-5">@Model.IPAddress</span>
|
|
||||||
<has-permission entity="MetaAliasUpdate" required-permission="Read">
|
|
||||||
<div class="dropdown dropright with-arrow">
|
|
||||||
<div data-toggle="dropdown" id="profileIPAddressHistory" aria-haspopup="true" aria-expanded="false">
|
|
||||||
@if (Model.IPs.Any(ip => !string.IsNullOrEmpty(ip.Item1)))
|
|
||||||
{
|
{
|
||||||
<i class="oi oi-caret-bottom font-size-12" aria-hidden="true"></i>
|
continue;
|
||||||
}
|
}
|
||||||
</div>
|
<div class="d-flex dropdown-item" data-toggle="tooltip" data-title="@dateAdded.HumanizeForCurrentCulture()">
|
||||||
<div class="dropdown-menu" aria-labelledby="profileAliasHistory">
|
<a asp-controller="Client" asp-action="Find" asp-route-clientName="@ip">
|
||||||
@foreach (var (ip, dateAdded) in Model.IPs.OrderByDescending(ip => ip.Item2).Take(15))
|
|
||||||
{
|
|
||||||
<a asp-controller="Client" asp-action="Find" asp-route-clientName="@ip" class="dropdown-item" data-toggle="tooltip" data-title="@dateAdded.HumanizeForCurrentCulture()">
|
|
||||||
<i class="oi oi-magnifying-glass text-muted mr-5"></i>
|
<i class="oi oi-magnifying-glass text-muted mr-5"></i>
|
||||||
|
</a>
|
||||||
|
<a href="#contextModal" class="profile-ip-lookup dropdown-item p-0 m-0" data-ip="@ip">
|
||||||
<color-code value="@ip"></color-code>
|
<color-code value="@ip"></color-code>
|
||||||
</a>
|
</a>
|
||||||
}
|
</div>
|
||||||
@if (Model.IPs.Count > 15)
|
}
|
||||||
{
|
@if (Model.IPs.Count > 15)
|
||||||
<div class="dropdown-divider"></div>
|
{
|
||||||
<span class="dropdown-item bg-dark-dm bg-light-lm">...and @(Model.IPs.Count - 15) more</span>
|
<div class="dropdown-divider"></div>
|
||||||
}
|
<span class="dropdown-item bg-dark-dm bg-light-lm">...and @(Model.IPs.Count - 15) more</span>
|
||||||
</div>
|
}
|
||||||
</div>
|
</div>
|
||||||
</has-permission>
|
</div>
|
||||||
</div>
|
</has-permission>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex-fill d-flex justify-content-center justify-content-md-end mt-10 mt-md-0">
|
|
||||||
<!-- country flag -->
|
|
||||||
<div id="ipGeoDropdown" class="dropdown dropleft with-arrow align-self-center">
|
|
||||||
<a href="#" data-toggle="dropdown" id="avatar-popover-toggle" aria-haspopup="true" aria-expanded="false">
|
|
||||||
@if (!string.IsNullOrEmpty(Model.GeoLocationInfo.CountryCode))
|
|
||||||
{
|
|
||||||
<div class="ip-lookup-profile w-100 rounded align-self-center" style="height:5rem;background-image: url('https://flagcdn.com/w80/@(Model.GeoLocationInfo.CountryCode.ToLower()).png')" data-ip="@Model.IPAddress"></div>
|
|
||||||
}
|
|
||||||
</a>
|
|
||||||
<div class="dropdown-menu dropdown-menu-center z-30" aria-labelledby="avatar-popover-toggle">
|
|
||||||
<has-permission entity="ClientIPAddress" required-permission="Read">
|
|
||||||
<h6 class="dropdown-header font-weight-bold">@Model.IPAddress</h6>
|
|
||||||
<div class="dropdown-divider"></div>
|
|
||||||
</has-permission>
|
|
||||||
|
|
||||||
<div class="dropdown-item geo-country">@Model.GeoLocationInfo.Country</div>
|
|
||||||
@if (!string.IsNullOrEmpty(Model.GeoLocationInfo.Region))
|
|
||||||
{
|
|
||||||
<div class="dropdown-item text-muted geo-region">@Model.GeoLocationInfo.Region</div>
|
|
||||||
<div class="dropdown-divider"></div>
|
|
||||||
}
|
|
||||||
@if (!string.IsNullOrEmpty(Model.GeoLocationInfo.Organization))
|
|
||||||
{
|
|
||||||
<div class="dropdown-item geo-organization">@Model.GeoLocationInfo.Organization</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="flex-fill d-flex justify-content-center justify-content-md-end mt-10 mt-md-0">
|
||||||
|
<!-- country flag -->
|
||||||
|
<div id="ipGeoDropdown" class="dropdown with-arrow align-self-center">
|
||||||
|
<a href="#" data-toggle="dropdown" id="avatar-popover-toggle" aria-haspopup="true" aria-expanded="false">
|
||||||
|
@if (!string.IsNullOrEmpty(Model.GeoLocationInfo.CountryCode))
|
||||||
|
{
|
||||||
|
<div class="profile-country-flag w-100 rounded align-self-center" style="background-image: url('https://flagcdn.com/w160/@(Model.GeoLocationInfo.CountryCode.ToLower()).png')" data-ip="@Model.IPAddress"></div>
|
||||||
|
}
|
||||||
|
</a>
|
||||||
|
<div class="dropdown-menu dropdown-menu-center z-30" aria-labelledby="avatar-popover-toggle">
|
||||||
|
<has-permission entity="ClientIPAddress" required-permission="Read">
|
||||||
|
<h6 class="dropdown-header font-weight-bold">@Model.IPAddress</h6>
|
||||||
|
<div class="dropdown-divider"></div>
|
||||||
|
</has-permission>
|
||||||
|
|
||||||
|
<div class="dropdown-item geo-country">@Model.GeoLocationInfo.Country</div>
|
||||||
|
@if (!string.IsNullOrEmpty(Model.GeoLocationInfo.Region))
|
||||||
|
{
|
||||||
|
<div class="dropdown-item text-muted geo-region">@Model.GeoLocationInfo.Region</div>
|
||||||
|
<div class="dropdown-divider"></div>
|
||||||
|
}
|
||||||
|
@if (!string.IsNullOrEmpty(Model.GeoLocationInfo.Organization))
|
||||||
|
{
|
||||||
|
<div class="dropdown-item geo-organization">@Model.GeoLocationInfo.Organization</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<hr class="mr-5 ml-5"/>
|
||||||
|
<!-- meta info block -->
|
||||||
|
<div class="d-flex flex-column flex-md-row text-center text-md-left flex-wrap">
|
||||||
|
<partial name="Meta/_Information.cshtml" model="@Model.Meta"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- meta info block -->
|
|
||||||
<div class="d-flex flex-column flex-md-row text-center text-md-left flex-wrap">
|
|
||||||
<partial name="Meta/_Information.cshtml" model="@Model.Meta"/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<hr class="mt-10 mb-10"/>
|
<hr class="mt-10 mb-10"/>
|
||||||
|
|
||||||
<!-- meta filter list -->
|
<!-- meta filter list -->
|
||||||
<div class="mb-10 mt-10">
|
<div class="mb-10 mt-15">
|
||||||
@foreach (var type in Enum.GetValues(typeof(MetaType)).Cast<MetaType>().Where(meta => !ignoredMetaTypes.Contains(meta)).OrderByDescending(meta => meta == MetaType.All))
|
@foreach (var type in Enum.GetValues(typeof(MetaType)).Cast<MetaType>().Where(meta => !ignoredMetaTypes.Contains(meta)).OrderByDescending(meta => meta == MetaType.All))
|
||||||
{
|
|
||||||
var buttonClass = !Model.MetaFilterType.HasValue && type == MetaType.All || Model.MetaFilterType.HasValue && Model.MetaFilterType.Value.ToString() == type.ToString() ? "btn-primary text-light" : "text-muted";
|
|
||||||
<a asp-action="Profile" asp-controller="Client"
|
|
||||||
class="meta-filter no-decoration"
|
|
||||||
asp-route-id="@Model.ClientId"
|
|
||||||
asp-route-metaFilterType="@type"
|
|
||||||
data-meta-type="@type">
|
|
||||||
<button class="btn btn-sm d-none d-md-inline mt-5 mb-5 @buttonClass">@type.ToTranslatedName()</button>
|
|
||||||
<button class="btn btn-block d-block d-md-none mt-10 mb-10 @buttonClass">@type.ToTranslatedName()</button>
|
|
||||||
</a>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@if (!ViewBag.Authorized && !ViewBag.EnablePrivilegedUserPrivacy || ViewBag.Authorized)
|
|
||||||
{
|
{
|
||||||
<div class="row d-md-flex pt-2">
|
var buttonClass = !Model.MetaFilterType.HasValue && type == MetaType.All || Model.MetaFilterType.HasValue && Model.MetaFilterType.Value.ToString() == type.ToString() ? "btn-primary text-light" : "text-muted";
|
||||||
<div id="profile_events" class="text-muted text-left pl-md-0 pr-md-0">
|
<a asp-action="Profile" asp-controller="Client"
|
||||||
@await Component.InvokeAsync("ProfileMetaList", new { clientId = Model.ClientId, count = 30, offset = 0, startAt = DateTime.UtcNow, metaType = Model.MetaFilterType })
|
class="meta-filter no-decoration"
|
||||||
</div>
|
asp-route-id="@Model.ClientId"
|
||||||
</div>
|
asp-route-metaFilterType="@type"
|
||||||
|
data-meta-type="@type">
|
||||||
|
<button class="btn btn-sm d-none d-md-inline mt-5 mb-5 @buttonClass">@type.ToTranslatedName()</button>
|
||||||
|
<button class="btn btn-block d-block d-md-none mt-10 mb-10 @buttonClass">@type.ToTranslatedName()</button>
|
||||||
|
</a>
|
||||||
}
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
<hr class="mt-10 mb-10"/>
|
@if (!ViewBag.Authorized && !ViewBag.EnablePrivilegedUserPrivacy || ViewBag.Authorized)
|
||||||
|
{
|
||||||
<div class="text-center">
|
<div class="row d-md-flex pt-2">
|
||||||
<i id="loaderLoad" class="oi oi-chevron-bottom loader-load-more text-primary" aria-hidden="true"></i>
|
<div id="profile_events" class="text-muted text-left pl-md-0 pr-md-0">
|
||||||
|
@await Component.InvokeAsync("ProfileMetaList", new { clientId = Model.ClientId, count = 30, offset = 0, startAt = DateTime.UtcNow, metaType = Model.MetaFilterType })
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
<hr class="mt-10 mb-10"/>
|
||||||
|
|
||||||
|
<div class="text-center">
|
||||||
|
<i id="loaderLoad" class="oi oi-chevron-bottom loader-load-more text-primary" aria-hidden="true"></i>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
@model IEnumerable<SharedLibraryCore.Dtos.Meta.Responses.InformationResponse>
|
@using Humanizer
|
||||||
|
@model IEnumerable<SharedLibraryCore.Dtos.Meta.Responses.InformationResponse>
|
||||||
@{
|
@{
|
||||||
var informationMeta = Model
|
var informationMeta = Model
|
||||||
.Where(meta => meta.Type == SharedLibraryCore.Interfaces.MetaType.Information)
|
.Where(meta => meta.Type == SharedLibraryCore.Interfaces.MetaType.Information)
|
||||||
@ -7,12 +8,13 @@
|
|||||||
.GroupBy(meta => meta.index / 5);
|
.GroupBy(meta => meta.index / 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<div class="d-flex flex-wrap">
|
||||||
@foreach (var metaColumn in informationMeta)
|
@foreach (var metaColumn in informationMeta)
|
||||||
{
|
{
|
||||||
<div class="mr-20">
|
<!-- <div class="mr-20"> -->
|
||||||
@foreach (var meta in metaColumn)
|
@foreach (var meta in metaColumn)
|
||||||
{
|
{
|
||||||
<div class="profile-meta-entry font-size-12" data-toggle="@(!string.IsNullOrEmpty(meta.meta.ToolTipText) ? "tooltip" : "")" data-title="@meta.meta.ToolTipText" data-placement="bottom">
|
<div class="m-md-5 p-15 w-half rounded bg-very-dark-dm bg-light-ex-lm profile-meta-entry font-size-12 w-md-100 w-lg-150" data-toggle="@(!string.IsNullOrEmpty(meta.meta.ToolTipText) ? "tooltip" : "")" data-title="@meta.meta.ToolTipText" data-placement="bottom">
|
||||||
|
|
||||||
@{var results = Utilities.SplitTranslationTokens(meta.meta.Key);}
|
@{var results = Utilities.SplitTranslationTokens(meta.meta.Key);}
|
||||||
|
|
||||||
@ -22,22 +24,23 @@
|
|||||||
{
|
{
|
||||||
if (result.IsInterpolation)
|
if (result.IsInterpolation)
|
||||||
{
|
{
|
||||||
<span class="profile-meta-value text-primary"><color-code value="@meta.meta.Value"></color-code></span>
|
<div class="profile-meta-value text-primary font-size-14"><color-code value="@meta.meta.Value"></color-code></div>
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<span class="profile-meta-title text-muted">@result.MatchValue</span>
|
<span class="profile-meta-title text-muted font-size-12">@result.MatchValue.Titleize()</span>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<span class="profile-meta-value text-primary"><color-code value="@meta.meta.Value"></color-code></span>
|
<div class="profile-meta-value text-primary font-size-14"><color-code value="@meta.meta.Value"></color-code></div>
|
||||||
<span class="profile-meta-title text-muted">@meta.meta.Key</span>
|
<div class="profile-meta-title text-muted font-size-12">@meta.meta.Key.Titleize()</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
<!-- </div> -->
|
||||||
}
|
}
|
||||||
|
</div>
|
||||||
|
@ -32,7 +32,7 @@
|
|||||||
|
|
||||||
else if (match.MatchValue == "reason")
|
else if (match.MatchValue == "reason")
|
||||||
{
|
{
|
||||||
<span class="text-light-dm text-dark-lm">
|
<span class="text-white-dm text-black-lm">
|
||||||
@if (ViewBag.Authorized && !string.IsNullOrEmpty(Model.AutomatedOffense) && Model.PenaltyType != Data.Models.EFPenalty.PenaltyType.Warning && Model.PenaltyType != Data.Models.EFPenalty.PenaltyType.Kick)
|
@if (ViewBag.Authorized && !string.IsNullOrEmpty(Model.AutomatedOffense) && Model.PenaltyType != Data.Models.EFPenalty.PenaltyType.Warning && Model.PenaltyType != Data.Models.EFPenalty.PenaltyType.Kick)
|
||||||
{
|
{
|
||||||
<span>@Utilities.FormatExt(ViewBag.Localization["WEBFRONT_PROFILE_ANTICHEAT_DETECTION"], Model.AutomatedOffense)</span>
|
<span>@Utilities.FormatExt(ViewBag.Localization["WEBFRONT_PROFILE_ANTICHEAT_DETECTION"], Model.AutomatedOffense)</span>
|
||||||
|
@ -1,11 +1,16 @@
|
|||||||
@{
|
@{
|
||||||
Layout = null;
|
Layout = null;
|
||||||
|
|
||||||
|
bool CanSeeLevel(PenaltyInfo penalty) => (ViewBag.PermissionsSet as IEnumerable<string>).HasPermission(WebfrontEntity.ClientLevel, WebfrontPermission.Read) && penalty.PenaltyType == EFPenalty.PenaltyType.Report;
|
||||||
}
|
}
|
||||||
|
@using WebfrontCore.Permissions
|
||||||
|
@using SharedLibraryCore.Dtos
|
||||||
|
@using Data.Models
|
||||||
@model IList<SharedLibraryCore.Dtos.PenaltyInfo>
|
@model IList<SharedLibraryCore.Dtos.PenaltyInfo>
|
||||||
|
|
||||||
@{
|
@{
|
||||||
foreach (var penalty in Model)
|
foreach (var penalty in Model.Where(CanSeeLevel))
|
||||||
{
|
{
|
||||||
await Html.RenderPartialAsync("_Penalty", penalty);
|
await Html.RenderPartialAsync("_Penalty", penalty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,52 +1,33 @@
|
|||||||
@model IEnumerable<SharedLibraryCore.Dtos.PlayerInfo>
|
@model IEnumerable<SharedLibraryCore.Dtos.PlayerInfo>
|
||||||
@{
|
@{
|
||||||
Layout = null;
|
Layout = null;
|
||||||
var loc = SharedLibraryCore.Utilities.CurrentLocalization.LocalizationIndex;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<div class="mb-15 text-center font-weight-lighter">New clients connected in the last <span class="text-primary">24</span> hours</div>
|
||||||
@*<table class="table table-striped">
|
|
||||||
<thead>
|
|
||||||
<tr class="bg-primary pt-2 pb-2">
|
|
||||||
<th scope="col">@loc["WEBFRONT_PENALTY_TEMPLATE_NAME"]</th>
|
|
||||||
<th scope="col">@loc["WEBFRONT_CONFIGURATION_SERVER_IP"]</th>
|
|
||||||
<th scope="col">@loc["WEBFRONT_PROFILE_LOOKUP_LOCATION"]</th>
|
|
||||||
<th scope="col" class="text-right">@loc["WEBFRONT_SEARCH_LAST_CONNECTED"]</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
|
|
||||||
@foreach (var client in Model)
|
|
||||||
{
|
|
||||||
<tr>
|
|
||||||
<td class="w-25">
|
|
||||||
<a asp-controller="Client" asp-action="Profile" asp-route-id="@client.ClientId" class="link-inverse">@client.Name</a>
|
|
||||||
</td>
|
|
||||||
<td class="w-25">
|
|
||||||
@client.IPAddress
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div class="client-location-flag" data-ip="@client.IPAddress" />
|
|
||||||
</td>
|
|
||||||
<td class="text-right">
|
|
||||||
@client.LastConnectionText
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
}
|
|
||||||
</table>*@
|
|
||||||
|
|
||||||
@foreach (var client in Model)
|
@foreach (var client in Model)
|
||||||
{
|
{
|
||||||
<div class="p-2 mb-3 border-bottom" style="background-color: #222;">
|
<div class="bg-very-dark-dm bg-light-ex-lm p-15 rounded mb-10">
|
||||||
<div class="d-flex flex-row">
|
<div class="d-flex flex-row">
|
||||||
<a asp-controller="Client" asp-action="Profile" asp-route-id="@client.ClientId" class="h4 mr-auto">
|
<a asp-controller="Client" asp-action="Profile" asp-route-id="@client.ClientId" class="h4 mr-auto">
|
||||||
<color-code value="@client.Name"></color-code>
|
<color-code value="@client.Name"></color-code>
|
||||||
</a>
|
</a>
|
||||||
<div class="client-location-flag align-self-center" data-ip="@client.IPAddress"></div>
|
@if (client.GeoLocationInfo is not null)
|
||||||
|
{
|
||||||
|
@if (!string.IsNullOrEmpty(client.GeoLocationInfo.CountryCode))
|
||||||
|
{
|
||||||
|
<div data-toggle="tooltip" data-title="@client.GeoLocationInfo.Country">
|
||||||
|
<div class="rounded" style="width: 40px; height: 25.66px; background-repeat: no-repeat; background-position: center center; background-image: url('https://flagcdn.com/w40/@(client.GeoLocationInfo.CountryCode.ToLower()).png')"></div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex flex-row">
|
<div class="d-flex flex-row">
|
||||||
<div class="align-self-center mr-auto">@client.IPAddress</div>
|
<has-permission entity="ClientIPAddress" required-permission="Read">
|
||||||
<div class="align-self-center">@client.LastConnectionText</div>
|
<div class="align-self-center mr-auto">@client.IPAddress</div>
|
||||||
|
</has-permission>
|
||||||
|
<div class="align-self-center text-muted font-size-12">@client.LastConnection.HumanizeForCurrentCulture()</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
39
WebfrontCore/Views/Shared/Partials/_Reports.cshtml
Normal file
39
WebfrontCore/Views/Shared/Partials/_Reports.cshtml
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
@using Data.Models
|
||||||
|
@using SharedLibraryCore.Dtos.Meta.Responses
|
||||||
|
@model IEnumerable<SharedLibraryCore.Dtos.ServerInfo>
|
||||||
|
@{
|
||||||
|
Layout = null;
|
||||||
|
}
|
||||||
|
<div class="content-title">Recent Reports</div>
|
||||||
|
<div class="text-muted">Last 24 hours</div>
|
||||||
|
|
||||||
|
@foreach (var server in Model)
|
||||||
|
{
|
||||||
|
<div class="rounded bg-very-dark-dm bg-light-ex-lm mt-10 mb-10 p-10">
|
||||||
|
<h5 class="mt-0 text-truncate">
|
||||||
|
<color-code value="@server.Name"></color-code>
|
||||||
|
</h5>
|
||||||
|
@foreach (var report in server.Reports.OrderByDescending(report => report.ReportedOn))
|
||||||
|
{
|
||||||
|
var penalty = new ReceivedPenaltyResponse
|
||||||
|
{
|
||||||
|
OffenderName = report.Target.Name,
|
||||||
|
OffenderClientId = report.Target.ClientId,
|
||||||
|
PunisherName = report.Origin.Name,
|
||||||
|
PunisherClientId = report.Origin.ClientId,
|
||||||
|
Offense = report.Reason,
|
||||||
|
PenaltyType = EFPenalty.PenaltyType.Report,
|
||||||
|
ClientId = report.Target.ClientId
|
||||||
|
};
|
||||||
|
<div class="font-weight-bold">@report.ReportedOn.HumanizeForCurrentCulture()</div>
|
||||||
|
<div class="font-size-12">
|
||||||
|
<a asp-action="Profile" asp-controller="Client" asp-route-id="@report.Target.ClientId">
|
||||||
|
<color-code value="@report.Target.Name"></color-code>
|
||||||
|
</a>
|
||||||
|
<partial name="~/Views/Client/Profile/Meta/_ReceivedPenaltyResponse.cshtml" for="@penalty"/>
|
||||||
|
</div>
|
||||||
|
<hr/>
|
||||||
|
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
@ -23,30 +23,30 @@
|
|||||||
@if (ViewBag.Configuration.WebfrontPrimaryColor is not null)
|
@if (ViewBag.Configuration.WebfrontPrimaryColor is not null)
|
||||||
{
|
{
|
||||||
<style>
|
<style>
|
||||||
:root {
|
:root {
|
||||||
--blue-color: @ViewBag.Configuration.WebfrontPrimaryColor;
|
--blue-color: @ViewBag.Configuration.WebfrontPrimaryColor;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
}
|
}
|
||||||
@if (ViewBag.Configuration.WebfrontSecondaryColor is not null)
|
@if (ViewBag.Configuration.WebfrontSecondaryColor is not null)
|
||||||
{
|
{
|
||||||
<style>
|
<style>
|
||||||
:root {
|
:root {
|
||||||
--yellow-color: @ViewBag.Configuration.WebfrontSecondaryColor;
|
--yellow-color: @ViewBag.Configuration.WebfrontSecondaryColor;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
}
|
}
|
||||||
@await RenderSectionAsync("styles", false)
|
@await RenderSectionAsync("styles", false)
|
||||||
</head>
|
</head>
|
||||||
<body class="dark-mode with-custom-webkit-scrollbars with-custom-css-scrollbars" data-set-preferred-mode-onload="true">
|
<body class="dark-mode with-custom-webkit-scrollbars with-custom-css-scrollbars" data-set-preferred-mode-onload="true">
|
||||||
|
|
||||||
<!-- Action Modal -->
|
<!-- action modal -->
|
||||||
<div class="modal" id="actionModal" tabindex="-1" role="dialog">
|
<div class="modal" id="actionModal" tabindex="-1" role="dialog">
|
||||||
<div class="modal-dialog" role="document">
|
<div class="modal-dialog" role="document">
|
||||||
<div id="modalLoadingBar" class="progress-bar position-absolute flex-fill position-fixed z-30" style="display:none">
|
<div id="modalLoadingBar" class="modal-loading-bar progress-bar position-absolute flex-fill position-fixed z-30" style="display:none">
|
||||||
<div class="progress-bar-value"></div>
|
<div class="progress-bar-value"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-content">
|
<div class="modal-content p-10">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<a href="#" class="btn close" role="button" aria-label="Close">
|
<a href="#" class="btn close" role="button" aria-label="Close">
|
||||||
<span aria-hidden="true">×</span>
|
<span aria-hidden="true">×</span>
|
||||||
@ -59,6 +59,27 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- context modal -->
|
||||||
|
<div class="modal" id="contextModal" tabindex="-1" role="dialog">
|
||||||
|
<div class="modal-dialog" role="document">
|
||||||
|
<div class="modal-loading-bar progress-bar position-absolute flex-fill position-fixed z-30" style="display:none">
|
||||||
|
<div class="progress-bar-value"></div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-content p-10">
|
||||||
|
<a href="#" class="btn close" role="button" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</a>
|
||||||
|
<div id="contextModalContent">
|
||||||
|
<div class="modal-title"></div>
|
||||||
|
<hr/>
|
||||||
|
<div class="modal-body font-size-12"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="page-wrapper with-navbar with-sidebar" data-sidebar-type="overlayed-sm-and-down">
|
<div class="page-wrapper with-navbar with-sidebar" data-sidebar-type="overlayed-sm-and-down">
|
||||||
<!-- toast notifications -->
|
<!-- toast notifications -->
|
||||||
<div class="sticky-alerts"></div>
|
<div class="sticky-alerts"></div>
|
||||||
@ -88,47 +109,36 @@
|
|||||||
</has-permission>
|
</has-permission>
|
||||||
|
|
||||||
<has-permission entity="AdminMenu" required-permission="Read">
|
<has-permission entity="AdminMenu" required-permission="Read">
|
||||||
<div class="badge-group ml-10" role="group">
|
<a href="#actionModal" class="profile-action no-decoration" data-action="RecentReports" data-toggle="tooltip" data-title="View recent reports" data-placement="bottom">
|
||||||
<span class="badge badge-danger">@(ViewBag.ReportCount ?? "-")</span>
|
<div class="badge-group ml-10" role="group">
|
||||||
<span class="badge bg-dark-dm bg-light-lm">Reports</span>
|
<span class="badge badge-danger">@(ViewBag.ReportCount ?? "-")</span>
|
||||||
</div>
|
<span class="badge bg-dark-dm bg-light-lm">Reports</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
</has-permission>
|
</has-permission>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="d-flex d-lg-none ml-auto">
|
<div class="d-flex ml-auto">
|
||||||
<a href="#contextMenuModal">
|
<div class="btn btn-action mr-10 ml-10" onclick="halfmoon.toggleDarkMode()" data-toggle="tooltip" data-title="Toggle display mode" data-placement="bottom">
|
||||||
<button class="btn" type="button">
|
<i class="oi oi-moon"></i>
|
||||||
<i class="oi oi-ellipses"></i>
|
</div>
|
||||||
</button>
|
<div class="d-none d-md-block ">
|
||||||
</a>
|
|
||||||
|
<partial name="_SearchResourceForm"/>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex d-lg-none">
|
||||||
|
<a href="#contextMenuModal">
|
||||||
|
<button class="btn" type="button">
|
||||||
|
<i class="oi oi-ellipses"></i>
|
||||||
|
</button>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@*<div class="d-none d-md-flex ml-auto">
|
|
||||||
<div class="btn oi btn-square btn-action mr-10" data-glyph="moon" onclick="halfmoon.toggleDarkMode()" title="Toggle display mode"></div>
|
|
||||||
</div>*@
|
|
||||||
|
|
||||||
<div class="d-none d-lg-block ml-auto">
|
|
||||||
<partial name="_SearchResourceForm"/>
|
|
||||||
</div>
|
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<partial name="_LeftNavBar"/>
|
<partial name="_LeftNavBar"/>
|
||||||
|
|
||||||
<!-- Main Modal -->
|
|
||||||
<!--<div class="modal fade" id="mainModal" tabindex="-1" role="dialog" aria-labelledby="mainModalLabel" aria-hidden="true">
|
|
||||||
<div class="modal-dialog" role="document">
|
|
||||||
<div class="modal-content bg-dark">
|
|
||||||
<div class="modal-header">
|
|
||||||
<h5 class="modal-title" id="mainModalLabel"></h5>
|
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
|
||||||
<span aria-hidden="true" class="text-danger">×</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>-->
|
|
||||||
<!-- End Main Modal -->
|
<!-- End Main Modal -->
|
||||||
<div id="target_id">
|
<div id="target_id">
|
||||||
@await RenderSectionAsync("targetid", required: false)
|
@await RenderSectionAsync("targetid", required: false)
|
||||||
|
@ -23,10 +23,12 @@
|
|||||||
<span class="name">@ViewBag.Localization["WEBFRONT_NAV_ABOUT"]</span>
|
<span class="name">@ViewBag.Localization["WEBFRONT_NAV_ABOUT"]</span>
|
||||||
</a>
|
</a>
|
||||||
<!-- penalties -->
|
<!-- penalties -->
|
||||||
<a asp-controller="Penalty" asp-action="List" class="sidebar-link">
|
<has-permission entity="Penalty" required-permission="Read">
|
||||||
<i class="oi oi-lock-locked mr-5"></i>
|
<a asp-controller="Penalty" asp-action="List" class="sidebar-link">
|
||||||
<span class="name">@ViewBag.Localization["WEBFRONT_NAV_PENALTIES"]</span>
|
<i class="oi oi-lock-locked mr-5"></i>
|
||||||
</a>
|
<span class="name">@ViewBag.Localization["WEBFRONT_NAV_PENALTIES"]</span>
|
||||||
|
</a>
|
||||||
|
</has-permission>
|
||||||
<!-- privileged -->
|
<!-- privileged -->
|
||||||
<has-permission entity="PrivilegedClientsPage" required-permission="Read">
|
<has-permission entity="PrivilegedClientsPage" required-permission="Read">
|
||||||
<a asp-controller="Client" asp-action="Privileged" class="sidebar-link">
|
<a asp-controller="Client" asp-action="Privileged" class="sidebar-link">
|
||||||
@ -126,12 +128,12 @@
|
|||||||
<span class="name">@ViewBag.Localization["WEBFRONT_NAV_AUDIT_LOG"]</span>
|
<span class="name">@ViewBag.Localization["WEBFRONT_NAV_AUDIT_LOG"]</span>
|
||||||
</a>
|
</a>
|
||||||
</has-permission>
|
</has-permission>
|
||||||
@*<has-permission entity="RecentPlayersPage" required-permission="Read">
|
<has-permission entity="RecentPlayersPage" required-permission="Read">
|
||||||
<a class="sidebar-link profile-action" href="#actionModal" data-action="RecentClients" title="@ViewBag.Localization["WEBFRONT_ACTION_RECENT_CLIENTS"]">
|
<a class="sidebar-link profile-action" href="#actionModal" data-action="RecentClients" title="@ViewBag.Localization["WEBFRONT_ACTION_RECENT_CLIENTS"]">
|
||||||
<i class="oi oi-timer mr-5"></i>
|
<i class="oi oi-timer mr-5"></i>
|
||||||
<span class="name">@ViewBag.Localization["WEBFRONT_ACTION_RECENT_CLIENTS"]</span>
|
<span class="name">@ViewBag.Localization["WEBFRONT_ACTION_RECENT_CLIENTS"]</span>
|
||||||
</a>
|
</a>
|
||||||
</has-permission>*@
|
</has-permission>
|
||||||
<a class="sidebar-link profile-action" href="#actionModal" data-action="GenerateLoginToken" title="@ViewBag.Localization["WEBFRONT_ACTION_TOKEN"]">
|
<a class="sidebar-link profile-action" href="#actionModal" data-action="GenerateLoginToken" title="@ViewBag.Localization["WEBFRONT_ACTION_TOKEN"]">
|
||||||
<i class="oi oi-key mr-5"></i>
|
<i class="oi oi-key mr-5"></i>
|
||||||
<span class="name">@ViewBag.Localization["WEBFRONT_ACTION_TOKEN"]</span>
|
<span class="name">@ViewBag.Localization["WEBFRONT_ACTION_TOKEN"]</span>
|
||||||
|
@ -78,6 +78,6 @@
|
|||||||
</ProjectExtensions>
|
</ProjectExtensions>
|
||||||
|
|
||||||
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
|
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
|
||||||
<Exec Command="if $(ConfigurationName) == Debug ( 
powershell -Command wget https://raw.githubusercontent.com/iconic/open-iconic/master/font/css/open-iconic-bootstrap.scss -o $(ProjectDir)wwwroot\lib\open-iconic\font\css\open-iconic-bootstrap-override.scss
echo d | xcopy /f /y $(ProjectDir)wwwroot\lib\open-iconic\font\fonts $(ProjectDir)wwwroot\font\
powershell -Command "((Get-Content -path $(ProjectDir)wwwroot\lib\open-iconic\font\css\open-iconic-bootstrap-override.scss -Raw) -replace '../fonts/','/fonts/') | Set-Content -Path $(ProjectDir)wwwroot\lib\open-iconic\font\css\open-iconic-bootstrap-override.scss"
)" />
|
<Exec Command="if $(ConfigurationName) == Debug ( 
powershell -Command wget https://raw.githubusercontent.com/iconic/open-iconic/master/font/css/open-iconic-bootstrap.scss -o $(ProjectDir)wwwroot\lib\open-iconic\font\css\open-iconic-bootstrap-override.scss
echo d | xcopy /f /y $(ProjectDir)wwwroot\lib\open-iconic\font\fonts $(ProjectDir)wwwroot\font\
powershell -Command "((Get-Content -path $(ProjectDir)wwwroot\lib\open-iconic\font\css\open-iconic-bootstrap-override.scss -Raw) -replace '../fonts/','/font/') | Set-Content -Path $(ProjectDir)wwwroot\lib\open-iconic\font\css\open-iconic-bootstrap-override.scss"
)" />
|
||||||
</Target>
|
</Target>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
@import 'profile.scss';
|
@import 'profile.scss';
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--blue-color: #007ACC;
|
--blue-color: #117ac0;
|
||||||
|
|
||||||
--yellow-color: #fe7e4c;
|
--yellow-color: #fe7e4c;
|
||||||
--yellow-color-dark: #fe7e4c88;
|
--yellow-color-dark: #fe7e4c88;
|
||||||
@ -20,6 +20,8 @@
|
|||||||
--lm-card-bg-color: var(--gray-color-light);
|
--lm-card-bg-color: var(--gray-color-light);
|
||||||
--gray-color-light: white;
|
--gray-color-light: white;
|
||||||
--card-border-width: 0;
|
--card-border-width: 0;
|
||||||
|
|
||||||
|
--dm-modal-overlay-bg-color: rgba(0, 0, 0, 0.8);
|
||||||
}
|
}
|
||||||
|
|
||||||
.server-history-row {
|
.server-history-row {
|
||||||
|
@ -5,11 +5,15 @@
|
|||||||
.level-color-user, .level-color-guest, .level-color-0 {
|
.level-color-user, .level-color-guest, .level-color-0 {
|
||||||
}
|
}
|
||||||
|
|
||||||
.level-bgcolor-user, .level-bgcolor-guest, .level-bgcolor-0 {
|
.dark-mode .level-bgcolor-user, .dark-mode .level-bgcolor-guest, .dark-mode .level-bgcolor-0 {
|
||||||
background-color: #6c757d !important;
|
background-color: #6c757d !important;
|
||||||
background-color: rgba(255, 255, 255, 0.68) !important;
|
background-color: rgba(255, 255, 255, 0.68) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.level-bgcolor-user, .level-bgcolor-guest, .level-bgcolor-0 {
|
||||||
|
background-color: var(--lm-base-body-bg-color) !important;
|
||||||
|
}
|
||||||
|
|
||||||
.level-color-trusted, .level-color-2 {
|
.level-color-trusted, .level-color-2 {
|
||||||
color: #749363 !important;
|
color: #749363 !important;
|
||||||
color: rgba(116,147,99,1) !important;
|
color: rgba(116,147,99,1) !important;
|
||||||
@ -188,10 +192,11 @@
|
|||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ip-lookup-profile {
|
.profile-country-flag {
|
||||||
background-size: contain;
|
background-size: contain;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
|
height:5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-indicator {
|
.status-indicator {
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
function hideLoader() {
|
function hideLoader() {
|
||||||
$('#mainLoadingBar').fadeOut();
|
$('#mainLoadingBar').fadeOut();
|
||||||
$('#modalLoadingBar').fadeOut();
|
$('.modal-loading-bar').fadeOut();
|
||||||
}
|
}
|
||||||
|
|
||||||
function showLoader() {
|
function showLoader() {
|
||||||
$('#mainLoadingBar').fadeIn();
|
$('#mainLoadingBar').fadeIn();
|
||||||
$('#modalLoadingBar').fadeIn();
|
$('.modal-loading-bar').fadeIn();
|
||||||
}
|
}
|
||||||
|
|
||||||
function errorLoader() {
|
function errorLoader() {
|
||||||
@ -151,8 +151,13 @@ $(document).ready(function () {
|
|||||||
|
|
||||||
catch{}
|
catch{}
|
||||||
|
|
||||||
|
if (message instanceof Array)
|
||||||
|
{
|
||||||
|
message = message.join("<br/>");
|
||||||
|
}
|
||||||
|
|
||||||
halfmoon.initStickyAlert({
|
halfmoon.initStickyAlert({
|
||||||
content: message.join("<br/>"),
|
content: message,
|
||||||
title: 'Error',
|
title: 'Error',
|
||||||
alertType: 'alert-danger',
|
alertType: 'alert-danger',
|
||||||
fillType: 'filled'
|
fillType: 'filled'
|
||||||
|
@ -55,36 +55,35 @@
|
|||||||
/*
|
/*
|
||||||
get ip geolocation info into modal
|
get ip geolocation info into modal
|
||||||
*/
|
*/
|
||||||
$('.ip-locate-link').click(function (e) {
|
$('.profile-ip-lookup').click(function (e) {
|
||||||
e.preventDefault();
|
|
||||||
const ip = $(this).data("ip");
|
const ip = $(this).data("ip");
|
||||||
$.getJSON(`https://ipwhois.app/json/${ip}`)
|
$.getJSON(`https://ipwhois.app/json/${ip}`)
|
||||||
.done(function (response) {
|
.done(function (response) {
|
||||||
$('#mainModal .modal-title').text(ip);
|
$('#contextModal .modal-title').text(ip);
|
||||||
$('#mainModal .modal-body').text('');
|
const modalBody = $('#contextModal .modal-body');
|
||||||
|
modalBody.text('');
|
||||||
if (response.isp.length > 0) {
|
if (response.isp.length > 0) {
|
||||||
$('#mainModal .modal-body').append(`${_localization['WEBFRONT_PROFILE_LOOKUP_ISP']} — ${response.isp}<br/>`);
|
modalBody.append(`${_localization['WEBFRONT_PROFILE_LOOKUP_ISP']} — <span class="text-muted">${response.isp}</span><br/>`);
|
||||||
}
|
}
|
||||||
if (response.org.length > 0) {
|
if (response.org.length > 0) {
|
||||||
$('#mainModal .modal-body').append(`${_localization['WEBFRONT_PROFILE_LOOKUP_ORG']} — ${response.org}<br/>`);
|
modalBody.append(`${_localization['WEBFRONT_PROFILE_LOOKUP_ORG']} — <span class="text-muted">${response.org}</span><br/>`);
|
||||||
}
|
}
|
||||||
if (response.region.length > 0 || response.city.length > 0 || response.country.length > 0 || response.timezone_gmt.length > 0) {
|
if (response.region.length > 0 || response.city.length > 0 || response.country.length > 0 || response.timezone_gmt.length > 0) {
|
||||||
$('#mainModal .modal-body').append(`${_localization['WEBFRONT_PROFILE_LOOKUP_LOCATION']} — `);
|
modalBody.append(`${_localization['WEBFRONT_PROFILE_LOOKUP_LOCATION']} —`);
|
||||||
}
|
}
|
||||||
if (response.city.length > 0) {
|
if (response.city.length > 0) {
|
||||||
$('#mainModal .modal-body').append(response.city);
|
modalBody.append(`<span class="text-muted">${response.city}</span>`);
|
||||||
}
|
}
|
||||||
if (response.region.length > 0) {
|
if (response.region.length > 0) {
|
||||||
$('#mainModal .modal-body').append((response.region.length > 0 ? ', ' : '') + response.region);
|
modalBody.append(`<span class="text-muted">${(response.region.length > 0 ? ', ' : '') + response.region}</span>`);
|
||||||
}
|
}
|
||||||
if (response.country.length > 0) {
|
if (response.country.length > 0) {
|
||||||
$('#mainModal .modal-body').append((response.country.length > 0 ? ', ' : '') + response.country);
|
modalBody.append(`<span class="text-muted">${(response.country.length > 0 ? ', ' : '') + response.country}</span>`);
|
||||||
}
|
}
|
||||||
if (response.timezone_gmt.length > 0) {
|
if (response.timezone_gmt.length > 0) {
|
||||||
$('#mainModal .modal-body').append((response.timezone_gmt.length > 0 ? ', ' : '') + response.timezone_gmt);
|
modalBody.append(`<br/>Timezone — <span class="text-muted">UTC${response.timezone_gmt}</span>`);
|
||||||
}
|
}
|
||||||
|
modalBody.append('</span>');
|
||||||
$('#mainModal').modal();
|
|
||||||
})
|
})
|
||||||
.fail(function (jqxhr, textStatus, error) {
|
.fail(function (jqxhr, textStatus, error) {
|
||||||
$('#mainModal .modal-title').text("Error");
|
$('#mainModal .modal-title').text("Error");
|
||||||
|
Loading…
Reference in New Issue
Block a user