CMS — comment moderation and approval capture
Purpose
CMS owns the comment domain: pending save capture, diff enrichment, publication after approval, and the moderation adapter registered on Core's ModerationAdapterRegistry.
CMS does not import AI config or jobs. Orchestration events are emitted by Core.
See also: Modules/Core/docs/rag/EVENT_ORCHESTRATION.md, Modules/AI/docs/rag/MODERATION_AND_SEARCH.md.
Capabilities
| Concern | Owner | Artifact |
|---|---|---|
| Pending comment save | CMS | Comment + CommentApprovalCapture |
| Modification diff | CMS | CommentApprovalCapture::enrichDiff() |
| Emit moderation need | Core | ModificationRequiresModeration on modification create |
| LLM context + prompts | CMS | CommentModerationAdapter, CommentModerationPrompt |
| AI analysis + vote | AI | ApproveModificationJob (via registry) |
| Publish approved comment | CMS | Comment::applyModificationChanges() |
| Post-approve translate | Core + AI | ModificationApproved, fired by ModificationVoteService |
InternalFlow
- User saves comment →
CommentApprovalCapture::captureSave()creates activeModificationwith diff (body,locale,content_id, optionalrating_score). - Core saves modification →
ModificationRequiresModeration(first save,wasRecentlyCreated). - AI may run if
ai.features.moderation.entities.cms_comments(declared by the AI module) and global moderation are enabled. - Comment hidden from public queries until approved.
- Human approves in Filament →
ModificationVoteService::cast()→Comment::applyModificationChanges()in the vote's transaction → public comment; the service firesModificationApprovedafter the commit.
CommentApprovalCapture and Comment do not dispatch events; Core's emitter and ModificationVoteService handle that.
Only creations and updates of a comment go through moderation (Comment::approvalOperations()): its author deletes their own comment directly, and a comment's deletion never becomes a request. The author may also withdraw a pending comment request from Modifications before it is decided.
HowToUse — posting and moderating over the API
The application creates comments through the generic CRUD API: POST /app/crud/insert/cms/comments
with content_id, body, and optionally parent_id (a reply) and rating_score (1-5). The author is
the authenticated user unless user_id is given. The comment is validated before it is captured
(Comment::getRules(): the content and the parent must exist, the text is required, the rating is
1-5), so an invalid comment answers 422 and never becomes a request. A valid one answers 202 with
{modification, operation: "create"}: it is not visible until approved.
A moderator approves or rejects it with PATCH /app/crud/approve|disapprove/cms/comments and
modification set to that id, with no id: a pending comment has no record yet. On approval the
comment is created with its text and author and becomes visible.
HowToUse — CommentModerationAdapter
Path: Modules/CMS/app/Services/CommentModerationAdapter.php
Implements Modules\Core\Contracts\ModerationAdapter:
supports():modifiable_type === Comment::classbuild(): loads article context fromcontent_id; whenparent_idis in the modification diff, loads the parent comment body for thread context; returnsModerationRequestwith CMS-owned prompts (CommentModerationPrompt)
CommentApprovalCapture::enrichDiff() adds parent_id to the modification when the comment is a reply.
Registration in CMSServiceProvider::boot():
$this->app->make(ModerationAdapterRegistry::class)
->register($this->app->make(CommentModerationAdapter::class));
Configuration
| Setting | Group | Effect |
|---|---|---|
ai.features.moderation.entities.cms_comments |
moderation |
Enables AI vote for comments (declared by the AI module) |
auto_translate_comments |
translations |
Post-approve TranslateModelJob via ModificationApproved |
Seeded via Core settings; cache flushed on Setting save (PerModelSettingResolver).
PermissionsAndSecurity
- Pending comments: not visible in public listings until modification approved.
- Comment deletions are never captured: the author deletes their own comment directly.
- AI vote is additive; human approval still required per
approvers_required. - Audit trail in
approvals.meta/disapprovals.meta(AI module writes JSON).
ErrorsAndTroubleshooting
| Symptom | Check |
|---|---|
Modification without modifiable_id on create |
Expected for pending comments; builder uses diff JSON |
| AI never runs for comments | ai.features.moderation.entities.cms_comments, AI moderation enabled, builder registered |
| Comment stuck pending | Human approval in Filament, modification active |
FAQPrompts
- How does CommentApprovalCapture work?
- When is ModificationRequiresModeration fired for comments?
- Where is CommentModerationContextBuilder registered?
- Does CMS call AI directly?
- What setting enables AI moderation for comments?