WP Manifestindependent plugin directory
manifest / media / ecom-wp-reviews

BHT Reviews

Front-end review showcase for WordPress, driven by the ACF `review` CPT.

by Blue Heart Travel · github.com/josipmestrovic/ecom-wp-reviews

0stars
0forks

Install

No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:

wp plugin install https://github.com/josipmestrovic/ecom-wp-reviews/archive/refs/heads/main.zip

Module version: 1.1.1 — repo: josipmestrovic/ecom-wp-reviews

Reviews slider preview

Front-end review showcase for Blue Heart Travel, driven by the ACF review CPT. Self-contained Divi child-theme module — drop the folder in, require the bootstrap from functions.php, and you have:

  • [ecom-reviews-slider] — Swiper carousel of all published reviews
  • [ecom-reviews-grid] — paginated grid (15 per page) with AJAX Load more
  • A Reviews → Settings panel to pick the site-wide sort order
  • A polished review CPT admin list (avatar, rating stars, date, excerpt, rating filter, smart search across reviewer name + content, sortable headers)
  • Auto-synced post titles + slugs from the ACF reviewer-name field

Install

Option A — clone (recommended, lets you git pull updates)

From the theme root:

cd wp-content/themes/<your-theme>
git clone https://github.com/josipmestrovic/ecom-wp-reviews.git reviews

The folder MUST end up named reviews/ — the bootstrap path below depends on it.

Option B — manual copy

Download the repo as a ZIP, extract, and drop the reviews/ folder into wp-content/themes/<your-theme>/.

Activation (both options)

Add this single line to your child theme's functions.php:

require_once get_stylesheet_directory() . '/reviews/reviews.php';

No DB schema and no migrations — the module piggybacks on the existing ACF review CPT and its postmeta.

Dependency: ACF (free or Pro)

This module reads its data from the ACF review CPT and a single ACF field group. Both are bundled in this repo as a ready-to-import export:

  • File: docs/acf-review-export.json
  • Import via WP Admin → ACF → Tools → Import — this registers the review CPT and the Reviews fields field group in one go.

After import you can edit / extend either the CPT or the field group in the ACF UI; the export in this repo is the canonical baseline.


File structure

