Shopper's Mind (Fork by Goran Brbot)
Fork of Shopper's Mind WooCommerce export plugin, updated for PHP 8.1+/current WordPress compatibility
by Shopper's Mind · github.com/goranbrbot/wp-plugin-jeftinije
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/goranbrbot/wp-plugin-jeftinije/archive/refs/heads/main.zipReadme
wp-plugin-jeftinije
Fork of the Shopper's Mind WooCommerce plugin, updated for PHP 8.1+ and current WordPress. Original plugin by Shopper's Mind (smind.si) exports WooCommerce products as an XML feed for the comparison-shopping platforms ceneje.si, jeftinije.hr, idealno.rs and idealno.ba.
Why this fork exists
An older custom page-jeftinije.php export script was audited for compatibility with current WordPress/PHP. That audit surfaced two structural problems that were hard to fix cheaply:
- The script relied on a
page-{slug}.phptemplate, which block themes can ignore entirely — the feed can silently stop working on theme switch. - Reading WooCommerce product attributes mixed array- and object-style access, which throws a fatal error on taxonomy-based attributes.
Shopper's Mind already solves both: it registers a WordPress REST API route (theme-independent) and consistently uses get_terms()/get_options() for attributes. Rather than rewrite a plugin from scratch, this fork takes Shopper's Mind's source (last released ~2021, PHP 5.2+) and brings it up to date instead.
Changelog
Changes are grouped by the plugin version they shipped in (matches readme.txt) — append a new ### subsection here for each future version rather than one flat list, so it stays clear what happened when.
1.0.3 — initial fork release
Verified with phpcs against the PHPCompatibilityWP standard, testVersion 8.1- (zero findings) plus manual review:
- Fixed broken CDATA tag —
src/Export/WooCommerceExport.php: thepluginVersionnode was missing its opening<![CDATA[, so the tag content was emitted as literal text instead of a CDATA section, producing invalid-looking output. - Added
permission_callbackto the REST export route —src/Export/XMLEndpoint.php: missing on WP 5.5+ triggers a_doing_it_wrongnotice. Set to__return_truesince the feed is intentionally public (comparison-shopping platforms fetch it unauthenticated). - Guarded against null/false returns from WooCommerce APIs in
src/Export/WooCommerceExport.php, any of which could previously throw a fatal error and abort the whole export mid-run:wc_get_product()for a variation ID that no longer resolves to a product.get_term_by()when a variation's stored attribute value doesn't match a term.wc_get_attribute()when a configured attribute ID no longer exists.- A product with no assigned category (indexing an empty array).
- Raised
Requires PHPto 8.1 in the plugin header andreadme.txt. - Fixed the
pluginVersionvalue reported in the feed —src/Config/Config.php: was hardcoded to1.0.1, one behind the actual plugin version; caught while validating the live feed output. - Renamed the plugin and added an
Update URIheader —shoppers_mind.php: WordPress derives the "View plugin details" slug from thePlugin Nameheader, and since this fork kept the original name (Shopper's Mind), that slug collided with the original plugin's real listing on wordpress.org. WordPress admin was showing that plugin's version, changelog, "tested up to" and contributors instead of this fork's — and would have offered an "update" that silently overwrites this fork with the unfixed original. Renaming toShopper's Mind (Fork by Goran Brbot)breaks the collision; the addedUpdate URIheader is WordPress's own mechanism (since 5.8) for telling core not to check wordpress.org for updates on a fork.
1.0.4 — full audit pass
A follow-up pass reviewed the remaining parts of the plugin not touched by the initial fork work (admin settings, widget, script enqueueing, plugin header) for 2026-era WordPress/WooCommerce standards:
- Fixed a fatal error on every wp-admin page when WooCommerce is deactivated —
shoppers_mind.php: the settings-page bootstrap insrc/Admin/AdminPluginForm.phpran on the globaladmin_inithook and calledget_woocommerce_currency_symbol()unconditionally, with noclass_exists('WooCommerce')guard anywhere in the codebase. The entire plugin bootstrap now short-circuits (showing an admin notice instead) when WooCommerce isn't active. - Added a
Requires Plugins: woocommerceheader (the WP 6.5+ mechanism for declaring plugin dependencies) so WordPress blocks activation without WooCommerce, and also guarantees WooCommerce loads first so the runtimeclass_existscheck above is reliable. - Declared WooCommerce HPOS (custom order tables) compatibility on
before_woocommerce_init— the plugin never touches order data, but WooCommerce shows an incompatibility notice for any plugin lacking the explicit declaration. - Fixed a
wp_enqueue_script()misuse —src/Plugin.php: the second parameter is$src, not$deps;wp_enqueue_script($scriptName, array('jquery'))silently corrupted the registered script's source and never actually declared the jQuery dependency. - Escaped output that was missing it — the widget title in
src/Widgets/CsTrustmarkWidget.php(esc_html) and an admin help link inAdminPluginForm.php(esc_url), for consistency with theesc_url()/esc_attr()pattern already used elsewhere on that page. - Used a real capability instead of a role name for the settings page (
manage_optionsinstead of'administrator'), per the WordPress Plugin Handbook's capability-check guidance. - Tightened sanitization of the XML feed URL setting to a safe REST-route character set, since it's passed directly as a
register_rest_route()path segment. - Fixed three sanitize callbacks that wiped a setting to empty on invalid input instead of keeping the previously stored value.
- Renamed the widget class to
CsTrustmarkWidgetto match its filename (it wasTrustmarkWidget), for consistency now that the codebase doesn't use PSR-4 autoloading. - Reworked the plugin
Descriptionheader —shoppers_mind.php: removed a phrase duplicating theRequires PHP/Requires at leastheaders, and added a dedicated line (via<br>, which WordPress's plugin-list rendering allows) showing the last-updated date and a PHP/WordPress compatibility summary.
One esc_html__() call was added (the WooCommerce-inactive admin notice, text domain wp-plugin-jeftinije), but there's no Text Domain header or load_plugin_textdomain() call, so it can't actually load a translation yet — full i18n (declaring the text domain and wrapping the rest of the admin UI strings) is intentionally still skipped, since this is an internal single-site fork, not slated for wordpress.org. Also intentionally left as-is: switching the manual require_once chain to Composer's PSR-4 autoloading (no functional benefit for a plugin this size).
Building an installable zip
powershell -File scripts\build-zip.ps1
Writes the current runtime files (no .git, vendor/, composer.*, or this README) to dist/wp-plugin-jeftinije.zip, ready to upload via Plugins → Add New → Upload Plugin. Re-run it after any code change to keep dist/ at the latest version; the zip itself is git-ignored, so it's always a local build, not a stale committed copy.
The script also stamps today's date into the "Datum ažuriranja verzije" line of the plugin's Description header in shoppers_mind.php before zipping, so that date always reflects when the zip was actually built.
Development
Dev tooling (squizlabs/php_codesniffer + phpcompatibility/phpcompatibility-wp) is declared in composer.json as dev dependencies only — not required at plugin runtime, and vendor/ is git-ignored.
composer install
vendor/bin/phpcs --standard=PHPCompatibilityWP --runtime-set testVersion 8.1- shoppers_mind.php src/
License
GPLv2 or later, same as upstream. See LICENSE.txt. Copyright 2016–2021 Shopper's Mind; fork changes as noted above.