Show ban and note count in ahelp window (#15328)

This commit is contained in:
DrSmugleaf
2023-04-11 17:34:25 -07:00
committed by GitHub
parent 98b4af8df0
commit 26cd16eeaa
13 changed files with 142 additions and 34 deletions

View File

@@ -10,10 +10,11 @@
<cc:PlayerListControl Access="Public" Name="ChannelSelector" HorizontalExpand="True" SizeFlagsStretchRatio="1" /> <cc:PlayerListControl Access="Public" Name="ChannelSelector" HorizontalExpand="True" SizeFlagsStretchRatio="1" />
<BoxContainer Orientation="Vertical" HorizontalExpand="True" SizeFlagsStretchRatio="2"> <BoxContainer Orientation="Vertical" HorizontalExpand="True" SizeFlagsStretchRatio="2">
<BoxContainer Access="Public" Name="BwoinkArea" VerticalExpand="True" /> <BoxContainer Access="Public" Name="BwoinkArea" VerticalExpand="True" />
<BoxContainer Orientation="Horizontal" HorizontalAlignment="Right"> <BoxContainer Orientation="Horizontal" HorizontalExpand="True">
<Button Margin="0 0 10 0" Visible="True" Name="PopOut" Access="Public" Text="{Loc 'admin-logs-pop-out'}"/> <Button Margin="0 0 10 0" Visible="True" Name="PopOut" Access="Public" Text="{Loc 'admin-logs-pop-out'}"
<Button Visible="False" Name="Bans" Text="{Loc 'admin-player-actions-bans'}" /> HorizontalAlignment="Left"/>
<Button Visible="False" Name="Notes" Text="{Loc 'admin-player-actions-notes'}" /> <Button Visible="False" Name="Bans" />
<Button Visible="False" Name="Notes" />
<Button Visible="False" Name="Kick" Text="{Loc 'admin-player-actions-kick'}" /> <Button Visible="False" Name="Kick" Text="{Loc 'admin-player-actions-kick'}" />
<Button Visible="False" Name="Ban" Text="{Loc 'admin-player-actions-ban'}" /> <Button Visible="False" Name="Ban" Text="{Loc 'admin-player-actions-ban'}" />
<Button Visible="False" Name="Respawn" Text="{Loc 'admin-player-actions-respawn'}" /> <Button Visible="False" Name="Respawn" Text="{Loc 'admin-player-actions-respawn'}" />

View File

@@ -6,13 +6,11 @@ using Content.Client.Administration.UI.CustomControls;
using Content.Client.Administration.UI.Tabs.AdminTab; using Content.Client.Administration.UI.Tabs.AdminTab;
using Content.Client.Stylesheets; using Content.Client.Stylesheets;
using Content.Client.UserInterface.Systems.Bwoink; using Content.Client.UserInterface.Systems.Bwoink;
using Content.Client.UserInterface.Systems.Chat.Controls;
using Content.Shared.Administration; using Content.Shared.Administration;
using Robust.Client.AutoGenerated; using Robust.Client.AutoGenerated;
using Robust.Client.Console; using Robust.Client.Console;
using Robust.Client.UserInterface; using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML; using Robust.Client.UserInterface.XAML;
using Robust.Shared.Network; using Robust.Shared.Network;
using Robust.Shared.Utility; using Robust.Shared.Utility;
@@ -51,9 +49,17 @@ namespace Content.Client.Administration.UI.Bwoink
ChannelSelector.OnSelectionChanged += sel => ChannelSelector.OnSelectionChanged += sel =>
{ {
_currentPlayer = sel; _currentPlayer = sel;
if (sel is not null) if (sel == null)
{
SetPlayerActionsVisibility(false);
}
else
{ {
SwitchToChannel(sel.SessionId); SwitchToChannel(sel.SessionId);
SetPlayerActionsVisibility(true);
Bans.Text = Loc.GetString("admin-player-actions-bans", ("count", sel.Bans));
Notes.Text = Loc.GetString("admin-player-actions-notes", ("count", sel.Notes));
} }
ChannelSelector.PlayerListContainer.DirtyList(); ChannelSelector.PlayerListContainer.DirtyList();
@@ -164,6 +170,8 @@ namespace Content.Client.Administration.UI.Bwoink
{ {
uiController.PopOut(); uiController.PopOut();
}; };
SetPlayerActionsVisibility(_currentPlayer != null);
} }
private Dictionary<Control, (CancellationTokenSource cancellation, string? originalText)> Confirmations { get; } = new(); private Dictionary<Control, (CancellationTokenSource cancellation, string? originalText)> Confirmations { get; } = new();
@@ -245,8 +253,7 @@ namespace Content.Client.Administration.UI.Bwoink
{ {
foreach (var bw in BwoinkArea.Children) foreach (var bw in BwoinkArea.Children)
bw.Visible = false; bw.Visible = false;
var panel = AHelpHelper.EnsurePanel(ch); AHelpHelper.EnsurePanel(ch).Visible = true;
panel.Visible = true;
} }
private bool TryConfirm(Button button) private bool TryConfirm(Button button)
@@ -273,5 +280,15 @@ namespace Content.Client.Administration.UI.Bwoink
button.Text = Loc.GetString("admin-player-actions-confirm"); button.Text = Loc.GetString("admin-player-actions-confirm");
return false; return false;
} }
private void SetPlayerActionsVisibility(bool visible)
{
Bans.Visible = visible;
Notes.Visible = visible;
Kick.Visible = visible;
Ban.Visible = visible;
Respawn.Visible = visible;
Teleport.Visible = visible;
}
} }
} }

