R6 moderation in decePub: users and personas, accounts, report actions, deletions, suspended personas

Administration's Users & personas (administrators): logins searched and opened into their personas and groups; roles,
bans, two-factor reset and deletion of a login; silence, suspension and deletion of a persona; a group deleted. Accounts
(moderators): local personas and remote accounts searched or listed as moderated, silenced and suspended with a staff
note and a warning. Reports resolve with an action. ActDialog asks for the note, the warning or the password and is the
act's confirmation. A login deletes its own persona and the groups it owns with its password. Suspended personas can't
be chosen; one refused a token mid-session moves the session to another, or to none, once. Warnings show in
notifications, limited accounts on their profile, and moderators remove a post from its card.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WpVCoDeM1qUTRp4fXQdfof
This commit is contained in:
thepraandClaude Opus 5.5 committed 2026-10-17 05:41:10 +02:00
1 parent 4c88980374
commit c572471b88
30 files changed
+2604 -67

No files matched your search

+15 -2
View File
@@ -114,14 +114,27 @@
<div class="flex flex-col gap-4 w-full max-w-md mt-6 md:mt-16">
<h1 class="text-xl font-bold text-center">@Localizer["Who are you today?"]</h1>
<p class="text-sm opacity-70 text-center">@Localizer["Each persona has its own token; PrivaPub never tells anyone they share a login."]</p>
@if (CascadingState.Personas.All(p => !CascadingState.CanUse(p)))
{
<p class="text-sm text-center" data-personas-suspended>
@Localizer["Every persona of this login is suspended by this server's staff. Settings can still make a new one."]
</p>
<DButton Href="settings">@Localizer["Settings"]</DButton>
}
@foreach (var persona in CascadingState.Personas)
{
<button type="button" class="flex items-center gap-3 p-3 rounded-xl text-left neo-raised neo-xs hover:neo-sm transition-shadow @(persona.Id == CascadingState.PersonaId ? "ring-2 ring-primary" : null)"
disabled="@(IsDefaultDisabled || isWorking)" @onclick="() => Choose(persona.Id)">
@* a persona the staff suspended can't be chosen until that is lifted *@
<button type="button" class="flex items-center gap-3 p-3 rounded-xl text-left neo-raised neo-xs hover:neo-sm transition-shadow disabled:opacity-60 @(persona.Id == CascadingState.PersonaId ? "ring-2 ring-primary" : null)"
disabled="@(IsDefaultDisabled || isWorking || !CascadingState.CanUse(persona))" @onclick="() => Choose(persona.Id)"
data-persona-choice="@persona.UserName" data-persona-state="@(CascadingState.CanUse(persona) ? "normal" : "suspended")">
<img alt="" class="h-12 w-12 flex-none object-cover rounded-full neo-raised neo-2xs" src="@(persona.PictureURL ?? "imgs/icon-192.png")" />
<span class="flex flex-col min-w-0">
<b class="truncate">@(persona.Name ?? persona.UserName)</b>
<span class="text-xs opacity-70 truncate">@(persona.Handle ?? "@" + persona.UserName)</span>
@if (!CascadingState.CanUse(persona))
{
<span class="text-xs text-error">@Localizer["Suspended by this server's staff: it can't act until that is lifted."]</span>
}
</span>
@if (persona.Id == CascadingState.PersonaId)
{