reviews/
├── README.md                  ← you are here
├── screenshot.png             ← README preview image
├── reviews.php                ← thin bootstrap, requires every includes/*.php
├── reviews.css                ← shared front-end styles (CSS variables)
├── reviews.js                 ← Swiper init, Read-more toggle, AJAX load-more
├── docs/
│   └── acf-review-export.json ← ACF Tools → Import this to register the `review` CPT + fields
├── vendor/
│   └── swiper/                ← vendored Swiper bundle (CSS + JS), version pinned
└── includes/
    ├── cpt.php                ← CPT visibility override + title sync + auto-draft fallback
    ├── assets.php             ← register CSS/JS + wp_localize_script(EcomReviews)
    ├── query.php              ← ecom_reviews_get_query_args() + avatar-first SQL ordering
    ├── card.php               ← ecom_reviews_render_card() + initials helper
    ├── shortcode-slider.php   ← [ecom-reviews-slider]
    ├── shortcode-grid.php     ← [ecom-reviews-grid]
    ├── handler.php            ← AJAX: ecom_reviews_load_more
    ├── admin-list-table.php   ← review CPT columns / sortable / search / rating filter
    └── admin.php              ← Settings submenu + ecom_reviews_settings option

ACF fields consumed

The module reads the following fields off each review post. Full definitions live in docs/acf-review-export.json.

Field Type Req. Notes
first_and_last_name_of_a_reviewer text (max 100) yes Used as post title + slug; drives initials avatar fallback. Placeholder: John Doe.
user_avatar image (array, return array) no Falls back to a coloured initials circle if empty.
date_of_a_review date_picker (display + return F j, Y, week starts Mon) yes Sortable.
review_rating button_group 1..5 (string), default 5, horizontal yes Sortable + filterable.
review_content textarea yes Searchable. Placeholder: "Type here what user said about the experience with Blue Heart Travel...".

review CPT (registered by the bundled ACF export)

Setting Value
Post type slug review
Public / queryable yes / yes
show_in_menu / show_in_admin_bar / show_in_rest yes
Menu position 3
Menu icon dashicons-star-filled
supports (per ACF export) custom-fields only \u2014 the module re-adds title support at runtime via add_post_type_support() in includes/cpt.php so the auto-synced reviewer name has somewhere to live.
has_archive no
Hierarchical no
Capabilities inherit post

Sort modes (Reviews → Settings)

Stored as wp_options.ecom_reviews_settings{ sort: <slug> }. Applies to both shortcodes.

Slug Behaviour
image_rating_date (default) Reviews with an avatar first, then highest rating, then newest.
rating_date Highest rating, then newest.
date_desc Newest first.
random Random — stable per page load when the ecom_reviews_seed cookie is set.

The "image first" priority is implemented via a posts_clauses filter (see includes/query.php) because WP_Query has no first-class way to order by "a meta key exists AND is non-empty".


Public surface

Surface Where Notes
[ecom-reviews-slider] includes/shortcode-slider.php Swiper carousel of all reviews.
[ecom-reviews-grid] includes/shortcode-grid.php 15-per-page grid with Load more.
AJAX action ecom_reviews_load_more includes/handler.php Nonce: ecom_reviews_load_more. Logged-in + nopriv.
Option ecom_reviews_settings includes/admin.php { sort: <slug> }. Sanitised against the choice whitelist.
JS global EcomReviews includes/assets.php (localised) { ajax_url, nonce, per_page, i18n }.
Constant ECOM_REVIEWS_PER_PAGE reviews.php 15.
Constant ECOM_REVIEWS_SWIPER_VERSION reviews.php Pinned Swiper CDN version.
Constant BHT_REVIEWS_DIR / BHT_REVIEWS_VERSION reviews.php Module paths / version (parity with sibling modules).

Public PHP functions

Function File Purpose
ecom_reviews_get_sort() includes/admin.php Returns the active sort slug (validated).
ecom_reviews_sort_choices() includes/admin.php Sort-mode label map.
ecom_reviews_get_query_args( $sort, $paged, $per_page ) includes/query.php Builds WP_Query args.
ecom_reviews_render_card( $post_id ) includes/card.php Returns one card's HTML.
ecom_reviews_initials( $name ) includes/card.php Reviewer-name initials helper.
ecom_reviews_enqueue() includes/assets.php Enqueues Swiper + module CSS/JS on demand.

Admin UI

review list table

  • Columns: Avatar (image or coloured initials) · Reviewer · Rating (★★★★★) · Review date · Excerpt (120 chars) · Published.
  • Sortable headers: Reviewer (title), Rating (review_rating numeric), Review date (date_of_a_review).
  • Search: matches post_title, first_and_last_name_of_a_reviewer, and review_content (joined via a posts_clauses filter — ecom_reviews_admin_search_clauses).
  • Rating filter dropdown: "All ratings" + 5..1 stars; combines (AND) with search.
  • Auto title sync: when the reviewer-name field changes, the post title and slug are updated on acf/save_post (recursion-guarded). A the_title filter shows the reviewer name as a fallback for any unsynced legacy rows.

Reviews → Settings

manage_options only. Single radio group choosing one of the four sort modes above. Saved via the standard Settings API.


How to extend

Want to… Edit
Add a new sort mode includes/query.php (add a case in ecom_reviews_get_query_args()) and includes/admin.php (add a label to ecom_reviews_sort_choices()).
Change the card markup includes/card.phpecom_reviews_render_card().
Bump Swiper version Update ECOM_REVIEWS_SWIPER_VERSION in reviews.php, then re-download the two vendor/swiper/swiper-bundle.min.* files from https://cdn.jsdelivr.net/npm/swiper@<version>/.
Tweak per-page count ECOM_REVIEWS_PER_PAGE in reviews.php.
Change admin columns includes/admin-list-table.php.
Add another shortcode Drop a new includes/shortcode-*.php file and require_once it from reviews.php.

Working with this component (git workflow)

The folder on disk is a clone of the GitHub repo — there's no subtree, submodule, or Composer layer. Every command below is plain vanilla git, run from inside reviews/.

Pull updates from upstream

cd wp-content/themes/<your-theme>/reviews
git pull

Hard-refresh the browser to defeat asset caching.

Make a change and push it

git status
git add .
git commit -m "Fix: <short description>"
git push

"I edited the same file on two sites"

Whichever clone pushes first wins. The second one will see rejected — non-fast-forward on git push. Resolve with:

git pull        # may produce a merge conflict — resolve in editor
git add .
git commit -m "Merge upstream"
git push

Tip: git pull before starting any non-trivial edit saves you the merge dance.

Release a new version

  1. Bump the version in two places:
    • BHT_REVIEWS_VERSION constant + plugin header in reviews.php.
    • "Module version" line at the top of this README + a Changelog entry.
  2. Commit, tag, push:
git add .
git commit -m "Release 1.2.0: <one-line summary>"
git tag v1.2.0
git push
git push --tags

Other sites opt into the new version with their next git pull. To pin a site to a specific version (no future auto-updates):

git checkout v1.2.0   # detached HEAD on that exact tag

Semver guideline

Change Bump
Bug fix, no behavior change patch (1.1.11.1.2)
New feature, backward compatible minor (1.1.11.2.0)
Breaking change (removed shortcode arg, renamed public function, renamed CSS class consumers depend on, ACF field rename) major (1.1.12.0.0)

Common pitfalls

Symptom Cause Fix
git push rejected (non-fast-forward) Someone else pushed first git pull, resolve any conflict, git push
Shortcodes stop rendering after pull The require_once line in functions.php was removed, or PHP opcache stale Re-check functions.php; clear opcache / WP cache
Old CSS / JS after pull Browser cache Hard refresh (Ctrl+Shift+R)
fatal: not a git repository You're outside the reviews/ folder cd into it first
Nested .git warning when wrapping the parent theme in git Component was cloned before parent was git-init'd Add reviews/ to the parent's .gitignore

Changelog

1.1.1 — 2026-05-06

  • Grid: sibling cards no longer stretch when one card is expanded via Read more. The shared .ecom-review rule keeps height: 100% for the slider (each card fills its flex swiper-slide), but the grid now overrides it with height: auto; align-self: start; on .ecom-reviews-grid__items .ecom-review, matching the slider's independent-height behaviour.

1.1.0 — 2026-05-06

Consolidated visual / typography / interaction refresh of the reviews module.

Card surface

  • Removed box-shadow, background changed to #FCFCFD, border-radius increased to 18px (--ecom-review-card-bg, --ecom-review-card-radius, --ecom-review-card-shadow).
  • Card flex gap reset to 0 so explicit per-element spacing is authoritative.
  • Independent card heights: grid uses align-items: start and slider slides use align-self: flex-start, so expanding Read more on one card no longer stretches sibling cards in the same row.

Typography

  • Reviewer name: 20px / weight 500, colour #384049.
  • Review date: 18px / weight 400, colour #384049.
  • Review text: 18px / 1.6 line-height / weight 400 / colour #384049.
  • Read more toggle: 18px / weight 500, colour #909090.
  • Mobile (<= 600px): every text element drops by 2px (avatar initials 20→18, name 20→18, date 18→16, review text 18→16, Read more toggle 18→16, Load more button 18→16).

Avatar

  • Image and initials variants now render at 50×50. Initials are 20px on desktop and 18px on mobile. card.php image markup updated to width="50" height="50".

Spacing

  • Stars row has 12px top/bottom spacing.
  • Read more toggle: 30px top spacing; bottom margin set to 4px so that, combined with the card's 22px bottom padding, the visible space below the toggle is 26px.

Slider arrows

  • Replaced chevron SVGs with the long-arrow vector (25×18, currentColor fill). The next button reuses the same SVG rotated 180° via .ecom-reviews-slider__nav--next svg.
  • Restyled buttons: 18.5px border-radius, 1px solid #CED3DC border, no box-shadow, arrow colour #384049.
  • Pinned vertically on desktop: pinSliderArrows() measures the first non-expanded card on init/resize and writes --ecom-reviews-arrow-top, so expanding Read more no longer pushes the arrows downward.
  • Hidden on tablet / mobile (<= 1023px).

Slider scrollbar (tablet / mobile)

  • New Swiper scrollbar indicator (.ecom-reviews-slider__scrollbar): 3px track in #E5E7EB, draggable thumb in #384049. Hidden on desktop.
  • Centered at 80% width with 12px top spacing; sits directly below the tallest visible slide and grows with it when a card is expanded.
  • Slider collapses any expanded cards on slideChange, so the swiper wrapper returns to its natural height and the scrollbar / arrows snap back to their resting position when the user navigates.

Load more button

  • Typography updated to 18px / weight 500 with 5% letter-spacing (16px on mobile <= 600px).
  • Renders the same long-arrow SVG as the slider arrows, rotated 90° to point downward, alongside the label (.ecom-reviews-grid__load-more-label + .ecom-reviews-grid__load-more-icon).
  • AJAX handler swaps only the label text during loading so the icon is preserved across the request lifecycle.

Docs / version

  • Added this Changelog section to record module version history.
  • Bumped BHT_REVIEWS_VERSION and plugin header to 1.1.0.

1.0.0 — Initial release

  • [ecom-reviews-slider] Swiper carousel of all published reviews.
  • [ecom-reviews-grid] paginated 15-per-page grid with AJAX Load more (ecom_reviews_load_more action, nonce-protected, logged-in + nopriv).
  • Reviews → Settings panel with four sort modes: image_rating_date (default), rating_date, date_desc, random (stable per-session via ecom_reviews_seed cookie).
  • Avatar-first ordering implemented via posts_clauses filter in includes/query.php.
  • review CPT admin list customisations: avatar/initials column, rating stars, review-date column, excerpt, sortable headers, rating-filter dropdown, smart search across reviewer name + content.
  • Auto title + slug sync from ACF first_and_last_name_of_a_reviewer on acf/save_post (recursion-guarded), with the_title fallback for legacy unsynced rows.
  • Vendored Swiper 11.1.4 under vendor/swiper/, version pinned via ECOM_REVIEWS_SWIPER_VERSION.
  • Lazy asset loading: CSS/JS only enqueued when a shortcode renders.
  • Public surface: ecom_reviews_settings option, EcomReviews JS global, ECOM_REVIEWS_PER_PAGE, BHT_REVIEWS_DIR, BHT_REVIEWS_VERSION.