=== Init Review System – Reactions, Multi-Criteria, Guest-Friendly ===
Contributors: brokensmile.2103
Tags: review, rating, vote, reaction, schema
Requires at least: 5.5
Tested up to: 7.0
Requires PHP: 7.4
Stable tag: 1.18.2
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Fast 5-star rating plugin with schema, REST API, shortcode control, localStorage voting. Now with multi-criteria review support.

== 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** and an **emoji reactions system** for richer user interaction.

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:**

- 5-star voting via frontend
- **NEW: Multi-criteria review support**
- **NEW: 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 ==

- 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

== 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 `0` (cache disabled). Set a value greater than `0` to enable caching via `wp_cache_set` with group `init_review_system`.
**Applies to:** REST get reviews, get total reviews
**Params:** `int $ttl` (default `0`)

== 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 ==

= 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 `<style>` tags — every style moved to inline attribute (WordPress admin standard)
  - Metabox does **not appear at all** until the first vote exists (no "empty" box)

= 1.9 – October 31, 2025 =
- Added **Double-Click Confirmation Rating** (anti-misclick mechanic)
  - New setting: `Require double-click to rate`
  - When enabled, users must click the same star **twice** to confirm rating
  - First click enters **pending state**, visually marked with `.hovering`
  - Second click submits rating and disables the block as usual
- JavaScript improvements:
  - New pending handler with auto-timeout (2.2s) — no accidental lock state
  - `.hovering` class persists across mouseleave while pending
  - Clean async state teardown ensures no ghost states
- No UI popups, no alerts — rating confirmation is **purely action-based**
- Zero breaking changes — existing rating logic untouched

= 1.8 – October 6, 2025 =
- Updated function `init_plugin_suite_review_system_get_reviews_by_post_id()`:
  - When `$post_id = 0`, orphaned reviews are now automatically excluded (posts that no longer exist across all CPTs).
- Shortcode `[init_review_criteria]` enhanced:
  - Added new attribute `paged` to control review pagination.
  - Internal call now passes `$paged` dynamically to `init_plugin_suite_review_system_get_reviews_by_post_id()`.

= 1.7 – September 13, 2025 =
- Added content moderation: banned words, banned phrases, no-whitespace, excessive repetition
- New settings: enable JS precheck (optional), manage banned words/phrases in textarea fields
- Improved modal UX: must rate all required criteria, inline error/success messages under submit button
- JavaScript updated: i18n error mapping, client-side prechecks, red outline highlight for missing scores
- CSS enhancements: `.init-review-inline-msg` for messages, `.init-review-criteria-error` for criteria validation (with dark mode support)

= 1.6 – September 2, 2025 =
- Reactions endpoint `/reactions/toggle` now **requires login**: enforced `is_user_logged_in()` and nonce validation
- Updated REST API registration: `permission_callback` for `/reactions/toggle` now only allows logged-in users
- Shortcode `[init_reactions]` updated: `require_login` is always `true`, ensuring consistent frontend behavior
- Improved JS logic: 
  - Removed guest/localStorage fallback (no more anonymous reactions)
  - Preserved initial `disabled` state for buttons; guest users always see counts but cannot interact
  - Fixed bug where guest buttons could be re-enabled after API calls
- CSS adjustments: new `.is-disabled` style keeps emoji + counts **fully visible**, while visually indicating login requirement
- Enhanced accessibility: reaction buttons now toggle `aria-pressed` accurately and support `is-active` state for current user reaction
- Code cleanup: removed obsolete guest handling code paths and simplified state management

= 1.5 – September 1, 2025 =
- Enhanced Reactions System with **total reactions counter** displayed under “What do you think?”
- Added unique `id` for total counter span (`irs-total-reactions-{post_id}`) to support JS live updates
- Updated JavaScript: total reaction count now updates instantly when users toggle reactions
- Synced UIkit theme template with total reactions output for consistent frontend display
- Added CSS styles for `.init-reaction-total` (font weight, spacing, responsive display)
- Improved accessibility: total reactions wrapped with `aria-live="polite"` for screen reader updates
- Minor code cleanups and consistency improvements

= 1.4 – August 31, 2025 =
- Introduced **Reactions System**: emoji-based reactions (👍 😄 😍 😯 😠 😢)
- Added shortcode + template for reactions bar
- Reactions stored in both post meta (counts) and dedicated `init_reactions` table (user↔post map)
- Guest-friendly: works without login (tracked via localStorage)
- Added developer filters:
  - `init_plugin_suite_review_system_get_reaction_types`
  - `init_plugin_suite_review_system_reaction_meta_key`
- Internal refactor: extracted reaction-core functions, silent table creation with `dbDelta()`

= 1.3 – August 25, 2025 =
- Restructured admin interface: moved from Settings submenu to dedicated main menu with star icon
- Added comprehensive review management system with bulk operations and filtering capabilities
- Implemented review approval workflow with pending/approved/rejected status management
- Enhanced admin dashboard with review statistics, search functionality, and pagination
- Added individual review actions: approve, reject, delete with proper nonce security
- Integrated bulk actions for managing multiple reviews simultaneously
- Created dedicated review management page with detailed review display and user information
- Improved database queries with proper prepared statements and PHPCS compliance
- Added admin-only review management scripts with proper enqueueing standards
- Refined plugin architecture for better scalability and maintainability

= 1.2 – July 27, 2025 =
- Standardized all output with `esc_html()`, `esc_attr()`, and `wp_kses()` for frontend safety
- Secured REST API endpoint with enforced login + nonce validation for logged-in users
- Added `uninstall.php` to clean up plugin options when uninstalled
- Refined UI labels and descriptions on settings page
- Improved shortcode documentation and attributes in `readme.txt`
- Enhanced multi-criteria input layout for better clarity

= 1.1 – July 11, 2025 =
- Added support for multi-criteria reviews
- New shortcode for criteria-based score display
- Separate logic and schema handling for criteria reviews
- Improved review interface and modal UX
- Refactored code to support both single and multi-criteria review paths

= 1.0 – June 28, 2025 =
- Initial release
- Shortcode `[init_review_system]` for 5-star voting block
- Shortcode `[init_review_score]` for average score display
- REST API endpoint `/wp-json/initrsys/v1/vote` with conditional login and nonce check
- Vote tracking via `localStorage` for guest users
- Optional login restriction + strict IP check using hashed IP + transient
- JSON-LD schema output using `AggregateRating`
- Auto-insert system: before/after post content or comment form
- Admin settings page with shortcode builder and control toggles
- Minimal, responsive, theme-inheriting design
- Developer-friendly: filters, actions, and reusable helpers

== License ==

This plugin is licensed under the GPLv2 or later.
You are free to use, modify, and distribute it under the same license.
