Discussions — Reference
The Discussions surface is the comment and threaded-conversation engine for a site. It owns the comment records themselves, the threading model that connects replies to their parents, the moderation queue that decides which comments surface publicly, the anti-spam ruleset that filters submissions before moderation ever sees them, and the ban-list integration that holds repeat offenders out of the queue entirely. Every comment under a post, every reply under a comment, and every notification mailed to a thread participant traces back to records managed here.
This page is a reference for platform engineers and integrators who need to understand the surface before extending it, scripting against it, or mapping a legacy Disqus or native WordPress comment set into SGEN. Customer-facing walkthroughs for moderating discussions and configuring policies live in the customer docs set; this page describes the shape of the surface, not the editorial flow.
Overview
Discussions live under the Discussions module in SG-Admin. The module renders five primary views — the comment moderation queue, the comment detail view (with the surrounding thread), the ban list, the anti-spam settings panel, and the per-post discussion settings panel — and exposes a set of write operations for approve, reject, mark-as-spam, reply, edit, soft delete, restore, permanent delete, ban, unban, and bulk action.
The module is paired by convention: one half renders the views and prepares the data (queue, thread, ban list, settings), the other half handles writes and returns AJAX responses (moderation actions, ban writes, settings updates). The pairing matches the broader SG-Admin convention used across Pages, Posts, Forms, Media, and Users.
Where it lives in SG-Admin:
- Sidebar: SG-Admin → Discussions
- URL prefix:
/sg-admin/discussions/ - View templates:
application/views/Admin/Discussions/
Discussions are paired with the Posts and Pages modules: every comment attaches to either a post or a page record. The per-post discussion settings (open / moderated / closed) live on the post record itself, but the discussion-wide defaults live in this module's settings panel.
┌──────────────────────────────────────────────────────────────────────┐│ SG-Admin → Discussions → Moderation queue [Pending: 24] │├──────────────────────────────────────────────────────────────────────┤│ Author On post Excerpt Status ││ ───────────────── ────────────────── ───────────────────── ─────││ □ Maria Reyes Launch announcement excited to see… pending ││ □ spam-bot-9182 Five tips for site "Great article, visit… spam ││ □ Tom (anon) Engineering deep… Quick question about… pending ││ □ Editor (staff) Customer story Approved and replied. approved││ □ banned-user-2 News update (held — author banned) blocked ││ ││ [⋯ Approve] [⋯ Reject] [⋯ Spam] [⋯ Reply] [⋯ Ban author] [⋯ Bulk] │└──────────────────────────────────────────────────────────────────────┘Actions
The Discussions surface exposes the following operations. Each is described by what it does to the data and to the public surface, not by its internal method name.
List moderation queue
Returns the comments waiting on operator action, paginated, with author, target post, excerpt, posted timestamp, and current moderation state columns. Supports column sort, free-text filter across author and excerpt, state filter (pending / spam / rejected / approved / trash), and post filter (queue scoped to a single post). The default view shows pending and spam together — the two states an operator most commonly drives.
Approve comment
Moves the comment from pending or spam into approved. Approved comments surface on the public post page immediately. The author is notified by email if the comment is a reply to a previously approved comment on the same post (configurable in the settings panel).
Reject comment
Moves the comment into rejected. Rejected comments do not surface publicly and the author is not notified. Used for spam, abuse, or off-topic content that the operator does not want to keep around for reference.
Mark as spam
Moves the comment into spam and feeds the comment body, author email, and source IP into the anti-spam training set. Future submissions matching the same signature route directly to spam without hitting the queue.
Reply to comment
Opens a reply form scoped to the comment's thread. The reply posts as an operator-authored comment, attributed to the calling user. Operator replies skip the moderation queue and surface publicly on next render. The replied-to author is notified by email.
Edit comment
Opens the comment body in a small editor. Submit replaces the stored body and stamps an "edited by operator" flag visible only in SG-Admin. The public surface re-renders with the new body on next request.
Edits to a comment body do not change the original posted timestamp. The "edited" flag is operator-side only — readers see a comment timestamped at original-post-time, with the operator-edited body.
Soft delete
Moves the comment into trash. Trashed comments disappear from the moderation queue and from the public surface. Replies attached to the trashed comment also disappear from public view but remain queryable from the trashed-replies filter.
Restore
Moves the comment out of trash back to its prior moderation state (pending or approved). Attached replies re-emerge.
Permanent delete
Hard-removes the comment record. Available only after a soft delete. Replies attached to the comment become orphaned — the thread re-points the replies to the deleted comment's parent (collapsing the tree), so the conversation remains coherent.
Manage ban list
The ban list holds author identifiers (email, IP, or both) that should not be allowed to comment. Submissions matching a ban-list entry route directly to a blocked state without hitting the moderation queue. The list view shows entry, scope (email / IP / both), date banned, banning operator, and a free-text reason field.
Ban author
A shortcut from the comment detail view that adds the author's email and source IP to the ban list. Used in one motion with reject-or-spam to close out a problematic submitter.
Unban
Removes a ban-list entry. Future submissions from the previously banned identifier route through the normal pipeline.
Bulk action
Applies an action (approve / reject / spam / trash / restore / permanent delete / ban author) to every selected row in the queue. Used to clear a flood of spam in one motion. Bulk action emits a single Activity Log entry summarizing the batch.
Edit per-post discussion settings
A focused settings panel scoped to one post. Owns the discussion policy for that post (open / moderated / closed), the closure window (auto-close after N days), and an override for the site-wide reply notification policy.
Edit anti-spam ruleset
Owns the regex-and-signature filter set, the third-party anti-spam connector configuration (when one is attached), the keyword denylist, the link-count threshold (comments with more than N links auto-route to spam), and the new-author throttle (per-IP comment rate cap).
Data model
A comment record carries the following fields. Field names below are the conceptual shape — the on-disk column names match closely but are not contractually stable across releases.
| Field | Type | Notes |
|---|---|---|
id | integer | Primary key. Stable across edits. |
target_type | enum | post or page. |
target_id | integer | Foreign key to the post or page record. |
parent_id | integer | Foreign key to the parent comment for replies. Null for top-level. |
thread_root_id | integer | Denormalized pointer to the top-level ancestor for fast thread retrieval. |
author_name | string | Display name. Operator-authored comments carry the operator's display name. |
author_email | string | Used for the reply-notification email and for ban-list matching. |
author_url | string | Optional. Renders as a link on the public surface. |
author_user_id | integer | Foreign key to a user record when the author is a logged-in operator. Null for anonymous. |
source_ip | string | Submitter IP. Used for ban-list matching and throttle enforcement. |
user_agent | string | Submitter user agent. Used for anti-spam signature matching. |
body | text | The comment text. Markdown subset supported. |
body_html | text | Rendered HTML. Generated on save and on edit; never authored. |
moderation_state | enum | pending, approved, rejected, spam, trash. |
edited_by_operator | boolean | True when an operator has edited the body. |
edited_by_user_id | integer | Foreign key to the operator who edited. |
posted_at | timestamp | Submit time. Immutable across edits. |
moderated_at | timestamp | Set on the first moderation action. |
updated_at | timestamp | Touched on any edit. |
| Field | Type | Notes |
|---|---|---|
id | integer | Primary key. |
scope | enum | email, ip, or both. |
email_value | string | Required when scope is email or both. |
ip_value | string | Required when scope is ip or both. Supports CIDR. |
reason | string | Free-text. Operator-side only. |
banned_by_user_id | integer | Foreign key to the operator who created the entry. |
banned_at | timestamp | Immutable. |
parent_id and the conversation root via thread_root_id. The denormalized root pointer lets the read path fetch a full thread in a single indexed query, rather than a recursive walk. Thread depth is configurable in the settings panel; the default is 5 levels.Soft-delete semantics. moderation_state = trash is the soft-deleted state for individual comments. Trashed comments retain all other fields. Permanent delete removes the comment row and orphans the replies up to the deleted comment's parent.
Anti-spam signature. A submitted comment is signed by a hash of body text + author email + source IP + user agent. The signature feeds the spam-training set when an operator marks-as-spam. Future submissions with a matching signature route directly to spam without queue.
PUBLIC-SITE COMMENT SUBMIT│▼┌──────────────────────────┐│ Per-post policy check │ closed → reject; moderated/open → continue└──────────────┬───────────┘│▼┌──────────────────────────┐│ Ban-list match │ email or IP banned → state = blocked└──────────────┬───────────┘│ no match▼┌──────────────────────────┐│ Throttle check │ per-IP rate cap exceeded → reject└──────────────┬───────────┘│ ok▼┌──────────────────────────┐│ Anti-spam ruleset │ signature match → state = spam│ ├─ signature matching ││ ├─ link-count threshold ││ ├─ keyword denylist ││ └─ third-party connector│└──────────────┬───────────┘│ pass▼┌──────────────────────────┐│ Per-post policy applies │ moderated → state = pending│ │ open → state = approved└──────────────┬───────────┘│▼Comment persisted│▼Reply-notification email(if reply + author opted in)Permissions
Access to the Discussions surface is gated at two layers.
Layer 1 — admin gate. Every action under SG-Admin passes through the standard admin access check at request entry. Unauthenticated requests never reach the Discussions surface. The public-site comment submit entry point bypasses this gate but routes every submission through the per-post policy, ban list, throttle, and anti-spam pipeline described above.
Layer 2 — per-action capability. Within SG-Admin, each Discussions action checks a capability on the calling operator's role. The default role configuration ships with three roles — Administrator, Editor, Viewer — and a Moderator role is a common custom addition on community-heavy sites. The capability map is:
| Capability | Administrator | Editor | Moderator | Viewer |
|---|---|---|---|---|
| List queue | ✔ | ✔ | ✔ | ✔ |
| Approve / reject / spam | ✔ | ✔ | ✔ | — |
| Reply | ✔ | ✔ | ✔ | — |
| Edit comment body | ✔ | ✔ | — | — |
| Soft delete | ✔ | ✔ | ✔ | — |
| Restore | ✔ | ✔ | ✔ | — |
| Permanent delete | ✔ | — | — | — |
| Manage ban list | ✔ | ✔ | ✔ | — |
| Bulk action | ✔ | ✔ | ✔ | — |
| Edit per-post policy | ✔ | ✔ | — | — |
| Edit anti-spam ruleset | ✔ | — | — | — |
Self-protection rules. An operator cannot ban their own email or IP. The ban list cannot grow beyond the configurable site-wide ceiling (default 10,000 entries) without an explicit raise under Settings → Discussions. Anti-spam rule edits are throttled — no more than 20 ruleset edits per hour, to prevent a misconfigured automation from emptying the queue.
Audit. Every write — approve, reject, mark-as-spam, reply, edit, delete, restore, ban, unban, settings change — emits an Activity Log entry. The log records the acting operator, the comment or ban entry, and the change shape (state transition for moderation, field-level diff for edits, summary for bulk). Public-side comment submissions are not in the Activity Log — the volume would swamp the log; the comments themselves are the audit surface for submissions.
REQUEST → /sg-admin/discussions/...│▼┌────────────────────────┐│ Admin gate │ unauth → reject└──────────┬─────────────┘│ authed▼┌────────────────────────┐│ Capability check │ role lacks cap → reject│ (per-action) │ (custom roles override defaults)└──────────┬─────────────┘│ allowed▼┌────────────────────────┐│ Self-protection rules │ ban-self / list-ceiling /│ │ rule-edit-throttle → reject└──────────┬─────────────┘│ passes▼write executes│▼Activity Log entry│▼Public submissions bypassActivity Log; comments arethe audit surface for those.Related references
- Posts — Reference. Every comment attaches to either a post or a page. The Posts module owns the discussion-policy flag on the post record.
- Pages — Reference. Same — page comments live here, page-level discussion policy lives on the page record.
- Users — Reference. Operator-authored comments and replies reference user IDs. Per-action capability checks resolve against Users.
- Email — Reference. Reply notifications and operator-mention emails route through the Email module's templates.
- Settings — Reference. Discussion-wide defaults, thread depth, throttle thresholds, and the ban-list ceiling live in Settings.
- Activity Log — Reference. Every moderation write surfaces here for audit and review.
/docs/discussions.