{"id":274427,"date":"2026-01-13T21:12:28","date_gmt":"2026-01-13T21:12:28","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/store-manager-tools\/"},"modified":"2026-05-26T10:04:07","modified_gmt":"2026-05-26T10:04:07","slug":"smt-toolkit","status":"publish","type":"plugin","link":"https:\/\/pe.wordpress.org\/plugins\/smt-toolkit\/","author":14521254,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"1.3.4","stable_tag":"trunk","tested":"7.0","requires":"5.8","requires_php":"8.0","requires_plugins":null,"header_name":"SMT Toolkit for WooCommerce","header_author":"limchik","header_description":"Modular toolkit for store automation: imports, discounts, transliteration and cron-based workflows.","assets_banners_color":"545a6b","last_updated":"2026-05-26 10:04:07","external_support_url":"","external_repository_url":"","donate_link":"https:\/\/ko-fi.com\/J3J41RU9NJ","header_plugin_uri":"https:\/\/truffle.od.ua","header_author_uri":"","rating":0,"author_block_rating":0,"active_installs":0,"downloads":488,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","faq","changelog"],"tags":[],"upgrade_notice":{"1.3.4":"<p>Fixes a token-refresh stalemate (a single failed cron run could lock retries out for 45 days), restores checkout-page tracking by moving the disabler to <code>template_redirect<\/code>, drops the deprecated Facebook <code>link<\/code> parameter, schedules Threads publish 60s after creation, and migrates LinkedIn Post Share to the current <code>\/rest\/posts<\/code> endpoint. Cache-correctness fix for the tracking disabler (DONOTCACHEPAGE) plus a <code>script_loader_tag<\/code> last-resort tag filter. Upgrade strongly recommended.<\/p>","1.3.3":"<p>Social token maintenance update. Adds 45-day refresh checks for providers that support refresh tokens and keeps Mastodon\/OAuth1 providers stable without forced rotation.<\/p>","1.3.2":"<p>Fixes add-to-cart from single product pages and Quick View after the AJAX Archive CSRF guard was added.<\/p>","1.3.1":"<p>Packaging fix: includes the missing Nova Poshta checkout helper class required by Store Settings checkout helpers.<\/p>","1.3.0":"<p>Security &amp; stability release. Hardens Google Drive importer, AJAX add-to-cart, Nova Poshta rate-limit, social AJAX, wishlist JS escaping, SVG sanitiser and newsletter queue. Fixes a points double-credit edge case on partial-refund \/ re-completion sequences and adds locks against several read-modify-write races. Major performance wins: discounts profile cache eliminates N+1 on every cart calculate, store-settings best-seller cache invalidates on order completion, AI Search primes term cache, GDI bounds memory in long-running cron. Upgrade strongly recommended.<\/p>","1.2.0":"<p>Six new modules: AI Search, Wishlist, Rewards, Abandon Cart, Newsletter and Social. Refund-aware rewards accrual, sidebar refresh after AJAX filtering, smoother grid transitions and many fixes. Upgrade recommended.<\/p>","1.1.0":"<p>New Role-Based Pricing module. Internal data layer and caching rewritten for better performance. Several bug fixes - upgrade recommended.<\/p>","1.0.0":"<p>Initial release.<\/p>"},"ratings":[],"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3439002,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3439002,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256}},"assets_banners":{"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3439002,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3439002,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{},"all_blocks":[],"tagged_versions":[],"block_files":[],"assets_screenshots":{"screenshot-1.png":{"filename":"screenshot-1.png","revision":3439002,"resolution":"1","location":"assets","locale":"","width":1600,"height":694},"screenshot-2.png":{"filename":"screenshot-2.png","revision":3439002,"resolution":"2","location":"assets","locale":"","width":1600,"height":603},"screenshot-3.png":{"filename":"screenshot-3.png","revision":3439002,"resolution":"3","location":"assets","locale":"","width":1600,"height":693},"screenshot-4.png":{"filename":"screenshot-4.png","revision":3439002,"resolution":"4","location":"assets","locale":"","width":1600,"height":694},"screenshot-5.png":{"filename":"screenshot-5.png","revision":3439002,"resolution":"5","location":"assets","locale":"","width":1600,"height":76}},"screenshots":{"1":"Modules management screen","2":"Google Drive Importer profiles","3":"Import progress and logs","4":"Discount profiles and rules","5":"Transliteration tools"}},"plugin_section":[],"plugin_tags":[32289,220145,294,1666,286],"plugin_category":[45],"plugin_contributors":[253936],"plugin_business_model":[],"class_list":["post-274427","plugin","type-plugin","status-publish","hentry","plugin_tags-abandon-cart","plugin_tags-ai-search","plugin_tags-rewards","plugin_tags-wishlist","plugin_tags-woocommerce","plugin_category-ecommerce","plugin_contributors-limchik","plugin_committers-limchik"],"banners":{"banner":"https:\/\/ps.w.org\/smt-toolkit\/assets\/banner-772x250.png?rev=3439002","banner_2x":"https:\/\/ps.w.org\/smt-toolkit\/assets\/banner-1544x500.png?rev=3439002","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":false,"icon":"https:\/\/ps.w.org\/smt-toolkit\/assets\/icon-128x128.png?rev=3439002","icon_2x":"https:\/\/ps.w.org\/smt-toolkit\/assets\/icon-256x256.png?rev=3439002","generated":false},"screenshots":[{"src":"https:\/\/ps.w.org\/smt-toolkit\/assets\/screenshot-1.png?rev=3439002","caption":"Modules management screen"},{"src":"https:\/\/ps.w.org\/smt-toolkit\/assets\/screenshot-2.png?rev=3439002","caption":"Google Drive Importer profiles"},{"src":"https:\/\/ps.w.org\/smt-toolkit\/assets\/screenshot-3.png?rev=3439002","caption":"Import progress and logs"},{"src":"https:\/\/ps.w.org\/smt-toolkit\/assets\/screenshot-4.png?rev=3439002","caption":"Discount profiles and rules"},{"src":"https:\/\/ps.w.org\/smt-toolkit\/assets\/screenshot-5.png?rev=3439002","caption":"Transliteration tools"}],"raw_content":"<!--section=description-->\n<p><strong>SMT Toolkit for WooCommerce<\/strong> is a modular automation and management toolkit designed for WooCommerce-based stores.<\/p>\n\n<p>The plugin provides a collection of independent modules that can be enabled or disabled as needed, allowing store owners to build flexible workflows without unnecessary features or performance overhead.<\/p>\n\n<p>SMT Toolkit focuses on automation, data consistency, and repeatable processes - especially for stores that rely on bulk imports, scheduled updates, and advanced pricing logic.<\/p>\n\n<h3>Available Modules<\/h3>\n\n<h4>Google Drive Importer<\/h4>\n\n<p>Import products, images, and updates directly from Google Drive using CSV files.<\/p>\n\n<p>Features:\n- Import products and images from Google Drive folders\n- Batch processing with progress tracking\n- Detailed logs and safety checks\n- Update rules for existing products\n- Optional cron-based automation<\/p>\n\n<p><strong>Setup note:<\/strong>\nTo use this module, you must create a Google API project and obtain OAuth credentials.<\/p>\n\n<p>Official documentation:\nhttps:\/\/developers.google.com\/drive\/api\/guides\/enable-drive-api<\/p>\n\n<p><strong>Automation:<\/strong>\nFor scheduled or unattended imports, a WordPress cron task is required.<\/p>\n\n\n\n<h4>CSV File Structure<\/h4>\n\n<p>The importer uses CSV files to create or update products.<\/p>\n\n<p><strong>Required fields:<\/strong>\n- <code>id<\/code> <strong>or<\/strong> <code>sku<\/code> (at least one is required)<\/p>\n\n<p><strong>Optional fields:<\/strong>\n- <code>type<\/code>\n- <code>barcode<\/code>\n- <code>short_description<\/code>\n- <code>description<\/code>\n- <code>sale_start<\/code>\n- <code>sale_end<\/code>\n- <code>in_stock<\/code>\n- <code>price<\/code>\n- <code>sale_price<\/code>\n- <code>categories<\/code>\n- <code>tags<\/code>\n- custom meta fields (any column name will be treated as a meta key)\n- <code>image<\/code>\n- <code>alt_text<\/code>\nCustom meta fields are stored as product meta keys without overwriting protected WooCommerce core fields.<\/p>\n\n<p>The CSV structure is flexible. Only the required identifier field must be present.\nAll other fields are optional and processed only if provided.<\/p>\n\n\n\n<h4>Discount Engine<\/h4>\n\n<p>Create flexible discount profiles with conditions, priorities, schedules, and visual indicators.<\/p>\n\n<p>Features:\n- Rule-based discount profiles\n- Support for scheduled discounts\n- Priority handling and conflict resolution\n- Customizable discount badges\n- Cron-based recalculation support<\/p>\n\n<p><strong>Automation:<\/strong>\nA cron task is required to automatically apply, update, or expire scheduled discounts.<\/p>\n\n<p><strong>Theme compatibility note:<\/strong>\nTo replace the default sale badge, the theme must include a <code>span.onsale<\/code> element (the wrapper does not matter, but the <code>onsale<\/code> class must be present).<\/p>\n\n\n\n<h4>Transliteration<\/h4>\n\n<p>Automatically transliterate non-Latin product URLs and filenames.<\/p>\n\n<p>Features:\n- URL and filename transliteration\n- Custom rule editor\n- Testing and preview tools\n- Bulk conversion and rollback support<\/p>\n\n<p><strong>Language support:<\/strong>\nPredefined rules are included for:\n- Ukrainian\n- Russian\n- Bulgarian<\/p>\n\n<p>For other languages, custom transliteration rules can be added manually.<\/p>\n\n<p>This module does not require cron configuration.<\/p>\n\n\n\n<h4>Store Settings<\/h4>\n\n<p>The Store Settings module centralizes WooCommerce configuration in one place - no need to edit theme files or functions.php.<\/p>\n\n<p>It allows you to safely manage store behavior, performance tweaks, product badges, and Classic checkout fields through a clean admin interface.<\/p>\n\n<p><strong>Key Features<\/strong>\n- Disable unused frontend scripts and styles\n- Add context-based inline CSS and JavaScript\n- Clean up unnecessary WordPress and WooCommerce head output\n- Configure product badges (Sale, New, Sold Out, Featured, Best Seller)\n- Customize Classic WooCommerce checkout fields\n- Define custom product fields with tab display support<\/p>\n\n<p>Checkout customization works with Classic WooCommerce checkout (shortcode).\nBlocks checkout is automatically detected.<\/p>\n\n\n\n<h4>Ajax Archive Engine<\/h4>\n\n<p>AJAX-powered WooCommerce archive system with:\n- AJAX pagination\n- AJAX sorting\n- Lazy-loaded filters sidebar\n- Clean URL handling\n- History (back\/forward) state support\n- Mobile-first optimized behavior<\/p>\n\n\n\n<h4>Centralized SVG Registry<\/h4>\n\n<p>Secure SVG management module:\n- Centralized SVG storage\n- Sanitized SVG processing\n- Automatic CSS generation\n- Base64 background rendering\n- Hash-based file versioning\n- Automatic cleanup of old files\n- Security-hardened SVG sanitizer<\/p>\n\n\n\n<h4>Role-Based Pricing<\/h4>\n\n<p>Assign custom prices or percentage discounts based on the customer's WordPress user role.<\/p>\n\n<p>Features:\n- Per-role price overrides (fixed price or percentage discount)\n- Works with simple and variable products\n- Role-specific prices visible only to the matching role\n- Per-product manual price override via product edit screen\n- Fully integrated with WooCommerce cart and checkout totals\n- Compatible with WooCommerce price display hooks<\/p>\n\n<p><strong>Setup note:<\/strong>\nRole configurations are managed globally from the plugin settings.\nPer-product price overrides are set directly on each product's edit screen.<\/p>\n\n\n\n<h4>AI Search<\/h4>\n\n<p>Semantic product search powered by OpenAI embeddings. Customers find products by intent and natural-language queries, not just exact keywords.<\/p>\n\n<p>Features:\n- OpenAI-backed embeddings index (built incrementally in the background)\n- Live drop-down with brand, attribute and price chips\n- Quick-view + add-to-cart directly from results\n- Configurable result count, image size and ranking\n- Rate limiting (burst, per-minute, per-hour, global hourly cap)\n- Index status dashboard with manual rebuild and per-product diagnostics\n- Works with simple and variable products<\/p>\n\n<p><strong>Setup note:<\/strong>\nAn OpenAI API key is required. Index build is throttled and can be paused.<\/p>\n\n\n\n<h4>Wishlist<\/h4>\n\n<p>Personal wishlists with shareable links and multi-list support.<\/p>\n\n<p>Features:\n- One default list plus user-created named lists\n- Guest wishlists merge into the user account after login\n- Shareable public link per list (read-only by default)\n- AJAX add\/remove without page reload\n- Wishlist counter in header (theme-friendly via hooks and helper functions)\n- Quick-view and add-to-cart from the wishlist modal\n- Optional auto-cleanup of old guest sessions<\/p>\n\n\n\n<h4>Rewards \/ Loyalty Points<\/h4>\n\n<p>Points-based rewards programme. Customers earn points on completed orders and redeem them for discount on future purchases.<\/p>\n\n<p>Features:\n- Per-role or per-user-level earn rate (integrates with Discount user levels)\n- Configurable redemption cap as a percentage of the cart total\n- Auto-actions: registration bonus, review bonus, birthday gift, level-up bonus\n- Popup + email notifications (rate-limited, branded via Newsletter settings)\n- Refund-aware: points reverse on cancel\/refund and re-credit on subsequent re-completion\n- Points history with paginated load-more\n- Opt-in\/opt-out toggle on the customer's account page\n- Optional self-hosted \"do not load module CSS\" switch for themed stores<\/p>\n\n\n\n<h4>Abandon Cart<\/h4>\n\n<p>Recover sales from carts that customers leave behind.<\/p>\n\n<p>Features:\n- Tracks logged-in and guest carts (via email capture on checkout)\n- Configurable reminder schedule (multiple stages)\n- Branded reminder emails (template shared with Newsletter \/ Rewards)\n- Rate-limited send queue with admin dashboard and per-cart audit\n- One-click cart restore link (token-protected)\n- Auto-prune of recovered or aged-out carts<\/p>\n\n\n\n<h4>Newsletter<\/h4>\n\n<p>Lightweight newsletter system: subscriber list, campaign composer and email branding.<\/p>\n\n<p>Features:\n- Subscriber list with import \/ export and bulk actions\n- Visual template with logo, From-name\/address and footer (shared by all modules)\n- Composer with token replacement (<code>{first_name}<\/code>, <code>{site_name}<\/code>, etc.)\n- Per-hour send cap and queue worker (cron-based)\n- Unsubscribe handling with one-click token link\n- Bounce\/error logging\n- Integration hook so other modules (Rewards, Abandon Cart) re-use the same branding<\/p>\n\n\n\n<h4>Social<\/h4>\n\n<p>Social touchpoints in a single module: share buttons, login providers and a floating contact CTA.<\/p>\n\n<p>Features:\n- Share buttons (Facebook, X\/Twitter, Telegram, Viber, copy-link)\n- Social login (Google, Facebook, Apple) with token validation\n- Floating CTA dock with Phone \/ Telegram \/ Viber \/ WhatsApp \/ Instagram \/ Messenger channels\n- Per-channel labels (translatable)\n- Hover-to-reveal label on desktop, full label on mobile\n- Theme-friendly: every block is exposed via shortcode or hook<\/p>\n\n\n\n<h3>Modular Architecture<\/h3>\n\n<p>Each module operates independently and can be enabled or disabled at any time.\nThis allows you to use only the functionality you need while keeping the system lightweight and predictable.<\/p>\n\n<h3>External Services<\/h3>\n\n<p>This plugin connects to external services to provide its functionality.<\/p>\n\n<h4>Google Drive API<\/h4>\n\n<p>The Google Drive Importer module connects to the Google Drive API to allow users to import files and images from their own Google Drive accounts into WooCommerce.<\/p>\n\n<p>What data is sent:\n- Google Drive file IDs\n- OAuth access tokens provided by the user<\/p>\n\n<p>When data is sent:\n- Only when the user explicitly initiates an import action from the plugin interface<\/p>\n\n<p>Why data is sent:\n- To retrieve selected files and images from the user's Google Drive account<\/p>\n\n<p>Service provider:\n- Google LLC<\/p>\n\n<p>Terms of Service:\nhttps:\/\/policies.google.com\/terms<\/p>\n\n<p>Privacy Policy:\nhttps:\/\/policies.google.com\/privacy<\/p>\n\n<h4>OpenAI API<\/h4>\n\n<p>The AI Search module connects to the OpenAI API to build a semantic embeddings index from your product catalog.<\/p>\n\n<p>What data is sent:\n- Product titles, short descriptions, brand and taxonomy terms (no customer data)\n- API key supplied by the site owner<\/p>\n\n<p>When data is sent:\n- During an index build or rebuild, manually triggered by an administrator or by an admin-configured cron task\n- Customer search queries are NOT sent to OpenAI - they are matched against the locally stored vector index<\/p>\n\n<p>Why data is sent:\n- To compute vector embeddings used for semantic matching<\/p>\n\n<p>Service provider:\n- OpenAI, L.L.C.<\/p>\n\n<p>Terms of Service:\nhttps:\/\/openai.com\/policies\/terms-of-use<\/p>\n\n<p>Privacy Policy:\nhttps:\/\/openai.com\/policies\/privacy-policy<\/p>\n\n<h4>Social Login Providers (optional)<\/h4>\n\n<p>The Social module can optionally connect to Google, Facebook and Apple for one-click login. These connections only run when an administrator has configured the corresponding OAuth credentials and a user clicks the login button.<\/p>\n\n<p>What data is sent:\n- OAuth token round-trips initiated by the visitor's click<\/p>\n\n<p>When data is sent:\n- Only on explicit user action (clicking a social-login button)<\/p>\n\n<p>Why data is sent:\n- To authenticate the user via the chosen provider<\/p>\n\n<p>Service providers:\n- Google LLC: https:\/\/policies.google.com\/terms \/ https:\/\/policies.google.com\/privacy\n- Meta Platforms, Inc. (Facebook): https:\/\/www.facebook.com\/legal\/terms \/ https:\/\/www.facebook.com\/privacy\/policy\/\n- Apple Inc.: https:\/\/www.apple.com\/legal\/internet-services\/terms\/site.html \/ https:\/\/www.apple.com\/legal\/privacy\/<\/p>\n\n<!--section=faq-->\n<dl>\n<dt id=\"is%20this%20plugin%20free%3F\"><h3>Is this plugin free?<\/h3><\/dt>\n<dd><p>Yes. All features are fully available without restrictions. If you find it useful and want to support further development, you can do so via the donation link.<\/p><\/dd>\n<dt id=\"can%20i%20disable%20unused%20modules%3F\"><h3>Can I disable unused modules?<\/h3><\/dt>\n<dd><p>Yes. Each module can be enabled or disabled independently.<\/p><\/dd>\n<dt id=\"does%20it%20work%20only%20with%20woocommerce%3F\"><h3>Does it work only with WooCommerce?<\/h3><\/dt>\n<dd><p>Currently optimized for WooCommerce stores, but the architecture allows future expansion.<\/p><\/dd>\n<dt id=\"is%20it%20safe%20to%20run%20imports%20automatically%3F\"><h3>Is it safe to run imports automatically?<\/h3><\/dt>\n<dd><p>Yes. Imports use batch processing, logging, and safety checks.<\/p><\/dd>\n<dt id=\"where%20can%20i%20find%20google%20api%20setup%20instructions%3F\"><h3>Where can I find Google API setup instructions?<\/h3><\/dt>\n<dd><p>Official Google Drive API documentation is available at:\nhttps:\/\/developers.google.com\/drive\/api\/guides\/enable-drive-api<\/p><\/dd>\n<dt id=\"do%20i%20need%20cron%20to%20use%20this%20plugin%3F\"><h3>Do I need cron to use this plugin?<\/h3><\/dt>\n<dd><p>Cron is required only for automated imports and scheduled discounts.\nManual operations work without cron.<\/p><\/dd>\n<dt id=\"does%20role-based%20pricing%20work%20with%20variable%20products%3F\"><h3>Does Role-Based Pricing work with variable products?<\/h3><\/dt>\n<dd><p>Yes. Role-based prices apply to both simple and variable products. Per-product manual overrides are also supported on the product edit screen.<\/p><\/dd>\n<dt id=\"does%20ai%20search%20require%20an%20openai%20subscription%3F\"><h3>Does AI Search require an OpenAI subscription?<\/h3><\/dt>\n<dd><p>Yes. You need an OpenAI API key. The module uses the embeddings endpoint - small per-product cost, charged only when the index is (re)built. Search queries themselves do not call OpenAI at runtime; matches are computed from the local vector index.<\/p><\/dd>\n<dt id=\"how%20are%20abandoned%20cart%20and%20loyalty%20notifications%20sent%3F\"><h3>How are abandoned cart and loyalty notifications sent?<\/h3><\/dt>\n<dd><p>Both modules use the shared SMT Mailer. Email branding (logo, From, footer) is sourced from the Newsletter module when configured, otherwise from your site defaults. Sending is rate-limited per module.<\/p><\/dd>\n<dt id=\"do%20wishlist%20and%20rewards%20work%20for%20guests%3F\"><h3>Do wishlist and rewards work for guests?<\/h3><\/dt>\n<dd><p>Wishlist: guest lists are kept in a cookie and merge into the user account on login. Rewards: points are tied to the user account, so customers need to sign in to earn or redeem.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>1.3.4<\/h4>\n\n<ul>\n<li>FIX: Social token refresh - <code>is_due()<\/code> no longer treats <code>token_refresh_checked<\/code> as a \"last refreshed\" timestamp; a failing endpoint can no longer lock retries out for 45 days.<\/li>\n<li>FIX: Store Settings - tracking-plugin disabler moved from <code>wp<\/code> priority 1 to <code>template_redirect<\/code> so <code>is_cart()\/is_checkout()\/is_order_received_page()\/is_account_page()<\/code> are reliable; conversion tracking on checkout\/thank-you pages is no longer accidentally stripped.<\/li>\n<li>FIX: Social Post Share (Facebook) - drop deprecated <code>link<\/code> parameter; URL is now embedded in the message body so Graph still produces an OG card.<\/li>\n<li>FIX: Social Post Share (Threads) - 60-second async delay between <code>\/threads<\/code> creation and <code>\/threads_publish<\/code> via <code>wp_schedule_single_event<\/code> to match Meta's recommended media-processing window.<\/li>\n<li>FIX: Social Post Share (LinkedIn) - migrate from deprecated <code>\/v2\/ugcPosts<\/code> to current <code>\/rest\/posts<\/code> endpoint with <code>LinkedIn-Version<\/code> header.<\/li>\n<li>FIX: Social settings save - <code>preserve_token_meta()<\/code> keeps <code>refresh_token<\/code>, <code>token_refreshed<\/code> and <code>token_expires_at<\/code> when an admin rotates the access token; only the status meta is reset so the next cron re-checks freshness.<\/li>\n<li>FIX: Store Settings - WLW manifest 410 path check uses <code>parse_url($uri, PHP_URL_PATH)<\/code> before <code>basename()<\/code> so query strings can't trigger false positives.<\/li>\n<li>SECURITY: Token refresh cron - per-run <code>add_option<\/code> lock prevents an ad-hoc trigger from racing the daily cron and double-writing the options blob.<\/li>\n<li>SECURITY: Instagram widget - debug HTML comments are now opt-in via <code>?smt_ig_debug=1<\/code> so a passing admin (with WP_DEBUG enabled) never sees diagnostic strings in the page source.<\/li>\n<li>PERFORMANCE: Store Settings - tracking-disable branch now calls <code>mark_uncacheable()<\/code> (defines DONOTCACHEPAGE \/ sends <code>nocache_headers()<\/code>) so a logged-in admin's \"no-tracking\" render is never served by page caches to logged-out visitors.<\/li>\n<li>PERFORMANCE: Store Settings - added <code>script_loader_tag<\/code> last-resort filter that strips known Google \/ Meta tracking handles even when another plugin re-registers them after our dequeue pass.<\/li>\n<li>IMPROVED: Social - unified Facebook Graph API version to v25.0 across post-share, Instagram widget and token refresh.<\/li>\n<li>CLEANUP: Social module - removed dead <code>smttool_social_instagram_refresh<\/code> cron unschedule (the hook is no longer registered since Graph long-lived tokens are managed manually).<\/li>\n<\/ul>\n\n<h4>1.3.3<\/h4>\n\n<ul>\n<li>IMPROVED: Social - add scheduled 45-day token refresh checks for Facebook\/Instagram, Threads, X, LinkedIn and Tumblr OAuth2 credentials; Mastodon is checked but skipped because its tokens do not expire automatically.<\/li>\n<li>IMPROVED: Social - add X \/ Twitter post-sharing credentials and Tumblr OAuth2 token fields while keeping legacy Tumblr OAuth1 sharing as a fallback.<\/li>\n<\/ul>\n\n<h4>1.3.2<\/h4>\n\n<ul>\n<li>FIX: AJAX Archive - pass the plugin add-to-cart nonce from single-product and Quick View forms so the CSRF guard added in 1.3.0 accepts valid customer add-to-cart requests.<\/li>\n<\/ul>\n\n<h4>1.3.1<\/h4>\n\n<ul>\n<li>FIX: Include the Nova Poshta checkout helper class in the release package so Store Settings checkout helpers load without warnings.<\/li>\n<\/ul>\n\n<h4>1.3.0<\/h4>\n\n<ul>\n<li>SECURITY: Google Drive Importer - strict whitelist on Drive folder IDs prevents query injection into the Drive API <code>q<\/code> parameter<\/li>\n<li>SECURITY: AJAX Archive - add-to-cart endpoint now requires a valid nonce (CSRF guard); accepts WC core, Store API or plugin-issued nonces<\/li>\n<li>SECURITY: Nova Poshta - <code>HTTP_X_FORWARDED_FOR<\/code> \/ <code>CF-Connecting-IP<\/code> headers are honoured only when REMOTE_ADDR is in the new TRUSTED_PROXIES whitelist; rate-limit is now atomic via <code>wp_cache_incr<\/code><\/li>\n<li>SECURITY: Social AJAX - 64KB hard cap on POST payload before <code>json_decode<\/code> (memory-exhaustion DoS)<\/li>\n<li>SECURITY: Wishlist frontend JS - all user-controlled fields (thumbnail \/ url \/ added_at) now run through escHtml before HTML concatenation<\/li>\n<li>SECURITY: SVG Registry - explicit <code>libxml_disable_entity_loader(true)<\/code> on PHP &lt; 8.0, plus a 50-level depth guard in clean_node (XXE + stack DoS hardening)<\/li>\n<li>SECURITY: Newsletter - <code>created_by<\/code> queue check is now default-deny (only the original author or an administrator can resume a queue)<\/li>\n<li>SECURITY: Discounts - reward add-to-cart AJAX now re-validates <code>is_purchasable()<\/code> and <code>is_in_stock()<\/code><\/li>\n<li>SECURITY: Google Drive Importer - <code>wp_delete_attachment()<\/code> is now skipped when the same attachment is referenced by another product as a featured image or in a product gallery<\/li>\n<li>SECURITY: Discounts - discount percent is clamped to 0-100 in all reward calculation paths<\/li>\n<li>STABILITY: Rewards points - first-time <code>add()<\/code> uses atomic <code>INSERT ... ON DUPLICATE KEY UPDATE<\/code> to eliminate the lost-update race<\/li>\n<li>STABILITY: Rewards order reverse - when <code>deduct()<\/code> is capped by a low balance, the shortage is now persisted to order meta and subtracted from any re-completion credit (prevents double-credit on partial-refund edge sequences)<\/li>\n<li>STABILITY: Rewards daily cron - global lock + per-user lock around birthday bonus eliminates double-award between racing crons; soft 25s time budget per task<\/li>\n<li>STABILITY: Rewards review bonus - dedup key switched from order_id to per-comment marker so a real <code>order_id == product_id<\/code> cannot suppress a legitimate bonus<\/li>\n<li>STABILITY: Rewards popup queue - read-modify-write protected by per-user option lock; queue capped at 20 entries to prevent meta growth<\/li>\n<li>STABILITY: Discounts - cart mutations made inside <code>cart_loaded_from_session<\/code> now call <code>cart-&gt;set_session()<\/code> so they survive the next request<\/li>\n<li>STABILITY: Wishlist toggle - 3-second per-product transient lock serialises rapid double-clicks<\/li>\n<li>STABILITY: SVG Registry - <code>recompile_now()<\/code> serialised via option lock; old-CSS retention raised from 2 to 5 files to absorb CDN cache lag<\/li>\n<li>STABILITY: Store Settings save - admin saves serialised via option lock so concurrent tab saves don't race<\/li>\n<li>STABILITY: Store Settings AJAX - <code>wp_enqueue_scripts<\/code> trigger in admin context wrapped in try\/catch so misbehaving 3rd-party plugins can't break the response<\/li>\n<li>PERFORMANCE: Discounts - profile \/ conditions \/ rewards reads are now request-scope-cached; eliminates N+1 SQL on every <code>before_calculate_totals<\/code><\/li>\n<li>PERFORMANCE: AI Search - product term cache primed once per result set; eliminates per-product brand lookup queries<\/li>\n<li>PERFORMANCE: AI Search - attribute term cap raised from 500 to 5000 with truncation warning to surface silent omissions<\/li>\n<li>PERFORMANCE: Store Settings - variation REST handler primes post + post-meta caches in one batch instead of per-variation SQL<\/li>\n<li>PERFORMANCE: Store Settings - <code>_smttool_discount_sort<\/code> clause filter now idempotent; will not duplicate JOINs when the filter fires multiple times in a query<\/li>\n<li>PERFORMANCE: Store Settings - best-seller IDs cache invalidated on order completion<\/li>\n<li>PERFORMANCE: Google Drive Importer - 10k-file \/ 60-second cap inside <code>list_drive_files<\/code>, bounded static product caches, explicit 30s timeout on <code>download_url<\/code>, 50MB cap on CSV body<\/li>\n<li>PERFORMANCE: Rewards apply-points AJAX - per-user rate-limit (5\/min) protects expensive <code>calculate_totals()<\/code> recalculation<\/li>\n<li>PERFORMANCE: Wishlist modal - product + brand caches primed before the per-item loop<\/li>\n<li>PERFORMANCE: Newsletter - server-side minimum query length on user \/ product \/ category search; transient TTL reduced from 7 to 3 days<\/li>\n<li>PERFORMANCE: Newsletter - send queue now tracks already-emailed user IDs across batches (cap 50k) to stop multi-role users receiving the same newsletter twice<\/li>\n<li>IMPROVED: Checkout phone mask - caret position math fixed (delta against pre-assignment length); now accepts 10-16 digit international numbers instead of forcing exactly 13<\/li>\n<li>IMPROVED: AJAX Archive - <code>filter_stock_status<\/code> query string is cast to string before <code>explode<\/code>, preventing silent failures when sent as an array<\/li>\n<li>IMPROVED: AJAX Archive - internal product taxonomies (<code>product_visibility<\/code>, <code>product_shipping_class<\/code>) refused as archive context to prevent hidden-product enumeration<\/li>\n<li>IMPROVED: Nova Poshta - transport-layer error messages no longer leaked to client (logged server-side)<\/li>\n<li>IMPROVED: AI Search frontend - <code>price_html<\/code> injection routed through DOMPurify when available<\/li>\n<\/ul>\n\n<h4>1.2.0<\/h4>\n\n<ul>\n<li>NEW: AI Search module - OpenAI-powered semantic product search with live drop-down, rate limiting and incremental index builder<\/li>\n<li>NEW: Wishlist module - personal wishlists with multi-list support, shareable links and AJAX add\/remove<\/li>\n<li>NEW: Rewards module - points-based loyalty programme with redemption, auto-actions (registration \/ review \/ birthday \/ level-up), refund-aware accrual and popup + email notifications<\/li>\n<li>NEW: Abandon Cart module - logged-in and guest cart recovery with token-protected restore links and branded reminder schedule<\/li>\n<li>NEW: Newsletter module - subscriber list, branded composer, queued sending with per-hour cap and shared email template re-used by Rewards and Abandon Cart<\/li>\n<li>NEW: Social module - share buttons, social login (Google \/ Facebook \/ Apple) and a floating contact-channels CTA dock<\/li>\n<li>NEW: SMT Router - unified REST response wrapper (success\/data\/message)<\/li>\n<li>NEW: SMT Mailer - shared email service with rate limiting and template branding<\/li>\n<li>IMPROVED: Rewards refund\/re-completion flow - re-completing a previously refunded order now correctly re-credits points (was blocked by a stale flag)<\/li>\n<li>IMPROVED: AJAX archive sidebar refresh - filter chips and dynamic counts now update after every filter change<\/li>\n<li>IMPROVED: My Account AJAX tabs auto-scroll to content with theme-controlled offset (scroll-margin-top)<\/li>\n<li>IMPROVED: Recently viewed cookie parsed once per request (was re-parsed on every helper call)<\/li>\n<li>IMPROVED: Cart count in header cached per request (fragments handle live updates)<\/li>\n<li>IMPROVED: 404 fallback products query no longer uses orderby:rand on large catalogs (date + PHP shuffle)<\/li>\n<li>IMPROVED: Custom logo loaded eagerly with high fetch priority (LCP)<\/li>\n<li>IMPROVED: WC Brands sale-flash unhook now runs once per request (static guard)<\/li>\n<li>IMPROVED: Skeleton-loading state + soft fade-in for AJAX archive grid swaps<\/li>\n<li>IMPROVED: Reduced-motion preference respected globally (animation\/transition durations collapse)<\/li>\n<li>IMPROVED: HPOS-ready - wc_get_orders() used throughout, no legacy posts queries<\/li>\n<li>IMPROVED: Plugin Check warnings cleaned up (line endings, prepared SQL placeholder helpers, file system operation comments)<\/li>\n<li>SECURITY: Filter-combination archive URLs emit noindex via wp_robots (prevents duplicate-content indexation, plays nice with Rank Math \/ Yoast)<\/li>\n<li>SECURITY: REST error paths now return immediately after wp_send_json_error() (defence in depth)<\/li>\n<li>SECURITY: All JSON input read with wp_unslash() before json_decode(), no double sanitisation that could corrupt UTF-8<\/li>\n<li>FIX: Rewards popup now shows the configured subject as a title (was dropped from the queue, only body was rendered)<\/li>\n<li>FIX: Rewards popup queue accepts both new {subject, body} entries and legacy strings for backward compatibility<\/li>\n<li>FIX: Variations table reset_variations link no longer reserves layout space when visibility-hidden<\/li>\n<li>FIX: Floating shop-toolbar clone no longer creates duplicate ids \/ for \/ aria-controls in the DOM<\/li>\n<li>FIX: search.php setup_postdata now assigns $GLOBALS['post'] so the_*() functions inside template parts work<\/li>\n<li>FIX: cart modal home_url(REQUEST_URI) build path corrected<\/li>\n<li>FIX: Yoast \/ Rank Math primary category honoured by the post card meta helper<\/li>\n<li>FIX: tag_escape() used for dynamic heading tags (was esc_attr)<\/li>\n<\/ul>\n\n<h4>1.1.0<\/h4>\n\n<ul>\n<li>NEW: Role-Based Pricing module (per-role fixed price or percentage discount, per-product overrides)<\/li>\n<li>NEW: Unified data layer - SMT_Data_Provider centralizes all option reads and writes<\/li>\n<li>NEW: SMT_Cache - three-tier caching (in-memory, WP object cache, transients) with a unified API<\/li>\n<li>IMPROVED: Lazy module initialization - Admin\/Frontend\/Ajax classes load only in the context where they are needed<\/li>\n<li>IMPROVED: Active discount profiles query cached per request (reduces DB load during cron runs)<\/li>\n<li>IMPROVED: Inline context detection cached per request (was computed three times per page)<\/li>\n<li>FIX: Discount badge always fell back to woocommerce_sale_flash due to incorrect module status check<\/li>\n<li>FIX: get_profile() and get_rules_for_profile() now use the table name helpers consistently<\/li>\n<li>FIX: Admin page classes were not loaded when render_admin() was called after lazy init<\/li>\n<li>FIX: Transient cleanup in uninstall.php now uses wpdb::prepare()<\/li>\n<\/ul>\n\n<h4>1.0.4<\/h4>\n\n<ul>\n<li>NEW: Ajax Archive Engine module<\/li>\n<li>NEW: Centralized SVG Registry module<\/li>\n<li>NEW: SVG background support for Discounts and Store Settings badges<\/li>\n<li>IMPROVED: Badge rendering performance<\/li>\n<li>IMPROVED: Centralized SVG CSS compilation system<\/li>\n<li>IMPROVED: 10% faster initial page load (SVG optimization)<\/li>\n<li>SECURITY: Improved JSON input sanitization<\/li>\n<li>FIX: Nonce verification improvements<\/li>\n<li>FIX: Plugin Check warnings resolved<\/li>\n<li>REMOVED: inline SVG<\/li>\n<\/ul>\n\n<h4>1.0.3<\/h4>\n\n<ul>\n<li>Fixed missing store-settings module in release package<\/li>\n<li>Minor stability improvements<\/li>\n<\/ul>\n\n<h4>1.0.2<\/h4>\n\n<ul>\n<li>Fixed undefined variable in Discounts cron handler<\/li>\n<\/ul>\n\n<h4>1.0.1<\/h4>\n\n<ul>\n<li>Added Store Settings module (assets control, cleanup, inline CSS\/JS, checkout fields)<\/li>\n<li>Checkout customization refactored (Classic checkout only, Block checkout detection added)<\/li>\n<li>Fixed WooCommerce checkout field re-rendering (wc-checkout handling)<\/li>\n<li>Improved checkout field diff-based saving (no translation overwrite)<\/li>\n<li>Added reset option for checkout fields<\/li>\n<li>Enhanced badge system (heavy SVG handling, template injection, mutation observer)<\/li>\n<li>Optimized best-seller query with transient caching<\/li>\n<li>Improved security sanitization (wp_unslash handling, SVG hardening)<\/li>\n<li>Added advanced cleanup options (WooCommerce blocks, cart fragments, brands)<\/li>\n<li>Performance improvements and codebase stabilization<\/li>\n<li>Multiple security and Plugin Check compliance fixes<\/li>\n<\/ul>\n\n<h4>1.0.0<\/h4>\n\n<ul>\n<li>Initial public release<\/li>\n<li>Google Drive Importer module<\/li>\n<li>Discount Engine module<\/li>\n<li>Transliteration module<\/li>\n<li>Modular system and cron support<\/li>\n<\/ul>","raw_excerpt":"Modular WooCommerce toolkit: AI search, cart recovery, wishlist, loyalty rewards, role-based pricing, discounts, AJAX archive and more.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/pe.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/274427","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/pe.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/pe.wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/pe.wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=274427"}],"author":[{"embeddable":true,"href":"https:\/\/pe.wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/limchik"}],"wp:attachment":[{"href":"https:\/\/pe.wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=274427"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/pe.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=274427"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/pe.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=274427"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/pe.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=274427"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/pe.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=274427"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/pe.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=274427"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}