feat(comment): comments, ratings and reviews — one primitive, attachable to anything - #200
Merged
Conversation
…ble to anything Builds COMMENTS.md. The last WP-parity platform gap, shipped as a core module that is OFF by default (tiger.comment.enabled) — an open comment endpoint is the most-attacked surface a CMS has, and it carries a standing moderation duty a brochure site never asked for. A review IS a comment with a rating. One `comment` table with a nullable `rating`: null = a plain comment, 1-5 = a review, parent_id set = a reply. One moderation queue, one spam path, one admin screen, one /api service. - Tiger_Comment — the subject registry. A module declares how to resolve a subject to a title/URL/exists, which ACL resource gates READING the thread (the SUBJECT's own, so a thread can never expose content the caller can't see), whether stars apply, reply depth, and optionally an entitlement gate + an ownership check. Core ships providers for `page` and `blog.post` only; everything else opts in. Fail-soft throughout: a resolver that throws degrades to "gone" so the moderation queue still renders the orphaned row, and an entitlement check that errors can never mint a verified badge. - Tiger_Model_Comment + Tiger_Model_CommentAggregate (0045/0046). The rollup is recomputed inside the same transaction as the write, so a card can never quote a number the thread doesn't support. Pending and spam rows are excluded from the average — otherwise a spammer moves a score merely by posting, before anyone moderates. - Tiger_View_Helper_Stars — half-star DISPLAY of averages (whole-star input), role="img" with a real aria-label and the numeric value as text. A row of glyphs is not an accessible rating. - modules/comment — the /api service (where nearly all the policy lives: feature gate, subject ACL, self-review refusal, rate limits, honeypot + time-trap, moderation posture), the moderation queue, four shortcodes, the reader JS, and six locales. Settled while building, recorded in COMMENTS.md §11: the edit window is BOUNDED (15 min default) — unbounded lets a 1-star review be quietly rewritten after a refund; an edited body re-enters moderation but a changed rating does not; no guest ratings (an anonymous score is an open ballot box) though guest commenting stays a config opt-in. 31 tests. Two real bugs they caught: softDelete() takes a WHERE clause, not an id, so deletes were silently no-ops; and the grouped aggregates were built on activeSelect(), which omits the FROM part by default. A third only appeared in the FULL suite — merging onto a bootstrap-published read-only Zend_Config throws, so the test now merges onto a modifiable copy. Full suite: OK (2142 tests, 21551 assertions), zero deprecations.
…lup read The coverage gate caught the gap honestly — 71.5% against a 72% floor — so this closes it with tests rather than by lowering the ratchet. 19 more: the star helper (including the accessibility contract, which is the part that would rot silently), the shortcode renderers, core's own page/blog subject resolvers, and the batch rollup read that is the entire reason comment_aggregate exists. One of them found a real bug rather than just adding coverage: Comment_Service_Render fell back to a bare Zend_View outside a themed request, and a bare view does not know Tiger's helpers — so $this->stars() threw "Plugin by name 'Stars' was not found" anywhere the renderer ran without a booted front controller (a CLI render, a queued job, a test). It now registers the helper path explicitly instead of assuming the bootstrap ran. Full suite: OK (2161 tests, 21594 assertions).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Builds COMMENTS.md. The last WP-parity platform gap, as a core module that is off by default.
A review IS a comment with a rating
One
commenttable, nullablerating— null = a plain comment, 1–5 = a review,parent_idset = a reply. One moderation queue, one spam path, one admin screen, one/apiservice.What ships
Tiger_CommentTiger_Model_Comment/_CommentAggregateTiger_View_Helper_Starsmodules/comment/apiservice, moderation queue, 4 shortcodes, reader JS, 6 localesAttaches to anything, without core learning what anything is
A provider declares how to resolve a subject to title/URL/exists, which ACL resource gates reading the thread (the subject's own, so a thread can never expose content the caller can't see), whether stars apply, reply depth, and optionally an entitlement gate + ownership check. Core ships
pageandblog.post; everything else opts in.Fail-soft throughout: a resolver that throws degrades to "gone" so the moderation queue still renders the orphaned row — precisely when an operator needs to see it — and an entitlement check that errors can never mint a verified badge.
Policy that makes it usable
Pending and spam rows are excluded from the average, so a spammer can't move a score merely by posting. The rollup recomputes inside the same transaction as the write, so a card can never quote a number the thread doesn't support. Plus: hold-then-approve by default, per-user and per-IP rate limits, a honeypot and a time-trap, one rating per person per subject, and no reviewing your own listing.
Settled while building (COMMENTS.md §11)
Tests
31 new. Three real bugs they caught:
softDelete()takes a WHERE clause, not an id — deletes were silent no-ops.activeSelect(), which omits the FROM part by default.Zend_Configthrows. Isolation-passing / suite-failing is exactly the flake worth killing at the source.Full suite: OK (2142 tests, 21551 assertions), zero deprecations.