View File

@@ -1,6 +1,6 @@
<DefaultWindow xmlns="https://spacestation14.io" <DefaultWindow xmlns="https://spacestation14.io"
xmlns:cc="clr-namespace:Content.Client.Administration.UI.Bwoink" xmlns:cc="clr-namespace:Content.Client.Administration.UI.Bwoink"
SetSize="900 500" SetSize="1000 500"
HeaderClass="windowHeaderAlert" HeaderClass="windowHeaderAlert"
TitleClass="windowTitleAlert" TitleClass="windowTitleAlert"
Title="{Loc 'bwoink-user-title'}" > Title="{Loc 'bwoink-user-title'}" >

View File

@@ -1,20 +1,6 @@
using System.Text;
using System.Threading;
using Content.Client.Administration.Managers;
using Content.Client.Administration.UI.CustomControls;
using Content.Client.Administration.UI.Tabs.AdminTab;
using Content.Client.Stylesheets;
using Content.Client.UserInterface.Systems.Bwoink;
using Content.Shared.Administration;
using Robust.Client.AutoGenerated; using Robust.Client.AutoGenerated;
using Robust.Client.Console;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls; using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML; using Robust.Client.UserInterface.XAML;
using Robust.Shared.Network;
using Robust.Shared.Utility;
using Timer = Robust.Shared.Timing.Timer;
namespace Content.Client.Administration.UI.Bwoink namespace Content.Client.Administration.UI.Bwoink
{ {

View File

@@ -124,6 +124,11 @@ public sealed class AdminNotesManager : IAdminNotesManager, IPostInjectInit
return await _db.GetAdminNotes(player); return await _db.GetAdminNotes(player);
} }
public async Task<int> CountNotes(Guid player)
{
return await _db.CountAdminNotes(player);
}
public async Task<string> GetPlayerName(Guid player) public async Task<string> GetPlayerName(Guid player)
{ {
return (await _db.GetPlayerRecordByUserId(new NetUserId(player)))?.LastSeenUserName ?? string.Empty; return (await _db.GetPlayerRecordByUserId(new NetUserId(player)))?.LastSeenUserName ?? string.Empty;

View File

@@ -20,5 +20,6 @@ public interface IAdminNotesManager
Task DeleteNote(int noteId, IPlayerSession deletedBy); Task DeleteNote(int noteId, IPlayerSession deletedBy);
Task ModifyNote(int noteId, IPlayerSession editedBy, string message); Task ModifyNote(int noteId, IPlayerSession editedBy, string message);
Task<List<AdminNote>> GetNotes(Guid player); Task<List<AdminNote>> GetNotes(Guid player);
Task<int> CountNotes(Guid player);
Task<string> GetPlayerName(Guid player); Task<string> GetPlayerName(Guid player);
} }

