Admin notes (#7259)
Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
This commit is contained in:
156
Content.Server/Administration/Notes/AdminNotesEui.cs
Normal file
156
Content.Server/Administration/Notes/AdminNotesEui.cs
Normal file
@@ -0,0 +1,156 @@
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.Administration.Managers;
|
||||
using Content.Server.EUI;
|
||||
using Content.Shared.Administration.Notes;
|
||||
using Content.Shared.Eui;
|
||||
using static Content.Shared.Administration.Notes.AdminNoteEuiMsg;
|
||||
|
||||
namespace Content.Server.Administration.Notes;
|
||||
|
||||
public sealed class AdminNotesEui : BaseEui
|
||||
{
|
||||
[Dependency] private readonly IAdminManager _admins = default!;
|
||||
[Dependency] private readonly IAdminNotesManager _notesMan = default!;
|
||||
|
||||
public AdminNotesEui()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
}
|
||||
|
||||
private Guid NotedPlayer { get; set; }
|
||||
private string NotedPlayerName { get; set; } = string.Empty;
|
||||
private Dictionary<int, SharedAdminNote> Notes { get; set; } = new();
|
||||
|
||||
public override async void Opened()
|
||||
{
|
||||
base.Opened();
|
||||
|
||||
_admins.OnPermsChanged += OnPermsChanged;
|
||||
_notesMan.NoteAdded += NoteModified;
|
||||
_notesMan.NoteModified += NoteModified;
|
||||
_notesMan.NoteDeleted += NoteDeleted;
|
||||
}
|
||||
|
||||
public override void Closed()
|
||||
{
|
||||
base.Closed();
|
||||
|
||||
_admins.OnPermsChanged -= OnPermsChanged;
|
||||
_notesMan.NoteAdded -= NoteModified;
|
||||
_notesMan.NoteModified -= NoteModified;
|
||||
_notesMan.NoteDeleted -= NoteDeleted;
|
||||
}
|
||||
|
||||
public override EuiStateBase GetNewState()
|
||||
{
|
||||
return new AdminNotesEuiState(
|
||||
NotedPlayerName,
|
||||
Notes,
|
||||
_notesMan.CanCreate(Player),
|
||||
_notesMan.CanDelete(Player),
|
||||
_notesMan.CanEdit(Player)
|
||||
);
|
||||
}
|
||||
|
||||
public override async void HandleMessage(EuiMessageBase msg)
|
||||
{
|
||||
base.HandleMessage(msg);
|
||||
|
||||
switch (msg)
|
||||
{
|
||||
case Close _:
|
||||
{
|
||||
Close();
|
||||
break;
|
||||
}
|
||||
case CreateNoteRequest {Message: var message}:
|
||||
{
|
||||
if (!_notesMan.CanCreate(Player))
|
||||
{
|
||||
Close();
|
||||
break;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(message))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
await _notesMan.AddNote(Player, NotedPlayer, message);
|
||||
break;
|
||||
}
|
||||
case DeleteNoteRequest request:
|
||||
{
|
||||
if (!_notesMan.CanDelete(Player))
|
||||
{
|
||||
Close();
|
||||
break;
|
||||
}
|
||||
|
||||
await _notesMan.DeleteNote(request.Id, Player);
|
||||
break;
|
||||
}
|
||||
case EditNoteRequest request:
|
||||
{
|
||||
if (!_notesMan.CanEdit(Player))
|
||||
{
|
||||
Close();
|
||||
break;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(request.Message))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
await _notesMan.ModifyNote(request.Id, Player, request.Message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task ChangeNotedPlayer(Guid notedPlayer)
|
||||
{
|
||||
NotedPlayer = notedPlayer;
|
||||
await LoadFromDb();
|
||||
}
|
||||
|
||||
private void NoteModified(SharedAdminNote note)
|
||||
{
|
||||
Notes[note.Id] = note;
|
||||
StateDirty();
|
||||
}
|
||||
|
||||
private void NoteDeleted(int id)
|
||||
{
|
||||
Notes.Remove(id);
|
||||
StateDirty();
|
||||
}
|
||||
|
||||
private async Task LoadFromDb()
|
||||
{
|
||||
NotedPlayerName = await _notesMan.GetPlayerName(NotedPlayer);
|
||||
|
||||
var notes = new Dictionary<int, SharedAdminNote>();
|
||||
foreach (var note in await _notesMan.GetNotes(NotedPlayer))
|
||||
{
|
||||
notes.Add(note.Id, note.ToShared());
|
||||
}
|
||||
|
||||
Notes = notes;
|
||||
|
||||
StateDirty();
|
||||
}
|
||||
|
||||
private void OnPermsChanged(AdminPermsChangedEventArgs args)
|
||||
{
|
||||
if (args.Player == Player && !_notesMan.CanView(Player))
|
||||
{
|
||||
Close();
|
||||
}
|
||||
else
|
||||
{
|
||||
StateDirty();
|
||||
}
|
||||
}
|
||||
}
|
||||
20
Content.Server/Administration/Notes/AdminNotesExtensions.cs
Normal file
20
Content.Server/Administration/Notes/AdminNotesExtensions.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using Content.Server.Database;
|
||||
using Content.Shared.Administration.Notes;
|
||||
|
||||
namespace Content.Server.Administration.Notes;
|
||||
|
||||
public static class AdminNotesExtensions
|
||||
{
|
||||
public static SharedAdminNote ToShared(this AdminNote note)
|
||||
{
|
||||
return new SharedAdminNote(
|
||||
note.Id,
|
||||
note.RoundId,
|
||||
note.Message,
|
||||
note.CreatedBy.LastSeenUserName,
|
||||
note.LastEditedBy.LastSeenUserName,
|
||||
note.CreatedAt,
|
||||
note.LastEditedAt
|
||||
);
|
||||
}
|
||||
}
|
||||
136
Content.Server/Administration/Notes/AdminNotesManager.cs
Normal file
136
Content.Server/Administration/Notes/AdminNotesManager.cs
Normal file
@@ -0,0 +1,136 @@
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.Administration.Managers;
|
||||
using Content.Server.Database;
|
||||
using Content.Server.EUI;
|
||||
using Content.Server.GameTicking;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.Administration.Notes;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Network;
|
||||
|
||||
namespace Content.Server.Administration.Notes;
|
||||
|
||||
public sealed class AdminNotesManager : IAdminNotesManager, IPostInjectInit
|
||||
{
|
||||
[Dependency] private readonly IAdminManager _admins = default!;
|
||||
[Dependency] private readonly IServerDbManager _db = default!;
|
||||
[Dependency] private readonly ILogManager _logManager = default!;
|
||||
[Dependency] private readonly EuiManager _euis = default!;
|
||||
[Dependency] private readonly IEntitySystemManager _systems = default!;
|
||||
|
||||
public const string SawmillId = "admin.notes";
|
||||
|
||||
public event Action<SharedAdminNote>? NoteAdded;
|
||||
public event Action<SharedAdminNote>? NoteModified;
|
||||
public event Action<int>? NoteDeleted;
|
||||
|
||||
private ISawmill _sawmill = default!;
|
||||
|
||||
public bool CanCreate(IPlayerSession admin)
|
||||
{
|
||||
return CanEdit(admin);
|
||||
}
|
||||
|
||||
public bool CanDelete(IPlayerSession admin)
|
||||
{
|
||||
return CanEdit(admin);
|
||||
}
|
||||
|
||||
public bool CanEdit(IPlayerSession admin)
|
||||
{
|
||||
return _admins.HasAdminFlag(admin, AdminFlags.EditNotes);
|
||||
}
|
||||
|
||||
public bool CanView(IPlayerSession admin)
|
||||
{
|
||||
return _admins.HasAdminFlag(admin, AdminFlags.ViewNotes);
|
||||
}
|
||||
|
||||
public async Task OpenEui(IPlayerSession admin, Guid notedPlayer)
|
||||
{
|
||||
var ui = new AdminNotesEui();
|
||||
_euis.OpenEui(ui, admin);
|
||||
|
||||
await ui.ChangeNotedPlayer(notedPlayer);
|
||||
}
|
||||
|
||||
public async Task AddNote(IPlayerSession createdBy, Guid player, string message)
|
||||
{
|
||||
_sawmill.Info($"Player {createdBy.Name} added note with message {message}");
|
||||
|
||||
_systems.TryGetEntitySystem(out GameTicker? ticker);
|
||||
int? round = ticker == null || ticker.RoundId == 0 ? null : ticker.RoundId;
|
||||
var createdAt = DateTime.UtcNow;
|
||||
var noteId = await _db.AddAdminNote(round, player, message, createdBy.UserId, createdAt);
|
||||
|
||||
var note = new SharedAdminNote(
|
||||
noteId,
|
||||
round,
|
||||
message,
|
||||
createdBy.Name,
|
||||
createdBy.Name,
|
||||
createdAt,
|
||||
createdAt
|
||||
);
|
||||
NoteAdded?.Invoke(note);
|
||||
}
|
||||
|
||||
public async Task DeleteNote(int noteId, IPlayerSession deletedBy)
|
||||
{
|
||||
var note = await _db.GetAdminNote(noteId);
|
||||
if (note == null)
|
||||
{
|
||||
_sawmill.Info($"Player {deletedBy.Name} tried to delete non-existent note {noteId}");
|
||||
return;
|
||||
}
|
||||
|
||||
_sawmill.Info($"Player {deletedBy.Name} deleted note {noteId}");
|
||||
|
||||
var deletedAt = DateTime.UtcNow;
|
||||
await _db.DeleteAdminNote(noteId, deletedBy.UserId, deletedAt);
|
||||
|
||||
NoteDeleted?.Invoke(noteId);
|
||||
}
|
||||
|
||||
public async Task ModifyNote(int noteId, IPlayerSession editedBy, string message)
|
||||
{
|
||||
message = message.Trim();
|
||||
|
||||
var note = await _db.GetAdminNote(noteId);
|
||||
if (note == null || note.Message == message)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_sawmill.Info($"Player {editedBy.Name} modified note {noteId} with message {message}");
|
||||
|
||||
var editedAt = DateTime.UtcNow;
|
||||
await _db.EditAdminNote(noteId, message, editedBy.UserId, editedAt);
|
||||
|
||||
var sharedNote = new SharedAdminNote(
|
||||
noteId,
|
||||
note.RoundId,
|
||||
message,
|
||||
note.CreatedBy.LastSeenUserName,
|
||||
editedBy.Name,
|
||||
note.CreatedAt,
|
||||
note.LastEditedAt
|
||||
);
|
||||
NoteModified?.Invoke(sharedNote);
|
||||
}
|
||||
|
||||
public async Task<List<AdminNote>> GetNotes(Guid player)
|
||||
{
|
||||
return await _db.GetAdminNotes(player);
|
||||
}
|
||||
|
||||
public async Task<string> GetPlayerName(Guid player)
|
||||
{
|
||||
return (await _db.GetPlayerRecordByUserId(new NetUserId(player)))?.LastSeenUserName ?? string.Empty;
|
||||
}
|
||||
|
||||
public void PostInject()
|
||||
{
|
||||
_sawmill = _logManager.GetSawmill(SawmillId);
|
||||
}
|
||||
}
|
||||
43
Content.Server/Administration/Notes/AdminNotesSystem.cs
Normal file
43
Content.Server/Administration/Notes/AdminNotesSystem.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
using Content.Server.Administration.Commands;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Console;
|
||||
|
||||
namespace Content.Server.Administration.Notes;
|
||||
|
||||
public sealed class AdminNotesSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IConsoleHost _console = default!;
|
||||
[Dependency] private readonly IAdminNotesManager _notes = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<GetVerbsEvent<Verb>>(AddVerbs);
|
||||
}
|
||||
|
||||
private void AddVerbs(GetVerbsEvent<Verb> ev)
|
||||
{
|
||||
if (EntityManager.GetComponentOrNull<ActorComponent>(ev.User) is not {PlayerSession: var user} ||
|
||||
EntityManager.GetComponentOrNull<ActorComponent>(ev.Target) is not {PlayerSession: var target})
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_notes.CanView(user))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var verb = new Verb
|
||||
{
|
||||
Text = Loc.GetString("admin-notes-verb-text"),
|
||||
Category = VerbCategory.Admin,
|
||||
IconTexture = "/Textures/Interface/VerbIcons/examine.svg.192dpi.png",
|
||||
Act = () => _console.RemoteExecuteCommand(user, $"{OpenAdminNotesCommand.CommandName} \"{target.UserId}\""),
|
||||
Impact = LogImpact.Low
|
||||
};
|
||||
|
||||
ev.Verbs.Add(verb);
|
||||
}
|
||||
}
|
||||
24
Content.Server/Administration/Notes/IAdminNotesManager.cs
Normal file
24
Content.Server/Administration/Notes/IAdminNotesManager.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.Database;
|
||||
using Content.Shared.Administration.Notes;
|
||||
using Robust.Server.Player;
|
||||
|
||||
namespace Content.Server.Administration.Notes;
|
||||
|
||||
public interface IAdminNotesManager
|
||||
{
|
||||
event Action<SharedAdminNote>? NoteAdded;
|
||||
event Action<SharedAdminNote>? NoteModified;
|
||||
event Action<int>? NoteDeleted;
|
||||
|
||||
bool CanCreate(IPlayerSession admin);
|
||||
bool CanDelete(IPlayerSession admin);
|
||||
bool CanEdit(IPlayerSession admin);
|
||||
bool CanView(IPlayerSession admin);
|
||||
Task OpenEui(IPlayerSession admin, Guid notedPlayer);
|
||||
Task AddNote(IPlayerSession createdBy, Guid player, string message);
|
||||
Task DeleteNote(int noteId, IPlayerSession deletedBy);
|
||||
Task ModifyNote(int noteId, IPlayerSession editedBy, string message);
|
||||
Task<List<AdminNote>> GetNotes(Guid player);
|
||||
Task<string> GetPlayerName(Guid player);
|
||||
}
|
||||
Reference in New Issue
Block a user