Better notes and bans (#14228)
Co-authored-by: Chief-Engineer <119664036+Chief-Engineer@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
using Content.Client.Eui;
|
||||
using Content.Shared.Administration.Notes;
|
||||
using Content.Shared.Eui;
|
||||
using JetBrains.Annotations;
|
||||
using static Content.Shared.Administration.Notes.AdminMessageEuiMsg;
|
||||
|
||||
namespace Content.Client.Administration.UI.AdminRemarks;
|
||||
|
||||
[UsedImplicitly]
|
||||
public sealed class AdminMessageEui : BaseEui
|
||||
{
|
||||
private readonly AdminMessagePopupWindow _popup;
|
||||
|
||||
public AdminMessageEui()
|
||||
{
|
||||
_popup = new AdminMessagePopupWindow();
|
||||
_popup.OnAcceptPressed += () => SendMessage(new Accept());
|
||||
_popup.OnDismissPressed += () => SendMessage(new Dismiss());
|
||||
_popup.OnClose += () => SendMessage(new CloseEuiMessage());
|
||||
}
|
||||
|
||||
public override void HandleState(EuiStateBase state)
|
||||
{
|
||||
if (state is not AdminMessageEuiState s)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_popup.SetMessage(s.Message);
|
||||
_popup.SetDetails(s.AdminName, s.AddedOn);
|
||||
_popup.Timer = s.Time;
|
||||
}
|
||||
|
||||
public override void Opened()
|
||||
{
|
||||
_popup.OpenCentered();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<ui:FancyWindow xmlns="https://spacestation14.io"
|
||||
xmlns:ui="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
xmlns:graphics="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
|
||||
VerticalExpand="True" HorizontalExpand="True"
|
||||
Title="{Loc admin-notes-message-window-title}"
|
||||
MinSize="600 170">
|
||||
<PanelContainer VerticalExpand="True" HorizontalExpand="True">
|
||||
<PanelContainer.PanelOverride>
|
||||
<graphics:StyleBoxFlat BackgroundColor="#25252A" />
|
||||
</PanelContainer.PanelOverride>
|
||||
<ScrollContainer HScrollEnabled="False" VerticalExpand="True" HorizontalExpand="True" Margin="4">
|
||||
<BoxContainer Orientation="Vertical" SeparationOverride="10" VerticalAlignment="Bottom">
|
||||
<Label Name="AdminLabel" Text="Loading..." />
|
||||
<RichTextLabel Name="MessageLabel" />
|
||||
<Label Name="WaitLabel" />
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Button Name="DismissButton"
|
||||
Text="{Loc 'admin-notes-message-dismiss'}" />
|
||||
<Button Name="AcceptButton"
|
||||
Text="{Loc 'admin-notes-message-accept'}"
|
||||
Disabled="True" />
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</ScrollContainer>
|
||||
</PanelContainer>
|
||||
</ui:FancyWindow>
|
||||
@@ -0,0 +1,75 @@
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client.Administration.UI.AdminRemarks;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class AdminMessagePopupWindow : FancyWindow
|
||||
{
|
||||
private float _timer = float.MaxValue;
|
||||
public float Timer
|
||||
{
|
||||
get => _timer;
|
||||
set
|
||||
{
|
||||
WaitLabel.Text = Loc.GetString("admin-notes-message-wait", ("time", MathF.Floor(value)));
|
||||
_timer = value;
|
||||
}
|
||||
}
|
||||
|
||||
public event Action? OnDismissPressed;
|
||||
public event Action? OnAcceptPressed;
|
||||
|
||||
public AdminMessagePopupWindow()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
AcceptButton.OnPressed += OnAcceptButtonPressed;
|
||||
DismissButton.OnPressed += OnDismissButtonPressed;
|
||||
}
|
||||
|
||||
public void SetMessage(string message)
|
||||
{
|
||||
MessageLabel.SetMessage(message);
|
||||
}
|
||||
|
||||
public void SetDetails(string adminName, DateTime addedOn)
|
||||
{
|
||||
AdminLabel.Text = Loc.GetString("admin-notes-message-admin", ("admin", adminName), ("date", addedOn));
|
||||
}
|
||||
|
||||
private void OnDismissButtonPressed(BaseButton.ButtonEventArgs obj)
|
||||
{
|
||||
OnDismissPressed?.Invoke();
|
||||
Close();
|
||||
}
|
||||
|
||||
private void OnAcceptButtonPressed(BaseButton.ButtonEventArgs obj)
|
||||
{
|
||||
OnAcceptPressed?.Invoke();
|
||||
Close();
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
|
||||
if (!AcceptButton.Disabled)
|
||||
return;
|
||||
|
||||
if (Timer > 0.0)
|
||||
{
|
||||
if (Timer - args.DeltaSeconds < 0)
|
||||
Timer = 0;
|
||||
else
|
||||
Timer -= args.DeltaSeconds;
|
||||
}
|
||||
else
|
||||
{
|
||||
AcceptButton.Disabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<ui:FancyWindow xmlns="https://spacestation14.io"
|
||||
xmlns:ui="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
xmlns:graphics="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
|
||||
VerticalExpand="True" HorizontalExpand="True"
|
||||
Title="{Loc admin-remarks-title}"
|
||||
SetSize="600 400">
|
||||
<PanelContainer>
|
||||
<PanelContainer.PanelOverride>
|
||||
<graphics:StyleBoxFlat BackgroundColor="#25252A" />
|
||||
</PanelContainer.PanelOverride>
|
||||
<BoxContainer Orientation="Vertical" Margin="4">
|
||||
<ScrollContainer VerticalExpand="True" HorizontalExpand="True" HScrollEnabled="False">
|
||||
<BoxContainer Orientation="Vertical" Name="NotesContainer" Access="Public" VerticalExpand="True" />
|
||||
</ScrollContainer>
|
||||
</BoxContainer>
|
||||
</PanelContainer>
|
||||
</ui:FancyWindow>
|
||||
@@ -0,0 +1,49 @@
|
||||
using System.Linq;
|
||||
using Content.Client.Administration.UI.Notes;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Shared.Administration.Notes;
|
||||
using Content.Shared.Database;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
namespace Content.Client.Administration.UI.AdminRemarks;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class AdminRemarksWindow : FancyWindow
|
||||
{
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystem = default!;
|
||||
private readonly SpriteSystem _sprites;
|
||||
private readonly Dictionary<(int, NoteType), AdminNotesLine> _inputs = new();
|
||||
|
||||
public AdminRemarksWindow()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
_sprites = _entitySystem.GetEntitySystem<SpriteSystem>();
|
||||
}
|
||||
|
||||
public void SetNotes(Dictionary<(int, NoteType), SharedAdminNote> notes)
|
||||
{
|
||||
foreach (var (id, input) in _inputs)
|
||||
{
|
||||
if (notes.ContainsKey(id))
|
||||
continue;
|
||||
NotesContainer.RemoveChild(input);
|
||||
_inputs.Remove(id);
|
||||
}
|
||||
|
||||
foreach (var note in notes.Values.OrderByDescending(note => note.CreatedAt))
|
||||
{
|
||||
if (_inputs.TryGetValue((note.Id, note.NoteType), out var input))
|
||||
{
|
||||
input.UpdateNote(note);
|
||||
continue;
|
||||
}
|
||||
|
||||
input = new AdminNotesLine(_sprites, note);
|
||||
NotesContainer.AddChild(input);
|
||||
_inputs[(note.Id, note.NoteType)] = input;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using Content.Client.Administration.UI.Notes;
|
||||
using Content.Client.Eui;
|
||||
using Content.Shared.Administration.Notes;
|
||||
using Content.Shared.Eui;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Content.Client.Administration.UI.AdminRemarks;
|
||||
|
||||
[UsedImplicitly]
|
||||
public sealed class UserNotesEui : BaseEui
|
||||
{
|
||||
public UserNotesEui()
|
||||
{
|
||||
NoteWindow = new AdminRemarksWindow();
|
||||
NoteWindow.OnClose += () => SendMessage(new CloseEuiMessage());
|
||||
}
|
||||
|
||||
private AdminRemarksWindow NoteWindow { get; }
|
||||
|
||||
public override void HandleState(EuiStateBase state)
|
||||
{
|
||||
if (state is not UserNotesEuiState s)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
NoteWindow.SetNotes(s.Notes);
|
||||
}
|
||||
|
||||
public override void Opened()
|
||||
{
|
||||
NoteWindow.OpenCentered();
|
||||
}
|
||||
}
|
||||
49
Content.Client/Administration/UI/BanPanel/BanPanel.xaml
Normal file
49
Content.Client/Administration/UI/BanPanel/BanPanel.xaml
Normal file
@@ -0,0 +1,49 @@
|
||||
<DefaultWindow
|
||||
xmlns="https://spacestation14.io"
|
||||
xmlns:cc="clr-namespace:Content.Client.Administration.UI.CustomControls"
|
||||
Title="{Loc ban-panel-title}" MinSize="350 500">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<TabContainer Name="Tabs" VerticalExpand="True">
|
||||
<!-- Basic info -->
|
||||
<BoxContainer Orientation="Vertical" HorizontalExpand="True">
|
||||
<BoxContainer Orientation="Horizontal" Margin="2">
|
||||
<CheckBox Name="PlayerCheckbox" MinWidth="100" Text="{Loc ban-panel-player}" Pressed="True" />
|
||||
<Control MinWidth="50" />
|
||||
<LineEdit Name="PlayerNameLine" MinWidth="100" HorizontalExpand="True" PlaceHolder="{Loc ban-panel-player}" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal" Margin="2">
|
||||
<CheckBox Name="IpCheckbox" MinWidth="100" Text="{Loc ban-panel-ip}" Pressed="False" />
|
||||
<Control MinWidth="50" />
|
||||
<LineEdit Name="IpLine" MinWidth="100" HorizontalExpand="True" PlaceHolder="{Loc ban-panel-ip}" ToolTip="{Loc ban-panel-ip-hwid-tooltip}" Editable="False" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal" Margin="2">
|
||||
<CheckBox Name="HwidCheckbox" MinWidth="100" Text="{Loc ban-panel-hwid}" Pressed="True" />
|
||||
<Control MinWidth="50" />
|
||||
<LineEdit Name="HwidLine" MinWidth="100" HorizontalExpand="True" PlaceHolder="{Loc ban-panel-hwid}" ToolTip="{Loc ban-panel-ip-hwid-tooltip}" />
|
||||
</BoxContainer>
|
||||
<CheckBox Name="LastConnCheckbox" Margin="2" Text="{Loc ban-panel-last-conn}" Pressed="True" />
|
||||
<BoxContainer Orientation="Horizontal" Margin="2">
|
||||
<LineEdit Name="TimeLine" MaxWidth="150" MinWidth="70" PlaceHolder="0" />
|
||||
<OptionButton Name="MultiplierOption" />
|
||||
<Control MinWidth="50" />
|
||||
<Label Name="ExpiresLabel" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal" Margin="4">
|
||||
<OptionButton Name="TypeOption" />
|
||||
<Control MinWidth="30"></Control>
|
||||
<Label Text="{Loc ban-panel-severity}" />
|
||||
<OptionButton Name="SeverityOption" />
|
||||
</BoxContainer>
|
||||
<cc:HSeparator Margin="1"/>
|
||||
<TextEdit Name="ReasonTextEdit" MinHeight="100" VerticalExpand="True" HorizontalExpand="True" />
|
||||
</BoxContainer>
|
||||
<!-- Player List -->
|
||||
<cc:PlayerListControl Name="PlayerList" VerticalExpand="True" />
|
||||
<!-- Role list (auto-generated) -->
|
||||
<ScrollContainer>
|
||||
<BoxContainer Name="RolesContainer" Orientation="Vertical" />
|
||||
</ScrollContainer>
|
||||
</TabContainer>
|
||||
<Button Name="SubmitButton" Text="{Loc ban-panel-submit}" HorizontalExpand="True" />
|
||||
</BoxContainer>
|
||||
</DefaultWindow>
|
||||
459
Content.Client/Administration/UI/BanPanel/BanPanel.xaml.cs
Normal file
459
Content.Client/Administration/UI/BanPanel/BanPanel.xaml.cs
Normal file
@@ -0,0 +1,459 @@
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text.RegularExpressions;
|
||||
using Content.Client.Administration.UI.CustomControls;
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Roles;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.Administration.UI.BanPanel;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class BanPanel : DefaultWindow
|
||||
{
|
||||
public event Action<string?, (IPAddress, int)?, bool, byte[]?, bool, uint, string, NoteSeverity, string[]?>? BanSubmitted;
|
||||
public event Action<string>? PlayerChanged;
|
||||
private string? PlayerUsername { get; set; }
|
||||
private (IPAddress, int)? IpAddress { get; set; }
|
||||
private byte[]? Hwid { get; set; }
|
||||
private double TimeEntered { get; set; }
|
||||
private uint Multiplier { get; set; }
|
||||
private bool HasBanFlag { get; set; }
|
||||
private TimeSpan? ButtonResetOn { get; set; }
|
||||
// This is less efficient than just holding a reference to the root control and enumerating children, but you
|
||||
// have to know how the controls are nested, which makes the code more complicated.
|
||||
private readonly List<CheckBox> _roleCheckboxes = new();
|
||||
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
|
||||
private enum TabNumbers
|
||||
{
|
||||
BasicInfo,
|
||||
//Text,
|
||||
Players,
|
||||
Roles
|
||||
}
|
||||
|
||||
private enum Multipliers
|
||||
{
|
||||
Minutes,
|
||||
Hours,
|
||||
Days,
|
||||
Weeks,
|
||||
Months,
|
||||
Years,
|
||||
Permanent
|
||||
}
|
||||
|
||||
private enum Types
|
||||
{
|
||||
None,
|
||||
Server,
|
||||
Role
|
||||
}
|
||||
|
||||
public BanPanel()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
PlayerList.OnSelectionChanged += OnPlayerSelectionChanged;
|
||||
PlayerNameLine.OnFocusExit += _ => OnPlayerNameChanged();
|
||||
PlayerCheckbox.OnPressed += _ =>
|
||||
{
|
||||
PlayerNameLine.Editable = PlayerCheckbox.Pressed;
|
||||
PlayerNameLine.ModulateSelfOverride = null;
|
||||
};
|
||||
TimeLine.OnTextChanged += OnMinutesChanged;
|
||||
MultiplierOption.OnItemSelected += args =>
|
||||
{
|
||||
MultiplierOption.SelectId(args.Id);
|
||||
OnMultiplierChanged();
|
||||
};
|
||||
IpLine.OnFocusExit += _ => OnIpChanged();
|
||||
IpCheckbox.OnPressed += _ =>
|
||||
{
|
||||
IpLine.Editable = IpCheckbox.Pressed;
|
||||
OnIpChanged();
|
||||
};
|
||||
HwidLine.OnFocusExit += _ => OnHwidChanged();
|
||||
HwidCheckbox.OnPressed += _ =>
|
||||
{
|
||||
HwidLine.Editable = HwidCheckbox.Pressed;
|
||||
OnHwidChanged();
|
||||
};
|
||||
TypeOption.OnItemSelected += args =>
|
||||
{
|
||||
TypeOption.SelectId(args.Id);
|
||||
OnTypeChanged();
|
||||
};
|
||||
LastConnCheckbox.OnPressed += args =>
|
||||
{
|
||||
IpLine.ModulateSelfOverride = null;
|
||||
HwidLine.ModulateSelfOverride = null;
|
||||
OnIpChanged();
|
||||
OnHwidChanged();
|
||||
};
|
||||
SubmitButton.OnPressed += SubmitButtonOnOnPressed;
|
||||
|
||||
SeverityOption.AddItem(Loc.GetString("admin-note-editor-severity-none"), (int) NoteSeverity.None);
|
||||
SeverityOption.AddItem(Loc.GetString("admin-note-editor-severity-low"), (int) NoteSeverity.Minor);
|
||||
SeverityOption.AddItem(Loc.GetString("admin-note-editor-severity-medium"), (int) NoteSeverity.Medium);
|
||||
SeverityOption.AddItem(Loc.GetString("admin-note-editor-severity-high"), (int) NoteSeverity.High);
|
||||
SeverityOption.SelectId((int) NoteSeverity.Medium);
|
||||
SeverityOption.OnItemSelected += args => SeverityOption.SelectId(args.Id);
|
||||
|
||||
MultiplierOption.AddItem(Loc.GetString("ban-panel-minutes"), (int) Multipliers.Minutes);
|
||||
MultiplierOption.AddItem(Loc.GetString("ban-panel-hours"), (int) Multipliers.Hours);
|
||||
MultiplierOption.AddItem(Loc.GetString("ban-panel-days"), (int) Multipliers.Days);
|
||||
MultiplierOption.AddItem(Loc.GetString("ban-panel-weeks"), (int) Multipliers.Weeks);
|
||||
MultiplierOption.AddItem(Loc.GetString("ban-panel-months"), (int) Multipliers.Months);
|
||||
MultiplierOption.AddItem(Loc.GetString("ban-panel-years"), (int) Multipliers.Years);
|
||||
MultiplierOption.AddItem(Loc.GetString("ban-panel-permanent"), (int) Multipliers.Permanent);
|
||||
MultiplierOption.SelectId((int) Multipliers.Minutes);
|
||||
OnMultiplierChanged();
|
||||
|
||||
Tabs.SetTabTitle((int) TabNumbers.BasicInfo, Loc.GetString("ban-panel-tabs-basic"));
|
||||
//Tabs.SetTabTitle((int) TabNumbers.Text, Loc.GetString("ban-panel-tabs-reason"));
|
||||
Tabs.SetTabTitle((int) TabNumbers.Players, Loc.GetString("ban-panel-tabs-players"));
|
||||
Tabs.SetTabTitle((int) TabNumbers.Roles, Loc.GetString("ban-panel-tabs-role"));
|
||||
Tabs.SetTabVisible((int) TabNumbers.Roles, false);
|
||||
|
||||
TypeOption.AddItem(Loc.GetString("ban-panel-select"), (int) Types.None);
|
||||
TypeOption.AddItem(Loc.GetString("ban-panel-server"), (int) Types.Server);
|
||||
TypeOption.AddItem(Loc.GetString("ban-panel-role"), (int) Types.Role);
|
||||
|
||||
ReasonTextEdit.Placeholder = new Rope.Leaf(Loc.GetString("ban-panel-reason"));
|
||||
|
||||
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
|
||||
foreach (var proto in prototypeManager.EnumeratePrototypes<DepartmentPrototype>())
|
||||
{
|
||||
CreateRoleGroup(proto.ID, proto.Roles, proto.Color);
|
||||
}
|
||||
|
||||
CreateRoleGroup("Antagonist", prototypeManager.EnumeratePrototypes<AntagPrototype>().Select(p => p.ID), Color.Red);
|
||||
}
|
||||
|
||||
private void CreateRoleGroup(string roleName, IEnumerable<string> roleList, Color color)
|
||||
{
|
||||
var outerContainer = new BoxContainer
|
||||
{
|
||||
Name = $"{roleName}GroupOuterBox",
|
||||
HorizontalExpand = true,
|
||||
VerticalExpand = true,
|
||||
Orientation = BoxContainer.LayoutOrientation.Vertical,
|
||||
Margin = new Thickness(4)
|
||||
};
|
||||
var departmentCheckbox = new CheckBox
|
||||
{
|
||||
Name = $"{roleName}GroupCheckbox",
|
||||
Text = roleName,
|
||||
Modulate = color,
|
||||
HorizontalAlignment = HAlignment.Left
|
||||
};
|
||||
outerContainer.AddChild(departmentCheckbox);
|
||||
var innerContainer = new BoxContainer
|
||||
{
|
||||
Name = $"{roleName}GroupInnerBox",
|
||||
HorizontalExpand = true,
|
||||
Orientation = BoxContainer.LayoutOrientation.Horizontal
|
||||
};
|
||||
departmentCheckbox.OnToggled += args =>
|
||||
{
|
||||
foreach (var child in innerContainer.Children)
|
||||
{
|
||||
if (child is CheckBox c)
|
||||
{
|
||||
c.Pressed = args.Pressed;
|
||||
}
|
||||
}
|
||||
};
|
||||
outerContainer.AddChild(innerContainer);
|
||||
foreach (var role in roleList)
|
||||
{
|
||||
AddRoleCheckbox(role, innerContainer, departmentCheckbox);
|
||||
}
|
||||
RolesContainer.AddChild(new PanelContainer
|
||||
{
|
||||
PanelOverride = new StyleBoxFlat
|
||||
{
|
||||
BackgroundColor = color
|
||||
}
|
||||
});
|
||||
RolesContainer.AddChild(outerContainer);
|
||||
RolesContainer.AddChild(new HSeparator());
|
||||
}
|
||||
|
||||
private void AddRoleCheckbox(string role, Control container, CheckBox header)
|
||||
{
|
||||
var roleCheckbox = new CheckBox
|
||||
{
|
||||
Name = $"{role}RoleCheckbox",
|
||||
Text = role
|
||||
};
|
||||
roleCheckbox.OnToggled += args =>
|
||||
{
|
||||
if (args is { Pressed: true, Button.Parent: { } } && args.Button.Parent.Children.Where(e => e is CheckBox).All(e => ((CheckBox) e).Pressed))
|
||||
header.Pressed = args.Pressed;
|
||||
else
|
||||
header.Pressed = false;
|
||||
};
|
||||
container.AddChild(roleCheckbox);
|
||||
_roleCheckboxes.Add(roleCheckbox);
|
||||
}
|
||||
|
||||
public void UpdateBanFlag(bool newFlag)
|
||||
{
|
||||
HasBanFlag = newFlag;
|
||||
SubmitButton.Visible = HasBanFlag;
|
||||
ModulateSelfOverride = HasBanFlag ? Color.Red : null;
|
||||
}
|
||||
|
||||
public void UpdatePlayerData(string playerName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(playerName))
|
||||
{
|
||||
PlayerNameLine.ModulateSelfOverride = Color.Red;
|
||||
ErrorLevel |= ErrorLevelEnum.PlayerName;
|
||||
UpdateSubmitEnabled();
|
||||
return;
|
||||
}
|
||||
PlayerNameLine.ModulateSelfOverride = null;
|
||||
ErrorLevel &= ~ErrorLevelEnum.PlayerName;
|
||||
UpdateSubmitEnabled();
|
||||
PlayerUsername = playerName;
|
||||
PlayerNameLine.Text = playerName;
|
||||
}
|
||||
|
||||
[Flags]
|
||||
private enum ErrorLevelEnum : byte
|
||||
{
|
||||
None = 0,
|
||||
Minutes = 1 << 0,
|
||||
PlayerName = 1 << 1,
|
||||
IpAddress = 1 << 2,
|
||||
Hwid = 1 << 3,
|
||||
}
|
||||
|
||||
private ErrorLevelEnum ErrorLevel { get; set; }
|
||||
|
||||
private void OnMinutesChanged(LineEdit.LineEditEventArgs args)
|
||||
{
|
||||
TimeLine.Text = args.Text;
|
||||
if (!double.TryParse(args.Text, out var result))
|
||||
{
|
||||
ExpiresLabel.Text = "err";
|
||||
ErrorLevel |= ErrorLevelEnum.Minutes;
|
||||
TimeLine.ModulateSelfOverride = Color.Red;
|
||||
UpdateSubmitEnabled();
|
||||
return;
|
||||
}
|
||||
|
||||
ErrorLevel &= ~ErrorLevelEnum.Minutes;
|
||||
TimeLine.ModulateSelfOverride = null;
|
||||
TimeEntered = result;
|
||||
UpdateSubmitEnabled();
|
||||
UpdateExpiresLabel();
|
||||
}
|
||||
|
||||
private void OnMultiplierChanged()
|
||||
{
|
||||
TimeLine.Editable = MultiplierOption.SelectedId != (int) Multipliers.Permanent;
|
||||
Multiplier = MultiplierOption.SelectedId switch
|
||||
{
|
||||
(int) Multipliers.Minutes => 1,
|
||||
(int) Multipliers.Hours => 60,
|
||||
(int) Multipliers.Days => 60 * 24,
|
||||
(int) Multipliers.Weeks => 60 * 24 * 7,
|
||||
(int) Multipliers.Months => 60 * 24 * 30,
|
||||
(int) Multipliers.Years => 60 * 24 * 365,
|
||||
(int) Multipliers.Permanent => 0,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(MultiplierOption.SelectedId), "Multiplier out of range")
|
||||
};
|
||||
UpdateExpiresLabel();
|
||||
}
|
||||
|
||||
private void UpdateExpiresLabel()
|
||||
{
|
||||
var minutes = (uint) (TimeEntered * Multiplier);
|
||||
ExpiresLabel.Text = minutes == 0
|
||||
? $"{Loc.GetString("admin-note-editor-expiry-label")} {Loc.GetString("server-ban-string-never")}"
|
||||
: $"{Loc.GetString("admin-note-editor-expiry-label")} {DateTime.Now + TimeSpan.FromMinutes(minutes):yyyy/MM/dd HH:mm:ss}";
|
||||
}
|
||||
|
||||
private void OnIpChanged()
|
||||
{
|
||||
if (LastConnCheckbox.Pressed && IpAddress is null || !IpCheckbox.Pressed)
|
||||
{
|
||||
IpAddress = null;
|
||||
ErrorLevel &= ~ErrorLevelEnum.IpAddress;
|
||||
IpLine.ModulateSelfOverride = null;
|
||||
UpdateSubmitEnabled();
|
||||
return;
|
||||
}
|
||||
var ip = IpLine.Text;
|
||||
var hid = "0";
|
||||
if (ip.Contains('/'))
|
||||
{
|
||||
var split = ip.Split('/');
|
||||
ip = split[0];
|
||||
hid = split[1];
|
||||
}
|
||||
|
||||
if (!IPAddress.TryParse(ip, out var parsedIp) || !byte.TryParse(hid, out var hidInt) || hidInt > 128 || hidInt > 32 && parsedIp.AddressFamily == AddressFamily.InterNetwork)
|
||||
{
|
||||
ErrorLevel |= ErrorLevelEnum.IpAddress;
|
||||
IpLine.ModulateSelfOverride = Color.Red;
|
||||
UpdateSubmitEnabled();
|
||||
return;
|
||||
}
|
||||
|
||||
if (hidInt == 0)
|
||||
hidInt = (byte) (parsedIp.AddressFamily == AddressFamily.InterNetworkV6 ? 128 : 32);
|
||||
IpAddress = (parsedIp, hidInt);
|
||||
ErrorLevel &= ~ErrorLevelEnum.IpAddress;
|
||||
IpLine.ModulateSelfOverride = null;
|
||||
UpdateSubmitEnabled();
|
||||
}
|
||||
|
||||
private void OnHwidChanged()
|
||||
{
|
||||
var hwidString = HwidLine.Text;
|
||||
var length = 3 * (hwidString.Length / 4) - hwidString.TakeLast(2).Count(c => c == '=');
|
||||
Hwid = new byte[length];
|
||||
if (HwidCheckbox.Pressed && !(string.IsNullOrEmpty(hwidString) && LastConnCheckbox.Pressed) && !Convert.TryFromBase64String(hwidString, Hwid, out _))
|
||||
{
|
||||
ErrorLevel |= ErrorLevelEnum.Hwid;
|
||||
HwidLine.ModulateSelfOverride = Color.Red;
|
||||
UpdateSubmitEnabled();
|
||||
return;
|
||||
}
|
||||
|
||||
ErrorLevel &= ~ErrorLevelEnum.Hwid;
|
||||
HwidLine.ModulateSelfOverride = null;
|
||||
UpdateSubmitEnabled();
|
||||
|
||||
if (LastConnCheckbox.Pressed || !HwidCheckbox.Pressed)
|
||||
{
|
||||
Hwid = null;
|
||||
return;
|
||||
}
|
||||
Hwid = Convert.FromHexString(hwidString);
|
||||
}
|
||||
|
||||
private void OnTypeChanged()
|
||||
{
|
||||
TypeOption.ModulateSelfOverride = null;
|
||||
Tabs.SetTabVisible((int) TabNumbers.Roles, TypeOption.SelectedId == (int) Types.Role);
|
||||
}
|
||||
|
||||
private void UpdateSubmitEnabled()
|
||||
{
|
||||
SubmitButton.Disabled = ErrorLevel != ErrorLevelEnum.None;
|
||||
}
|
||||
|
||||
private void OnPlayerNameChanged()
|
||||
{
|
||||
if (PlayerUsername == PlayerNameLine.Text)
|
||||
return;
|
||||
PlayerUsername = PlayerNameLine.Text;
|
||||
if (!PlayerCheckbox.Pressed)
|
||||
return;
|
||||
if (string.IsNullOrWhiteSpace(PlayerUsername))
|
||||
ErrorLevel |= ErrorLevelEnum.PlayerName;
|
||||
else
|
||||
ErrorLevel &= ~ErrorLevelEnum.PlayerName;
|
||||
|
||||
UpdateSubmitEnabled();
|
||||
PlayerChanged?.Invoke(PlayerUsername);
|
||||
}
|
||||
|
||||
public void OnPlayerSelectionChanged(PlayerInfo? player)
|
||||
{
|
||||
PlayerNameLine.Text = player?.Username ?? string.Empty;
|
||||
OnPlayerNameChanged();
|
||||
}
|
||||
|
||||
private void ResetTextEditor(GUIBoundKeyEventArgs _)
|
||||
{
|
||||
ReasonTextEdit.ModulateSelfOverride = null;
|
||||
ReasonTextEdit.OnKeyBindDown -= ResetTextEditor;
|
||||
}
|
||||
|
||||
private void SubmitButtonOnOnPressed(BaseButton.ButtonEventArgs obj)
|
||||
{
|
||||
string[]? roles = null;
|
||||
if (TypeOption.SelectedId == (int) Types.Role)
|
||||
{
|
||||
var rolesList = new List<string>();
|
||||
if (_roleCheckboxes.Count == 0)
|
||||
throw new DebugAssertException("RoleCheckboxes was empty");
|
||||
|
||||
rolesList.AddRange(_roleCheckboxes.Where(c => c is { Pressed: true, Text: { } }).Select(c => c.Text!));
|
||||
|
||||
if (rolesList.Count == 0)
|
||||
{
|
||||
Tabs.CurrentTab = (int) TabNumbers.Roles;
|
||||
return;
|
||||
}
|
||||
|
||||
roles = rolesList.ToArray();
|
||||
}
|
||||
|
||||
if (TypeOption.SelectedId == (int) Types.None)
|
||||
{
|
||||
TypeOption.ModulateSelfOverride = Color.Red;
|
||||
Tabs.CurrentTab = (int) TabNumbers.BasicInfo;
|
||||
return;
|
||||
}
|
||||
|
||||
var reason = Rope.Collapse(ReasonTextEdit.TextRope);
|
||||
if (string.IsNullOrWhiteSpace(reason))
|
||||
{
|
||||
//Tabs.CurrentTab = (int) TabNumbers.Text;
|
||||
Tabs.CurrentTab = (int) TabNumbers.BasicInfo;
|
||||
ReasonTextEdit.GrabKeyboardFocus();
|
||||
ReasonTextEdit.ModulateSelfOverride = Color.Red;
|
||||
ReasonTextEdit.OnKeyBindDown += ResetTextEditor;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ButtonResetOn is null)
|
||||
{
|
||||
ButtonResetOn = _gameTiming.CurTime.Add(TimeSpan.FromSeconds(3));
|
||||
SubmitButton.ModulateSelfOverride = Color.Red;
|
||||
SubmitButton.Text = Loc.GetString("ban-panel-confirm");
|
||||
return;
|
||||
}
|
||||
|
||||
var player = PlayerCheckbox.Pressed ? PlayerUsername : null;
|
||||
var useLastIp = IpCheckbox.Pressed && LastConnCheckbox.Pressed && IpAddress is null;
|
||||
var useLastHwid = HwidCheckbox.Pressed && LastConnCheckbox.Pressed && Hwid is null;
|
||||
var severity = (NoteSeverity) SeverityOption.SelectedId;
|
||||
BanSubmitted?.Invoke(player, IpAddress, useLastIp, Hwid, useLastHwid, (uint) (TimeEntered * Multiplier), reason, severity, roles);
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
|
||||
// This checks for null for free, do not invert it as null always produces a false value
|
||||
if (_gameTiming.CurTime > ButtonResetOn)
|
||||
{
|
||||
ButtonResetOn = null;
|
||||
SubmitButton.ModulateSelfOverride = null;
|
||||
SubmitButton.Text = Loc.GetString("ban-panel-submit");
|
||||
}
|
||||
}
|
||||
}
|
||||
48
Content.Client/Administration/UI/BanPanel/BanPanelEui.cs
Normal file
48
Content.Client/Administration/UI/BanPanel/BanPanelEui.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Client.Eui;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.Eui;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Content.Client.Administration.UI.BanPanel;
|
||||
|
||||
[UsedImplicitly]
|
||||
public sealed class BanPanelEui : BaseEui
|
||||
{
|
||||
private BanPanel BanPanel { get; }
|
||||
|
||||
public BanPanelEui()
|
||||
{
|
||||
BanPanel = new BanPanel();
|
||||
BanPanel.OnClose += () => SendMessage(new CloseEuiMessage());
|
||||
BanPanel.BanSubmitted += (player, ip, useLastIp, hwid, useLastHwid, minutes, reason, severity, roles)
|
||||
=> SendMessage(new BanPanelEuiStateMsg.CreateBanRequest(player, ip, useLastIp, hwid, useLastHwid, minutes, reason, severity, roles));
|
||||
BanPanel.PlayerChanged += player => SendMessage(new BanPanelEuiStateMsg.GetPlayerInfoRequest(player));
|
||||
}
|
||||
|
||||
public override void HandleState(EuiStateBase state)
|
||||
{
|
||||
if (state is not BanPanelEuiState s)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
BanPanel.UpdateBanFlag(s.HasBan);
|
||||
BanPanel.UpdatePlayerData(s.PlayerName);
|
||||
}
|
||||
|
||||
public override void Opened()
|
||||
{
|
||||
BanPanel.OpenCentered();
|
||||
}
|
||||
|
||||
public override void Closed()
|
||||
{
|
||||
BanPanel.Close();
|
||||
BanPanel.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Linq;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using Content.Client.Administration.Managers;
|
||||
@@ -123,12 +123,10 @@ namespace Content.Client.Administration.UI.Bwoink
|
||||
_console.ExecuteCommand($"adminnotes \"{_currentPlayer.SessionId}\"");
|
||||
};
|
||||
|
||||
// ew
|
||||
Ban.OnPressed += _ =>
|
||||
{
|
||||
var bw = new BanWindow();
|
||||
bw.OnPlayerSelectionChanged(_currentPlayer);
|
||||
bw.Open();
|
||||
if (_currentPlayer is not null)
|
||||
_console.ExecuteCommand($"banpanel \"{_currentPlayer.SessionId}\"");
|
||||
};
|
||||
|
||||
Kick.OnPressed += _ =>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<Control xmlns="https://spacestation14.io"
|
||||
<Control xmlns="https://spacestation14.io"
|
||||
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client">
|
||||
<PanelContainer>
|
||||
<PanelContainer.PanelOverride>
|
||||
@@ -8,8 +8,8 @@
|
||||
<ScrollContainer VerticalExpand="True" HorizontalExpand="True" HScrollEnabled="False">
|
||||
<BoxContainer Orientation="Vertical" Name="Notes" Access="Public" VerticalExpand="True"/>
|
||||
</ScrollContainer>
|
||||
<Label Name="NewNoteLabel" Text="{Loc admin-notes-new-note}" />
|
||||
<HistoryLineEdit Name="NewNote"/>
|
||||
<Button Name="ShowMoreButton" Text="{Loc admin-notes-show-more}" Visible="False" HorizontalAlignment="Center" />
|
||||
<Button Name="NewNoteButton" Text="{Loc admin-notes-new-note}" Disabled="True" />
|
||||
</BoxContainer>
|
||||
</PanelContainer>
|
||||
</Control>
|
||||
|
||||
@@ -1,119 +1,167 @@
|
||||
using System.Linq;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Shared.Administration.Notes;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Database;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using static Robust.Client.UserInterface.Controls.LineEdit;
|
||||
using Robust.Shared.Configuration;
|
||||
|
||||
namespace Content.Client.Administration.UI.Notes;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class AdminNotesControl : Control
|
||||
{
|
||||
public event Action<int, string>? OnNoteChanged;
|
||||
public event Action<string>? OnNewNoteEntered;
|
||||
public event Action<int>? OnNoteDeleted;
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystem = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
|
||||
public event Action<int, NoteType, string, NoteSeverity?, bool, DateTime?>? NoteChanged;
|
||||
public event Action<NoteType, string, NoteSeverity?, bool, DateTime?>? NewNoteEntered;
|
||||
public event Action<int, NoteType>? NoteDeleted;
|
||||
|
||||
private AdminNotesLinePopup? _popup;
|
||||
private readonly SpriteSystem _sprites;
|
||||
private readonly double _noteFreshDays;
|
||||
private readonly double _noteStaleDays;
|
||||
|
||||
public AdminNotesControl()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
_sprites = _entitySystem.GetEntitySystem<SpriteSystem>();
|
||||
|
||||
NewNote.OnTextEntered += NewNoteEntered;
|
||||
// There should be a warning somewhere if fresh > stale
|
||||
// I thought about putting it here but then it would spam you every time you open notes
|
||||
_noteFreshDays = _cfg.GetCVar(CCVars.NoteFreshDays);
|
||||
_noteStaleDays = _cfg.GetCVar(CCVars.NoteStaleDays);
|
||||
|
||||
NewNoteButton.OnPressed += OnNewNoteButtonPressed;
|
||||
ShowMoreButton.OnPressed += OnShowMoreButtonPressed;
|
||||
}
|
||||
|
||||
private Dictionary<int, AdminNotesLine> Inputs { get; } = new();
|
||||
private Dictionary<(int noteId, NoteType noteType), AdminNotesLine> Inputs { get; } = new();
|
||||
private bool CanCreate { get; set; }
|
||||
private bool CanDelete { get; set; }
|
||||
private bool CanEdit { get; set; }
|
||||
private string PlayerName { get; set; } = "<Error>";
|
||||
|
||||
private void NewNoteEntered(LineEditEventArgs args)
|
||||
public void SetPlayerName(string playerName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(args.Text))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
NewNote.Clear();
|
||||
OnNewNoteEntered?.Invoke(args.Text);
|
||||
PlayerName = playerName;
|
||||
}
|
||||
|
||||
private void NoteSubmitted(AdminNotesLine input)
|
||||
private void OnNewNoteButtonPressed(BaseButton.ButtonEventArgs obj)
|
||||
{
|
||||
var text = input.EditText.Trim();
|
||||
if (input.OriginalMessage == text)
|
||||
var noteEdit = new NoteEdit(null, PlayerName, CanCreate, CanEdit);
|
||||
noteEdit.SubmitPressed += OnNoteSubmitted;
|
||||
noteEdit.OpenCentered();
|
||||
}
|
||||
|
||||
private void OnNoteSubmitted(int id, NoteType type, string message, NoteSeverity? severity, bool secret, DateTime? expiryTime)
|
||||
{
|
||||
if (id == 0)
|
||||
{
|
||||
NewNoteEntered?.Invoke(type, message, severity, secret, expiryTime);
|
||||
return;
|
||||
}
|
||||
|
||||
OnNoteChanged?.Invoke(input.Id, text);
|
||||
NoteChanged?.Invoke(id, type, message, severity, secret, expiryTime);
|
||||
}
|
||||
|
||||
private bool NoteClicked(AdminNotesLine line)
|
||||
{
|
||||
ClosePopup();
|
||||
|
||||
_popup = new AdminNotesLinePopup(line.Note, CanDelete, CanEdit);
|
||||
_popup.OnEditPressed += noteId =>
|
||||
_popup = new AdminNotesLinePopup(line.Note, PlayerName, CanDelete, CanEdit);
|
||||
_popup.OnEditPressed += (noteId, noteType) =>
|
||||
{
|
||||
if (!Inputs.TryGetValue(noteId, out var input))
|
||||
if (!Inputs.TryGetValue((noteId, noteType), out var input))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
input.SetEditable(true);
|
||||
var noteEdit = new NoteEdit(input.Note, PlayerName, CanCreate, CanEdit);
|
||||
noteEdit.SubmitPressed += OnNoteSubmitted;
|
||||
noteEdit.OpenCentered();
|
||||
};
|
||||
_popup.OnDeletePressed += noteId => OnNoteDeleted?.Invoke(noteId);
|
||||
|
||||
_popup.OnDeletePressed += (noteId, noteType) => NoteDeleted?.Invoke(noteId, noteType);
|
||||
var box = UIBox2.FromDimensions(UserInterfaceManager.MousePositionScaled.Position, Vector2.One);
|
||||
_popup.Open(box);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void ClosePopup()
|
||||
public void SetNotes(Dictionary<(int, NoteType), SharedAdminNote> notes)
|
||||
{
|
||||
_popup?.Close();
|
||||
_popup = null;
|
||||
}
|
||||
|
||||
public void SetNotes(Dictionary<int, SharedAdminNote> notes)
|
||||
{
|
||||
foreach (var (id, input) in Inputs)
|
||||
foreach (var (key, input) in Inputs)
|
||||
{
|
||||
if (!notes.ContainsKey(id))
|
||||
if (!notes.ContainsKey(key))
|
||||
{
|
||||
Notes.RemoveChild(input);
|
||||
Inputs.Remove(id);
|
||||
// Yes this is slower than just updating, but new notes get added at the bottom. The user won't notice.
|
||||
Notes.RemoveAllChildren();
|
||||
Inputs.Clear();
|
||||
break;
|
||||
}
|
||||
Notes.RemoveChild(input);
|
||||
Inputs.Remove(key);
|
||||
}
|
||||
|
||||
foreach (var note in notes.Values.OrderBy(note => note.Id))
|
||||
var showMoreButtonVisible = false;
|
||||
foreach (var note in notes.Values.OrderByDescending(note => note.CreatedAt))
|
||||
{
|
||||
if (Inputs.TryGetValue(note.Id, out var input))
|
||||
if (Inputs.TryGetValue((note.Id, note.NoteType), out var input))
|
||||
{
|
||||
input.UpdateNote(note);
|
||||
continue;
|
||||
}
|
||||
|
||||
input = new AdminNotesLine(note);
|
||||
input.OnSubmitted += NoteSubmitted;
|
||||
input = new AdminNotesLine(_sprites, note);
|
||||
input.OnClicked += NoteClicked;
|
||||
|
||||
var timeDiff = DateTime.UtcNow - note.CreatedAt;
|
||||
float alpha;
|
||||
if (_noteFreshDays == 0 || timeDiff.TotalDays <= _noteFreshDays)
|
||||
{
|
||||
alpha = 1f;
|
||||
}
|
||||
else if (_noteStaleDays == 0 || timeDiff.TotalDays > _noteStaleDays)
|
||||
{
|
||||
alpha = 0f;
|
||||
input.Visible = false;
|
||||
showMoreButtonVisible = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
alpha = (float) (1 - Math.Clamp((timeDiff.TotalDays - _noteFreshDays) / (_noteStaleDays - _noteFreshDays), 0, 1));
|
||||
}
|
||||
|
||||
input.Modulate = input.Modulate.WithAlpha(alpha);
|
||||
Notes.AddChild(input);
|
||||
Inputs[note.Id] = input;
|
||||
Inputs[(note.Id, note.NoteType)] = input;
|
||||
ShowMoreButton.Visible = showMoreButtonVisible;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnShowMoreButtonPressed(BaseButton.ButtonEventArgs obj)
|
||||
{
|
||||
foreach (var input in Inputs.Values)
|
||||
{
|
||||
input.Modulate = input.Modulate.WithAlpha(1f);
|
||||
input.Visible = true;
|
||||
}
|
||||
|
||||
ShowMoreButton.Visible = false;
|
||||
}
|
||||
|
||||
public void SetPermissions(bool create, bool delete, bool edit)
|
||||
{
|
||||
CanCreate = create;
|
||||
CanDelete = delete;
|
||||
CanEdit = edit;
|
||||
NewNoteLabel.Visible = create;
|
||||
NewNote.Visible = create;
|
||||
NewNoteButton.Visible = create;
|
||||
NewNoteButton.Disabled = !create;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
@@ -125,21 +173,14 @@ public sealed partial class AdminNotesControl : Control
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var input in Inputs.Values)
|
||||
{
|
||||
input.OnSubmitted -= NoteSubmitted;
|
||||
}
|
||||
|
||||
Inputs.Clear();
|
||||
NewNote.OnTextEntered -= NewNoteEntered;
|
||||
NewNoteButton.OnPressed -= OnNewNoteButtonPressed;
|
||||
|
||||
if (_popup != null)
|
||||
{
|
||||
UserInterfaceManager.PopupRoot.RemoveChild(_popup);
|
||||
}
|
||||
|
||||
OnNoteChanged = null;
|
||||
OnNewNoteEntered = null;
|
||||
OnNoteDeleted = null;
|
||||
NoteDeleted = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Content.Client.Eui;
|
||||
using Content.Client.Eui;
|
||||
using Content.Shared.Administration.Notes;
|
||||
using Content.Shared.Eui;
|
||||
using JetBrains.Annotations;
|
||||
@@ -14,15 +14,10 @@ public sealed class AdminNotesEui : BaseEui
|
||||
NoteWindow = new AdminNotesWindow();
|
||||
NoteControl = NoteWindow.Notes;
|
||||
|
||||
NoteControl.OnNoteChanged += (id, text) => SendMessage(new EditNoteRequest(id, text));
|
||||
NoteControl.OnNewNoteEntered += text => SendMessage(new CreateNoteRequest(text));
|
||||
NoteControl.OnNoteDeleted += id => SendMessage(new DeleteNoteRequest(id));
|
||||
NoteWindow.OnClose += OnClosed;
|
||||
}
|
||||
|
||||
private void OnClosed()
|
||||
{
|
||||
SendMessage(new CloseEuiMessage());
|
||||
NoteControl.NoteChanged += (id, type, text, severity, secret, expiryTime) => SendMessage(new EditNoteRequest(id, type, text, severity, secret, expiryTime));
|
||||
NoteControl.NewNoteEntered += (type, text, severity, secret, expiryTime) => SendMessage(new CreateNoteRequest(type, text, severity, secret, expiryTime));
|
||||
NoteControl.NoteDeleted += (id, type) => SendMessage(new DeleteNoteRequest(id, type));
|
||||
NoteWindow.OnClose += () => SendMessage(new CloseEuiMessage());
|
||||
}
|
||||
|
||||
public override void Closed()
|
||||
@@ -43,6 +38,7 @@ public sealed class AdminNotesEui : BaseEui
|
||||
}
|
||||
|
||||
NoteWindow.SetTitlePlayer(s.NotedPlayerName);
|
||||
NoteControl.SetPlayerName(s.NotedPlayerName);
|
||||
NoteControl.SetNotes(s.Notes);
|
||||
NoteControl.SetPermissions(s.CanCreate, s.CanDelete, s.CanEdit);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
<BoxContainer xmlns="https://spacestation14.io"
|
||||
<BoxContainer xmlns="https://spacestation14.io"
|
||||
xmlns:cc="clr-namespace:Content.Client.Administration.UI.CustomControls"
|
||||
Orientation="Vertical">
|
||||
<cc:HSeparator Name="Separator"/>
|
||||
<BoxContainer Name ="MetadataContainer" Orientation="Horizontal">
|
||||
<TextureRect Name="SeverityRect" Margin="2"/>
|
||||
<Label Name="TimeLabel" Margin="4 0" />
|
||||
<cc:HSeparator Margin="4 0" />
|
||||
<Label Name="ServerLabel" />
|
||||
<cc:HSeparator Margin="4 0" />
|
||||
<Label Name="RoundLabel" />
|
||||
<cc:HSeparator Margin="4 0" />
|
||||
<Label Name="AdminLabel" />
|
||||
<cc:HSeparator Margin="4 0" />
|
||||
<Label Name="PlaytimeLabel" />
|
||||
<cc:HSeparator Name="SecretSeparator" Visible="False" Margin="4 0" />
|
||||
<Label Name="SecretLabel" Text="{Loc admin-notes-secret} " Visible="False" />
|
||||
</BoxContainer>
|
||||
<RichTextLabel Name="NoteLabel" />
|
||||
<Label Name="ExpiresLabel" Text="{Loc admin-note-editor-expiry-label}" Visible="False" />
|
||||
<Label Name="ExtraLabel" Visible="False" Modulate="#1AA7EC" />
|
||||
<Label Name="EditedLabel" Visible="False" />
|
||||
<cc:HSeparator Name="Separator" />
|
||||
</BoxContainer>
|
||||
|
||||
@@ -1,85 +1,172 @@
|
||||
using Content.Shared.Administration.Notes;
|
||||
using System.Text;
|
||||
using Content.Client.Resources;
|
||||
using Content.Shared.Administration.Notes;
|
||||
using Content.Shared.Database;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Input;
|
||||
using static Robust.Client.UserInterface.Controls.LineEdit;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.Administration.UI.Notes;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class AdminNotesLine : BoxContainer
|
||||
{
|
||||
private RichTextLabel? _label;
|
||||
private LineEdit? _edit;
|
||||
private readonly SpriteSystem _sprites;
|
||||
|
||||
public AdminNotesLine(SharedAdminNote note)
|
||||
private const string AdminNotesTextureBase = "/Textures/Interface/AdminNotes/";
|
||||
private static readonly Dictionary<NoteSeverity, string> SeverityIcons = new()
|
||||
{
|
||||
{ NoteSeverity.None, AdminNotesTextureBase + "none_button.png" },
|
||||
{ NoteSeverity.Minor, AdminNotesTextureBase + "minor_button.png" },
|
||||
{ NoteSeverity.Medium, AdminNotesTextureBase + "medium_button.png" },
|
||||
{ NoteSeverity.High, AdminNotesTextureBase + "high_button.png" },
|
||||
};
|
||||
private static readonly Dictionary<NoteType, string> NoteTypeIcons = new()
|
||||
{
|
||||
{ NoteType.Message, AdminNotesTextureBase + "message.png" },
|
||||
{ NoteType.Watchlist, AdminNotesTextureBase + "watchlist.png" },
|
||||
};
|
||||
|
||||
public AdminNotesLine(SpriteSystem sprites, SharedAdminNote note)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
_sprites = sprites;
|
||||
|
||||
Note = note;
|
||||
MouseFilter = MouseFilterMode.Pass;
|
||||
|
||||
AddLabel();
|
||||
Separator.Visible = true;
|
||||
Refresh();
|
||||
}
|
||||
|
||||
public SharedAdminNote Note { get; private set; }
|
||||
public int Id => Note.Id;
|
||||
public string OriginalMessage => Note.Message;
|
||||
public string EditText => _edit?.Text ?? OriginalMessage;
|
||||
|
||||
public event Action<AdminNotesLine>? OnSubmitted;
|
||||
public event Func<AdminNotesLine, bool>? OnClicked;
|
||||
|
||||
private void AddLabel()
|
||||
/// <summary>
|
||||
/// Attempts to refresh the current note line with new data. The note it draws data on is stored in <see cref="Note"/>
|
||||
/// </summary>
|
||||
private void Refresh()
|
||||
{
|
||||
if (_edit != null)
|
||||
{
|
||||
_edit.OnTextEntered -= Submitted;
|
||||
_edit.OnFocusExit -= Submitted;
|
||||
string? iconPath;
|
||||
if(Note.NoteSeverity is not null)
|
||||
SeverityIcons.TryGetValue(Note.NoteSeverity.Value, out iconPath);
|
||||
else
|
||||
NoteTypeIcons.TryGetValue(Note.NoteType, out iconPath);
|
||||
|
||||
RemoveChild(_edit);
|
||||
_edit = null;
|
||||
if (iconPath is null)
|
||||
{
|
||||
SeverityRect.Visible = false;
|
||||
Logger.WarningS("admin.notes", $"Could not find an icon for note ID {Note.Id}");
|
||||
}
|
||||
else
|
||||
{
|
||||
SeverityRect.Texture = _sprites.Frame0(new SpriteSpecifier.Texture(new ResPath(iconPath)));
|
||||
}
|
||||
|
||||
_label = new RichTextLabel();
|
||||
_label.SetMessage(Note.Message);
|
||||
TimeLabel.Text = Note.CreatedAt.ToString("yyyy-MM-dd HH:mm:ss");
|
||||
ServerLabel.Text = Note.ServerName ?? "Unknown";
|
||||
RoundLabel.Text = Note.Round == null ? "Unknown round" : "Round " + Note.Round;
|
||||
AdminLabel.Text = Note.CreatedByName;
|
||||
PlaytimeLabel.Text = $"{Note.PlaytimeAtNote.TotalHours: 0.0}h";
|
||||
|
||||
AddChild(_label);
|
||||
_label.SetPositionFirst();
|
||||
|
||||
Separator.Visible = true;
|
||||
}
|
||||
|
||||
private void AddLineEdit()
|
||||
{
|
||||
if (_label != null)
|
||||
if (Note.Secret)
|
||||
{
|
||||
RemoveChild(_label);
|
||||
_label = null;
|
||||
SecretSeparator.Visible = true;
|
||||
SecretLabel.Visible = true;
|
||||
}
|
||||
|
||||
_edit = new LineEdit {Text = Note.Message};
|
||||
_edit.OnTextEntered += Submitted;
|
||||
_edit.OnFocusExit += Submitted;
|
||||
if (Note.UnbannedTime is not null)
|
||||
{
|
||||
ExtraLabel.Text = Loc.GetString("admin-notes-unbanned", ("admin", Note.UnbannedByName ?? "[error]"), ("date", Note.UnbannedTime));
|
||||
ExtraLabel.Visible = true;
|
||||
}
|
||||
else if (Note.ExpiryTime is not null)
|
||||
{
|
||||
// Notes should never be visible when expired, bans should
|
||||
if (Note.ExpiryTime.Value > DateTime.UtcNow)
|
||||
{
|
||||
ExpiresLabel.Text = Loc.GetString("admin-note-editor-expiry-label-params",
|
||||
("date", Note.ExpiryTime.Value.ToString("yyyy-MM-dd HH:mm:ss")),
|
||||
("expiresIn", (Note.ExpiryTime.Value - DateTime.UtcNow).ToString("d'd 'hh':'mm")));
|
||||
ExpiresLabel.Modulate = Color.FromHex("#86DC3D");
|
||||
}
|
||||
else
|
||||
{
|
||||
ExpiresLabel.Text = Loc.GetString("admin-note-editor-expiry-label-expired");
|
||||
}
|
||||
ExpiresLabel.Visible = true;
|
||||
}
|
||||
|
||||
AddChild(_edit);
|
||||
_edit.SetPositionFirst();
|
||||
_edit.GrabKeyboardFocus();
|
||||
_edit.CursorPosition = _edit.Text.Length;
|
||||
if (Note.LastEditedAt > Note.CreatedAt)
|
||||
{
|
||||
EditedLabel.Text = Loc.GetString("admin-notes-edited", ("author", Note.EditedByName), ("date", Note.LastEditedAt));
|
||||
EditedLabel.Visible = true;
|
||||
}
|
||||
|
||||
Separator.Visible = false;
|
||||
switch (Note.NoteType)
|
||||
{
|
||||
case NoteType.ServerBan:
|
||||
NoteLabel.SetMessage(FormatBanMessage());
|
||||
break;
|
||||
case NoteType.RoleBan:
|
||||
NoteLabel.SetMessage(FormatRoleBanMessage());
|
||||
break;
|
||||
case NoteType.Note:
|
||||
case NoteType.Watchlist:
|
||||
case NoteType.Message:
|
||||
default:
|
||||
NoteLabel.SetMessage(Note.Message);
|
||||
break;
|
||||
}
|
||||
|
||||
if (Note.Seen == true)
|
||||
{
|
||||
ExtraLabel.Text = Loc.GetString("admin-notes-message-seen");
|
||||
ExtraLabel.Visible = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void Submitted(LineEditEventArgs args)
|
||||
private string FormatBanMessage()
|
||||
{
|
||||
OnSubmitted?.Invoke(this);
|
||||
var banMessage = new StringBuilder($"{Loc.GetString("admin-notes-banned-from")} {Loc.GetString("admin-notes-the-server")} ");
|
||||
return FormatBanMessageCommon(banMessage);
|
||||
}
|
||||
|
||||
AddLabel();
|
||||
private string FormatRoleBanMessage()
|
||||
{
|
||||
var banMessage = new StringBuilder($"{Loc.GetString("admin-notes-banned-from")} {string.Join(", ", Note.BannedRoles ?? new []{"unknown"})} ");
|
||||
return FormatBanMessageCommon(banMessage);
|
||||
}
|
||||
|
||||
var note = Note with {Message = args.Text};
|
||||
UpdateNote(note);
|
||||
private string FormatBanMessageCommon(StringBuilder sb)
|
||||
{
|
||||
if (Note.ExpiryTime is null)
|
||||
{
|
||||
sb.Append(Loc.GetString("admin-notes-permanently"));
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append("for ");
|
||||
var banLength = Note.ExpiryTime.Value - Note.CreatedAt;
|
||||
if (banLength.Days > 0)
|
||||
sb.Append(Loc.GetString("admin-notes-days", ("days", banLength.TotalDays.ToString(".00"))));
|
||||
else if (banLength.Hours > 0)
|
||||
sb.Append(Loc.GetString("admin-notes-hours", ("hours", banLength.TotalHours.ToString(".00"))));
|
||||
else
|
||||
sb.Append(Loc.GetString("admin-notes-minutes", ("minutes", banLength.TotalMinutes.ToString(".00"))));
|
||||
}
|
||||
|
||||
sb.Append(" - ");
|
||||
sb.Append(Note.Message);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
protected override void KeyBindDown(GUIBoundKeyEventArgs args)
|
||||
@@ -101,24 +188,7 @@ public sealed partial class AdminNotesLine : BoxContainer
|
||||
public void UpdateNote(SharedAdminNote note)
|
||||
{
|
||||
Note = note;
|
||||
_label?.SetMessage(note.Message);
|
||||
|
||||
if (_edit != null && _edit.Text != note.Message)
|
||||
{
|
||||
_edit.Text = note.Message;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetEditable(bool editable)
|
||||
{
|
||||
if (editable)
|
||||
{
|
||||
AddLineEdit();
|
||||
}
|
||||
else
|
||||
{
|
||||
AddLabel();
|
||||
}
|
||||
Refresh();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
@@ -130,13 +200,6 @@ public sealed partial class AdminNotesLine : BoxContainer
|
||||
return;
|
||||
}
|
||||
|
||||
if (_edit != null)
|
||||
{
|
||||
_edit.OnTextEntered -= Submitted;
|
||||
_edit.OnFocusExit -= Submitted;
|
||||
}
|
||||
|
||||
OnSubmitted = null;
|
||||
OnClicked = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
<Popup xmlns="https://spacestation14.io"
|
||||
<Popup xmlns="https://spacestation14.io"
|
||||
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client">
|
||||
<PanelContainer>
|
||||
<PanelContainer.PanelOverride>
|
||||
<gfx:StyleBoxFlat BackgroundColor="#25252A"/>
|
||||
<gfx:StyleBoxFlat BackgroundColor="#25252A" BorderThickness="1" BorderColor="#18181B"/>
|
||||
</PanelContainer.PanelOverride>
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<Label Name="PlayerNameLabel"/>
|
||||
<Label Name="IdLabel"/>
|
||||
<Label Name="TypeLabel"/>
|
||||
<Label Name="SeverityLabel"/>
|
||||
<Label Name="RoundIdLabel"/>
|
||||
<Label Name="CreatedByLabel"/>
|
||||
<Label Name="CreatedAtLabel"/>
|
||||
<Label Name="EditedByLabel"/>
|
||||
<Label Name="EditedAtLabel"/>
|
||||
<Label Name="ExpiryTimeLabel"/>
|
||||
<TextEdit Name="NoteTextEdit" Editable="False" MinHeight="24" />
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Button Name="EditButton" Text="{Loc admin-notes-edit}"/>
|
||||
<Control HorizontalExpand="True"/>
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
using Content.Shared.Administration.Notes;
|
||||
using Content.Shared.Administration.Notes;
|
||||
using Content.Shared.Database;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Timing;
|
||||
using static Robust.Client.UserInterface.Controls.BaseButton;
|
||||
|
||||
namespace Content.Client.Administration.UI.Notes;
|
||||
@@ -9,57 +11,89 @@ namespace Content.Client.Administration.UI.Notes;
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class AdminNotesLinePopup : Popup
|
||||
{
|
||||
public event Action<int>? OnEditPressed;
|
||||
public event Action<int>? OnDeletePressed;
|
||||
public event Action<int, NoteType>? OnEditPressed;
|
||||
public event Action<int, NoteType>? OnDeletePressed;
|
||||
|
||||
public AdminNotesLinePopup(SharedAdminNote note, bool showDelete, bool showEdit)
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
|
||||
public AdminNotesLinePopup(SharedAdminNote note, string playerName, bool showDelete, bool showEdit)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
NoteId = note.Id;
|
||||
NoteType = note.NoteType;
|
||||
DeleteButton.Visible = showDelete;
|
||||
EditButton.Visible = showEdit;
|
||||
|
||||
UserInterfaceManager.ModalRoot.AddChild(this);
|
||||
|
||||
PlayerNameLabel.Text = Loc.GetString("admin-notes-for", ("player", playerName));
|
||||
IdLabel.Text = Loc.GetString("admin-notes-id", ("id", note.Id));
|
||||
TypeLabel.Text = Loc.GetString("admin-notes-type", ("type", note.NoteType));
|
||||
SeverityLabel.Text = Loc.GetString("admin-notes-severity", ("severity", note.NoteSeverity ?? NoteSeverity.None));
|
||||
RoundIdLabel.Text = note.Round == null
|
||||
? Loc.GetString("admin-notes-round-id-unknown")
|
||||
: Loc.GetString("admin-notes-round-id", ("id", note.Round));
|
||||
CreatedByLabel.Text = Loc.GetString("admin-notes-created-by", ("author", note.CreatedByName));
|
||||
CreatedAtLabel.Text = Loc.GetString("admin-notes-created-at", ("date", note.CreatedAt.ToString("dd MMM yyyy HH:mm:ss")));
|
||||
CreatedAtLabel.Text = Loc.GetString("admin-notes-created-at", ("date", note.CreatedAt.ToString("yyyy-MM-dd HH:mm:ss")));
|
||||
EditedByLabel.Text = Loc.GetString("admin-notes-last-edited-by", ("author", note.EditedByName));
|
||||
EditedAtLabel.Text = Loc.GetString("admin-notes-last-edited-at", ("date", note.LastEditedAt.ToString("dd MMM yyyy HH:mm:ss")));
|
||||
EditedAtLabel.Text = Loc.GetString("admin-notes-last-edited-at", ("date", note.LastEditedAt?.ToString("yyyy-MM-dd HH:mm:ss") ?? Loc.GetString("admin-notes-edited-never")));
|
||||
ExpiryTimeLabel.Text = note.ExpiryTime == null
|
||||
? Loc.GetString("admin-notes-expires-never")
|
||||
: Loc.GetString("admin-notes-expires", ("expires", note.ExpiryTime.Value.ToString("yyyy-MM-dd HH:mm:ss")));
|
||||
NoteTextEdit.InsertAtCursor(note.Message);
|
||||
|
||||
if (note.NoteType is NoteType.ServerBan or NoteType.RoleBan)
|
||||
{
|
||||
DeleteButton.Text = Loc.GetString("admin-notes-hide");
|
||||
}
|
||||
|
||||
EditButton.OnPressed += EditPressed;
|
||||
DeleteButton.OnPressed += DeletePressed;
|
||||
}
|
||||
|
||||
private int NoteId { get; }
|
||||
private bool ConfirmingDelete { get; set; }
|
||||
private NoteType NoteType { get; }
|
||||
private TimeSpan? DeleteResetOn { get; set; }
|
||||
|
||||
private void EditPressed(ButtonEventArgs args)
|
||||
{
|
||||
OnEditPressed?.Invoke(NoteId);
|
||||
OnEditPressed?.Invoke(NoteId, NoteType);
|
||||
Close();
|
||||
}
|
||||
|
||||
private void DeletePressed(ButtonEventArgs args)
|
||||
{
|
||||
if (!ConfirmingDelete)
|
||||
if (DeleteResetOn is null)
|
||||
{
|
||||
ConfirmingDelete = true;
|
||||
DeleteResetOn = _gameTiming.CurTime.Add(TimeSpan.FromSeconds(3));
|
||||
DeleteButton.Text = Loc.GetString("admin-notes-delete-confirm");
|
||||
DeleteButton.ModulateSelfOverride = Color.Red;
|
||||
return;
|
||||
}
|
||||
|
||||
ConfirmingDelete = false;
|
||||
DeleteButton.ModulateSelfOverride = null;
|
||||
OnDeletePressed?.Invoke(NoteId);
|
||||
ResetDeleteButton();
|
||||
OnDeletePressed?.Invoke(NoteId, NoteType);
|
||||
Close();
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
// This checks for null for free, do not invert it as null always produces a false value
|
||||
if (DeleteResetOn < _gameTiming.CurTime)
|
||||
{
|
||||
ResetDeleteButton();
|
||||
DeleteResetOn = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void ResetDeleteButton()
|
||||
{
|
||||
DeleteButton.Text = Loc.GetString("admin-notes-delete");
|
||||
DeleteButton.ModulateSelfOverride = null;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<DefaultWindow xmlns="https://spacestation14.io"
|
||||
<ui:FancyWindow xmlns="https://spacestation14.io"
|
||||
xmlns:notes="clr-namespace:Content.Client.Administration.UI.Notes"
|
||||
SetSize="400 400">
|
||||
<notes:AdminNotesControl Name="Notes" Access="Public"/>
|
||||
</DefaultWindow>
|
||||
xmlns:ui="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
SetSize="600 400"
|
||||
Title="Loading...">
|
||||
<notes:AdminNotesControl Name="Notes" Access="Public" Margin="4"/>
|
||||
</ui:FancyWindow>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
namespace Content.Client.Administration.UI.Notes;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class AdminNotesWindow : DefaultWindow
|
||||
public sealed partial class AdminNotesWindow : FancyWindow
|
||||
{
|
||||
public AdminNotesWindow()
|
||||
{
|
||||
|
||||
22
Content.Client/Administration/UI/Notes/NoteEdit.xaml
Normal file
22
Content.Client/Administration/UI/Notes/NoteEdit.xaml
Normal file
@@ -0,0 +1,22 @@
|
||||
<controls:FancyWindow xmlns="https://spacestation14.io"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
Title="Loading..."
|
||||
MinSize="400 200">
|
||||
<BoxContainer Orientation="Vertical" Margin="4">
|
||||
<TextEdit Name="NoteTextEdit" HorizontalExpand="True" VerticalExpand="True" />
|
||||
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
|
||||
<Label Name="ExpiryLabel" Text="{Loc admin-note-editor-expiry-label}" Visible="False" />
|
||||
<HistoryLineEdit Name="ExpiryLineEdit" PlaceHolder="{Loc admin-note-editor-expiry-placeholder}"
|
||||
Visible="False" HorizontalExpand="True" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
|
||||
<OptionButton Name="TypeOption" HorizontalAlignment="Center" />
|
||||
<OptionButton Name="SeverityOption" HorizontalAlignment="Center" />
|
||||
<CheckBox Name="SecretCheckBox" Text="{Loc admin-note-editor-secret}"
|
||||
ToolTip="{Loc admin-note-editor-secret-tooltip}" />
|
||||
<CheckBox Name="PermanentCheckBox" Pressed="True" Text="{Loc admin-note-editor-expiry-checkbox}"
|
||||
ToolTip="{Loc admin-note-editor-expiry-checkbox-tooltip}" />
|
||||
<Button Name="SubmitButton" Text="{Loc admin-note-editor-submit}" HorizontalAlignment="Right" />
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</controls:FancyWindow>
|
||||
242
Content.Client/Administration/UI/Notes/NoteEdit.xaml.cs
Normal file
242
Content.Client/Administration/UI/Notes/NoteEdit.xaml.cs
Normal file
@@ -0,0 +1,242 @@
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Shared.Administration.Notes;
|
||||
using Content.Shared.Database;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Console;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.Administration.UI.Notes;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class NoteEdit : FancyWindow
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly IClientConsoleHost _console = default!;
|
||||
|
||||
public event Action<int, NoteType, string, NoteSeverity?, bool, DateTime?>? SubmitPressed;
|
||||
|
||||
public NoteEdit(SharedAdminNote? note, string playerName, bool canCreate, bool canEdit)
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
RobustXamlLoader.Load(this);
|
||||
PlayerName = playerName;
|
||||
Title = Loc.GetString("admin-note-editor-title-new", ("player", PlayerName));
|
||||
|
||||
ResetSubmitButton();
|
||||
|
||||
TypeOption.AddItem(Loc.GetString("admin-note-editor-type-note"), (int) NoteType.Note);
|
||||
TypeOption.AddItem(Loc.GetString("admin-note-editor-type-message"), (int) NoteType.Message);
|
||||
TypeOption.AddItem(Loc.GetString("admin-note-editor-type-watchlist"), (int) NoteType.Watchlist);
|
||||
TypeOption.OnItemSelected += OnTypeChanged;
|
||||
|
||||
|
||||
SeverityOption.AddItem(Loc.GetString("admin-note-editor-severity-none"), (int) Shared.Database.NoteSeverity.None);
|
||||
SeverityOption.AddItem(Loc.GetString("admin-note-editor-severity-low"), (int) Shared.Database.NoteSeverity.Minor);
|
||||
SeverityOption.AddItem(Loc.GetString("admin-note-editor-severity-medium"), (int) Shared.Database.NoteSeverity.Medium);
|
||||
SeverityOption.AddItem(Loc.GetString("admin-note-editor-severity-high"), (int) Shared.Database.NoteSeverity.High);
|
||||
SeverityOption.OnItemSelected += OnSeverityChanged;
|
||||
|
||||
PermanentCheckBox.OnPressed += OnPermanentPressed;
|
||||
SecretCheckBox.OnPressed += OnSecretPressed;
|
||||
SubmitButton.OnPressed += OnSubmitButtonPressed;
|
||||
|
||||
if (note is null && !canCreate)
|
||||
{
|
||||
SubmitButton.Disabled = true;
|
||||
TypeOption.Disabled = true;
|
||||
SeverityOption.Disabled = true;
|
||||
}
|
||||
|
||||
if (note is not null)
|
||||
{
|
||||
Title = Loc.GetString("admin-note-editor-title-existing", ("id", note.Id), ("player", PlayerName), ("author", note.CreatedByName));
|
||||
NoteId = note.Id;
|
||||
|
||||
NoteType = note.NoteType;
|
||||
TypeOption.AddItem(Loc.GetString("admin-note-editor-type-server-ban"), (int) NoteType.ServerBan);
|
||||
TypeOption.AddItem(Loc.GetString("admin-note-editor-type-role-ban"), (int) NoteType.RoleBan);
|
||||
TypeOption.SelectId((int)NoteType);
|
||||
TypeOption.Disabled = true;
|
||||
|
||||
NoteTextEdit.InsertAtCursor(note.Message);
|
||||
|
||||
NoteSeverity = note.NoteSeverity ?? Shared.Database.NoteSeverity.Minor;
|
||||
SeverityOption.SelectId((int)NoteSeverity);
|
||||
SeverityOption.Disabled = note.NoteType is not (NoteType.Note or NoteType.ServerBan or NoteType.RoleBan);
|
||||
|
||||
IsSecret = note.Secret;
|
||||
SecretCheckBox.Pressed = note.Secret;
|
||||
SecretCheckBox.Disabled = note.NoteType is not NoteType.Note;
|
||||
ExpiryTime = note.ExpiryTime;
|
||||
if (ExpiryTime is not null)
|
||||
{
|
||||
PermanentCheckBox.Pressed = false;
|
||||
UpdatePermanentCheckboxFields();
|
||||
ExpiryLineEdit.Text = ExpiryTime.Value.ToString("yyyy-MM-dd HH:mm:ss");
|
||||
}
|
||||
|
||||
if (!canEdit)
|
||||
{
|
||||
SubmitButton.Disabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string PlayerName { get; }
|
||||
private int NoteId { get; }
|
||||
private bool IsSecret { get; set; }
|
||||
private NoteType NoteType { get; set; }
|
||||
private NoteSeverity? NoteSeverity { get; set; } = Shared.Database.NoteSeverity.None;
|
||||
private DateTime? ExpiryTime { get; set; }
|
||||
private TimeSpan? DeleteResetOn { get; set; }
|
||||
|
||||
private void OnTypeChanged(OptionButton.ItemSelectedEventArgs args)
|
||||
{
|
||||
// We should be resetting the underlying values too but the server handles that anyway
|
||||
switch (args.Id)
|
||||
{
|
||||
case (int) NoteType.Note: // Note: your standard note, does nothing special
|
||||
NoteType = NoteType.Note;
|
||||
SecretCheckBox.Disabled = false;
|
||||
SecretCheckBox.Pressed = false;
|
||||
SeverityOption.Disabled = false;
|
||||
PermanentCheckBox.Pressed = true;
|
||||
UpdatePermanentCheckboxFields();
|
||||
break;
|
||||
case (int) NoteType.Message: // Message: these are shown to the player when they log on
|
||||
NoteType = NoteType.Message;
|
||||
SecretCheckBox.Disabled = true;
|
||||
SecretCheckBox.Pressed = false;
|
||||
SeverityOption.Disabled = true;
|
||||
SeverityOption.SelectId((int) Shared.Database.NoteSeverity.None);
|
||||
NoteSeverity = null;
|
||||
PermanentCheckBox.Pressed = false;
|
||||
UpdatePermanentCheckboxFields();
|
||||
break;
|
||||
case (int) NoteType.Watchlist: // Watchlist: these are always secret and only shown to admins when the player logs on
|
||||
NoteType = NoteType.Watchlist;
|
||||
SecretCheckBox.Disabled = true;
|
||||
SecretCheckBox.Pressed = true;
|
||||
SeverityOption.Disabled = true;
|
||||
SeverityOption.SelectId((int) Shared.Database.NoteSeverity.None);
|
||||
NoteSeverity = null;
|
||||
PermanentCheckBox.Pressed = false;
|
||||
UpdatePermanentCheckboxFields();
|
||||
break;
|
||||
default: // Wuh oh
|
||||
throw new ArgumentOutOfRangeException(nameof(args.Id), args.Id, "Unknown note type");
|
||||
}
|
||||
|
||||
TypeOption.SelectId(args.Id);
|
||||
}
|
||||
|
||||
private void OnPermanentPressed(BaseButton.ButtonEventArgs _)
|
||||
{
|
||||
UpdatePermanentCheckboxFields();
|
||||
}
|
||||
|
||||
private void UpdatePermanentCheckboxFields()
|
||||
{
|
||||
ExpiryLabel.Visible = !PermanentCheckBox.Pressed;
|
||||
ExpiryLineEdit.Visible = !PermanentCheckBox.Pressed;
|
||||
|
||||
ExpiryLineEdit.Text = !PermanentCheckBox.Pressed ? DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss") : string.Empty;
|
||||
}
|
||||
|
||||
private void OnSecretPressed(BaseButton.ButtonEventArgs _)
|
||||
{
|
||||
IsSecret = SecretCheckBox.Pressed;
|
||||
}
|
||||
|
||||
private void OnSeverityChanged(OptionButton.ItemSelectedEventArgs args)
|
||||
{
|
||||
NoteSeverity = (NoteSeverity) args.Id;
|
||||
SeverityOption.SelectId(args.Id);
|
||||
}
|
||||
|
||||
private void OnSubmitButtonPressed(BaseButton.ButtonEventArgs args)
|
||||
{
|
||||
if (!ParseExpiryTime())
|
||||
return;
|
||||
if (DeleteResetOn is null)
|
||||
{
|
||||
DeleteResetOn = _gameTiming.CurTime.Add(TimeSpan.FromSeconds(3));
|
||||
SubmitButton.Text = Loc.GetString("admin-note-editor-submit-confirm");
|
||||
SubmitButton.ModulateSelfOverride = Color.Red;
|
||||
// Task.Delay(3000).ContinueWith(_ => ResetSubmitButton()); // TODO: fix
|
||||
return;
|
||||
}
|
||||
|
||||
ResetSubmitButton();
|
||||
|
||||
SubmitPressed?.Invoke(NoteId, NoteType, Rope.Collapse(NoteTextEdit.TextRope), NoteSeverity, IsSecret, ExpiryTime);
|
||||
|
||||
if (Parent is null)
|
||||
{
|
||||
_console.ExecuteCommand($"adminnotes \"{PlayerName}\"");
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
// This checks for null for free, do not invert it as null always produces a false value
|
||||
if (DeleteResetOn > _gameTiming.CurTime)
|
||||
{
|
||||
ResetSubmitButton();
|
||||
DeleteResetOn = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void ResetSubmitButton()
|
||||
{
|
||||
SubmitButton.Text = Loc.GetString("admin-note-editor-submit");
|
||||
SubmitButton.ModulateSelfOverride = null;
|
||||
UpdateDraw();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to parse the currently entered expiry time. As a side effect this function
|
||||
/// will colour its respective line edit to indicate an error
|
||||
/// </summary>
|
||||
/// <returns>True if parsing was successful, false if not</returns>
|
||||
private bool ParseExpiryTime()
|
||||
{
|
||||
// If the checkbox is pressed the note is permanent, so expiry is null
|
||||
if (PermanentCheckBox.Pressed)
|
||||
{
|
||||
ExpiryTime = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(ExpiryLineEdit.Text) || !DateTime.TryParse(ExpiryLineEdit.Text, out var result) || DateTime.UtcNow > result)
|
||||
{
|
||||
ExpiryLineEdit.ModulateSelfOverride = Color.Red;
|
||||
return false;
|
||||
}
|
||||
|
||||
ExpiryTime = result;
|
||||
ExpiryLineEdit.ModulateSelfOverride = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
if (!disposing)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PermanentCheckBox.OnPressed -= OnPermanentPressed;
|
||||
SecretCheckBox.OnPressed -= OnSecretPressed;
|
||||
SubmitButton.OnPressed -= OnSubmitButtonPressed;
|
||||
|
||||
SubmitPressed = null;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
<Control
|
||||
<Control
|
||||
xmlns="https://spacestation14.io"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:cc="clr-namespace:Content.Client.Administration.UI.CustomControls"
|
||||
@@ -8,7 +8,7 @@
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<GridContainer Columns="3">
|
||||
<cc:UICommandButton Command="kick" Text="{Loc admin-player-actions-window-title}" WindowType="{x:Type at:PlayerActionsWindow}" />
|
||||
<cc:UICommandButton Command="ban" Text="{Loc admin-player-actions-window-ban}" WindowType="{x:Type at:BanWindow}" />
|
||||
<cc:CommandButton Command="banpanel" Text="{Loc admin-player-actions-window-ban}" />
|
||||
<cc:CommandButton Command="aghost" Text="{Loc admin-player-actions-window-admin-ghost}" />
|
||||
<cc:UICommandButton Command="tpto" Text="{Loc admin-player-actions-window-teleport}" WindowType="{x:Type at:TeleportWindow}" />
|
||||
<cc:CommandButton Command="permissions" Text="{Loc admin-player-actions-window-permissions}" />
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
<DefaultWindow
|
||||
xmlns="https://spacestation14.io"
|
||||
xmlns:cc="clr-namespace:Content.Client.Administration.UI.CustomControls"
|
||||
Title="{Loc Ban}" MinSize="425 325">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Text="{Loc Player}" MinWidth="100" />
|
||||
<Control MinWidth="50" />
|
||||
<LineEdit Name="PlayerNameLine" MinWidth="100" HorizontalExpand="True" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Text="{Loc Reason}" MinSize="100 0" />
|
||||
<Control MinSize="50 0" />
|
||||
<LineEdit Name="ReasonLine" MinSize="100 0" HorizontalExpand="True" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Text="{Loc Minutes}" MinWidth="100" />
|
||||
<Control MinWidth="50" />
|
||||
<LineEdit Name="MinutesLine" MinWidth="100" HorizontalExpand="True" PlaceHolder="{Loc 0 minutes for a permanent ban}" />
|
||||
<Button Name="HourButton" Text="+1h (0)"/>
|
||||
<Button Name="DayButton" Text="+1d (0)"/>
|
||||
<Button Name="WeekButton" Text="+1w (0)"/>
|
||||
<Button Name="MonthButton" Text="+1M (0)"/>
|
||||
</BoxContainer>
|
||||
<cc:PlayerListControl Name="PlayerList" VerticalExpand="True" />
|
||||
<Control MinWidth="50" />
|
||||
<Button Name="SubmitButton" Text="{Loc Ban}" />
|
||||
</BoxContainer>
|
||||
</DefaultWindow>
|
||||
@@ -1,84 +0,0 @@
|
||||
using Content.Shared.Administration;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Console;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Utility;
|
||||
using static Robust.Client.UserInterface.Controls.LineEdit;
|
||||
|
||||
namespace Content.Client.Administration.UI.Tabs.AdminTab
|
||||
{
|
||||
[GenerateTypedNameReferences]
|
||||
[UsedImplicitly]
|
||||
public sealed partial class BanWindow : DefaultWindow
|
||||
{
|
||||
public BanWindow()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
PlayerNameLine.OnTextChanged += _ => OnPlayerNameChanged();
|
||||
PlayerList.OnSelectionChanged += OnPlayerSelectionChanged;
|
||||
SubmitButton.OnPressed += SubmitButtonOnOnPressed;
|
||||
MinutesLine.OnTextChanged += UpdateButtonsText;
|
||||
HourButton.OnPressed += _ => AddMinutes(60);
|
||||
DayButton.OnPressed += _ => AddMinutes(1440);
|
||||
WeekButton.OnPressed += _ => AddMinutes(10080);
|
||||
MonthButton.OnPressed += _ => AddMinutes(43200);
|
||||
}
|
||||
|
||||
private bool TryGetMinutes(string str, out uint minutes)
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(str))
|
||||
{
|
||||
minutes = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
return uint.TryParse(str, out minutes);
|
||||
}
|
||||
|
||||
private void AddMinutes(uint add)
|
||||
{
|
||||
if (!TryGetMinutes(MinutesLine.Text, out var minutes))
|
||||
return;
|
||||
|
||||
MinutesLine.Text = $"{minutes + add}";
|
||||
UpdateButtons(minutes+add);
|
||||
}
|
||||
|
||||
private void UpdateButtonsText(LineEditEventArgs obj)
|
||||
{
|
||||
if (!TryGetMinutes(obj.Text, out var minutes))
|
||||
return;
|
||||
UpdateButtons(minutes);
|
||||
}
|
||||
|
||||
private void UpdateButtons(uint minutes)
|
||||
{
|
||||
HourButton.Text = $"+1h ({minutes / 60})";
|
||||
DayButton.Text = $"+1d ({minutes / 1440})";
|
||||
WeekButton.Text = $"+1w ({minutes / 10080})";
|
||||
MonthButton.Text = $"+1M ({minutes / 43200})";
|
||||
}
|
||||
|
||||
private void OnPlayerNameChanged()
|
||||
{
|
||||
SubmitButton.Disabled = string.IsNullOrEmpty(PlayerNameLine.Text);
|
||||
}
|
||||
|
||||
public void OnPlayerSelectionChanged(PlayerInfo? player)
|
||||
{
|
||||
PlayerNameLine.Text = player?.Username ?? string.Empty;
|
||||
OnPlayerNameChanged();
|
||||
}
|
||||
|
||||
private void SubmitButtonOnOnPressed(BaseButton.ButtonEventArgs obj)
|
||||
{
|
||||
// Small verification if Player Name exists
|
||||
IoCManager.Resolve<IClientConsoleHost>().ExecuteCommand(
|
||||
$"ban \"{PlayerNameLine.Text}\" \"{CommandParsing.Escape(ReasonLine.Text)}\" {MinutesLine.Text}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
|
||||
1770
Content.Server.Database/Migrations/Postgres/20230503001749_AdminNotesImprovement.Designer.cs
generated
Normal file
1770
Content.Server.Database/Migrations/Postgres/20230503001749_AdminNotesImprovement.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,714 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Content.Server.Database.Migrations.Postgres
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AdminNotesImprovement : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_admin_notes_player_created_by_id",
|
||||
table: "admin_notes");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_admin_notes_player_deleted_by_id",
|
||||
table: "admin_notes");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_admin_notes_player_last_edited_by_id",
|
||||
table: "admin_notes");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_admin_notes_player_player_user_id",
|
||||
table: "admin_notes");
|
||||
|
||||
migrationBuilder.DropCheckConstraint(
|
||||
name: "HaveEitherAddressOrUserIdOrHWId",
|
||||
table: "server_role_ban");
|
||||
|
||||
migrationBuilder.DropCheckConstraint(
|
||||
name: "HaveEitherAddressOrUserIdOrHWId",
|
||||
table: "server_ban");
|
||||
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "user_id",
|
||||
table: "server_role_ban",
|
||||
newName: "player_user_id");
|
||||
|
||||
migrationBuilder.RenameIndex(
|
||||
name: "IX_server_role_ban_user_id",
|
||||
table: "server_role_ban",
|
||||
newName: "IX_server_role_ban_player_user_id");
|
||||
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "user_id",
|
||||
table: "server_ban",
|
||||
newName: "player_user_id");
|
||||
|
||||
migrationBuilder.RenameIndex(
|
||||
name: "IX_server_ban_user_id",
|
||||
table: "server_ban",
|
||||
newName: "IX_server_ban_player_user_id");
|
||||
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "shown_to_player",
|
||||
table: "admin_notes",
|
||||
newName: "secret");
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "admin_notes",
|
||||
keyColumn: "secret",
|
||||
keyValue: false,
|
||||
column: "secret",
|
||||
value: true);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "hidden",
|
||||
table: "server_role_ban",
|
||||
type: "boolean",
|
||||
nullable: false,
|
||||
defaultValue: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "last_edited_at",
|
||||
table: "server_role_ban",
|
||||
type: "timestamp with time zone",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<Guid>(
|
||||
name: "last_edited_by_id",
|
||||
table: "server_role_ban",
|
||||
type: "uuid",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<TimeSpan>(
|
||||
name: "playtime_at_note",
|
||||
table: "server_role_ban",
|
||||
type: "interval",
|
||||
nullable: false,
|
||||
defaultValue: new TimeSpan(0, 0, 0, 0, 0));
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "round_id",
|
||||
table: "server_role_ban",
|
||||
type: "integer",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "severity",
|
||||
table: "server_role_ban",
|
||||
type: "integer",
|
||||
nullable: false,
|
||||
defaultValue: 2);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "hidden",
|
||||
table: "server_ban",
|
||||
type: "boolean",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "last_edited_at",
|
||||
table: "server_ban",
|
||||
type: "timestamp with time zone",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<Guid>(
|
||||
name: "last_edited_by_id",
|
||||
table: "server_ban",
|
||||
type: "uuid",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<TimeSpan>(
|
||||
name: "playtime_at_note",
|
||||
table: "server_ban",
|
||||
type: "interval",
|
||||
nullable: false,
|
||||
defaultValue: new TimeSpan(0, 0, 0, 0, 0));
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "round_id",
|
||||
table: "server_ban",
|
||||
type: "integer",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "severity",
|
||||
table: "server_ban",
|
||||
type: "integer",
|
||||
nullable: false,
|
||||
defaultValue: 3);
|
||||
|
||||
migrationBuilder.AlterColumn<Guid>(
|
||||
name: "player_user_id",
|
||||
table: "admin_notes",
|
||||
type: "uuid",
|
||||
nullable: true,
|
||||
oldClrType: typeof(Guid),
|
||||
oldType: "uuid");
|
||||
|
||||
migrationBuilder.AlterColumn<Guid>(
|
||||
name: "last_edited_by_id",
|
||||
table: "admin_notes",
|
||||
type: "uuid",
|
||||
nullable: true,
|
||||
oldClrType: typeof(Guid),
|
||||
oldType: "uuid");
|
||||
|
||||
migrationBuilder.AlterColumn<Guid>(
|
||||
name: "created_by_id",
|
||||
table: "admin_notes",
|
||||
type: "uuid",
|
||||
nullable: true,
|
||||
oldClrType: typeof(Guid),
|
||||
oldType: "uuid");
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "expiration_time",
|
||||
table: "admin_notes",
|
||||
type: "timestamp with time zone",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "severity",
|
||||
table: "admin_notes",
|
||||
type: "integer",
|
||||
nullable: false,
|
||||
defaultValue: 1);
|
||||
|
||||
migrationBuilder.AddColumn<TimeSpan>(
|
||||
name: "playtime_at_note",
|
||||
table: "admin_notes",
|
||||
type: "interval",
|
||||
nullable: false,
|
||||
defaultValue: new TimeSpan(0, 0, 0, 0, 0));
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "admin_messages",
|
||||
columns: table => new
|
||||
{
|
||||
admin_messages_id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
round_id = table.Column<int>(type: "integer", nullable: true),
|
||||
player_user_id = table.Column<Guid>(type: "uuid", nullable: true),
|
||||
playtime_at_note = table.Column<TimeSpan>(type: "interval", nullable: false),
|
||||
message = table.Column<string>(type: "character varying(4096)", maxLength: 4096, nullable: false),
|
||||
created_by_id = table.Column<Guid>(type: "uuid", nullable: true),
|
||||
created_at = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||
last_edited_by_id = table.Column<Guid>(type: "uuid", nullable: true),
|
||||
last_edited_at = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
|
||||
expiration_time = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
|
||||
deleted = table.Column<bool>(type: "boolean", nullable: false),
|
||||
deleted_by_id = table.Column<Guid>(type: "uuid", nullable: true),
|
||||
deleted_at = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
|
||||
seen = table.Column<bool>(type: "boolean", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_admin_messages", x => x.admin_messages_id);
|
||||
table.ForeignKey(
|
||||
name: "FK_admin_messages_player_created_by_id",
|
||||
column: x => x.created_by_id,
|
||||
principalTable: "player",
|
||||
principalColumn: "user_id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
table.ForeignKey(
|
||||
name: "FK_admin_messages_player_deleted_by_id",
|
||||
column: x => x.deleted_by_id,
|
||||
principalTable: "player",
|
||||
principalColumn: "user_id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
table.ForeignKey(
|
||||
name: "FK_admin_messages_player_last_edited_by_id",
|
||||
column: x => x.last_edited_by_id,
|
||||
principalTable: "player",
|
||||
principalColumn: "user_id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
table.ForeignKey(
|
||||
name: "FK_admin_messages_player_player_user_id",
|
||||
column: x => x.player_user_id,
|
||||
principalTable: "player",
|
||||
principalColumn: "user_id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
table.ForeignKey(
|
||||
name: "FK_admin_messages_round_round_id",
|
||||
column: x => x.round_id,
|
||||
principalTable: "round",
|
||||
principalColumn: "round_id");
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "admin_watchlists",
|
||||
columns: table => new
|
||||
{
|
||||
admin_watchlists_id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
round_id = table.Column<int>(type: "integer", nullable: true),
|
||||
player_user_id = table.Column<Guid>(type: "uuid", nullable: true),
|
||||
playtime_at_note = table.Column<TimeSpan>(type: "interval", nullable: false),
|
||||
message = table.Column<string>(type: "character varying(4096)", maxLength: 4096, nullable: false),
|
||||
created_by_id = table.Column<Guid>(type: "uuid", nullable: true),
|
||||
created_at = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||
last_edited_by_id = table.Column<Guid>(type: "uuid", nullable: true),
|
||||
last_edited_at = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||
expiration_time = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
|
||||
deleted = table.Column<bool>(type: "boolean", nullable: false),
|
||||
deleted_by_id = table.Column<Guid>(type: "uuid", nullable: true),
|
||||
deleted_at = table.Column<DateTime>(type: "timestamp with time zone", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_admin_watchlists", x => x.admin_watchlists_id);
|
||||
table.ForeignKey(
|
||||
name: "FK_admin_watchlists_player_created_by_id",
|
||||
column: x => x.created_by_id,
|
||||
principalTable: "player",
|
||||
principalColumn: "user_id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
table.ForeignKey(
|
||||
name: "FK_admin_watchlists_player_deleted_by_id",
|
||||
column: x => x.deleted_by_id,
|
||||
principalTable: "player",
|
||||
principalColumn: "user_id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
table.ForeignKey(
|
||||
name: "FK_admin_watchlists_player_last_edited_by_id",
|
||||
column: x => x.last_edited_by_id,
|
||||
principalTable: "player",
|
||||
principalColumn: "user_id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
table.ForeignKey(
|
||||
name: "FK_admin_watchlists_player_player_user_id",
|
||||
column: x => x.player_user_id,
|
||||
principalTable: "player",
|
||||
principalColumn: "user_id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
table.ForeignKey(
|
||||
name: "FK_admin_watchlists_round_round_id",
|
||||
column: x => x.round_id,
|
||||
principalTable: "round",
|
||||
principalColumn: "round_id");
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_server_role_ban_banning_admin",
|
||||
table: "server_role_ban",
|
||||
column: "banning_admin");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_server_role_ban_last_edited_by_id",
|
||||
table: "server_role_ban",
|
||||
column: "last_edited_by_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_server_role_ban_round_id",
|
||||
table: "server_role_ban",
|
||||
column: "round_id");
|
||||
|
||||
migrationBuilder.AddCheckConstraint(
|
||||
name: "HaveEitherAddressOrUserIdOrHWId",
|
||||
table: "server_role_ban",
|
||||
sql: "address IS NOT NULL OR player_user_id IS NOT NULL OR hwid IS NOT NULL");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_server_ban_banning_admin",
|
||||
table: "server_ban",
|
||||
column: "banning_admin");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_server_ban_last_edited_by_id",
|
||||
table: "server_ban",
|
||||
column: "last_edited_by_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_server_ban_round_id",
|
||||
table: "server_ban",
|
||||
column: "round_id");
|
||||
|
||||
migrationBuilder.AddCheckConstraint(
|
||||
name: "HaveEitherAddressOrUserIdOrHWId",
|
||||
table: "server_ban",
|
||||
sql: "address IS NOT NULL OR player_user_id IS NOT NULL OR hwid IS NOT NULL");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_admin_messages_created_by_id",
|
||||
table: "admin_messages",
|
||||
column: "created_by_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_admin_messages_deleted_by_id",
|
||||
table: "admin_messages",
|
||||
column: "deleted_by_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_admin_messages_last_edited_by_id",
|
||||
table: "admin_messages",
|
||||
column: "last_edited_by_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_admin_messages_player_user_id",
|
||||
table: "admin_messages",
|
||||
column: "player_user_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_admin_messages_round_id",
|
||||
table: "admin_messages",
|
||||
column: "round_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_admin_watchlists_created_by_id",
|
||||
table: "admin_watchlists",
|
||||
column: "created_by_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_admin_watchlists_deleted_by_id",
|
||||
table: "admin_watchlists",
|
||||
column: "deleted_by_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_admin_watchlists_last_edited_by_id",
|
||||
table: "admin_watchlists",
|
||||
column: "last_edited_by_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_admin_watchlists_player_user_id",
|
||||
table: "admin_watchlists",
|
||||
column: "player_user_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_admin_watchlists_round_id",
|
||||
table: "admin_watchlists",
|
||||
column: "round_id");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_admin_notes_player_created_by_id",
|
||||
table: "admin_notes",
|
||||
column: "created_by_id",
|
||||
principalTable: "player",
|
||||
principalColumn: "user_id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_admin_notes_player_deleted_by_id",
|
||||
table: "admin_notes",
|
||||
column: "deleted_by_id",
|
||||
principalTable: "player",
|
||||
principalColumn: "user_id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_admin_notes_player_last_edited_by_id",
|
||||
table: "admin_notes",
|
||||
column: "last_edited_by_id",
|
||||
principalTable: "player",
|
||||
principalColumn: "user_id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_admin_notes_player_player_user_id",
|
||||
table: "admin_notes",
|
||||
column: "player_user_id",
|
||||
principalTable: "player",
|
||||
principalColumn: "user_id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_server_ban_player_banning_admin",
|
||||
table: "server_ban",
|
||||
column: "banning_admin",
|
||||
principalTable: "player",
|
||||
principalColumn: "user_id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_server_ban_player_last_edited_by_id",
|
||||
table: "server_ban",
|
||||
column: "last_edited_by_id",
|
||||
principalTable: "player",
|
||||
principalColumn: "user_id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_server_ban_round_round_id",
|
||||
table: "server_ban",
|
||||
column: "round_id",
|
||||
principalTable: "round",
|
||||
principalColumn: "round_id");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_server_role_ban_player_banning_admin",
|
||||
table: "server_role_ban",
|
||||
column: "banning_admin",
|
||||
principalTable: "player",
|
||||
principalColumn: "user_id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_server_role_ban_player_last_edited_by_id",
|
||||
table: "server_role_ban",
|
||||
column: "last_edited_by_id",
|
||||
principalTable: "player",
|
||||
principalColumn: "user_id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_server_role_ban_round_round_id",
|
||||
table: "server_role_ban",
|
||||
column: "round_id",
|
||||
principalTable: "round",
|
||||
principalColumn: "round_id");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_admin_notes_player_created_by_id",
|
||||
table: "admin_notes");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_admin_notes_player_deleted_by_id",
|
||||
table: "admin_notes");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_admin_notes_player_last_edited_by_id",
|
||||
table: "admin_notes");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_admin_notes_player_player_user_id",
|
||||
table: "admin_notes");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_server_ban_player_banning_admin",
|
||||
table: "server_ban");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_server_ban_player_last_edited_by_id",
|
||||
table: "server_ban");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_server_ban_round_round_id",
|
||||
table: "server_ban");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_server_role_ban_player_banning_admin",
|
||||
table: "server_role_ban");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_server_role_ban_player_last_edited_by_id",
|
||||
table: "server_role_ban");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_server_role_ban_round_round_id",
|
||||
table: "server_role_ban");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "admin_messages");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "admin_watchlists");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_server_role_ban_banning_admin",
|
||||
table: "server_role_ban");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_server_role_ban_last_edited_by_id",
|
||||
table: "server_role_ban");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_server_role_ban_round_id",
|
||||
table: "server_role_ban");
|
||||
|
||||
migrationBuilder.DropCheckConstraint(
|
||||
name: "HaveEitherAddressOrUserIdOrHWId",
|
||||
table: "server_role_ban");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_server_ban_banning_admin",
|
||||
table: "server_ban");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_server_ban_last_edited_by_id",
|
||||
table: "server_ban");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_server_ban_round_id",
|
||||
table: "server_ban");
|
||||
|
||||
migrationBuilder.DropCheckConstraint(
|
||||
name: "HaveEitherAddressOrUserIdOrHWId",
|
||||
table: "server_ban");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "hidden",
|
||||
table: "server_role_ban");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "last_edited_at",
|
||||
table: "server_role_ban");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "last_edited_by_id",
|
||||
table: "server_role_ban");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "playtime_at_note",
|
||||
table: "server_role_ban");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "round_id",
|
||||
table: "server_role_ban");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "severity",
|
||||
table: "server_role_ban");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "hidden",
|
||||
table: "server_ban");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "last_edited_at",
|
||||
table: "server_ban");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "last_edited_by_id",
|
||||
table: "server_ban");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "playtime_at_note",
|
||||
table: "server_ban");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "round_id",
|
||||
table: "server_ban");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "severity",
|
||||
table: "server_ban");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "expiration_time",
|
||||
table: "admin_notes");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "severity",
|
||||
table: "admin_notes");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "playtime_at_note",
|
||||
table: "admin_notes");
|
||||
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "player_user_id",
|
||||
table: "server_role_ban",
|
||||
newName: "user_id");
|
||||
|
||||
migrationBuilder.RenameIndex(
|
||||
name: "IX_server_role_ban_player_user_id",
|
||||
table: "server_role_ban",
|
||||
newName: "IX_server_role_ban_user_id");
|
||||
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "player_user_id",
|
||||
table: "server_ban",
|
||||
newName: "user_id");
|
||||
|
||||
migrationBuilder.RenameIndex(
|
||||
name: "IX_server_ban_player_user_id",
|
||||
table: "server_ban",
|
||||
newName: "IX_server_ban_user_id");
|
||||
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "secret",
|
||||
table: "admin_notes",
|
||||
newName: "shown_to_player");
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "admin_notes",
|
||||
keyColumn: "shown_to_player",
|
||||
keyValue: true,
|
||||
column: "shown_to_player",
|
||||
value: false);
|
||||
|
||||
migrationBuilder.AlterColumn<Guid>(
|
||||
name: "player_user_id",
|
||||
table: "admin_notes",
|
||||
type: "uuid",
|
||||
nullable: false,
|
||||
defaultValue: new Guid("00000000-0000-0000-0000-000000000000"),
|
||||
oldClrType: typeof(Guid),
|
||||
oldType: "uuid",
|
||||
oldNullable: true);
|
||||
|
||||
migrationBuilder.AlterColumn<Guid>(
|
||||
name: "last_edited_by_id",
|
||||
table: "admin_notes",
|
||||
type: "uuid",
|
||||
nullable: false,
|
||||
defaultValue: new Guid("00000000-0000-0000-0000-000000000000"),
|
||||
oldClrType: typeof(Guid),
|
||||
oldType: "uuid",
|
||||
oldNullable: true);
|
||||
|
||||
migrationBuilder.AlterColumn<Guid>(
|
||||
name: "created_by_id",
|
||||
table: "admin_notes",
|
||||
type: "uuid",
|
||||
nullable: false,
|
||||
defaultValue: new Guid("00000000-0000-0000-0000-000000000000"),
|
||||
oldClrType: typeof(Guid),
|
||||
oldType: "uuid",
|
||||
oldNullable: true);
|
||||
|
||||
migrationBuilder.AddCheckConstraint(
|
||||
name: "HaveEitherAddressOrUserIdOrHWId",
|
||||
table: "server_role_ban",
|
||||
sql: "address IS NOT NULL OR user_id IS NOT NULL OR hwid IS NOT NULL");
|
||||
|
||||
migrationBuilder.AddCheckConstraint(
|
||||
name: "HaveEitherAddressOrUserIdOrHWId",
|
||||
table: "server_ban",
|
||||
sql: "address IS NOT NULL OR user_id IS NOT NULL OR hwid IS NOT NULL");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_admin_notes_player_created_by_id",
|
||||
table: "admin_notes",
|
||||
column: "created_by_id",
|
||||
principalTable: "player",
|
||||
principalColumn: "user_id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_admin_notes_player_deleted_by_id",
|
||||
table: "admin_notes",
|
||||
column: "deleted_by_id",
|
||||
principalTable: "player",
|
||||
principalColumn: "user_id");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_admin_notes_player_last_edited_by_id",
|
||||
table: "admin_notes",
|
||||
column: "last_edited_by_id",
|
||||
principalTable: "player",
|
||||
principalColumn: "user_id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_admin_notes_player_player_user_id",
|
||||
table: "admin_notes",
|
||||
column: "player_user_id",
|
||||
principalTable: "player",
|
||||
principalColumn: "user_id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// <auto-generated />
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Text.Json;
|
||||
@@ -188,12 +188,12 @@ namespace Content.Server.Database.Migrations.Postgres
|
||||
b.ToTable("admin_log_player", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.AdminNote", b =>
|
||||
modelBuilder.Entity("Content.Server.Database.AdminMessage", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("admin_notes_id");
|
||||
.HasColumnName("admin_messages_id");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
@@ -201,7 +201,7 @@ namespace Content.Server.Database.Migrations.Postgres
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("created_at");
|
||||
|
||||
b.Property<Guid>("CreatedById")
|
||||
b.Property<Guid?>("CreatedById")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("created_by_id");
|
||||
|
||||
@@ -217,11 +217,15 @@ namespace Content.Server.Database.Migrations.Postgres
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("deleted_by_id");
|
||||
|
||||
b.Property<DateTime>("LastEditedAt")
|
||||
b.Property<DateTime?>("ExpirationTime")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("expiration_time");
|
||||
|
||||
b.Property<DateTime?>("LastEditedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("last_edited_at");
|
||||
|
||||
b.Property<Guid>("LastEditedById")
|
||||
b.Property<Guid?>("LastEditedById")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("last_edited_by_id");
|
||||
|
||||
@@ -231,17 +235,107 @@ namespace Content.Server.Database.Migrations.Postgres
|
||||
.HasColumnType("character varying(4096)")
|
||||
.HasColumnName("message");
|
||||
|
||||
b.Property<Guid>("PlayerUserId")
|
||||
b.Property<Guid?>("PlayerUserId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("player_user_id");
|
||||
|
||||
b.Property<TimeSpan>("PlaytimeAtNote")
|
||||
.HasColumnType("interval")
|
||||
.HasColumnName("playtime_at_note");
|
||||
|
||||
b.Property<int?>("RoundId")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("round_id");
|
||||
|
||||
b.Property<bool>("ShownToPlayer")
|
||||
b.Property<bool>("Seen")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("shown_to_player");
|
||||
.HasColumnName("seen");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("PK_admin_messages");
|
||||
|
||||
b.HasIndex("CreatedById");
|
||||
|
||||
b.HasIndex("DeletedById");
|
||||
|
||||
b.HasIndex("LastEditedById");
|
||||
|
||||
b.HasIndex("PlayerUserId")
|
||||
.HasDatabaseName("IX_admin_messages_player_user_id");
|
||||
|
||||
b.HasIndex("RoundId")
|
||||
.HasDatabaseName("IX_admin_messages_round_id");
|
||||
|
||||
b.ToTable("admin_messages", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.AdminNote", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("admin_notes_id");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("created_at");
|
||||
|
||||
b.Property<Guid?>("CreatedById")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("created_by_id");
|
||||
|
||||
b.Property<bool>("Deleted")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("deleted");
|
||||
|
||||
b.Property<DateTime?>("DeletedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("deleted_at");
|
||||
|
||||
b.Property<Guid?>("DeletedById")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("deleted_by_id");
|
||||
|
||||
b.Property<DateTime?>("ExpirationTime")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("expiration_time");
|
||||
|
||||
b.Property<DateTime?>("LastEditedAt")
|
||||
.IsRequired()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("last_edited_at");
|
||||
|
||||
b.Property<Guid?>("LastEditedById")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("last_edited_by_id");
|
||||
|
||||
b.Property<string>("Message")
|
||||
.IsRequired()
|
||||
.HasMaxLength(4096)
|
||||
.HasColumnType("character varying(4096)")
|
||||
.HasColumnName("message");
|
||||
|
||||
b.Property<int>("Severity")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("severity");
|
||||
|
||||
b.Property<Guid?>("PlayerUserId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("player_user_id");
|
||||
|
||||
b.Property<TimeSpan>("PlaytimeAtNote")
|
||||
.HasColumnType("interval")
|
||||
.HasColumnName("playtime_at_note");
|
||||
|
||||
b.Property<int?>("RoundId")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("round_id");
|
||||
|
||||
b.Property<bool>("Secret")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("secret");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("PK_admin_notes");
|
||||
@@ -310,6 +404,84 @@ namespace Content.Server.Database.Migrations.Postgres
|
||||
b.ToTable("admin_rank_flag", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.AdminWatchlist", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("admin_watchlists_id");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("created_at");
|
||||
|
||||
b.Property<Guid?>("CreatedById")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("created_by_id");
|
||||
|
||||
b.Property<bool>("Deleted")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("deleted");
|
||||
|
||||
b.Property<DateTime?>("DeletedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("deleted_at");
|
||||
|
||||
b.Property<Guid?>("DeletedById")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("deleted_by_id");
|
||||
|
||||
b.Property<DateTime?>("ExpirationTime")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("expiration_time");
|
||||
|
||||
b.Property<DateTime?>("LastEditedAt")
|
||||
.IsRequired()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("last_edited_at");
|
||||
|
||||
b.Property<Guid?>("LastEditedById")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("last_edited_by_id");
|
||||
|
||||
b.Property<string>("Message")
|
||||
.IsRequired()
|
||||
.HasMaxLength(4096)
|
||||
.HasColumnType("character varying(4096)")
|
||||
.HasColumnName("message");
|
||||
|
||||
b.Property<Guid?>("PlayerUserId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("player_user_id");
|
||||
|
||||
b.Property<TimeSpan>("PlaytimeAtNote")
|
||||
.HasColumnType("interval")
|
||||
.HasColumnName("playtime_at_note");
|
||||
|
||||
b.Property<int?>("RoundId")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("round_id");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("PK_admin_watchlists");
|
||||
|
||||
b.HasIndex("CreatedById");
|
||||
|
||||
b.HasIndex("DeletedById");
|
||||
|
||||
b.HasIndex("LastEditedById");
|
||||
|
||||
b.HasIndex("PlayerUserId")
|
||||
.HasDatabaseName("IX_admin_watchlists_player_user_id");
|
||||
|
||||
b.HasIndex("RoundId")
|
||||
.HasDatabaseName("IX_admin_watchlists_round_id");
|
||||
|
||||
b.ToTable("admin_watchlists", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.Antag", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
@@ -753,27 +925,59 @@ namespace Content.Server.Database.Migrations.Postgres
|
||||
.HasColumnType("bytea")
|
||||
.HasColumnName("hwid");
|
||||
|
||||
b.Property<bool>("Hidden")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("hidden");
|
||||
|
||||
b.Property<DateTime?>("LastEditedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("last_edited_at");
|
||||
|
||||
b.Property<Guid?>("LastEditedById")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("last_edited_by_id");
|
||||
|
||||
b.Property<Guid?>("PlayerUserId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("player_user_id");
|
||||
|
||||
b.Property<TimeSpan>("PlaytimeAtNote")
|
||||
.HasColumnType("interval")
|
||||
.HasColumnName("playtime_at_note");
|
||||
|
||||
b.Property<string>("Reason")
|
||||
.IsRequired()
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("reason");
|
||||
|
||||
b.Property<Guid?>("UserId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("user_id");
|
||||
b.Property<int?>("RoundId")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("round_id");
|
||||
|
||||
b.Property<int>("Severity")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("severity");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("PK_server_ban");
|
||||
|
||||
b.HasIndex("Address");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
b.HasIndex("BanningAdmin");
|
||||
|
||||
b.HasIndex("LastEditedById");
|
||||
|
||||
b.HasIndex("PlayerUserId")
|
||||
.HasDatabaseName("IX_server_ban_player_user_id");
|
||||
|
||||
b.HasIndex("RoundId")
|
||||
.HasDatabaseName("IX_server_ban_round_id");
|
||||
|
||||
b.ToTable("server_ban", null, t =>
|
||||
{
|
||||
t.HasCheckConstraint("AddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= address");
|
||||
|
||||
t.HasCheckConstraint("HaveEitherAddressOrUserIdOrHWId", "address IS NOT NULL OR user_id IS NOT NULL OR hwid IS NOT NULL");
|
||||
t.HasCheckConstraint("HaveEitherAddressOrUserIdOrHWId", "address IS NOT NULL OR player_user_id IS NOT NULL OR hwid IS NOT NULL");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -855,6 +1059,26 @@ namespace Content.Server.Database.Migrations.Postgres
|
||||
.HasColumnType("bytea")
|
||||
.HasColumnName("hwid");
|
||||
|
||||
b.Property<bool>("Hidden")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("hidden");
|
||||
|
||||
b.Property<DateTime?>("LastEditedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("last_edited_at");
|
||||
|
||||
b.Property<Guid?>("LastEditedById")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("last_edited_by_id");
|
||||
|
||||
b.Property<Guid?>("PlayerUserId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("player_user_id");
|
||||
|
||||
b.Property<TimeSpan>("PlaytimeAtNote")
|
||||
.HasColumnType("interval")
|
||||
.HasColumnName("playtime_at_note");
|
||||
|
||||
b.Property<string>("Reason")
|
||||
.IsRequired()
|
||||
.HasColumnType("text")
|
||||
@@ -865,22 +1089,34 @@ namespace Content.Server.Database.Migrations.Postgres
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("role_id");
|
||||
|
||||
b.Property<Guid?>("UserId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("user_id");
|
||||
b.Property<int?>("RoundId")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("round_id");
|
||||
|
||||
b.Property<int>("Severity")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("severity");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("PK_server_role_ban");
|
||||
|
||||
b.HasIndex("Address");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
b.HasIndex("BanningAdmin");
|
||||
|
||||
b.HasIndex("LastEditedById");
|
||||
|
||||
b.HasIndex("PlayerUserId")
|
||||
.HasDatabaseName("IX_server_role_ban_player_user_id");
|
||||
|
||||
b.HasIndex("RoundId")
|
||||
.HasDatabaseName("IX_server_role_ban_round_id");
|
||||
|
||||
b.ToTable("server_role_ban", null, t =>
|
||||
{
|
||||
t.HasCheckConstraint("AddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= address");
|
||||
|
||||
t.HasCheckConstraint("HaveEitherAddressOrUserIdOrHWId", "address IS NOT NULL OR user_id IS NOT NULL OR hwid IS NOT NULL");
|
||||
t.HasCheckConstraint("HaveEitherAddressOrUserIdOrHWId", "address IS NOT NULL OR player_user_id IS NOT NULL OR hwid IS NOT NULL");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1101,36 +1337,80 @@ namespace Content.Server.Database.Migrations.Postgres
|
||||
b.Navigation("Player");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.AdminMessage", b =>
|
||||
{
|
||||
b.HasOne("Content.Server.Database.Player", "CreatedBy")
|
||||
.WithMany("AdminMessagesCreated")
|
||||
.HasForeignKey("CreatedById")
|
||||
.HasPrincipalKey("UserId")
|
||||
.OnDelete(DeleteBehavior.SetNull)
|
||||
.HasConstraintName("FK_admin_messages_player_created_by_id");
|
||||
|
||||
b.HasOne("Content.Server.Database.Player", "DeletedBy")
|
||||
.WithMany("AdminMessagesDeleted")
|
||||
.HasForeignKey("DeletedById")
|
||||
.HasPrincipalKey("UserId")
|
||||
.OnDelete(DeleteBehavior.SetNull)
|
||||
.HasConstraintName("FK_admin_messages_player_deleted_by_id");
|
||||
|
||||
b.HasOne("Content.Server.Database.Player", "LastEditedBy")
|
||||
.WithMany("AdminMessagesLastEdited")
|
||||
.HasForeignKey("LastEditedById")
|
||||
.HasPrincipalKey("UserId")
|
||||
.OnDelete(DeleteBehavior.SetNull)
|
||||
.HasConstraintName("FK_admin_messages_player_last_edited_by_id");
|
||||
|
||||
b.HasOne("Content.Server.Database.Player", "Player")
|
||||
.WithMany("AdminMessagesReceived")
|
||||
.HasForeignKey("PlayerUserId")
|
||||
.HasPrincipalKey("UserId")
|
||||
.OnDelete(DeleteBehavior.SetNull)
|
||||
.HasConstraintName("FK_admin_messages_player_player_user_id");
|
||||
|
||||
b.HasOne("Content.Server.Database.Round", "Round")
|
||||
.WithMany()
|
||||
.HasForeignKey("RoundId")
|
||||
.HasConstraintName("FK_admin_messages_round_round_id");
|
||||
|
||||
b.Navigation("CreatedBy");
|
||||
|
||||
b.Navigation("DeletedBy");
|
||||
|
||||
b.Navigation("LastEditedBy");
|
||||
|
||||
b.Navigation("Player");
|
||||
|
||||
b.Navigation("Round");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.AdminNote", b =>
|
||||
{
|
||||
b.HasOne("Content.Server.Database.Player", "CreatedBy")
|
||||
.WithMany("AdminNotesCreated")
|
||||
.HasForeignKey("CreatedById")
|
||||
.HasPrincipalKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.OnDelete(DeleteBehavior.SetNull)
|
||||
.HasConstraintName("FK_admin_notes_player_created_by_id");
|
||||
|
||||
b.HasOne("Content.Server.Database.Player", "DeletedBy")
|
||||
.WithMany("AdminNotesDeleted")
|
||||
.HasForeignKey("DeletedById")
|
||||
.HasPrincipalKey("UserId")
|
||||
.OnDelete(DeleteBehavior.SetNull)
|
||||
.HasConstraintName("FK_admin_notes_player_deleted_by_id");
|
||||
|
||||
b.HasOne("Content.Server.Database.Player", "LastEditedBy")
|
||||
.WithMany("AdminNotesLastEdited")
|
||||
.HasForeignKey("LastEditedById")
|
||||
.HasPrincipalKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.OnDelete(DeleteBehavior.SetNull)
|
||||
.HasConstraintName("FK_admin_notes_player_last_edited_by_id");
|
||||
|
||||
b.HasOne("Content.Server.Database.Player", "Player")
|
||||
.WithMany("AdminNotesReceived")
|
||||
.HasForeignKey("PlayerUserId")
|
||||
.HasPrincipalKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.OnDelete(DeleteBehavior.SetNull)
|
||||
.HasConstraintName("FK_admin_notes_player_player_user_id");
|
||||
|
||||
b.HasOne("Content.Server.Database.Round", "Round")
|
||||
@@ -1161,6 +1441,52 @@ namespace Content.Server.Database.Migrations.Postgres
|
||||
b.Navigation("Rank");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.AdminWatchlist", b =>
|
||||
{
|
||||
b.HasOne("Content.Server.Database.Player", "CreatedBy")
|
||||
.WithMany("AdminWatchlistsCreated")
|
||||
.HasForeignKey("CreatedById")
|
||||
.HasPrincipalKey("UserId")
|
||||
.OnDelete(DeleteBehavior.SetNull)
|
||||
.HasConstraintName("FK_admin_watchlists_player_created_by_id");
|
||||
|
||||
b.HasOne("Content.Server.Database.Player", "DeletedBy")
|
||||
.WithMany("AdminWatchlistsDeleted")
|
||||
.HasForeignKey("DeletedById")
|
||||
.HasPrincipalKey("UserId")
|
||||
.OnDelete(DeleteBehavior.SetNull)
|
||||
.HasConstraintName("FK_admin_watchlists_player_deleted_by_id");
|
||||
|
||||
b.HasOne("Content.Server.Database.Player", "LastEditedBy")
|
||||
.WithMany("AdminWatchlistsLastEdited")
|
||||
.HasForeignKey("LastEditedById")
|
||||
.HasPrincipalKey("UserId")
|
||||
.OnDelete(DeleteBehavior.SetNull)
|
||||
.HasConstraintName("FK_admin_watchlists_player_last_edited_by_id");
|
||||
|
||||
b.HasOne("Content.Server.Database.Player", "Player")
|
||||
.WithMany("AdminWatchlistsReceived")
|
||||
.HasForeignKey("PlayerUserId")
|
||||
.HasPrincipalKey("UserId")
|
||||
.OnDelete(DeleteBehavior.SetNull)
|
||||
.HasConstraintName("FK_admin_watchlists_player_player_user_id");
|
||||
|
||||
b.HasOne("Content.Server.Database.Round", "Round")
|
||||
.WithMany()
|
||||
.HasForeignKey("RoundId")
|
||||
.HasConstraintName("FK_admin_watchlists_round_round_id");
|
||||
|
||||
b.Navigation("CreatedBy");
|
||||
|
||||
b.Navigation("DeletedBy");
|
||||
|
||||
b.Navigation("LastEditedBy");
|
||||
|
||||
b.Navigation("Player");
|
||||
|
||||
b.Navigation("Round");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.Antag", b =>
|
||||
{
|
||||
b.HasOne("Content.Server.Database.Profile", "Profile")
|
||||
@@ -1209,6 +1535,34 @@ namespace Content.Server.Database.Migrations.Postgres
|
||||
b.Navigation("Server");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.ServerBan", b =>
|
||||
{
|
||||
b.HasOne("Content.Server.Database.Player", "CreatedBy")
|
||||
.WithMany("AdminServerBansCreated")
|
||||
.HasForeignKey("BanningAdmin")
|
||||
.HasPrincipalKey("UserId")
|
||||
.OnDelete(DeleteBehavior.SetNull)
|
||||
.HasConstraintName("FK_server_ban_player_banning_admin");
|
||||
|
||||
b.HasOne("Content.Server.Database.Player", "LastEditedBy")
|
||||
.WithMany("AdminServerBansLastEdited")
|
||||
.HasForeignKey("LastEditedById")
|
||||
.HasPrincipalKey("UserId")
|
||||
.OnDelete(DeleteBehavior.SetNull)
|
||||
.HasConstraintName("FK_server_ban_player_last_edited_by_id");
|
||||
|
||||
b.HasOne("Content.Server.Database.Round", "Round")
|
||||
.WithMany()
|
||||
.HasForeignKey("RoundId")
|
||||
.HasConstraintName("FK_server_ban_round_round_id");
|
||||
|
||||
b.Navigation("CreatedBy");
|
||||
|
||||
b.Navigation("LastEditedBy");
|
||||
|
||||
b.Navigation("Round");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.ServerBanHit", b =>
|
||||
{
|
||||
b.HasOne("Content.Server.Database.ServerBan", "Ban")
|
||||
@@ -1230,6 +1584,34 @@ namespace Content.Server.Database.Migrations.Postgres
|
||||
b.Navigation("Connection");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.ServerRoleBan", b =>
|
||||
{
|
||||
b.HasOne("Content.Server.Database.Player", "CreatedBy")
|
||||
.WithMany("AdminServerRoleBansCreated")
|
||||
.HasForeignKey("BanningAdmin")
|
||||
.HasPrincipalKey("UserId")
|
||||
.OnDelete(DeleteBehavior.SetNull)
|
||||
.HasConstraintName("FK_server_role_ban_player_banning_admin");
|
||||
|
||||
b.HasOne("Content.Server.Database.Player", "LastEditedBy")
|
||||
.WithMany("AdminServerRoleBansLastEdited")
|
||||
.HasForeignKey("LastEditedById")
|
||||
.HasPrincipalKey("UserId")
|
||||
.OnDelete(DeleteBehavior.SetNull)
|
||||
.HasConstraintName("FK_server_role_ban_player_last_edited_by_id");
|
||||
|
||||
b.HasOne("Content.Server.Database.Round", "Round")
|
||||
.WithMany()
|
||||
.HasForeignKey("RoundId")
|
||||
.HasConstraintName("FK_server_role_ban_round_round_id");
|
||||
|
||||
b.Navigation("CreatedBy");
|
||||
|
||||
b.Navigation("LastEditedBy");
|
||||
|
||||
b.Navigation("Round");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.ServerRoleUnban", b =>
|
||||
{
|
||||
b.HasOne("Content.Server.Database.ServerRoleBan", "Ban")
|
||||
@@ -1311,6 +1693,14 @@ namespace Content.Server.Database.Migrations.Postgres
|
||||
{
|
||||
b.Navigation("AdminLogs");
|
||||
|
||||
b.Navigation("AdminMessagesCreated");
|
||||
|
||||
b.Navigation("AdminMessagesDeleted");
|
||||
|
||||
b.Navigation("AdminMessagesLastEdited");
|
||||
|
||||
b.Navigation("AdminMessagesReceived");
|
||||
|
||||
b.Navigation("AdminNotesCreated");
|
||||
|
||||
b.Navigation("AdminNotesDeleted");
|
||||
@@ -1318,6 +1708,22 @@ namespace Content.Server.Database.Migrations.Postgres
|
||||
b.Navigation("AdminNotesLastEdited");
|
||||
|
||||
b.Navigation("AdminNotesReceived");
|
||||
|
||||
b.Navigation("AdminServerBansCreated");
|
||||
|
||||
b.Navigation("AdminServerBansLastEdited");
|
||||
|
||||
b.Navigation("AdminServerRoleBansCreated");
|
||||
|
||||
b.Navigation("AdminServerRoleBansLastEdited");
|
||||
|
||||
b.Navigation("AdminWatchlistsCreated");
|
||||
|
||||
b.Navigation("AdminWatchlistsDeleted");
|
||||
|
||||
b.Navigation("AdminWatchlistsLastEdited");
|
||||
|
||||
b.Navigation("AdminWatchlistsReceived");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.Preference", b =>
|
||||
|
||||
1698
Content.Server.Database/Migrations/Sqlite/20230503001739_AdminNotesImprovement.Designer.cs
generated
Normal file
1698
Content.Server.Database/Migrations/Sqlite/20230503001739_AdminNotesImprovement.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,713 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Content.Server.Database.Migrations.Sqlite
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AdminNotesImprovement : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_admin_notes_player_created_by_id",
|
||||
table: "admin_notes");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_admin_notes_player_deleted_by_id",
|
||||
table: "admin_notes");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_admin_notes_player_last_edited_by_id",
|
||||
table: "admin_notes");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_admin_notes_player_player_user_id",
|
||||
table: "admin_notes");
|
||||
|
||||
migrationBuilder.DropCheckConstraint(
|
||||
name: "HaveEitherAddressOrUserIdOrHWId",
|
||||
table: "server_role_ban");
|
||||
|
||||
migrationBuilder.DropCheckConstraint(
|
||||
name: "HaveEitherAddressOrUserIdOrHWId",
|
||||
table: "server_ban");
|
||||
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "user_id",
|
||||
table: "server_role_ban",
|
||||
newName: "player_user_id");
|
||||
|
||||
migrationBuilder.RenameIndex(
|
||||
name: "IX_server_role_ban_user_id",
|
||||
table: "server_role_ban",
|
||||
newName: "IX_server_role_ban_player_user_id");
|
||||
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "user_id",
|
||||
table: "server_ban",
|
||||
newName: "player_user_id");
|
||||
|
||||
migrationBuilder.RenameIndex(
|
||||
name: "IX_server_ban_user_id",
|
||||
table: "server_ban",
|
||||
newName: "IX_server_ban_player_user_id");
|
||||
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "shown_to_player",
|
||||
table: "admin_notes",
|
||||
newName: "secret");
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "admin_notes",
|
||||
keyColumn: "secret",
|
||||
keyValue: false,
|
||||
column: "secret",
|
||||
value: true);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "hidden",
|
||||
table: "server_role_ban",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "last_edited_at",
|
||||
table: "server_role_ban",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<Guid>(
|
||||
name: "last_edited_by_id",
|
||||
table: "server_role_ban",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<TimeSpan>(
|
||||
name: "playtime_at_note",
|
||||
table: "server_role_ban",
|
||||
type: "TEXT",
|
||||
nullable: false,
|
||||
defaultValue: new TimeSpan(0, 0, 0, 0, 0));
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "round_id",
|
||||
table: "server_role_ban",
|
||||
type: "INTEGER",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "severity",
|
||||
table: "server_role_ban",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: 2);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "hidden",
|
||||
table: "server_ban",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "last_edited_at",
|
||||
table: "server_ban",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<Guid>(
|
||||
name: "last_edited_by_id",
|
||||
table: "server_ban",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<TimeSpan>(
|
||||
name: "playtime_at_note",
|
||||
table: "server_ban",
|
||||
type: "TEXT",
|
||||
nullable: false,
|
||||
defaultValue: new TimeSpan(0, 0, 0, 0, 0));
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "round_id",
|
||||
table: "server_ban",
|
||||
type: "INTEGER",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "severity",
|
||||
table: "server_ban",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: 3);
|
||||
|
||||
migrationBuilder.AlterColumn<Guid>(
|
||||
name: "player_user_id",
|
||||
table: "admin_notes",
|
||||
type: "TEXT",
|
||||
nullable: true,
|
||||
oldClrType: typeof(Guid),
|
||||
oldType: "TEXT");
|
||||
|
||||
migrationBuilder.AlterColumn<Guid>(
|
||||
name: "last_edited_by_id",
|
||||
table: "admin_notes",
|
||||
type: "TEXT",
|
||||
nullable: true,
|
||||
oldClrType: typeof(Guid),
|
||||
oldType: "TEXT");
|
||||
|
||||
migrationBuilder.AlterColumn<Guid>(
|
||||
name: "created_by_id",
|
||||
table: "admin_notes",
|
||||
type: "TEXT",
|
||||
nullable: true,
|
||||
oldClrType: typeof(Guid),
|
||||
oldType: "TEXT");
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "expiration_time",
|
||||
table: "admin_notes",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "severity",
|
||||
table: "admin_notes",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: 1);
|
||||
|
||||
migrationBuilder.AddColumn<TimeSpan>(
|
||||
name: "playtime_at_note",
|
||||
table: "admin_notes",
|
||||
type: "TEXT",
|
||||
nullable: false,
|
||||
defaultValue: new TimeSpan(0, 0, 0, 0, 0));
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "admin_messages",
|
||||
columns: table => new
|
||||
{
|
||||
admin_messages_id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
round_id = table.Column<int>(type: "INTEGER", nullable: true),
|
||||
player_user_id = table.Column<Guid>(type: "TEXT", nullable: true),
|
||||
playtime_at_note = table.Column<TimeSpan>(type: "TEXT", nullable: false),
|
||||
message = table.Column<string>(type: "TEXT", maxLength: 4096, nullable: false),
|
||||
created_by_id = table.Column<Guid>(type: "TEXT", nullable: true),
|
||||
created_at = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||
last_edited_by_id = table.Column<Guid>(type: "TEXT", nullable: true),
|
||||
last_edited_at = table.Column<DateTime>(type: "TEXT", nullable: true),
|
||||
expiration_time = table.Column<DateTime>(type: "TEXT", nullable: true),
|
||||
deleted = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||
deleted_by_id = table.Column<Guid>(type: "TEXT", nullable: true),
|
||||
deleted_at = table.Column<DateTime>(type: "TEXT", nullable: true),
|
||||
seen = table.Column<bool>(type: "INTEGER", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_admin_messages", x => x.admin_messages_id);
|
||||
table.ForeignKey(
|
||||
name: "FK_admin_messages_player_created_by_id",
|
||||
column: x => x.created_by_id,
|
||||
principalTable: "player",
|
||||
principalColumn: "user_id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
table.ForeignKey(
|
||||
name: "FK_admin_messages_player_deleted_by_id",
|
||||
column: x => x.deleted_by_id,
|
||||
principalTable: "player",
|
||||
principalColumn: "user_id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
table.ForeignKey(
|
||||
name: "FK_admin_messages_player_last_edited_by_id",
|
||||
column: x => x.last_edited_by_id,
|
||||
principalTable: "player",
|
||||
principalColumn: "user_id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
table.ForeignKey(
|
||||
name: "FK_admin_messages_player_player_user_id",
|
||||
column: x => x.player_user_id,
|
||||
principalTable: "player",
|
||||
principalColumn: "user_id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
table.ForeignKey(
|
||||
name: "FK_admin_messages_round_round_id",
|
||||
column: x => x.round_id,
|
||||
principalTable: "round",
|
||||
principalColumn: "round_id");
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "admin_watchlists",
|
||||
columns: table => new
|
||||
{
|
||||
admin_watchlists_id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
round_id = table.Column<int>(type: "INTEGER", nullable: true),
|
||||
player_user_id = table.Column<Guid>(type: "TEXT", nullable: true),
|
||||
playtime_at_note = table.Column<TimeSpan>(type: "TEXT", nullable: false),
|
||||
message = table.Column<string>(type: "TEXT", maxLength: 4096, nullable: false),
|
||||
created_by_id = table.Column<Guid>(type: "TEXT", nullable: true),
|
||||
created_at = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||
last_edited_by_id = table.Column<Guid>(type: "TEXT", nullable: true),
|
||||
last_edited_at = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||
expiration_time = table.Column<DateTime>(type: "TEXT", nullable: true),
|
||||
deleted = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||
deleted_by_id = table.Column<Guid>(type: "TEXT", nullable: true),
|
||||
deleted_at = table.Column<DateTime>(type: "TEXT", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_admin_watchlists", x => x.admin_watchlists_id);
|
||||
table.ForeignKey(
|
||||
name: "FK_admin_watchlists_player_created_by_id",
|
||||
column: x => x.created_by_id,
|
||||
principalTable: "player",
|
||||
principalColumn: "user_id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
table.ForeignKey(
|
||||
name: "FK_admin_watchlists_player_deleted_by_id",
|
||||
column: x => x.deleted_by_id,
|
||||
principalTable: "player",
|
||||
principalColumn: "user_id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
table.ForeignKey(
|
||||
name: "FK_admin_watchlists_player_last_edited_by_id",
|
||||
column: x => x.last_edited_by_id,
|
||||
principalTable: "player",
|
||||
principalColumn: "user_id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
table.ForeignKey(
|
||||
name: "FK_admin_watchlists_player_player_user_id",
|
||||
column: x => x.player_user_id,
|
||||
principalTable: "player",
|
||||
principalColumn: "user_id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
table.ForeignKey(
|
||||
name: "FK_admin_watchlists_round_round_id",
|
||||
column: x => x.round_id,
|
||||
principalTable: "round",
|
||||
principalColumn: "round_id");
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_server_role_ban_banning_admin",
|
||||
table: "server_role_ban",
|
||||
column: "banning_admin");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_server_role_ban_last_edited_by_id",
|
||||
table: "server_role_ban",
|
||||
column: "last_edited_by_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_server_role_ban_round_id",
|
||||
table: "server_role_ban",
|
||||
column: "round_id");
|
||||
|
||||
migrationBuilder.AddCheckConstraint(
|
||||
name: "HaveEitherAddressOrUserIdOrHWId",
|
||||
table: "server_role_ban",
|
||||
sql: "address IS NOT NULL OR player_user_id IS NOT NULL OR hwid IS NOT NULL");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_server_ban_banning_admin",
|
||||
table: "server_ban",
|
||||
column: "banning_admin");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_server_ban_last_edited_by_id",
|
||||
table: "server_ban",
|
||||
column: "last_edited_by_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_server_ban_round_id",
|
||||
table: "server_ban",
|
||||
column: "round_id");
|
||||
|
||||
migrationBuilder.AddCheckConstraint(
|
||||
name: "HaveEitherAddressOrUserIdOrHWId",
|
||||
table: "server_ban",
|
||||
sql: "address IS NOT NULL OR player_user_id IS NOT NULL OR hwid IS NOT NULL");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_admin_messages_created_by_id",
|
||||
table: "admin_messages",
|
||||
column: "created_by_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_admin_messages_deleted_by_id",
|
||||
table: "admin_messages",
|
||||
column: "deleted_by_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_admin_messages_last_edited_by_id",
|
||||
table: "admin_messages",
|
||||
column: "last_edited_by_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_admin_messages_player_user_id",
|
||||
table: "admin_messages",
|
||||
column: "player_user_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_admin_messages_round_id",
|
||||
table: "admin_messages",
|
||||
column: "round_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_admin_watchlists_created_by_id",
|
||||
table: "admin_watchlists",
|
||||
column: "created_by_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_admin_watchlists_deleted_by_id",
|
||||
table: "admin_watchlists",
|
||||
column: "deleted_by_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_admin_watchlists_last_edited_by_id",
|
||||
table: "admin_watchlists",
|
||||
column: "last_edited_by_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_admin_watchlists_player_user_id",
|
||||
table: "admin_watchlists",
|
||||
column: "player_user_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_admin_watchlists_round_id",
|
||||
table: "admin_watchlists",
|
||||
column: "round_id");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_admin_notes_player_created_by_id",
|
||||
table: "admin_notes",
|
||||
column: "created_by_id",
|
||||
principalTable: "player",
|
||||
principalColumn: "user_id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_admin_notes_player_deleted_by_id",
|
||||
table: "admin_notes",
|
||||
column: "deleted_by_id",
|
||||
principalTable: "player",
|
||||
principalColumn: "user_id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_admin_notes_player_last_edited_by_id",
|
||||
table: "admin_notes",
|
||||
column: "last_edited_by_id",
|
||||
principalTable: "player",
|
||||
principalColumn: "user_id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_admin_notes_player_player_user_id",
|
||||
table: "admin_notes",
|
||||
column: "player_user_id",
|
||||
principalTable: "player",
|
||||
principalColumn: "user_id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_server_ban_player_banning_admin",
|
||||
table: "server_ban",
|
||||
column: "banning_admin",
|
||||
principalTable: "player",
|
||||
principalColumn: "user_id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_server_ban_player_last_edited_by_id",
|
||||
table: "server_ban",
|
||||
column: "last_edited_by_id",
|
||||
principalTable: "player",
|
||||
principalColumn: "user_id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_server_ban_round_round_id",
|
||||
table: "server_ban",
|
||||
column: "round_id",
|
||||
principalTable: "round",
|
||||
principalColumn: "round_id");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_server_role_ban_player_banning_admin",
|
||||
table: "server_role_ban",
|
||||
column: "banning_admin",
|
||||
principalTable: "player",
|
||||
principalColumn: "user_id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_server_role_ban_player_last_edited_by_id",
|
||||
table: "server_role_ban",
|
||||
column: "last_edited_by_id",
|
||||
principalTable: "player",
|
||||
principalColumn: "user_id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_server_role_ban_round_round_id",
|
||||
table: "server_role_ban",
|
||||
column: "round_id",
|
||||
principalTable: "round",
|
||||
principalColumn: "round_id");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_admin_notes_player_created_by_id",
|
||||
table: "admin_notes");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_admin_notes_player_deleted_by_id",
|
||||
table: "admin_notes");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_admin_notes_player_last_edited_by_id",
|
||||
table: "admin_notes");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_admin_notes_player_player_user_id",
|
||||
table: "admin_notes");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_server_ban_player_banning_admin",
|
||||
table: "server_ban");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_server_ban_player_last_edited_by_id",
|
||||
table: "server_ban");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_server_ban_round_round_id",
|
||||
table: "server_ban");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_server_role_ban_player_banning_admin",
|
||||
table: "server_role_ban");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_server_role_ban_player_last_edited_by_id",
|
||||
table: "server_role_ban");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_server_role_ban_round_round_id",
|
||||
table: "server_role_ban");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "admin_messages");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "admin_watchlists");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_server_role_ban_banning_admin",
|
||||
table: "server_role_ban");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_server_role_ban_last_edited_by_id",
|
||||
table: "server_role_ban");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_server_role_ban_round_id",
|
||||
table: "server_role_ban");
|
||||
|
||||
migrationBuilder.DropCheckConstraint(
|
||||
name: "HaveEitherAddressOrUserIdOrHWId",
|
||||
table: "server_role_ban");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_server_ban_banning_admin",
|
||||
table: "server_ban");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_server_ban_last_edited_by_id",
|
||||
table: "server_ban");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_server_ban_round_id",
|
||||
table: "server_ban");
|
||||
|
||||
migrationBuilder.DropCheckConstraint(
|
||||
name: "HaveEitherAddressOrUserIdOrHWId",
|
||||
table: "server_ban");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "hidden",
|
||||
table: "server_role_ban");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "last_edited_at",
|
||||
table: "server_role_ban");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "last_edited_by_id",
|
||||
table: "server_role_ban");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "playtime_at_note",
|
||||
table: "server_role_ban");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "round_id",
|
||||
table: "server_role_ban");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "severity",
|
||||
table: "server_role_ban");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "hidden",
|
||||
table: "server_ban");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "last_edited_at",
|
||||
table: "server_ban");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "last_edited_by_id",
|
||||
table: "server_ban");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "playtime_at_note",
|
||||
table: "server_ban");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "round_id",
|
||||
table: "server_ban");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "severity",
|
||||
table: "server_ban");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "expiration_time",
|
||||
table: "admin_notes");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "severity",
|
||||
table: "admin_notes");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "playtime_at_note",
|
||||
table: "admin_notes");
|
||||
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "player_user_id",
|
||||
table: "server_role_ban",
|
||||
newName: "user_id");
|
||||
|
||||
migrationBuilder.RenameIndex(
|
||||
name: "IX_server_role_ban_player_user_id",
|
||||
table: "server_role_ban",
|
||||
newName: "IX_server_role_ban_user_id");
|
||||
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "player_user_id",
|
||||
table: "server_ban",
|
||||
newName: "user_id");
|
||||
|
||||
migrationBuilder.RenameIndex(
|
||||
name: "IX_server_ban_player_user_id",
|
||||
table: "server_ban",
|
||||
newName: "IX_server_ban_user_id");
|
||||
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "secret",
|
||||
table: "admin_notes",
|
||||
newName: "shown_to_player");
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "admin_notes",
|
||||
keyColumn: "shown_to_player",
|
||||
keyValue: true,
|
||||
column: "shown_to_player",
|
||||
value: false);
|
||||
|
||||
migrationBuilder.AlterColumn<Guid>(
|
||||
name: "player_user_id",
|
||||
table: "admin_notes",
|
||||
type: "TEXT",
|
||||
nullable: false,
|
||||
defaultValue: new Guid("00000000-0000-0000-0000-000000000000"),
|
||||
oldClrType: typeof(Guid),
|
||||
oldType: "TEXT",
|
||||
oldNullable: true);
|
||||
|
||||
migrationBuilder.AlterColumn<Guid>(
|
||||
name: "last_edited_by_id",
|
||||
table: "admin_notes",
|
||||
type: "TEXT",
|
||||
nullable: false,
|
||||
defaultValue: new Guid("00000000-0000-0000-0000-000000000000"),
|
||||
oldClrType: typeof(Guid),
|
||||
oldType: "TEXT",
|
||||
oldNullable: true);
|
||||
|
||||
migrationBuilder.AlterColumn<Guid>(
|
||||
name: "created_by_id",
|
||||
table: "admin_notes",
|
||||
type: "TEXT",
|
||||
nullable: false,
|
||||
defaultValue: new Guid("00000000-0000-0000-0000-000000000000"),
|
||||
oldClrType: typeof(Guid),
|
||||
oldType: "TEXT",
|
||||
oldNullable: true);
|
||||
|
||||
migrationBuilder.AddCheckConstraint(
|
||||
name: "HaveEitherAddressOrUserIdOrHWId",
|
||||
table: "server_role_ban",
|
||||
sql: "address IS NOT NULL OR user_id IS NOT NULL OR hwid IS NOT NULL");
|
||||
|
||||
migrationBuilder.AddCheckConstraint(
|
||||
name: "HaveEitherAddressOrUserIdOrHWId",
|
||||
table: "server_ban",
|
||||
sql: "address IS NOT NULL OR user_id IS NOT NULL OR hwid IS NOT NULL");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_admin_notes_player_created_by_id",
|
||||
table: "admin_notes",
|
||||
column: "created_by_id",
|
||||
principalTable: "player",
|
||||
principalColumn: "user_id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_admin_notes_player_deleted_by_id",
|
||||
table: "admin_notes",
|
||||
column: "deleted_by_id",
|
||||
principalTable: "player",
|
||||
principalColumn: "user_id");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_admin_notes_player_last_edited_by_id",
|
||||
table: "admin_notes",
|
||||
column: "last_edited_by_id",
|
||||
principalTable: "player",
|
||||
principalColumn: "user_id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_admin_notes_player_player_user_id",
|
||||
table: "admin_notes",
|
||||
column: "player_user_id",
|
||||
principalTable: "player",
|
||||
principalColumn: "user_id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// <auto-generated />
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Content.Server.Database;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
@@ -170,18 +170,18 @@ namespace Content.Server.Database.Migrations.Sqlite
|
||||
b.ToTable("admin_log_player", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.AdminNote", b =>
|
||||
modelBuilder.Entity("Content.Server.Database.AdminMessage", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("admin_notes_id");
|
||||
.HasColumnName("admin_messages_id");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("created_at");
|
||||
|
||||
b.Property<Guid>("CreatedById")
|
||||
b.Property<Guid?>("CreatedById")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("created_by_id");
|
||||
|
||||
@@ -197,11 +197,15 @@ namespace Content.Server.Database.Migrations.Sqlite
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("deleted_by_id");
|
||||
|
||||
b.Property<DateTime>("LastEditedAt")
|
||||
b.Property<DateTime?>("ExpirationTime")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("expiration_time");
|
||||
|
||||
b.Property<DateTime?>("LastEditedAt")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("last_edited_at");
|
||||
|
||||
b.Property<Guid>("LastEditedById")
|
||||
b.Property<Guid?>("LastEditedById")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("last_edited_by_id");
|
||||
|
||||
@@ -211,17 +215,105 @@ namespace Content.Server.Database.Migrations.Sqlite
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("message");
|
||||
|
||||
b.Property<Guid>("PlayerUserId")
|
||||
b.Property<Guid?>("PlayerUserId")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("player_user_id");
|
||||
|
||||
b.Property<TimeSpan>("PlaytimeAtNote")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("playtime_at_note");
|
||||
|
||||
b.Property<int?>("RoundId")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("round_id");
|
||||
|
||||
b.Property<bool>("ShownToPlayer")
|
||||
b.Property<bool>("Seen")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("shown_to_player");
|
||||
.HasColumnName("seen");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("PK_admin_messages");
|
||||
|
||||
b.HasIndex("CreatedById");
|
||||
|
||||
b.HasIndex("DeletedById");
|
||||
|
||||
b.HasIndex("LastEditedById");
|
||||
|
||||
b.HasIndex("PlayerUserId")
|
||||
.HasDatabaseName("IX_admin_messages_player_user_id");
|
||||
|
||||
b.HasIndex("RoundId")
|
||||
.HasDatabaseName("IX_admin_messages_round_id");
|
||||
|
||||
b.ToTable("admin_messages", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.AdminNote", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("admin_notes_id");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("created_at");
|
||||
|
||||
b.Property<Guid?>("CreatedById")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("created_by_id");
|
||||
|
||||
b.Property<bool>("Deleted")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("deleted");
|
||||
|
||||
b.Property<DateTime?>("DeletedAt")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("deleted_at");
|
||||
|
||||
b.Property<Guid?>("DeletedById")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("deleted_by_id");
|
||||
|
||||
b.Property<DateTime?>("ExpirationTime")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("expiration_time");
|
||||
|
||||
b.Property<DateTime?>("LastEditedAt")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("last_edited_at");
|
||||
|
||||
b.Property<Guid?>("LastEditedById")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("last_edited_by_id");
|
||||
|
||||
b.Property<string>("Message")
|
||||
.IsRequired()
|
||||
.HasMaxLength(4096)
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("message");
|
||||
|
||||
b.Property<int>("Severity")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("severity");
|
||||
|
||||
b.Property<Guid?>("PlayerUserId")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("player_user_id");
|
||||
|
||||
b.Property<TimeSpan>("PlaytimeAtNote")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("playtime_at_note");
|
||||
|
||||
b.Property<int?>("RoundId")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("round_id");
|
||||
|
||||
b.Property<bool>("Secret")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("secret");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("PK_admin_notes");
|
||||
@@ -286,6 +378,82 @@ namespace Content.Server.Database.Migrations.Sqlite
|
||||
b.ToTable("admin_rank_flag", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.AdminWatchlist", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("admin_watchlists_id");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("created_at");
|
||||
|
||||
b.Property<Guid?>("CreatedById")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("created_by_id");
|
||||
|
||||
b.Property<bool>("Deleted")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("deleted");
|
||||
|
||||
b.Property<DateTime?>("DeletedAt")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("deleted_at");
|
||||
|
||||
b.Property<Guid?>("DeletedById")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("deleted_by_id");
|
||||
|
||||
b.Property<DateTime?>("ExpirationTime")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("expiration_time");
|
||||
|
||||
b.Property<DateTime?>("LastEditedAt")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("last_edited_at");
|
||||
|
||||
b.Property<Guid?>("LastEditedById")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("last_edited_by_id");
|
||||
|
||||
b.Property<string>("Message")
|
||||
.IsRequired()
|
||||
.HasMaxLength(4096)
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("message");
|
||||
|
||||
b.Property<Guid?>("PlayerUserId")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("player_user_id");
|
||||
|
||||
b.Property<TimeSpan>("PlaytimeAtNote")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("playtime_at_note");
|
||||
|
||||
b.Property<int?>("RoundId")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("round_id");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("PK_admin_watchlists");
|
||||
|
||||
b.HasIndex("CreatedById");
|
||||
|
||||
b.HasIndex("DeletedById");
|
||||
|
||||
b.HasIndex("LastEditedById");
|
||||
|
||||
b.HasIndex("PlayerUserId")
|
||||
.HasDatabaseName("IX_admin_watchlists_player_user_id");
|
||||
|
||||
b.HasIndex("RoundId")
|
||||
.HasDatabaseName("IX_admin_watchlists_round_id");
|
||||
|
||||
b.ToTable("admin_watchlists", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.Antag", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
@@ -701,25 +869,57 @@ namespace Content.Server.Database.Migrations.Sqlite
|
||||
.HasColumnType("BLOB")
|
||||
.HasColumnName("hwid");
|
||||
|
||||
b.Property<bool>("Hidden")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("hidden");
|
||||
|
||||
b.Property<DateTime?>("LastEditedAt")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("last_edited_at");
|
||||
|
||||
b.Property<Guid?>("LastEditedById")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("last_edited_by_id");
|
||||
|
||||
b.Property<Guid?>("PlayerUserId")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("player_user_id");
|
||||
|
||||
b.Property<TimeSpan>("PlaytimeAtNote")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("playtime_at_note");
|
||||
|
||||
b.Property<string>("Reason")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("reason");
|
||||
|
||||
b.Property<Guid?>("UserId")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("user_id");
|
||||
b.Property<int?>("RoundId")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("round_id");
|
||||
|
||||
b.Property<int>("Severity")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("severity");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("PK_server_ban");
|
||||
|
||||
b.HasIndex("Address");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
b.HasIndex("BanningAdmin");
|
||||
|
||||
b.HasIndex("LastEditedById");
|
||||
|
||||
b.HasIndex("PlayerUserId")
|
||||
.HasDatabaseName("IX_server_ban_player_user_id");
|
||||
|
||||
b.HasIndex("RoundId")
|
||||
.HasDatabaseName("IX_server_ban_round_id");
|
||||
|
||||
b.ToTable("server_ban", null, t =>
|
||||
{
|
||||
t.HasCheckConstraint("HaveEitherAddressOrUserIdOrHWId", "address IS NOT NULL OR user_id IS NOT NULL OR hwid IS NOT NULL");
|
||||
t.HasCheckConstraint("HaveEitherAddressOrUserIdOrHWId", "address IS NOT NULL OR player_user_id IS NOT NULL OR hwid IS NOT NULL");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -797,6 +997,26 @@ namespace Content.Server.Database.Migrations.Sqlite
|
||||
.HasColumnType("BLOB")
|
||||
.HasColumnName("hwid");
|
||||
|
||||
b.Property<bool>("Hidden")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("hidden");
|
||||
|
||||
b.Property<DateTime?>("LastEditedAt")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("last_edited_at");
|
||||
|
||||
b.Property<Guid?>("LastEditedById")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("last_edited_by_id");
|
||||
|
||||
b.Property<Guid?>("PlayerUserId")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("player_user_id");
|
||||
|
||||
b.Property<TimeSpan>("PlaytimeAtNote")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("playtime_at_note");
|
||||
|
||||
b.Property<string>("Reason")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
@@ -807,20 +1027,32 @@ namespace Content.Server.Database.Migrations.Sqlite
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("role_id");
|
||||
|
||||
b.Property<Guid?>("UserId")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("user_id");
|
||||
b.Property<int?>("RoundId")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("round_id");
|
||||
|
||||
b.Property<int>("Severity")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("severity");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("PK_server_role_ban");
|
||||
|
||||
b.HasIndex("Address");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
b.HasIndex("BanningAdmin");
|
||||
|
||||
b.HasIndex("LastEditedById");
|
||||
|
||||
b.HasIndex("PlayerUserId")
|
||||
.HasDatabaseName("IX_server_role_ban_player_user_id");
|
||||
|
||||
b.HasIndex("RoundId")
|
||||
.HasDatabaseName("IX_server_role_ban_round_id");
|
||||
|
||||
b.ToTable("server_role_ban", null, t =>
|
||||
{
|
||||
t.HasCheckConstraint("HaveEitherAddressOrUserIdOrHWId", "address IS NOT NULL OR user_id IS NOT NULL OR hwid IS NOT NULL");
|
||||
t.HasCheckConstraint("HaveEitherAddressOrUserIdOrHWId", "address IS NOT NULL OR player_user_id IS NOT NULL OR hwid IS NOT NULL");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1033,36 +1265,80 @@ namespace Content.Server.Database.Migrations.Sqlite
|
||||
b.Navigation("Player");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.AdminMessage", b =>
|
||||
{
|
||||
b.HasOne("Content.Server.Database.Player", "CreatedBy")
|
||||
.WithMany("AdminMessagesCreated")
|
||||
.HasForeignKey("CreatedById")
|
||||
.HasPrincipalKey("UserId")
|
||||
.OnDelete(DeleteBehavior.SetNull)
|
||||
.HasConstraintName("FK_admin_messages_player_created_by_id");
|
||||
|
||||
b.HasOne("Content.Server.Database.Player", "DeletedBy")
|
||||
.WithMany("AdminMessagesDeleted")
|
||||
.HasForeignKey("DeletedById")
|
||||
.HasPrincipalKey("UserId")
|
||||
.OnDelete(DeleteBehavior.SetNull)
|
||||
.HasConstraintName("FK_admin_messages_player_deleted_by_id");
|
||||
|
||||
b.HasOne("Content.Server.Database.Player", "LastEditedBy")
|
||||
.WithMany("AdminMessagesLastEdited")
|
||||
.HasForeignKey("LastEditedById")
|
||||
.HasPrincipalKey("UserId")
|
||||
.OnDelete(DeleteBehavior.SetNull)
|
||||
.HasConstraintName("FK_admin_messages_player_last_edited_by_id");
|
||||
|
||||
b.HasOne("Content.Server.Database.Player", "Player")
|
||||
.WithMany("AdminMessagesReceived")
|
||||
.HasForeignKey("PlayerUserId")
|
||||
.HasPrincipalKey("UserId")
|
||||
.OnDelete(DeleteBehavior.SetNull)
|
||||
.HasConstraintName("FK_admin_messages_player_player_user_id");
|
||||
|
||||
b.HasOne("Content.Server.Database.Round", "Round")
|
||||
.WithMany()
|
||||
.HasForeignKey("RoundId")
|
||||
.HasConstraintName("FK_admin_messages_round_round_id");
|
||||
|
||||
b.Navigation("CreatedBy");
|
||||
|
||||
b.Navigation("DeletedBy");
|
||||
|
||||
b.Navigation("LastEditedBy");
|
||||
|
||||
b.Navigation("Player");
|
||||
|
||||
b.Navigation("Round");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.AdminNote", b =>
|
||||
{
|
||||
b.HasOne("Content.Server.Database.Player", "CreatedBy")
|
||||
.WithMany("AdminNotesCreated")
|
||||
.HasForeignKey("CreatedById")
|
||||
.HasPrincipalKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.OnDelete(DeleteBehavior.SetNull)
|
||||
.HasConstraintName("FK_admin_notes_player_created_by_id");
|
||||
|
||||
b.HasOne("Content.Server.Database.Player", "DeletedBy")
|
||||
.WithMany("AdminNotesDeleted")
|
||||
.HasForeignKey("DeletedById")
|
||||
.HasPrincipalKey("UserId")
|
||||
.OnDelete(DeleteBehavior.SetNull)
|
||||
.HasConstraintName("FK_admin_notes_player_deleted_by_id");
|
||||
|
||||
b.HasOne("Content.Server.Database.Player", "LastEditedBy")
|
||||
.WithMany("AdminNotesLastEdited")
|
||||
.HasForeignKey("LastEditedById")
|
||||
.HasPrincipalKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.OnDelete(DeleteBehavior.SetNull)
|
||||
.HasConstraintName("FK_admin_notes_player_last_edited_by_id");
|
||||
|
||||
b.HasOne("Content.Server.Database.Player", "Player")
|
||||
.WithMany("AdminNotesReceived")
|
||||
.HasForeignKey("PlayerUserId")
|
||||
.HasPrincipalKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.OnDelete(DeleteBehavior.SetNull)
|
||||
.HasConstraintName("FK_admin_notes_player_player_user_id");
|
||||
|
||||
b.HasOne("Content.Server.Database.Round", "Round")
|
||||
@@ -1093,6 +1369,52 @@ namespace Content.Server.Database.Migrations.Sqlite
|
||||
b.Navigation("Rank");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.AdminWatchlist", b =>
|
||||
{
|
||||
b.HasOne("Content.Server.Database.Player", "CreatedBy")
|
||||
.WithMany("AdminWatchlistsCreated")
|
||||
.HasForeignKey("CreatedById")
|
||||
.HasPrincipalKey("UserId")
|
||||
.OnDelete(DeleteBehavior.SetNull)
|
||||
.HasConstraintName("FK_admin_watchlists_player_created_by_id");
|
||||
|
||||
b.HasOne("Content.Server.Database.Player", "DeletedBy")
|
||||
.WithMany("AdminWatchlistsDeleted")
|
||||
.HasForeignKey("DeletedById")
|
||||
.HasPrincipalKey("UserId")
|
||||
.OnDelete(DeleteBehavior.SetNull)
|
||||
.HasConstraintName("FK_admin_watchlists_player_deleted_by_id");
|
||||
|
||||
b.HasOne("Content.Server.Database.Player", "LastEditedBy")
|
||||
.WithMany("AdminWatchlistsLastEdited")
|
||||
.HasForeignKey("LastEditedById")
|
||||
.HasPrincipalKey("UserId")
|
||||
.OnDelete(DeleteBehavior.SetNull)
|
||||
.HasConstraintName("FK_admin_watchlists_player_last_edited_by_id");
|
||||
|
||||
b.HasOne("Content.Server.Database.Player", "Player")
|
||||
.WithMany("AdminWatchlistsReceived")
|
||||
.HasForeignKey("PlayerUserId")
|
||||
.HasPrincipalKey("UserId")
|
||||
.OnDelete(DeleteBehavior.SetNull)
|
||||
.HasConstraintName("FK_admin_watchlists_player_player_user_id");
|
||||
|
||||
b.HasOne("Content.Server.Database.Round", "Round")
|
||||
.WithMany()
|
||||
.HasForeignKey("RoundId")
|
||||
.HasConstraintName("FK_admin_watchlists_round_round_id");
|
||||
|
||||
b.Navigation("CreatedBy");
|
||||
|
||||
b.Navigation("DeletedBy");
|
||||
|
||||
b.Navigation("LastEditedBy");
|
||||
|
||||
b.Navigation("Player");
|
||||
|
||||
b.Navigation("Round");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.Antag", b =>
|
||||
{
|
||||
b.HasOne("Content.Server.Database.Profile", "Profile")
|
||||
@@ -1141,6 +1463,34 @@ namespace Content.Server.Database.Migrations.Sqlite
|
||||
b.Navigation("Server");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.ServerBan", b =>
|
||||
{
|
||||
b.HasOne("Content.Server.Database.Player", "CreatedBy")
|
||||
.WithMany("AdminServerBansCreated")
|
||||
.HasForeignKey("BanningAdmin")
|
||||
.HasPrincipalKey("UserId")
|
||||
.OnDelete(DeleteBehavior.SetNull)
|
||||
.HasConstraintName("FK_server_ban_player_banning_admin");
|
||||
|
||||
b.HasOne("Content.Server.Database.Player", "LastEditedBy")
|
||||
.WithMany("AdminServerBansLastEdited")
|
||||
.HasForeignKey("LastEditedById")
|
||||
.HasPrincipalKey("UserId")
|
||||
.OnDelete(DeleteBehavior.SetNull)
|
||||
.HasConstraintName("FK_server_ban_player_last_edited_by_id");
|
||||
|
||||
b.HasOne("Content.Server.Database.Round", "Round")
|
||||
.WithMany()
|
||||
.HasForeignKey("RoundId")
|
||||
.HasConstraintName("FK_server_ban_round_round_id");
|
||||
|
||||
b.Navigation("CreatedBy");
|
||||
|
||||
b.Navigation("LastEditedBy");
|
||||
|
||||
b.Navigation("Round");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.ServerBanHit", b =>
|
||||
{
|
||||
b.HasOne("Content.Server.Database.ServerBan", "Ban")
|
||||
@@ -1162,6 +1512,34 @@ namespace Content.Server.Database.Migrations.Sqlite
|
||||
b.Navigation("Connection");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.ServerRoleBan", b =>
|
||||
{
|
||||
b.HasOne("Content.Server.Database.Player", "CreatedBy")
|
||||
.WithMany("AdminServerRoleBansCreated")
|
||||
.HasForeignKey("BanningAdmin")
|
||||
.HasPrincipalKey("UserId")
|
||||
.OnDelete(DeleteBehavior.SetNull)
|
||||
.HasConstraintName("FK_server_role_ban_player_banning_admin");
|
||||
|
||||
b.HasOne("Content.Server.Database.Player", "LastEditedBy")
|
||||
.WithMany("AdminServerRoleBansLastEdited")
|
||||
.HasForeignKey("LastEditedById")
|
||||
.HasPrincipalKey("UserId")
|
||||
.OnDelete(DeleteBehavior.SetNull)
|
||||
.HasConstraintName("FK_server_role_ban_player_last_edited_by_id");
|
||||
|
||||
b.HasOne("Content.Server.Database.Round", "Round")
|
||||
.WithMany()
|
||||
.HasForeignKey("RoundId")
|
||||
.HasConstraintName("FK_server_role_ban_round_round_id");
|
||||
|
||||
b.Navigation("CreatedBy");
|
||||
|
||||
b.Navigation("LastEditedBy");
|
||||
|
||||
b.Navigation("Round");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.ServerRoleUnban", b =>
|
||||
{
|
||||
b.HasOne("Content.Server.Database.ServerRoleBan", "Ban")
|
||||
@@ -1243,6 +1621,14 @@ namespace Content.Server.Database.Migrations.Sqlite
|
||||
{
|
||||
b.Navigation("AdminLogs");
|
||||
|
||||
b.Navigation("AdminMessagesCreated");
|
||||
|
||||
b.Navigation("AdminMessagesDeleted");
|
||||
|
||||
b.Navigation("AdminMessagesLastEdited");
|
||||
|
||||
b.Navigation("AdminMessagesReceived");
|
||||
|
||||
b.Navigation("AdminNotesCreated");
|
||||
|
||||
b.Navigation("AdminNotesDeleted");
|
||||
@@ -1250,6 +1636,22 @@ namespace Content.Server.Database.Migrations.Sqlite
|
||||
b.Navigation("AdminNotesLastEdited");
|
||||
|
||||
b.Navigation("AdminNotesReceived");
|
||||
|
||||
b.Navigation("AdminServerBansCreated");
|
||||
|
||||
b.Navigation("AdminServerBansLastEdited");
|
||||
|
||||
b.Navigation("AdminServerRoleBansCreated");
|
||||
|
||||
b.Navigation("AdminServerRoleBansLastEdited");
|
||||
|
||||
b.Navigation("AdminWatchlistsCreated");
|
||||
|
||||
b.Navigation("AdminWatchlistsDeleted");
|
||||
|
||||
b.Navigation("AdminWatchlistsLastEdited");
|
||||
|
||||
b.Navigation("AdminWatchlistsReceived");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.Preference", b =>
|
||||
|
||||
@@ -37,6 +37,8 @@ namespace Content.Server.Database
|
||||
public DbSet<PlayTime> PlayTime { get; set; } = default!;
|
||||
public DbSet<UploadedResourceLog> UploadedResourceLog { get; set; } = default!;
|
||||
public DbSet<AdminNote> AdminNotes { get; set; } = null!;
|
||||
public DbSet<AdminWatchlist> AdminWatchlists { get; set; } = null!;
|
||||
public DbSet<AdminMessage> AdminMessages { get; set; } = null!;
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
@@ -114,20 +116,20 @@ namespace Content.Server.Database
|
||||
.HasKey(logPlayer => new {logPlayer.PlayerUserId, logPlayer.LogId, logPlayer.RoundId});
|
||||
|
||||
modelBuilder.Entity<ServerBan>()
|
||||
.HasIndex(p => p.UserId);
|
||||
.HasIndex(p => p.PlayerUserId);
|
||||
|
||||
modelBuilder.Entity<ServerBan>()
|
||||
.HasIndex(p => p.Address);
|
||||
|
||||
modelBuilder.Entity<ServerBan>()
|
||||
.HasIndex(p => p.UserId);
|
||||
.HasIndex(p => p.PlayerUserId);
|
||||
|
||||
modelBuilder.Entity<ServerUnban>()
|
||||
.HasIndex(p => p.BanId)
|
||||
.IsUnique();
|
||||
|
||||
modelBuilder.Entity<ServerBan>().ToTable(t =>
|
||||
t.HasCheckConstraint("HaveEitherAddressOrUserIdOrHWId", "address IS NOT NULL OR user_id IS NOT NULL OR hwid IS NOT NULL"));
|
||||
t.HasCheckConstraint("HaveEitherAddressOrUserIdOrHWId", "address IS NOT NULL OR player_user_id IS NOT NULL OR hwid IS NOT NULL"));
|
||||
|
||||
// Ban exemption can't have flags 0 since that wouldn't exempt anything.
|
||||
// The row should be removed if setting to 0.
|
||||
@@ -135,20 +137,20 @@ namespace Content.Server.Database
|
||||
t.HasCheckConstraint("FlagsNotZero", "flags != 0"));
|
||||
|
||||
modelBuilder.Entity<ServerRoleBan>()
|
||||
.HasIndex(p => p.UserId);
|
||||
.HasIndex(p => p.PlayerUserId);
|
||||
|
||||
modelBuilder.Entity<ServerRoleBan>()
|
||||
.HasIndex(p => p.Address);
|
||||
|
||||
modelBuilder.Entity<ServerRoleBan>()
|
||||
.HasIndex(p => p.UserId);
|
||||
.HasIndex(p => p.PlayerUserId);
|
||||
|
||||
modelBuilder.Entity<ServerRoleUnban>()
|
||||
.HasIndex(p => p.BanId)
|
||||
.IsUnique();
|
||||
|
||||
modelBuilder.Entity<ServerRoleBan>().ToTable(t =>
|
||||
t.HasCheckConstraint("HaveEitherAddressOrUserIdOrHWId", "address IS NOT NULL OR user_id IS NOT NULL OR hwid IS NOT NULL"));
|
||||
t.HasCheckConstraint("HaveEitherAddressOrUserIdOrHWId", "address IS NOT NULL OR player_user_id IS NOT NULL OR hwid IS NOT NULL"));
|
||||
|
||||
modelBuilder.Entity<Player>()
|
||||
.HasIndex(p => p.UserId)
|
||||
@@ -160,29 +162,118 @@ namespace Content.Server.Database
|
||||
modelBuilder.Entity<ConnectionLog>()
|
||||
.HasIndex(p => p.UserId);
|
||||
|
||||
// SetNull is necessary here so you can safely delete admins (GDPR right to erasure) while keeping the notes intact
|
||||
modelBuilder.Entity<AdminNote>()
|
||||
.HasOne(note => note.Player)
|
||||
.WithMany(player => player.AdminNotesReceived)
|
||||
.HasForeignKey(note => note.PlayerUserId)
|
||||
.HasPrincipalKey(player => player.UserId);
|
||||
.HasPrincipalKey(player => player.UserId)
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
modelBuilder.Entity<AdminNote>()
|
||||
.HasOne(version => version.CreatedBy)
|
||||
.WithMany(author => author.AdminNotesCreated)
|
||||
.HasForeignKey(note => note.CreatedById)
|
||||
.HasPrincipalKey(author => author.UserId);
|
||||
.HasPrincipalKey(author => author.UserId)
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
modelBuilder.Entity<AdminNote>()
|
||||
.HasOne(version => version.LastEditedBy)
|
||||
.WithMany(author => author.AdminNotesLastEdited)
|
||||
.HasForeignKey(note => note.LastEditedById)
|
||||
.HasPrincipalKey(author => author.UserId);
|
||||
.HasPrincipalKey(author => author.UserId)
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
modelBuilder.Entity<AdminNote>()
|
||||
.HasOne(version => version.DeletedBy)
|
||||
.WithMany(author => author.AdminNotesDeleted)
|
||||
.HasForeignKey(note => note.DeletedById)
|
||||
.HasPrincipalKey(author => author.UserId);
|
||||
.HasPrincipalKey(author => author.UserId)
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
modelBuilder.Entity<AdminWatchlist>()
|
||||
.HasOne(note => note.Player)
|
||||
.WithMany(player => player.AdminWatchlistsReceived)
|
||||
.HasForeignKey(note => note.PlayerUserId)
|
||||
.HasPrincipalKey(player => player.UserId)
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
modelBuilder.Entity<AdminWatchlist>()
|
||||
.HasOne(version => version.CreatedBy)
|
||||
.WithMany(author => author.AdminWatchlistsCreated)
|
||||
.HasForeignKey(note => note.CreatedById)
|
||||
.HasPrincipalKey(author => author.UserId)
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
modelBuilder.Entity<AdminWatchlist>()
|
||||
.HasOne(version => version.LastEditedBy)
|
||||
.WithMany(author => author.AdminWatchlistsLastEdited)
|
||||
.HasForeignKey(note => note.LastEditedById)
|
||||
.HasPrincipalKey(author => author.UserId)
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
modelBuilder.Entity<AdminWatchlist>()
|
||||
.HasOne(version => version.DeletedBy)
|
||||
.WithMany(author => author.AdminWatchlistsDeleted)
|
||||
.HasForeignKey(note => note.DeletedById)
|
||||
.HasPrincipalKey(author => author.UserId)
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
modelBuilder.Entity<AdminMessage>()
|
||||
.HasOne(note => note.Player)
|
||||
.WithMany(player => player.AdminMessagesReceived)
|
||||
.HasForeignKey(note => note.PlayerUserId)
|
||||
.HasPrincipalKey(player => player.UserId)
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
modelBuilder.Entity<AdminMessage>()
|
||||
.HasOne(version => version.CreatedBy)
|
||||
.WithMany(author => author.AdminMessagesCreated)
|
||||
.HasForeignKey(note => note.CreatedById)
|
||||
.HasPrincipalKey(author => author.UserId)
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
modelBuilder.Entity<AdminMessage>()
|
||||
.HasOne(version => version.LastEditedBy)
|
||||
.WithMany(author => author.AdminMessagesLastEdited)
|
||||
.HasForeignKey(note => note.LastEditedById)
|
||||
.HasPrincipalKey(author => author.UserId)
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
modelBuilder.Entity<AdminMessage>()
|
||||
.HasOne(version => version.DeletedBy)
|
||||
.WithMany(author => author.AdminMessagesDeleted)
|
||||
.HasForeignKey(note => note.DeletedById)
|
||||
.HasPrincipalKey(author => author.UserId)
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
modelBuilder.Entity<ServerBan>()
|
||||
.HasOne(ban => ban.CreatedBy)
|
||||
.WithMany(author => author.AdminServerBansCreated)
|
||||
.HasForeignKey(ban => ban.BanningAdmin)
|
||||
.HasPrincipalKey(author => author.UserId)
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
modelBuilder.Entity<ServerBan>()
|
||||
.HasOne(ban => ban.LastEditedBy)
|
||||
.WithMany(author => author.AdminServerBansLastEdited)
|
||||
.HasForeignKey(ban => ban.LastEditedById)
|
||||
.HasPrincipalKey(author => author.UserId)
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
modelBuilder.Entity<ServerRoleBan>()
|
||||
.HasOne(ban => ban.CreatedBy)
|
||||
.WithMany(author => author.AdminServerRoleBansCreated)
|
||||
.HasForeignKey(ban => ban.BanningAdmin)
|
||||
.HasPrincipalKey(author => author.UserId)
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
modelBuilder.Entity<ServerRoleBan>()
|
||||
.HasOne(ban => ban.LastEditedBy)
|
||||
.WithMany(author => author.AdminServerRoleBansLastEdited)
|
||||
.HasForeignKey(ban => ban.LastEditedById)
|
||||
.HasPrincipalKey(author => author.UserId)
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
}
|
||||
|
||||
public virtual IQueryable<AdminLog> SearchLogs(IQueryable<AdminLog> query, string searchText)
|
||||
@@ -313,6 +404,18 @@ namespace Content.Server.Database
|
||||
public List<AdminNote> AdminNotesCreated { get; set; } = null!;
|
||||
public List<AdminNote> AdminNotesLastEdited { get; set; } = null!;
|
||||
public List<AdminNote> AdminNotesDeleted { get; set; } = null!;
|
||||
public List<AdminWatchlist> AdminWatchlistsReceived { get; set; } = null!;
|
||||
public List<AdminWatchlist> AdminWatchlistsCreated { get; set; } = null!;
|
||||
public List<AdminWatchlist> AdminWatchlistsLastEdited { get; set; } = null!;
|
||||
public List<AdminWatchlist> AdminWatchlistsDeleted { get; set; } = null!;
|
||||
public List<AdminMessage> AdminMessagesReceived { get; set; } = null!;
|
||||
public List<AdminMessage> AdminMessagesCreated { get; set; } = null!;
|
||||
public List<AdminMessage> AdminMessagesLastEdited { get; set; } = null!;
|
||||
public List<AdminMessage> AdminMessagesDeleted { get; set; } = null!;
|
||||
public List<ServerBan> AdminServerBansCreated { get; set; } = null!;
|
||||
public List<ServerBan> AdminServerBansLastEdited { get; set; } = null!;
|
||||
public List<ServerRoleBan> AdminServerRoleBansCreated { get; set; } = null!;
|
||||
public List<ServerRoleBan> AdminServerRoleBansLastEdited { get; set; } = null!;
|
||||
}
|
||||
|
||||
[Table("whitelist")]
|
||||
@@ -427,12 +530,13 @@ namespace Content.Server.Database
|
||||
public interface IBanCommon<TUnban> where TUnban : IUnbanCommon
|
||||
{
|
||||
int Id { get; set; }
|
||||
Guid? UserId { get; set; }
|
||||
Guid? PlayerUserId { get; set; }
|
||||
(IPAddress, int)? Address { get; set; }
|
||||
byte[]? HWId { get; set; }
|
||||
DateTime BanTime { get; set; }
|
||||
DateTime? ExpirationTime { get; set; }
|
||||
string Reason { get; set; }
|
||||
NoteSeverity Severity { get; set; }
|
||||
Guid? BanningAdmin { get; set; }
|
||||
TUnban? Unban { get; set; }
|
||||
}
|
||||
@@ -477,20 +581,26 @@ namespace Content.Server.Database
|
||||
/// <remarks>
|
||||
/// At least one of UserID, IP, or HWID must be given (otherwise the ban would match nothing).
|
||||
/// </remarks>
|
||||
[Table("server_ban")]
|
||||
[Table("server_ban"), Index(nameof(PlayerUserId))]
|
||||
public class ServerBan : IBanCommon<ServerUnban>
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
[ForeignKey("Round")]
|
||||
public int? RoundId { get; set; }
|
||||
public Round? Round { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The user ID of the banned player.
|
||||
/// </summary>
|
||||
public Guid? UserId { get; set; }
|
||||
public Guid? PlayerUserId { get; set; }
|
||||
[Required] public TimeSpan PlaytimeAtNote { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// CIDR IP address range of the ban. The whole range can match the ban.
|
||||
/// </summary>
|
||||
[Column(TypeName = "inet")] public (IPAddress, int)? Address { get; set; }
|
||||
[Column(TypeName = "inet")]
|
||||
public (IPAddress, int)? Address { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Hardware ID of the banned player.
|
||||
@@ -512,11 +622,32 @@ namespace Content.Server.Database
|
||||
/// </summary>
|
||||
public string Reason { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// The severity of the incident
|
||||
/// </summary>
|
||||
public NoteSeverity Severity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// User ID of the admin that applied the ban.
|
||||
/// </summary>
|
||||
[ForeignKey("CreatedBy")]
|
||||
public Guid? BanningAdmin { get; set; }
|
||||
|
||||
public Player? CreatedBy { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// User ID of the admin that last edited the note
|
||||
/// </summary>
|
||||
[ForeignKey("LastEditedBy")]
|
||||
public Guid? LastEditedById { get; set; }
|
||||
|
||||
public Player? LastEditedBy { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// When the ban was last edited
|
||||
/// </summary>
|
||||
public DateTime? LastEditedAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional flags that allow adding exemptions to the ban via <see cref="ServerBanExemption"/>.
|
||||
/// </summary>
|
||||
@@ -538,6 +669,11 @@ namespace Content.Server.Database
|
||||
/// </remarks>
|
||||
public bool AutoDelete { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether to display this ban in the admin remarks (notes) panel
|
||||
/// </summary>
|
||||
public bool Hidden { get; set; }
|
||||
|
||||
public List<ServerBanHit> BanHits { get; set; } = null!;
|
||||
}
|
||||
|
||||
@@ -633,11 +769,14 @@ namespace Content.Server.Database
|
||||
public ConnectionLog Connection { get; set; } = null!;
|
||||
}
|
||||
|
||||
[Table("server_role_ban")]
|
||||
[Table("server_role_ban"), Index(nameof(PlayerUserId))]
|
||||
public sealed class ServerRoleBan : IBanCommon<ServerRoleUnban>
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public Guid? UserId { get; set; }
|
||||
public int? RoundId { get; set; }
|
||||
public Round? Round { get; set; }
|
||||
public Guid? PlayerUserId { get; set; }
|
||||
[Required] public TimeSpan PlaytimeAtNote { get; set; }
|
||||
[Column(TypeName = "inet")] public (IPAddress, int)? Address { get; set; }
|
||||
public byte[]? HWId { get; set; }
|
||||
|
||||
@@ -646,9 +785,17 @@ namespace Content.Server.Database
|
||||
public DateTime? ExpirationTime { get; set; }
|
||||
|
||||
public string Reason { get; set; } = null!;
|
||||
public Guid? BanningAdmin { get; set; }
|
||||
|
||||
public NoteSeverity Severity { get; set; }
|
||||
[ForeignKey("CreatedBy")] public Guid? BanningAdmin { get; set; }
|
||||
public Player? CreatedBy { get; set; }
|
||||
|
||||
[ForeignKey("LastEditedBy")] public Guid? LastEditedById { get; set; }
|
||||
public Player? LastEditedBy { get; set; }
|
||||
public DateTime? LastEditedAt { get; set; }
|
||||
|
||||
public ServerRoleUnban? Unban { get; set; }
|
||||
public bool Hidden { get; set; }
|
||||
|
||||
public string RoleId { get; set; } = null!;
|
||||
}
|
||||
@@ -695,34 +842,126 @@ namespace Content.Server.Database
|
||||
public byte[] Data { get; set; } = default!;
|
||||
}
|
||||
|
||||
public interface IAdminRemarksCommon
|
||||
{
|
||||
public int Id { get; }
|
||||
|
||||
public int? RoundId { get; }
|
||||
public Round? Round { get; }
|
||||
|
||||
public Guid? PlayerUserId { get; }
|
||||
public Player? Player { get; }
|
||||
public TimeSpan PlaytimeAtNote { get; }
|
||||
|
||||
public string Message { get; }
|
||||
|
||||
public Player? CreatedBy { get; }
|
||||
|
||||
public DateTime CreatedAt { get; }
|
||||
|
||||
public Player? LastEditedBy { get; }
|
||||
|
||||
public DateTime? LastEditedAt { get; }
|
||||
public DateTime? ExpirationTime { get; }
|
||||
|
||||
public bool Deleted { get; }
|
||||
}
|
||||
|
||||
[Index(nameof(PlayerUserId))]
|
||||
public class AdminNote
|
||||
public class AdminNote : IAdminRemarksCommon
|
||||
{
|
||||
[Required, Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id { get; set; }
|
||||
|
||||
[ForeignKey("Round")] public int? RoundId { get; set; }
|
||||
public Round? Round { get; set; }
|
||||
|
||||
[Required, ForeignKey("Player")] public Guid PlayerUserId { get; set; }
|
||||
public Player Player { get; set; } = default!;
|
||||
[ForeignKey("Player")] public Guid? PlayerUserId { get; set; }
|
||||
public Player? Player { get; set; }
|
||||
[Required] public TimeSpan PlaytimeAtNote { get; set; }
|
||||
|
||||
[Required, MaxLength(4096)] public string Message { get; set; } = string.Empty;
|
||||
[Required] public NoteSeverity Severity { get; set; }
|
||||
|
||||
[Required, ForeignKey("CreatedBy")] public Guid CreatedById { get; set; }
|
||||
[Required] public Player CreatedBy { get; set; } = default!;
|
||||
[ForeignKey("CreatedBy")] public Guid? CreatedById { get; set; }
|
||||
public Player? CreatedBy { get; set; }
|
||||
|
||||
[Required] public DateTime CreatedAt { get; set; }
|
||||
|
||||
[Required, ForeignKey("LastEditedBy")] public Guid LastEditedById { get; set; }
|
||||
[Required] public Player LastEditedBy { get; set; } = default!;
|
||||
[ForeignKey("LastEditedBy")] public Guid? LastEditedById { get; set; }
|
||||
public Player? LastEditedBy { get; set; }
|
||||
|
||||
[Required] public DateTime LastEditedAt { get; set; }
|
||||
[Required] public DateTime? LastEditedAt { get; set; }
|
||||
public DateTime? ExpirationTime { get; set; }
|
||||
|
||||
public bool Deleted { get; set; }
|
||||
[ForeignKey("DeletedBy")] public Guid? DeletedById { get; set; }
|
||||
public Player? DeletedBy { get; set; }
|
||||
public DateTime? DeletedAt { get; set; }
|
||||
|
||||
public bool ShownToPlayer { get; set; }
|
||||
public bool Secret { get; set; }
|
||||
}
|
||||
|
||||
[Index(nameof(PlayerUserId))]
|
||||
public class AdminWatchlist : IAdminRemarksCommon
|
||||
{
|
||||
[Required, Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id { get; set; }
|
||||
|
||||
[ForeignKey("Round")] public int? RoundId { get; set; }
|
||||
public Round? Round { get; set; }
|
||||
|
||||
[ForeignKey("Player")] public Guid? PlayerUserId { get; set; }
|
||||
public Player? Player { get; set; }
|
||||
[Required] public TimeSpan PlaytimeAtNote { get; set; }
|
||||
|
||||
[Required, MaxLength(4096)] public string Message { get; set; } = string.Empty;
|
||||
|
||||
[ForeignKey("CreatedBy")] public Guid? CreatedById { get; set; }
|
||||
public Player? CreatedBy { get; set; }
|
||||
|
||||
[Required] public DateTime CreatedAt { get; set; }
|
||||
|
||||
[ForeignKey("LastEditedBy")] public Guid? LastEditedById { get; set; }
|
||||
public Player? LastEditedBy { get; set; }
|
||||
|
||||
[Required] public DateTime? LastEditedAt { get; set; }
|
||||
public DateTime? ExpirationTime { get; set; }
|
||||
|
||||
public bool Deleted { get; set; }
|
||||
[ForeignKey("DeletedBy")] public Guid? DeletedById { get; set; }
|
||||
public Player? DeletedBy { get; set; }
|
||||
public DateTime? DeletedAt { get; set; }
|
||||
}
|
||||
|
||||
[Index(nameof(PlayerUserId))]
|
||||
public class AdminMessage : IAdminRemarksCommon
|
||||
{
|
||||
[Required, Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id { get; set; }
|
||||
|
||||
[ForeignKey("Round")] public int? RoundId { get; set; }
|
||||
public Round? Round { get; set; }
|
||||
|
||||
[ForeignKey("Player")]
|
||||
public Guid? PlayerUserId { get; set; }
|
||||
public Player? Player { get; set; }
|
||||
[Required] public TimeSpan PlaytimeAtNote { get; set; }
|
||||
|
||||
[Required, MaxLength(4096)] public string Message { get; set; } = string.Empty;
|
||||
|
||||
[ForeignKey("CreatedBy")] public Guid? CreatedById { get; set; }
|
||||
public Player? CreatedBy { get; set; }
|
||||
|
||||
[Required] public DateTime CreatedAt { get; set; }
|
||||
|
||||
[ForeignKey("LastEditedBy")] public Guid? LastEditedById { get; set; }
|
||||
public Player? LastEditedBy { get; set; }
|
||||
|
||||
public DateTime? LastEditedAt { get; set; }
|
||||
public DateTime? ExpirationTime { get; set; }
|
||||
|
||||
public bool Deleted { get; set; }
|
||||
[ForeignKey("DeletedBy")] public Guid? DeletedById { get; set; }
|
||||
public Player? DeletedBy { get; set; }
|
||||
public DateTime? DeletedAt { get; set; }
|
||||
public bool Seen { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
169
Content.Server/Administration/BanPanelEui.cs
Normal file
169
Content.Server/Administration/BanPanelEui.cs
Normal file
@@ -0,0 +1,169 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.Administration.Managers;
|
||||
using Content.Server.Chat.Managers;
|
||||
using Content.Server.Construction.Conditions;
|
||||
using Content.Server.EUI;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Eui;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Network;
|
||||
|
||||
namespace Content.Server.Administration;
|
||||
|
||||
public sealed class BanPanelEui : BaseEui
|
||||
{
|
||||
[Dependency] private readonly IBanManager _banManager = default!;
|
||||
[Dependency] private readonly IPlayerLocator _playerLocator = default!;
|
||||
[Dependency] private readonly IChatManager _chat = default!;
|
||||
[Dependency] private readonly IAdminManager _admins = default!;
|
||||
|
||||
private NetUserId? PlayerId { get; set; }
|
||||
private string PlayerName { get; set; } = string.Empty;
|
||||
private IPAddress? LastAddress { get; set; }
|
||||
private ImmutableArray<byte>? LastHwid { get; set; }
|
||||
|
||||
public BanPanelEui()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
}
|
||||
|
||||
public override EuiStateBase GetNewState()
|
||||
{
|
||||
var hasBan = _admins.HasAdminFlag(Player, AdminFlags.Ban);
|
||||
return new BanPanelEuiState(PlayerName, hasBan);
|
||||
}
|
||||
|
||||
public override void HandleMessage(EuiMessageBase msg)
|
||||
{
|
||||
base.HandleMessage(msg);
|
||||
|
||||
switch (msg)
|
||||
{
|
||||
case BanPanelEuiStateMsg.CreateBanRequest r:
|
||||
BanPlayer(r.Player, r.IpAddress, r.UseLastIp, r.Hwid?.ToImmutableArray(), r.UseLastHwid, r.Minutes, r.Severity, r.Reason, r.Roles);
|
||||
break;
|
||||
case BanPanelEuiStateMsg.GetPlayerInfoRequest r:
|
||||
ChangePlayer(r.PlayerUsername);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private async void BanPlayer(string? target, string? ipAddressString, bool useLastIp, ImmutableArray<byte>? hwid, bool useLastHwid, uint minutes, NoteSeverity severity, string reason, IReadOnlyCollection<string>? roles)
|
||||
{
|
||||
if (!_admins.HasAdminFlag(Player, AdminFlags.Ban))
|
||||
{
|
||||
Logger.WarningS("admin.bans_eui", $"{Player.Name} ({Player.UserId}) tried to create a ban with no ban flag");
|
||||
return;
|
||||
}
|
||||
if (target == null && string.IsNullOrWhiteSpace(ipAddressString) && hwid == null)
|
||||
{
|
||||
_chat.DispatchServerMessage(Player, Loc.GetString("ban-panel-no-data"));
|
||||
return;
|
||||
}
|
||||
|
||||
(IPAddress, int)? addressRange = null;
|
||||
if (ipAddressString is not null)
|
||||
{
|
||||
var hid = "0";
|
||||
var split = ipAddressString.Split('/', 2);
|
||||
ipAddressString = split[0];
|
||||
if (split.Length > 1)
|
||||
hid = split[1];
|
||||
|
||||
if (!IPAddress.TryParse(ipAddressString, out var ipAddress) || !uint.TryParse(hid, out var hidInt) || hidInt > 128 || hidInt > 32 && ipAddress.AddressFamily == AddressFamily.InterNetwork)
|
||||
{
|
||||
_chat.DispatchServerMessage(Player, Loc.GetString("ban-panel-invalid-ip"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (hidInt == 0)
|
||||
hidInt = (uint) (ipAddress.AddressFamily == AddressFamily.InterNetworkV6 ? 128 : 32);
|
||||
|
||||
addressRange = (ipAddress, (int) hidInt);
|
||||
}
|
||||
|
||||
var targetUid = target is not null ? PlayerId : null;
|
||||
addressRange = useLastIp && LastAddress is not null ? (LastAddress, LastAddress.AddressFamily == AddressFamily.InterNetworkV6 ? 128 : 32) : addressRange;
|
||||
var targetHWid = useLastHwid ? LastHwid : hwid;
|
||||
if (target != null && target != PlayerName || Guid.TryParse(target, out var parsed) && parsed != PlayerId)
|
||||
{
|
||||
var located = await _playerLocator.LookupIdByNameOrIdAsync(target);
|
||||
if (located == null)
|
||||
{
|
||||
_chat.DispatchServerMessage(Player, Loc.GetString("cmd-ban-player"));
|
||||
return;
|
||||
}
|
||||
targetUid = located.UserId;
|
||||
var targetAddress = located.LastAddress;
|
||||
if (useLastIp && targetAddress != null)
|
||||
{
|
||||
if (targetAddress.IsIPv4MappedToIPv6)
|
||||
targetAddress = targetAddress.MapToIPv4();
|
||||
|
||||
// Ban /128 for IPv6, /32 for IPv4.
|
||||
var hid = targetAddress.AddressFamily == AddressFamily.InterNetworkV6 ? 128 : 32;
|
||||
addressRange = (targetAddress, hid);
|
||||
}
|
||||
targetHWid = useLastHwid ? located.LastHWId : hwid;
|
||||
}
|
||||
|
||||
if (roles?.Count > 0)
|
||||
{
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
foreach (var role in roles)
|
||||
{
|
||||
_banManager.CreateRoleBan(targetUid, target, Player.UserId, addressRange, targetHWid, role, minutes, severity, reason, now);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
_banManager.CreateServerBan(targetUid, target, Player.UserId, addressRange, targetHWid, minutes, severity, reason);
|
||||
Close();
|
||||
}
|
||||
|
||||
public async void ChangePlayer(string playerNameOrId)
|
||||
{
|
||||
var located = await _playerLocator.LookupIdByNameOrIdAsync(playerNameOrId);
|
||||
ChangePlayer(located?.UserId, located?.Username ?? string.Empty, located?.LastAddress, located?.LastHWId);
|
||||
}
|
||||
|
||||
public void ChangePlayer(NetUserId? playerId, string playerName, IPAddress? lastAddress, ImmutableArray<byte>? lastHwid)
|
||||
{
|
||||
PlayerId = playerId;
|
||||
PlayerName = playerName;
|
||||
LastAddress = lastAddress;
|
||||
LastHwid = lastHwid;
|
||||
StateDirty();
|
||||
}
|
||||
|
||||
public override async void Opened()
|
||||
{
|
||||
base.Opened();
|
||||
_admins.OnPermsChanged += OnPermsChanged;
|
||||
}
|
||||
|
||||
public override void Closed()
|
||||
{
|
||||
base.Closed();
|
||||
_admins.OnPermsChanged -= OnPermsChanged;
|
||||
}
|
||||
|
||||
private void OnPermsChanged(AdminPermsChangedEventArgs args)
|
||||
{
|
||||
if (args.Player != Player)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
StateDirty();
|
||||
}
|
||||
}
|
||||
@@ -2,144 +2,141 @@ using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using Content.Server.Administration.Managers;
|
||||
using Content.Server.Administration.Notes;
|
||||
using Content.Server.Database;
|
||||
using Content.Server.GameTicking;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Players.PlayTimeTracking;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Console;
|
||||
|
||||
|
||||
namespace Content.Server.Administration.Commands
|
||||
namespace Content.Server.Administration.Commands;
|
||||
|
||||
[AdminCommand(AdminFlags.Ban)]
|
||||
public sealed class BanCommand : LocalizedCommands
|
||||
{
|
||||
[AdminCommand(AdminFlags.Ban)]
|
||||
public sealed class BanCommand : LocalizedCommands
|
||||
|
||||
[Dependency] private readonly IPlayerLocator _locator = default!;
|
||||
[Dependency] private readonly IBanManager _bans = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
|
||||
public override string Command => "ban";
|
||||
|
||||
public override async void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
|
||||
public override string Command => "ban";
|
||||
|
||||
public override async void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
string target;
|
||||
string reason;
|
||||
uint minutes;
|
||||
if (!Enum.TryParse(_cfg.GetCVar(CCVars.ServerBanDefaultSeverity), out NoteSeverity severity))
|
||||
{
|
||||
var player = shell.Player as IPlayerSession;
|
||||
var plyMgr = IoCManager.Resolve<IPlayerManager>();
|
||||
var locator = IoCManager.Resolve<IPlayerLocator>();
|
||||
var dbMan = IoCManager.Resolve<IServerDbManager>();
|
||||
|
||||
string target;
|
||||
string reason;
|
||||
uint minutes;
|
||||
|
||||
switch (args.Length)
|
||||
{
|
||||
case 2:
|
||||
target = args[0];
|
||||
reason = args[1];
|
||||
minutes = 0;
|
||||
break;
|
||||
case 3:
|
||||
target = args[0];
|
||||
reason = args[1];
|
||||
|
||||
if (!uint.TryParse(args[2], out minutes))
|
||||
{
|
||||
shell.WriteLine($"{args[2]} is not a valid amount of minutes.\n{Help}");
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
shell.WriteLine($"Invalid amount of arguments.{Help}");
|
||||
return;
|
||||
}
|
||||
|
||||
var located = await locator.LookupIdByNameOrIdAsync(target);
|
||||
if (located == null)
|
||||
{
|
||||
shell.WriteError(LocalizationManager.GetString("cmd-ban-player"));
|
||||
return;
|
||||
}
|
||||
|
||||
var targetUid = located.UserId;
|
||||
var targetHWid = located.LastHWId;
|
||||
var targetAddr = located.LastAddress;
|
||||
|
||||
if (player != null && player.UserId == targetUid)
|
||||
{
|
||||
shell.WriteLine(LocalizationManager.GetString("cmd-ban-self"));
|
||||
return;
|
||||
}
|
||||
|
||||
DateTimeOffset? expires = null;
|
||||
if (minutes > 0)
|
||||
{
|
||||
expires = DateTimeOffset.Now + TimeSpan.FromMinutes(minutes);
|
||||
}
|
||||
|
||||
(IPAddress, int)? addrRange = null;
|
||||
if (targetAddr != null)
|
||||
{
|
||||
if (targetAddr.IsIPv4MappedToIPv6)
|
||||
targetAddr = targetAddr.MapToIPv4();
|
||||
|
||||
// Ban /64 for IPv4, /32 for IPv4.
|
||||
var cidr = targetAddr.AddressFamily == AddressFamily.InterNetworkV6 ? 64 : 32;
|
||||
addrRange = (targetAddr, cidr);
|
||||
}
|
||||
|
||||
var banDef = new ServerBanDef(
|
||||
null,
|
||||
targetUid,
|
||||
addrRange,
|
||||
targetHWid,
|
||||
DateTimeOffset.Now,
|
||||
expires,
|
||||
reason,
|
||||
player?.UserId,
|
||||
null);
|
||||
|
||||
await dbMan.AddServerBanAsync(banDef);
|
||||
|
||||
var response = new StringBuilder($"Banned {target} with reason \"{reason}\"");
|
||||
|
||||
response.Append(expires == null ? " permanently." : $" until {expires}");
|
||||
|
||||
shell.WriteLine(response.ToString());
|
||||
|
||||
if (plyMgr.TryGetSessionById(targetUid, out var targetPlayer))
|
||||
{
|
||||
var message = banDef.FormatBanMessage(_cfg, LocalizationManager);
|
||||
targetPlayer.ConnectedClient.Disconnect(message);
|
||||
}
|
||||
Logger.WarningS("admin.server_ban", "Server ban severity could not be parsed from config! Defaulting to high.");
|
||||
severity = NoteSeverity.High;
|
||||
}
|
||||
|
||||
public override CompletionResult GetCompletion(IConsoleShell shell, string[] args)
|
||||
switch (args.Length)
|
||||
{
|
||||
if (args.Length == 1)
|
||||
{
|
||||
var playerMgr = IoCManager.Resolve<IPlayerManager>();
|
||||
var options = playerMgr.ServerSessions.Select(c => c.Name).OrderBy(c => c).ToArray();
|
||||
return CompletionResult.FromHintOptions(options, LocalizationManager.GetString("cmd-ban-hint"));
|
||||
}
|
||||
case 2:
|
||||
target = args[0];
|
||||
reason = args[1];
|
||||
minutes = 0;
|
||||
break;
|
||||
case 3:
|
||||
target = args[0];
|
||||
reason = args[1];
|
||||
|
||||
if (args.Length == 2)
|
||||
return CompletionResult.FromHint(LocalizationManager.GetString("cmd-ban-hint-reason"));
|
||||
|
||||
if (args.Length == 3)
|
||||
{
|
||||
var durations = new CompletionOption[]
|
||||
if (!uint.TryParse(args[2], out minutes))
|
||||
{
|
||||
new("0", LocalizationManager.GetString("cmd-ban-hint-duration-1")),
|
||||
new("1440", LocalizationManager.GetString("cmd-ban-hint-duration-2")),
|
||||
new("4320", LocalizationManager.GetString("cmd-ban-hint-duration-3")),
|
||||
new("10080", LocalizationManager.GetString("cmd-ban-hint-duration-4")),
|
||||
new("20160", LocalizationManager.GetString("cmd-ban-hint-duration-5")),
|
||||
new("43800", LocalizationManager.GetString("cmd-ban-hint-duration-6")),
|
||||
};
|
||||
shell.WriteLine(Loc.GetString("cmd-ban-invalid-minutes", ("minutes", args[2])));
|
||||
shell.WriteLine(Help);
|
||||
return;
|
||||
}
|
||||
|
||||
return CompletionResult.FromHintOptions(durations, LocalizationManager.GetString("cmd-ban-hint-duration"));
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
target = args[0];
|
||||
reason = args[1];
|
||||
|
||||
return CompletionResult.Empty;
|
||||
if (!uint.TryParse(args[2], out minutes))
|
||||
{
|
||||
shell.WriteLine(Loc.GetString("cmd-ban-invalid-minutes", ("minutes", args[2])));
|
||||
shell.WriteLine(Help);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Enum.TryParse(args[3], ignoreCase: true, out severity))
|
||||
{
|
||||
shell.WriteLine(Loc.GetString("cmd-ban-invalid-severity", ("severity", args[3])));
|
||||
shell.WriteLine(Help);
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
shell.WriteLine(Loc.GetString("cmd-ban-invalid-arguments"));
|
||||
shell.WriteLine(Help);
|
||||
return;
|
||||
}
|
||||
|
||||
var located = await _locator.LookupIdByNameOrIdAsync(target);
|
||||
var player = shell.Player as IPlayerSession;
|
||||
|
||||
if (located == null)
|
||||
{
|
||||
shell.WriteError(Loc.GetString("cmd-ban-player"));
|
||||
return;
|
||||
}
|
||||
|
||||
var targetUid = located.UserId;
|
||||
var targetHWid = located.LastHWId;
|
||||
|
||||
_bans.CreateServerBan(targetUid, target, player?.UserId, null, targetHWid, minutes, severity, reason);
|
||||
}
|
||||
|
||||
public override CompletionResult GetCompletion(IConsoleShell shell, string[] args)
|
||||
{
|
||||
if (args.Length == 1)
|
||||
{
|
||||
var options = _playerManager.ServerSessions.Select(c => c.Name).OrderBy(c => c).ToArray();
|
||||
return CompletionResult.FromHintOptions(options, LocalizationManager.GetString("cmd-ban-hint"));
|
||||
}
|
||||
|
||||
if (args.Length == 2)
|
||||
return CompletionResult.FromHint(LocalizationManager.GetString("cmd-ban-hint-reason"));
|
||||
|
||||
if (args.Length == 3)
|
||||
{
|
||||
var durations = new CompletionOption[]
|
||||
{
|
||||
new("0", LocalizationManager.GetString("cmd-ban-hint-duration-1")),
|
||||
new("1440", LocalizationManager.GetString("cmd-ban-hint-duration-2")),
|
||||
new("4320", LocalizationManager.GetString("cmd-ban-hint-duration-3")),
|
||||
new("10080", LocalizationManager.GetString("cmd-ban-hint-duration-4")),
|
||||
new("20160", LocalizationManager.GetString("cmd-ban-hint-duration-5")),
|
||||
new("43800", LocalizationManager.GetString("cmd-ban-hint-duration-6")),
|
||||
};
|
||||
|
||||
return CompletionResult.FromHintOptions(durations, LocalizationManager.GetString("cmd-ban-hint-duration"));
|
||||
}
|
||||
|
||||
if (args.Length == 4)
|
||||
{
|
||||
var severities = new CompletionOption[]
|
||||
{
|
||||
new("none", Loc.GetString("admin-note-editor-severity-none")),
|
||||
new("minor", Loc.GetString("admin-note-editor-severity-low")),
|
||||
new("medium", Loc.GetString("admin-note-editor-severity-medium")),
|
||||
new("high", Loc.GetString("admin-note-editor-severity-high")),
|
||||
};
|
||||
|
||||
return CompletionResult.FromHintOptions(severities, Loc.GetString("cmd-ban-hint-severity"));
|
||||
}
|
||||
|
||||
return CompletionResult.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
52
Content.Server/Administration/Commands/BanPanelCommand.cs
Normal file
52
Content.Server/Administration/Commands/BanPanelCommand.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using Content.Shared.Administration;
|
||||
using Robust.Shared.Console;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.EUI;
|
||||
using Robust.Server.Player;
|
||||
|
||||
namespace Content.Server.Administration.Commands;
|
||||
|
||||
[AdminCommand(AdminFlags.Ban)]
|
||||
public sealed class BanPanelCommand : LocalizedCommands
|
||||
{
|
||||
|
||||
[Dependency] private readonly IPlayerLocator _locator = default!;
|
||||
[Dependency] private readonly EuiManager _euis = default!;
|
||||
|
||||
public override string Command => "banpanel";
|
||||
|
||||
public override async void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (shell.Player is not IPlayerSession player)
|
||||
{
|
||||
shell.WriteError(Loc.GetString("cmd-banpanel-server"));
|
||||
return;
|
||||
}
|
||||
|
||||
switch (args.Length)
|
||||
{
|
||||
case 0:
|
||||
_euis.OpenEui(new BanPanelEui(), player);
|
||||
break;
|
||||
case 1:
|
||||
var located = await _locator.LookupIdByNameOrIdAsync(args[0]);
|
||||
if (located is null)
|
||||
{
|
||||
shell.WriteError(Loc.GetString("cmd-banpanel-player-err"));
|
||||
return;
|
||||
}
|
||||
var ui = new BanPanelEui();
|
||||
_euis.OpenEui(ui, player);
|
||||
ui.ChangePlayer(located.UserId, located.Username, located.LastAddress, located.LastHWId);
|
||||
break;
|
||||
default:
|
||||
shell.WriteLine(Loc.GetString("cmd-ban-invalid-arguments"));
|
||||
shell.WriteLine(Help);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
using Content.Server.Administration.Managers;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Roles;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
@@ -9,9 +12,10 @@ namespace Content.Server.Administration.Commands;
|
||||
[AdminCommand(AdminFlags.Ban)]
|
||||
public sealed class DepartmentBanCommand : IConsoleCommand
|
||||
{
|
||||
[Dependency] private readonly IPlayerLocator _locater = default!;
|
||||
[Dependency] private readonly IPrototypeManager _protoManager = default!;
|
||||
[Dependency] private readonly RoleBanManager _bans = default!;
|
||||
[Dependency] private readonly IPlayerLocator _locator = default!;
|
||||
[Dependency] private readonly IBanManager _banManager = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
|
||||
public string Command => "departmentban";
|
||||
public string Description => Loc.GetString("cmd-departmentban-desc");
|
||||
@@ -23,6 +27,11 @@ public sealed class DepartmentBanCommand : IConsoleCommand
|
||||
string department;
|
||||
string reason;
|
||||
uint minutes;
|
||||
if (!Enum.TryParse(_cfg.GetCVar(CCVars.DepartmentBanDefaultSeverity), out NoteSeverity severity))
|
||||
{
|
||||
Logger.WarningS("admin.department_ban", "Department ban severity could not be parsed from config! Defaulting to medium.");
|
||||
severity = NoteSeverity.Medium;
|
||||
}
|
||||
|
||||
switch (args.Length)
|
||||
{
|
||||
@@ -43,6 +52,24 @@ public sealed class DepartmentBanCommand : IConsoleCommand
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
case 5:
|
||||
target = args[0];
|
||||
department = args[1];
|
||||
reason = args[2];
|
||||
|
||||
if (!uint.TryParse(args[3], out minutes))
|
||||
{
|
||||
shell.WriteError(Loc.GetString("cmd-roleban-minutes-parse", ("time", args[3]), ("help", Help)));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Enum.TryParse(args[4], ignoreCase: true, out severity))
|
||||
{
|
||||
shell.WriteLine(Loc.GetString("cmd-roleban-severity-parse", ("severity", args[4]), ("help", Help)));
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
shell.WriteError(Loc.GetString("cmd-roleban-arg-count"));
|
||||
@@ -55,20 +82,25 @@ public sealed class DepartmentBanCommand : IConsoleCommand
|
||||
return;
|
||||
}
|
||||
|
||||
var located = await _locater.LookupIdByNameOrIdAsync(target);
|
||||
|
||||
var located = await _locator.LookupIdByNameOrIdAsync(target);
|
||||
if (located == null)
|
||||
{
|
||||
shell.WriteError(Loc.GetString("cmd-roleban-name-parse"));
|
||||
return;
|
||||
}
|
||||
|
||||
var targetUid = located.UserId;
|
||||
var targetHWid = located.LastHWId;
|
||||
|
||||
// If you are trying to remove the following variable, please don't. It's there because the note system groups role bans by time, reason and banning admin.
|
||||
// Without it the note list will get needlessly cluttered.
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
foreach (var job in departmentProto.Roles)
|
||||
{
|
||||
_bans.CreateJobBan(shell, located, job, reason, minutes);
|
||||
_banManager.CreateRoleBan(targetUid, located.Username, shell.Player?.UserId, null, targetHWid, job, minutes, severity, reason, now);
|
||||
}
|
||||
|
||||
_bans.SendRoleBans(located);
|
||||
_banManager.SendRoleBans(located.UserId);
|
||||
}
|
||||
|
||||
public CompletionResult GetCompletion(IConsoleShell shell, string[] args)
|
||||
@@ -83,6 +115,14 @@ public sealed class DepartmentBanCommand : IConsoleCommand
|
||||
new("43800", Loc.GetString("cmd-roleban-hint-duration-6")),
|
||||
};
|
||||
|
||||
var severities = new CompletionOption[]
|
||||
{
|
||||
new("none", Loc.GetString("admin-note-editor-severity-none")),
|
||||
new("minor", Loc.GetString("admin-note-editor-severity-low")),
|
||||
new("medium", Loc.GetString("admin-note-editor-severity-medium")),
|
||||
new("high", Loc.GetString("admin-note-editor-severity-high")),
|
||||
};
|
||||
|
||||
return args.Length switch
|
||||
{
|
||||
1 => CompletionResult.FromHintOptions(CompletionHelper.SessionNames(),
|
||||
@@ -91,6 +131,7 @@ public sealed class DepartmentBanCommand : IConsoleCommand
|
||||
Loc.GetString("cmd-roleban-hint-2")),
|
||||
3 => CompletionResult.FromHint(Loc.GetString("cmd-roleban-hint-3")),
|
||||
4 => CompletionResult.FromHintOptions(durOpts, Loc.GetString("cmd-roleban-hint-4")),
|
||||
5 => CompletionResult.FromHintOptions(severities, Loc.GetString("cmd-roleban-hint-5")),
|
||||
_ => CompletionResult.Empty
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
using Content.Server.Administration.Notes;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.CCVar;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Console;
|
||||
|
||||
namespace Content.Server.Administration.Commands;
|
||||
|
||||
[AnyCommand]
|
||||
public sealed class OpenUserVisibleNotesCommand : IConsoleCommand
|
||||
{
|
||||
[Dependency] private readonly IConfigurationManager _configuration = default!;
|
||||
[Dependency] private readonly IAdminNotesManager _notes = default!;
|
||||
|
||||
public const string CommandName = "adminremarks";
|
||||
|
||||
public string Command => CommandName;
|
||||
public string Description => Loc.GetString("admin-remarks-command-description");
|
||||
public string Help => $"Usage: {Command}";
|
||||
|
||||
public async void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (!_configuration.GetCVar(CCVars.SeeOwnNotes))
|
||||
{
|
||||
shell.WriteError(Loc.GetString("admin-remarks-command-error"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (shell.Player is not IPlayerSession player)
|
||||
{
|
||||
shell.WriteError("This does not work from the server console.");
|
||||
return;
|
||||
}
|
||||
|
||||
await _notes.OpenUserNotesEui(player);
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,20 @@
|
||||
using System.Linq;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Content.Server.Administration.Managers;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Roles;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Serilog;
|
||||
|
||||
namespace Content.Server.Administration.Commands;
|
||||
|
||||
[AdminCommand(AdminFlags.Ban)]
|
||||
public sealed class RoleBanCommand : IConsoleCommand
|
||||
{
|
||||
[Dependency] private readonly IPlayerLocator _locator = default!;
|
||||
[Dependency] private readonly RoleBanManager _bans = default!;
|
||||
[Dependency] private readonly IBanManager _bans = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
|
||||
public string Command => "roleban";
|
||||
public string Description => Loc.GetString("cmd-roleban-desc");
|
||||
@@ -25,6 +26,11 @@ public sealed class RoleBanCommand : IConsoleCommand
|
||||
string job;
|
||||
string reason;
|
||||
uint minutes;
|
||||
if (!Enum.TryParse(_cfg.GetCVar(CCVars.RoleBanDefaultSeverity), out NoteSeverity severity))
|
||||
{
|
||||
Logger.WarningS("admin.role_ban", "Role ban severity could not be parsed from config! Defaulting to medium.");
|
||||
severity = NoteSeverity.Medium;
|
||||
}
|
||||
|
||||
switch (args.Length)
|
||||
{
|
||||
@@ -45,6 +51,24 @@ public sealed class RoleBanCommand : IConsoleCommand
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
case 5:
|
||||
target = args[0];
|
||||
job = args[1];
|
||||
reason = args[2];
|
||||
|
||||
if (!uint.TryParse(args[3], out minutes))
|
||||
{
|
||||
shell.WriteError(Loc.GetString("cmd-roleban-minutes-parse", ("time", args[3]), ("help", Help)));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Enum.TryParse(args[4], ignoreCase: true, out severity))
|
||||
{
|
||||
shell.WriteLine(Loc.GetString("cmd-roleban-severity-parse", ("severity", args[4]), ("help", Help)));
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
shell.WriteError(Loc.GetString("cmd-roleban-arg-count"));
|
||||
@@ -53,15 +77,17 @@ public sealed class RoleBanCommand : IConsoleCommand
|
||||
}
|
||||
|
||||
var located = await _locator.LookupIdByNameOrIdAsync(target);
|
||||
|
||||
if (located == null)
|
||||
{
|
||||
shell.WriteError(Loc.GetString("cmd-roleban-name-parse"));
|
||||
return;
|
||||
}
|
||||
|
||||
_bans.CreateJobBan(shell, located, job, reason, minutes);
|
||||
_bans.SendRoleBans(located);
|
||||
var targetUid = located.UserId;
|
||||
var targetHWid = located.LastHWId;
|
||||
|
||||
_bans.CreateRoleBan(targetUid, located.Username, shell.Player?.UserId, null, targetHWid, job, minutes, severity, reason, DateTimeOffset.UtcNow);
|
||||
_bans.SendRoleBans(located.UserId);
|
||||
}
|
||||
|
||||
public CompletionResult GetCompletion(IConsoleShell shell, string[] args)
|
||||
@@ -76,6 +102,14 @@ public sealed class RoleBanCommand : IConsoleCommand
|
||||
new("43800", Loc.GetString("cmd-roleban-hint-duration-6")),
|
||||
};
|
||||
|
||||
var severities = new CompletionOption[]
|
||||
{
|
||||
new("none", Loc.GetString("admin-note-editor-severity-none")),
|
||||
new("minor", Loc.GetString("admin-note-editor-severity-low")),
|
||||
new("medium", Loc.GetString("admin-note-editor-severity-medium")),
|
||||
new("high", Loc.GetString("admin-note-editor-severity-high")),
|
||||
};
|
||||
|
||||
return args.Length switch
|
||||
{
|
||||
1 => CompletionResult.FromHintOptions(CompletionHelper.SessionNames(),
|
||||
@@ -84,6 +118,7 @@ public sealed class RoleBanCommand : IConsoleCommand
|
||||
Loc.GetString("cmd-roleban-hint-2")),
|
||||
3 => CompletionResult.FromHint(Loc.GetString("cmd-roleban-hint-3")),
|
||||
4 => CompletionResult.FromHintOptions(durOpts, Loc.GetString("cmd-roleban-hint-4")),
|
||||
5 => CompletionResult.FromHintOptions(severities, Loc.GetString("cmd-roleban-hint-5")),
|
||||
_ => CompletionResult.Empty
|
||||
};
|
||||
}
|
||||
|
||||
259
Content.Server/Administration/Managers/BanManager.cs
Normal file
259
Content.Server/Administration/Managers/BanManager.cs
Normal file
@@ -0,0 +1,259 @@
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.Chat.Managers;
|
||||
using Content.Server.Database;
|
||||
using Content.Server.GameTicking;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Players;
|
||||
using Content.Shared.Players.PlayTimeTracking;
|
||||
using Content.Shared.Roles;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.Administration.Managers;
|
||||
|
||||
public sealed class BanManager : IBanManager, IPostInjectInit
|
||||
{
|
||||
[Dependency] private readonly IServerDbManager _db = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IEntitySystemManager _systems = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
[Dependency] private readonly ILocalizationManager _localizationManager = default!;
|
||||
[Dependency] private readonly IChatManager _chat = default!;
|
||||
[Dependency] private readonly INetManager _netManager = default!;
|
||||
[Dependency] private readonly ILogManager _logManager = default!;
|
||||
|
||||
private ISawmill _sawmill = default!;
|
||||
|
||||
public const string SawmillId = "admin.bans";
|
||||
public const string JobPrefix = "Job:";
|
||||
|
||||
private readonly Dictionary<NetUserId, HashSet<ServerRoleBanDef>> _cachedRoleBans = new();
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
_playerManager.PlayerStatusChanged += OnPlayerStatusChanged;
|
||||
}
|
||||
|
||||
private async void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs e)
|
||||
{
|
||||
if (e.NewStatus != SessionStatus.Connected
|
||||
|| _cachedRoleBans.ContainsKey(e.Session.UserId))
|
||||
return;
|
||||
|
||||
var netChannel = e.Session.ConnectedClient;
|
||||
await CacheDbRoleBans(e.Session.UserId, netChannel.RemoteEndPoint.Address, netChannel.UserData.HWId.Length == 0 ? null : netChannel.UserData.HWId);
|
||||
}
|
||||
|
||||
private async Task<bool> AddRoleBan(ServerRoleBanDef banDef)
|
||||
{
|
||||
if (banDef.UserId != null)
|
||||
{
|
||||
if (!_cachedRoleBans.TryGetValue(banDef.UserId.Value, out var roleBans))
|
||||
{
|
||||
roleBans = new HashSet<ServerRoleBanDef>();
|
||||
_cachedRoleBans.Add(banDef.UserId.Value, roleBans);
|
||||
}
|
||||
if (!roleBans.Contains(banDef))
|
||||
roleBans.Add(banDef);
|
||||
}
|
||||
|
||||
await _db.AddServerRoleBanAsync(banDef);
|
||||
return true;
|
||||
}
|
||||
|
||||
public HashSet<string>? GetRoleBans(NetUserId playerUserId)
|
||||
{
|
||||
return _cachedRoleBans.TryGetValue(playerUserId, out var roleBans) ? roleBans.Select(banDef => banDef.Role).ToHashSet() : null;
|
||||
}
|
||||
|
||||
private async Task CacheDbRoleBans(NetUserId userId, IPAddress? address = null, ImmutableArray<byte>? hwId = null)
|
||||
{
|
||||
var roleBans = await _db.GetServerRoleBansAsync(address, userId, hwId, false);
|
||||
|
||||
var userRoleBans = new HashSet<ServerRoleBanDef>();
|
||||
foreach (var ban in roleBans)
|
||||
{
|
||||
userRoleBans.Add(ban);
|
||||
}
|
||||
|
||||
_cachedRoleBans[userId] = userRoleBans;
|
||||
}
|
||||
|
||||
public void Restart()
|
||||
{
|
||||
// Clear out players that have disconnected.
|
||||
var toRemove = new List<NetUserId>();
|
||||
foreach (var player in _cachedRoleBans.Keys)
|
||||
{
|
||||
if (!_playerManager.TryGetSessionById(player, out _))
|
||||
toRemove.Add(player);
|
||||
}
|
||||
|
||||
foreach (var player in toRemove)
|
||||
{
|
||||
_cachedRoleBans.Remove(player);
|
||||
}
|
||||
|
||||
// Check for expired bans
|
||||
foreach (var roleBans in _cachedRoleBans.Values)
|
||||
{
|
||||
roleBans.RemoveWhere(ban => DateTimeOffset.Now > ban.ExpirationTime);
|
||||
}
|
||||
}
|
||||
|
||||
#region Server Bans
|
||||
public async void CreateServerBan(NetUserId? target, string? targetUsername, NetUserId? banningAdmin, (IPAddress, int)? addressRange, ImmutableArray<byte>? hwid, uint? minutes, NoteSeverity severity, string reason)
|
||||
{
|
||||
DateTimeOffset? expires = null;
|
||||
if (minutes > 0)
|
||||
{
|
||||
expires = DateTimeOffset.Now + TimeSpan.FromMinutes(minutes.Value);
|
||||
}
|
||||
|
||||
_systems.TryGetEntitySystem<GameTicker>(out var ticker);
|
||||
int? roundId = ticker == null || ticker.RoundId == 0 ? null : ticker.RoundId;
|
||||
var playtime = target == null ? TimeSpan.Zero : (await _db.GetPlayTimes(target.Value)).Find(p => p.Tracker == PlayTimeTrackingShared.TrackerOverall)?.TimeSpent ?? TimeSpan.Zero;
|
||||
|
||||
var banDef = new ServerBanDef(
|
||||
null,
|
||||
target,
|
||||
addressRange,
|
||||
hwid,
|
||||
DateTimeOffset.Now,
|
||||
expires,
|
||||
roundId,
|
||||
playtime,
|
||||
reason,
|
||||
severity,
|
||||
banningAdmin,
|
||||
null);
|
||||
|
||||
await _db.AddServerBanAsync(banDef);
|
||||
var adminName = banningAdmin == null
|
||||
? Loc.GetString("system-user")
|
||||
: (await _db.GetPlayerRecordByUserId(banningAdmin.Value))?.LastSeenUserName ?? Loc.GetString("system-user");
|
||||
var targetName = target is null ? "null" : $"{targetUsername} ({target})";
|
||||
var addressRangeString = addressRange != null
|
||||
? $"{addressRange.Value.Item1}/{addressRange.Value.Item2}"
|
||||
: "null";
|
||||
var hwidString = hwid != null
|
||||
? string.Concat(hwid.Value.Select(x => x.ToString("x2")))
|
||||
: "null";
|
||||
var expiresString = expires == null ? Loc.GetString("server-ban-string-never") : $"{expires}";
|
||||
|
||||
var logMessage = Loc.GetString("server-ban-string", ("admin", adminName), ("severity", severity),
|
||||
("expires", expiresString), ("name", targetName), ("ip", addressRangeString),
|
||||
("hwid", hwidString), ("reason", reason));
|
||||
_sawmill.Info(logMessage);
|
||||
_chat.SendAdminAlert(logMessage);
|
||||
|
||||
// If we're not banning a player we don't care about disconnecting people
|
||||
if (target == null)
|
||||
return;
|
||||
|
||||
// Is the player connected?
|
||||
if (!_playerManager.TryGetSessionById(target.Value, out var targetPlayer))
|
||||
return;
|
||||
// If they are, kick them
|
||||
var message = banDef.FormatBanMessage(_cfg, _localizationManager);
|
||||
targetPlayer.ConnectedClient.Disconnect(message);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Job Bans
|
||||
// If you are trying to remove timeOfBan, please don't. It's there because the note system groups role bans by time, reason and banning admin.
|
||||
// Removing it will clutter the note list. Please also make sure that department bans are applied to roles with the same DateTimeOffset.
|
||||
public async void CreateRoleBan(NetUserId? target, string? targetUsername, NetUserId? banningAdmin, (IPAddress, int)? addressRange, ImmutableArray<byte>? hwid, string role, uint? minutes, NoteSeverity severity, string reason, DateTimeOffset timeOfBan)
|
||||
{
|
||||
if (!_prototypeManager.TryIndex(role, out JobPrototype? _))
|
||||
{
|
||||
throw new ArgumentException($"Invalid role '{role}'", nameof(role));
|
||||
}
|
||||
|
||||
role = string.Concat(JobPrefix, role);
|
||||
DateTimeOffset? expires = null;
|
||||
if (minutes > 0)
|
||||
{
|
||||
expires = DateTimeOffset.Now + TimeSpan.FromMinutes(minutes.Value);
|
||||
}
|
||||
|
||||
_systems.TryGetEntitySystem(out GameTicker? ticker);
|
||||
int? roundId = ticker == null || ticker.RoundId == 0 ? null : ticker.RoundId;
|
||||
var playtime = target == null ? TimeSpan.Zero : (await _db.GetPlayTimes(target.Value)).Find(p => p.Tracker == PlayTimeTrackingShared.TrackerOverall)?.TimeSpent ?? TimeSpan.Zero;
|
||||
|
||||
var banDef = new ServerRoleBanDef(
|
||||
null,
|
||||
target,
|
||||
addressRange,
|
||||
hwid,
|
||||
timeOfBan,
|
||||
expires,
|
||||
roundId,
|
||||
playtime,
|
||||
reason,
|
||||
severity,
|
||||
banningAdmin,
|
||||
null,
|
||||
role);
|
||||
|
||||
if (!await AddRoleBan(banDef))
|
||||
{
|
||||
_chat.SendAdminAlert(Loc.GetString("cmd-roleban-existing", ("target", targetUsername ?? "null"), ("role", role)));
|
||||
return;
|
||||
}
|
||||
|
||||
var length = expires == null ? Loc.GetString("cmd-roleban-inf") : Loc.GetString("cmd-roleban-until", ("expires", expires));
|
||||
_chat.SendAdminAlert(Loc.GetString("cmd-roleban-success", ("target", targetUsername ?? "null"), ("role", role), ("reason", reason), ("length", length)));
|
||||
}
|
||||
|
||||
public HashSet<string>? GetJobBans(NetUserId playerUserId)
|
||||
{
|
||||
if (!_cachedRoleBans.TryGetValue(playerUserId, out var roleBans))
|
||||
return null;
|
||||
return roleBans
|
||||
.Where(ban => ban.Role.StartsWith(JobPrefix, StringComparison.Ordinal))
|
||||
.Select(ban => ban.Role[JobPrefix.Length..])
|
||||
.ToHashSet();
|
||||
}
|
||||
#endregion
|
||||
|
||||
public void SendRoleBans(NetUserId userId)
|
||||
{
|
||||
if (!_playerManager.TryGetSessionById(userId, out var player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SendRoleBans(player);
|
||||
}
|
||||
|
||||
public void SendRoleBans(IPlayerSession pSession)
|
||||
{
|
||||
if (!_cachedRoleBans.TryGetValue(pSession.UserId, out var roleBans))
|
||||
{
|
||||
_sawmill.Error($"Tried to send rolebans for {pSession.Name} but none cached?");
|
||||
return;
|
||||
}
|
||||
|
||||
var bans = new MsgRoleBans()
|
||||
{
|
||||
Bans = roleBans.Select(o => o.Role).ToList()
|
||||
};
|
||||
|
||||
_sawmill.Debug($"Sent rolebans to {pSession.Name}");
|
||||
_netManager.ServerSendMessage(bans, pSession.ConnectedClient);
|
||||
}
|
||||
|
||||
public void PostInject()
|
||||
{
|
||||
_sawmill = _logManager.GetSawmill(SawmillId);
|
||||
}
|
||||
}
|
||||
50
Content.Server/Administration/Managers/IBanManager.cs
Normal file
50
Content.Server/Administration/Managers/IBanManager.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using System.Collections.Immutable;
|
||||
using Content.Shared.Database;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Network;
|
||||
using System.Net;
|
||||
|
||||
namespace Content.Server.Administration.Managers;
|
||||
|
||||
public interface IBanManager
|
||||
{
|
||||
public void Initialize();
|
||||
public void Restart();
|
||||
|
||||
/// <summary>
|
||||
/// Bans the specified target, address range and / or HWID. One of them must be non-null
|
||||
/// </summary>
|
||||
/// <param name="target">Target user, username or GUID, null for none</param>
|
||||
/// <param name="banningAdmin">The person who banned our target</param>
|
||||
/// <param name="addressRange">Address range, null for none</param>
|
||||
/// <param name="hwid">H</param>
|
||||
/// <param name="minutes">Number of minutes to ban for. 0 and null mean permanent</param>
|
||||
/// <param name="severity">Severity of the resulting ban note</param>
|
||||
/// <param name="reason">Reason for the ban</param>
|
||||
public void CreateServerBan(NetUserId? target, string? targetUsername, NetUserId? banningAdmin, (IPAddress, int)? addressRange, ImmutableArray<byte>? hwid, uint? minutes, NoteSeverity severity, string reason);
|
||||
public HashSet<string>? GetRoleBans(NetUserId playerUserId);
|
||||
public HashSet<string>? GetJobBans(NetUserId playerUserId);
|
||||
/// <summary>
|
||||
/// Creates a job ban for the specified target, username or GUID
|
||||
/// </summary>
|
||||
/// <param name="shell">Shell reference so we can write messages</param>
|
||||
/// <param name="target">Target user, username or GUID, null for none</param>
|
||||
/// <param name="job">Job to be banned from</param>
|
||||
/// <param name="severity">Severity of the resulting ban note</param>
|
||||
/// <param name="reason">Reason for the ban</param>
|
||||
/// <param name="minutes">Number of minutes to ban for. 0 and null mean permanent</param>
|
||||
/// <param name="timeOfBan">Time when the ban was applied, used for grouping role bans</param>
|
||||
public void CreateRoleBan(NetUserId? target, string? targetUsername, NetUserId? banningAdmin, (IPAddress, int)? addressRange, ImmutableArray<byte>? hwid, string role, uint? minutes, NoteSeverity severity, string reason, DateTimeOffset timeOfBan);
|
||||
|
||||
/// <summary>
|
||||
/// Sends role bans to the target
|
||||
/// </summary>
|
||||
/// <param name="pSession">Player's user ID</param>
|
||||
public void SendRoleBans(NetUserId userId);
|
||||
|
||||
/// <summary>
|
||||
/// Sends role bans to the target
|
||||
/// </summary>
|
||||
/// <param name="pSession">Player's session</param>
|
||||
public void SendRoleBans(IPlayerSession pSession);
|
||||
}
|
||||
@@ -1,206 +0,0 @@
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.Database;
|
||||
using Content.Shared.Players;
|
||||
using Content.Shared.Roles;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.Administration.Managers;
|
||||
|
||||
public sealed class RoleBanManager
|
||||
{
|
||||
[Dependency] private readonly INetManager _netManager = default!;
|
||||
[Dependency] private readonly IServerDbManager _db = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
private const string JobPrefix = "Job:";
|
||||
|
||||
private ISawmill _sawmill = default!;
|
||||
|
||||
private readonly Dictionary<NetUserId, HashSet<ServerRoleBanDef>> _cachedRoleBans = new();
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
_sawmill = Logger.GetSawmill("rolebans");
|
||||
_netManager.RegisterNetMessage<MsgRoleBans>();
|
||||
_playerManager.PlayerStatusChanged += OnPlayerStatusChanged;
|
||||
}
|
||||
|
||||
private async void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs e)
|
||||
{
|
||||
if (e.NewStatus != SessionStatus.Connected
|
||||
|| _cachedRoleBans.ContainsKey(e.Session.UserId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var netChannel = e.Session.ConnectedClient;
|
||||
await CacheDbRoleBans(e.Session.UserId, netChannel.RemoteEndPoint.Address, netChannel.UserData.HWId.Length == 0 ? null : netChannel.UserData.HWId);
|
||||
SendRoleBans(e.Session);
|
||||
}
|
||||
|
||||
private async Task<bool> AddRoleBan(ServerRoleBanDef banDef)
|
||||
{
|
||||
if (banDef.UserId != null)
|
||||
{
|
||||
if (!_cachedRoleBans.TryGetValue(banDef.UserId.Value, out var roleBans))
|
||||
{
|
||||
roleBans = new HashSet<ServerRoleBanDef>();
|
||||
_cachedRoleBans.Add(banDef.UserId.Value, roleBans);
|
||||
}
|
||||
|
||||
roleBans.Add(banDef);
|
||||
}
|
||||
|
||||
await _db.AddServerRoleBanAsync(banDef);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void SendRoleBans(LocatedPlayerData located)
|
||||
{
|
||||
if (!_playerManager.TryGetSessionById(located.UserId, out var player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SendRoleBans(player);
|
||||
}
|
||||
|
||||
public void SendRoleBans(IPlayerSession pSession)
|
||||
{
|
||||
if (!_cachedRoleBans.TryGetValue(pSession.UserId, out var roleBans))
|
||||
{
|
||||
_sawmill.Error($"Tried to send rolebans for {pSession.Name} but none cached?");
|
||||
return;
|
||||
}
|
||||
|
||||
var bans = new MsgRoleBans()
|
||||
{
|
||||
Bans = roleBans.Select(o => o.Role).ToList()
|
||||
};
|
||||
|
||||
_sawmill.Debug($"Sent rolebans to {pSession.Name}");
|
||||
_netManager.ServerSendMessage(bans, pSession.ConnectedClient);
|
||||
}
|
||||
|
||||
public HashSet<string>? GetRoleBans(NetUserId playerUserId)
|
||||
{
|
||||
return _cachedRoleBans.TryGetValue(playerUserId, out var roleBans) ? roleBans.Select(banDef => banDef.Role).ToHashSet() : null;
|
||||
}
|
||||
|
||||
private async Task CacheDbRoleBans(NetUserId userId, IPAddress? address = null, ImmutableArray<byte>? hwId = null)
|
||||
{
|
||||
var roleBans = await _db.GetServerRoleBansAsync(address, userId, hwId, false);
|
||||
|
||||
var userRoleBans = new HashSet<ServerRoleBanDef>();
|
||||
foreach (var ban in roleBans)
|
||||
{
|
||||
userRoleBans.Add(ban);
|
||||
}
|
||||
|
||||
_cachedRoleBans[userId] = userRoleBans;
|
||||
}
|
||||
|
||||
public void Restart()
|
||||
{
|
||||
// Clear out players that have disconnected.
|
||||
var toRemove = new List<NetUserId>();
|
||||
foreach (var player in _cachedRoleBans.Keys)
|
||||
{
|
||||
if (!_playerManager.TryGetSessionById(player, out _))
|
||||
toRemove.Add(player);
|
||||
}
|
||||
|
||||
foreach (var player in toRemove)
|
||||
{
|
||||
_cachedRoleBans.Remove(player);
|
||||
}
|
||||
|
||||
// Check for expired bans
|
||||
foreach (var (_, roleBans) in _cachedRoleBans)
|
||||
{
|
||||
roleBans.RemoveWhere(ban => DateTimeOffset.Now > ban.ExpirationTime);
|
||||
}
|
||||
}
|
||||
|
||||
#region Job Bans
|
||||
public async void CreateJobBan(IConsoleShell shell, LocatedPlayerData located, string job, string reason, uint minutes)
|
||||
{
|
||||
if (!_prototypeManager.TryIndex(job, out JobPrototype? _))
|
||||
{
|
||||
shell.WriteError(Loc.GetString("cmd-roleban-job-parse", ("job", job)));
|
||||
return;
|
||||
}
|
||||
|
||||
job = string.Concat(JobPrefix, job);
|
||||
CreateRoleBan(shell, located, job, reason, minutes);
|
||||
}
|
||||
|
||||
public HashSet<string>? GetJobBans(NetUserId playerUserId)
|
||||
{
|
||||
if (!_cachedRoleBans.TryGetValue(playerUserId, out var roleBans))
|
||||
return null;
|
||||
return roleBans
|
||||
.Where(ban => ban.Role.StartsWith(JobPrefix, StringComparison.Ordinal))
|
||||
.Select(ban => ban.Role[JobPrefix.Length..])
|
||||
.ToHashSet();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Commands
|
||||
private async void CreateRoleBan(IConsoleShell shell, LocatedPlayerData located, string role, string reason, uint minutes)
|
||||
{
|
||||
var targetUid = located.UserId;
|
||||
var targetHWid = located.LastHWId;
|
||||
var targetAddress = located.LastAddress;
|
||||
|
||||
DateTimeOffset? expires = null;
|
||||
if (minutes > 0)
|
||||
{
|
||||
expires = DateTimeOffset.Now + TimeSpan.FromMinutes(minutes);
|
||||
}
|
||||
|
||||
(IPAddress, int)? addressRange = null;
|
||||
if (targetAddress != null)
|
||||
{
|
||||
if (targetAddress.IsIPv4MappedToIPv6)
|
||||
targetAddress = targetAddress.MapToIPv4();
|
||||
|
||||
// Ban /64 for IPv4, /32 for IPv4.
|
||||
var cidr = targetAddress.AddressFamily == AddressFamily.InterNetworkV6 ? 64 : 32;
|
||||
addressRange = (targetAddress, cidr);
|
||||
}
|
||||
|
||||
var player = shell.Player as IPlayerSession;
|
||||
var banDef = new ServerRoleBanDef(
|
||||
null,
|
||||
targetUid,
|
||||
addressRange,
|
||||
targetHWid,
|
||||
DateTimeOffset.Now,
|
||||
expires,
|
||||
reason,
|
||||
player?.UserId,
|
||||
null,
|
||||
role);
|
||||
|
||||
if (!await AddRoleBan(banDef))
|
||||
{
|
||||
shell.WriteLine(Loc.GetString("cmd-roleban-existing", ("target", located.Username), ("role", role)));
|
||||
return;
|
||||
}
|
||||
|
||||
var length = expires == null ? Loc.GetString("cmd-roleban-inf") : Loc.GetString("cmd-roleban-until", ("expires", expires));
|
||||
shell.WriteLine(Loc.GetString("cmd-roleban-success", ("target", located.Username), ("role", role), ("reason", reason), ("length", length)));
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
63
Content.Server/Administration/Notes/AdminMessageEui.cs
Normal file
63
Content.Server/Administration/Notes/AdminMessageEui.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
using Content.Server.Database;
|
||||
using Content.Server.EUI;
|
||||
using Content.Shared.Administration.Notes;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Eui;
|
||||
using Robust.Shared.Configuration;
|
||||
using static Content.Shared.Administration.Notes.AdminMessageEuiMsg;
|
||||
|
||||
namespace Content.Server.Administration.Notes;
|
||||
|
||||
public sealed class AdminMessageEui : BaseEui
|
||||
{
|
||||
[Dependency] private readonly IAdminNotesManager _notesMan = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
private readonly float _closeWait;
|
||||
private AdminMessage? _message;
|
||||
private DateTime _startTime;
|
||||
|
||||
public AdminMessageEui()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
_closeWait = _cfg.GetCVar(CCVars.MessageWaitTime);
|
||||
}
|
||||
|
||||
public void SetMessage(AdminMessage message)
|
||||
{
|
||||
_message = message;
|
||||
_startTime = DateTime.UtcNow;
|
||||
StateDirty();
|
||||
}
|
||||
|
||||
public override EuiStateBase GetNewState()
|
||||
{
|
||||
if (_message == null)
|
||||
return new AdminMessageEuiState(float.MaxValue, "An error has occurred.", string.Empty, DateTime.MinValue);
|
||||
return new AdminMessageEuiState(
|
||||
_closeWait,
|
||||
_message.Message,
|
||||
_message.CreatedBy?.LastSeenUserName ?? "[System]",
|
||||
_message.CreatedAt
|
||||
);
|
||||
}
|
||||
|
||||
public override async void HandleMessage(EuiMessageBase msg)
|
||||
{
|
||||
base.HandleMessage(msg);
|
||||
|
||||
switch (msg)
|
||||
{
|
||||
case Accept:
|
||||
if (_message == null)
|
||||
break;
|
||||
// No escape
|
||||
if (DateTime.UtcNow - _startTime >= TimeSpan.FromSeconds(_closeWait))
|
||||
await _notesMan.MarkMessageAsSeen(_message.Id);
|
||||
Close();
|
||||
break;
|
||||
case Dismiss:
|
||||
Close();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,12 @@
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.Administration.Managers;
|
||||
using Content.Server.EUI;
|
||||
using Content.Shared.Administration.Notes;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Eui;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.Database;
|
||||
using Robust.Shared.Network;
|
||||
using static Content.Shared.Administration.Notes.AdminNoteEuiMsg;
|
||||
|
||||
namespace Content.Server.Administration.Notes;
|
||||
@@ -11,6 +15,8 @@ public sealed class AdminNotesEui : BaseEui
|
||||
{
|
||||
[Dependency] private readonly IAdminManager _admins = default!;
|
||||
[Dependency] private readonly IAdminNotesManager _notesMan = default!;
|
||||
[Dependency] private readonly IPlayerLocator _locator = default!;
|
||||
[Dependency] private readonly IServerDbManager _db = default!;
|
||||
|
||||
public AdminNotesEui()
|
||||
{
|
||||
@@ -19,7 +25,8 @@ public sealed class AdminNotesEui : BaseEui
|
||||
|
||||
private Guid NotedPlayer { get; set; }
|
||||
private string NotedPlayerName { get; set; } = string.Empty;
|
||||
private Dictionary<int, SharedAdminNote> Notes { get; set; } = new();
|
||||
private bool HasConnectedBefore { get; set; }
|
||||
private Dictionary<(int, NoteType), SharedAdminNote> Notes { get; set; } = new();
|
||||
|
||||
public override async void Opened()
|
||||
{
|
||||
@@ -46,7 +53,7 @@ public sealed class AdminNotesEui : BaseEui
|
||||
return new AdminNotesEuiState(
|
||||
NotedPlayerName,
|
||||
Notes,
|
||||
_notesMan.CanCreate(Player),
|
||||
_notesMan.CanCreate(Player) && HasConnectedBefore,
|
||||
_notesMan.CanDelete(Player),
|
||||
_notesMan.CanEdit(Player)
|
||||
);
|
||||
@@ -58,49 +65,51 @@ public sealed class AdminNotesEui : BaseEui
|
||||
|
||||
switch (msg)
|
||||
{
|
||||
case CreateNoteRequest {Message: var message}:
|
||||
{
|
||||
if (!_notesMan.CanCreate(Player))
|
||||
case CreateNoteRequest request:
|
||||
{
|
||||
Close();
|
||||
if (!_notesMan.CanCreate(Player))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(request.Message))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (request.ExpiryTime is not null && request.ExpiryTime <= DateTime.UtcNow)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
await _notesMan.AddAdminRemark(Player, NotedPlayer, request.NoteType, request.Message, request.NoteSeverity, request.Secret, request.ExpiryTime);
|
||||
break;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(message))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
await _notesMan.AddNote(Player, NotedPlayer, message);
|
||||
break;
|
||||
}
|
||||
case DeleteNoteRequest request:
|
||||
{
|
||||
if (!_notesMan.CanDelete(Player))
|
||||
{
|
||||
Close();
|
||||
if (!_notesMan.CanDelete(Player))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
await _notesMan.DeleteAdminRemark(request.Id, request.Type, Player);
|
||||
break;
|
||||
}
|
||||
|
||||
await _notesMan.DeleteNote(request.Id, Player);
|
||||
break;
|
||||
}
|
||||
case EditNoteRequest request:
|
||||
{
|
||||
if (!_notesMan.CanEdit(Player))
|
||||
{
|
||||
Close();
|
||||
if (!_notesMan.CanEdit(Player))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(request.Message))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
await _notesMan.ModifyAdminRemark(request.Id, request.Type, Player, request.Message, request.NoteSeverity, request.Secret, request.ExpiryTime);
|
||||
break;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(request.Message))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
await _notesMan.ModifyNote(request.Id, Player, request.Message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,7 +124,7 @@ public sealed class AdminNotesEui : BaseEui
|
||||
if (note.Player != NotedPlayer)
|
||||
return;
|
||||
|
||||
Notes[note.Id] = note;
|
||||
Notes[(note.Id, note.NoteType)] = note;
|
||||
StateDirty();
|
||||
}
|
||||
|
||||
@@ -124,28 +133,29 @@ public sealed class AdminNotesEui : BaseEui
|
||||
if (note.Player != NotedPlayer)
|
||||
return;
|
||||
|
||||
Notes.Remove(note.Id);
|
||||
Notes.Remove((note.Id, note.NoteType));
|
||||
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;
|
||||
|
||||
var locatedPlayer = await _locator.LookupIdAsync((NetUserId) NotedPlayer);
|
||||
NotedPlayerName = locatedPlayer?.Username ?? string.Empty;
|
||||
HasConnectedBefore = locatedPlayer?.LastAddress is not null;
|
||||
Notes = (from note in await _notesMan.GetAllAdminRemarks(NotedPlayer)
|
||||
select note.ToShared())
|
||||
.ToDictionary(sharedNote => (sharedNote.Id, sharedNote.NoteType));
|
||||
StateDirty();
|
||||
}
|
||||
|
||||
private void OnPermsChanged(AdminPermsChangedEventArgs args)
|
||||
{
|
||||
if (args.Player == Player && !_notesMan.CanView(Player))
|
||||
if (args.Player != Player)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_notesMan.CanView(Player))
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
@@ -1,21 +1,75 @@
|
||||
using Content.Server.Database;
|
||||
using System.Diagnostics;
|
||||
using Content.Server.Database;
|
||||
using Content.Shared.Administration.Notes;
|
||||
using Content.Shared.Database;
|
||||
|
||||
namespace Content.Server.Administration.Notes;
|
||||
|
||||
public static class AdminNotesExtensions
|
||||
{
|
||||
public static SharedAdminNote ToShared(this AdminNote note)
|
||||
public static SharedAdminNote ToShared(this IAdminRemarksCommon note)
|
||||
{
|
||||
NoteSeverity? severity = null;
|
||||
var secret = false;
|
||||
NoteType type;
|
||||
string[]? bannedRoles = null;
|
||||
string? unbannedByName = null;
|
||||
DateTime? unbannedTime = null;
|
||||
bool? seen = null;
|
||||
switch (note)
|
||||
{
|
||||
case AdminNote adminNote:
|
||||
type = NoteType.Note;
|
||||
severity = adminNote.Severity;
|
||||
secret = adminNote.Secret;
|
||||
break;
|
||||
case AdminWatchlist:
|
||||
type = NoteType.Watchlist;
|
||||
secret = true;
|
||||
break;
|
||||
case AdminMessage adminMessage:
|
||||
type = NoteType.Message;
|
||||
seen = adminMessage.Seen;
|
||||
break;
|
||||
case ServerBanNote ban:
|
||||
type = NoteType.ServerBan;
|
||||
severity = ban.Severity;
|
||||
unbannedTime = ban.UnbanTime;
|
||||
unbannedByName = ban.UnbanningAdmin?.LastSeenUserName ?? Loc.GetString("system-user");
|
||||
break;
|
||||
case ServerRoleBanNote roleBan:
|
||||
type = NoteType.RoleBan;
|
||||
severity = roleBan.Severity;
|
||||
bannedRoles = roleBan.Roles;
|
||||
unbannedTime = roleBan.UnbanTime;
|
||||
unbannedByName = roleBan.UnbanningAdmin?.LastSeenUserName ?? Loc.GetString("system-user");
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(type), note.GetType(), "Unknown note type");
|
||||
}
|
||||
|
||||
// There may be bans without a user, but why would we ever be converting them to shared notes?
|
||||
if (note.PlayerUserId is null)
|
||||
throw new ArgumentNullException(nameof(note.PlayerUserId), "Player user ID cannot be null for a note");
|
||||
return new SharedAdminNote(
|
||||
note.Id,
|
||||
note.PlayerUserId.Value,
|
||||
note.RoundId,
|
||||
note.PlayerUserId,
|
||||
note.Round?.Server.Name,
|
||||
note.PlaytimeAtNote,
|
||||
type,
|
||||
note.Message,
|
||||
note.CreatedBy.LastSeenUserName,
|
||||
note.LastEditedBy.LastSeenUserName,
|
||||
severity,
|
||||
secret,
|
||||
note.CreatedBy?.LastSeenUserName ?? Loc.GetString("system-user"),
|
||||
note.LastEditedBy?.LastSeenUserName ?? string.Empty,
|
||||
note.CreatedAt,
|
||||
note.LastEditedAt
|
||||
note.LastEditedAt,
|
||||
note.ExpirationTime,
|
||||
bannedRoles,
|
||||
unbannedTime,
|
||||
unbannedByName,
|
||||
seen
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
using System.Threading.Tasks;
|
||||
using System.Text;
|
||||
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 Content.Shared.CCVar;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Players.PlayTimeTracking;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Network;
|
||||
|
||||
namespace Content.Server.Administration.Notes;
|
||||
@@ -17,6 +23,7 @@ public sealed class AdminNotesManager : IAdminNotesManager, IPostInjectInit
|
||||
[Dependency] private readonly ILogManager _logManager = default!;
|
||||
[Dependency] private readonly EuiManager _euis = default!;
|
||||
[Dependency] private readonly IEntitySystemManager _systems = default!;
|
||||
[Dependency] private readonly IConfigurationManager _config = default!;
|
||||
|
||||
public const string SawmillId = "admin.notes";
|
||||
|
||||
@@ -54,91 +61,280 @@ public sealed class AdminNotesManager : IAdminNotesManager, IPostInjectInit
|
||||
await ui.ChangeNotedPlayer(notedPlayer);
|
||||
}
|
||||
|
||||
public async Task AddNote(IPlayerSession createdBy, Guid player, string message)
|
||||
public async Task OpenUserNotesEui(IPlayerSession player)
|
||||
{
|
||||
_sawmill.Info($"Player {createdBy.Name} added note with message {message}");
|
||||
var ui = new UserNotesEui();
|
||||
_euis.OpenEui(ui, player);
|
||||
|
||||
await ui.UpdateNotes();
|
||||
}
|
||||
|
||||
public async Task AddAdminRemark(IPlayerSession createdBy, Guid player, NoteType type, string message, NoteSeverity? severity, bool secret, DateTime? expiryTime)
|
||||
{
|
||||
message = message.Trim();
|
||||
|
||||
// There's a foreign key constraint in place here. If there's no player record, it will fail.
|
||||
// Not like there's much use in adding notes on accounts that have never connected.
|
||||
// You can still ban them just fine, which is why we should allow admins to view their bans with the notes panel
|
||||
if (await _db.GetPlayerRecordByUserId((NetUserId) player) is null)
|
||||
return;
|
||||
|
||||
var sb = new StringBuilder($"{createdBy.Name} added a");
|
||||
|
||||
if (secret && type == NoteType.Note)
|
||||
{
|
||||
sb.Append(" secret");
|
||||
}
|
||||
|
||||
sb.Append($" {type} with message {message}");
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case NoteType.Note:
|
||||
sb.Append($" with {severity} severity");
|
||||
break;
|
||||
case NoteType.Message:
|
||||
severity = null;
|
||||
secret = false;
|
||||
break;
|
||||
case NoteType.Watchlist:
|
||||
severity = null;
|
||||
secret = true;
|
||||
break;
|
||||
case NoteType.ServerBan:
|
||||
case NoteType.RoleBan:
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(type), type, "Unknown note type");
|
||||
}
|
||||
|
||||
if (expiryTime is not null)
|
||||
{
|
||||
sb.Append($" which expires on {expiryTime.Value.ToUniversalTime(): yyyy-MM-dd HH:mm:ss} UTC");
|
||||
}
|
||||
|
||||
_sawmill.Info(sb.ToString());
|
||||
|
||||
_systems.TryGetEntitySystem(out GameTicker? ticker);
|
||||
int? round = ticker == null || ticker.RoundId == 0 ? null : ticker.RoundId;
|
||||
int? roundId = ticker == null || ticker.RoundId == 0 ? null : ticker.RoundId;
|
||||
var serverName = _config.GetCVar(CCVars.AdminLogsServerName); // This could probably be done another way, but this is fine. For displaying only.
|
||||
var createdAt = DateTime.UtcNow;
|
||||
var noteId = await _db.AddAdminNote(round, player, message, createdBy.UserId, createdAt);
|
||||
var playtime = (await _db.GetPlayTimes(player)).Find(p => p.Tracker == PlayTimeTrackingShared.TrackerOverall)?.TimeSpent ?? TimeSpan.Zero;
|
||||
int noteId;
|
||||
bool? seen = null;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case NoteType.Note:
|
||||
if (severity is null)
|
||||
throw new ArgumentException("Severity cannot be null for a note", nameof(severity));
|
||||
noteId = await _db.AddAdminNote(roundId, player, playtime, message, severity.Value, secret, createdBy.UserId, createdAt, expiryTime);
|
||||
break;
|
||||
case NoteType.Watchlist:
|
||||
secret = true;
|
||||
noteId = await _db.AddAdminWatchlist(roundId, player, playtime, message, createdBy.UserId, createdAt, expiryTime);
|
||||
break;
|
||||
case NoteType.Message:
|
||||
noteId = await _db.AddAdminMessage(roundId, player, playtime, message, createdBy.UserId, createdAt, expiryTime);
|
||||
seen = false;
|
||||
break;
|
||||
case NoteType.ServerBan: // Add bans using the ban panel, not note edit
|
||||
case NoteType.RoleBan:
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(type), type, "Unknown note type");
|
||||
}
|
||||
|
||||
var note = new SharedAdminNote(
|
||||
noteId,
|
||||
round,
|
||||
player,
|
||||
roundId,
|
||||
serverName,
|
||||
playtime,
|
||||
type,
|
||||
message,
|
||||
severity,
|
||||
secret,
|
||||
createdBy.Name,
|
||||
createdBy.Name,
|
||||
createdAt,
|
||||
createdAt
|
||||
createdAt,
|
||||
expiryTime,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
seen
|
||||
);
|
||||
NoteAdded?.Invoke(note);
|
||||
}
|
||||
|
||||
public async Task DeleteNote(int noteId, IPlayerSession deletedBy)
|
||||
private async Task<SharedAdminNote?> GetAdminRemark(int id, NoteType type)
|
||||
{
|
||||
var note = await _db.GetAdminNote(noteId);
|
||||
return type switch
|
||||
{
|
||||
NoteType.Note => (await _db.GetAdminNote(id))?.ToShared(),
|
||||
NoteType.Watchlist => (await _db.GetAdminWatchlist(id))?.ToShared(),
|
||||
NoteType.Message => (await _db.GetAdminMessage(id))?.ToShared(),
|
||||
NoteType.ServerBan => (await _db.GetServerBanAsNoteAsync(id))?.ToShared(),
|
||||
NoteType.RoleBan => (await _db.GetServerRoleBanAsNoteAsync(id))?.ToShared(),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(type), type, "Unknown note type")
|
||||
};
|
||||
}
|
||||
|
||||
public async Task DeleteAdminRemark(int noteId, NoteType type, IPlayerSession deletedBy)
|
||||
{
|
||||
var note = await GetAdminRemark(noteId, type);
|
||||
if (note == null)
|
||||
{
|
||||
_sawmill.Info($"Player {deletedBy.Name} tried to delete non-existent note {noteId}");
|
||||
_sawmill.Warning($"Player {deletedBy.Name} has tried to delete non-existent {type} {noteId}");
|
||||
return;
|
||||
}
|
||||
|
||||
_sawmill.Info($"Player {deletedBy.Name} deleted note {noteId}");
|
||||
|
||||
var deletedAt = DateTime.UtcNow;
|
||||
await _db.DeleteAdminNote(noteId, deletedBy.UserId, deletedAt);
|
||||
|
||||
var sharedNote = new SharedAdminNote(
|
||||
noteId,
|
||||
note.RoundId,
|
||||
note.PlayerUserId,
|
||||
note.Message,
|
||||
note.CreatedBy.LastSeenUserName,
|
||||
note.LastEditedBy.LastSeenUserName,
|
||||
note.CreatedAt,
|
||||
note.LastEditedAt
|
||||
);
|
||||
NoteDeleted?.Invoke(sharedNote);
|
||||
switch (type)
|
||||
{
|
||||
case NoteType.Note:
|
||||
await _db.DeleteAdminNote(noteId, deletedBy.UserId, deletedAt);
|
||||
break;
|
||||
case NoteType.Watchlist:
|
||||
await _db.DeleteAdminWatchlist(noteId, deletedBy.UserId, deletedAt);
|
||||
break;
|
||||
case NoteType.Message:
|
||||
await _db.DeleteAdminMessage(noteId, deletedBy.UserId, deletedAt);
|
||||
break;
|
||||
case NoteType.ServerBan:
|
||||
await _db.HideServerBanFromNotes(noteId, deletedBy.UserId, deletedAt);
|
||||
break;
|
||||
case NoteType.RoleBan:
|
||||
await _db.HideServerRoleBanFromNotes(noteId, deletedBy.UserId, deletedAt);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(type), type, "Unknown note type");
|
||||
}
|
||||
|
||||
_sawmill.Info($"{deletedBy.Name} has deleted {type} {noteId}");
|
||||
NoteDeleted?.Invoke(note);
|
||||
}
|
||||
|
||||
public async Task ModifyNote(int noteId, IPlayerSession editedBy, string message)
|
||||
public async Task ModifyAdminRemark(int noteId, NoteType type, IPlayerSession editedBy, string message, NoteSeverity? severity, bool secret, DateTime? expiryTime)
|
||||
{
|
||||
message = message.Trim();
|
||||
|
||||
var note = await _db.GetAdminNote(noteId);
|
||||
if (note == null || note.Message == message)
|
||||
var note = await GetAdminRemark(noteId, type);
|
||||
|
||||
// If the note doesn't exist or is the same, we skip updating it
|
||||
if (note == null ||
|
||||
note.Message == message &&
|
||||
note.NoteSeverity == severity &&
|
||||
note.Secret == secret &&
|
||||
note.ExpiryTime == expiryTime)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_sawmill.Info($"Player {editedBy.Name} modified note {noteId} with message {message}");
|
||||
var sb = new StringBuilder($"{editedBy.Name} has modified {type} {noteId}");
|
||||
|
||||
if (note.Message != message)
|
||||
{
|
||||
sb.Append($", modified message from {note.Message} to {message}");
|
||||
}
|
||||
|
||||
if (note.Secret != secret)
|
||||
{
|
||||
sb.Append($", made it {(secret ? "secret" : "visible")}");
|
||||
}
|
||||
|
||||
if (note.NoteSeverity != severity)
|
||||
{
|
||||
sb.Append($", updated the severity from {note.NoteSeverity} to {severity}");
|
||||
}
|
||||
|
||||
if (note.ExpiryTime != expiryTime)
|
||||
{
|
||||
sb.Append(", updated the expiry time from ");
|
||||
if (note.ExpiryTime is null)
|
||||
sb.Append("never");
|
||||
else
|
||||
sb.Append($"{note.ExpiryTime.Value.ToUniversalTime(): yyyy-MM-dd HH:mm:ss} UTC");
|
||||
|
||||
sb.Append(" to ");
|
||||
|
||||
if (expiryTime is null)
|
||||
sb.Append("never");
|
||||
else
|
||||
sb.Append($"{expiryTime.Value.ToUniversalTime(): yyyy-MM-dd HH:mm:ss} UTC");
|
||||
}
|
||||
|
||||
_sawmill.Info(sb.ToString());
|
||||
|
||||
var editedAt = DateTime.UtcNow;
|
||||
await _db.EditAdminNote(noteId, message, editedBy.UserId, editedAt);
|
||||
|
||||
var sharedNote = new SharedAdminNote(
|
||||
noteId,
|
||||
note.RoundId,
|
||||
note.PlayerUserId,
|
||||
message,
|
||||
note.CreatedBy.LastSeenUserName,
|
||||
editedBy.Name,
|
||||
note.CreatedAt,
|
||||
note.LastEditedAt
|
||||
);
|
||||
NoteModified?.Invoke(sharedNote);
|
||||
switch (type)
|
||||
{
|
||||
case NoteType.Note:
|
||||
if (severity is null)
|
||||
throw new ArgumentException("Severity cannot be null for a note", nameof(severity));
|
||||
await _db.EditAdminNote(noteId, message, severity.Value, secret, editedBy.UserId, editedAt, expiryTime);
|
||||
break;
|
||||
case NoteType.Watchlist:
|
||||
await _db.EditAdminWatchlist(noteId, message, editedBy.UserId, editedAt, expiryTime);
|
||||
break;
|
||||
case NoteType.Message:
|
||||
await _db.EditAdminMessage(noteId, message, editedBy.UserId, editedAt, expiryTime);
|
||||
break;
|
||||
case NoteType.ServerBan:
|
||||
if (severity is null)
|
||||
throw new ArgumentException("Severity cannot be null for a ban", nameof(severity));
|
||||
await _db.EditServerBan(noteId, message, severity.Value, expiryTime, editedBy.UserId, editedAt);
|
||||
break;
|
||||
case NoteType.RoleBan:
|
||||
if (severity is null)
|
||||
throw new ArgumentException("Severity cannot be null for a role ban", nameof(severity));
|
||||
await _db.EditServerRoleBan(noteId, message, severity.Value, expiryTime, editedBy.UserId, editedAt);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(type), type, "Unknown note type");
|
||||
}
|
||||
|
||||
var newNote = note with
|
||||
{
|
||||
Message = message,
|
||||
NoteSeverity = severity,
|
||||
Secret = secret,
|
||||
LastEditedAt = editedAt,
|
||||
EditedByName = editedBy.Name,
|
||||
ExpiryTime = expiryTime
|
||||
};
|
||||
NoteModified?.Invoke(newNote);
|
||||
}
|
||||
|
||||
public async Task<List<AdminNote>> GetNotes(Guid player)
|
||||
public async Task<List<IAdminRemarksCommon>> GetAllAdminRemarks(Guid player)
|
||||
{
|
||||
return await _db.GetAdminNotes(player);
|
||||
return await _db.GetAllAdminRemarks(player);
|
||||
}
|
||||
|
||||
public async Task<string> GetPlayerName(Guid player)
|
||||
public async Task<List<IAdminRemarksCommon>> GetVisibleRemarks(Guid player)
|
||||
{
|
||||
return (await _db.GetPlayerRecordByUserId(new NetUserId(player)))?.LastSeenUserName ?? string.Empty;
|
||||
if (_config.GetCVar(CCVars.SeeOwnNotes))
|
||||
{
|
||||
return await _db.GetVisibleAdminNotes(player);
|
||||
}
|
||||
_sawmill.Warning($"Someone tried to call GetVisibleNotes for {player} when see_own_notes was false");
|
||||
return new List<IAdminRemarksCommon>();
|
||||
}
|
||||
|
||||
public async Task<List<AdminWatchlist>> GetActiveWatchlists(Guid player)
|
||||
{
|
||||
return await _db.GetActiveWatchlists(player);
|
||||
}
|
||||
|
||||
public async Task<List<AdminMessage>> GetNewMessages(Guid player)
|
||||
{
|
||||
return await _db.GetMessages(player);
|
||||
}
|
||||
|
||||
public async Task MarkMessageAsSeen(int id)
|
||||
{
|
||||
await _db.MarkMessageAsSeen(id);
|
||||
}
|
||||
|
||||
public void PostInject()
|
||||
|
||||
@@ -1,20 +1,33 @@
|
||||
using Content.Server.Administration.Commands;
|
||||
using Content.Server.Administration.Commands;
|
||||
using Content.Server.Chat.Managers;
|
||||
using Content.Server.EUI;
|
||||
using Content.Shared.Chat;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Administration.Notes;
|
||||
|
||||
public sealed class AdminNotesSystem : EntitySystem
|
||||
public sealed class AdminNotesSystem : EntitySystem, IPostInjectInit
|
||||
{
|
||||
[Dependency] private readonly IConsoleHost _console = default!;
|
||||
[Dependency] private readonly IAdminNotesManager _notes = default!;
|
||||
[Dependency] private readonly ILogManager _logManager = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IChatManager _chat = default!;
|
||||
[Dependency] private readonly EuiManager _euis = default!;
|
||||
|
||||
public const string SawmillId = "admin.notes_system";
|
||||
private ISawmill _sawmill = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<GetVerbsEvent<Verb>>(AddVerbs);
|
||||
_playerManager.PlayerStatusChanged += OnPlayerStatusChanged;
|
||||
}
|
||||
|
||||
private void AddVerbs(GetVerbsEvent<Verb> ev)
|
||||
@@ -41,4 +54,43 @@ public sealed class AdminNotesSystem : EntitySystem
|
||||
|
||||
ev.Verbs.Add(verb);
|
||||
}
|
||||
|
||||
private async void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs e)
|
||||
{
|
||||
if (e.NewStatus != SessionStatus.Connected)
|
||||
return;
|
||||
|
||||
var messages = await _notes.GetNewMessages(e.Session.UserId);
|
||||
var watchlists = await _notes.GetActiveWatchlists(e.Session.UserId);
|
||||
|
||||
if (!_playerManager.TryGetPlayerData(e.Session.UserId, out var playerData))
|
||||
{
|
||||
_sawmill.Error($"Could not get player data for ID {e.Session.UserId}");
|
||||
}
|
||||
|
||||
var username = playerData?.UserName ?? e.Session.UserId.ToString();
|
||||
foreach (var watchlist in watchlists)
|
||||
{
|
||||
_chat.SendAdminAlert(Loc.GetString("admin-notes-watchlist", ("player", username), ("message", watchlist.Message)));
|
||||
}
|
||||
|
||||
foreach (var message in messages)
|
||||
{
|
||||
var messageString = Loc.GetString("admin-notes-new-message", ("admin", message.CreatedBy?.LastSeenUserName ?? "[System]"), ("message", message.Message));
|
||||
// Only open the popup if the user hasn't seen it yet
|
||||
if (!message.Seen)
|
||||
{
|
||||
var ui = new AdminMessageEui();
|
||||
_euis.OpenEui(ui, e.Session);
|
||||
ui.SetMessage(message);
|
||||
}
|
||||
// Send the message anyway
|
||||
_chat.DispatchServerMessage(e.Session, messageString);
|
||||
}
|
||||
}
|
||||
|
||||
public void PostInject()
|
||||
{
|
||||
_sawmill = _logManager.GetSawmill(SawmillId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.Database;
|
||||
using Content.Shared.Administration.Notes;
|
||||
using Content.Shared.Database;
|
||||
using Robust.Server.Player;
|
||||
|
||||
namespace Content.Server.Administration.Notes;
|
||||
@@ -16,9 +17,33 @@ public interface IAdminNotesManager
|
||||
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);
|
||||
Task OpenUserNotesEui(IPlayerSession player);
|
||||
Task AddAdminRemark(IPlayerSession createdBy, Guid player, NoteType type, string message, NoteSeverity? severity, bool secret, DateTime? expiryTime);
|
||||
Task DeleteAdminRemark(int noteId, NoteType type, IPlayerSession deletedBy);
|
||||
Task ModifyAdminRemark(int noteId, NoteType type, IPlayerSession editedBy, string message, NoteSeverity? severity, bool secret, DateTime? expiryTime);
|
||||
/// <summary>
|
||||
/// Queries the database and retrieves all notes, secret and visible
|
||||
/// </summary>
|
||||
/// <param name="player">Desired player's <see cref="Guid"/></param>
|
||||
/// <returns>ALL non-deleted notes, secret or not</returns>
|
||||
Task<List<IAdminRemarksCommon>> GetAllAdminRemarks(Guid player);
|
||||
/// <summary>
|
||||
/// Queries the database and retrieves the notes a player should see
|
||||
/// </summary>
|
||||
/// <param name="player">Desired player's <see cref="Guid"/></param>
|
||||
/// <returns>All player-visible notes</returns>
|
||||
Task<List<IAdminRemarksCommon>> GetVisibleRemarks(Guid player);
|
||||
/// <summary>
|
||||
/// Queries the database and retrieves watchlists that may have been placed on the player
|
||||
/// </summary>
|
||||
/// <param name="player">Desired player's <see cref="Guid"/></param>
|
||||
/// <returns>Active watchlists</returns>
|
||||
Task<List<AdminWatchlist>> GetActiveWatchlists(Guid player);
|
||||
/// <summary>
|
||||
/// Queries the database and retrieves new messages a player has gotten
|
||||
/// </summary>
|
||||
/// <param name="player">Desired player's <see cref="Guid"/></param>
|
||||
/// <returns>All unread messages</returns>
|
||||
Task<List<AdminMessage>> GetNewMessages(Guid player);
|
||||
Task MarkMessageAsSeen(int id);
|
||||
}
|
||||
|
||||
49
Content.Server/Administration/Notes/UserNotesEui.cs
Normal file
49
Content.Server/Administration/Notes/UserNotesEui.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.EUI;
|
||||
using Content.Shared.Administration.Notes;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Eui;
|
||||
using Robust.Shared.Configuration;
|
||||
|
||||
namespace Content.Server.Administration.Notes;
|
||||
|
||||
public sealed class UserNotesEui : BaseEui
|
||||
{
|
||||
[Dependency] private readonly IAdminNotesManager _notesMan = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
private readonly bool _seeOwnNotes;
|
||||
|
||||
public UserNotesEui()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
_seeOwnNotes = _cfg.GetCVar(CCVars.SeeOwnNotes);
|
||||
|
||||
if (!_seeOwnNotes)
|
||||
{
|
||||
Logger.WarningS("admin.notes", "User notes initialized when see_own_notes set to false");
|
||||
}
|
||||
}
|
||||
|
||||
private Dictionary<(int, NoteType), SharedAdminNote> Notes { get; set; } = new();
|
||||
|
||||
public override EuiStateBase GetNewState()
|
||||
{
|
||||
return new UserNotesEuiState(
|
||||
Notes
|
||||
);
|
||||
}
|
||||
|
||||
public async Task UpdateNotes()
|
||||
{
|
||||
if (!_seeOwnNotes)
|
||||
{
|
||||
Logger.WarningS("admin.notes", $"User {Player.Name} with ID {Player.UserId} tried to update their own user notes when see_own_notes was set to false");
|
||||
return;
|
||||
}
|
||||
|
||||
Notes = (await _notesMan.GetVisibleRemarks(Player.UserId)).Select(note => note.ToShared()).ToDictionary(note => (note.Id, note.NoteType));
|
||||
StateDirty();
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<!-- Work around https://github.com/dotnet/project-system/issues/4314 -->
|
||||
<TargetFramework>$(TargetFramework)</TargetFramework>
|
||||
@@ -15,6 +15,7 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2022.1.0" PrivateAssets="All" />
|
||||
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Content.Packaging\Content.Packaging.csproj" />
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Collections.Immutable;
|
||||
using System.Collections.Immutable;
|
||||
using System.Net;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Database;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Network;
|
||||
|
||||
@@ -16,7 +17,10 @@ namespace Content.Server.Database
|
||||
|
||||
public DateTimeOffset BanTime { get; }
|
||||
public DateTimeOffset? ExpirationTime { get; }
|
||||
public int? RoundId { get; }
|
||||
public TimeSpan PlaytimeAtNote { get; }
|
||||
public string Reason { get; }
|
||||
public NoteSeverity Severity { get; set; }
|
||||
public NetUserId? BanningAdmin { get; }
|
||||
public ServerUnbanDef? Unban { get; }
|
||||
|
||||
@@ -27,7 +31,10 @@ namespace Content.Server.Database
|
||||
ImmutableArray<byte>? hwId,
|
||||
DateTimeOffset banTime,
|
||||
DateTimeOffset? expirationTime,
|
||||
int? roundId,
|
||||
TimeSpan playtimeAtNote,
|
||||
string reason,
|
||||
NoteSeverity severity,
|
||||
NetUserId? banningAdmin,
|
||||
ServerUnbanDef? unban)
|
||||
{
|
||||
@@ -49,7 +56,10 @@ namespace Content.Server.Database
|
||||
HWId = hwId;
|
||||
BanTime = banTime;
|
||||
ExpirationTime = expirationTime;
|
||||
RoundId = roundId;
|
||||
PlaytimeAtNote = playtimeAtNote;
|
||||
Reason = reason;
|
||||
Severity = severity;
|
||||
BanningAdmin = banningAdmin;
|
||||
Unban = unban;
|
||||
}
|
||||
@@ -66,10 +76,9 @@ namespace Content.Server.Database
|
||||
else
|
||||
{
|
||||
var appeal = cfg.GetCVar(CCVars.InfoLinksAppeal);
|
||||
if (!string.IsNullOrWhiteSpace(appeal))
|
||||
expires = loc.GetString("ban-banned-permanent-appeal", ("link", appeal));
|
||||
else
|
||||
expires = loc.GetString("ban-banned-permanent");
|
||||
expires = !string.IsNullOrWhiteSpace(appeal)
|
||||
? loc.GetString("ban-banned-permanent-appeal", ("link", appeal))
|
||||
: loc.GetString("ban-banned-permanent");
|
||||
}
|
||||
|
||||
return $"""
|
||||
|
||||
14
Content.Server/Database/ServerBanNote.cs
Normal file
14
Content.Server/Database/ServerBanNote.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Shared.Database;
|
||||
|
||||
namespace Content.Server.Database
|
||||
{
|
||||
public record ServerBanNote(int Id, int? RoundId, Round? Round, Guid? PlayerUserId, Player? Player,
|
||||
TimeSpan PlaytimeAtNote, string Message, NoteSeverity Severity, Player? CreatedBy, DateTime CreatedAt,
|
||||
Player? LastEditedBy, DateTime? LastEditedAt, DateTime? ExpirationTime, bool Deleted, Player? UnbanningAdmin,
|
||||
DateTime? UnbanTime) : IAdminRemarksCommon;
|
||||
}
|
||||
@@ -5,7 +5,9 @@ using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.Administration.Logs;
|
||||
using Content.Server.Administration.Managers;
|
||||
using Content.Shared.Administration.Logs;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.Humanoid.Markings;
|
||||
using Content.Shared.Preferences;
|
||||
@@ -30,7 +32,8 @@ namespace Content.Server.Database
|
||||
.AsSingleQuery()
|
||||
.SingleOrDefaultAsync(p => p.UserId == userId.UserId);
|
||||
|
||||
if (prefs is null) return null;
|
||||
if (prefs is null)
|
||||
return null;
|
||||
|
||||
var maxSlot = prefs.Profiles.Max(p => p.Slot) + 1;
|
||||
var profiles = new Dictionary<int, ICharacterProfile>(maxSlot);
|
||||
@@ -339,6 +342,21 @@ namespace Content.Server.Database
|
||||
public abstract Task AddServerBanAsync(ServerBanDef serverBan);
|
||||
public abstract Task AddServerUnbanAsync(ServerUnbanDef serverUnban);
|
||||
|
||||
public async Task EditServerBan(int id, string reason, NoteSeverity severity, DateTime? expiration, Guid editedBy, DateTime editedAt)
|
||||
{
|
||||
await using var db = await GetDb();
|
||||
|
||||
var ban = await db.DbContext.Ban.SingleOrDefaultAsync(b => b.Id == id);
|
||||
if (ban is null)
|
||||
return;
|
||||
ban.Severity = severity;
|
||||
ban.Reason = reason;
|
||||
ban.ExpirationTime = expiration;
|
||||
ban.LastEditedById = editedBy;
|
||||
ban.LastEditedAt = editedAt;
|
||||
await db.DbContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
protected static async Task<ServerBanExemptFlags?> GetBanExemptionCore(DbGuard db, NetUserId? userId)
|
||||
{
|
||||
if (userId == null)
|
||||
@@ -415,6 +433,21 @@ namespace Content.Server.Database
|
||||
|
||||
public abstract Task AddServerRoleBanAsync(ServerRoleBanDef serverRoleBan);
|
||||
public abstract Task AddServerRoleUnbanAsync(ServerRoleUnbanDef serverRoleUnban);
|
||||
|
||||
public async Task EditServerRoleBan(int id, string reason, NoteSeverity severity, DateTime? expiration, Guid editedBy, DateTime editedAt)
|
||||
{
|
||||
await using var db = await GetDb();
|
||||
|
||||
var ban = await db.DbContext.RoleBan.SingleOrDefaultAsync(b => b.Id == id);
|
||||
if (ban is null)
|
||||
return;
|
||||
ban.Severity = severity;
|
||||
ban.Reason = reason;
|
||||
ban.ExpirationTime = expiration;
|
||||
ban.LastEditedById = editedBy;
|
||||
ban.LastEditedAt = editedAt;
|
||||
await db.DbContext.SaveChangesAsync();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Playtime
|
||||
@@ -959,12 +992,29 @@ INSERT INTO player_round (players_id, rounds_id) VALUES ({players[player]}, {id}
|
||||
return note.Id;
|
||||
}
|
||||
|
||||
public virtual async Task<int> AddAdminWatchlist(AdminWatchlist watchlist)
|
||||
{
|
||||
await using var db = await GetDb();
|
||||
db.DbContext.AdminWatchlists.Add(watchlist);
|
||||
await db.DbContext.SaveChangesAsync();
|
||||
return watchlist.Id;
|
||||
}
|
||||
|
||||
public virtual async Task<int> AddAdminMessage(AdminMessage message)
|
||||
{
|
||||
await using var db = await GetDb();
|
||||
db.DbContext.AdminMessages.Add(message);
|
||||
await db.DbContext.SaveChangesAsync();
|
||||
return message.Id;
|
||||
}
|
||||
|
||||
public async Task<AdminNote?> GetAdminNote(int id)
|
||||
{
|
||||
await using var db = await GetDb();
|
||||
return await db.DbContext.AdminNotes
|
||||
.Where(note => note.Id == id)
|
||||
.Include(note => note.Round)
|
||||
.ThenInclude(r => r!.Server)
|
||||
.Include(note => note.CreatedBy)
|
||||
.Include(note => note.LastEditedBy)
|
||||
.Include(note => note.DeletedBy)
|
||||
@@ -972,17 +1022,145 @@ INSERT INTO player_round (players_id, rounds_id) VALUES ({players[player]}, {id}
|
||||
.SingleOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task<List<AdminNote>> GetAdminNotes(Guid player)
|
||||
public async Task<AdminWatchlist?> GetAdminWatchlist(int id)
|
||||
{
|
||||
await using var db = await GetDb();
|
||||
return await db.DbContext.AdminNotes
|
||||
.Where(note => note.PlayerUserId == player)
|
||||
.Where(note => !note.Deleted)
|
||||
return await db.DbContext.AdminWatchlists
|
||||
.Where(note => note.Id == id)
|
||||
.Include(note => note.Round)
|
||||
.ThenInclude(r => r!.Server)
|
||||
.Include(note => note.CreatedBy)
|
||||
.Include(note => note.LastEditedBy)
|
||||
.Include(note => note.DeletedBy)
|
||||
.Include(note => note.Player)
|
||||
.SingleOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task<AdminMessage?> GetAdminMessage(int id)
|
||||
{
|
||||
await using var db = await GetDb();
|
||||
return await db.DbContext.AdminMessages
|
||||
.Where(note => note.Id == id)
|
||||
.Include(note => note.Round)
|
||||
.ThenInclude(r => r!.Server)
|
||||
.Include(note => note.CreatedBy)
|
||||
.Include(note => note.LastEditedBy)
|
||||
.Include(note => note.DeletedBy)
|
||||
.Include(note => note.Player)
|
||||
.SingleOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task<ServerBanNote?> GetServerBanAsNoteAsync(int id)
|
||||
{
|
||||
await using var db = await GetDb();
|
||||
|
||||
var ban = await db.DbContext.Ban
|
||||
.Include(ban => ban.Unban)
|
||||
.Include(ban => ban.Round)
|
||||
.ThenInclude(r => r!.Server)
|
||||
.Include(ban => ban.CreatedBy)
|
||||
.Include(ban => ban.LastEditedBy)
|
||||
.Include(ban => ban.Unban)
|
||||
.SingleOrDefaultAsync(b => b.Id == id);
|
||||
|
||||
if (ban is null)
|
||||
return null;
|
||||
|
||||
var player = await db.DbContext.Player.SingleOrDefaultAsync(p => p.UserId == ban.PlayerUserId);
|
||||
return new ServerBanNote(ban.Id, ban.RoundId, ban.Round, ban.PlayerUserId, player,
|
||||
ban.PlaytimeAtNote, ban.Reason, ban.Severity, ban.CreatedBy, ban.BanTime,
|
||||
ban.LastEditedBy, ban.LastEditedAt, ban.ExpirationTime, ban.Hidden,
|
||||
ban.Unban?.UnbanningAdmin == null
|
||||
? null
|
||||
: await db.DbContext.Player.SingleOrDefaultAsync(p =>
|
||||
p.UserId == ban.Unban.UnbanningAdmin.Value),
|
||||
ban.Unban?.UnbanTime);
|
||||
}
|
||||
|
||||
public async Task<ServerRoleBanNote?> GetServerRoleBanAsNoteAsync(int id)
|
||||
{
|
||||
await using var db = await GetDb();
|
||||
|
||||
var ban = await db.DbContext.RoleBan
|
||||
.Include(b => b.Unban)
|
||||
.SingleOrDefaultAsync(b => b.Id == id);
|
||||
|
||||
if (ban is null)
|
||||
return null;
|
||||
|
||||
var player = await db.DbContext.Player.SingleOrDefaultAsync(p => p.UserId == ban.PlayerUserId);
|
||||
var unbanningAdmin =
|
||||
ban.Unban is null
|
||||
? null
|
||||
: await db.DbContext.Player.SingleOrDefaultAsync(b => b.UserId == ban.Unban.UnbanningAdmin);
|
||||
return new ServerRoleBanNote(ban.Id, ban.RoundId, ban.Round, ban.PlayerUserId,
|
||||
player, ban.PlaytimeAtNote, ban.Reason, ban.Severity, ban.CreatedBy,
|
||||
ban.BanTime, ban.LastEditedBy, ban.LastEditedAt, ban.ExpirationTime,
|
||||
ban.Hidden, new [] { ban.RoleId.Replace(BanManager.JobPrefix, null) },
|
||||
unbanningAdmin, ban.Unban?.UnbanTime);
|
||||
}
|
||||
|
||||
public async Task<List<IAdminRemarksCommon>> GetAllAdminRemarks(Guid player)
|
||||
{
|
||||
await using var db = await GetDb();
|
||||
List<IAdminRemarksCommon> notes = new();
|
||||
notes.AddRange(
|
||||
await (from note in db.DbContext.AdminNotes
|
||||
where note.PlayerUserId == player &&
|
||||
!note.Deleted &&
|
||||
(note.ExpirationTime == null || DateTime.UtcNow < note.ExpirationTime)
|
||||
select note)
|
||||
.Include(note => note.Round)
|
||||
.ThenInclude(r => r!.Server)
|
||||
.Include(note => note.CreatedBy)
|
||||
.Include(note => note.LastEditedBy)
|
||||
.Include(note => note.Player)
|
||||
.ToListAsync();
|
||||
.ToListAsync());
|
||||
notes.AddRange(await GetActiveWatchlistsImpl(db, player));
|
||||
notes.AddRange(await GetMessagesImpl(db, player));
|
||||
notes.AddRange(await GetServerBansAsNotesForUser(db, player));
|
||||
notes.AddRange(await GetGroupedServerRoleBansAsNotesForUser(db, player));
|
||||
return notes;
|
||||
}
|
||||
public async Task EditAdminNote(int id, string message, NoteSeverity severity, bool secret, Guid editedBy, DateTime editedAt, DateTime? expiryTime)
|
||||
{
|
||||
await using var db = await GetDb();
|
||||
|
||||
var note = await db.DbContext.AdminNotes.Where(note => note.Id == id).SingleAsync();
|
||||
note.Message = message;
|
||||
note.Severity = severity;
|
||||
note.Secret = secret;
|
||||
note.LastEditedById = editedBy;
|
||||
note.LastEditedAt = editedAt;
|
||||
note.ExpirationTime = expiryTime;
|
||||
|
||||
await db.DbContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task EditAdminWatchlist(int id, string message, Guid editedBy, DateTime editedAt, DateTime? expiryTime)
|
||||
{
|
||||
await using var db = await GetDb();
|
||||
|
||||
var note = await db.DbContext.AdminWatchlists.Where(note => note.Id == id).SingleAsync();
|
||||
note.Message = message;
|
||||
note.LastEditedById = editedBy;
|
||||
note.LastEditedAt = editedAt;
|
||||
note.ExpirationTime = expiryTime;
|
||||
|
||||
await db.DbContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task EditAdminMessage(int id, string message, Guid editedBy, DateTime editedAt, DateTime? expiryTime)
|
||||
{
|
||||
await using var db = await GetDb();
|
||||
|
||||
var note = await db.DbContext.AdminMessages.Where(note => note.Id == id).SingleAsync();
|
||||
note.Message = message;
|
||||
note.LastEditedById = editedBy;
|
||||
note.LastEditedAt = editedAt;
|
||||
note.ExpirationTime = expiryTime;
|
||||
|
||||
await db.DbContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task DeleteAdminNote(int id, Guid deletedBy, DateTime deletedAt)
|
||||
@@ -998,18 +1176,204 @@ INSERT INTO player_round (players_id, rounds_id) VALUES ({players[player]}, {id}
|
||||
await db.DbContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task EditAdminNote(int id, string message, Guid editedBy, DateTime editedAt)
|
||||
public async Task DeleteAdminWatchlist(int id, Guid deletedBy, DateTime deletedAt)
|
||||
{
|
||||
await using var db = await GetDb();
|
||||
|
||||
var note = await db.DbContext.AdminNotes.Where(note => note.Id == id).SingleAsync();
|
||||
note.Message = message;
|
||||
note.LastEditedById = editedBy;
|
||||
note.LastEditedAt = editedAt;
|
||||
var watchlist = await db.DbContext.AdminWatchlists.Where(note => note.Id == id).SingleAsync();
|
||||
|
||||
watchlist.Deleted = true;
|
||||
watchlist.DeletedById = deletedBy;
|
||||
watchlist.DeletedAt = deletedAt;
|
||||
|
||||
await db.DbContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task DeleteAdminMessage(int id, Guid deletedBy, DateTime deletedAt)
|
||||
{
|
||||
await using var db = await GetDb();
|
||||
|
||||
var message = await db.DbContext.AdminMessages.Where(note => note.Id == id).SingleAsync();
|
||||
|
||||
message.Deleted = true;
|
||||
message.DeletedById = deletedBy;
|
||||
message.DeletedAt = deletedAt;
|
||||
|
||||
await db.DbContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task HideServerBanFromNotes(int id, Guid deletedBy, DateTime deletedAt)
|
||||
{
|
||||
await using var db = await GetDb();
|
||||
|
||||
var ban = await db.DbContext.Ban.Where(ban => ban.Id == id).SingleAsync();
|
||||
|
||||
ban.Hidden = true;
|
||||
ban.LastEditedById = deletedBy;
|
||||
ban.LastEditedAt = deletedAt;
|
||||
|
||||
await db.DbContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task HideServerRoleBanFromNotes(int id, Guid deletedBy, DateTime deletedAt)
|
||||
{
|
||||
await using var db = await GetDb();
|
||||
|
||||
var roleBan = await db.DbContext.RoleBan.Where(roleBan => roleBan.Id == id).SingleAsync();
|
||||
|
||||
roleBan.Hidden = true;
|
||||
roleBan.LastEditedById = deletedBy;
|
||||
roleBan.LastEditedAt = deletedAt;
|
||||
|
||||
await db.DbContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task<List<IAdminRemarksCommon>> GetVisibleAdminRemarks(Guid player)
|
||||
{
|
||||
await using var db = await GetDb();
|
||||
List<IAdminRemarksCommon> notesCol = new();
|
||||
notesCol.AddRange(
|
||||
await (from note in db.DbContext.AdminNotes
|
||||
where note.PlayerUserId == player &&
|
||||
!note.Secret &&
|
||||
!note.Deleted &&
|
||||
(note.ExpirationTime == null || DateTime.UtcNow < note.ExpirationTime)
|
||||
select note)
|
||||
.Include(note => note.Round)
|
||||
.ThenInclude(r => r!.Server)
|
||||
.Include(note => note.CreatedBy)
|
||||
.Include(note => note.Player)
|
||||
.ToListAsync());
|
||||
notesCol.AddRange(await GetMessagesImpl(db, player));
|
||||
return notesCol;
|
||||
}
|
||||
|
||||
public async Task<List<AdminWatchlist>> GetActiveWatchlists(Guid player)
|
||||
{
|
||||
await using var db = await GetDb();
|
||||
return await GetActiveWatchlistsImpl(db, player);
|
||||
}
|
||||
|
||||
protected async Task<List<AdminWatchlist>> GetActiveWatchlistsImpl(DbGuard db, Guid player)
|
||||
{
|
||||
return await (from watchlist in db.DbContext.AdminWatchlists
|
||||
where watchlist.PlayerUserId == player &&
|
||||
!watchlist.Deleted &&
|
||||
(watchlist.ExpirationTime == null || DateTime.UtcNow < watchlist.ExpirationTime)
|
||||
select watchlist)
|
||||
.Include(note => note.Round)
|
||||
.ThenInclude(r => r!.Server)
|
||||
.Include(note => note.CreatedBy)
|
||||
.Include(note => note.LastEditedBy)
|
||||
.Include(note => note.Player)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<List<AdminMessage>> GetMessages(Guid player)
|
||||
{
|
||||
await using var db = await GetDb();
|
||||
return await GetMessagesImpl(db, player);
|
||||
}
|
||||
|
||||
protected async Task<List<AdminMessage>> GetMessagesImpl(DbGuard db, Guid player)
|
||||
{
|
||||
return await (from message in db.DbContext.AdminMessages
|
||||
where message.PlayerUserId == player &&
|
||||
!message.Deleted &&
|
||||
(message.ExpirationTime == null || DateTime.UtcNow < message.ExpirationTime)
|
||||
select message)
|
||||
.Include(note => note.Round)
|
||||
.ThenInclude(r => r!.Server)
|
||||
.Include(note => note.CreatedBy)
|
||||
.Include(note => note.LastEditedBy)
|
||||
.Include(note => note.Player)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task MarkMessageAsSeen(int id)
|
||||
{
|
||||
await using var db = await GetDb();
|
||||
var message = await db.DbContext.AdminMessages.SingleAsync(m => m.Id == id);
|
||||
message.Seen = true;
|
||||
await db.DbContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
// These two are here because they get converted into notes later
|
||||
protected async Task<List<ServerBanNote>> GetServerBansAsNotesForUser(DbGuard db, Guid user)
|
||||
{
|
||||
// You can't group queries, as player will not always exist. When it doesn't, the
|
||||
// whole query returns nothing
|
||||
var player = await db.DbContext.Player.SingleOrDefaultAsync(p => p.UserId == user);
|
||||
return await (from ban in db.DbContext.Ban
|
||||
where ban.PlayerUserId == user &&
|
||||
!ban.Hidden
|
||||
select ban)
|
||||
.Include(ban => ban.Unban)
|
||||
.Include(ban => ban.Round)
|
||||
.ThenInclude(r => r!.Server)
|
||||
.Include(ban => ban.CreatedBy)
|
||||
.Include(ban => ban.LastEditedBy)
|
||||
.Include(ban => ban.Unban)
|
||||
.ToAsyncEnumerable()
|
||||
.SelectAwait(async ban =>
|
||||
new ServerBanNote(ban.Id, ban.RoundId, ban.Round, ban.PlayerUserId, player,
|
||||
ban.PlaytimeAtNote, ban.Reason, ban.Severity, ban.CreatedBy, ban.BanTime,
|
||||
ban.LastEditedBy, ban.LastEditedAt, ban.ExpirationTime, ban.Hidden,
|
||||
ban.Unban?.UnbanningAdmin == null
|
||||
? null
|
||||
: await db.DbContext.Player.SingleOrDefaultAsync(p => p.UserId == ban.Unban.UnbanningAdmin.Value),
|
||||
ban.Unban?.UnbanTime)
|
||||
).ToListAsync();
|
||||
}
|
||||
|
||||
protected async Task<List<ServerRoleBanNote>> GetGroupedServerRoleBansAsNotesForUser(DbGuard db, Guid user)
|
||||
{
|
||||
// Server side query
|
||||
var bansQuery =
|
||||
(from ban in db.DbContext.RoleBan
|
||||
where ban.PlayerUserId == user &&
|
||||
!ban.Hidden
|
||||
select ban)
|
||||
.Include(ban => ban.Unban)
|
||||
.Include(ban => ban.Round)
|
||||
.ThenInclude(r => r!.Server)
|
||||
.Include(ban => ban.CreatedBy)
|
||||
.Include(ban => ban.LastEditedBy)
|
||||
.Include(ban => ban.Unban)
|
||||
.ToAsyncEnumerable();
|
||||
|
||||
// Client side query, as EF can't do groups yet
|
||||
var bansEnumerable =
|
||||
(from ban in bansQuery
|
||||
group ban by new
|
||||
{
|
||||
ban.BanTime,
|
||||
ban.CreatedBy,
|
||||
ban.Reason,
|
||||
Unbanned = ban.Unban == null
|
||||
}
|
||||
into banGroup
|
||||
select banGroup)
|
||||
.AsAsyncEnumerable();
|
||||
|
||||
List<ServerRoleBanNote> bans = new();
|
||||
var player = await db.DbContext.Player.SingleOrDefaultAsync(p => p.UserId == user);
|
||||
await foreach (var banGroup in bansEnumerable)
|
||||
{
|
||||
var firstBan = await banGroup.FirstAsync();
|
||||
Player? unbanningAdmin = null;
|
||||
if (firstBan.Unban?.UnbanningAdmin is not null)
|
||||
unbanningAdmin = await db.DbContext.Player.SingleOrDefaultAsync(p => p.UserId == firstBan.Unban.UnbanningAdmin.Value);
|
||||
bans.Add(new ServerRoleBanNote(firstBan.Id, firstBan.RoundId, firstBan.Round, firstBan.PlayerUserId,
|
||||
player, firstBan.PlaytimeAtNote, firstBan.Reason, firstBan.Severity, firstBan.CreatedBy,
|
||||
firstBan.BanTime, firstBan.LastEditedBy, firstBan.LastEditedAt, firstBan.ExpirationTime,
|
||||
firstBan.Hidden, await banGroup.Select(ban => ban.RoleId.Replace(BanManager.JobPrefix, null)).ToArrayAsync(),
|
||||
unbanningAdmin, firstBan.Unban?.UnbanTime));
|
||||
}
|
||||
|
||||
return bans;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
protected abstract Task<DbGuard> GetDb();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Collections.Immutable;
|
||||
using System.Collections.Immutable;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Text.Json;
|
||||
@@ -7,6 +7,7 @@ using System.Threading.Tasks;
|
||||
using Content.Server.Administration.Logs;
|
||||
using Content.Shared.Administration.Logs;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Preferences;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
@@ -87,6 +88,14 @@ namespace Content.Server.Database
|
||||
Task AddServerBanAsync(ServerBanDef serverBan);
|
||||
Task AddServerUnbanAsync(ServerUnbanDef serverBan);
|
||||
|
||||
public Task EditServerBan(
|
||||
int id,
|
||||
string reason,
|
||||
NoteSeverity severity,
|
||||
DateTime? expiration,
|
||||
Guid editedBy,
|
||||
DateTime editedAt);
|
||||
|
||||
/// <summary>
|
||||
/// Update ban exemption information for a player.
|
||||
/// </summary>
|
||||
@@ -132,6 +141,14 @@ namespace Content.Server.Database
|
||||
|
||||
Task AddServerRoleBanAsync(ServerRoleBanDef serverBan);
|
||||
Task AddServerRoleUnbanAsync(ServerRoleUnbanDef serverBan);
|
||||
|
||||
public Task EditServerRoleBan(
|
||||
int id,
|
||||
string reason,
|
||||
NoteSeverity severity,
|
||||
DateTime? expiration,
|
||||
Guid editedBy,
|
||||
DateTime editedAt);
|
||||
#endregion
|
||||
|
||||
#region Playtime
|
||||
@@ -236,11 +253,27 @@ namespace Content.Server.Database
|
||||
|
||||
#region Admin Notes
|
||||
|
||||
Task<int> AddAdminNote(int? roundId, Guid player, string message, Guid createdBy, DateTime createdAt);
|
||||
Task<int> AddAdminNote(int? roundId, Guid player, TimeSpan playtimeAtNote, string message, NoteSeverity severity, bool secret, Guid createdBy, DateTime createdAt, DateTime? expiryTime);
|
||||
Task<int> AddAdminWatchlist(int? roundId, Guid player, TimeSpan playtimeAtNote, string message, Guid createdBy, DateTime createdAt, DateTime? expiryTime);
|
||||
Task<int> AddAdminMessage(int? roundId, Guid player, TimeSpan playtimeAtNote, string message, Guid createdBy, DateTime createdAt, DateTime? expiryTime);
|
||||
Task<AdminNote?> GetAdminNote(int id);
|
||||
Task<List<AdminNote>> GetAdminNotes(Guid player);
|
||||
Task<AdminWatchlist?> GetAdminWatchlist(int id);
|
||||
Task<AdminMessage?> GetAdminMessage(int id);
|
||||
Task<ServerBanNote?> GetServerBanAsNoteAsync(int id);
|
||||
Task<ServerRoleBanNote?> GetServerRoleBanAsNoteAsync(int id);
|
||||
Task<List<IAdminRemarksCommon>> GetAllAdminRemarks(Guid player);
|
||||
Task<List<IAdminRemarksCommon>> GetVisibleAdminNotes(Guid player);
|
||||
Task<List<AdminWatchlist>> GetActiveWatchlists(Guid player);
|
||||
Task<List<AdminMessage>> GetMessages(Guid player);
|
||||
Task EditAdminNote(int id, string message, NoteSeverity severity, bool secret, Guid editedBy, DateTime editedAt, DateTime? expiryTime);
|
||||
Task EditAdminWatchlist(int id, string message, Guid editedBy, DateTime editedAt, DateTime? expiryTime);
|
||||
Task EditAdminMessage(int id, string message, Guid editedBy, DateTime editedAt, DateTime? expiryTime);
|
||||
Task DeleteAdminNote(int id, Guid deletedBy, DateTime deletedAt);
|
||||
Task EditAdminNote(int id, string message, Guid editedBy, DateTime editedAt);
|
||||
Task DeleteAdminWatchlist(int id, Guid deletedBy, DateTime deletedAt);
|
||||
Task DeleteAdminMessage(int id, Guid deletedBy, DateTime deletedAt);
|
||||
Task HideServerBanFromNotes(int id, Guid deletedBy, DateTime deletedAt);
|
||||
Task HideServerRoleBanFromNotes(int id, Guid deletedBy, DateTime deletedAt);
|
||||
Task MarkMessageAsSeen(int id);
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -384,16 +417,22 @@ namespace Content.Server.Database
|
||||
return RunDbCommand(() => _db.AddServerUnbanAsync(serverUnban));
|
||||
}
|
||||
|
||||
public Task EditServerBan(int id, string reason, NoteSeverity severity, DateTime? expiration, Guid editedBy, DateTime editedAt)
|
||||
{
|
||||
DbWriteOpsMetric.Inc();
|
||||
return RunDbCommand(() => _db.EditServerBan(id, reason, severity, expiration, editedBy, editedAt));
|
||||
}
|
||||
|
||||
public Task UpdateBanExemption(NetUserId userId, ServerBanExemptFlags flags)
|
||||
{
|
||||
DbWriteOpsMetric.Inc();
|
||||
return _db.UpdateBanExemption(userId, flags);
|
||||
return RunDbCommand(() => _db.UpdateBanExemption(userId, flags));
|
||||
}
|
||||
|
||||
public Task<ServerBanExemptFlags> GetBanExemption(NetUserId userId)
|
||||
{
|
||||
DbReadOpsMetric.Inc();
|
||||
return _db.GetBanExemption(userId);
|
||||
return RunDbCommand(() => _db.GetBanExemption(userId));
|
||||
}
|
||||
|
||||
#region Role Ban
|
||||
@@ -424,6 +463,12 @@ namespace Content.Server.Database
|
||||
DbWriteOpsMetric.Inc();
|
||||
return RunDbCommand(() => _db.AddServerRoleUnbanAsync(serverRoleUnban));
|
||||
}
|
||||
|
||||
public Task EditServerRoleBan(int id, string reason, NoteSeverity severity, DateTime? expiration, Guid editedBy, DateTime editedAt)
|
||||
{
|
||||
DbWriteOpsMetric.Inc();
|
||||
return RunDbCommand(() => _db.EditServerRoleBan(id, reason, severity, expiration, editedBy, editedAt));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Playtime
|
||||
@@ -637,7 +682,7 @@ namespace Content.Server.Database
|
||||
return RunDbCommand(() => _db.SetLastReadRules(player, time));
|
||||
}
|
||||
|
||||
public Task<int> AddAdminNote(int? roundId, Guid player, string message, Guid createdBy, DateTime createdAt)
|
||||
public Task<int> AddAdminNote(int? roundId, Guid player, TimeSpan playtimeAtNote, string message, NoteSeverity severity, bool secret, Guid createdBy, DateTime createdAt, DateTime? expiryTime)
|
||||
{
|
||||
DbWriteOpsMetric.Inc();
|
||||
var note = new AdminNote
|
||||
@@ -646,24 +691,123 @@ namespace Content.Server.Database
|
||||
CreatedById = createdBy,
|
||||
LastEditedById = createdBy,
|
||||
PlayerUserId = player,
|
||||
PlaytimeAtNote = playtimeAtNote,
|
||||
Message = message,
|
||||
Severity = severity,
|
||||
Secret = secret,
|
||||
CreatedAt = createdAt,
|
||||
LastEditedAt = createdAt
|
||||
LastEditedAt = createdAt,
|
||||
ExpirationTime = expiryTime
|
||||
};
|
||||
|
||||
return RunDbCommand(() => _db.AddAdminNote(note));
|
||||
}
|
||||
|
||||
public Task<int> AddAdminWatchlist(int? roundId, Guid player, TimeSpan playtimeAtNote, string message, Guid createdBy, DateTime createdAt, DateTime? expiryTime)
|
||||
{
|
||||
DbWriteOpsMetric.Inc();
|
||||
var note = new AdminWatchlist
|
||||
{
|
||||
RoundId = roundId,
|
||||
CreatedById = createdBy,
|
||||
LastEditedById = createdBy,
|
||||
PlayerUserId = player,
|
||||
PlaytimeAtNote = playtimeAtNote,
|
||||
Message = message,
|
||||
CreatedAt = createdAt,
|
||||
LastEditedAt = createdAt,
|
||||
ExpirationTime = expiryTime
|
||||
};
|
||||
|
||||
return RunDbCommand(() => _db.AddAdminWatchlist(note));
|
||||
}
|
||||
|
||||
public Task<int> AddAdminMessage(int? roundId, Guid player, TimeSpan playtimeAtNote, string message, Guid createdBy, DateTime createdAt, DateTime? expiryTime)
|
||||
{
|
||||
DbWriteOpsMetric.Inc();
|
||||
var note = new AdminMessage
|
||||
{
|
||||
RoundId = roundId,
|
||||
CreatedById = createdBy,
|
||||
LastEditedById = createdBy,
|
||||
PlayerUserId = player,
|
||||
PlaytimeAtNote = playtimeAtNote,
|
||||
Message = message,
|
||||
CreatedAt = createdAt,
|
||||
LastEditedAt = createdAt,
|
||||
ExpirationTime = expiryTime
|
||||
};
|
||||
|
||||
return RunDbCommand(() => _db.AddAdminMessage(note));
|
||||
}
|
||||
|
||||
public Task<AdminNote?> GetAdminNote(int id)
|
||||
{
|
||||
DbReadOpsMetric.Inc();
|
||||
return RunDbCommand(() => _db.GetAdminNote(id));
|
||||
}
|
||||
|
||||
public Task<List<AdminNote>> GetAdminNotes(Guid player)
|
||||
public Task<AdminWatchlist?> GetAdminWatchlist(int id)
|
||||
{
|
||||
DbReadOpsMetric.Inc();
|
||||
return RunDbCommand(() => _db.GetAdminNotes(player));
|
||||
return RunDbCommand(() => _db.GetAdminWatchlist(id));
|
||||
}
|
||||
public Task<AdminMessage?> GetAdminMessage(int id)
|
||||
{
|
||||
DbReadOpsMetric.Inc();
|
||||
return RunDbCommand(() => _db.GetAdminMessage(id));
|
||||
}
|
||||
|
||||
public Task<ServerBanNote?> GetServerBanAsNoteAsync(int id)
|
||||
{
|
||||
DbReadOpsMetric.Inc();
|
||||
return RunDbCommand(() => _db.GetServerBanAsNoteAsync(id));
|
||||
}
|
||||
|
||||
public Task<ServerRoleBanNote?> GetServerRoleBanAsNoteAsync(int id)
|
||||
{
|
||||
DbReadOpsMetric.Inc();
|
||||
return RunDbCommand(() => _db.GetServerRoleBanAsNoteAsync(id));
|
||||
}
|
||||
|
||||
public Task<List<IAdminRemarksCommon>> GetAllAdminRemarks(Guid player)
|
||||
{
|
||||
DbReadOpsMetric.Inc();
|
||||
return RunDbCommand(() => _db.GetAllAdminRemarks(player));
|
||||
}
|
||||
|
||||
public Task<List<IAdminRemarksCommon>> GetVisibleAdminNotes(Guid player)
|
||||
{
|
||||
DbReadOpsMetric.Inc();
|
||||
return RunDbCommand(() => _db.GetVisibleAdminRemarks(player));
|
||||
}
|
||||
|
||||
public Task<List<AdminWatchlist>> GetActiveWatchlists(Guid player)
|
||||
{
|
||||
DbReadOpsMetric.Inc();
|
||||
return RunDbCommand(() => _db.GetActiveWatchlists(player));
|
||||
}
|
||||
|
||||
public Task<List<AdminMessage>> GetMessages(Guid player)
|
||||
{
|
||||
DbReadOpsMetric.Inc();
|
||||
return RunDbCommand(() => _db.GetMessages(player));
|
||||
}
|
||||
public Task EditAdminNote(int id, string message, NoteSeverity severity, bool secret, Guid editedBy, DateTime editedAt, DateTime? expiryTime)
|
||||
{
|
||||
DbWriteOpsMetric.Inc();
|
||||
return RunDbCommand(() => _db.EditAdminNote(id, message, severity, secret, editedBy, editedAt, expiryTime));
|
||||
}
|
||||
|
||||
public Task EditAdminWatchlist(int id, string message, Guid editedBy, DateTime editedAt, DateTime? expiryTime)
|
||||
{
|
||||
DbWriteOpsMetric.Inc();
|
||||
return RunDbCommand(() => _db.EditAdminWatchlist(id, message, editedBy, editedAt, expiryTime));
|
||||
}
|
||||
|
||||
public Task EditAdminMessage(int id, string message, Guid editedBy, DateTime editedAt, DateTime? expiryTime)
|
||||
{
|
||||
DbWriteOpsMetric.Inc();
|
||||
return RunDbCommand(() => _db.EditAdminMessage(id, message, editedBy, editedAt, expiryTime));
|
||||
}
|
||||
|
||||
public Task DeleteAdminNote(int id, Guid deletedBy, DateTime deletedAt)
|
||||
@@ -672,10 +816,34 @@ namespace Content.Server.Database
|
||||
return RunDbCommand(() => _db.DeleteAdminNote(id, deletedBy, deletedAt));
|
||||
}
|
||||
|
||||
public Task EditAdminNote(int id, string message, Guid editedBy, DateTime editedAt)
|
||||
public Task DeleteAdminWatchlist(int id, Guid deletedBy, DateTime deletedAt)
|
||||
{
|
||||
DbWriteOpsMetric.Inc();
|
||||
return RunDbCommand(() => _db.EditAdminNote(id, message, editedBy, editedAt));
|
||||
return RunDbCommand(() => _db.DeleteAdminWatchlist(id, deletedBy, deletedAt));
|
||||
}
|
||||
|
||||
public Task DeleteAdminMessage(int id, Guid deletedBy, DateTime deletedAt)
|
||||
{
|
||||
DbWriteOpsMetric.Inc();
|
||||
return RunDbCommand(() => _db.DeleteAdminMessage(id, deletedBy, deletedAt));
|
||||
}
|
||||
|
||||
public Task HideServerBanFromNotes(int id, Guid deletedBy, DateTime deletedAt)
|
||||
{
|
||||
DbWriteOpsMetric.Inc();
|
||||
return RunDbCommand(() => _db.HideServerBanFromNotes(id, deletedBy, deletedAt));
|
||||
}
|
||||
|
||||
public Task HideServerRoleBanFromNotes(int id, Guid deletedBy, DateTime deletedAt)
|
||||
{
|
||||
DbWriteOpsMetric.Inc();
|
||||
return RunDbCommand(() => _db.HideServerRoleBanFromNotes(id, deletedBy, deletedAt));
|
||||
}
|
||||
|
||||
public Task MarkMessageAsSeen(int id)
|
||||
{
|
||||
DbWriteOpsMetric.Inc();
|
||||
return RunDbCommand(() => _db.MarkMessageAsSeen(id));
|
||||
}
|
||||
|
||||
// Wrapper functions to run DB commands from the thread pool.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Collections.Immutable;
|
||||
using System.Collections.Immutable;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
@@ -120,7 +120,7 @@ namespace Content.Server.Database
|
||||
{
|
||||
var newQ = db.PgDbContext.Ban
|
||||
.Include(p => p.Unban)
|
||||
.Where(b => b.UserId == uid.UserId);
|
||||
.Where(b => b.PlayerUserId == uid.UserId);
|
||||
|
||||
query = query == null ? newQ : query.Union(newQ);
|
||||
}
|
||||
@@ -169,7 +169,7 @@ namespace Content.Server.Database
|
||||
}
|
||||
|
||||
NetUserId? uid = null;
|
||||
if (ban.UserId is {} guid)
|
||||
if (ban.PlayerUserId is {} guid)
|
||||
{
|
||||
uid = new NetUserId(guid);
|
||||
}
|
||||
@@ -189,7 +189,10 @@ namespace Content.Server.Database
|
||||
ban.HWId == null ? null : ImmutableArray.Create(ban.HWId),
|
||||
ban.BanTime,
|
||||
ban.ExpirationTime,
|
||||
ban.RoundId,
|
||||
ban.PlaytimeAtNote,
|
||||
ban.Reason,
|
||||
ban.Severity,
|
||||
aUid,
|
||||
unbanDef);
|
||||
}
|
||||
@@ -222,10 +225,13 @@ namespace Content.Server.Database
|
||||
Address = serverBan.Address,
|
||||
HWId = serverBan.HWId?.ToArray(),
|
||||
Reason = serverBan.Reason,
|
||||
Severity = serverBan.Severity,
|
||||
BanningAdmin = serverBan.BanningAdmin?.UserId,
|
||||
BanTime = serverBan.BanTime.UtcDateTime,
|
||||
ExpirationTime = serverBan.ExpirationTime?.UtcDateTime,
|
||||
UserId = serverBan.UserId?.UserId
|
||||
RoundId = serverBan.RoundId,
|
||||
PlaytimeAtNote = serverBan.PlaytimeAtNote,
|
||||
PlayerUserId = serverBan.UserId?.UserId
|
||||
});
|
||||
|
||||
await db.PgDbContext.SaveChangesAsync();
|
||||
@@ -310,7 +316,7 @@ namespace Content.Server.Database
|
||||
{
|
||||
var newQ = db.PgDbContext.RoleBan
|
||||
.Include(p => p.Unban)
|
||||
.Where(b => b.UserId == uid.UserId);
|
||||
.Where(b => b.PlayerUserId == uid.UserId);
|
||||
|
||||
query = query == null ? newQ : query.Union(newQ);
|
||||
}
|
||||
@@ -351,7 +357,7 @@ namespace Content.Server.Database
|
||||
}
|
||||
|
||||
NetUserId? uid = null;
|
||||
if (ban.UserId is {} guid)
|
||||
if (ban.PlayerUserId is {} guid)
|
||||
{
|
||||
uid = new NetUserId(guid);
|
||||
}
|
||||
@@ -371,7 +377,10 @@ namespace Content.Server.Database
|
||||
ban.HWId == null ? null : ImmutableArray.Create(ban.HWId),
|
||||
ban.BanTime,
|
||||
ban.ExpirationTime,
|
||||
ban.RoundId,
|
||||
ban.PlaytimeAtNote,
|
||||
ban.Reason,
|
||||
ban.Severity,
|
||||
aUid,
|
||||
unbanDef,
|
||||
ban.RoleId);
|
||||
@@ -405,10 +414,13 @@ namespace Content.Server.Database
|
||||
Address = serverRoleBan.Address,
|
||||
HWId = serverRoleBan.HWId?.ToArray(),
|
||||
Reason = serverRoleBan.Reason,
|
||||
Severity = serverRoleBan.Severity,
|
||||
BanningAdmin = serverRoleBan.BanningAdmin?.UserId,
|
||||
BanTime = serverRoleBan.BanTime.UtcDateTime,
|
||||
ExpirationTime = serverRoleBan.ExpirationTime?.UtcDateTime,
|
||||
UserId = serverRoleBan.UserId?.UserId,
|
||||
RoundId = serverRoleBan.RoundId,
|
||||
PlaytimeAtNote = serverRoleBan.PlaytimeAtNote,
|
||||
PlayerUserId = serverRoleBan.UserId?.UserId,
|
||||
RoleId = serverRoleBan.Role,
|
||||
});
|
||||
|
||||
|
||||
@@ -133,22 +133,17 @@ namespace Content.Server.Database
|
||||
ServerBanExemptFlags? exemptFlags)
|
||||
{
|
||||
if (!exemptFlags.GetValueOrDefault(ServerBanExemptFlags.None).HasFlag(ServerBanExemptFlags.IP)
|
||||
&& address != null && ban.Address is not null && IPAddressExt.IsInSubnet(address, ban.Address.Value))
|
||||
&& address != null && ban.Address is not null && address.IsInSubnet(ban.Address.Value))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (userId is { } id && ban.UserId == id.UserId)
|
||||
if (userId is { } id && ban.PlayerUserId == id.UserId)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (hwId is { } hwIdVar && hwIdVar.Length > 0 && hwIdVar.AsSpan().SequenceEqual(ban.HWId))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return hwId is { Length: > 0 } hwIdVar && hwIdVar.AsSpan().SequenceEqual(ban.HWId);
|
||||
}
|
||||
|
||||
public override async Task AddServerBanAsync(ServerBanDef serverBan)
|
||||
@@ -159,11 +154,14 @@ namespace Content.Server.Database
|
||||
{
|
||||
Address = serverBan.Address,
|
||||
Reason = serverBan.Reason,
|
||||
Severity = serverBan.Severity,
|
||||
BanningAdmin = serverBan.BanningAdmin?.UserId,
|
||||
HWId = serverBan.HWId?.ToArray(),
|
||||
BanTime = serverBan.BanTime.UtcDateTime,
|
||||
ExpirationTime = serverBan.ExpirationTime?.UtcDateTime,
|
||||
UserId = serverBan.UserId?.UserId
|
||||
RoundId = serverBan.RoundId,
|
||||
PlaytimeAtNote = serverBan.PlaytimeAtNote,
|
||||
PlayerUserId = serverBan.UserId?.UserId
|
||||
});
|
||||
|
||||
await db.SqliteDbContext.SaveChangesAsync();
|
||||
@@ -197,7 +195,8 @@ namespace Content.Server.Database
|
||||
return ConvertRoleBan(ban);
|
||||
}
|
||||
|
||||
public override async Task<List<ServerRoleBanDef>> GetServerRoleBansAsync(IPAddress? address,
|
||||
public override async Task<List<ServerRoleBanDef>> GetServerRoleBansAsync(
|
||||
IPAddress? address,
|
||||
NetUserId? userId,
|
||||
ImmutableArray<byte>? hwId,
|
||||
bool includeUnbanned)
|
||||
@@ -234,22 +233,17 @@ namespace Content.Server.Database
|
||||
NetUserId? userId,
|
||||
ImmutableArray<byte>? hwId)
|
||||
{
|
||||
if (address != null && ban.Address is not null && IPAddressExt.IsInSubnet(address, ban.Address.Value))
|
||||
if (address != null && ban.Address is not null && address.IsInSubnet(ban.Address.Value))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (userId is { } id && ban.UserId == id.UserId)
|
||||
if (userId is { } id && ban.PlayerUserId == id.UserId)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (hwId is { } hwIdVar && hwIdVar.Length > 0 && hwIdVar.AsSpan().SequenceEqual(ban.HWId))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return hwId is { Length: > 0 } hwIdVar && hwIdVar.AsSpan().SequenceEqual(ban.HWId);
|
||||
}
|
||||
|
||||
public override async Task AddServerRoleBanAsync(ServerRoleBanDef serverBan)
|
||||
@@ -260,11 +254,14 @@ namespace Content.Server.Database
|
||||
{
|
||||
Address = serverBan.Address,
|
||||
Reason = serverBan.Reason,
|
||||
Severity = serverBan.Severity,
|
||||
BanningAdmin = serverBan.BanningAdmin?.UserId,
|
||||
HWId = serverBan.HWId?.ToArray(),
|
||||
BanTime = serverBan.BanTime.UtcDateTime,
|
||||
ExpirationTime = serverBan.ExpirationTime?.UtcDateTime,
|
||||
UserId = serverBan.UserId?.UserId,
|
||||
RoundId = serverBan.RoundId,
|
||||
PlaytimeAtNote = serverBan.PlaytimeAtNote,
|
||||
PlayerUserId = serverBan.UserId?.UserId,
|
||||
RoleId = serverBan.Role,
|
||||
});
|
||||
|
||||
@@ -293,7 +290,7 @@ namespace Content.Server.Database
|
||||
}
|
||||
|
||||
NetUserId? uid = null;
|
||||
if (ban.UserId is { } guid)
|
||||
if (ban.PlayerUserId is { } guid)
|
||||
{
|
||||
uid = new NetUserId(guid);
|
||||
}
|
||||
@@ -314,7 +311,10 @@ namespace Content.Server.Database
|
||||
// SQLite apparently always reads DateTime as unspecified, but we always write as UTC.
|
||||
DateTime.SpecifyKind(ban.BanTime, DateTimeKind.Utc),
|
||||
ban.ExpirationTime == null ? null : DateTime.SpecifyKind(ban.ExpirationTime.Value, DateTimeKind.Utc),
|
||||
ban.RoundId,
|
||||
ban.PlaytimeAtNote,
|
||||
ban.Reason,
|
||||
ban.Severity,
|
||||
aUid,
|
||||
unban,
|
||||
ban.RoleId);
|
||||
@@ -360,7 +360,7 @@ namespace Content.Server.Database
|
||||
}
|
||||
|
||||
NetUserId? uid = null;
|
||||
if (ban.UserId is { } guid)
|
||||
if (ban.PlayerUserId is { } guid)
|
||||
{
|
||||
uid = new NetUserId(guid);
|
||||
}
|
||||
@@ -381,7 +381,10 @@ namespace Content.Server.Database
|
||||
// SQLite apparently always reads DateTime as unspecified, but we always write as UTC.
|
||||
DateTime.SpecifyKind(ban.BanTime, DateTimeKind.Utc),
|
||||
ban.ExpirationTime == null ? null : DateTime.SpecifyKind(ban.ExpirationTime.Value, DateTimeKind.Utc),
|
||||
ban.RoundId,
|
||||
ban.PlaytimeAtNote,
|
||||
ban.Reason,
|
||||
ban.Severity,
|
||||
aUid,
|
||||
unban);
|
||||
}
|
||||
@@ -483,7 +486,7 @@ namespace Content.Server.Database
|
||||
var nextId = 1;
|
||||
if (await db.DbContext.AdminNotes.AnyAsync())
|
||||
{
|
||||
nextId = await db.DbContext.AdminNotes.MaxAsync(dbVersion => dbVersion.Id) + 1;
|
||||
nextId = await db.DbContext.AdminNotes.MaxAsync(adminNote => adminNote.Id) + 1;
|
||||
}
|
||||
|
||||
note.Id = nextId;
|
||||
@@ -491,6 +494,37 @@ namespace Content.Server.Database
|
||||
|
||||
return await base.AddAdminNote(note);
|
||||
}
|
||||
public override async Task<int> AddAdminWatchlist(AdminWatchlist watchlist)
|
||||
{
|
||||
await using (var db = await GetDb())
|
||||
{
|
||||
var nextId = 1;
|
||||
if (await db.DbContext.AdminWatchlists.AnyAsync())
|
||||
{
|
||||
nextId = await db.DbContext.AdminWatchlists.MaxAsync(adminWatchlist => adminWatchlist.Id) + 1;
|
||||
}
|
||||
|
||||
watchlist.Id = nextId;
|
||||
}
|
||||
|
||||
return await base.AddAdminWatchlist(watchlist);
|
||||
}
|
||||
|
||||
public override async Task<int> AddAdminMessage(AdminMessage message)
|
||||
{
|
||||
await using (var db = await GetDb())
|
||||
{
|
||||
var nextId = 1;
|
||||
if (await db.DbContext.AdminMessages.AnyAsync())
|
||||
{
|
||||
nextId = await db.DbContext.AdminMessages.MaxAsync(adminMessage => adminMessage.Id) + 1;
|
||||
}
|
||||
|
||||
message.Id = nextId;
|
||||
}
|
||||
|
||||
return await base.AddAdminMessage(message);
|
||||
}
|
||||
|
||||
private async Task<DbGuardImpl> GetDbImpl()
|
||||
{
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Collections.Immutable;
|
||||
using System.Collections.Immutable;
|
||||
using System.Net;
|
||||
using Content.Shared.Database;
|
||||
using Robust.Shared.Network;
|
||||
|
||||
namespace Content.Server.Database;
|
||||
@@ -13,7 +14,10 @@ public sealed class ServerRoleBanDef
|
||||
|
||||
public DateTimeOffset BanTime { get; }
|
||||
public DateTimeOffset? ExpirationTime { get; }
|
||||
public int? RoundId { get; }
|
||||
public TimeSpan PlaytimeAtNote { get; }
|
||||
public string Reason { get; }
|
||||
public NoteSeverity Severity { get; set; }
|
||||
public NetUserId? BanningAdmin { get; }
|
||||
public ServerRoleUnbanDef? Unban { get; }
|
||||
public string Role { get; }
|
||||
@@ -25,7 +29,10 @@ public sealed class ServerRoleBanDef
|
||||
ImmutableArray<byte>? hwId,
|
||||
DateTimeOffset banTime,
|
||||
DateTimeOffset? expirationTime,
|
||||
int? roundId,
|
||||
TimeSpan playtimeAtNote,
|
||||
string reason,
|
||||
NoteSeverity severity,
|
||||
NetUserId? banningAdmin,
|
||||
ServerRoleUnbanDef? unban,
|
||||
string role)
|
||||
@@ -48,7 +55,10 @@ public sealed class ServerRoleBanDef
|
||||
HWId = hwId;
|
||||
BanTime = banTime;
|
||||
ExpirationTime = expirationTime;
|
||||
RoundId = roundId;
|
||||
PlaytimeAtNote = playtimeAtNote;
|
||||
Reason = reason;
|
||||
Severity = severity;
|
||||
BanningAdmin = banningAdmin;
|
||||
Unban = unban;
|
||||
Role = role;
|
||||
|
||||
14
Content.Server/Database/ServerRoleBanNote.cs
Normal file
14
Content.Server/Database/ServerRoleBanNote.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Shared.Database;
|
||||
|
||||
namespace Content.Server.Database
|
||||
{
|
||||
public record ServerRoleBanNote(int Id, int? RoundId, Round? Round, Guid? PlayerUserId, Player? Player,
|
||||
TimeSpan PlaytimeAtNote, string Message, NoteSeverity Severity, Player? CreatedBy, DateTime CreatedAt,
|
||||
Player? LastEditedBy, DateTime? LastEditedAt, DateTime? ExpirationTime, bool Deleted, string[] Roles,
|
||||
Player? UnbanningAdmin, DateTime? UnbanTime) : IAdminRemarksCommon;
|
||||
}
|
||||
@@ -146,7 +146,7 @@ namespace Content.Server.Entry
|
||||
IoCManager.Resolve<IGameMapManager>().Initialize();
|
||||
IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<GameTicker>().PostInitialize();
|
||||
IoCManager.Resolve<IBqlQueryManager>().DoAutoRegistrations();
|
||||
IoCManager.Resolve<RoleBanManager>().Initialize();
|
||||
IoCManager.Resolve<IBanManager>().Initialize();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -198,7 +198,7 @@ namespace Content.Server.GameTicking
|
||||
#if DEBUG
|
||||
DebugTools.Assert(_userDb.IsLoadComplete(session), $"Player was readied up but didn't have user DB data loaded yet??");
|
||||
#endif
|
||||
if (_roleBanManager.GetRoleBans(userId) == null)
|
||||
if (_banManager.GetRoleBans(userId) == null)
|
||||
{
|
||||
Logger.ErrorS("RoleBans", $"Role bans for player {session} {userId} have not been loaded yet.");
|
||||
continue;
|
||||
@@ -437,7 +437,7 @@ namespace Content.Server.GameTicking
|
||||
|
||||
_mapManager.Restart();
|
||||
|
||||
_roleBanManager.Restart();
|
||||
_banManager.Restart();
|
||||
|
||||
_gameMapManager.ClearSelectedMap();
|
||||
|
||||
|
||||
@@ -102,7 +102,7 @@ namespace Content.Server.GameTicking
|
||||
{
|
||||
var character = GetPlayerProfile(player);
|
||||
|
||||
var jobBans = _roleBanManager.GetJobBans(player.UserId);
|
||||
var jobBans = _banManager.GetJobBans(player.UserId);
|
||||
if (jobBans == null || jobId != null && jobBans.Contains(jobId))
|
||||
return;
|
||||
|
||||
@@ -150,7 +150,7 @@ namespace Content.Server.GameTicking
|
||||
var getDisallowed = _playTimeTrackings.GetDisallowedJobs(player);
|
||||
restrictedRoles.UnionWith(getDisallowed);
|
||||
|
||||
var jobBans = _roleBanManager.GetJobBans(player.UserId);
|
||||
var jobBans = _banManager.GetJobBans(player.UserId);
|
||||
if(jobBans != null) restrictedRoles.UnionWith(jobBans);
|
||||
|
||||
// Pick best job best on prefs.
|
||||
|
||||
@@ -119,7 +119,7 @@ namespace Content.Server.GameTicking
|
||||
[Dependency] private readonly StationJobsSystem _stationJobs = default!;
|
||||
[Dependency] private readonly DamageableSystem _damageable = default!;
|
||||
[Dependency] private readonly GhostSystem _ghosts = default!;
|
||||
[Dependency] private readonly RoleBanManager _roleBanManager = default!;
|
||||
[Dependency] private readonly IBanManager _banManager = default!;
|
||||
[Dependency] private readonly ChatSystem _chatSystem = default!;
|
||||
[Dependency] private readonly ServerUpdateManager _serverUpdates = default!;
|
||||
[Dependency] private readonly PlayTimeTrackingSystem _playTimeTrackings = default!;
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace Content.Server.IoC
|
||||
IoCManager.Register<IAfkManager, AfkManager>();
|
||||
IoCManager.Register<IGameMapManager, GameMapManager>();
|
||||
IoCManager.Register<RulesManager, RulesManager>();
|
||||
IoCManager.Register<RoleBanManager, RoleBanManager>();
|
||||
IoCManager.Register<IBanManager, BanManager>();
|
||||
IoCManager.Register<ContentNetworkResourceManager>();
|
||||
IoCManager.Register<IAdminNotesManager, AdminNotesManager>();
|
||||
IoCManager.Register<GhostKickManager>();
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace Content.Server.Station.Systems;
|
||||
public sealed partial class StationJobsSystem
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly RoleBanManager _roleBanManager = default!;
|
||||
[Dependency] private readonly IBanManager _banManager = default!;
|
||||
[Dependency] private readonly PlayTimeTrackingSystem _playTime = default!;
|
||||
|
||||
private Dictionary<int, HashSet<string>> _jobsByWeight = default!;
|
||||
@@ -342,7 +342,7 @@ public sealed partial class StationJobsSystem
|
||||
|
||||
foreach (var (player, profile) in profiles)
|
||||
{
|
||||
var roleBans = _roleBanManager.GetJobBans(player);
|
||||
var roleBans = _banManager.GetJobBans(player);
|
||||
var profileJobs = profile.JobPriorities.Keys.ToList();
|
||||
_playTime.RemoveDisallowedJobs(player, ref profileJobs);
|
||||
|
||||
|
||||
28
Content.Shared.Database/NoteSeverity.cs
Normal file
28
Content.Shared.Database/NoteSeverity.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
namespace Content.Shared.Database
|
||||
{
|
||||
/// <summary>
|
||||
/// Kinds of severity that a note can have
|
||||
/// </summary>
|
||||
public enum NoteSeverity
|
||||
{
|
||||
/// <summary>
|
||||
/// No severity, displays a checkmark
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Minor severity, displays a minus
|
||||
/// </summary>
|
||||
Minor = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Medium severity, displays one exclamation mark
|
||||
/// </summary>
|
||||
Medium = 2,
|
||||
|
||||
/// <summary>
|
||||
/// High severity, displays three exclamation marks
|
||||
/// </summary>
|
||||
High = 3,
|
||||
}
|
||||
}
|
||||
38
Content.Shared.Database/NoteType.cs
Normal file
38
Content.Shared.Database/NoteType.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
namespace Content.Shared.Database;
|
||||
|
||||
/*
|
||||
* Editing the numbers here may obliterate DB records, you have been warned.
|
||||
* If you do have to edit the numbers for some reason, please create migrations.
|
||||
* Adding new types is fine (or even renaming), but do not remove or change them.
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// Different types of notes
|
||||
/// </summary>
|
||||
public enum NoteType
|
||||
{
|
||||
/// <summary>
|
||||
/// Normal note
|
||||
/// </summary>
|
||||
Note = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Watchlist, a secret note that gets shown to online admins every time a player connects
|
||||
/// </summary>
|
||||
Watchlist = 1,
|
||||
|
||||
/// <summary>
|
||||
/// A message, type of note that gets explicitly shown to the player
|
||||
/// </summary>
|
||||
Message = 2,
|
||||
|
||||
/// <summary>
|
||||
/// A server ban, converted to a shared note
|
||||
/// </summary>
|
||||
ServerBan = 3,
|
||||
|
||||
/// <summary>
|
||||
/// A role ban, converted to a shared note
|
||||
/// </summary>
|
||||
RoleBan = 4,
|
||||
}
|
||||
61
Content.Shared/Administration/BanPanelEuiState.cs
Normal file
61
Content.Shared/Administration/BanPanelEuiState.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
using System.Collections.Immutable;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Eui;
|
||||
using Robust.Shared.Serialization;
|
||||
using System.Net;
|
||||
|
||||
namespace Content.Shared.Administration;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class BanPanelEuiState : EuiStateBase
|
||||
{
|
||||
public string PlayerName { get; set; }
|
||||
public bool HasBan { get; set; }
|
||||
|
||||
public BanPanelEuiState(string playerName, bool hasBan)
|
||||
{
|
||||
PlayerName = playerName;
|
||||
HasBan = hasBan;
|
||||
}
|
||||
}
|
||||
|
||||
public static class BanPanelEuiStateMsg
|
||||
{
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class CreateBanRequest : EuiMessageBase
|
||||
{
|
||||
public string? Player { get; set; }
|
||||
public string? IpAddress { get; set; }
|
||||
public byte[]? Hwid { get; set; }
|
||||
public uint Minutes { get; set; }
|
||||
public string Reason { get; set; }
|
||||
public NoteSeverity Severity { get; set; }
|
||||
public string[]? Roles { get; set; }
|
||||
public bool UseLastIp { get; set; }
|
||||
public bool UseLastHwid { get; set; }
|
||||
|
||||
public CreateBanRequest(string? player, (IPAddress, int)? ipAddress, bool useLastIp, byte[]? hwid, bool useLastHwid, uint minutes, string reason, NoteSeverity severity, string[]? roles)
|
||||
{
|
||||
Player = player;
|
||||
IpAddress = ipAddress == null ? null : $"{ipAddress.Value.Item1}/{ipAddress.Value.Item2}";
|
||||
UseLastIp = useLastIp;
|
||||
Hwid = hwid;
|
||||
UseLastHwid = useLastHwid;
|
||||
Minutes = minutes;
|
||||
Reason = reason;
|
||||
Severity = severity;
|
||||
Roles = roles;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class GetPlayerInfoRequest : EuiMessageBase
|
||||
{
|
||||
public string PlayerUsername { get; set; }
|
||||
|
||||
public GetPlayerInfoRequest(string username)
|
||||
{
|
||||
PlayerUsername = username;
|
||||
}
|
||||
}
|
||||
}
|
||||
39
Content.Shared/Administration/Notes/AdminMessageEuiState.cs
Normal file
39
Content.Shared/Administration/Notes/AdminMessageEuiState.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Shared.Eui;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Administration.Notes;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class AdminMessageEuiState : EuiStateBase
|
||||
{
|
||||
public float Time { get; set; }
|
||||
public string Message { get; set; }
|
||||
public string AdminName { get; set; }
|
||||
public DateTime AddedOn { get; set; }
|
||||
|
||||
public AdminMessageEuiState(float time, string message, string adminName, DateTime addedOn)
|
||||
{
|
||||
Message = message;
|
||||
Time = time;
|
||||
AdminName = adminName;
|
||||
AddedOn = addedOn;
|
||||
}
|
||||
}
|
||||
|
||||
public static class AdminMessageEuiMsg
|
||||
{
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class Accept : EuiMessageBase
|
||||
{
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class Dismiss : EuiMessageBase
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using Content.Shared.Eui;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Eui;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Administration.Notes;
|
||||
@@ -6,7 +7,7 @@ namespace Content.Shared.Administration.Notes;
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class AdminNotesEuiState : EuiStateBase
|
||||
{
|
||||
public AdminNotesEuiState(string notedPlayerName, Dictionary<int, SharedAdminNote> notes, bool canCreate, bool canDelete, bool canEdit)
|
||||
public AdminNotesEuiState(string notedPlayerName, Dictionary<(int, NoteType), SharedAdminNote> notes, bool canCreate, bool canDelete, bool canEdit)
|
||||
{
|
||||
NotedPlayerName = notedPlayerName;
|
||||
Notes = notes;
|
||||
@@ -16,7 +17,7 @@ public sealed class AdminNotesEuiState : EuiStateBase
|
||||
}
|
||||
|
||||
public string NotedPlayerName { get; }
|
||||
public Dictionary<int, SharedAdminNote> Notes { get; }
|
||||
public Dictionary<(int noteId, NoteType noteType), SharedAdminNote> Notes { get; }
|
||||
public bool CanCreate { get; }
|
||||
public bool CanDelete { get; }
|
||||
public bool CanEdit { get; }
|
||||
@@ -27,35 +28,53 @@ public static class AdminNoteEuiMsg
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class CreateNoteRequest : EuiMessageBase
|
||||
{
|
||||
public CreateNoteRequest(string message)
|
||||
public CreateNoteRequest(NoteType type, string message, NoteSeverity? severity, bool secret, DateTime? expiryTime)
|
||||
{
|
||||
NoteType = type;
|
||||
Message = message;
|
||||
NoteSeverity = severity;
|
||||
Secret = secret;
|
||||
ExpiryTime = expiryTime;
|
||||
}
|
||||
|
||||
public NoteType NoteType { get; set; }
|
||||
public string Message { get; set; }
|
||||
public NoteSeverity? NoteSeverity { get; set; }
|
||||
public bool Secret { get; set; }
|
||||
public DateTime? ExpiryTime { get; set; }
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class DeleteNoteRequest : EuiMessageBase
|
||||
{
|
||||
public DeleteNoteRequest(int id)
|
||||
public DeleteNoteRequest(int id, NoteType type)
|
||||
{
|
||||
Id = id;
|
||||
Type = type;
|
||||
}
|
||||
|
||||
public int Id { get; set; }
|
||||
public NoteType Type { get; set; }
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class EditNoteRequest : EuiMessageBase
|
||||
{
|
||||
public EditNoteRequest(int id, string message)
|
||||
public EditNoteRequest(int id, NoteType type, string message, NoteSeverity? severity, bool secret, DateTime? expiryTime)
|
||||
{
|
||||
Id = id;
|
||||
Type = type;
|
||||
Message = message;
|
||||
NoteSeverity = severity;
|
||||
Secret = secret;
|
||||
ExpiryTime = expiryTime;
|
||||
}
|
||||
|
||||
public int Id { get; set; }
|
||||
public NoteType Type { get; set; }
|
||||
public string Message { get; set; }
|
||||
public NoteSeverity? NoteSeverity { get; set; }
|
||||
public bool Secret { get; set; }
|
||||
public DateTime? ExpiryTime { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,26 @@
|
||||
using Robust.Shared.Serialization;
|
||||
using Content.Shared.Database;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Administration.Notes;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed record SharedAdminNote(int Id, int? Round, Guid Player, string Message, string CreatedByName, string EditedByName, DateTime CreatedAt, DateTime LastEditedAt);
|
||||
public sealed record SharedAdminNote(
|
||||
int Id, // Id of note, message, watchlist, ban or role ban. Should be paired with NoteType to uniquely identify a shared admin note.
|
||||
Guid Player, // Notes player
|
||||
int? Round, // Which round was it added in?
|
||||
string? ServerName, // Which server was this added on?
|
||||
TimeSpan PlaytimeAtNote, // Playtime at the time of getting the note
|
||||
NoteType NoteType, // Type of note
|
||||
string Message, // Attached message
|
||||
NoteSeverity? NoteSeverity, // Severity of the note, ban or role ban. Otherwise null.
|
||||
bool Secret, // Is it visible to the player (only relevant if players can see their own notes)
|
||||
string CreatedByName, // Who created it?
|
||||
string EditedByName, // Who edited it last?
|
||||
DateTime CreatedAt, // When was it created?
|
||||
DateTime? LastEditedAt, // When was it last edited?
|
||||
DateTime? ExpiryTime, // Does it expire?
|
||||
string[]? BannedRoles, // Only valid for role bans. List of banned roles
|
||||
DateTime? UnbannedTime, // Only valid for bans. Set if unbanned
|
||||
string? UnbannedByName, // Only valid for bans. Set if unbanned
|
||||
bool? Seen // Only valid for messages, otherwise should be null. Has the user seen this message?
|
||||
);
|
||||
|
||||
15
Content.Shared/Administration/Notes/UserNotesEuiState.cs
Normal file
15
Content.Shared/Administration/Notes/UserNotesEuiState.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Eui;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Administration.Notes;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class UserNotesEuiState : EuiStateBase
|
||||
{
|
||||
public UserNotesEuiState(Dictionary<(int, NoteType), SharedAdminNote> notes)
|
||||
{
|
||||
Notes = notes;
|
||||
}
|
||||
public Dictionary<(int, NoteType), SharedAdminNote> Notes { get; }
|
||||
}
|
||||
@@ -647,7 +647,7 @@ namespace Content.Shared.CCVar
|
||||
CVarDef.Create("net.gasoverlaythresholds", 20);
|
||||
|
||||
/*
|
||||
* Admin stuff
|
||||
* Admin
|
||||
*/
|
||||
|
||||
public static readonly CVarDef<bool> AdminAnnounceLogin =
|
||||
@@ -656,6 +656,49 @@ namespace Content.Shared.CCVar
|
||||
public static readonly CVarDef<bool> AdminAnnounceLogout =
|
||||
CVarDef.Create("admin.announce_logout", true, CVar.SERVERONLY);
|
||||
|
||||
/// <summary>
|
||||
/// Should users be able to see their own notes? Admins will be able to see and set notes regardless
|
||||
/// </summary>
|
||||
public static readonly CVarDef<bool> SeeOwnNotes =
|
||||
CVarDef.Create("admin.see_own_notes", false, CVar.ARCHIVE | CVar.REPLICATED | CVar.SERVER);
|
||||
|
||||
/// <summary>
|
||||
/// The amount of days before the note starts fading. It will slowly lose opacity until it reaches stale. Set to 0 to disable.
|
||||
/// </summary>
|
||||
public static readonly CVarDef<double> NoteFreshDays =
|
||||
CVarDef.Create("admin.note_fresh_days", 91.31055, CVar.ARCHIVE | CVar.REPLICATED | CVar.SERVER);
|
||||
|
||||
/// <summary>
|
||||
/// The amount of days before the note completely fades, and can only be seen by admins if they press "see more notes". Set to 0
|
||||
/// if you want the note to immediately disappear without fading.
|
||||
/// </summary>
|
||||
public static readonly CVarDef<double> NoteStaleDays =
|
||||
CVarDef.Create("admin.note_stale_days", 365.2422, CVar.ARCHIVE | CVar.REPLICATED | CVar.SERVER);
|
||||
|
||||
/// <summary>
|
||||
/// How much time does the user have to wait in seconds before confirming that they saw an admin message?
|
||||
/// </summary>
|
||||
public static readonly CVarDef<float> MessageWaitTime =
|
||||
CVarDef.Create("admin.message_wait_time", 3f, CVar.ARCHIVE | CVar.REPLICATED | CVar.SERVER);
|
||||
|
||||
/// <summary>
|
||||
/// Default severity for role bans
|
||||
/// </summary>
|
||||
public static readonly CVarDef<string> RoleBanDefaultSeverity =
|
||||
CVarDef.Create("admin.role_ban_default_severity", "medium", CVar.ARCHIVE | CVar.SERVER);
|
||||
|
||||
/// <summary>
|
||||
/// Default severity for department bans
|
||||
/// </summary>
|
||||
public static readonly CVarDef<string> DepartmentBanDefaultSeverity =
|
||||
CVarDef.Create("admin.department_ban_default_severity", "medium", CVar.ARCHIVE | CVar.SERVER);
|
||||
|
||||
/// <summary>
|
||||
/// Default severity for server bans
|
||||
/// </summary>
|
||||
public static readonly CVarDef<string> ServerBanDefaultSeverity =
|
||||
CVarDef.Create("admin.server_ban_default_severity", "high", CVar.ARCHIVE | CVar.SERVER);
|
||||
|
||||
/// <summary>
|
||||
/// Minimum explosion intensity to create an admin alert message. -1 to disable the alert.
|
||||
/// </summary>
|
||||
|
||||
@@ -21,3 +21,6 @@ grid_fill = false
|
||||
auto_call_time = 0
|
||||
emergency = false
|
||||
arrivals = false
|
||||
|
||||
[admin]
|
||||
see_own_notes = true
|
||||
|
||||
@@ -1,7 +1,16 @@
|
||||
# UI
|
||||
admin-notes-title = Notes for {$player}
|
||||
admin-notes-new-note = New note:
|
||||
admin-notes-new-note = New note
|
||||
admin-notes-show-more = Show more
|
||||
admin-notes-for = Note for: {$player}
|
||||
admin-notes-id = Id: {$id}
|
||||
admin-notes-type = Type: {$type}
|
||||
admin-notes-severity = Severity: {$severity}
|
||||
admin-notes-secret = Secret
|
||||
admin-notes-notsecret = Not secret
|
||||
admin-notes-expires = Expires on: {$expires}
|
||||
admin-notes-expires-never = Does not expire
|
||||
admin-notes-edited-never = Never
|
||||
admin-notes-round-id = Round Id: {$id}
|
||||
admin-notes-round-id-unknown = Round Id: Unknown
|
||||
admin-notes-created-by = Created by: {$author}
|
||||
@@ -10,7 +19,59 @@ admin-notes-last-edited-by = Last edited by: {$author}
|
||||
admin-notes-last-edited-at = Last edited at: {$date}
|
||||
admin-notes-edit = Edit
|
||||
admin-notes-delete = Delete
|
||||
admin-notes-delete-confirm = Are you sure?
|
||||
admin-notes-hide = Hide
|
||||
admin-notes-delete-confirm = Confirm delete
|
||||
admin-notes-edited = Last edit by {$author} on {$date}
|
||||
admin-notes-unbanned = Unbanned by {$admin} on {$date}
|
||||
admin-notes-message-window-title = Alert!
|
||||
admin-notes-message-admin = New message from {$admin}, added on {$date}
|
||||
admin-notes-message-wait = The accept button will be enabled after {$time} seconds.
|
||||
admin-notes-message-accept = Dismiss permanently
|
||||
admin-notes-message-dismiss = Dismiss for now
|
||||
admin-notes-message-seen = Seen
|
||||
admin-notes-banned-from = Banned from
|
||||
admin-notes-the-server = the server
|
||||
admin-notes-permanently = permanently
|
||||
admin-notes-for = for {$player}
|
||||
admin-notes-days = {$days} days
|
||||
admin-notes-hours = {$hours} hours
|
||||
admin-notes-minutes = {$minutes} minutes
|
||||
|
||||
# Note editor UI
|
||||
admin-note-editor-title-new = Creating a new note for {$player}
|
||||
admin-note-editor-title-existing = Editing note {$id} on {$player} by {$author}
|
||||
admin-note-editor-pop-out = Pop out
|
||||
admin-note-editor-secret = Secret?
|
||||
admin-note-editor-secret-tooltip = Checking this will make the note not be visible by the player
|
||||
admin-note-editor-type-note = Note
|
||||
admin-note-editor-type-message = Message
|
||||
admin-note-editor-type-watchlist = Watchlist
|
||||
admin-note-editor-type-server-ban = Server Ban
|
||||
admin-note-editor-type-role-ban = Role Ban
|
||||
admin-note-editor-severity-none = None
|
||||
admin-note-editor-severity-low = Low
|
||||
admin-note-editor-severity-medium = Medium
|
||||
admin-note-editor-severity-high = High
|
||||
admin-note-editor-expiry-checkbox = Permanent?
|
||||
admin-note-editor-expiry-checkbox-tooltip = Check this to make it expire
|
||||
admin-note-editor-expiry-label = Expires on:
|
||||
admin-note-editor-expiry-label-params = Expires on: {$date} (in {$expiresIn})
|
||||
admin-note-editor-expiry-label-expired = Expired
|
||||
admin-note-editor-expiry-placeholder = Enter expiration date (yyyy-MM-dd HH:mm:ss)
|
||||
admin-note-editor-submit = Submit
|
||||
admin-note-editor-submit-confirm = Are you sure?
|
||||
|
||||
# Verb
|
||||
admin-notes-verb-text = Open Admin Notes
|
||||
|
||||
# Watchlist and message login
|
||||
admin-notes-watchlist = Watchlist for {$player}: {$message}
|
||||
admin-notes-new-message = You've received an admin message from {$admin}: {$message}
|
||||
|
||||
# Admin remarks
|
||||
admin-remarks-command-description = Opens the admin remarks page
|
||||
admin-remarks-command-error = Admin remarks have been disabled
|
||||
admin-remarks-title = Admin remarks
|
||||
|
||||
# Misc
|
||||
system-user = [System]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
admin-player-actions-window-title = Player Actions Panel
|
||||
admin-player-actions-window-ban = Ban
|
||||
admin-player-actions-window-ban = Banning panel
|
||||
admin-player-actions-window-admin-ghost = Admin Ghost
|
||||
admin-player-actions-window-teleport = Teleport
|
||||
admin-player-actions-window-permissions = Permissions Panel
|
||||
|
||||
@@ -2,10 +2,13 @@
|
||||
cmd-ban-desc = Bans somebody
|
||||
cmd-ban-help = Usage: ban <name or user ID> <reason> [duration in minutes, leave out or 0 for permanent ban]
|
||||
cmd-ban-player = Unable to find a player with that name.
|
||||
cmd-ban-self = You can't ban yourself!
|
||||
cmd-ban-invalid-minutes = ${minutes} is not a valid amount of minutes!
|
||||
cmd-ban-invalid-severity = ${severity} is not a valid severity!
|
||||
cmd-ban-invalid-arguments = Invalid amount of arguments
|
||||
cmd-ban-hint = <name/user ID>
|
||||
cmd-ban-hint-reason = <reason>
|
||||
cmd-ban-hint-duration = [duration]
|
||||
cmd-ban-hint-severity = [severity]
|
||||
|
||||
cmd-ban-hint-duration-1 = Permanent
|
||||
cmd-ban-hint-duration-2 = 1 day
|
||||
@@ -14,6 +17,12 @@ cmd-ban-hint-duration-4 = 1 week
|
||||
cmd-ban-hint-duration-5 = 2 week
|
||||
cmd-ban-hint-duration-6 = 1 month
|
||||
|
||||
# ban panel
|
||||
cmd-banpanel-desc = Opens the ban panel
|
||||
cmd-banpanel-help = Usage: banpanel [name or user guid]
|
||||
cmd-banpanel-server = This can not be used from the server console
|
||||
cmd-banpanel-player-err = The specified player could not be found
|
||||
|
||||
# listbans
|
||||
cmd-banlist-desc = Lists a user's active bans.
|
||||
cmd-banlist-help = Usage: banlist <name or user ID>
|
||||
@@ -39,3 +48,35 @@ cmd-ban_exemption_get-nargs = Expected exactly 1 argument
|
||||
cmd-ban_exemption_get-none = User is not exempt from any bans.
|
||||
cmd-ban_exemption_get-show = User is exempt from the following ban flags: {$flags}.
|
||||
cmd-ban_exemption_get-arg-player = <player>
|
||||
|
||||
# Ban panel
|
||||
ban-panel-title = Banning panel
|
||||
ban-panel-player = Player
|
||||
ban-panel-ip = IP
|
||||
ban-panel-hwid = HWID
|
||||
ban-panel-reason = Reason
|
||||
ban-panel-last-conn = Use IP and HWID from last connection?
|
||||
ban-panel-submit = Ban
|
||||
ban-panel-confirm = Are you sure?
|
||||
ban-panel-tabs-basic = Basic info
|
||||
ban-panel-tabs-reason = Reason
|
||||
ban-panel-tabs-players = Player List
|
||||
ban-panel-tabs-role = Role ban info
|
||||
ban-panel-no-data = You must provide either a user, IP or HWID to ban
|
||||
ban-panel-invalid-ip = The IP address could not be parsed. Please try again
|
||||
ban-panel-select = Select type
|
||||
ban-panel-server = Server ban
|
||||
ban-panel-role = Role ban
|
||||
ban-panel-minutes = Minutes
|
||||
ban-panel-hours = Hours
|
||||
ban-panel-days = Days
|
||||
ban-panel-weeks = Weeks
|
||||
ban-panel-months = Months
|
||||
ban-panel-years = Years
|
||||
ban-panel-permanent = Permanent
|
||||
ban-panel-ip-hwid-tooltip = Leave empty and check the checkbox below to use last connection's details
|
||||
ban-panel-severity = Severity:
|
||||
|
||||
# Ban string
|
||||
server-ban-string = {$admin} created a {$severity} severity server ban that expires {$expires} for [{$name}, {$ip}, {$hwid}], with reason: {$reason}
|
||||
server-ban-string-never = never
|
||||
|
||||
@@ -8,6 +8,7 @@ cmd-roleban-hint-1 = <name or user ID>
|
||||
cmd-roleban-hint-2 = <job>
|
||||
cmd-roleban-hint-3 = <reason>
|
||||
cmd-roleban-hint-4 = [duration in minutes, leave out or 0 for permanent ban]
|
||||
cmd-roleban-hint-5 = [severity]
|
||||
|
||||
cmd-roleban-hint-duration-1 = Permanent
|
||||
cmd-roleban-hint-duration-2 = 1 day
|
||||
@@ -37,6 +38,7 @@ cmd-rolebanlist-hint-2 = [include unbanned]
|
||||
|
||||
|
||||
cmd-roleban-minutes-parse = {$time} is not a valid amount of minutes.\n{$help}
|
||||
cmd-roleban-severity-parse = ${severity} is not a valid severity\n{$help}.
|
||||
cmd-roleban-arg-count = Invalid amount of arguments.
|
||||
cmd-roleban-job-parse = Job {$job} does not exist.
|
||||
cmd-roleban-name-parse = Unable to find a player with that name.
|
||||
|
||||
9
Resources/Textures/Interface/AdminNotes/attributions.yml
Normal file
9
Resources/Textures/Interface/AdminNotes/attributions.yml
Normal file
@@ -0,0 +1,9 @@
|
||||
- files: ["none_button.png", "minor_button.png", "medium_button.png", "high_button.png"]
|
||||
license: "CC-BY-SA-3.0"
|
||||
copyright: "CitrusGender"
|
||||
source: "https://github.com/tgstation/tgstation/pull/39808"
|
||||
|
||||
- files: ["watchlist.png", "message.png"]
|
||||
license: "CC-BY-SA-3.0"
|
||||
copyright: "Riggle"
|
||||
source: "https://github.com/RigglePrime/"
|
||||
BIN
Resources/Textures/Interface/AdminNotes/high_button.png
Normal file
BIN
Resources/Textures/Interface/AdminNotes/high_button.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 434 B |
BIN
Resources/Textures/Interface/AdminNotes/medium_button.png
Normal file
BIN
Resources/Textures/Interface/AdminNotes/medium_button.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 434 B |
BIN
Resources/Textures/Interface/AdminNotes/message.png
Normal file
BIN
Resources/Textures/Interface/AdminNotes/message.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 790 B |
BIN
Resources/Textures/Interface/AdminNotes/minor_button.png
Normal file
BIN
Resources/Textures/Interface/AdminNotes/minor_button.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 389 B |
BIN
Resources/Textures/Interface/AdminNotes/none_button.png
Normal file
BIN
Resources/Textures/Interface/AdminNotes/none_button.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 491 B |
BIN
Resources/Textures/Interface/AdminNotes/watchlist.png
Normal file
BIN
Resources/Textures/Interface/AdminNotes/watchlist.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 835 B |
@@ -150,7 +150,7 @@ def clear_server_ban(cur: "psycopg2.cursor", user_id: str):
|
||||
DELETE FROM
|
||||
server_ban
|
||||
WHERE
|
||||
user_id = %s
|
||||
player_user_id = %s
|
||||
""", (user_id,))
|
||||
|
||||
|
||||
@@ -172,7 +172,7 @@ def clear_server_role_ban(cur: "psycopg2.cursor", user_id: str):
|
||||
DELETE FROM
|
||||
server_role_ban
|
||||
WHERE
|
||||
user_id = %s
|
||||
player_user_id = %s
|
||||
""", (user_id,))
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user