QOL updates for profile meta
implement filterable meta for issue #158 update translations and use humanizer lib with datetime/timespan for issue #80
This commit is contained in:
parent
1ef2ba5344
commit
778e339a61
@ -25,20 +25,20 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Jint" Version="3.0.0-beta-1632" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.1.3">
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.1.7">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.3" />
|
||||
<PackageReference Include="RestEase" Version="1.4.10" />
|
||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="4.7.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.7" />
|
||||
<PackageReference Include="RestEase" Version="1.5.0" />
|
||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="4.7.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<ServerGarbageCollection>false</ServerGarbageCollection>
|
||||
<ConcurrentGarbageCollection>true</ConcurrentGarbageCollection>
|
||||
<TieredCompilation>true</TieredCompilation>
|
||||
<LangVersion>7.1</LangVersion>
|
||||
<LangVersion>Latest</LangVersion>
|
||||
<StartupObject></StartupObject>
|
||||
</PropertyGroup>
|
||||
|
||||
|
@ -13,6 +13,7 @@ using SharedLibraryCore.Dtos;
|
||||
using SharedLibraryCore.Exceptions;
|
||||
using SharedLibraryCore.Helpers;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using SharedLibraryCore.QueryHelper;
|
||||
using SharedLibraryCore.Services;
|
||||
using System;
|
||||
using System.Collections;
|
||||
@ -54,7 +55,7 @@ namespace IW4MAdmin.Application
|
||||
public IConfigurationHandler<ApplicationConfiguration> ConfigHandler;
|
||||
readonly IPageList PageList;
|
||||
private readonly Dictionary<long, ILogger> _loggers = new Dictionary<long, ILogger>();
|
||||
private readonly MetaService _metaService;
|
||||
private readonly IMetaService _metaService;
|
||||
private readonly TimeSpan _throttleTimeout = new TimeSpan(0, 1, 0);
|
||||
private readonly CancellationTokenSource _tokenSource;
|
||||
private readonly Dictionary<string, Task<IList>> _operationLookup = new Dictionary<string, Task<IList>>();
|
||||
@ -65,12 +66,14 @@ namespace IW4MAdmin.Application
|
||||
private readonly IEnumerable<IRegisterEvent> _customParserEvents;
|
||||
private readonly IEventHandler _eventHandler;
|
||||
private readonly IScriptCommandFactory _scriptCommandFactory;
|
||||
private readonly IMetaRegistration _metaRegistration;
|
||||
|
||||
public ApplicationManager(ILogger logger, IMiddlewareActionHandler actionHandler, IEnumerable<IManagerCommand> commands,
|
||||
ITranslationLookup translationLookup, IConfigurationHandler<CommandConfiguration> commandConfiguration,
|
||||
IConfigurationHandler<ApplicationConfiguration> appConfigHandler, IGameServerInstanceFactory serverInstanceFactory,
|
||||
IEnumerable<IPlugin> plugins, IParserRegexFactory parserRegexFactory, IEnumerable<IRegisterEvent> customParserEvents,
|
||||
IEventHandler eventHandler, IScriptCommandFactory scriptCommandFactory, IDatabaseContextFactory contextFactory)
|
||||
IEventHandler eventHandler, IScriptCommandFactory scriptCommandFactory, IDatabaseContextFactory contextFactory, IMetaService metaService,
|
||||
IMetaRegistration metaRegistration)
|
||||
{
|
||||
MiddlewareActionHandler = actionHandler;
|
||||
_servers = new ConcurrentBag<Server>();
|
||||
@ -85,7 +88,7 @@ namespace IW4MAdmin.Application
|
||||
AdditionalRConParsers = new List<IRConParser>() { new BaseRConParser(parserRegexFactory) };
|
||||
TokenAuthenticator = new TokenAuthentication();
|
||||
_logger = logger;
|
||||
_metaService = new MetaService();
|
||||
_metaService = metaService;
|
||||
_tokenSource = new CancellationTokenSource();
|
||||
_loggers.Add(0, logger);
|
||||
_commands = commands.ToList();
|
||||
@ -96,6 +99,7 @@ namespace IW4MAdmin.Application
|
||||
_customParserEvents = customParserEvents;
|
||||
_eventHandler = eventHandler;
|
||||
_scriptCommandFactory = scriptCommandFactory;
|
||||
_metaRegistration = metaRegistration;
|
||||
Plugins = plugins;
|
||||
}
|
||||
|
||||
@ -453,133 +457,7 @@ namespace IW4MAdmin.Application
|
||||
await _commandConfiguration.Save();
|
||||
#endregion
|
||||
|
||||
#region META
|
||||
async Task<List<ProfileMeta>> getProfileMeta(int clientId, int offset, int count, DateTime? startAt)
|
||||
{
|
||||
var metaList = new List<ProfileMeta>();
|
||||
|
||||
// we don't want to return anything because it means we're trying to retrieve paged meta data
|
||||
if (count > 1)
|
||||
{
|
||||
return metaList;
|
||||
}
|
||||
|
||||
var lastMapMeta = await _metaService.GetPersistentMeta("LastMapPlayed", new EFClient() { ClientId = clientId });
|
||||
|
||||
if (lastMapMeta != null)
|
||||
{
|
||||
metaList.Add(new ProfileMeta()
|
||||
{
|
||||
Id = lastMapMeta.MetaId,
|
||||
Key = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_LAST_MAP"],
|
||||
Value = lastMapMeta.Value,
|
||||
Show = true,
|
||||
Type = ProfileMeta.MetaType.Information,
|
||||
});
|
||||
}
|
||||
|
||||
var lastServerMeta = await _metaService.GetPersistentMeta("LastServerPlayed", new EFClient() { ClientId = clientId });
|
||||
|
||||
if (lastServerMeta != null)
|
||||
{
|
||||
metaList.Add(new ProfileMeta()
|
||||
{
|
||||
Id = lastServerMeta.MetaId,
|
||||
Key = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_LAST_SERVER"],
|
||||
Value = lastServerMeta.Value,
|
||||
Show = true,
|
||||
Type = ProfileMeta.MetaType.Information
|
||||
});
|
||||
}
|
||||
|
||||
var client = await GetClientService().Get(clientId);
|
||||
|
||||
if (client == null)
|
||||
{
|
||||
_logger.WriteWarning($"No client found with id {clientId} when generating profile meta");
|
||||
return metaList;
|
||||
}
|
||||
|
||||
metaList.Add(new ProfileMeta()
|
||||
{
|
||||
Id = client.ClientId,
|
||||
Key = $"{Utilities.CurrentLocalization.LocalizationIndex["GLOBAL_TIME_HOURS"]} {Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_PROFILE_PLAYER"]}",
|
||||
Value = Math.Round(client.TotalConnectionTime / 3600.0, 1).ToString("#,##0", new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)),
|
||||
Show = true,
|
||||
Column = 1,
|
||||
Order = 0,
|
||||
Type = ProfileMeta.MetaType.Information
|
||||
});
|
||||
|
||||
metaList.Add(new ProfileMeta()
|
||||
{
|
||||
Id = client.ClientId,
|
||||
Key = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_PROFILE_FSEEN"],
|
||||
Value = Utilities.GetTimePassed(client.FirstConnection, false),
|
||||
Show = true,
|
||||
Column = 1,
|
||||
Order = 1,
|
||||
Type = ProfileMeta.MetaType.Information
|
||||
});
|
||||
|
||||
metaList.Add(new ProfileMeta()
|
||||
{
|
||||
Id = client.ClientId,
|
||||
Key = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_PROFILE_LSEEN"],
|
||||
Value = Utilities.GetTimePassed(client.LastConnection, false),
|
||||
Show = true,
|
||||
Column = 1,
|
||||
Order = 2,
|
||||
Type = ProfileMeta.MetaType.Information
|
||||
});
|
||||
|
||||
metaList.Add(new ProfileMeta()
|
||||
{
|
||||
Id = client.ClientId,
|
||||
Key = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_CONNECTIONS"],
|
||||
Value = client.Connections.ToString("#,##0", new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)),
|
||||
Show = true,
|
||||
Column = 1,
|
||||
Order = 3,
|
||||
Type = ProfileMeta.MetaType.Information
|
||||
});
|
||||
|
||||
metaList.Add(new ProfileMeta()
|
||||
{
|
||||
Key = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_MASKED"],
|
||||
Value = client.Masked ? Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_TRUE"] : Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_FALSE"],
|
||||
Sensitive = true,
|
||||
Column = 1,
|
||||
Order = 4,
|
||||
Type = ProfileMeta.MetaType.Information
|
||||
});
|
||||
|
||||
return metaList;
|
||||
}
|
||||
|
||||
async Task<List<ProfileMeta>> getPenaltyMeta(int clientId, int offset, int count, DateTime? startAt)
|
||||
{
|
||||
if (count <= 1)
|
||||
{
|
||||
return new List<ProfileMeta>();
|
||||
}
|
||||
|
||||
var penalties = await GetPenaltyService().GetClientPenaltyForMetaAsync(clientId, count, offset, startAt);
|
||||
|
||||
return penalties.Select(_penalty => new ProfileMeta()
|
||||
{
|
||||
Id = _penalty.Id,
|
||||
Type = _penalty.PunisherId == clientId ? ProfileMeta.MetaType.Penalized : ProfileMeta.MetaType.ReceivedPenalty,
|
||||
Value = _penalty,
|
||||
When = _penalty.TimePunished,
|
||||
Sensitive = _penalty.Sensitive
|
||||
})
|
||||
.ToList();
|
||||
}
|
||||
|
||||
MetaService.AddRuntimeMeta(getProfileMeta);
|
||||
MetaService.AddRuntimeMeta(getPenaltyMeta);
|
||||
#endregion
|
||||
_metaRegistration.Register();
|
||||
|
||||
#region CUSTOM_EVENTS
|
||||
foreach (var customEvent in _customParserEvents.SelectMany(_events => _events.Events))
|
||||
|
@ -13,17 +13,19 @@ namespace IW4MAdmin.Application.Factories
|
||||
private readonly ITranslationLookup _translationLookup;
|
||||
private readonly IRConConnectionFactory _rconConnectionFactory;
|
||||
private readonly IGameLogReaderFactory _gameLogReaderFactory;
|
||||
private readonly IMetaService _metaService;
|
||||
|
||||
/// <summary>
|
||||
/// base constructor
|
||||
/// </summary>
|
||||
/// <param name="translationLookup"></param>
|
||||
/// <param name="rconConnectionFactory"></param>
|
||||
public GameServerInstanceFactory(ITranslationLookup translationLookup, IRConConnectionFactory rconConnectionFactory, IGameLogReaderFactory gameLogReaderFactory)
|
||||
public GameServerInstanceFactory(ITranslationLookup translationLookup, IRConConnectionFactory rconConnectionFactory, IGameLogReaderFactory gameLogReaderFactory, IMetaService metaService)
|
||||
{
|
||||
_translationLookup = translationLookup;
|
||||
_rconConnectionFactory = rconConnectionFactory;
|
||||
_gameLogReaderFactory = gameLogReaderFactory;
|
||||
_metaService = metaService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -34,7 +36,7 @@ namespace IW4MAdmin.Application.Factories
|
||||
/// <returns></returns>
|
||||
public Server CreateServer(ServerConfiguration config, IManager manager)
|
||||
{
|
||||
return new IW4MServer(manager, config, _translationLookup, _rconConnectionFactory, _gameLogReaderFactory);
|
||||
return new IW4MServer(manager, config, _translationLookup, _rconConnectionFactory, _gameLogReaderFactory, _metaService);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ using SharedLibraryCore.Dtos;
|
||||
using SharedLibraryCore.Exceptions;
|
||||
using SharedLibraryCore.Helpers;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using SharedLibraryCore.Services;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
@ -26,15 +25,17 @@ namespace IW4MAdmin
|
||||
private static readonly SharedLibraryCore.Localization.TranslationLookup loc = Utilities.CurrentLocalization.LocalizationIndex;
|
||||
public GameLogEventDetection LogEvent;
|
||||
private readonly ITranslationLookup _translationLookup;
|
||||
private readonly IMetaService _metaService;
|
||||
private const int REPORT_FLAG_COUNT = 4;
|
||||
private int lastGameTime = 0;
|
||||
|
||||
public int Id { get; private set; }
|
||||
|
||||
public IW4MServer(IManager mgr, ServerConfiguration cfg, ITranslationLookup lookup,
|
||||
IRConConnectionFactory connectionFactory, IGameLogReaderFactory gameLogReaderFactory) : base(cfg, mgr, connectionFactory, gameLogReaderFactory)
|
||||
IRConConnectionFactory connectionFactory, IGameLogReaderFactory gameLogReaderFactory, IMetaService metaService) : base(cfg, mgr, connectionFactory, gameLogReaderFactory)
|
||||
{
|
||||
_translationLookup = lookup;
|
||||
_metaService = metaService;
|
||||
}
|
||||
|
||||
override public async Task<EFClient> OnClientConnected(EFClient clientFromLog)
|
||||
@ -475,8 +476,8 @@ namespace IW4MAdmin
|
||||
Time = DateTime.UtcNow
|
||||
});
|
||||
|
||||
await new MetaService().AddPersistentMeta("LastMapPlayed", CurrentMap.Alias, E.Origin);
|
||||
await new MetaService().AddPersistentMeta("LastServerPlayed", E.Owner.Hostname, E.Origin);
|
||||
await _metaService.AddPersistentMeta("LastMapPlayed", CurrentMap.Alias, E.Origin);
|
||||
await _metaService.AddPersistentMeta("LastServerPlayed", E.Owner.Hostname, E.Origin);
|
||||
}
|
||||
|
||||
else if (E.Type == GameEvent.EventType.PreDisconnect)
|
||||
@ -610,13 +611,10 @@ namespace IW4MAdmin
|
||||
|
||||
if (E.Type == GameEvent.EventType.Broadcast)
|
||||
{
|
||||
#if DEBUG == false
|
||||
// this is a little ugly but I don't want to change the abstract class
|
||||
if (E.Data != null)
|
||||
if (!Utilities.IsDevelopment && E.Data != null) // hides broadcast when in development mode
|
||||
{
|
||||
await E.Owner.ExecuteCommandAsync(E.Data);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
lock (ChatHistory)
|
||||
@ -1083,9 +1081,11 @@ namespace IW4MAdmin
|
||||
Logger.WriteInfo($"Log file is {LogPath}");
|
||||
|
||||
_ = Task.Run(() => LogEvent.PollForChanges());
|
||||
#if !DEBUG
|
||||
|
||||
if (!Utilities.IsDevelopment)
|
||||
{
|
||||
Broadcast(loc["BROADCAST_ONLINE"]);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
public Uri[] GenerateUriForLog(string logPath, string gameLogServerUrl)
|
||||
@ -1233,6 +1233,7 @@ namespace IW4MAdmin
|
||||
if (targetClient.IsIngame)
|
||||
{
|
||||
string formattedKick = string.Format(RconParser.Configuration.CommandPrefixes.Kick, targetClient.ClientNumber, $"^7{loc["SERVER_TB_TEXT"]}- ^5{Reason}");
|
||||
Logger.WriteDebug($"Executing tempban kick command for {targetClient}");
|
||||
await targetClient.CurrentServer.ExecuteCommandAsync(formattedKick);
|
||||
}
|
||||
}
|
||||
|
@ -2,16 +2,22 @@
|
||||
using IW4MAdmin.Application.EventParsers;
|
||||
using IW4MAdmin.Application.Factories;
|
||||
using IW4MAdmin.Application.Helpers;
|
||||
using IW4MAdmin.Application.Meta;
|
||||
using IW4MAdmin.Application.Migration;
|
||||
using IW4MAdmin.Application.Misc;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using RestEase;
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Configuration;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using SharedLibraryCore.Dtos.Meta.Requests;
|
||||
using SharedLibraryCore.Dtos.Meta.Responses;
|
||||
using SharedLibraryCore.Exceptions;
|
||||
using SharedLibraryCore.Helpers;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using SharedLibraryCore.QueryHelper;
|
||||
using SharedLibraryCore.Repositories;
|
||||
using SharedLibraryCore.Services;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
@ -128,7 +134,11 @@ namespace IW4MAdmin.Application
|
||||
await ApplicationTask;
|
||||
}
|
||||
|
||||
catch { }
|
||||
catch (Exception e)
|
||||
{
|
||||
string failMessage = translationLookup == null ? "Failed to initalize IW4MAdmin" : translationLookup["MANAGER_INIT_FAIL"];
|
||||
Console.WriteLine($"{failMessage}: {e.GetExceptionInfo()}");
|
||||
}
|
||||
|
||||
if (ServerManager.IsRestartRequested)
|
||||
{
|
||||
@ -235,6 +245,12 @@ namespace IW4MAdmin.Application
|
||||
.AddSingleton<IGameLogReaderFactory, GameLogReaderFactory>()
|
||||
.AddSingleton<IScriptCommandFactory, ScriptCommandFactory>()
|
||||
.AddSingleton<IAuditInformationRepository, AuditInformationRepository>()
|
||||
.AddSingleton<IEntityService<EFClient>, ClientService>()
|
||||
.AddSingleton<IMetaService, MetaService>()
|
||||
.AddSingleton<IMetaRegistration, MetaRegistration>()
|
||||
.AddSingleton<IResourceQueryHelper<ClientPaginationRequest, ReceivedPenaltyResponse>, ReceivedPenaltyResourceQueryHelper>()
|
||||
.AddSingleton<IResourceQueryHelper<ClientPaginationRequest, AdministeredPenaltyResponse>, AdministeredPenaltyResourceQueryHelper>()
|
||||
.AddSingleton<IResourceQueryHelper<ClientPaginationRequest, UpdatedAliasResponse>, UpdatedAliasResourceQueryHelper>()
|
||||
.AddTransient<IParserPatternMatcher, ParserPatternMatcher>()
|
||||
.AddSingleton(_serviceProvider =>
|
||||
{
|
||||
|
64
Application/Meta/AdministeredPenaltyResourceQueryHelper.cs
Normal file
64
Application/Meta/AdministeredPenaltyResourceQueryHelper.cs
Normal file
@ -0,0 +1,64 @@
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using SharedLibraryCore.Dtos.Meta.Responses;
|
||||
using SharedLibraryCore.Helpers;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using SharedLibraryCore.QueryHelper;
|
||||
|
||||
namespace IW4MAdmin.Application.Meta
|
||||
{
|
||||
/// <summary>
|
||||
/// implementation of IResourceQueryHelper
|
||||
/// query helper that retrieves administered penalties for provided client id
|
||||
/// </summary>
|
||||
public class AdministeredPenaltyResourceQueryHelper : IResourceQueryHelper<ClientPaginationRequest, AdministeredPenaltyResponse>
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly IDatabaseContextFactory _contextFactory;
|
||||
|
||||
public AdministeredPenaltyResourceQueryHelper(ILogger logger, IDatabaseContextFactory contextFactory)
|
||||
{
|
||||
_contextFactory = contextFactory;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<ResourceQueryHelperResult<AdministeredPenaltyResponse>> QueryResource(ClientPaginationRequest query)
|
||||
{
|
||||
using var ctx = _contextFactory.CreateContext(enableTracking: false);
|
||||
|
||||
var iqPenalties = ctx.Penalties.AsNoTracking()
|
||||
.Where(_penalty => query.ClientId == _penalty.PunisherId)
|
||||
.Where(_penalty => _penalty.When < query.Before)
|
||||
.OrderByDescending(_penalty => _penalty.When);
|
||||
|
||||
var penalties = await iqPenalties
|
||||
.Take(query.Count)
|
||||
.Select(_penalty => new AdministeredPenaltyResponse()
|
||||
{
|
||||
PenaltyId = _penalty.PenaltyId,
|
||||
Offense = _penalty.Offense,
|
||||
AutomatedOffense = _penalty.AutomatedOffense,
|
||||
ClientId = _penalty.OffenderId,
|
||||
OffenderName = _penalty.Offender.CurrentAlias.Name,
|
||||
OffenderClientId = _penalty.Offender.ClientId,
|
||||
PunisherClientId = _penalty.PunisherId,
|
||||
PunisherName = _penalty.Punisher.CurrentAlias.Name,
|
||||
PenaltyType = _penalty.Type,
|
||||
When = _penalty.When,
|
||||
ExpirationDate = _penalty.Expires,
|
||||
IsLinked = _penalty.OffenderId != query.ClientId,
|
||||
IsSensitive = _penalty.Type == EFPenalty.PenaltyType.Flag
|
||||
})
|
||||
.ToListAsync();
|
||||
|
||||
return new ResourceQueryHelperResult<AdministeredPenaltyResponse>
|
||||
{
|
||||
// todo: might need to do count at some point
|
||||
RetrievedResultCount = penalties.Count,
|
||||
Results = penalties
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
165
Application/Meta/MetaRegistration.cs
Normal file
165
Application/Meta/MetaRegistration.cs
Normal file
@ -0,0 +1,165 @@
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using SharedLibraryCore.Dtos.Meta.Responses;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using SharedLibraryCore.QueryHelper;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace IW4MAdmin.Application.Meta
|
||||
{
|
||||
public class MetaRegistration : IMetaRegistration
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private ITranslationLookup _transLookup;
|
||||
private readonly IMetaService _metaService;
|
||||
private readonly IEntityService<EFClient> _clientEntityService;
|
||||
private readonly IResourceQueryHelper<ClientPaginationRequest, ReceivedPenaltyResponse> _receivedPenaltyHelper;
|
||||
private readonly IResourceQueryHelper<ClientPaginationRequest, AdministeredPenaltyResponse> _administeredPenaltyHelper;
|
||||
private readonly IResourceQueryHelper<ClientPaginationRequest, UpdatedAliasResponse> _updatedAliasHelper;
|
||||
|
||||
public MetaRegistration(ILogger logger, IMetaService metaService, ITranslationLookup transLookup, IEntityService<EFClient> clientEntityService,
|
||||
IResourceQueryHelper<ClientPaginationRequest, ReceivedPenaltyResponse> receivedPenaltyHelper,
|
||||
IResourceQueryHelper<ClientPaginationRequest, AdministeredPenaltyResponse> administeredPenaltyHelper,
|
||||
IResourceQueryHelper<ClientPaginationRequest, UpdatedAliasResponse> updatedAliasHelper)
|
||||
{
|
||||
_logger = logger;
|
||||
_transLookup = transLookup;
|
||||
_metaService = metaService;
|
||||
_clientEntityService = clientEntityService;
|
||||
_receivedPenaltyHelper = receivedPenaltyHelper;
|
||||
_administeredPenaltyHelper = administeredPenaltyHelper;
|
||||
_updatedAliasHelper = updatedAliasHelper;
|
||||
}
|
||||
|
||||
public void Register()
|
||||
{
|
||||
_metaService.AddRuntimeMeta<ClientPaginationRequest, InformationResponse>(MetaType.Information, GetProfileMeta);
|
||||
_metaService.AddRuntimeMeta<ClientPaginationRequest, ReceivedPenaltyResponse>(MetaType.ReceivedPenalty, GetReceivedPenaltiesMeta);
|
||||
_metaService.AddRuntimeMeta<ClientPaginationRequest, AdministeredPenaltyResponse>(MetaType.Penalized, GetAdministeredPenaltiesMeta);
|
||||
_metaService.AddRuntimeMeta<ClientPaginationRequest, UpdatedAliasResponse>(MetaType.AliasUpdate, GetUpdatedAliasMeta);
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<InformationResponse>> GetProfileMeta(ClientPaginationRequest request)
|
||||
{
|
||||
var metaList = new List<InformationResponse>();
|
||||
var lastMapMeta = await _metaService.GetPersistentMeta("LastMapPlayed", new EFClient() { ClientId = request.ClientId });
|
||||
|
||||
if (lastMapMeta != null)
|
||||
{
|
||||
metaList.Add(new InformationResponse()
|
||||
{
|
||||
ClientId = request.ClientId,
|
||||
MetaId = lastMapMeta.MetaId,
|
||||
Key = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_LAST_MAP"],
|
||||
Value = lastMapMeta.Value,
|
||||
ShouldDisplay = true,
|
||||
Type = MetaType.Information,
|
||||
Column = 1,
|
||||
Order = 6
|
||||
});
|
||||
}
|
||||
|
||||
var lastServerMeta = await _metaService.GetPersistentMeta("LastServerPlayed", new EFClient() { ClientId = request.ClientId });
|
||||
|
||||
if (lastServerMeta != null)
|
||||
{
|
||||
metaList.Add(new InformationResponse()
|
||||
{
|
||||
ClientId = request.ClientId,
|
||||
MetaId = lastServerMeta.MetaId,
|
||||
Key = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_LAST_SERVER"],
|
||||
Value = lastServerMeta.Value,
|
||||
ShouldDisplay = true,
|
||||
Type = MetaType.Information,
|
||||
Column = 0,
|
||||
Order = 6
|
||||
});
|
||||
}
|
||||
|
||||
var client = await _clientEntityService.Get(request.ClientId);
|
||||
|
||||
if (client == null)
|
||||
{
|
||||
_logger.WriteWarning($"No client found with id {request.ClientId} when generating profile meta");
|
||||
return metaList;
|
||||
}
|
||||
|
||||
metaList.Add(new InformationResponse()
|
||||
{
|
||||
ClientId = client.ClientId,
|
||||
Key = _transLookup["WEBFRONT_PROFILE_META_PLAY_TIME"],
|
||||
Value = TimeSpan.FromHours(client.TotalConnectionTime / 3600.0).HumanizeForCurrentCulture(),
|
||||
ShouldDisplay = true,
|
||||
Column = 1,
|
||||
Order = 0,
|
||||
Type = MetaType.Information
|
||||
});
|
||||
|
||||
metaList.Add(new InformationResponse()
|
||||
{
|
||||
ClientId = client.ClientId,
|
||||
Key = _transLookup["WEBFRONT_PROFILE_META_FIRST_SEEN"],
|
||||
Value = (DateTime.UtcNow - client.FirstConnection).HumanizeForCurrentCulture(),
|
||||
ShouldDisplay = true,
|
||||
Column = 1,
|
||||
Order = 1,
|
||||
Type = MetaType.Information
|
||||
});
|
||||
|
||||
metaList.Add(new InformationResponse()
|
||||
{
|
||||
ClientId = client.ClientId,
|
||||
Key = _transLookup["WEBFRONT_PROFILE_META_LAST_SEEN"],
|
||||
Value = (DateTime.UtcNow - client.LastConnection).HumanizeForCurrentCulture(),
|
||||
ShouldDisplay = true,
|
||||
Column = 1,
|
||||
Order = 2,
|
||||
Type = MetaType.Information
|
||||
});
|
||||
|
||||
metaList.Add(new InformationResponse()
|
||||
{
|
||||
ClientId = client.ClientId,
|
||||
Key = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_CONNECTIONS"],
|
||||
Value = client.Connections.ToString("#,##0", new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)),
|
||||
ShouldDisplay = true,
|
||||
Column = 1,
|
||||
Order = 3,
|
||||
Type = MetaType.Information
|
||||
});
|
||||
|
||||
metaList.Add(new InformationResponse()
|
||||
{
|
||||
ClientId = client.ClientId,
|
||||
Key = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_MASKED"],
|
||||
Value = client.Masked ? Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_TRUE"] : Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_FALSE"],
|
||||
IsSensitive = true,
|
||||
Column = 1,
|
||||
Order = 4,
|
||||
Type = MetaType.Information
|
||||
});
|
||||
|
||||
return metaList;
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<ReceivedPenaltyResponse>> GetReceivedPenaltiesMeta(ClientPaginationRequest request)
|
||||
{
|
||||
var penalties = await _receivedPenaltyHelper.QueryResource(request);
|
||||
return penalties.Results;
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<AdministeredPenaltyResponse>> GetAdministeredPenaltiesMeta(ClientPaginationRequest request)
|
||||
{
|
||||
var penalties = await _administeredPenaltyHelper.QueryResource(request);
|
||||
return penalties.Results;
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<UpdatedAliasResponse>> GetUpdatedAliasMeta(ClientPaginationRequest request)
|
||||
{
|
||||
var aliases = await _updatedAliasHelper.QueryResource(request);
|
||||
return aliases.Results;
|
||||
}
|
||||
}
|
||||
}
|
72
Application/Meta/ReceivedPenaltyResourceQueryHelper.cs
Normal file
72
Application/Meta/ReceivedPenaltyResourceQueryHelper.cs
Normal file
@ -0,0 +1,72 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using SharedLibraryCore.Dtos.Meta.Responses;
|
||||
using SharedLibraryCore.Helpers;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using SharedLibraryCore.QueryHelper;
|
||||
|
||||
namespace IW4MAdmin.Application.Meta
|
||||
{
|
||||
/// <summary>
|
||||
/// implementation of IResourceQueryHelper
|
||||
/// used to pull in penalties applied to a given client id
|
||||
/// </summary>
|
||||
public class ReceivedPenaltyResourceQueryHelper : IResourceQueryHelper<ClientPaginationRequest, ReceivedPenaltyResponse>
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly IDatabaseContextFactory _contextFactory;
|
||||
|
||||
public ReceivedPenaltyResourceQueryHelper(ILogger logger, IDatabaseContextFactory contextFactory)
|
||||
{
|
||||
_contextFactory = contextFactory;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<ResourceQueryHelperResult<ReceivedPenaltyResponse>> QueryResource(ClientPaginationRequest query)
|
||||
{
|
||||
var linkedPenaltyType = Utilities.LinkedPenaltyTypes();
|
||||
using var ctx = _contextFactory.CreateContext(enableTracking: false);
|
||||
|
||||
var linkId = await ctx.Clients.AsNoTracking()
|
||||
.Where(_client => _client.ClientId == query.ClientId)
|
||||
.Select(_client => _client.AliasLinkId)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
var iqPenalties = ctx.Penalties.AsNoTracking()
|
||||
.Where(_penalty => _penalty.OffenderId == query.ClientId || (linkedPenaltyType.Contains(_penalty.Type) && _penalty.LinkId == linkId))
|
||||
.Where(_penalty => _penalty.When < query.Before)
|
||||
.OrderByDescending(_penalty => _penalty.When);
|
||||
|
||||
var penalties = await iqPenalties
|
||||
.Take(query.Count)
|
||||
.Select(_penalty => new ReceivedPenaltyResponse()
|
||||
{
|
||||
PenaltyId = _penalty.PenaltyId,
|
||||
ClientId = query.ClientId,
|
||||
Offense = _penalty.Offense,
|
||||
AutomatedOffense = _penalty.AutomatedOffense,
|
||||
OffenderClientId = _penalty.OffenderId,
|
||||
OffenderName = _penalty.Offender.CurrentAlias.Name,
|
||||
PunisherClientId = _penalty.PunisherId,
|
||||
PunisherName = _penalty.Punisher.CurrentAlias.Name,
|
||||
PenaltyType = _penalty.Type,
|
||||
When = _penalty.When,
|
||||
ExpirationDate = _penalty.Expires,
|
||||
IsLinked = _penalty.OffenderId != query.ClientId,
|
||||
IsSensitive = _penalty.Type == EFPenalty.PenaltyType.Flag
|
||||
})
|
||||
.ToListAsync();
|
||||
|
||||
return new ResourceQueryHelperResult<ReceivedPenaltyResponse>
|
||||
{
|
||||
// todo: maybe actually count
|
||||
RetrievedResultCount = penalties.Count,
|
||||
Results = penalties
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
60
Application/Meta/UpdatedAliasResourceQueryHelper.cs
Normal file
60
Application/Meta/UpdatedAliasResourceQueryHelper.cs
Normal file
@ -0,0 +1,60 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Dtos.Meta.Responses;
|
||||
using SharedLibraryCore.Helpers;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using SharedLibraryCore.QueryHelper;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace IW4MAdmin.Application.Meta
|
||||
{
|
||||
/// <summary>
|
||||
/// implementation if IResrouceQueryHerlp
|
||||
/// used to pull alias changes for given client id
|
||||
/// </summary>
|
||||
public class UpdatedAliasResourceQueryHelper : IResourceQueryHelper<ClientPaginationRequest, UpdatedAliasResponse>
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly IDatabaseContextFactory _contextFactory;
|
||||
|
||||
public UpdatedAliasResourceQueryHelper(ILogger logger, IDatabaseContextFactory contextFactory)
|
||||
{
|
||||
_logger = logger;
|
||||
_contextFactory = contextFactory;
|
||||
}
|
||||
|
||||
public async Task<ResourceQueryHelperResult<UpdatedAliasResponse>> QueryResource(ClientPaginationRequest query)
|
||||
{
|
||||
using var ctx = _contextFactory.CreateContext(enableTracking: false);
|
||||
int linkId = ctx.Clients.First(_client => _client.ClientId == query.ClientId).AliasLinkId;
|
||||
|
||||
var iqAliasUpdates = ctx.Aliases
|
||||
.Where(_alias => _alias.LinkId == linkId)
|
||||
.Where(_alias => _alias.DateAdded < query.Before)
|
||||
.Where(_alias => _alias.IPAddress != null)
|
||||
.OrderByDescending(_alias => _alias.DateAdded)
|
||||
.Select(_alias => new UpdatedAliasResponse
|
||||
{
|
||||
MetaId = _alias.AliasId,
|
||||
Name = _alias.Name,
|
||||
IPAddress = _alias.IPAddress.ConvertIPtoString(),
|
||||
When = _alias.DateAdded,
|
||||
Type = MetaType.AliasUpdate,
|
||||
IsSensitive = true
|
||||
});
|
||||
|
||||
var result = (await iqAliasUpdates
|
||||
.Take(query.Count)
|
||||
.ToListAsync())
|
||||
.Distinct();
|
||||
|
||||
|
||||
return new ResourceQueryHelperResult<UpdatedAliasResponse>
|
||||
{
|
||||
Results = result, // we can potentially have duplicates
|
||||
RetrievedResultCount = result.Count()
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
187
Application/Misc/MetaService.cs
Normal file
187
Application/Misc/MetaService.cs
Normal file
@ -0,0 +1,187 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using SharedLibraryCore.Dtos;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using SharedLibraryCore.QueryHelper;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace IW4MAdmin.Application.Misc
|
||||
{
|
||||
/// <summary>
|
||||
/// implementation of IMetaService
|
||||
/// used to add and retrieve runtime and persistent meta
|
||||
/// </summary>
|
||||
public class MetaService : IMetaService
|
||||
{
|
||||
private readonly IDictionary<MetaType, List<dynamic>> _metaActions;
|
||||
private readonly IDatabaseContextFactory _contextFactory;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public MetaService(ILogger logger, IDatabaseContextFactory contextFactory)
|
||||
{
|
||||
_logger = logger;
|
||||
_metaActions = new Dictionary<MetaType, List<dynamic>>();
|
||||
_contextFactory = contextFactory;
|
||||
}
|
||||
|
||||
public async Task AddPersistentMeta(string metaKey, string metaValue, EFClient client)
|
||||
{
|
||||
// this seems to happen if the client disconnects before they've had time to authenticate and be added
|
||||
if (client.ClientId < 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
using var ctx = _contextFactory.CreateContext();
|
||||
|
||||
var existingMeta = await ctx.EFMeta
|
||||
.Where(_meta => _meta.Key == metaKey)
|
||||
.Where(_meta => _meta.ClientId == client.ClientId)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
if (existingMeta != null)
|
||||
{
|
||||
existingMeta.Value = metaValue;
|
||||
existingMeta.Updated = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
ctx.EFMeta.Add(new EFMeta()
|
||||
{
|
||||
ClientId = client.ClientId,
|
||||
Created = DateTime.UtcNow,
|
||||
Key = metaKey,
|
||||
Value = metaValue
|
||||
});
|
||||
}
|
||||
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task<EFMeta> GetPersistentMeta(string metaKey, EFClient client)
|
||||
{
|
||||
using var ctx = _contextFactory.CreateContext(enableTracking: false);
|
||||
|
||||
return await ctx.EFMeta
|
||||
.Where(_meta => _meta.Key == metaKey)
|
||||
.Where(_meta => _meta.ClientId == client.ClientId)
|
||||
.Select(_meta => new EFMeta()
|
||||
{
|
||||
MetaId = _meta.MetaId,
|
||||
Key = _meta.Key,
|
||||
ClientId = _meta.ClientId,
|
||||
Value = _meta.Value
|
||||
})
|
||||
.FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
public void AddRuntimeMeta<T, V>(MetaType metaKey, Func<T, Task<IEnumerable<V>>> metaAction) where T : PaginationRequest where V : IClientMeta
|
||||
{
|
||||
if (!_metaActions.ContainsKey(metaKey))
|
||||
{
|
||||
_metaActions.Add(metaKey, new List<dynamic>() { metaAction });
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
_metaActions[metaKey].Add(metaAction);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<IClientMeta>> GetRuntimeMeta(ClientPaginationRequest request)
|
||||
{
|
||||
var meta = new List<IClientMeta>();
|
||||
|
||||
foreach (var (type, actions) in _metaActions)
|
||||
{
|
||||
// information is not listed chronologically
|
||||
if (type != MetaType.Information)
|
||||
{
|
||||
var metaItems = await actions[0](request);
|
||||
meta.AddRange(metaItems);
|
||||
}
|
||||
}
|
||||
|
||||
return meta.OrderByDescending(_meta => _meta.When)
|
||||
.Take(request.Count)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<T>> GetRuntimeMeta<T>(ClientPaginationRequest request, MetaType metaType) where T : IClientMeta
|
||||
{
|
||||
IEnumerable<T> meta;
|
||||
if (metaType == MetaType.Information)
|
||||
{
|
||||
var allMeta = new List<T>();
|
||||
|
||||
foreach (var individualMetaRegistration in _metaActions[metaType])
|
||||
{
|
||||
allMeta.AddRange(await individualMetaRegistration(request));
|
||||
}
|
||||
|
||||
return ProcessInformationMeta(allMeta);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
meta = await _metaActions[metaType][0](request) as IEnumerable<T>;
|
||||
}
|
||||
|
||||
return meta;
|
||||
}
|
||||
|
||||
private static IEnumerable<T> ProcessInformationMeta<T>(IEnumerable<T> meta) where T : IClientMeta
|
||||
{
|
||||
var table = new List<List<T>>();
|
||||
var metaWithColumn = meta
|
||||
.Where(_meta => _meta.Column != null);
|
||||
|
||||
var columnGrouping = metaWithColumn
|
||||
.GroupBy(_meta => _meta.Column);
|
||||
|
||||
var metaToSort = meta.Except(metaWithColumn).ToList();
|
||||
|
||||
foreach (var metaItem in columnGrouping)
|
||||
{
|
||||
table.Add(new List<T>(metaItem));
|
||||
}
|
||||
|
||||
while (metaToSort.Count > 0)
|
||||
{
|
||||
var sortingMeta = metaToSort.First();
|
||||
|
||||
int indexOfSmallestColumn()
|
||||
{
|
||||
int index = 0;
|
||||
int smallestColumnSize = int.MaxValue;
|
||||
for (int i = 0; i < table.Count; i++)
|
||||
{
|
||||
if (table[i].Count < smallestColumnSize)
|
||||
{
|
||||
smallestColumnSize = table[i].Count;
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
int columnIndex = indexOfSmallestColumn();
|
||||
|
||||
sortingMeta.Column = columnIndex;
|
||||
sortingMeta.Order = columnGrouping
|
||||
.First(_group => _group.Key == columnIndex)
|
||||
.Count();
|
||||
|
||||
table[columnIndex].Add(sortingMeta);
|
||||
|
||||
metaToSort.Remove(sortingMeta);
|
||||
}
|
||||
|
||||
return meta;
|
||||
}
|
||||
}
|
||||
}
|
@ -10,7 +10,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.SyndicationFeed.ReaderWriter" Version="1.0.2" />
|
||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2.4.3" PrivateAssets="All" />
|
||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2.4.6" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||
|
@ -10,7 +10,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2.4.3" PrivateAssets="All" />
|
||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2.4.6" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||
|
@ -16,7 +16,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2.4.3" PrivateAssets="All" />
|
||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2.4.6" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -23,7 +23,7 @@
|
||||
</Target>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2.4.3" PrivateAssets="All" />
|
||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2.4.6" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
@ -16,7 +16,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2.4.3" PrivateAssets="All" />
|
||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2.4.6" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||
|
@ -22,7 +22,7 @@ namespace IW4MAdmin.Plugins.Stats.Commands
|
||||
|
||||
List<string> mostPlayed = new List<string>()
|
||||
{
|
||||
$"^5--{Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_COMMANDS_MOSTPLAYED_TEXT"]}--"
|
||||
$"^5--{translationLookup["PLUGINS_STATS_COMMANDS_MOSTPLAYED_TEXT"]}--"
|
||||
};
|
||||
|
||||
using (var db = new DatabaseContext(true))
|
||||
@ -51,8 +51,7 @@ namespace IW4MAdmin.Plugins.Stats.Commands
|
||||
|
||||
var iqList = await iqStats.ToListAsync();
|
||||
|
||||
mostPlayed.AddRange(iqList.Select(stats =>
|
||||
$"^3{stats.Name}^7 - ^5{stats.Kills} ^7{translationLookup["PLUGINS_STATS_TEXT_KILLS"]} | ^5{Utilities.GetTimePassed(DateTime.UtcNow.AddSeconds(-stats.TotalConnectionTime), false)} ^7{translationLookup["WEBFRONT_PROFILE_PLAYER"].ToLower()}"));
|
||||
mostPlayed.AddRange(iqList.Select(stats => translationLookup["COMMANDS_MOST_PLAYED_FORMAT"].FormatExt(stats.Name, (DateTime.UtcNow - DateTime.UtcNow.AddSeconds(-stats.TotalConnectionTime)).HumanizeForCurrentCulture())));
|
||||
}
|
||||
|
||||
|
||||
|
@ -156,7 +156,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
Deaths = s.Deaths,
|
||||
Kills = s.Kills,
|
||||
KDR = Math.Round(s.KDR, 2),
|
||||
LastSeen = Utilities.GetTimePassed(clientRatingsDict[s.ClientId].LastConnection, false),
|
||||
LastSeen = (DateTime.UtcNow - clientRatingsDict[s.ClientId].LastConnection).HumanizeForCurrentCulture(),
|
||||
Name = clientRatingsDict[s.ClientId].Name,
|
||||
Performance = Math.Round(clientRatingsDict[s.ClientId].Performance, 2),
|
||||
RatingChange = ratingInfo.First(r => r.Key == s.ClientId).Ratings.First().Ranking - ratingInfo.First(r => r.Key == s.ClientId).Ratings.Last().Ranking,
|
||||
@ -663,6 +663,14 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
$"{penalty.Type}-{(int)penalty.Location}-{Math.Round(penalty.Value, 2)}@{penalty.HitCount}" :
|
||||
$"{penalty.Type}-{Math.Round(penalty.Value, 2)}@{penalty.HitCount}";
|
||||
|
||||
penaltyClient.AdministeredPenalties = new List<EFPenalty>()
|
||||
{
|
||||
new EFPenalty()
|
||||
{
|
||||
AutomatedOffense = flagReason
|
||||
}
|
||||
};
|
||||
|
||||
await attacker.Flag(flagReason, penaltyClient, new TimeSpan(168, 0, 0)).WaitAsync(Utilities.DefaultCommandTimeout, attacker.CurrentServer.Manager.CancellationToken);
|
||||
break;
|
||||
}
|
||||
|
@ -6,8 +6,10 @@ using SharedLibraryCore;
|
||||
using SharedLibraryCore.Database;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using SharedLibraryCore.Dtos;
|
||||
using SharedLibraryCore.Dtos.Meta.Responses;
|
||||
using SharedLibraryCore.Helpers;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using SharedLibraryCore.QueryHelper;
|
||||
using SharedLibraryCore.Services;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@ -33,13 +35,15 @@ namespace IW4MAdmin.Plugins.Stats
|
||||
#endif
|
||||
private readonly IDatabaseContextFactory _databaseContextFactory;
|
||||
private readonly ITranslationLookup _translationLookup;
|
||||
private readonly IMetaService _metaService;
|
||||
|
||||
public Plugin(IConfigurationHandlerFactory configurationHandlerFactory, IDatabaseContextFactory databaseContextFactory,
|
||||
ITranslationLookup translationLookup)
|
||||
ITranslationLookup translationLookup, IMetaService metaService)
|
||||
{
|
||||
Config = configurationHandlerFactory.GetConfigurationHandler<StatsConfiguration>("StatsPluginSettings");
|
||||
_databaseContextFactory = databaseContextFactory;
|
||||
_translationLookup = translationLookup;
|
||||
_metaService = metaService;
|
||||
}
|
||||
|
||||
public async Task OnEventAsync(GameEvent E, Server S)
|
||||
@ -188,17 +192,14 @@ namespace IW4MAdmin.Plugins.Stats
|
||||
"/Stats/TopPlayersAsync");
|
||||
|
||||
// meta data info
|
||||
async Task<List<ProfileMeta>> getStats(int clientId, int offset, int count, DateTime? startAt)
|
||||
async Task<IEnumerable<InformationResponse>> getStats(ClientPaginationRequest request)
|
||||
{
|
||||
if (count > 1)
|
||||
{
|
||||
return new List<ProfileMeta>();
|
||||
}
|
||||
|
||||
IList<EFClientStatistics> clientStats;
|
||||
using (var ctx = new DatabaseContext(disableTracking: true))
|
||||
int messageCount = 0;
|
||||
using (var ctx = _databaseContextFactory.CreateContext(enableTracking: false))
|
||||
{
|
||||
clientStats = await ctx.Set<EFClientStatistics>().Where(c => c.ClientId == clientId).ToListAsync();
|
||||
clientStats = await ctx.Set<EFClientStatistics>().Where(c => c.ClientId == request.ClientId).ToListAsync();
|
||||
messageCount = await ctx.Set<EFClientMessage>().CountAsync(_message => _message.ClientId == request.ClientId);
|
||||
}
|
||||
|
||||
int kills = clientStats.Sum(c => c.Kills);
|
||||
@ -209,73 +210,76 @@ namespace IW4MAdmin.Plugins.Stats
|
||||
double performance = Math.Round(validPerformanceValues.Sum(c => c.Performance * c.TimePlayed / performancePlayTime), 2);
|
||||
double spm = Math.Round(clientStats.Sum(c => c.SPM) / clientStats.Where(c => c.SPM > 0).Count(), 1);
|
||||
|
||||
return new List<ProfileMeta>()
|
||||
return new List<InformationResponse>()
|
||||
{
|
||||
new ProfileMeta()
|
||||
new InformationResponse()
|
||||
{
|
||||
Key = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_RANKING"],
|
||||
Value = "#" + (await Manager.GetClientOverallRanking(clientId)).ToString("#,##0", new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)),
|
||||
Value = "#" + (await Manager.GetClientOverallRanking(request.ClientId)).ToString("#,##0", new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)),
|
||||
Column = 0,
|
||||
Order = 0,
|
||||
Type = ProfileMeta.MetaType.Information
|
||||
Type = MetaType.Information
|
||||
},
|
||||
new ProfileMeta()
|
||||
new InformationResponse()
|
||||
{
|
||||
Key = Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_TEXT_KILLS"],
|
||||
Value = kills.ToString("#,##0", new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)),
|
||||
Column = 0,
|
||||
Order = 1,
|
||||
Type = ProfileMeta.MetaType.Information
|
||||
Type = MetaType.Information
|
||||
},
|
||||
new ProfileMeta()
|
||||
new InformationResponse()
|
||||
{
|
||||
Key = Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_TEXT_DEATHS"],
|
||||
Value = deaths.ToString("#,##0", new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)),
|
||||
Column = 0,
|
||||
Order = 2,
|
||||
Type = ProfileMeta.MetaType.Information
|
||||
Type = MetaType.Information
|
||||
},
|
||||
new ProfileMeta()
|
||||
new InformationResponse()
|
||||
{
|
||||
Key = Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_TEXT_KDR"],
|
||||
Value = kdr.ToString(new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)),
|
||||
Column = 0,
|
||||
Order = 3,
|
||||
Type = ProfileMeta.MetaType.Information
|
||||
Type = MetaType.Information
|
||||
},
|
||||
new ProfileMeta()
|
||||
new InformationResponse()
|
||||
{
|
||||
Key = Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_COMMANDS_PERFORMANCE"],
|
||||
Key = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_PROFILE_PERFORMANCE"],
|
||||
Value = performance.ToString("#,##0", new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)),
|
||||
Column = 0,
|
||||
Order = 4,
|
||||
Type = ProfileMeta.MetaType.Information
|
||||
Type = MetaType.Information
|
||||
},
|
||||
new ProfileMeta()
|
||||
new InformationResponse()
|
||||
{
|
||||
Key = Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_META_SPM"],
|
||||
Value = spm.ToString(new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)),
|
||||
Column = 0,
|
||||
Order = 5,
|
||||
Type = ProfileMeta.MetaType.Information
|
||||
Type = MetaType.Information
|
||||
},
|
||||
new InformationResponse()
|
||||
{
|
||||
Key = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_PROFILE_MESSAGES"],
|
||||
Value = messageCount.ToString("#,##0", new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)),
|
||||
Column = 1,
|
||||
Order = 4,
|
||||
Type = MetaType.Information
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
async Task<List<ProfileMeta>> getAnticheatInfo(int clientId, int offset, int count, DateTime? startAt)
|
||||
async Task<IEnumerable<InformationResponse>> getAnticheatInfo(ClientPaginationRequest request)
|
||||
{
|
||||
if (count > 1)
|
||||
{
|
||||
return new List<ProfileMeta>();
|
||||
}
|
||||
|
||||
IList<EFClientStatistics> clientStats;
|
||||
|
||||
using (var ctx = new DatabaseContext(disableTracking: true))
|
||||
using (var ctx = _databaseContextFactory.CreateContext(enableTracking: false))
|
||||
{
|
||||
clientStats = await ctx.Set<EFClientStatistics>()
|
||||
.Include(c => c.HitLocations)
|
||||
.Where(c => c.ClientId == clientId)
|
||||
.Where(c => c.ClientId == request.ClientId)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
@ -310,147 +314,120 @@ namespace IW4MAdmin.Plugins.Stats
|
||||
averageSnapValue = clientStats.Any(_stats => _stats.AverageSnapValue > 0) ? clientStats.Where(_stats => _stats.AverageSnapValue > 0).Average(_stat => _stat.AverageSnapValue) : 0;
|
||||
}
|
||||
|
||||
return new List<ProfileMeta>()
|
||||
return new List<InformationResponse>()
|
||||
{
|
||||
new ProfileMeta()
|
||||
new InformationResponse()
|
||||
{
|
||||
Key = $"{Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_AC_METRIC"]} 1",
|
||||
Value = chestRatio.ToString(new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)) + '%',
|
||||
Type = ProfileMeta.MetaType.Information,
|
||||
Type = MetaType.Information,
|
||||
Column = 2,
|
||||
Order = 0,
|
||||
Extra = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_TITLE_ACM1"],
|
||||
Sensitive = true
|
||||
ToolTipText = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_TITLE_ACM1"],
|
||||
IsSensitive = true
|
||||
},
|
||||
new ProfileMeta()
|
||||
new InformationResponse()
|
||||
{
|
||||
Key = $"{Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_AC_METRIC"]} 2",
|
||||
Value = abdomenRatio.ToString(new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)) + '%',
|
||||
Type = ProfileMeta.MetaType.Information,
|
||||
Type = MetaType.Information,
|
||||
Column = 2,
|
||||
Order = 1,
|
||||
Extra = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_TITLE_ACM2"],
|
||||
Sensitive = true
|
||||
ToolTipText = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_TITLE_ACM2"],
|
||||
IsSensitive = true
|
||||
},
|
||||
new ProfileMeta()
|
||||
new InformationResponse()
|
||||
{
|
||||
Key = $"{Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_AC_METRIC"]} 3",
|
||||
Value = chestAbdomenRatio.ToString(new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)) + '%',
|
||||
Type = ProfileMeta.MetaType.Information,
|
||||
Type = MetaType.Information,
|
||||
Column = 2,
|
||||
Order = 2,
|
||||
Extra = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_TITLE_ACM3"],
|
||||
Sensitive = true
|
||||
ToolTipText = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_TITLE_ACM3"],
|
||||
IsSensitive = true
|
||||
},
|
||||
new ProfileMeta()
|
||||
new InformationResponse()
|
||||
{
|
||||
Key = $"{Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_AC_METRIC"]} 4",
|
||||
Value = headRatio.ToString(new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)) + '%',
|
||||
Type = ProfileMeta.MetaType.Information,
|
||||
Type = MetaType.Information,
|
||||
Column = 2,
|
||||
Order = 3,
|
||||
Extra = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_TITLE_ACM4"],
|
||||
Sensitive = true
|
||||
ToolTipText = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_TITLE_ACM4"],
|
||||
IsSensitive = true
|
||||
},
|
||||
new ProfileMeta()
|
||||
new InformationResponse()
|
||||
{
|
||||
Key = $"{Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_AC_METRIC"]} 5",
|
||||
// todo: make sure this is wrapped somewhere else
|
||||
Value = $"{Math.Round(((float)hitOffsetAverage), 4).ToString(new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName))}°",
|
||||
Type = ProfileMeta.MetaType.Information,
|
||||
Type = MetaType.Information,
|
||||
Column = 2,
|
||||
Order = 4,
|
||||
Extra = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_TITLE_ACM5"],
|
||||
Sensitive = true
|
||||
ToolTipText = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_TITLE_ACM5"],
|
||||
IsSensitive = true
|
||||
},
|
||||
new ProfileMeta()
|
||||
new InformationResponse()
|
||||
{
|
||||
Key = $"{Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_AC_METRIC"]} 6",
|
||||
Value = Math.Round(maxStrain, 3).ToString(new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)),
|
||||
Type = ProfileMeta.MetaType.Information,
|
||||
Type = MetaType.Information,
|
||||
Column = 2,
|
||||
Order = 5,
|
||||
Extra = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_TITLE_ACM6"],
|
||||
Sensitive = true
|
||||
ToolTipText = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_TITLE_ACM6"],
|
||||
IsSensitive = true
|
||||
},
|
||||
new ProfileMeta()
|
||||
new InformationResponse()
|
||||
{
|
||||
Key = $"{Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_AC_METRIC"]} 7",
|
||||
Value = Math.Round(averageSnapValue, 3).ToString(new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)),
|
||||
Type = ProfileMeta.MetaType.Information,
|
||||
Type = MetaType.Information,
|
||||
Column = 2,
|
||||
Order = 6,
|
||||
Extra = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_TITLE_ACM7"],
|
||||
Sensitive = true
|
||||
ToolTipText = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_TITLE_ACM7"],
|
||||
IsSensitive = true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
async Task<List<ProfileMeta>> getMessages(int clientId, int offset, int count, DateTime? startAt)
|
||||
async Task<IEnumerable<MessageResponse>> getMessages(ClientPaginationRequest request)
|
||||
{
|
||||
if (count <= 1)
|
||||
{
|
||||
using (var ctx = new DatabaseContext(true))
|
||||
{
|
||||
return new List<ProfileMeta>
|
||||
{
|
||||
new ProfileMeta()
|
||||
{
|
||||
Key = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_PROFILE_MESSAGES"],
|
||||
Value = (await ctx.Set<EFClientMessage>()
|
||||
.CountAsync(_message => _message.ClientId == clientId))
|
||||
.ToString("#,##0", new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)),
|
||||
Column = 1,
|
||||
Order= 4,
|
||||
Type = ProfileMeta.MetaType.Information
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
List<ProfileMeta> messageMeta;
|
||||
using (var ctx = new DatabaseContext(disableTracking: true))
|
||||
List<MessageResponse> messageMeta;
|
||||
using (var ctx = _databaseContextFactory.CreateContext(enableTracking: false))
|
||||
{
|
||||
var messages = ctx.Set<EFClientMessage>()
|
||||
.Where(m => m.ClientId == clientId)
|
||||
.Where(_message => _message.TimeSent < startAt)
|
||||
.Where(m => m.ClientId == request.ClientId)
|
||||
.Where(_message => _message.TimeSent < request.Before)
|
||||
.OrderByDescending(_message => _message.TimeSent)
|
||||
.Skip(offset)
|
||||
.Take(count);
|
||||
.Take(request.Count);
|
||||
|
||||
messageMeta = await messages.Select(m => new ProfileMeta()
|
||||
messageMeta = await messages.Select(m => new MessageResponse()
|
||||
{
|
||||
Key = null,
|
||||
Value = new { m.Message, m.Server.GameName },
|
||||
// todo: game name
|
||||
Message = m.Message,
|
||||
When = m.TimeSent,
|
||||
Extra = m.ServerId.ToString(),
|
||||
Type = ProfileMeta.MetaType.ChatMessage
|
||||
ServerId = m.ServerId,
|
||||
Type = MetaType.ChatMessage
|
||||
}).ToListAsync();
|
||||
|
||||
foreach (var message in messageMeta)
|
||||
foreach (var meta in messageMeta)
|
||||
{
|
||||
if ((message.Value.Message as string).IsQuickMessage())
|
||||
if ((meta.Message).IsQuickMessage())
|
||||
{
|
||||
try
|
||||
{
|
||||
var quickMessages = ServerManager.GetApplicationSettings().Configuration()
|
||||
.QuickMessages
|
||||
.First(_qm => _qm.Game == message.Value.GameName);
|
||||
message.Value = quickMessages.Messages[(message.Value.Message as string).Substring(1)];
|
||||
message.Type = ProfileMeta.MetaType.QuickMessage;
|
||||
.First(/*_qm => _qm.Game == meta.GameName*/);
|
||||
meta.Message = quickMessages.Messages[(meta.Message as string).Substring(1)];
|
||||
meta.Type = MetaType.QuickMessage;
|
||||
}
|
||||
catch
|
||||
{
|
||||
message.Value = message.Value.Message;
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
message.Value = message.Value.Message;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return messageMeta;
|
||||
@ -458,11 +435,11 @@ namespace IW4MAdmin.Plugins.Stats
|
||||
|
||||
if (Config.Configuration().EnableAntiCheat)
|
||||
{
|
||||
MetaService.AddRuntimeMeta(getAnticheatInfo);
|
||||
_metaService.AddRuntimeMeta<ClientPaginationRequest, InformationResponse>(MetaType.Information, getAnticheatInfo);
|
||||
}
|
||||
|
||||
MetaService.AddRuntimeMeta(getStats);
|
||||
MetaService.AddRuntimeMeta(getMessages);
|
||||
_metaService.AddRuntimeMeta<ClientPaginationRequest, InformationResponse>(MetaType.Information, getStats);
|
||||
_metaService.AddRuntimeMeta<ClientPaginationRequest, MessageResponse>(MetaType.ChatMessage, getMessages);
|
||||
|
||||
async Task<string> totalKills(Server server)
|
||||
{
|
||||
|
@ -16,7 +16,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2.4.3" PrivateAssets="All" />
|
||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2.4.6" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||
|
@ -36,6 +36,7 @@ namespace IW4MAdmin.Plugins.Web.StatsWeb.Controllers
|
||||
ViewBag.Title = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_STATS_INDEX_TITLE"];
|
||||
ViewBag.Description = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_STATS_INDEX_DESC"];
|
||||
ViewBag.Servers = _manager.GetServers().Select(_server => new ServerInfo() { Name = _server.Hostname, ID = _server.EndPoint });
|
||||
ViewBag.Localization = _translationLookup;
|
||||
|
||||
return View("Index");
|
||||
}
|
||||
|
@ -1,9 +1,10 @@
|
||||
using SharedLibraryCore.Dtos;
|
||||
using SharedLibraryCore.QueryHelper;
|
||||
using System;
|
||||
|
||||
namespace StatsWeb.Dtos
|
||||
{
|
||||
public class ChatSearchQuery : PaginationInfo
|
||||
public class ChatSearchQuery : ClientPaginationRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// specifies the partial content of the message to search for
|
||||
@ -18,7 +19,7 @@ namespace StatsWeb.Dtos
|
||||
/// <summary>
|
||||
/// identifier for the client
|
||||
/// </summary>
|
||||
public int? ClientId { get; set; }
|
||||
public new int? ClientId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// only look for messages sent after this date
|
||||
|
@ -14,7 +14,7 @@
|
||||
<RunPostBuildEvent>Always</RunPostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2.4.3" PrivateAssets="All" />
|
||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2.4.6" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<ul class="nav nav-tabs border-top border-bottom nav-fill row" role="tablist" id="stats_top_players">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active top-players-link" href="#server_0" role="tab" data-toggle="tab" aria-selected="true" data-serverid="0">All Servers</a>
|
||||
<a class="nav-link active top-players-link" href="#server_0" role="tab" data-toggle="tab" aria-selected="true" data-serverid="0">@ViewBag.Localization["WEBFRONT_STATS_INDEX_ALL_SERVERS"]</a>
|
||||
</li>
|
||||
|
||||
@foreach (var server in ViewBag.Servers)
|
||||
|
@ -16,7 +16,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2.4.3" PrivateAssets="All" />
|
||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2.4.6" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||
|
@ -53,7 +53,6 @@ namespace SharedLibraryCore.Commands
|
||||
|
||||
public override Task ExecuteAsync(GameEvent E)
|
||||
{
|
||||
MetaService.Clear();
|
||||
E.Owner.Manager.Restart();
|
||||
E.Origin.Tell(_translationLookup["COMMANDS_RESTART_SUCCESS"]);
|
||||
return Task.CompletedTask;
|
||||
@ -292,7 +291,7 @@ namespace SharedLibraryCore.Commands
|
||||
switch ((await E.Target.TempBan(tempbanReason, length, E.Origin).WaitAsync(Utilities.DefaultCommandTimeout, E.Owner.Manager.CancellationToken)).FailReason)
|
||||
{
|
||||
case GameEvent.EventFailReason.None:
|
||||
E.Origin.Tell(_translationLookup["COMMANDS_TEMPBAN_SUCCESS"].FormatExt(E.Target, length.TimeSpanText()));
|
||||
E.Origin.Tell(_translationLookup["COMMANDS_TEMPBAN_SUCCESS"].FormatExt(E.Target, length.HumanizeForCurrentCulture()));
|
||||
break;
|
||||
case GameEvent.EventFailReason.Exception:
|
||||
E.Origin.Tell(_translationLookup["SERVER_ERROR_COMMAND_INGAME"]);
|
||||
@ -890,14 +889,9 @@ namespace SharedLibraryCore.Commands
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var P in db_players)
|
||||
foreach (var client in db_players)
|
||||
{
|
||||
// they're not going by another alias
|
||||
// /*P.AliasLink.Children.FirstOrDefault(a => a.Name.ToLower().Contains(E.Data.ToLower()))?.Name*/
|
||||
string msg = P.Name.ToLower().Contains(E.Data.ToLower()) ?
|
||||
$"[^3{P.Name}^7] [^3@{P.ClientId}^7] - [{ Utilities.ConvertLevelToColor((Permission)P.LevelInt, P.Level)}^7] - {P.IPAddress} | last seen {Utilities.GetTimePassed(P.LastConnection)}" :
|
||||
$"()->[^3{P.Name}^7] [^3@{P.ClientId}^7] - [{ Utilities.ConvertLevelToColor((Permission)P.LevelInt, P.Level)}^7] - {P.IPAddress} | last seen {Utilities.GetTimePassed(P.LastConnection)}";
|
||||
E.Origin.Tell(msg);
|
||||
E.Origin.Tell(_translationLookup["COMMANDS_FIND_FORMAT"].FormatExt(client.Name, client.ClientId, Utilities.ConvertLevelToColor((Permission)client.LevelInt, client.Level), client.IPAddress, client.LastConnectionText));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1259,7 +1253,7 @@ namespace SharedLibraryCore.Commands
|
||||
|
||||
else
|
||||
{
|
||||
string remainingTime = (penalty.Expires.Value - DateTime.UtcNow).TimeSpanText();
|
||||
string remainingTime = (penalty.Expires.Value - DateTime.UtcNow).HumanizeForCurrentCulture();
|
||||
E.Origin.Tell(_translationLookup["COMMANDS_BANINFO_TB_SUCCESS"].FormatExt(E.Target.Name, penalty.Offense, remainingTime));
|
||||
}
|
||||
}
|
||||
@ -1543,7 +1537,9 @@ namespace SharedLibraryCore.Commands
|
||||
/// </summary>
|
||||
public class SetGravatarCommand : Command
|
||||
{
|
||||
public SetGravatarCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup)
|
||||
private readonly IMetaService _metaService;
|
||||
|
||||
public SetGravatarCommand(CommandConfiguration config, ITranslationLookup translationLookup, IMetaService metaService) : base(config, translationLookup)
|
||||
{
|
||||
Name = "setgravatar";
|
||||
Description = _translationLookup["COMMANDS_GRAVATAR_DESC"];
|
||||
@ -1558,17 +1554,17 @@ namespace SharedLibraryCore.Commands
|
||||
Required = true
|
||||
}
|
||||
};
|
||||
|
||||
_metaService = metaService;
|
||||
}
|
||||
|
||||
public override async Task ExecuteAsync(GameEvent E)
|
||||
{
|
||||
var metaSvc = new MetaService();
|
||||
|
||||
using (var md5 = MD5.Create())
|
||||
{
|
||||
string gravatarEmail = string.Concat(md5.ComputeHash(E.Data.ToLower().Select(d => Convert.ToByte(d)).ToArray())
|
||||
.Select(h => h.ToString("x2")));
|
||||
await metaSvc.AddPersistentMeta("GravatarEmail", gravatarEmail, E.Origin);
|
||||
await _metaService.AddPersistentMeta("GravatarEmail", gravatarEmail, E.Origin);
|
||||
}
|
||||
|
||||
E.Origin.Tell(_translationLookup["COMMANDS_GRAVATAR_SUCCESS_NEW"]);
|
||||
|
@ -1,6 +1,6 @@
|
||||
namespace SharedLibraryCore.Dtos
|
||||
{
|
||||
public class FindClientRequest : PaginationInfo
|
||||
public class FindClientRequest : PaginationRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// name of client
|
||||
|
@ -0,0 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace SharedLibraryCore.Dtos.Meta.Requests
|
||||
{
|
||||
public class BaseClientMetaRequest : PaginationRequest
|
||||
{
|
||||
public int ClientId { get; set; }
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
using SharedLibraryCore.QueryHelper;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace SharedLibraryCore.Dtos.Meta.Requests
|
||||
{
|
||||
public class ReceivedPenaltyRequest : BaseClientMetaRequest
|
||||
{
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace SharedLibraryCore.Dtos.Meta.Responses
|
||||
{
|
||||
public class AdministeredPenaltyResponse : ReceivedPenaltyResponse
|
||||
{
|
||||
}
|
||||
}
|
19
SharedLibraryCore/Dtos/Meta/Responses/BaseMetaResponse.cs
Normal file
19
SharedLibraryCore/Dtos/Meta/Responses/BaseMetaResponse.cs
Normal file
@ -0,0 +1,19 @@
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace SharedLibraryCore.Dtos.Meta.Responses
|
||||
{
|
||||
public class BaseMetaResponse : IClientMeta, IClientMetaResponse
|
||||
{
|
||||
public int MetaId { get; set; }
|
||||
public int ClientId { get; set; }
|
||||
public MetaType Type { get; set; }
|
||||
public DateTime When { get; set; }
|
||||
public bool IsSensitive { get; set; }
|
||||
public bool ShouldDisplay { get; set; }
|
||||
public int? Column { get; set; }
|
||||
public int? Order { get; set; }
|
||||
}
|
||||
}
|
14
SharedLibraryCore/Dtos/Meta/Responses/InformationResponse.cs
Normal file
14
SharedLibraryCore/Dtos/Meta/Responses/InformationResponse.cs
Normal file
@ -0,0 +1,14 @@
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace SharedLibraryCore.Dtos.Meta.Responses
|
||||
{
|
||||
public class InformationResponse : BaseMetaResponse
|
||||
{
|
||||
public string Key { get; set; }
|
||||
public string Value { get; set; }
|
||||
public string ToolTipText { get; set; }
|
||||
}
|
||||
}
|
12
SharedLibraryCore/Dtos/Meta/Responses/MessageResponse.cs
Normal file
12
SharedLibraryCore/Dtos/Meta/Responses/MessageResponse.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace SharedLibraryCore.Dtos.Meta.Responses
|
||||
{
|
||||
public class MessageResponse : BaseMetaResponse
|
||||
{
|
||||
public long ServerId { get; set; }
|
||||
public string Message { get; set; }
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using static SharedLibraryCore.Database.Models.EFPenalty;
|
||||
|
||||
namespace SharedLibraryCore.Dtos.Meta.Responses
|
||||
{
|
||||
public class ReceivedPenaltyResponse : BaseMetaResponse
|
||||
{
|
||||
public int PenaltyId { get; set; }
|
||||
public int OffenderClientId { get; set; }
|
||||
public string OffenderName { get; set; }
|
||||
public string PunisherName { get; set; }
|
||||
public int PunisherClientId { get; set; }
|
||||
public PenaltyType PenaltyType { get; set; }
|
||||
public string Offense { get; set; }
|
||||
public string AutomatedOffense { get; set; }
|
||||
public DateTime? ExpirationDate { get; set; }
|
||||
public string ExpiresInText => ExpirationDate.HasValue && ExpirationDate.Value > DateTime.UtcNow ? (ExpirationDate - DateTime.UtcNow).Value.HumanizeForCurrentCulture() : "";
|
||||
public string LengthText => ExpirationDate.HasValue ? (ExpirationDate.Value.AddMinutes(1) - When).HumanizeForCurrentCulture() : "";
|
||||
public bool IsLinked { get; set; }
|
||||
public int LinkedClientId { get; set; }
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
using System;
|
||||
|
||||
namespace SharedLibraryCore.Dtos.Meta.Responses
|
||||
{
|
||||
public class UpdatedAliasResponse : BaseMetaResponse
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string IPAddress { get; set; } = "--";
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is UpdatedAliasResponse resp)
|
||||
{
|
||||
return resp.Name.StripColors() == Name.StripColors() && resp.IPAddress == IPAddress;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override int GetHashCode() => HashCode.Combine(Name.StripColors(), IPAddress);
|
||||
}
|
||||
}
|
13
SharedLibraryCore/Dtos/Meta/WebfrontTranslationHelper.cs
Normal file
13
SharedLibraryCore/Dtos/Meta/WebfrontTranslationHelper.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace SharedLibraryCore.Dtos.Meta
|
||||
{
|
||||
public class WebfrontTranslationHelper
|
||||
{
|
||||
public bool IsInterpolation { get; set; }
|
||||
public string MatchValue { get; set; }
|
||||
public string TranslationValue { get; set; }
|
||||
}
|
||||
}
|
@ -1,9 +1,11 @@
|
||||
namespace SharedLibraryCore.Dtos
|
||||
using System;
|
||||
|
||||
namespace SharedLibraryCore.Dtos
|
||||
{
|
||||
/// <summary>
|
||||
/// pagination information holder class
|
||||
/// </summary>
|
||||
public class PaginationInfo
|
||||
public class PaginationRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// how many items to skip
|
||||
@ -24,6 +26,8 @@
|
||||
/// direction of ordering
|
||||
/// </summary>
|
||||
public SortDirection Direction { get; set; } = SortDirection.Descending;
|
||||
|
||||
public DateTime? Before { get; set; }
|
||||
}
|
||||
|
||||
public enum SortDirection
|
@ -21,8 +21,8 @@ namespace SharedLibraryCore.Dtos
|
||||
public PenaltyType PenaltyType { get; set; }
|
||||
public string PenaltyTypeText => PenaltyType.ToString();
|
||||
public DateTime TimePunished { get; set; }
|
||||
public string TimePunishedString => Utilities.GetTimePassed(TimePunished, true);
|
||||
public string TimeRemaining => DateTime.UtcNow > Expires ? "" : $"{((Expires ?? DateTime.MaxValue).Year == DateTime.MaxValue.Year ? Utilities.GetTimePassed(TimePunished, true) : Utilities.TimeSpanText((Expires ?? DateTime.MaxValue) - DateTime.UtcNow))}";
|
||||
public string TimePunishedString => TimePunished.HumanizeForCurrentCulture();
|
||||
public string TimeRemaining => DateTime.UtcNow > Expires ? "" : $"{((Expires ?? DateTime.MaxValue).Year == DateTime.MaxValue.Year ? TimePunishedString : ((Expires ?? DateTime.MaxValue) - DateTime.UtcNow).HumanizeForCurrentCulture())}";
|
||||
public bool Expired => Expires.HasValue && Expires <= DateTime.UtcNow;
|
||||
public DateTime? Expires { get; set; }
|
||||
public override bool Sensitive => PenaltyType == PenaltyType.Flag || PenaltyType == PenaltyType.Unflag;
|
||||
|
@ -1,8 +1,8 @@
|
||||
using System;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using SharedLibraryCore.Dtos.Meta.Responses;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SharedLibraryCore.Dtos
|
||||
{
|
||||
@ -19,11 +19,13 @@ namespace SharedLibraryCore.Dtos
|
||||
public bool HasActivePenalty { get; set; }
|
||||
public string ActivePenaltyType { get; set; }
|
||||
public bool Authenticated { get; set; }
|
||||
public List<ProfileMeta> Meta { get; set; }
|
||||
public List<InformationResponse> Meta { get; set; }
|
||||
public EFPenalty ActivePenalty { get; set; }
|
||||
public bool Online { get; set; }
|
||||
public string TimeOnline { get; set; }
|
||||
public DateTime LastConnection { get; set; }
|
||||
public string LastConnectionText => Utilities.GetTimePassed(LastConnection, true);
|
||||
public string LastConnectionText => (DateTime.UtcNow - LastConnection).HumanizeForCurrentCulture();
|
||||
public IDictionary<int, long> LinkedAccounts { get; set; }
|
||||
public MetaType? MetaFilterType { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -1,32 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SharedLibraryCore.Dtos
|
||||
{
|
||||
public class ProfileMeta : SharedInfo
|
||||
{
|
||||
public enum MetaType
|
||||
{
|
||||
Other,
|
||||
Information,
|
||||
AliasUpdate,
|
||||
ChatMessage,
|
||||
Penalized,
|
||||
ReceivedPenalty,
|
||||
QuickMessage
|
||||
}
|
||||
|
||||
public DateTime When { get; set; }
|
||||
public string WhenString => Utilities.GetTimePassed(When, false);
|
||||
public string Key { get; set; }
|
||||
public dynamic Value { get; set; }
|
||||
public string Extra { get; set; }
|
||||
public virtual string Class => Value.GetType().ToString();
|
||||
public MetaType Type { get; set; }
|
||||
public int? Column { get; set; }
|
||||
public int? Order { get; set; }
|
||||
}
|
||||
}
|
@ -1,4 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using SharedLibraryCore.Dtos.Meta.Responses;
|
||||
|
||||
namespace SharedLibraryCore.Helpers
|
||||
{
|
||||
|
@ -14,6 +14,6 @@ namespace SharedLibraryCore.Interfaces
|
||||
/// </summary>
|
||||
/// <param name="paginationInfo">pagination info</param>
|
||||
/// <returns></returns>
|
||||
Task<IList<AuditInfo>> ListAuditInformation(PaginationInfo paginationInfo);
|
||||
Task<IList<AuditInfo>> ListAuditInformation(PaginationRequest paginationInfo);
|
||||
}
|
||||
}
|
||||
|
31
SharedLibraryCore/Interfaces/IClientMeta.cs
Normal file
31
SharedLibraryCore/Interfaces/IClientMeta.cs
Normal file
@ -0,0 +1,31 @@
|
||||
using System;
|
||||
|
||||
namespace SharedLibraryCore.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// describes all the base attributes of a client meta object
|
||||
/// </summary>
|
||||
public interface IClientMeta
|
||||
{
|
||||
MetaType Type { get; }
|
||||
DateTime When { get; }
|
||||
|
||||
bool IsSensitive { get; }
|
||||
bool ShouldDisplay { get; }
|
||||
|
||||
// sorting purposes
|
||||
public int? Column { get; set; }
|
||||
public int? Order { get; set; }
|
||||
}
|
||||
|
||||
public enum MetaType
|
||||
{
|
||||
Other,
|
||||
Information,
|
||||
AliasUpdate,
|
||||
ChatMessage,
|
||||
Penalized,
|
||||
ReceivedPenalty,
|
||||
QuickMessage
|
||||
}
|
||||
}
|
12
SharedLibraryCore/Interfaces/IClientMetaResponse.cs
Normal file
12
SharedLibraryCore/Interfaces/IClientMetaResponse.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace SharedLibraryCore.Interfaces
|
||||
{
|
||||
public interface IClientMetaResponse
|
||||
{
|
||||
int ClientId { get;}
|
||||
int MetaId { get; }
|
||||
}
|
||||
}
|
11
SharedLibraryCore/Interfaces/IMetaRegistration.cs
Normal file
11
SharedLibraryCore/Interfaces/IMetaRegistration.cs
Normal file
@ -0,0 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace SharedLibraryCore.Interfaces
|
||||
{
|
||||
public interface IMetaRegistration
|
||||
{
|
||||
void Register();
|
||||
}
|
||||
}
|
51
SharedLibraryCore/Interfaces/IMetaService.cs
Normal file
51
SharedLibraryCore/Interfaces/IMetaService.cs
Normal file
@ -0,0 +1,51 @@
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using SharedLibraryCore.Dtos;
|
||||
using SharedLibraryCore.QueryHelper;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SharedLibraryCore.Interfaces
|
||||
{
|
||||
public interface IMetaService
|
||||
{
|
||||
/// <summary>
|
||||
/// adds or updates meta key and value to the database
|
||||
/// </summary>
|
||||
/// <param name="metaKey">key of meta data</param>
|
||||
/// <param name="metaValue">value of the meta data</param>
|
||||
/// <param name="client">client to save the meta for</param>
|
||||
/// <returns></returns>
|
||||
Task AddPersistentMeta(string metaKey, string metaValue, EFClient client);
|
||||
|
||||
/// <summary>
|
||||
/// retrieves meta data for given client and key
|
||||
/// </summary>
|
||||
/// <param name="metaKey">key to retrieve value for</param>
|
||||
/// <param name="client">client to retrieve meta for</param>
|
||||
/// <returns></returns>
|
||||
Task<EFMeta> GetPersistentMeta(string metaKey, EFClient client);
|
||||
|
||||
/// <summary>
|
||||
/// adds a meta task to the runtime meta list
|
||||
/// </summary>
|
||||
/// <param name="metaKey">type of meta</param>
|
||||
/// <param name="metaAction">action to perform</param>
|
||||
void AddRuntimeMeta<T,V>(MetaType metaKey, Func<T, Task<IEnumerable<V>>> metaAction) where V : IClientMeta where T: PaginationRequest;
|
||||
|
||||
/// <summary>
|
||||
/// retrieves all the runtime meta information for given client idea
|
||||
/// </summary>
|
||||
/// <param name="request">request information</param>
|
||||
/// <returns></returns>
|
||||
Task<IEnumerable<IClientMeta>> GetRuntimeMeta(ClientPaginationRequest request);
|
||||
|
||||
/// <summary>
|
||||
/// retreives all the runtime of provided type
|
||||
/// </summary>
|
||||
/// <param name="request">>request information</param>
|
||||
/// <param name="metaType">type of meta to retreive</param>
|
||||
/// <returns></returns>
|
||||
Task<IEnumerable<T>> GetRuntimeMeta<T>(ClientPaginationRequest request, MetaType metaType) where T : IClientMeta;
|
||||
}
|
||||
}
|
@ -1,12 +1,23 @@
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
|
||||
namespace SharedLibraryCore.Localization
|
||||
{
|
||||
public class Layout
|
||||
{
|
||||
public string LocalizationName { get; set; }
|
||||
private string localizationName;
|
||||
public string LocalizationName
|
||||
{
|
||||
get => localizationName;
|
||||
set
|
||||
{
|
||||
localizationName = value;
|
||||
Culture = new CultureInfo(value);
|
||||
}
|
||||
}
|
||||
public TranslationLookup LocalizationIndex { get; set; }
|
||||
public CultureInfo Culture { get; private set; }
|
||||
|
||||
public Layout(Dictionary<string, string> set)
|
||||
{
|
||||
|
@ -618,7 +618,7 @@ namespace SharedLibraryCore.Database.Models
|
||||
if (tempbanPenalty != null)
|
||||
{
|
||||
CurrentServer.Logger.WriteDebug($"Kicking {this} because their GUID is temporarily banned");
|
||||
Kick($"{loc["SERVER_TB_REMAIN"]} ({(tempbanPenalty.Expires.Value - DateTime.UtcNow).TimeSpanText()} {loc["WEBFRONT_PENALTY_TEMPLATE_REMAINING"]})", autoKickClient);
|
||||
Kick($"{loc["SERVER_TB_REMAIN"]} ({(tempbanPenalty.Expires.Value - DateTime.UtcNow).HumanizeForCurrentCulture()} {loc["WEBFRONT_PENALTY_TEMPLATE_REMAINING"]})", autoKickClient);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
12
SharedLibraryCore/QueryHelper/ClientPaginationRequest.cs
Normal file
12
SharedLibraryCore/QueryHelper/ClientPaginationRequest.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using SharedLibraryCore.Dtos;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace SharedLibraryCore.QueryHelper
|
||||
{
|
||||
public class ClientPaginationRequest : PaginationRequest
|
||||
{
|
||||
public int ClientId { get; set; }
|
||||
}
|
||||
}
|
@ -20,7 +20,7 @@ namespace SharedLibraryCore.Repositories
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<IList<AuditInfo>> ListAuditInformation(PaginationInfo paginationInfo)
|
||||
public async Task<IList<AuditInfo>> ListAuditInformation(PaginationRequest paginationInfo)
|
||||
{
|
||||
using (var ctx = _contextFactory.CreateContext(enableTracking: false))
|
||||
{
|
||||
|
@ -1,169 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SharedLibraryCore.Database;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using SharedLibraryCore.Dtos;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SharedLibraryCore.Services
|
||||
{
|
||||
public class MetaService
|
||||
{
|
||||
private static List<Func<int, int, int, DateTime?, Task<List<ProfileMeta>>>> _metaActions = new List<Func<int, int, int, DateTime?, Task<List<ProfileMeta>>>>();
|
||||
|
||||
/// <summary>
|
||||
/// adds or updates meta key and value to the database
|
||||
/// </summary>
|
||||
/// <param name="metaKey">key of meta data</param>
|
||||
/// <param name="metaValue">value of the meta data</param>
|
||||
/// <param name="client">client to save the meta for</param>
|
||||
/// <returns></returns>
|
||||
public async Task AddPersistentMeta(string metaKey, string metaValue, EFClient client)
|
||||
{
|
||||
// this seems to happen if the client disconnects before they've had time to authenticate and be added
|
||||
if (client.ClientId < 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
using (var ctx = new DatabaseContext())
|
||||
{
|
||||
var existingMeta = await ctx.EFMeta
|
||||
.Where(_meta => _meta.Key == metaKey)
|
||||
.Where(_meta => _meta.ClientId == client.ClientId)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
if (existingMeta != null)
|
||||
{
|
||||
existingMeta.Value = metaValue;
|
||||
existingMeta.Updated = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
ctx.EFMeta.Add(new EFMeta()
|
||||
{
|
||||
ClientId = client.ClientId,
|
||||
Created = DateTime.UtcNow,
|
||||
Key = metaKey,
|
||||
Value = metaValue
|
||||
});
|
||||
}
|
||||
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
internal static void Clear()
|
||||
{
|
||||
_metaActions.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// retrieves meta data for given client and key
|
||||
/// </summary>
|
||||
/// <param name="metaKey">key to retrieve value for</param>
|
||||
/// <param name="client">client to retrieve meta for</param>
|
||||
/// <returns></returns>
|
||||
public async Task<EFMeta> GetPersistentMeta(string metaKey, EFClient client)
|
||||
{
|
||||
using (var ctx = new DatabaseContext(disableTracking: true))
|
||||
{
|
||||
return await ctx.EFMeta
|
||||
.Where(_meta => _meta.Key == metaKey)
|
||||
.Where(_meta => _meta.ClientId == client.ClientId)
|
||||
.Select(_meta => new EFMeta()
|
||||
{
|
||||
MetaId = _meta.MetaId,
|
||||
Key = _meta.Key,
|
||||
ClientId = _meta.ClientId,
|
||||
Value = _meta.Value
|
||||
})
|
||||
.FirstOrDefaultAsync();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// aads a meta task to the runtime meta list
|
||||
/// </summary>
|
||||
/// <param name="metaAction"></param>
|
||||
public static void AddRuntimeMeta(Func<int, int, int, DateTime?, Task<List<ProfileMeta>>> metaAction)
|
||||
{
|
||||
_metaActions.Add(metaAction);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// retrieves all the runtime meta information for given client idea
|
||||
/// </summary>
|
||||
/// <param name="clientId">id of the client</param>
|
||||
/// <param name="count">number of meta items to retrieve</param>
|
||||
/// <param name="offset">offset from the first item</param>
|
||||
/// <returns></returns>
|
||||
public static async Task<List<ProfileMeta>> GetRuntimeMeta(int clientId, int offset = 0, int count = int.MaxValue, DateTime? startAt = null)
|
||||
{
|
||||
var meta = new List<ProfileMeta>();
|
||||
|
||||
foreach (var action in _metaActions)
|
||||
{
|
||||
var metaItems = await action(clientId, offset, count, startAt);
|
||||
meta.AddRange(metaItems);
|
||||
}
|
||||
|
||||
if (count == 1)
|
||||
{
|
||||
var table = new List<List<ProfileMeta>>();
|
||||
var metaWithColumn = meta
|
||||
.Where(_meta => _meta.Column != null);
|
||||
|
||||
var columnGrouping = metaWithColumn
|
||||
.GroupBy(_meta => _meta.Column);
|
||||
|
||||
var metaToSort = meta.Except(metaWithColumn).ToList();
|
||||
|
||||
foreach (var metaItem in columnGrouping)
|
||||
{
|
||||
table.Add(new List<ProfileMeta>(metaItem));
|
||||
}
|
||||
|
||||
while (metaToSort.Count > 0)
|
||||
{
|
||||
var sortingMeta = metaToSort.First();
|
||||
|
||||
int indexOfSmallestColumn()
|
||||
{
|
||||
int index = 0;
|
||||
int smallestColumnSize = int.MaxValue;
|
||||
for (int i = 0; i < table.Count; i++)
|
||||
{
|
||||
if (table[i].Count < smallestColumnSize)
|
||||
{
|
||||
smallestColumnSize = table[i].Count;
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
int columnIndex = indexOfSmallestColumn();
|
||||
|
||||
sortingMeta.Column = columnIndex;
|
||||
sortingMeta.Order = columnGrouping
|
||||
.First(_group => _group.Key == columnIndex)
|
||||
.Count();
|
||||
|
||||
table[columnIndex].Add(sortingMeta);
|
||||
|
||||
metaToSort.Remove(sortingMeta);
|
||||
}
|
||||
|
||||
return meta;
|
||||
}
|
||||
|
||||
return meta.OrderByDescending(_meta => _meta.When)
|
||||
.Take(count)
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
}
|
@ -6,7 +6,7 @@
|
||||
<ApplicationIcon />
|
||||
<StartupObject />
|
||||
<PackageId>RaidMax.IW4MAdmin.SharedLibraryCore</PackageId>
|
||||
<Version>2.4.3</Version>
|
||||
<Version>2.4.6</Version>
|
||||
<Authors>RaidMax</Authors>
|
||||
<Company>Forever None</Company>
|
||||
<Configurations>Debug;Release;Prerelease</Configurations>
|
||||
@ -20,8 +20,8 @@
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<Description>Shared Library for IW4MAdmin</Description>
|
||||
<AssemblyVersion>2.4.3.0</AssemblyVersion>
|
||||
<FileVersion>2.4.3.0</FileVersion>
|
||||
<AssemblyVersion>2.4.6.0</AssemblyVersion>
|
||||
<FileVersion>2.4.6.0</FileVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Prerelease|AnyCPU'">
|
||||
@ -30,30 +30,33 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentValidation" Version="8.6.2" />
|
||||
<PackageReference Include="FluentValidation" Version="9.1.2" />
|
||||
<PackageReference Include="Humanizer.Core" Version="2.8.26" />
|
||||
<PackageReference Include="Humanizer.Core.pt" Version="2.8.26" />
|
||||
<PackageReference Include="Humanizer.Core.ru" Version="2.8.26" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.Cookies" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.1.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.3">
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.7" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.1.7" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.7">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="3.1.3" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.3" />
|
||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="3.1.3" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="3.1.3" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="3.1.3" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="3.1.3" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="3.1.7" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.7" />
|
||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="3.1.7" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="3.1.7" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="3.1.7" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="3.1.7" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="Npgsql" Version="4.1.3.1" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="3.1.3" />
|
||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="3.1.1" />
|
||||
<PackageReference Include="Npgsql" Version="4.1.4" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="3.1.4" />
|
||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="3.1.2" />
|
||||
<PackageReference Include="SimpleCrypto.NetCore" Version="1.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(Configuration)'=='Debug'">
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="3.1.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="3.1.7" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
|
||||
|
@ -1,5 +1,8 @@
|
||||
|
||||
using Humanizer;
|
||||
using Humanizer.Localisation;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using SharedLibraryCore.Dtos.Meta;
|
||||
using SharedLibraryCore.Helpers;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using System;
|
||||
@ -381,57 +384,6 @@ namespace SharedLibraryCore
|
||||
return !ip.HasValue ? "" : new IPAddress(BitConverter.GetBytes(ip.Value)).ToString();
|
||||
}
|
||||
|
||||
public static string GetTimePassed(DateTime start)
|
||||
{
|
||||
return GetTimePassed(start, true);
|
||||
}
|
||||
|
||||
public static string GetTimePassed(DateTime start, bool includeAgo)
|
||||
{
|
||||
TimeSpan Elapsed = DateTime.UtcNow - start;
|
||||
string ago = includeAgo ? $" {CurrentLocalization.LocalizationIndex["WEBFRONT_PENALTY_TEMPLATE_AGO"]}" : "";
|
||||
|
||||
if (Elapsed.TotalSeconds < 30)
|
||||
{
|
||||
return CurrentLocalization.LocalizationIndex["GLOBAL_TIME_JUSTNOW"] + ago;
|
||||
}
|
||||
if (Elapsed.TotalMinutes < 120)
|
||||
{
|
||||
if (Elapsed.TotalMinutes < 1.5)
|
||||
{
|
||||
return $"1 {CurrentLocalization.LocalizationIndex["GLOBAL_TIME_MINUTES"]}{ago}";
|
||||
}
|
||||
|
||||
return Math.Round(Elapsed.TotalMinutes, 0) + $" {CurrentLocalization.LocalizationIndex["GLOBAL_TIME_MINUTES"]}{ago}";
|
||||
}
|
||||
if (Elapsed.TotalHours <= 24)
|
||||
{
|
||||
if (Elapsed.TotalHours < 1.5)
|
||||
{
|
||||
return $"1 {CurrentLocalization.LocalizationIndex["GLOBAL_TIME_HOURS"]}{ago}";
|
||||
}
|
||||
|
||||
return Math.Round(Elapsed.TotalHours, 0) + $" { CurrentLocalization.LocalizationIndex["GLOBAL_TIME_HOURS"]}{ago}";
|
||||
}
|
||||
if (Elapsed.TotalDays <= 90)
|
||||
{
|
||||
if (Elapsed.TotalDays < 1.5)
|
||||
{
|
||||
return $"1 {CurrentLocalization.LocalizationIndex["GLOBAL_TIME_DAYS"]}{ago}";
|
||||
}
|
||||
|
||||
return Math.Round(Elapsed.TotalDays, 0) + $" {CurrentLocalization.LocalizationIndex["GLOBAL_TIME_DAYS"]}{ago}";
|
||||
}
|
||||
if (Elapsed.TotalDays <= 365)
|
||||
{
|
||||
return $"{Math.Round(Elapsed.TotalDays / 7)} {CurrentLocalization.LocalizationIndex["GLOBAL_TIME_WEEKS"]}{ago}";
|
||||
}
|
||||
else
|
||||
{
|
||||
return $"{Math.Round(Elapsed.TotalDays / 30, 0)} {CurrentLocalization.LocalizationIndex["GLOBAL_TIME_MONTHS"]}{ago}";
|
||||
}
|
||||
}
|
||||
|
||||
public static Game GetGame(string gameName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(gameName))
|
||||
@ -519,42 +471,6 @@ namespace SharedLibraryCore
|
||||
return new TimeSpan(1, 0, 0);
|
||||
}
|
||||
|
||||
public static string TimeSpanText(this TimeSpan span)
|
||||
{
|
||||
var loc = CurrentLocalization.LocalizationIndex;
|
||||
|
||||
if (span.TotalMinutes < 60)
|
||||
{
|
||||
return $"{span.Minutes} {loc["GLOBAL_TIME_MINUTES"]}";
|
||||
}
|
||||
else if (span.Hours >= 1 && span.TotalHours < 24)
|
||||
{
|
||||
return $"{span.Hours} {loc["GLOBAL_TIME_HOURS"]}";
|
||||
}
|
||||
else if (span.TotalDays >= 1 && span.TotalDays < 7)
|
||||
{
|
||||
return $"{span.Days} {loc["GLOBAL_TIME_DAYS"]}";
|
||||
}
|
||||
else if (span.TotalDays >= 7 && span.TotalDays < 90)
|
||||
{
|
||||
return $"{Math.Round(span.Days / 7.0, 0)} {loc["GLOBAL_TIME_WEEKS"]}";
|
||||
}
|
||||
else if (span.TotalDays >= 90 && span.TotalDays < 365)
|
||||
{
|
||||
return $"{Math.Round(span.Days / 30.0, 0)} {loc["GLOBAL_TIME_MONTHS"]}";
|
||||
}
|
||||
else if (span.TotalDays >= 365 && span.TotalDays < 36500)
|
||||
{
|
||||
return $"{Math.Round(span.Days / 365.0, 0)} {loc["GLOBAL_TIME_YEARS"]}";
|
||||
}
|
||||
else if (span.TotalDays >= 36500)
|
||||
{
|
||||
return loc["GLOBAL_TIME_FOREVER"];
|
||||
}
|
||||
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// returns a list of penalty types that should be shown across all profiles
|
||||
/// </summary>
|
||||
@ -932,7 +848,7 @@ namespace SharedLibraryCore
|
||||
/// <param name="penalty"></param>
|
||||
/// <param name="penaltyService"></param>
|
||||
/// <param name="logger"></param>
|
||||
/// <returns>true of the creat succeeds, false otherwise</returns>
|
||||
/// <returns>true of the create succeeds, false otherwise</returns>
|
||||
public static async Task<bool> TryCreatePenalty(this EFPenalty penalty, IEntityService<EFPenalty> penaltyService, ILogger logger)
|
||||
{
|
||||
try
|
||||
@ -970,6 +886,49 @@ namespace SharedLibraryCore
|
||||
|
||||
public static bool ShouldHideLevel(this Permission perm) => perm == Permission.Flagged;
|
||||
|
||||
/// <summary>
|
||||
/// parses translation string into tokens that are able to be formatted by the webfront
|
||||
/// </summary>
|
||||
/// <param name="translationKey">key for translation lookup</param>
|
||||
/// <returns></returns>
|
||||
public static WebfrontTranslationHelper[] SplitTranslationTokens(string translationKey)
|
||||
{
|
||||
string translationString = CurrentLocalization.LocalizationIndex[translationKey];
|
||||
var builder = new StringBuilder();
|
||||
var results = new List<WebfrontTranslationHelper>();
|
||||
|
||||
foreach (string word in translationString.Split(' '))
|
||||
{
|
||||
string finalWord = word;
|
||||
|
||||
if ((word.StartsWith("{{") && !word.EndsWith("}}")) ||
|
||||
(builder.Length > 0 && !word.EndsWith("}}")))
|
||||
{
|
||||
builder.Append($"{word} ");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (builder.Length > 0)
|
||||
{
|
||||
builder.Append(word);
|
||||
finalWord = builder.ToString();
|
||||
builder.Clear();
|
||||
}
|
||||
|
||||
var match = Regex.Match(finalWord, @"{{([^}|^-]+)(?:->)([^}]+)}}|{{([^}]+)}}");
|
||||
bool isInterpolation = match.Success;
|
||||
|
||||
results.Add(new WebfrontTranslationHelper
|
||||
{
|
||||
IsInterpolation = isInterpolation,
|
||||
MatchValue = isInterpolation ? match.Groups[3].Length > 0 ? match.Groups[3].ToString() : match.Groups[1].ToString() : finalWord,
|
||||
TranslationValue = isInterpolation && match.Groups[2].Length > 0 ? match.Groups[2].ToString() : ""
|
||||
});
|
||||
}
|
||||
|
||||
return results.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// indicates if running in development mode
|
||||
/// </summary>
|
||||
@ -991,5 +950,27 @@ namespace SharedLibraryCore
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// wrapper method for humanizee that uses current current culture
|
||||
/// </summary>
|
||||
public static string HumanizeForCurrentCulture(this TimeSpan timeSpan, int precision = 1, TimeUnit maxUnit = TimeUnit.Week,
|
||||
TimeUnit minUnit = TimeUnit.Millisecond, string collectionSeparator = ", ", bool toWords = false)
|
||||
{
|
||||
return timeSpan.Humanize(precision, CurrentLocalization.Culture, maxUnit, minUnit, collectionSeparator, toWords);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// wrapper method for humanizee that uses current current culture
|
||||
/// </summary>
|
||||
public static string HumanizeForCurrentCulture(this DateTime input, bool utcDate = true, DateTime? dateToCompareAgainst = null, CultureInfo culture = null)
|
||||
{
|
||||
return input.Humanize(utcDate, dateToCompareAgainst, CurrentLocalization.Culture);
|
||||
}
|
||||
|
||||
public static string ToTranslatedName(this MetaType metaType)
|
||||
{
|
||||
return CurrentLocalization.LocalizationIndex[$"META_TYPE_{metaType.ToString().ToUpper()}_NAME"];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,10 +6,10 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FakeItEasy" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
|
||||
<PackageReference Include="FakeItEasy" Version="6.2.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.0" />
|
||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.16.1">
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
@ -43,13 +43,14 @@ namespace ApplicationTests
|
||||
.AddSingleton(A.Fake<IParserRegexFactory>())
|
||||
.AddSingleton<DataFileLoader>()
|
||||
.AddSingleton(A.Fake<IGameLogReaderFactory>())
|
||||
.AddSingleton(A.Fake<IMetaService>())
|
||||
.AddSingleton(eventHandler)
|
||||
.AddSingleton(ConfigurationGenerators.CreateApplicationConfiguration())
|
||||
.AddSingleton(ConfigurationGenerators.CreateCommandConfiguration())
|
||||
.AddSingleton<IConfigurationHandler<ApplicationConfiguration>, ApplicationConfigurationHandlerMock>();
|
||||
|
||||
serviceCollection.AddSingleton(_sp => new IW4MServer(_sp.GetRequiredService<IManager>(), ConfigurationGenerators.CreateServerConfiguration(),
|
||||
_sp.GetRequiredService<ITranslationLookup>(), _sp.GetRequiredService<IRConConnectionFactory>(), _sp.GetRequiredService<IGameLogReaderFactory>())
|
||||
_sp.GetRequiredService<ITranslationLookup>(), _sp.GetRequiredService<IRConConnectionFactory>(), _sp.GetRequiredService<IGameLogReaderFactory>(), _sp.GetRequiredService<IMetaService>())
|
||||
{
|
||||
RconParser = _sp.GetRequiredService<IRConParser>()
|
||||
});
|
||||
|
@ -21,9 +21,9 @@ namespace ApplicationTests.Fixtures
|
||||
CurrentServer = currentServer
|
||||
};
|
||||
|
||||
public static EFClient CreateDatabaseClient(bool hasIp = true) => new EFClient()
|
||||
public static EFClient CreateDatabaseClient(bool hasIp = true, int clientId = 1) => new EFClient()
|
||||
{
|
||||
ClientId = 1,
|
||||
ClientId = clientId,
|
||||
ClientNumber = -1,
|
||||
AliasLinkId = 1,
|
||||
Level = EFClient.Permission.User,
|
||||
|
28
Tests/ApplicationTests/Fixtures/PenaltyGenerators.cs
Normal file
28
Tests/ApplicationTests/Fixtures/PenaltyGenerators.cs
Normal file
@ -0,0 +1,28 @@
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace ApplicationTests.Fixtures
|
||||
{
|
||||
class PenaltyGenerators
|
||||
{
|
||||
public static EFPenalty Create(EFPenalty.PenaltyType type = EFPenalty.PenaltyType.Ban, EFClient originClient = null, EFClient targetClient = null, DateTime? occurs = null, string reason = null)
|
||||
{
|
||||
originClient ??= ClientGenerators.CreateDatabaseClient(clientId: 1);
|
||||
targetClient ??= ClientGenerators.CreateDatabaseClient(clientId: 2);
|
||||
occurs ??= DateTime.UtcNow;
|
||||
reason ??= "test";
|
||||
|
||||
return new EFPenalty()
|
||||
{
|
||||
Offender = targetClient,
|
||||
Punisher = originClient,
|
||||
When = occurs.Value,
|
||||
Offense = reason,
|
||||
Type = type,
|
||||
LinkId = targetClient.AliasLinkId
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
209
Tests/ApplicationTests/QueryHelperTests.cs
Normal file
209
Tests/ApplicationTests/QueryHelperTests.cs
Normal file
@ -0,0 +1,209 @@
|
||||
using ApplicationTests.Fixtures;
|
||||
using IW4MAdmin.Application.Meta;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NUnit.Framework;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using SharedLibraryCore.Dtos;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using SharedLibraryCore.QueryHelper;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ApplicationTests
|
||||
{
|
||||
public class QueryHelperTests
|
||||
{
|
||||
private IServiceProvider serviceProvider;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
serviceProvider = new ServiceCollection().BuildBase()
|
||||
.AddSingleton<AdministeredPenaltyResourceQueryHelper>()
|
||||
.AddSingleton<ReceivedPenaltyResourceQueryHelper>()
|
||||
.AddSingleton<UpdatedAliasResourceQueryHelper>()
|
||||
.BuildServiceProvider();
|
||||
|
||||
SetupPenalties();
|
||||
SetupAliases();
|
||||
}
|
||||
|
||||
private void SetupAliases()
|
||||
{
|
||||
using var ctx = serviceProvider.GetRequiredService<IDatabaseContextFactory>().CreateContext();
|
||||
|
||||
var client = ClientGenerators.CreateDatabaseClient();
|
||||
|
||||
var aliases = new[]
|
||||
{
|
||||
new EFAlias()
|
||||
{
|
||||
LinkId = client.AliasLinkId,
|
||||
Name = "Test1",
|
||||
IPAddress = -1,
|
||||
DateAdded = DateTime.UtcNow.AddMinutes(-1)
|
||||
},
|
||||
new EFAlias()
|
||||
{
|
||||
LinkId = client.AliasLinkId,
|
||||
Name = "Test2",
|
||||
IPAddress = -1,
|
||||
DateAdded = DateTime.UtcNow
|
||||
}
|
||||
};
|
||||
|
||||
ctx.Aliases.AddRange(aliases);
|
||||
ctx.SaveChanges();
|
||||
}
|
||||
|
||||
private void SetupPenalties()
|
||||
{
|
||||
using var ctx = serviceProvider.GetRequiredService<IDatabaseContextFactory>().CreateContext();
|
||||
|
||||
var firstPenalty = PenaltyGenerators.Create(occurs: DateTime.UtcNow.AddMinutes(-2), reason: "first");
|
||||
var secondPenalty = PenaltyGenerators.Create(occurs: DateTime.UtcNow.AddMinutes(-1), reason: "second", originClient: firstPenalty.Punisher, targetClient: firstPenalty.Offender);
|
||||
var linkedPenalty = PenaltyGenerators.Create(occurs: DateTime.UtcNow, reason: "linked", originClient: firstPenalty.Punisher, targetClient: ClientGenerators.CreateDatabaseClient(clientId: 3));
|
||||
|
||||
ctx.Add(firstPenalty);
|
||||
ctx.Add(secondPenalty);
|
||||
ctx.Add(linkedPenalty);
|
||||
ctx.SaveChanges();
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void Teardown()
|
||||
{
|
||||
using var ctx = serviceProvider.GetRequiredService<IDatabaseContextFactory>().CreateContext();
|
||||
ctx.Database.EnsureDeleted();
|
||||
}
|
||||
|
||||
#region ADMINISTERED PENALTIES
|
||||
[Test]
|
||||
public async Task Test_AdministeredPenaltyResourceQueryHelper_QueryResource_TakesAppropriateCount()
|
||||
{
|
||||
var queryHelper = serviceProvider.GetRequiredService<AdministeredPenaltyResourceQueryHelper>();
|
||||
|
||||
var request = new ClientPaginationRequest
|
||||
{
|
||||
Count = 1,
|
||||
Before = DateTime.UtcNow,
|
||||
ClientId = 1
|
||||
};
|
||||
|
||||
var result = await queryHelper.QueryResource(request);
|
||||
|
||||
Assert.AreEqual(request.Count, result.RetrievedResultCount);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Test_AdministeredPenaltyResourceQueryHelper_QueryResource_OrdersDescending()
|
||||
{
|
||||
var queryHelper = serviceProvider.GetRequiredService<AdministeredPenaltyResourceQueryHelper>();
|
||||
|
||||
var request = new ClientPaginationRequest
|
||||
{
|
||||
Count = 2,
|
||||
Before = DateTime.UtcNow,
|
||||
ClientId = 1,
|
||||
Direction = SortDirection.Descending
|
||||
};
|
||||
|
||||
var result = await queryHelper.QueryResource(request);
|
||||
|
||||
Assert.Less(result.Results.Last().When.ToFileTimeUtc(), result.Results.First().When.ToFileTimeUtc());
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region RECEIVED PENALTIES
|
||||
[Test]
|
||||
public async Task Test_ReceivedPenaltyResourceQueryHelper_QueryResource_TakesAppropriateCount()
|
||||
{
|
||||
var queryHelper = serviceProvider.GetRequiredService<ReceivedPenaltyResourceQueryHelper>();
|
||||
|
||||
var request = new ClientPaginationRequest
|
||||
{
|
||||
Count = 1,
|
||||
Before = DateTime.UtcNow,
|
||||
ClientId = 2
|
||||
};
|
||||
|
||||
var result = await queryHelper.QueryResource(request);
|
||||
|
||||
Assert.AreEqual(request.Count, result.RetrievedResultCount);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Test_ReceivedPenaltyResourceQueryHelper_QueryResource_OrdersDescending()
|
||||
{
|
||||
var queryHelper = serviceProvider.GetRequiredService<ReceivedPenaltyResourceQueryHelper>();
|
||||
|
||||
var request = new ClientPaginationRequest
|
||||
{
|
||||
Count = 2,
|
||||
Before = DateTime.UtcNow,
|
||||
ClientId = 2,
|
||||
Direction = SortDirection.Descending
|
||||
};
|
||||
|
||||
var result = await queryHelper.QueryResource(request);
|
||||
|
||||
Assert.Less(result.Results.Last().When.ToFileTimeUtc(), result.Results.First().When.ToFileTimeUtc());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Test_ReceivedPenaltyResourceQueryHelper_QueryResource_IncludesLinkedPenalty()
|
||||
{
|
||||
var queryHelper = serviceProvider.GetRequiredService<ReceivedPenaltyResourceQueryHelper>();
|
||||
|
||||
var request = new ClientPaginationRequest
|
||||
{
|
||||
Count = 3,
|
||||
Before = DateTime.UtcNow,
|
||||
ClientId = 3,
|
||||
};
|
||||
|
||||
var result = await queryHelper.QueryResource(request);
|
||||
|
||||
Assert.AreEqual(request.Count, result.RetrievedResultCount);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ALIAS UPDATE
|
||||
[Test]
|
||||
public async Task Test_UpdatedAliasResourceQueryHelper_QueryResource_TakesAppropriateCount()
|
||||
{
|
||||
var queryHelper = serviceProvider.GetRequiredService<UpdatedAliasResourceQueryHelper>();
|
||||
|
||||
var request = new ClientPaginationRequest
|
||||
{
|
||||
Count = 1,
|
||||
Before = DateTime.UtcNow,
|
||||
ClientId = 1
|
||||
};
|
||||
|
||||
var result = await queryHelper.QueryResource(request);
|
||||
|
||||
Assert.AreEqual(request.Count, result.RetrievedResultCount);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Test_UpdatedAliasResourceQueryHelper_QueryResource_OrdersDescending()
|
||||
{
|
||||
var queryHelper = serviceProvider.GetRequiredService<UpdatedAliasResourceQueryHelper>();
|
||||
|
||||
var request = new ClientPaginationRequest
|
||||
{
|
||||
Count = 2,
|
||||
Before = DateTime.UtcNow,
|
||||
ClientId = 1,
|
||||
Direction = SortDirection.Descending
|
||||
};
|
||||
|
||||
var result = await queryHelper.QueryResource(request);
|
||||
|
||||
Assert.Less(result.Results.Last().When.ToFileTimeUtc(), result.Results.First().When.ToFileTimeUtc());
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
@ -34,7 +34,7 @@ namespace ApplicationTests
|
||||
var mgr = A.Fake<IManager>();
|
||||
var server = new IW4MServer(mgr,
|
||||
new SharedLibraryCore.Configuration.ServerConfiguration() { IPAddress = "127.0.0.1", Port = 28960 },
|
||||
A.Fake<ITranslationLookup>(), A.Fake<IRConConnectionFactory>(), A.Fake<IGameLogReaderFactory>());
|
||||
A.Fake<ITranslationLookup>(), A.Fake<IRConConnectionFactory>(), A.Fake<IGameLogReaderFactory>(), A.Fake<IMetaService>());
|
||||
|
||||
var parser = new BaseEventParser(A.Fake<IParserRegexFactory>(), A.Fake<ILogger>(), A.Fake<ApplicationConfiguration>());
|
||||
parser.Configuration.GuidNumberStyle = System.Globalization.NumberStyles.Integer;
|
||||
@ -60,7 +60,7 @@ namespace ApplicationTests
|
||||
|
||||
var server = new IW4MServer(mgr,
|
||||
new SharedLibraryCore.Configuration.ServerConfiguration() { IPAddress = "127.0.0.1", Port = 28960 },
|
||||
A.Fake<ITranslationLookup>(), A.Fake<IRConConnectionFactory>(), A.Fake<IGameLogReaderFactory>());
|
||||
A.Fake<ITranslationLookup>(), A.Fake<IRConConnectionFactory>(), A.Fake<IGameLogReaderFactory>(), A.Fake<IMetaService>());
|
||||
|
||||
var parser = new BaseEventParser(A.Fake<IParserRegexFactory>(), A.Fake<ILogger>(), A.Fake<ApplicationConfiguration>());
|
||||
parser.Configuration.GuidNumberStyle = System.Globalization.NumberStyles.Integer;
|
||||
|
@ -72,7 +72,7 @@ namespace ApplicationTests
|
||||
var server = new IW4MServer(mgr,
|
||||
new SharedLibraryCore.Configuration.ServerConfiguration() { IPAddress = "127.0.0.1", Port = 28960 },
|
||||
A.Fake<ITranslationLookup>(),
|
||||
A.Fake<IRConConnectionFactory>(), A.Fake<IGameLogReaderFactory>());
|
||||
A.Fake<IRConConnectionFactory>(), A.Fake<IGameLogReaderFactory>(), A.Fake<IMetaService>());
|
||||
|
||||
var parser = new BaseEventParser(A.Fake<IParserRegexFactory>(), A.Fake<ILogger>(), A.Fake<ApplicationConfiguration>());
|
||||
parser.Configuration.GuidNumberStyle = System.Globalization.NumberStyles.Integer;
|
||||
|
@ -242,7 +242,7 @@ namespace WebfrontCore.Controllers
|
||||
{
|
||||
var info = new ActionInfo()
|
||||
{
|
||||
ActionButtonLabel = "Generate",
|
||||
ActionButtonLabel = Localization["WEBFRONT_ACTION_LABEL_GENERATE_TOKEN"],
|
||||
Name = "GenerateLoginToken",
|
||||
Action = "GenerateLoginTokenAsync",
|
||||
Inputs = new List<InputInfo>()
|
||||
|
@ -27,7 +27,7 @@ namespace WebfrontCore.Controllers
|
||||
ViewBag.Title = _translationLookup["WEBFRONT_NAV_AUDIT_LOG"];
|
||||
ViewBag.InitialOffset = DEFAULT_COUNT;
|
||||
|
||||
var auditItems = await _auditInformationRepository.ListAuditInformation(new PaginationInfo()
|
||||
var auditItems = await _auditInformationRepository.ListAuditInformation(new PaginationRequest()
|
||||
{
|
||||
Count = DEFAULT_COUNT
|
||||
});
|
||||
@ -35,7 +35,7 @@ namespace WebfrontCore.Controllers
|
||||
return View(auditItems);
|
||||
}
|
||||
|
||||
public async Task<IActionResult> ListAuditLog([FromQuery] PaginationInfo paginationInfo)
|
||||
public async Task<IActionResult> ListAuditLog([FromQuery] PaginationRequest paginationInfo)
|
||||
{
|
||||
ViewBag.EnableColorCodes = Manager.GetApplicationSettings().Configuration().EnableColorCodes;
|
||||
var auditItems = await _auditInformationRepository.ListAuditInformation(paginationInfo);
|
||||
|
@ -2,12 +2,15 @@
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using SharedLibraryCore.Dtos;
|
||||
using SharedLibraryCore.Dtos.Meta.Responses;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using SharedLibraryCore.QueryHelper;
|
||||
using SharedLibraryCore.Services;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using WebfrontCore.ViewComponents;
|
||||
using static SharedLibraryCore.Database.Models.EFClient;
|
||||
using static SharedLibraryCore.Database.Models.EFPenalty;
|
||||
|
||||
@ -15,12 +18,14 @@ namespace WebfrontCore.Controllers
|
||||
{
|
||||
public class ClientController : BaseController
|
||||
{
|
||||
public ClientController(IManager manager) : base(manager)
|
||||
{
|
||||
private readonly IMetaService _metaService;
|
||||
|
||||
public ClientController(IManager manager, IMetaService metaService) : base(manager)
|
||||
{
|
||||
_metaService = metaService;
|
||||
}
|
||||
|
||||
public async Task<IActionResult> ProfileAsync(int id)
|
||||
public async Task<IActionResult> ProfileAsync(int id, MetaType? metaFilterType)
|
||||
{
|
||||
var client = await Manager.GetClientService().Get(id);
|
||||
|
||||
@ -29,8 +34,8 @@ namespace WebfrontCore.Controllers
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var activePenalties = (await Manager.GetPenaltyService().GetActivePenaltiesAsync(client.AliasLinkId, client.IPAddress))
|
||||
.Where(_penalty => _penalty.Type != PenaltyType.Flag);
|
||||
var activePenalties = (await Manager.GetPenaltyService().GetActivePenaltiesAsync(client.AliasLinkId, client.IPAddress));
|
||||
|
||||
|
||||
int displayLevelInt = (int)client.Level;
|
||||
string displayLevel = client.Level.ToLocalizedLevelName();
|
||||
@ -49,7 +54,7 @@ namespace WebfrontCore.Controllers
|
||||
ClientId = client.ClientId,
|
||||
IPAddress = client.IPAddressString,
|
||||
NetworkId = client.NetworkId,
|
||||
Meta = new List<ProfileMeta>(),
|
||||
Meta = new List<InformationResponse>(),
|
||||
Aliases = client.AliasLink.Children
|
||||
.Select(_alias => _alias.Name)
|
||||
.GroupBy(_alias => _alias.StripColors())
|
||||
@ -65,38 +70,32 @@ namespace WebfrontCore.Controllers
|
||||
.Prepend(client.CurrentAlias.IPAddress.ConvertIPtoString())
|
||||
.Distinct()
|
||||
.ToList(),
|
||||
HasActivePenalty = activePenalties.Count() > 0,
|
||||
ActivePenaltyType = activePenalties.Count() > 0 ? activePenalties.First().Type.ToString() : null,
|
||||
HasActivePenalty = activePenalties.Any(_penalty => _penalty.Type != PenaltyType.Flag),
|
||||
Online = Manager.GetActiveClients().FirstOrDefault(c => c.ClientId == client.ClientId) != null,
|
||||
TimeOnline = (DateTime.UtcNow - client.LastConnection).TimeSpanText(),
|
||||
LinkedAccounts = client.LinkedAccounts
|
||||
TimeOnline = (DateTime.UtcNow - client.LastConnection).HumanizeForCurrentCulture(),
|
||||
LinkedAccounts = client.LinkedAccounts,
|
||||
MetaFilterType = metaFilterType
|
||||
};
|
||||
|
||||
var meta = await MetaService.GetRuntimeMeta(client.ClientId, 0, 1, DateTime.UtcNow);
|
||||
var gravatar = await new MetaService().GetPersistentMeta("GravatarEmail", client);
|
||||
var meta = await _metaService.GetRuntimeMeta<InformationResponse>(new ClientPaginationRequest
|
||||
{
|
||||
ClientId = client.ClientId,
|
||||
Before = DateTime.UtcNow
|
||||
}, MetaType.Information);
|
||||
|
||||
var gravatar = await _metaService.GetPersistentMeta("GravatarEmail", client);
|
||||
if (gravatar != null)
|
||||
{
|
||||
clientDto.Meta.Add(new ProfileMeta()
|
||||
clientDto.Meta.Add(new InformationResponse()
|
||||
{
|
||||
Key = "GravatarEmail",
|
||||
Type = ProfileMeta.MetaType.Other,
|
||||
Type = MetaType.Other,
|
||||
Value = gravatar.Value
|
||||
});
|
||||
}
|
||||
|
||||
var currentPenalty = activePenalties.FirstOrDefault();
|
||||
|
||||
if (currentPenalty != null && currentPenalty.Type == PenaltyType.TempBan)
|
||||
{
|
||||
clientDto.Meta.Add(new ProfileMeta()
|
||||
{
|
||||
Key = Localization["WEBFRONT_CLIENT_META_REMAINING_BAN"],
|
||||
Value = ((currentPenalty.Expires - DateTime.UtcNow) ?? new TimeSpan()).TimeSpanText(),
|
||||
When = currentPenalty.When
|
||||
});
|
||||
}
|
||||
|
||||
clientDto.Meta.AddRange(Authorized ? meta : meta.Where(m => !m.Sensitive));
|
||||
clientDto.ActivePenalty = activePenalties.OrderByDescending(_penalty => _penalty.Type).FirstOrDefault();
|
||||
clientDto.Meta.AddRange(Authorized ? meta : meta.Where(m => !m.IsSensitive));
|
||||
|
||||
string strippedName = clientDto.Name.StripColors();
|
||||
ViewBag.Title = strippedName.Substring(strippedName.Length - 1).ToLower()[0] == 's' ?
|
||||
@ -160,14 +159,17 @@ namespace WebfrontCore.Controllers
|
||||
return View("Find/Index", clientsDto);
|
||||
}
|
||||
|
||||
public async Task<IActionResult> Meta(int id, int count, int offset, DateTime? startAt)
|
||||
public async Task<IActionResult> Meta(int id, int count, int offset, long? startAt, MetaType? metaFilterType)
|
||||
{
|
||||
IEnumerable<ProfileMeta> meta = await MetaService.GetRuntimeMeta(id, startAt == null ? offset : 0, count, startAt ?? DateTime.UtcNow);
|
||||
var request = new ClientPaginationRequest
|
||||
{
|
||||
ClientId = id,
|
||||
Count = count,
|
||||
Offset = offset,
|
||||
Before = DateTime.FromFileTimeUtc(startAt ?? DateTime.UtcNow.ToFileTimeUtc())
|
||||
};
|
||||
|
||||
if (!Authorized)
|
||||
{
|
||||
meta = meta.Where(_meta => !_meta.Sensitive);
|
||||
}
|
||||
var meta = await ProfileMetaListViewComponent.GetClientMeta(_metaService, metaFilterType, Client.Level, request);
|
||||
|
||||
if (meta.Count() == 0)
|
||||
{
|
||||
|
@ -19,6 +19,10 @@ using Stats.Dtos;
|
||||
using Stats.Helpers;
|
||||
using StatsWeb;
|
||||
using StatsWeb.Dtos;
|
||||
/*using Stats.Dtos;
|
||||
using Stats.Helpers;
|
||||
using StatsWeb;
|
||||
using StatsWeb.Dtos;*/
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@ -125,6 +129,7 @@ namespace WebfrontCore
|
||||
services.AddSingleton(Program.ApplicationServiceProvider.GetService<ITranslationLookup>());
|
||||
services.AddSingleton(Program.ApplicationServiceProvider.GetService<SharedLibraryCore.Interfaces.ILogger>());
|
||||
services.AddSingleton(Program.ApplicationServiceProvider.GetService<IEnumerable<IManagerCommand>>());
|
||||
services.AddSingleton(Program.ApplicationServiceProvider.GetService<IMetaService>());
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
|
@ -1,7 +1,8 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using SharedLibraryCore.Dtos;
|
||||
using SharedLibraryCore.Services;
|
||||
using SharedLibraryCore.Dtos.Meta.Responses;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using SharedLibraryCore.QueryHelper;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@ -12,18 +13,70 @@ namespace WebfrontCore.ViewComponents
|
||||
{
|
||||
public class ProfileMetaListViewComponent : ViewComponent
|
||||
{
|
||||
public async Task<IViewComponentResult> InvokeAsync(int clientId, int count, int offset, DateTime? startAt)
|
||||
private readonly IMetaService _metaService;
|
||||
|
||||
public ProfileMetaListViewComponent(IMetaService metaService)
|
||||
{
|
||||
_metaService = metaService;
|
||||
}
|
||||
|
||||
public async Task<IViewComponentResult> InvokeAsync(int clientId, int count, int offset, DateTime? startAt, MetaType? metaType)
|
||||
{
|
||||
var level = (EFClient.Permission)Enum.Parse(typeof(EFClient.Permission), UserClaimsPrincipal.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Role)?.Value ?? "User");
|
||||
|
||||
IEnumerable<ProfileMeta> meta = await MetaService.GetRuntimeMeta(clientId, offset, count, startAt ?? DateTime.UtcNow);
|
||||
|
||||
if (level < EFClient.Permission.Trusted)
|
||||
var request = new ClientPaginationRequest
|
||||
{
|
||||
meta = meta.Where(_meta => !_meta.Sensitive);
|
||||
}
|
||||
ClientId = clientId,
|
||||
Count = count,
|
||||
Offset = offset,
|
||||
Before = startAt,
|
||||
};
|
||||
|
||||
var meta = await GetClientMeta(_metaService, metaType, level, request);
|
||||
ViewBag.Localization = SharedLibraryCore.Utilities.CurrentLocalization.LocalizationIndex;
|
||||
|
||||
return View("_List", meta);
|
||||
}
|
||||
|
||||
public static async Task<IEnumerable<IClientMeta>> GetClientMeta(IMetaService metaService, MetaType? metaType, EFClient.Permission level, ClientPaginationRequest request)
|
||||
{
|
||||
IEnumerable<IClientMeta> meta = null;
|
||||
|
||||
if (metaType == null) // all types
|
||||
{
|
||||
meta = await metaService.GetRuntimeMeta(request);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
switch (metaType)
|
||||
{
|
||||
case MetaType.Information:
|
||||
meta = await metaService.GetRuntimeMeta<InformationResponse>(request, metaType.Value);
|
||||
break;
|
||||
case MetaType.AliasUpdate:
|
||||
meta = await metaService.GetRuntimeMeta<UpdatedAliasResponse>(request, metaType.Value);
|
||||
break;
|
||||
case MetaType.ChatMessage:
|
||||
meta = await metaService.GetRuntimeMeta<MessageResponse>(request, metaType.Value);
|
||||
break;
|
||||
case MetaType.Penalized:
|
||||
meta = await metaService.GetRuntimeMeta<AdministeredPenaltyResponse>(request, metaType.Value);
|
||||
break;
|
||||
case MetaType.ReceivedPenalty:
|
||||
meta = await metaService.GetRuntimeMeta<ReceivedPenaltyResponse>(request, metaType.Value);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (level < EFClient.Permission.Trusted)
|
||||
{
|
||||
meta = meta.Where(_meta => !_meta.IsSensitive);
|
||||
}
|
||||
|
||||
return meta;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +1,17 @@
|
||||
@model SharedLibraryCore.Dtos.PlayerInfo
|
||||
@using SharedLibraryCore.Database.Models
|
||||
@using SharedLibraryCore.Interfaces
|
||||
@using SharedLibraryCore
|
||||
@model SharedLibraryCore.Dtos.PlayerInfo
|
||||
@{
|
||||
string match = System.Text.RegularExpressions.Regex.Match(Model.Name.ToUpper(), "[A-Z]").Value;
|
||||
string shortCode = match == string.Empty ? "?" : match;
|
||||
var loc = SharedLibraryCore.Utilities.CurrentLocalization.LocalizationIndex;
|
||||
string gravatarUrl = Model.Meta.FirstOrDefault(m => m.Key == "GravatarEmail")?.Value;
|
||||
bool isTempBanned = Model.ActivePenaltyType == "TempBan";
|
||||
bool isFlagged = Model.LevelInt == (int)SharedLibraryCore.Database.Models.EFClient.Permission.Flagged;
|
||||
bool isPermBanned = Model.LevelInt == (int)SharedLibraryCore.Database.Models.EFClient.Permission.Banned;
|
||||
var informationMeta = Model.Meta
|
||||
.Where(_meta => _meta.Type == SharedLibraryCore.Dtos.ProfileMeta.MetaType.Information)
|
||||
.OrderBy(_meta => _meta.Order)
|
||||
.GroupBy(_meta => _meta.Column)
|
||||
.OrderBy(_grouping => _grouping.Key);
|
||||
bool isTempBanned = Model.ActivePenalty?.Type == EFPenalty.PenaltyType.TempBan;
|
||||
string translationKey = $"WEBFRONT_PROFILE_{Model.ActivePenalty?.Type.ToString().ToUpper()}_INFO";
|
||||
var ignoredMetaTypes = new[] { MetaType.Information, MetaType.Other, MetaType.QuickMessage };
|
||||
}
|
||||
|
||||
<div id="profile_wrapper" class="pb-3 row d-flex flex-column flex-lg-row">
|
||||
@ -23,7 +23,7 @@
|
||||
}
|
||||
</div>
|
||||
<!-- Name/Level Column -->
|
||||
<div class="d-block d-lg-inline-flex flex-column flex-fill text-center text-lg-left pb-3 pb-lg-0 pt-3 pt-lg-0 pl-3 pr-3">
|
||||
<div class="w-75 d-block d-lg-inline-flex flex-column flex-fill text-center text-lg-left pb-3 pb-lg-0 pt-3 pt-lg-0 pl-3 pr-3 ml-auto mr-auto" style="overflow-wrap: anywhere">
|
||||
<div class="mt-n2 flex-fill d-block d-lg-inline-flex">
|
||||
<div id="profile_name" class="client-name h1 mb-0"><color-code value="@Model.Name" allow="@ViewBag.EnableColorCodes"></color-code></div>
|
||||
@if (ViewBag.Authorized)
|
||||
@ -31,6 +31,7 @@
|
||||
<div id="profile_aliases_btn" class="oi oi-caret-bottom h3 ml-0 ml-lg-2 mb-0 pt-lg-2 mt-lg-1"></div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@if (ViewBag.Authorized)
|
||||
{
|
||||
<div id="profile_aliases" class="text-muted pt-0 pt-lg-2 pb-2">
|
||||
@ -49,9 +50,34 @@
|
||||
}
|
||||
</div>
|
||||
}
|
||||
<div id="profile_level" class="font-weight-bold h4 mb-0 level-color-@Model.LevelInt @(isTempBanned ? "penalties-color-tempban" : "")">
|
||||
@Model.Level @(isTempBanned ? $"({loc["WEBFRONT_PROFILE_TEMPBAN"]})" : "")
|
||||
@if (Model.ActivePenalty != null)
|
||||
{
|
||||
<div class="font-weight-bold h4 mb-0 penalties-color-@Model.ActivePenalty.Type.ToString().ToLower()">
|
||||
@foreach (var result in Utilities.SplitTranslationTokens(translationKey))
|
||||
{
|
||||
switch (result.MatchValue)
|
||||
{
|
||||
case "reason":
|
||||
<span class="text-white 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 : Model.ActivePenalty.Offense)</span>
|
||||
break;
|
||||
case "time":
|
||||
<span class="text-white font-weight-lighter">
|
||||
@Utilities.HumanizeForCurrentCulture(Model.ActivePenalty.Expires.Value - DateTime.UtcNow)
|
||||
</span>
|
||||
break;
|
||||
default:
|
||||
<span>@result.MatchValue</span>
|
||||
break;
|
||||
}
|
||||
}
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div id="profile_level" class="font-weight-bold h4 mb-0 level-color-@Model.LevelInt">
|
||||
@Model.Level
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
@if (ViewBag.Authorized)
|
||||
{
|
||||
@ -89,23 +115,32 @@
|
||||
</div>
|
||||
|
||||
<div id="profile_info" class="row d-block d-lg-flex flex-row border-bottom border-top pt-2 pb-2">
|
||||
@foreach (var metaColumn in informationMeta)
|
||||
{
|
||||
<div class="text-center text-lg-left mr-0 mr-lg-4">
|
||||
@foreach (var meta in metaColumn)
|
||||
{
|
||||
<div class="profile-meta-entry" title="@(string.IsNullOrEmpty(meta.Extra) ? meta.Key : meta.Extra)">
|
||||
<span class="profile-meta-value text-primary"><color-code value="@meta.Value" allow="@ViewBag.EnableColorCodes"></color-code></span>
|
||||
<span class="profile-meta-title text-muted"> @meta.Key</span>
|
||||
<partial name="Meta/_Information.cshtml" model="@Model.Meta" />
|
||||
</div>
|
||||
|
||||
<div class="row border-bottom">
|
||||
<div class="text-center bg-dark p-2 pl-3 pr-4 text-muted col-12 col-md-auto" id="filter_meta_container_button">
|
||||
<span class="oi oi-sort-ascending"></span>
|
||||
<a>Filter Meta</a>
|
||||
</div>
|
||||
<div class="d-none d-md-flex flex-fill" id="filter_meta_container">
|
||||
@foreach (MetaType type in Enum.GetValues(typeof(MetaType)))
|
||||
{
|
||||
if (!ignoredMetaTypes.Contains(type))
|
||||
{
|
||||
<a asp-action="ProfileAsync" asp-controller="Client"
|
||||
class="nav-link p-2 pl-3 pr-3 text-center col-12 col-md-auto text-md-left @(Model.MetaFilterType.HasValue && Model.MetaFilterType.Value.ToString() == type.ToString() ? "btn-primary text-white" : "text-muted")"
|
||||
asp-route-id="@Model.ClientId"
|
||||
asp-route-metaFilterType="@type"
|
||||
data-meta-type="@type">@type.ToTranslatedName()</a>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="row d-md-flex pt-2">
|
||||
<div id="profile_events" class="text-muted text-left ml-sm-0">
|
||||
@await Component.InvokeAsync("ProfileMetaList", new { clientId = Model.ClientId, count = 30, offset = 0 })
|
||||
<div id="profile_events" class="text-muted text-left pl-4 pr-4 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>
|
||||
|
||||
@ -122,5 +157,5 @@
|
||||
<script type="text/javascript" src="~/js/loader.js"></script>
|
||||
<script type="text/javascript" src="~/js/profile.js"></script>
|
||||
</environment>
|
||||
<script>initLoader('/Client/Meta/@Model.ClientId', '#profile_events', 30);</script>
|
||||
<script>initLoader('/Client/Meta/@Model.ClientId', '#profile_events', 30, 30, [{ 'name': 'metaFilterType', 'value': '@Model.MetaFilterType' }]);</script>
|
||||
}
|
||||
|
@ -0,0 +1,53 @@
|
||||
@using SharedLibraryCore.Dtos.Meta.Responses
|
||||
@model AdministeredPenaltyResponse
|
||||
@{
|
||||
string localizationKey = $"WEBFRONT_CLIENT_META_PENALIZED_{Model.PenaltyType.ToString().ToUpper()}_V2";
|
||||
}
|
||||
|
||||
<div class="d-inline">
|
||||
@foreach (var match in Utilities.SplitTranslationTokens(localizationKey))
|
||||
{
|
||||
if (match.IsInterpolation)
|
||||
{
|
||||
if (match.MatchValue == "action")
|
||||
{
|
||||
<span class="penalties-color-@Model.PenaltyType.ToString().ToLower()">@match.TranslationValue</span>
|
||||
}
|
||||
|
||||
else if (match.MatchValue == "offender")
|
||||
{
|
||||
<span class="text-highlight">
|
||||
<a class="link-inverse" href="@Model.OffenderClientId">
|
||||
<color-code value="@Model.OffenderName" allow="@ViewBag.EnableColorCodes"></color-code>
|
||||
</a>
|
||||
</span>
|
||||
}
|
||||
|
||||
else if (match.MatchValue == "reason")
|
||||
{
|
||||
<span class="text-white">
|
||||
@if (ViewBag.Authorized && !string.IsNullOrEmpty(Model.AutomatedOffense) && Model.PenaltyType != SharedLibraryCore.Database.Models.EFPenalty.PenaltyType.Warning)
|
||||
{
|
||||
<span>@Utilities.FormatExt(ViewBag.Localization["WEBFRONT_PROFILE_ANTICHEAT_DETECTION"], Model.AutomatedOffense)</span>
|
||||
<span class="oi oi-list-rich align-top text-primary automated-penalty-info-detailed" data-penalty-id="@Model.PenaltyId" style="margin-top: 0.125rem;" title="@ViewBag.Localization["WEBFRONT_CLIENT_META_AC_METRIC"]"></span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<color-code value="@Model.Offense" allow="@ViewBag.EnableColorCodes"></color-code>
|
||||
}
|
||||
|
||||
</span>
|
||||
}
|
||||
|
||||
else if (match.MatchValue == "time")
|
||||
{
|
||||
<span class="text-white">@Model.LengthText</span>
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
<span>@match.MatchValue</span>
|
||||
}
|
||||
}
|
||||
</div>
|
43
WebfrontCore/Views/Client/Profile/Meta/_Information.cshtml
Normal file
43
WebfrontCore/Views/Client/Profile/Meta/_Information.cshtml
Normal file
@ -0,0 +1,43 @@
|
||||
@model IEnumerable<SharedLibraryCore.Dtos.Meta.Responses.InformationResponse>
|
||||
@{
|
||||
var informationMeta = Model
|
||||
.Where(_meta => _meta.Type == SharedLibraryCore.Interfaces.MetaType.Information)
|
||||
.OrderBy(_meta => _meta.Order)
|
||||
.GroupBy(_meta => _meta.Column)
|
||||
.OrderBy(_grouping => _grouping.Key);
|
||||
}
|
||||
|
||||
@foreach (var metaColumn in informationMeta)
|
||||
{
|
||||
<div class="text-center text-lg-left mr-0 mr-lg-4">
|
||||
@foreach (var meta in metaColumn)
|
||||
{
|
||||
<div class="profile-meta-entry" title="@meta.ToolTipText">
|
||||
|
||||
@{var results = Utilities.SplitTranslationTokens(meta.Key);}
|
||||
|
||||
@if (results.Any(_result => _result.IsInterpolation))
|
||||
{
|
||||
foreach (var result in results)
|
||||
{
|
||||
if (result.IsInterpolation)
|
||||
{
|
||||
<span class="profile-meta-value text-primary"><color-code value="@meta.Value" allow="@ViewBag.EnableColorCodes"></color-code></span>
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
<span class="profile-meta-title text-muted">@result.MatchValue</span>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
<span class="profile-meta-value text-primary"><color-code value="@meta.Value" allow="@ViewBag.EnableColorCodes"></color-code></span>
|
||||
<span class="profile-meta-title text-muted"> @meta.Key</span>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
@using SharedLibraryCore.Dtos.Meta.Responses;
|
||||
|
||||
@model MessageResponse
|
||||
<span class="client-message" data-serverid="@Model.ServerId" data-when="@Model.When.ToFileTimeUtc()">
|
||||
<span class="oi oi-chevron-right text-white-50 align-middle client-message-prefix" title="@ViewBag.Localization["WEBFRONT_PROFILE_MESSAGE_CONTEXT"]" style="font-size: 0.75rem; margin-top: -0.256rem"></span>
|
||||
<span class="text-muted">
|
||||
<color-code value="@Model.Message" allow="@ViewBag.EnableColorCodes"></color-code>
|
||||
</span>
|
||||
</span>
|
@ -0,0 +1,74 @@
|
||||
@using SharedLibraryCore.Dtos.Meta.Responses
|
||||
@using SharedLibraryCore
|
||||
@model ReceivedPenaltyResponse
|
||||
|
||||
@{
|
||||
string localizationKey = $"WEBFRONT_CLIENT_META_WAS_PENALIZED_{Model.PenaltyType.ToString().ToUpper()}_V2";
|
||||
}
|
||||
|
||||
<div class="d-inline">
|
||||
@foreach (var match in Utilities.SplitTranslationTokens(localizationKey))
|
||||
{
|
||||
if (match.IsInterpolation)
|
||||
{
|
||||
if (match.MatchValue == "action")
|
||||
{
|
||||
<span class="penalties-color-@Model.PenaltyType.ToString().ToLower()">@match.TranslationValue</span>
|
||||
}
|
||||
|
||||
else if (match.MatchValue == "punisher")
|
||||
{
|
||||
<span class="text-highlight">
|
||||
<a class="link-inverse" href="@Model.PunisherClientId">
|
||||
<color-code value="@Model.PunisherName" allow="@ViewBag.EnableColorCodes"></color-code>
|
||||
</a>
|
||||
</span>
|
||||
}
|
||||
|
||||
else if (match.MatchValue == "reason")
|
||||
{
|
||||
<span class="text-white">
|
||||
@if (ViewBag.Authorized && !string.IsNullOrEmpty(Model.AutomatedOffense) && Model.PenaltyType != SharedLibraryCore.Database.Models.EFPenalty.PenaltyType.Warning)
|
||||
{
|
||||
<span>@Utilities.FormatExt(ViewBag.Localization["WEBFRONT_PROFILE_ANTICHEAT_DETECTION"], Model.AutomatedOffense)</span>
|
||||
<span class="oi oi-list-rich align-top text-primary automated-penalty-info-detailed" data-penalty-id="@Model.PenaltyId" style="margin-top: 0.125rem;" title="@ViewBag.Localization["WEBFRONT_CLIENT_META_AC_METRIC"]"></span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<color-code value="@Model.Offense" allow="@ViewBag.EnableColorCodes"></color-code>
|
||||
}
|
||||
|
||||
</span>
|
||||
}
|
||||
|
||||
else if (match.MatchValue == "time")
|
||||
{
|
||||
<span class="text-white">@Model.LengthText</span>
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
<span>@match.MatchValue</span>
|
||||
}
|
||||
}
|
||||
|
||||
@if (Model.ClientId != Model.OffenderClientId)
|
||||
{
|
||||
<span>—</span>
|
||||
@foreach (var helperResult in Utilities.SplitTranslationTokens("WEBFRONT_PROFILE_LINKED_ACCOUNT"))
|
||||
{
|
||||
if (!helperResult.IsInterpolation)
|
||||
{
|
||||
<span>@helperResult.MatchValue</span>
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
<a class="link-inverse" href="@Model.OffenderClientId">
|
||||
<color-code value="@Model.OffenderName" allow="@ViewBag.EnableColorCodes"></color-code>
|
||||
</a>
|
||||
}
|
||||
}
|
||||
}
|
||||
</div>
|
@ -0,0 +1,27 @@
|
||||
@using SharedLibraryCore.Dtos.Meta.Responses
|
||||
@using SharedLibraryCore
|
||||
@model UpdatedAliasResponse
|
||||
|
||||
@foreach (var token in Utilities.SplitTranslationTokens("WEBFRONT_PROFILE_META_CONNECT_ALIAS"))
|
||||
{
|
||||
if (token.IsInterpolation)
|
||||
{
|
||||
switch (token.MatchValue)
|
||||
{
|
||||
case "action":
|
||||
<span class="text-warning">@token.TranslationValue</span>
|
||||
break;
|
||||
case "alias":
|
||||
<span class="text-white">
|
||||
<color-code value="@Model.Name" allow="@ViewBag.EnableColorCodes"></color-code>
|
||||
[@Model.IPAddress]
|
||||
</span>
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
<span class="text-muted">@token.MatchValue</span>
|
||||
}
|
||||
}
|
@ -1,94 +1,66 @@
|
||||
@model IEnumerable<SharedLibraryCore.Dtos.ProfileMeta>
|
||||
@using SharedLibraryCore.Interfaces;
|
||||
|
||||
@model IEnumerable<IClientMeta>
|
||||
@{
|
||||
Layout = null;
|
||||
|
||||
var timeSinceLastEvent = DateTime.MinValue;
|
||||
var lastHeaderEventDate = DateTime.UtcNow;
|
||||
|
||||
dynamic formatPenaltyInfo(SharedLibraryCore.Dtos.ProfileMeta meta)
|
||||
TimeSpan timeSpanForEvent(DateTime When)
|
||||
{
|
||||
var penalty = meta.Value as SharedLibraryCore.Dtos.PenaltyInfo;
|
||||
var timePassed = (DateTime.UtcNow - When);
|
||||
var daysPassed = timePassed.TotalDays;
|
||||
var minutesPassed = timePassed.TotalMinutes;
|
||||
|
||||
string localizationKey = meta.Type == SharedLibraryCore.Dtos.ProfileMeta.MetaType.Penalized ?
|
||||
$"WEBFRONT_CLIENT_META_PENALIZED_{penalty.PenaltyTypeText.ToUpper()}" :
|
||||
$"WEBFRONT_CLIENT_META_WAS_PENALIZED_{penalty.PenaltyTypeText.ToUpper()}";
|
||||
|
||||
string localizationMessage = SharedLibraryCore.Utilities.CurrentLocalization.LocalizationIndex[localizationKey];
|
||||
var regexMatch = System.Text.RegularExpressions.Regex.Match(localizationMessage, @"^.*{{([^{}]+)}}.+$");
|
||||
string penaltyType = regexMatch.Groups[1].Value.ToString();
|
||||
var secondMatch = System.Text.RegularExpressions.Regex.Match(localizationMessage, @"\{\{.+\}\}(.+)\{0\}(.+)\{1\}");
|
||||
|
||||
return new
|
||||
if (minutesPassed <= 60)
|
||||
{
|
||||
Type = meta.Type,
|
||||
Match = secondMatch,
|
||||
Penalty = penalty,
|
||||
PenaltyType = penaltyType
|
||||
};
|
||||
return TimeSpan.FromMinutes(5);
|
||||
}
|
||||
|
||||
if (minutesPassed > 60 && daysPassed <= 1)
|
||||
{
|
||||
return TimeSpan.FromHours(1);
|
||||
}
|
||||
|
||||
if (daysPassed > 1 && daysPassed <= 7)
|
||||
{
|
||||
return TimeSpan.FromDays(1);
|
||||
}
|
||||
|
||||
if (daysPassed > 7 && daysPassed <= 31)
|
||||
{
|
||||
return TimeSpan.FromDays(31);
|
||||
}
|
||||
|
||||
if (daysPassed > 31 && daysPassed <= 365)
|
||||
{
|
||||
return TimeSpan.FromDays(31);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
return TimeSpan.FromDays(365);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@if (Model.Count() == 0)
|
||||
{
|
||||
<div class="p2 text-muted profile-event-timestep">@SharedLibraryCore.Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_NONE"]</div>
|
||||
<div class="p2 text-muted profile-event-timestep">@ViewBag.Localization["WEBFRONT_CLIENT_META_NONE"]</div>
|
||||
}
|
||||
|
||||
@foreach (var meta in Model.OrderByDescending(_meta => _meta.When))
|
||||
{
|
||||
@if (Math.Abs((meta.When - timeSinceLastEvent).TotalDays) >= 1)
|
||||
@if ((lastHeaderEventDate - meta.When) > timeSpanForEvent(lastHeaderEventDate))
|
||||
{
|
||||
<div class="p2 text-white profile-event-timestep">
|
||||
<span class="text-primary">—</span>
|
||||
<span>@SharedLibraryCore.Utilities.GetTimePassed(meta.When, true)</span>
|
||||
<span>@meta.When.HumanizeForCurrentCulture()</span>
|
||||
</div>
|
||||
|
||||
timeSinceLastEvent = meta.When;
|
||||
lastHeaderEventDate = meta.When;
|
||||
}
|
||||
|
||||
@switch (meta.Type)
|
||||
{
|
||||
case SharedLibraryCore.Dtos.ProfileMeta.MetaType.ChatMessage:
|
||||
case SharedLibraryCore.Dtos.ProfileMeta.MetaType.QuickMessage:
|
||||
<div class="profile-meta-entry loader-data-time" data-time="@meta.When">
|
||||
<span style="color:white;">></span>
|
||||
<span class="client-message text-muted @(meta.Type == SharedLibraryCore.Dtos.ProfileMeta.MetaType.QuickMessage ? "font-italic" : "")" data-serverid="@meta.Extra" data-when="@meta.When.ToFileTimeUtc()" title="@SharedLibraryCore.Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_PROFILE_MESSAGE_CONTEXT"]"> <color-code value="@meta.Value" allow="@ViewBag.EnableColorCodes"></color-code></span>
|
||||
<div class="profile-meta-entry loader-data-time" data-time="@meta.When.ToFileTimeUtc()" title="@Utilities.FormatExt(ViewBag.Localization["WEBFRONT_PROFILE_META_DATE_OCCURRED"], meta.When.ToString())">
|
||||
<partial name="~/Views/Client/Profile/Meta/_@(meta.GetType().Name).cshtml" model="meta" />
|
||||
</div>
|
||||
break;
|
||||
case SharedLibraryCore.Dtos.ProfileMeta.MetaType.ReceivedPenalty:
|
||||
case SharedLibraryCore.Dtos.ProfileMeta.MetaType.Penalized:
|
||||
<div class="profile-meta-entry loader-data-time" data-time="@meta.When">
|
||||
@{ var penaltyInfo = formatPenaltyInfo(meta); }
|
||||
@if (meta.Type == SharedLibraryCore.Dtos.ProfileMeta.MetaType.Penalized)
|
||||
{
|
||||
<span class="penalties-color-@penaltyInfo.Penalty.PenaltyTypeText.ToLower()">@penaltyInfo.PenaltyType</span>
|
||||
<span>@penaltyInfo.Match.Groups[1].ToString()</span> <!-- by -->
|
||||
|
||||
<span class="text-highlight">
|
||||
<!-- punisher -->
|
||||
<a class="link-inverse" href="@penaltyInfo.Penalty.OffenderId">
|
||||
<color-code value="@penaltyInfo.Penalty.OffenderName" allow="@ViewBag.EnableColorCodes"></color-code>
|
||||
</a>
|
||||
</span>
|
||||
<span>@penaltyInfo.Match.Groups[2].ToString()</span> <!-- for -->
|
||||
<span class="@(ViewBag.Authorized ? "automated-penalty-info-detailed" : "")} text-white"
|
||||
data-penalty-id="@penaltyInfo.Penalty.Id"><color-code value="@penaltyInfo.Penalty.Offense" allow="@ViewBag.EnableColorCodes"></color-code> @(ViewBag.Authorized ? penaltyInfo.Penalty.AdditionalPenaltyInformation : "")</span>
|
||||
}
|
||||
|
||||
@if (meta.Type == SharedLibraryCore.Dtos.ProfileMeta.MetaType.ReceivedPenalty)
|
||||
{
|
||||
<span class="penalties-color-@penaltyInfo.Penalty.PenaltyTypeText.ToLower()">@penaltyInfo.PenaltyType</span> <!-- actioned -->
|
||||
<span>@penaltyInfo.Match.Groups[1].ToString()</span> <!-- by -->
|
||||
<span class="text-highlight">
|
||||
<a class="link-inverse" href="@penaltyInfo.Penalty.PunisherId">
|
||||
<color-code value="@penaltyInfo.Penalty.PunisherName" allow="@ViewBag.EnableColorCodes"></color-code>
|
||||
</a> <!-- punisher -->
|
||||
</span>
|
||||
<span>@penaltyInfo.Match.Groups[2]</span>
|
||||
<span class="@(ViewBag.Authorized ? "automated-penalty-info-detailed" : "") text-white"
|
||||
data-penalty-id="@penaltyInfo.Penalty.Id">
|
||||
<color-code value="@penaltyInfo.Penalty.Offense" allow="@ViewBag.EnableColorCodes"></color-code> @(ViewBag.Authorized ? penaltyInfo.Penalty.AdditionalPenaltyInformation : "")
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
break;
|
||||
}
|
||||
}
|
@ -2,3 +2,4 @@
|
||||
@using WebfrontCore
|
||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
@addTagHelper *, SharedLibraryCore
|
||||
@addTagHelper *, WebfrontCore
|
@ -64,14 +64,14 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BuildBundlerMinifier" Version="3.2.435" />
|
||||
<PackageReference Include="BuildBundlerMinifier" Version="3.2.449" />
|
||||
<PackageReference Include="BuildWebCompiler" Version="1.12.405" />
|
||||
<PackageReference Include="FluentValidation.AspNetCore" Version="8.6.2" />
|
||||
<PackageReference Include="Microsoft.Web.LibraryManager.Build" Version="2.0.96" />
|
||||
<PackageReference Include="FluentValidation.AspNetCore" Version="9.1.2" />
|
||||
<PackageReference Include="Microsoft.Web.LibraryManager.Build" Version="2.1.76" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(Configuration)'=='Debug'">
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="3.1.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="3.1.7" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -1,7 +1,10 @@
|
||||
[
|
||||
[
|
||||
{
|
||||
"outputFile": "wwwroot/css/global.css",
|
||||
"inputFile": "wwwroot/css/src/main.scss",
|
||||
"sourceMap": false
|
||||
"inputFile": "wwwroot/css/src/main.scss"
|
||||
},
|
||||
{
|
||||
"outputFile": "wwwroot/css/src/profile.css",
|
||||
"inputFile": "wwwroot/css/src/profile.scss"
|
||||
}
|
||||
]
|
@ -1,5 +1,4 @@
|
||||
@import 'bootstrap-custom.scss';
|
||||
@import 'profile.scss';
|
||||
@import 'profile.scss';
|
||||
|
||||
$icon-font-path: '/font/' !default;
|
||||
@import '../../lib/open-iconic/font/css/open-iconic-bootstrap.scss';
|
||||
@ -195,7 +194,7 @@ form *, select {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.client-message, .automated-penalty-info-detailed, .oi {
|
||||
.oi {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,6 @@
|
||||
.level-bgcolor-console {
|
||||
@import 'bootstrap-custom.scss';
|
||||
|
||||
.level-bgcolor-console {
|
||||
background-color: grey;
|
||||
}
|
||||
|
||||
@ -194,3 +196,8 @@
|
||||
#profile_events span {
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
#filter_meta_container .nav-link:hover {
|
||||
background-color: $dark;
|
||||
color: $white !important;
|
||||
}
|
||||
|
@ -5,12 +5,14 @@ let startAt = null;
|
||||
let isLoaderLoading = false;
|
||||
let loadUri = '';
|
||||
let loaderResponseId = '';
|
||||
let additionalParams = [];
|
||||
|
||||
function initLoader(location, loaderId, count = 10, start = count) {
|
||||
function initLoader(location, loaderId, count = 10, start = count, additional) {
|
||||
loadUri = location;
|
||||
loaderResponseId = loaderId;
|
||||
loadCount = count;
|
||||
loaderOffset = start;
|
||||
additionalParams = additional;
|
||||
setupListeners();
|
||||
}
|
||||
|
||||
@ -21,7 +23,13 @@ function loadMoreItems() {
|
||||
|
||||
showLoader();
|
||||
isLoaderLoading = true;
|
||||
$.get(loadUri, { offset: loaderOffset, count: loadCount, startAt: startAt })
|
||||
let params = { offset: loaderOffset, count: loadCount, startAt: startAt };
|
||||
for (i = 0; i < additionalParams.length; i++) {
|
||||
let param = additionalParams[i];
|
||||
params[param.name] = param.value;
|
||||
}
|
||||
|
||||
$.get(loadUri, params)
|
||||
.done(function (response) {
|
||||
$(loaderResponseId).append(response);
|
||||
if (response.trim().length === 0) {
|
||||
|
@ -11,7 +11,15 @@
|
||||
});
|
||||
|
||||
/* set the end time for initial event query */
|
||||
startAt = $('#profile_events').children().last().data('time');
|
||||
startAt = $('.loader-data-time').last().data('time');
|
||||
|
||||
|
||||
$('#filter_meta_container_button').click(function () {
|
||||
$('#filter_meta_container').hide();
|
||||
$('#filter_meta_container').removeClass('d-none');
|
||||
$('#filter_meta_container').addClass('d-block');
|
||||
$('#filter_meta_container').slideDown();
|
||||
});
|
||||
|
||||
/*
|
||||
* load context of chat
|
||||
@ -20,6 +28,14 @@
|
||||
$(document).on('click', '.client-message', function (e) {
|
||||
showLoader();
|
||||
const location = $(this);
|
||||
$('.client-message-prefix').removeClass('oi-chevron-bottom');
|
||||
$('.client-message-prefix').removeClass('oi-chevron-right');
|
||||
|
||||
$('.client-message-prefix').addClass('oi-chevron-right');
|
||||
|
||||
$(this).children().filter('.client-message-prefix').removeClass('oi-chevron-right');
|
||||
$(this).children().filter('.client-message-prefix').addClass('oi-chevron-bottom');
|
||||
|
||||
$.get('/Stats/GetMessageAsync', {
|
||||
'serverId': $(this).data('serverid'),
|
||||
'when': $(this).data('when')
|
||||
|
Loading…
Reference in New Issue
Block a user