Version 5.2.0
If your Schema Item Type is
LocalBusiness,OrganizationorRestaurantand the entries being reviewed are other businesses (a directory or guide), turn on Reviews Are About Other Businesses after updating, or the JSON-LD stops being printed.
Added
- A Stars Rating field type. Each entry's average rating and review count are now stored, and
kept up to date as reviews are approved, rejected, edited, moved, deleted or restored. Add the
field to a layout and entries can be sorted by rating (
orderBy('rating DESC'), or'rating.count'), filtered by it (.rating('>= 4'),.rating({average: '>= 4', count: '>= 3'})), shown and sorted in element indexes, and used in condition rules. The summary never re-saves the entry, so moderation doesn't create entry revisions. Existing reviews are summarised by the update migration. craft.stars.reviews.summary(entry)returns the stored summary without the field.php craft stars/ratings/rebuild [--entry-id=…]recomputes the stored summaries.- Read-only GraphQL:
starsReviews,starsReviewCount,starsCommentsandstarsCommentCount, behind new "Query for approved reviews" and "Query for approved comments" schema components, andstarsRating { average count maxRating }on entries. Only approved items on live entries in sections the schema can read are returned. Emails, IPs, user agents and referrers are never exposed. - Add to Entry Pages Automatically (
injectSchemaOrg, off by default) adds the JSON-LD to the head of each entry page with approved reviews, with no template change, and never twice. schemaOrg(entry, {type, limit})takes per-call options, andSchemaService::EVENT_DEFINE_SCHEMAlets you change or drop the JSON-LD per entry.- Schema Review Limit (
schemaReviewLimit, default 10) caps the individual reviews in the JSON-LD.
Changed
- The JSON-LD now follows Google's review-snippet rules.
LocalBusiness,Organizationand their subtypes print nothing unless the new Reviews Are About Other Businesses (schemaThirdPartyReviews) setting is on, because Google treats reviews a business hosts about itself as self-serving. Reviews without a reviewer name are left out of the review list (they still count towards the aggregate), and the list holds the newest 10 rather than every review. - The JSON-LD's aggregate rating is read from the stored summary.
Version 5.1.2
Not released on its own; these changes ship in 5.2.0.
Security
- The submission URL is the browser's
Refererheader, which a script can set to anything. It was stored as given and linked in the review or comment sidebar, so ajavascript:value ran in a moderator's session when clicked. Only http(s) URLs are stored now, and anything else already stored is shown as plain text. craft.stars.reviews.schemaOrg()now escapes<,>,&and quotes in the JSON-LD, so review text can't close the<script>tag and run its own.- Reviews and comments can only be posted against a live entry. Before, any element id was accepted: an unpublished entry, a user, an asset.
- A reply has to go under an approved comment on the same entry. Before, it could hang off a comment on another entry, or one still in moderation, and that comment's author was emailed.
A new hourly budget per address (
maxSubmissionsPerHour, default 10) covers reviews and comments across every entry. The per-entry limit allowed one post per entry, so a script could work through the whole site. It also read the stored IP, so it did nothing with IP capture off. The budget lives in the cache, andX-Forwarded-Foris only believed oncetrustedHostsis set.Upgrading behind a proxy or CDN? Set Craft's
trustedHoststo the proxy's addresses. Otherwise every visitor is charged to the proxy, and the whole site shares one budget. RaisemaxSubmissionsPerHour, or set it to0, if you can't.
Fixed
- A review posted without a rating was saved with the top mark. It's now refused with "Please choose a rating."
Changed
schemaOrg()returns markup, so|rawis no longer needed. Templates that still use it keep working.
Version 5.1.1
Fixed
- The rate limiter compared a cutoff built in the site's timezone against
dateCreatedvalues Craft stores in UTC, so the window was off by the site's UTC offset. West of UTC it stretched (a 2-minute limit in New York lasted about four hours, blocking readers from replying twice in a conversation); east of UTC the cutoff landed in the future and nothing was ever limited. The cutoff is now built in UTC. - A rate-limited submission was rejected with "Your submission was flagged as spam." It now says "You posted here recently. Please wait a little while and try again.", so a real person isn't told they look like a bot.
- Admin responses saved their date as site-timezone time in a UTC column, so the "Response Date" shown in the CP was off by the site's UTC offset. New responses are stored in UTC; dates saved before this release keep the offset.
Added
SpamService::check()returns which spam check failed (one of theFAILURE_*constants) ornull.isSpam()still works and now wraps it.
Version 5.1.0
Added
- All Twig variables now live under a single namespaced root:
craft.stars. Reviews are atcraft.stars.reviews.*and comments atcraft.stars.comments.*.
Changed
- The plugin no longer takes over the top-level
craft.comments(orcraft.reviews) variable when another plugin has already registered it.commentsis a name verbb/comments has used for years, and because both plugins registered it onCraftVariable::EVENT_INIT, whichever ran last won — installing Stars alongside it silently replaced Verbb's API, 500ing its settings screen and any site template callingcraft.comments.*. Stars now only claims those names if they're still free. (#4)
Deprecated
craft.reviewsandcraft.commentsas top-level variables. They still work where they aren't contested, but will be removed in 6.0.0 — move templates tocraft.stars.reviews/craft.stars.comments.
Version 5.0.3
Fixed
- Adding an admin response (or making any other edit) to an existing review or
comment in the CP could silently clear its
entryId, orphaning it from its entry and dropping it from the frontend. The entry-select field's hidden input round-trips throughsetAttributes()on every CP save, and an empty submission there was written straight through asnullwith no validation error. Frontend submission and the bulk moderation actions were never affected — they don't go through this method — which is why only CP edits showed the problem. An empty submission for an existing, already-linked review or comment is now treated as the field being untouched rather than an explicit removal, so its entry link survives. (#3)
Version 5.0.2
Fixed
- Every setting on the settings page rendered a bare warning icon with no text next to it. The "overridden by config" macro returned the whitespace between its own tags when a setting wasn't overridden, and Twig treats that non-empty string as a real warning. Only genuinely overridden settings show a warning now.
Changed
- Reworked the Review and Comment editor screens. Every field used to be packed into the sidebar, leaving the main body empty. The submission's own content — rating, name, email, review/comment text, pros, cons, and admin response — now renders in the body via an in-code field layout, laid out two-up where it reads better. The sidebar keeps just what's about the submission: its moderation status and the entry it belongs to, above the existing metadata.
- Validation errors now appear inline on the field that caused them, instead of only in the error banner.
- A reply's parent comment is now shown in the Comment sidebar as a "Reply To" link.
Version 5.0.1
Fixed
- The Reviews and Comments CP sections rendered their element index as escaped
HTML instead of a working index. Both templates now extend Craft's
_layouts/elementindexrather than echoing the deprecated_elements/indexcontainerpartial throughrenderTemplate(). - Frontend review and comment submissions returned a 500 error whenever notifications were enabled. The element saved, but rendering the notification email threw, because the plugin's email templates live in a control-panel-only template root and were being rendered in site template mode. They're now rendered in CP template mode, and a template failure is logged instead of breaking the submission.
- Review and Comment index columns showed raw values — the entry ID instead of
the entry, the rating number instead of stars, and the lowercase status key
instead of a labelled status. The custom column rendering was declared as
tableAttributeHtml(), which Craft 5 renamed toattributeHtml(), so it was never called. - Fixed the frontend form and review display examples in the README, which used
a
repeatfilter that doesn't exist in Twig or Craft.
Known issues
- Reply notifications only fire when a reply is created with
approvedstatus, so with the defaultdefaultStatusofpendingthey never send. Approving a reply later does not trigger them.
Version 5.0.0
First public release on the Craft Plugin Store, for Craft CMS 5. Builds on the initial review system with a full comments system, a submitter blocklist, pluggable captcha, privacy controls, and an automated test suite.
Added
- Comments — a threaded comment system for entries, as a first-class element
alongside reviews:
- Four-state moderation, CP element index, and bulk actions (shared with
reviews via a
ModeratedElementbase). - Threaded replies with a configurable maximum depth (
maxCommentDepth); over-deep replies attach at the deepest allowed level. - Frontend submission (
actions/stars/comments/save), optional login gating (commentsRequireLogin/commentsAllowAnonymous), and auto-filled author details for logged-in users. craft.commentsTwig API (tree,forEntry,topLevel,replies,count) and a bundled recursivestars/_comments/thread.twigmacro.- Email notifications to moderators, plus reply notifications to the parent comment's author.
- Comment permissions: view / manage / moderate / reply / delete.
- Four-state moderation, CP element index, and bulk actions (shared with
reviews via a
- Blocklist — block submitters by email, IP, or user id. Enforced on both
review and comment submission, with a CP management section, a "Block Author"
bulk action, and a
stars:manageBlocklistpermission. - Pluggable captcha — reCAPTCHA v3, reCAPTCHA v2, hCaptcha, and Cloudflare
Turnstile, selectable per site. Applies to reviews and comments alike.
craft.reviews.captcha()/craft.comments.captcha()expose the provider and site key for the frontend widget. - Privacy controls —
captureIpAddress,captureUserAgent, andcaptureReferrersettings to independently disable storing each piece of submission metadata (GDPR-style data minimization). All default to on. - Reviewer/commenter email addresses are masked in error logs (e.g.
a***@example.com). - Automated test suite (Codeception + Craft's test framework), runnable via DDEV.
Fixed
craft.reviews.averageRating()— and theratingValuein the schema.org JSON-LD output — returned an incorrect value instead of the true average.- The per-IP rate limiter flagged legitimate first-time submissions as spam, rejecting valid reviews whenever rate limiting was enabled (on by default).
Changed
- Spam rate limiting is context-aware (reviews vs. comments count against their own tables).
- The legacy
enableRecaptcha/recaptcha*settings are superseded bycaptchaProvider/captcha*but are still honored (mapped to reCAPTCHA v3). - New plugin icon.
- Licensed under the standard Craft License.
Version 1.0.0
Added
- Initial release
- Review element type with star ratings (1-5 configurable up to 10)
- Four-state moderation: pending, approved, rejected, spam
- Pros/cons fields
- Admin response support
- Spam protection: honeypot, reCAPTCHA v3, rate limiting, submission time check
- Schema.org JSON-LD output (Review + AggregateRating)
- Email notifications on new submissions
- Twig variable
craft.reviewsfor frontend queries - User permissions for view, manage, moderate, respond, delete
- CP element index with bulk actions (approve, reject, mark as spam)