View File

@@ -1,7 +1,9 @@
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using Content.Server.Administration.Managers; using Content.Server.Administration.Managers;
using Content.Server.GameTicking.Events; using Content.Server.Administration.Notes;
using Content.Server.Database;
using Content.Server.IdentityManagement; using Content.Server.IdentityManagement;
using Content.Server.Players; using Content.Server.Players;
using Content.Server.Roles; using Content.Server.Roles;
@@ -13,6 +15,7 @@ using Robust.Server.GameObjects;
using Robust.Server.Player; using Robust.Server.Player;
using Robust.Shared.Enums; using Robust.Shared.Enums;
using Robust.Shared.Network; using Robust.Shared.Network;
using Job = Content.Server.Roles.Job;
namespace Content.Server.Administration.Systems namespace Content.Server.Administration.Systems
{ {
@@ -20,6 +23,8 @@ namespace Content.Server.Administration.Systems
{ {
[Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IAdminManager _adminManager = default!; [Dependency] private readonly IAdminManager _adminManager = default!;
[Dependency] private readonly IServerDbManager _db = default!;
[Dependency] private readonly IAdminNotesManager _notes = default!;
private readonly Dictionary<NetUserId, PlayerInfo> _playerList = new(); private readonly Dictionary<NetUserId, PlayerInfo> _playerList = new();
@@ -44,7 +49,7 @@ namespace Content.Server.Administration.Systems
SubscribeLocalEvent<RoundRestartCleanupEvent>(OnRoundRestartCleanup); SubscribeLocalEvent<RoundRestartCleanupEvent>(OnRoundRestartCleanup);
} }
private void OnRoundRestartCleanup(RoundRestartCleanupEvent ev) private async void OnRoundRestartCleanup(RoundRestartCleanupEvent ev)
{ {
_roundActivePlayers.Clear(); _roundActivePlayers.Clear();
@@ -57,7 +62,7 @@ namespace Content.Server.Administration.Systems
return; return;
_playerManager.TryGetSessionById(id, out var session); _playerManager.TryGetSessionById(id, out var session);
_playerList[id] = GetPlayerInfo(playerData, session); _playerList[id] = await GetPlayerInfo(playerData, session);
} }
var updateEv = new FullPlayerListEvent() { PlayersInfo = _playerList.Values.ToList() }; var updateEv = new FullPlayerListEvent() { PlayersInfo = _playerList.Values.ToList() };
@@ -68,9 +73,9 @@ namespace Content.Server.Administration.Systems
} }
} }
public void UpdatePlayerList(IPlayerSession player) public async void UpdatePlayerList(IPlayerSession player)
{ {
_playerList[player.UserId] = GetPlayerInfo(player.Data, player); _playerList[player.UserId] = await GetPlayerInfo(player.Data, player);
var playerInfoChangedEvent = new PlayerInfoChangedEvent var playerInfoChangedEvent = new PlayerInfoChangedEvent
{ {
@@ -157,7 +162,7 @@ namespace Content.Server.Administration.Systems
RaiseNetworkEvent(ev, playerSession.ConnectedClient); RaiseNetworkEvent(ev, playerSession.ConnectedClient);
} }
private PlayerInfo GetPlayerInfo(IPlayerData data, IPlayerSession? session) private async Task<PlayerInfo> GetPlayerInfo(IPlayerData data, IPlayerSession? session)
{ {
var name = data.UserName; var name = data.UserName;
var entityName = string.Empty; var entityName = string.Empty;
@@ -178,8 +183,11 @@ namespace Content.Server.Administration.Systems
var connected = session != null && session.Status is SessionStatus.Connected or SessionStatus.InGame; var connected = session != null && session.Status is SessionStatus.Connected or SessionStatus.InGame;
var bans = await _db.CountServerBansAsync(null, data.UserId, null);
var notes = await _notes.CountNotes(data.UserId);
return new PlayerInfo(name, entityName, identityName, startingRole, antag, session?.AttachedEntity, data.UserId, return new PlayerInfo(name, entityName, identityName, startingRole, antag, session?.AttachedEntity, data.UserId,
connected, _roundActivePlayers.Contains(data.UserId)); connected, _roundActivePlayers.Contains(data.UserId), bans, notes);
} }
} }
} }

