=== Init Review System – Reactions, Multi-Criteria, Guest-Friendly === Contributors: brokensmile.2103 Tags: review, rating, vote, reaction, schema Requires at least: 6.9 Tested up to: 7.1 Requires PHP: 7.4 Stable tag: 2.0.0 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html Fast 5-star + multi-criteria reviews with schema, REST API, Block Editor & Abilities API support, emoji reactions, and localStorage voting. == Description == **Init Review System** adds a clean and customizable 5-star rating system to your WordPress site. Votes are stored via REST API, tracked with `localStorage`, and the average score is auto-calculated and optionally displayed with schema markup. Built to be lightweight, developer-friendly, and easy to integrate into any theme or custom UI. Now with **multi-criteria reviews**, an **emoji reactions system** for richer user interaction, native **Block Editor** support, and read-only **Abilities API** integration for WordPress 6.9+. This plugin is part of the [Init Plugin Suite](https://en.inithtml.com/init-plugin-suite-minimalist-powerful-and-free-wordpress-plugins/) — a collection of minimalist, fast, and developer-focused tools for WordPress. GitHub repository: [https://github.com/brokensmile2103/init-review-system](https://github.com/brokensmile2103/init-review-system) **Highlights:** - **NEW: Block Editor (Gutenberg) support** — 4 blocks, one per shortcode - **NEW: Abilities API support (WordPress 6.9+)** — 3 read-only abilities - 5-star voting via frontend - Multi-criteria review support - Emoji Reactions with Login Enforcement - Average score display - Optional login requirement - Optional strict IP checking - REST API for vote submission - JSON-LD schema for SEO - Works with any post type - Minimal, theme-friendly UI == Features == - Block Editor (Gutenberg) blocks: Review Score, Review Widget, Review Criteria, Reactions Bar — each with a live server-side preview in the editor - Abilities API (WordPress 6.9+): review score, criteria reviews, and reactions summary exposed as discoverable, executable abilities via `wp_get_abilities()` and the `wp-abilities/v1` REST namespace - 5-star rating system - Multi-criteria review scoring (up to 5 custom criteria) - Emoji-based reactions bar with live counts (requires login) - REST API endpoint for reactions: `/wp-json/initrsys/v1/reactions/toggle` - Reactions stored in both post meta and dedicated user↔post mapping table - Accessibility-ready with `aria-pressed` + `aria-live` updates - Shortcode-based integration - Auto-insert blocks before/after content or comments - Optional login + IP check to prevent abuse - REST API endpoint: `/wp-json/initrsys/v1/vote` - Developer filters and extensible architecture - No jQuery, only minimal assets loaded when needed == Block Editor (Gutenberg) == Four dynamic blocks are available under their own **Init Review System** category in the block inserter — no shortcodes needed if you prefer working entirely in the editor: - **Review Score** — equivalent to `[init_review_score]` - **Review Widget** — equivalent to `[init_review_system]` - **Review Criteria** — equivalent to `[init_review_criteria]` - **Reactions Bar** — equivalent to `[init_reactions]` Each block shares the exact same rendering code as its shortcode, so switching between the Block Editor and shortcodes never changes the output. Block settings map directly to shortcode attributes, and a live preview (via `wp.serverSideRender`) is shown right in the editor as you configure it. == Abilities API (WordPress 6.9+) == On WordPress 6.9 and above, Init Review System registers three read-only abilities under the `init-review-system` category: - `init-review-system/get-review-score` — average score and vote count for a post - `init-review-system/get-criteria-reviews` — criteria breakdown and a page of written reviews for a post - `init-review-system/get-reactions-summary` — emoji reaction counts for a post These are discoverable and executable via PHP (`wp_get_abilities()`), and — for sites that opt into exposing it — the `wp-abilities/v1` REST namespace. Actions that write data (voting, submitting a review, toggling a reaction) are intentionally **not** exposed as abilities, since an ability can be discovered and invoked directly by an AI agent or automation tool — voting/reviewing/reacting on a visitor's behalf should never happen without the visitor's own, in-context action. This integration is fully optional: on WordPress versions older than 6.9, it silently does nothing and the rest of the plugin is unaffected. == Usage == === [init_review_system] === Displays interactive 5-star voting block. Attributes: - `id`: Post ID (default: current post) - `class`: Custom CSS class - `schema`: `true|false` – Output JSON-LD schema markup === [init_review_score] === Displays average score (read-only). Attributes: - `id`: Post ID (default: current post) - `icon`: `true|false` – Show star icon (default: false) - `sub`: `true|false` – Show `/5` subtext (default: true) - `class`: Custom CSS class - `hide_if_empty`: `true|false` – Hide if no reviews (default: false) === [init_review_criteria] === Displays multi-criteria review block. Attributes: - `id`: Post ID (default: current post) - `class`: Custom CSS class - `schema`: `true|false` – Output schema markup (default: false) - `per_page`: Number of reviews to show (default: 0 = all) - `paged`: Current review page number (default: 1) === [init_reactions] === Displays emoji reactions bar under a post. Attributes: - `id`: Post ID (default: current post) - `class`: Custom CSS class - `require_login`: Always true (login required) == Filters for Developers == This plugin provides filters and actions to let developers customize auto-insert behavior, schema output, review permissions, and REST API logic. **`init_plugin_suite_review_system_auto_insert_enabled_score`** Enable or disable automatic score output (before/after content). **Applies to:** Frontend filter **Params:** `bool $enabled`, `string $position`, `string $post_type` **`init_plugin_suite_review_system_auto_insert_enabled_vote`** Enable or disable automatic voting block insertion. **Applies to:** Frontend filter **Params:** `bool $enabled`, `string $position`, `string $post_type` **`init_plugin_suite_review_system_default_score_shortcode`** Change the default shortcode used for score auto-insertion. **Applies to:** Frontend **Params:** `string $shortcode` **`init_plugin_suite_review_system_default_vote_shortcode`** Change the default shortcode used for voting block auto-insertion. **Applies to:** Frontend **Params:** `string $shortcode` **`init_plugin_suite_review_system_require_login`** Force login for submitting reviews, even if disabled in settings. **Applies to:** REST `/submit-criteria-review` **Params:** `bool $require_login` **`init_plugin_suite_review_system_min_len_for_ws_check`** Adjust minimum length threshold for triggering no-whitespace check. **Applies to:** Backend + JS precheck **Params:** `int $threshold` (default `20`) **`init_plugin_suite_review_system_repetition_threshold`** Adjust repetition threshold for detecting excessive word repetition. **Applies to:** Backend + JS precheck **Params:** `int $threshold` (default `8`) **`init_plugin_suite_review_system_schema_type`** Customize schema.org type (e.g., `Book`, `Product`, `Course`). **Applies to:** Shortcode output **Params:** `string $type`, `string $post_type` **`init_plugin_suite_review_system_schema_data`** Modify JSON-LD schema output array. **Applies to:** Shortcode output **Params:** `array $data`, `int $post_id`, `string $schema_type` **`init_plugin_suite_review_system_after_vote`** Run custom logic after a single-star vote is submitted. **Applies to:** REST `/vote` **Params:** `int $post_id`, `float $score`, `float $avg_score`, `int $total_votes` **`init_plugin_suite_review_system_after_criteria_review`** Trigger custom logic after a multi-criteria review is submitted. **Applies to:** REST `/submit-criteria-review` **Params:** `int $post_id`, `int $user_id`, `float $avg_score`, `string $review_content`, `array $scores` **`init_plugin_suite_review_system_get_reaction_types`** Customize available reaction types (labels + emojis). **Applies to:** Reactions system **Params:** `array $types` **`init_plugin_suite_review_system_reaction_meta_key`** Customize the meta key used for storing reaction counts. **Applies to:** Reaction counts storage **Params:** `string $meta_key`, `string $rx_key` **`init_plugin_suite_review_system_ttl`** Set TTL (in seconds) for object cache on review queries. Defaults to `300` (5 minutes) since 1.19. Return `0` to disable caching entirely, or any other value to change the duration. Cache is stored via `wp_cache_set` with group `init_review_system` and is automatically invalidated on new review submission and on admin approve/reject/delete. **Applies to:** REST get reviews, get total reviews **Params:** `int $ttl` (default `300`, i.e. `5 * MINUTE_IN_SECONDS`) == Screenshots == 1. **Plugin Settings Page** – Configure general options like login requirement, IP restriction, auto-display position, and up to 5 custom criteria fields. 2. **Single-Star Rating Display** – Star-based rating shown on a post with average score and vote count. 3. **Multi-Criteria Review Display** – Frontend layout showing score breakdown per criteria and full user review content. 4. **Reactions Bar** – Emoji-based reactions under the post, showing total counts, current user reaction highlight, and requiring login to interact. == Installation == 1. Upload plugin to `/wp-content/plugins/` 2. Activate via Plugins menu 3. Go to **Settings > Init Review System** to configure options == FAQ == = Is login required to vote? = Not by default. You can enable it in plugin settings. = Does it support IP protection? = Yes. You can enable strict IP check to prevent duplicate votes. = Can I use this with custom post types? = Yes, it works with any post type that uses `the_content()` or `comment_form()`. = Does it support multi-criteria reviews? = Yes. You can define up to 5 custom criteria and show them using the provided shortcode. = Does it support 10-star ratings? = No. The plugin currently supports only a 5-star scale. == Changelog == = 2.0.0 – August 4, 2026 = - **New: Abilities API support (WordPress 6.9+)**: registers three read-only abilities under the `init-review-system` category — `init-review-system/get-review-score` (average score + vote count), `init-review-system/get-criteria-reviews` (criteria breakdown + a page of written reviews), and `init-review-system/get-reactions-summary` (emoji reaction counts). All three are discoverable and executable via PHP, `wp_get_abilities()`, and — when a site opts in — the `wp-abilities/v1` REST namespace. Actions that write data (vote, submit review, toggle reaction) are intentionally not exposed as abilities. Fully optional and backward-compatible: on WordPress versions older than 6.9, the integration silently does nothing - **New: Block Editor (Gutenberg) support**: four dynamic blocks, grouped under their own **Init Review System** block category (instead of the generic "Widgets" category) — **Review Score**, **Review Widget**, **Review Criteria**, and **Reactions Bar**, matching `[init_review_score]`, `[init_review_system]`, `[init_review_criteria]`, and `[init_reactions]` respectively. Each block is registered via `block.json` (with a PHP `render.php` file wired through the `"render"` field, WP 6.1+) that calls the exact same shortcode function as its shortcode counterpart — no duplicated display logic, output always matches. The editor integration is a single, no-build-step vanilla JavaScript file using `wp.serverSideRender` for a live preview directly in the editor - **Changed**: the four shortcode handlers were converted from anonymous closures to named functions (`init_plugin_suite_review_system_shortcode_*`) so the new blocks' `render.php` files can call them directly instead of duplicating logic. No change in shortcode behavior or output - **Changed**: `Requires at least` raised from 5.5 to 6.9 to support the Abilities API integration. `Requires PHP` remains 7.4 - **Improved**: `/get-criteria-reviews` (REST) and `/reactions/summary` (REST) now share their core data-assembly logic with the new abilities via two internal helper functions, instead of the same query/formatting logic existing twice - `Tested up to: 7.1` = 1.19 – August 2, 2026 = - **Fixed:** vote totals (`_init_review_total` / `_init_review_count`) were updated with a read-then-write pattern that could silently drop a vote when two requests landed at nearly the same time on a busy post. Now uses an atomic SQL increment so concurrent votes are never lost. - **Fixed:** reaction counters could drift from real data for the same read-then-write reason. Reaction counts are now computed directly (COUNT) from the `init_reactions` table — the table is the single source of truth — with a 1 hour object cache. Post meta `_irs_rx_*` is kept in sync for backward compatibility (e.g. sites using it in `orderby`/`meta_query`) but is no longer authoritative. - **Fixed:** the DB upgrade routine only ran table creation when a table was *missing*, so schema changes in new versions (like the indexes below) never reached sites that already had the plugin installed. It now always re-runs `dbDelta()`, which is additive/safe by design. - **Fixed:** after submitting a multi-criteria review, the frontend re-computed the new overall average/breakdown with a client-side moving-average formula based on numbers snapshotted at page load — if another visitor submitted a review in the meantime, the displayed score could be briefly wrong until the page was reloaded. `/submit-criteria-review` now returns the real, freshly-queried `summary` (overall_avg/breakdown/total) and the frontend just renders it directly. - **Changed:** `init_plugin_suite_review_system_get_reviews_by_post_id()` / `..._get_total_reviews_by_post_id()` are now cached for 5 minutes by default (was disabled by default). Still fully overridable via the `init_plugin_suite_review_system_ttl` filter (return `0` to disable). Cache is versioned per post and correctly invalidated on new review submission **and** on admin approve/reject/delete (single or bulk) — previously those admin actions modified the DB directly without clearing any cache. - **Added:** composite indexes `(post_id, status)` and `(user_id, status)` on `init_criteria_reviews` to speed up the most common lookups on large sites. - **Improved:** `init_plugin_suite_review_system_get_score_summary_by_post_id()` now computes the overall average with SQL `AVG()` instead of summing every row in PHP. - **Improved:** admin review list and the `/get-criteria-reviews` REST endpoint now prime post/user object caches in bulk instead of calling `get_post()` / `get_userdata()` once per row. = 1.18 – April 21, 2026 = - Fixed `admin_init` running database table checks on every admin page load - Introduced `irs_plugin_db_version` option flag to gate table checks behind a version comparison - Table creation now only runs when stored DB version is lower than current plugin version - Added `register_activation_hook` to handle fresh installs and multisite network activation - Added `wpmu_new_blog` hook to provision tables automatically on new multisite subsite creation - Added `upgrader_process_complete` hook to reset version flag after plugin update, triggering re-check on next admin load = 1.17 – March 25, 2026 = - Added object cache (1 hour TTL) to `init_plugin_suite_review_system_get_score_summary_by_post_id()` - Added object cache (1 hour TTL) to `init_plugin_suite_review_system_has_user_reviewed()` - Added object cache (1 hour TTL) to `init_plugin_suite_review_system_get_user_reaction()` - Cache for `has_user_reviewed` and `get_user_reaction` stores sentinel values to distinguish cache miss from negative results - Cache invalidation for score summary and has-reviewed fires on `init_plugin_suite_review_system_after_insert` action hook - Cache invalidation for user reaction fires inside `init_plugin_suite_review_system_apply_user_reaction()` after each write = 1.16 – March 1, 2026 = - Fixed Load More showing wrong user avatar and display name - Fixed post-submit review rendering hardcoded HTML incompatible with custom templates - Added `review-item.php` as standalone overridable template - Added `init_plugin_suite_review_system_get_criteria_by_post_id()` helper - Added `init_plugin_suite_review_system_get_review_by_id()` helper - REST endpoints now server-render review HTML; JS only injects, no longer builds DOM - Removed unused `insertNewReview()` JS function = 1.15 – February 25, 2026 = - Added `$per_page` cap (max 100) in REST endpoint to prevent excessive data queries - Added `init_plugin_suite_review_system_ttl` filter (default `0`) to opt-in object cache for review queries - When TTL > 0, query results are cached via `wp_cache_set` with group `init_review_system` - Cache invalidation can be handled via `init_plugin_suite_review_system_after_insert` action hook = 1.14 – January 29, 2026 = - Introduced **Bayesian Weighted Rating** for reliable ranking calculations - New derived meta key `_init_review_weighted` generated **on vote submission** - Prevents low-sample bias (e.g. 1×5★ no longer outranks 1000×4.9★) - Uses configurable minimum vote threshold via filter `init_plugin_suite_review_system_min_votes_threshold` - Rating architecture improvements: - Weighted score is calculated **on-write** (at REST endpoint), not at query time - Establishes clear separation between **raw data** and **ranking data** - Plugin now acts as the single source of truth for rating-based sorting - Performance & stability: - Global average rating is cached using **transients** for cross-environment reliability - Automatic transient invalidation on new vote to prevent stale calculations - Developer experience: - Enhanced `init_plugin_suite_review_system_after_vote` hook now passes: - New average score - Total vote count - Weighted ranking score - Fully backward compatible — no changes required for existing consumers - No UI or frontend behavior changes - No breaking changes to REST API responses = 1.13 – January 19, 2026 = - Added `.dark` modifier for `.init-reaction-bar` to support Dark Mode - Improved visual contrast for reactions in dark environments - Polished hover, active and disabled states in Dark Mode - Pure CSS enhancement, no JS or logic changes = 1.12 – November 14, 2025 = - Added **anti–double-submit protection** to the front-end rating widget - New `isSubmitting` state prevents rapid multi-clicks from firing multiple requests - Fully compatible with both **single-click** and **double-click confirmation** modes - Pending hover logic (`.hovering`) remains intact with zero behavior changes - Improved client-side stability: - Ensures only **one** vote request is sent at a time - Allows retrying when the API returns an error (non-blocking UX) - No changes to REST API, server logic, or existing rating flow — fully backward compatible = 1.11 – November 5, 2025 = - Added **User Review Fetching API** - New function `init_plugin_suite_review_system_get_reviews_by_user_id()` - Supports pagination (`paged`, `per_page`) just like the post-based fetcher - Automatically joins with `wp_posts` to **exclude orphaned reviews** - Returns unserialized `criteria_scores` for direct UI rendering - Added **Total Pages Counter for User Reviews** - New function `init_plugin_suite_review_system_get_total_pages_by_user_id()` - Counts reviews belonging to a specific user (filtered by `status`) - Returns the total number of pages (minimum value: `1` for UX consistency) - Performance and internal improvements: - Consistent sanitization and safety (`absint()`, `max()`, typed values) - All SQL queries use `$wpdb->prepare()` — no unbound parameters - PHPCS/WPCS compliant and follows plugin prefix standard = 1.10 – November 3, 2025 = - Added **Admin Reset & Manual Score Adjustment Metabox** - Visible **only when a post already has rating data** - New UI: compact **stat chips** showing Avg / Votes / Total (premium-style mini cards) - Supports manual override: admin can set **Average (0–5)** and **Vote Count** - Internal logic auto-calculates `Total = Avg × Count` (rounded to 2 decimals) - All displayed values are fully escaped (`esc_html() / esc_attr()`) following PHPCS - Metabox is registered **per-post**, not globally — zero UI clutter - Security and permissions: - Only users with capability `edit_others_posts` (**Editor+**) can view or interact with the metabox - `save_post` handler validates **nonce + capability**, protects against unauthorized POST submissions - Input sanitized: `sanitize_text_field( wp_unslash() )` before casting to float/int - UX improvements: - No inline `