Forum mod-logs at /forum/{id}/mod-log now have the same three-panel shape as /admin/users/{id} — Suspend, Permanent Ban, Shadow Ban — but scoped to the single forum. Site-wide access is unaffected; every forum-scope action auto-escalates to an admin for consideration of site-wide action.
What's new
- Three panels with the same reason-picker + "Other" + required-mod-note UX as the site-wide admin page. Reason templates are new — forum-scoped, wording explicit about "from this forum" and "site-wide access is unaffected". Picking a suspend template auto-fills the reason textarea and the day count.
- Active-restrictions table tags each row with a Permanent / Suspension / Shadow badge and a single Lift action that clears any kind.
- No session impact. Forum-scope actions never log the user out — they keep browsing and posting everywhere else.
- Auto-escalation to Sysops. Every forum-scope Suspend / Ban / Shadow creates a
forum_escalationreport in/admin/moderation, notifies every admin + super-admin, and lands a thread on /forums/privilege-use via the existing audit-log pipeline. Sysops decide whether the forum-level pattern warrants a matching site-wide action.
Schema + model
forum_bansgets two columns:mod_note(required internal note) andis_shadow(distinguishes shadow bans from overt restrictions). One table now covers all three action shapes:- permanent ban →
expires_atNULL,is_shadow0 - suspension →
expires_atset,is_shadow0 - shadow ban →
is_shadow1 (expires_at optional)
- permanent ban →
mod_templatesgets ascopecolumn (site/forum/category). Existing rows default tositeso the admin-user page keeps its template set untouched. Nine new forum-scoped rows seed the three new panels.ForumBan::isBanned()no longer returns true for shadow bans (those are post-visibility rules, not post-blocking ones).ForumBan::isShadowBanned()added for the visibility check.
Routes
POST /forum/{id}/suspend-user— newPOST /forum/{id}/shadow-ban-user— newPOST /forum/{id}/ban-user— now permanent-onlyPOST /forum/{id}/unban-user— lifts any kind
Known follow-up
Forum-level shadow ban is plumbed but not yet enforced at post render. The column is in place, isShadowBanned() resolves correctly, and the escalation path runs — but the thread view still displays shadow-banned authors' posts to every viewer. Next pass adds a filter in the post-fetch query to hide them from everyone except the author and mods. Until then, the admin escalation pathway lets Sysops promote to a site-wide is_shadow_banned if needed.
Not in this commit: category-level
Category moderators still act per-forum via the existing mod-log on any forum in their category. A separate category_bans table for one-shot "ban from every forum in this category" is a future build; flag it if you'd like it queued.
. __ ____ ___ ____ _ _
/ /_| ___| / _ \___ \(_)___| |__
| '_ \___ \| | | |__) | / __| '_ \
| (_) |__) | |_| / __/| \__ \ | | |
\___/____/ \___/_____|_|___/_| |_|
D2sk - Sysop