View File

@@ -330,6 +330,22 @@ namespace Content.Server.Database
ImmutableArray<byte>? hwId, ImmutableArray<byte>? hwId,
bool includeUnbanned); bool includeUnbanned);
/// <summary>
/// Counts an user's bans.
/// This will return pardoned bans as well.
/// One of <see cref="address"/> or <see cref="userId"/> need to not be null.
/// </summary>
/// <param name="address">The ip address of the user.</param>
/// <param name="userId">The id of the user.</param>
/// <param name="hwId">The HWId of the user.</param>
/// <param name="includeUnbanned">Include pardoned and expired bans.</param>
/// <returns>The user's ban history.</returns>
public abstract Task<int> CountServerBansAsync(
IPAddress? address,
NetUserId? userId,
ImmutableArray<byte>? hwId,
bool includeUnbanned);
public abstract Task AddServerBanAsync(ServerBanDef serverBan); public abstract Task AddServerBanAsync(ServerBanDef serverBan);
public abstract Task AddServerUnbanAsync(ServerUnbanDef serverUnban); public abstract Task AddServerUnbanAsync(ServerUnbanDef serverUnban);
@@ -996,6 +1012,19 @@ namespace Content.Server.Database
.ToListAsync(); .ToListAsync();
} }
public async Task<int> CountAdminNotes(Guid player)
{
await using var db = await GetDb();
return await db.DbContext.AdminNotes
.Where(note => note.PlayerUserId == player)
.Where(note => !note.Deleted)
.Include(note => note.Round)
.Include(note => note.CreatedBy)
.Include(note => note.LastEditedBy)
.Include(note => note.Player)
.CountAsync();
}
public async Task DeleteAdminNote(int id, Guid deletedBy, DateTime deletedAt) public async Task DeleteAdminNote(int id, Guid deletedBy, DateTime deletedAt)
{ {
await using var db = await GetDb(); await using var db = await GetDb();

View File

@@ -82,6 +82,21 @@ namespace Content.Server.Database
ImmutableArray<byte>? hwId, ImmutableArray<byte>? hwId,
bool includeUnbanned=true); bool includeUnbanned=true);
/// <summary>
/// Counts an user's bans.
/// One of <see cref="address"/> or <see cref="userId"/> need to not be null.
/// </summary>
/// <param name="address">The ip address of the user.</param>
/// <param name="userId">The id of the user.</param>
/// <param name="hwId">The HWId of the user.</param>
/// <param name="includeUnbanned">If true, bans that have been expired or pardoned are also included.</param>
/// <returns>The user's ban history.</returns>
Task<int> CountServerBansAsync(
IPAddress? address,
NetUserId? userId,
ImmutableArray<byte>? hwId,
bool includeUnbanned=true);
Task AddServerBanAsync(ServerBanDef serverBan); Task AddServerBanAsync(ServerBanDef serverBan);
Task AddServerUnbanAsync(ServerUnbanDef serverBan); Task AddServerUnbanAsync(ServerUnbanDef serverBan);
@@ -236,6 +251,7 @@ namespace Content.Server.Database
Task<int> AddAdminNote(int? roundId, Guid player, string message, Guid createdBy, DateTime createdAt); Task<int> AddAdminNote(int? roundId, Guid player, string message, Guid createdBy, DateTime createdAt);
Task<AdminNote?> GetAdminNote(int id); Task<AdminNote?> GetAdminNote(int id);
Task<List<AdminNote>> GetAdminNotes(Guid player); Task<List<AdminNote>> GetAdminNotes(Guid player);
Task<int> CountAdminNotes(Guid player);
Task DeleteAdminNote(int id, Guid deletedBy, DateTime deletedAt); Task DeleteAdminNote(int id, Guid deletedBy, DateTime deletedAt);
Task EditAdminNote(int id, string message, Guid editedBy, DateTime editedAt); Task EditAdminNote(int id, string message, Guid editedBy, DateTime editedAt);
@@ -358,6 +374,12 @@ namespace Content.Server.Database
return _db.GetServerBansAsync(address, userId, hwId, includeUnbanned); return _db.GetServerBansAsync(address, userId, hwId, includeUnbanned);
} }
public Task<int> CountServerBansAsync(IPAddress? address, NetUserId? userId, ImmutableArray<byte>? hwId, bool includeUnbanned = true)
{
DbReadOpsMetric.Inc();
return _db.CountServerBansAsync(address, userId, hwId, includeUnbanned);
}
public Task AddServerBanAsync(ServerBanDef serverBan) public Task AddServerBanAsync(ServerBanDef serverBan)
{ {
DbWriteOpsMetric.Inc(); DbWriteOpsMetric.Inc();
@@ -646,6 +668,12 @@ namespace Content.Server.Database
return _db.GetAdminNotes(player); return _db.GetAdminNotes(player);
} }
public Task<int> CountAdminNotes(Guid player)
{
DbReadOpsMetric.Inc();
return _db.CountAdminNotes(player);
}
public Task DeleteAdminNote(int id, Guid deletedBy, DateTime deletedAt) public Task DeleteAdminNote(int id, Guid deletedBy, DateTime deletedAt)
{ {
DbWriteOpsMetric.Inc(); DbWriteOpsMetric.Inc();

View File

@@ -98,6 +98,21 @@ namespace Content.Server.Database
return bans; return bans;
} }
public override async Task<int> CountServerBansAsync(IPAddress? address, NetUserId? userId, ImmutableArray<byte>? hwId, bool includeUnbanned)
{
if (address == null && userId == null && hwId == null)
{
throw new ArgumentException("Address, userId, and hwId cannot all be null");
}
await using var db = await GetDbImpl();
var exempt = await GetBanExemptionCore(db, userId);
var query = MakeBanLookupQuery(address, userId, hwId, db, includeUnbanned, exempt);
return await query.CountAsync();
}
private static IQueryable<ServerBan> MakeBanLookupQuery( private static IQueryable<ServerBan> MakeBanLookupQuery(
IPAddress? address, IPAddress? address,
NetUserId? userId, NetUserId? userId,

View File

@@ -97,6 +97,22 @@ namespace Content.Server.Database
.ToList()!; .ToList()!;
} }
public override async Task<int> CountServerBansAsync(IPAddress? address, NetUserId? userId, ImmutableArray<byte>? hwId, bool includeUnbanned)
{
await using var db = await GetDbImpl();
var exempt = await GetBanExemptionCore(db, userId);
// SQLite can't do the net masking stuff we need to match IP address ranges.
// So just pull down the whole list into memory.
var queryBans = await GetAllBans(db.SqliteDbContext, includeUnbanned, exempt);
return queryBans
.Where(b => BanMatches(b, address, userId, hwId))
.Select(ConvertBan)
.Count();
}
private static async Task<List<ServerBan>> GetAllBans( private static async Task<List<ServerBan>> GetAllBans(
SqliteServerDbContext db, SqliteServerDbContext db,
bool includeUnbanned, bool includeUnbanned,

View File

@@ -13,5 +13,7 @@ namespace Content.Shared.Administration
EntityUid? EntityUid, EntityUid? EntityUid,
NetUserId SessionId, NetUserId SessionId,
bool Connected, bool Connected,
bool ActiveThisRound); bool ActiveThisRound,
int Bans,
int Notes);
} }

View File

@@ -1,5 +1,5 @@
admin-player-actions-bans = Ban List admin-player-actions-bans = Ban List ({$count})
admin-player-actions-notes = Notes admin-player-actions-notes = Notes ({$count})
admin-player-actions-kick = Kick admin-player-actions-kick = Kick
admin-player-actions-ban = Ban admin-player-actions-ban = Ban
admin-player-actions-ahelp = AHelp admin-player-actions-ahelp = AHelp