Skwirrel PIM sync for WooCommerce

Description

Skwirrel PIM sync for WooCommerce connects your WooCommerce webshop to the Skwirrel PIM system. Products, variations, categories, brands, manufacturers, images, and documents are synchronised automatically or on demand.

Features:

  • Full and delta (incremental) product synchronisation
  • Simple and variable product support with ETIM classification for variation axes
  • Automatic category tree sync with parent-child hierarchy
  • Brand sync via WooCommerce native product_brand taxonomy
  • Manufacturer sync with dedicated product_manufacturer taxonomy
  • Product image and document import into the WordPress media library
  • Custom class attributes (alphanumeric, logical, numeric, range, date, multi)
  • Configurable product URL slugs (source field, suffix, update on re-sync)
  • GTIN and manufacturer product code search filter on the product list page
  • Scheduled synchronisation via WP-Cron or Action Scheduler
  • Manual synchronisation from the admin dashboard with live progress tracking
  • Date-grouped sync history (last 20 runs)
  • Stale product and category purge after full sync
  • Delete protection with warnings and automatic full re-sync
  • Multilingual support with 7 locales (nl_NL, nl_BE, de_DE, fr_FR, fr_BE, en_US, en_GB)

Requirements:

  • WordPress 6.0 or higher
  • WooCommerce 8.0 or higher (9.6+ recommended for native brand support; tested up to 10.6)
  • PHP 8.3 or higher
  • An active Skwirrel account with API access

Installation

  1. Upload the plugin files to /wp-content/plugins/skwirrel-pim-sync/, or install the plugin directly through the WordPress plugin screen.
  2. Activate the plugin through the ‘Plugins’ screen in WordPress.
  3. Navigate to WooCommerce > Skwirrel Sync to configure the plugin.
  4. Enter your Skwirrel API URL and authentication token.
  5. Click ‘Sync now’ to start the first synchronisation.

FAQ

Which Skwirrel API version is supported?

The plugin works with the Skwirrel JSON-RPC 2.0 API.

How often are products synchronised?

You can set an automatic schedule (hourly, twice daily, or daily) or synchronise manually from the settings page.

Are existing products overwritten?

The plugin uses the Skwirrel external ID as a unique key. Existing products are updated, not duplicated.

I use a media offload plugin (WP Offload Media, S3 Uploads, …) — will the sync delete my offloaded files?

No, the sync never invokes wp_delete_attachment() on a missing-file event in 3.8.0+. When the local file is gone, the plugin only clears its own Skwirrel-side meta keys from the WP attachment record so the next sync can download fresh; the WP record itself (and any remote copy your offload plugin manages) is left untouched.

If you want to go a step further and have the sync reuse the existing WP attachment (no fresh download, no churn) when the local file is gone but the remote copy is fine, hook into the skwirrel_wc_sync_attachment_is_valid filter. The simplest implementation as a mu-plugin:

<?php
add_filter( 'skwirrel_wc_sync_attachment_is_valid', function ( $local_present, $att_id ) {
    return $local_present || (bool) wp_get_attachment_url( $att_id );
}, 10, 2 );

Returning true tells the sync the attachment is still valid even though the local file is missing. The plugin ships a more thorough reference implementation (URL-equals-uploads-baseurl check) you can adapt — see the project’s mu-plugins/skwirrel-offload-compat.php.

Reviews

There are no reviews for this plugin.

Contributors & Developers

“Skwirrel PIM sync for WooCommerce” is open source software. The following people have contributed to this plugin.

Contributors

“Skwirrel PIM sync for WooCommerce” has been translated into 2 locales. Thank you to the translators for their contributions.

Translate “Skwirrel PIM sync for WooCommerce” into your language.

Interested in development?

Browse the code, check out the SVN repository, or subscribe to the development log by RSS.

Changelog

3.8.2

Release-hygiene fixes for WordPress.org Plugin Check:

  • Stop shipping dev-only files in the SVN trunk. The 10up deploy action skips .distignore whenever a BUILD_DIR is configured, so the previous releases inadvertently shipped empty composer.json, phpstan.neon.dist, phpunit.xml.dist, phpunit-integration.xml.dist, .phpcs.xml.dist, .gitignore, and .distignore placeholder files. Those placeholders have been removed from the plugin source so the deploy ZIP only contains runtime files.
  • Suppress Plugin Check false positives in plugin code: the active_plugins activation check is the WordPress core filter (not a plugin-defined hook), the _skwirrel_category_id term-meta lookup queries are documented as having no WP API equivalent, and the 'meta_key' array key inside a logger context is correctly flagged as not a database query argument. No runtime behavior changes.

3.8.1

  • Fix: grouped-products sync now honours every configured dynamic_selection_id. The 3.8.0 multi-selection fix only covered the main fetch loop; the grouped-products prefilter still used collection_ids[0] and silently skipped grouped products whose members lived only in selections 2..N. With this patch, a grouped product is kept whenever any of its members appears in any of the configured selections.

3.8.0

Media — real Skwirrel WordPress mapping + content-change detection:

  • Every imported attachment now stores the Skwirrel product_attachment_id as _skwirrel_attachment_id post meta. Re-syncs locate existing media by that stable id first; URL-hash matching is the legacy fallback. CDN URL rewrites no longer create duplicate WP attachments.
  • The API’s file_sha256_checksum lands on the WP attachment as _skwirrel_file_checksum. A re-sync that sees a different checksum replaces the underlying file in place — same WP attachment id, fresh bytes, sub-sizes regenerated, mime type updated. Failed downloads leave the existing attachment untouched.
  • Offload-plugin-safe missing-file guard — when the local file is gone the WP attachment record is preserved (no wp_delete_attachment() is ever invoked, which would have triggered offload-plugin hooks that may purge remote storage). Only the Skwirrel-side meta is cleared so the next sync can download fresh. New skwirrel_wc_sync_attachment_is_valid filter lets offload-aware site code declare an attachment valid even when the local file is missing.
  • Lazy migration: re-syncs of attachments imported before 3.8 silently backfill the new meta keys from the API payload. No re-download on upgrade.

Sync safety — fixes for issues surfaced in code review:

  • Mutex on concurrent runs: run_sync() refuses to start when another run’s heartbeat is still fresh, preventing the queue, per-product synced_at meta and purge step from being raced. A stale heartbeat is taken as “previous run died” and the new run takes over.
  • The global queue truncate at sync startup is gone. Each run uses its own sync_run_id and only touches its own rows; defends queue contents even if the mutex is bypassed.
  • Pagination atomicity: a later-page fetch failure is now a hard abort. Previously the run continued through every phase, advanced last_sync, and ran stale-product purge — at worst trashing the products that lived on the un-fetched pages.
  • Multi-selection support in the main fetch: getProductsByFilter is now called once per configured selection id. The previous code silently used only the first id, dropping every product that lived only in selections 2..N.
  • Empty cross-sells / upsells now actually clear. Previously an API payload that returned zero relations left existing WC cross-sells/upsells in place forever.

Plugin Check submission cleanup:

  • .distignore excludes all dev tooling configs from the deploy ZIP.
  • Tested up to: 6.9.4 6.9 (wp.org accepts only major.minor).
  • Bulk SQL in the purge handler reworked so Plugin Check‘s static analyser can verify int-sanitised IDs in the IN-clauses.
  • Google Fonts enqueue now passes a version string instead of null.

Tests:

  • 237 unit + 45 integration tests (up from 169 + 11 in 3.6.x). New MediaImporterIntegrationTest (10 cases) and SyncSafetyIntegrationTest (8 cases) pin every new behaviour.

3.7.0

  • Bump minimum PHP version to 8.3 (PHP 8.1 reached end-of-life on 2025-12-31; 8.2 is in security-only support until 2026-12-31). Requires PHP and composer.json runtime constraint both updated.
  • Bump minimum Node.js version to 22 LTS for the local dev environment (package.json engines.node). Node 18 reached end-of-life in April 2025 and Node 20 maintenance ends April 2026.
  • CI: bump GitHub Actions PHP version from 8.1 to 8.3 to match the dev tooling lock file (Pest 3 / PHPUnit 11 require PHP 8.2+). Cache key updated accordingly.
  • .wp-env.json: bump phpVersion to 8.3 for parity with CI and the runtime floor.
  • Internal: aligned source files with the WordPress coding standards now that CI no longer fails before phpcs runs. Class files renamed to class-skwirrel-wc-sync-{slug}.php (full class name in kebab-case); the Skwirrel_WC_Sync_Plugin bootstrap class extracted from skwirrel-pim-sync.php into its own file. ~125 Yoda condition flips, ~33 $camelCase $snake_case local variable renames, removed $mapper parameter from Skwirrel_WC_Sync_Category_Sync::assign_categories() (was unused), renamed $object $wc_object in Skwirrel_WC_Sync_Variation_Attributes_Fix::fix_rest_response_attributes() (PHP 8.2+ reserved keyword). No functional changes.

3.6.1

  • Fix: ship files that were missing from the 3.6.0 build — class-pim-link.php (the “Open in Skwirrel” deep-link implementation), the updated class-admin-settings.php (Settings-saved notice + transient-based test result), the updated class-product-sync-meta-box.php (PIM link button), the assets/s.png button icon, and the corresponding .po/.mo translation updates. Without these files an install of 3.6.0 fatalled on activation with “Failed opening required … class-pim-link.php”

3.6.0

  • “Open in Skwirrel” deep-link — Skwirrel meta box on the product edit screen and each row on the WP Products list now offer a button that jumps straight to the matching product in the Skwirrel PIM web UI. The link uses the host from the JSON-RPC endpoint. Simple products use /catalogue/products/edit/{product_id}; variable/grouped product shells use /catalogue/grouped-products/edit/{grouped_id}.
  • “Settings saved” notice on the settings page — saving settings now shows a proper confirmation. The connection test result moved to a transient so it no longer re-displays after every save (previously a stale ?test=ok URL parameter caused the test-success notice to fire on save).
  • New skwirrel_wc_sync_after_attributes_fetched action hook — fires during the attributes phase right after a product’s enriched payload (with _etim and _custom_classes) is fetched and before the WooCommerce attribute table is written. Enables site-specific code to persist the enriched payload as post meta for custom frontend rendering. Hook signature: do_action( 'skwirrel_wc_sync_after_attributes_fetched', int $wc_id, array $attr_product, ?array $group_info ). Fires in both the bulk Phase 3 loop and the single grouped product re-sync path
  • Fix: variation filter values no longer empty during sync — pre-sync rebuild of the variable product shell wiped the parent’s term-options to [] for the entire duration of the sync, leaving the frontend variation filter (storage, color, connectivity, etc.) empty until the sync finished. The shell-rebuild now preserves existing term-options for each axis taxonomy
  • flush_parent_attribute_terms() is now authoritative — at the end of Phase 3 the parent’s variation taxonomy term-list is rebuilt as exactly the set of term IDs whose slugs appear in the current children’s attribute_pa_* post meta. Stale terms from removed variants are dropped (previously merged in indefinitely); new terms from added or updated variants are picked up. Replaces the previous merge-on-merge pattern with a single derived-from-children compute

3.4.0

  • Live sync log viewer on the Debug page — tails the current sync log file with 2-second polling, auto-scroll, pause/clear/download controls
  • “View live log” anchor link on the in-progress sync banner jumps straight to the live log
  • Active log filename now tracked in a WP option so the viewer follows sync runs across page refreshes
  • Dashboard Debug block relabelled to surface the live log alongside variation attribute troubleshooting
  • CI actions bumped to Node 24 — checkout/cache/upload-artifact/github-script

3.3.0

  • Log viewer performance — progressive rendering (200 lines/frame), chunked server loading (100 KB/request), and download button
  • API Response meta box for grouped products — collapsible JSON display with syntax highlighting
  • Lazy-loaded variation API responses via AJAX with individual collapsible sections
  • Single grouped product sync — “Sync this product” button now works for variable/grouped products
  • Grouped product ID stored as variation meta for direct group membership lookup
  • Price fix: “Prices managed outside Skwirrel” now also protects simple product prices (previously only variations)
  • Quick-scroll link from Skwirrel sidebar meta box to API response section

3.2.2

  • New “Prices managed outside Skwirrel” setting — when enabled, the PIM sync no longer overwrites existing variation prices with 0 when the PIM payload contains no price. Use this when prices are synced from a separate system (e.g. ERP).

3.2.1

  • Single-variant groups synced as simple products instead of variable products
  • Custom features as variation axes alongside ETIM features
  • Custom feature matching by ID with translated labels
  • Attribute label auto-update for numeric and cc_ prefixed labels
  • No duplicate attributes for custom feature variation axes
  • Phase 3 custom class fetch when grouped products enabled

3.2.0

  • Custom class collection ID — new required setting for product fetching
  • Custom classes included in bulk product fetch
  • Text features (type T) now stored as product attributes
  • GTIN / Variant visibility toggles (default: hidden)
  • Sync aborts with a clear error when custom class collection ID is not configured

3.0.0

  • Virtual product content — variable products inherit name, descriptions, categories from virtual products
  • Variation slugs — deterministic URL slugs generated from attribute values during sync
  • Variation permalinks — optional clean URLs: /product/{product-slug}/{variation-slug}/
  • Enhanced Skwirrel meta box — navigation links between parent and variation products
  • Theme API — helper functions for theme developers (variation URLs, thumbnails, default variations)
  • Example theme snippets in snippets/ directory

2.6.2

  • Fix false “not managed by Skwirrel” message on variable products

2.6.1

  • Fix related products API flag and add smart relation type mapping (auto/cross-sells/upsells/both)

2.6.0

  • Related products sync — sync Skwirrel related products as WooCommerce cross-sells, upsells, or both
  • New settings for related products mapping type

2.5.0

  • Variant label setting — choose which field shows in the variant dropdown (SKU, ERP description, or product name)
  • Custom class attribute visibility filter — control which custom class attributes are visible on the product page

2.4.4

  • Fix “Stop sync” button — now checks during phases, not just between them
  • Show failed sync timestamp and error in status card

2.4.3

  • Flush WooCommerce object cache after every product in all processing phases

2.4.2

  • Flush WordPress object cache between sync phases to reclaim memory

2.4.1

  • Fix OOM in grouped products pre-sync phase — flush wpdb memory between API pages

2.4.0

  • Deferred attribute fetch — ETIM and custom classes fetched per-product in attribute phase instead of bulk fetch

2.3.5

  • Aggressive wpdb memory cleanup after every product in all sync phases

2.3.4

  • Fix OOM during fetch — flush wpdb between batches, default batch size 10

2.3.3

  • Fix OOM during fetch phase — smaller API batches + wpdb memory flush between pages

2.3.2

  • Fix unexpected output during plugin activation

2.3.1

  • Use WordPress timezone for log filenames instead of UTC

2.3.0

  • Database-backed sync queue — product data stored in temporary DB table instead of memory, preventing OOM crashes on low-memory servers
  • Products processed one at a time per phase via cursor pattern (O(1) memory usage)

2.2.9

  • Convert existing simple products to variations when a grouped product sync encounters a duplicate SKU
  • Reduce memory usage during phased sync by freeing heavy product data after each phase

2.2.8

  • Simplify per-product category assignment — use resolved map from tree sync instead of recursive per-product resolution

2.2.7

  • Add “Stop sync” button to progress banner — abort a running sync from the dashboard
  • Log timestamps now respect the WordPress timezone setting

2.2.6

  • Add include_contexts to category API call, improve category sync diagnostics

2.2.5

  • Fix approved download directory: enable existing disabled directories during sync

2.2.4

  • Fix category sync — use correct API parameter name for fetching categories
  • Fix per-product category assignment when API returns ID-only category data

2.2.3

  • Dark terminal-style log viewer with syntax highlighting for log levels
  • JSON objects and sync separators styled for readability

2.2.2

  • Raise PHP memory limit at sync start to prevent OOM crashes on large API responses
  • Detect fatal errors (OOM) during sync and record them as failed results
  • Fixes silent sync failures showing stale success status

2.2.1

  • Separate “Sync Logs” settings section with per-trigger log mode (per sync or per day)
  • Add “Manual (no auto-delete)” option to log retention
  • Fix super category ID field width to match selection IDs field

2.2.0

  • Per-sync log files — each sync run writes to its own log file for easy debugging
  • Manual syncs get a unique log file; scheduled syncs share a daily log file (appended)
  • Log viewer modal in sync history — click “View” to read log contents inline
  • Configurable log retention (12h, 1d, 2d, 7d, 30d) — old files cleaned up automatically

2.1.5

  • Recursively fetch full category tree from API — all depth levels are now synced, not just direct children of the super category

2.1.4

  • Auto-register WP uploads directory as WooCommerce approved download directory during sync
  • Fixes “downloadable file not in approved folder” errors for imported PDFs

2.1.3

  • Store Skwirrel API response for all product types: variations and variable product shells now also save _skwirrel_api_response

2.1.2

  • Fix grouped products ignoring dynamic selection ID — post-filter groups against selection product list
  • Fetch allowed product IDs from selection before processing groups, skip groups with no matching members

2.1.1

  • Fix grouped products ignoring selection ID filter — pass dynamic_selection_id to getGroupedProducts
  • Store raw Skwirrel API response as _skwirrel_api_response post meta during sync
  • Add dedicated “Skwirrel API Response” meta box on the product edit screen showing the stored JSON

2.1.0

  • Selection ID is now required — sync aborts if no selection ID is configured
  • Add “Show API response” button in the Skwirrel product meta box to view raw JSON from the API
  • Reduce batch size maximum from 500 to 50, default from 100 to 10
  • Fix translation: selection ID hint incorrectly said “category IDs” in all locales

2.0.8

  • Add raw API response logging for getCategories and per-product _categories data (verbose mode)

2.0.7

  • Fix category tree sync failing when API returns single root category object instead of array
  • Categories from getCategories are now correctly extracted from root _children when super category ID is used

2.0.6

  • Add diagnostic logging for category-to-product assignment to trace resolution failures
  • Log each category resolve step (meta lookup, name fallback, creation) when verbose logging is enabled
  • Warn when categories are extracted from API but no WooCommerce term IDs could be resolved
  • Check and log wp_set_object_terms errors instead of silently ignoring failures

2.0.5

  • Update README and plugin description to reflect current feature set
  • Replace “ERP/PIM” references with “PIM” throughout
  • Update WooCommerce minimum to 8.0 (9.6+ recommended for native brand support)
  • Update WooCommerce tested up to 10.6
  • Update WordPress tested up to 6.9.4

2.0.4

  • Inline “Update on re-sync” toggle in Permalinks section — saves instantly via AJAX
  • Slug warning only shown when “Update on re-sync” is enabled and settings have changed
  • Persistent hint when re-sync is enabled warning about URL overwrite and SEO impact
  • Add batch size hint text (1–500)

2.0.3

  • Add Permalinks section in Settings showing current slug configuration with link to Permalinks page
  • Show warning when slug settings change, advising a full resync and potential link breakage
  • Add Selection IDs hint link to Skwirrel selections page (dynamic subdomain URL)

2.0.2

  • Add GTIN / Manufacturer product code search filter on product list page
  • Store product GTIN and manufacturer product code as dedicated meta during sync
  • Add subtitles to Debug and Danger Zone dashboard blocks

2.0.1

  • Rename “Collection IDs” to “Selection IDs”
  • Add API token creation link with dynamic subdomain URL
  • Add category finder link on Super category ID field
  • Move WordPress admin notices below the Skwirrel header

2.0.0

  • New admin dashboard with block-grid layout replacing the tab-based UI
  • Sync progress banner with 6-phase checklist and live counters
  • Date-grouped sync history table (Today, Yesterday, day name, or date)
  • Settings page redesigned with grouped fieldsets and Tailwind-inspired styling
  • Simplified API connection: subdomain-only input with visual prefix/suffix
  • Remove auth type selector (always uses static token)
  • Sync Logs block links directly to WooCommerce logs
  • Debug and Danger Zone inline in the dashboard grid
  • Full translation update for all 7 locales (nl_NL, nl_BE, de_DE, fr_FR, fr_BE, en_US, en_GB)

1.10.1

  • Add Domain Path header for automatic translation loading on WordPress 6.7+
  • Add load_plugin_textdomain() fallback for older WordPress versions
  • Add nl.mo/nl.po locale files for sites using “nl” instead of “nl_NL”
  • Fix Danger Zone purge not removing all product attribute taxonomies — now cleans up all orphaned attributes, not just etim_* and skwirrel_variant

1.10.0

  • Phased sync architecture — sync now runs in 5 sequential phases (fetch, products, taxonomy, attributes, media) instead of processing everything per product
  • Live progress checklist on the sync tab — shows current phase, status icon, and counter (e.g. “247 / 500”)
  • Performance fix: restore getProducts API call for full sync (faster than getProductsByFilter with empty filter)
  • Auto-refresh now only fires on the sync tab, not on other admin pages

1.9.9

  • Fix Danger Zone purge silently timing out on large datasets — add set_time_limit(0) to prevent PHP timeout
  • Rewrite Danger Zone purge to use bulk SQL — orders of magnitude faster on large stores

1.9.8

  • Add “Skwirrel” meta box on product edit screen with single-product sync button

1.9.7

  • Add configurable “Product manufacturer base” slug on Settings Permalinks page

1.9.6

  • Fix product sync failing when downloadable files are not in WooCommerce’s approved directory
  • Downloads/documents errors no longer block category, brand and manufacturer assignment

1.9.5

  • Brand sync always active (uses WooCommerce native product_brand taxonomy)
  • Add “Sync manufacturers” setting with product_manufacturer taxonomy
  • Default product list columns: hide Tags, show Manufacturers
  • Add “Filter by manufacturer” dropdown on product list page
  • Manufacturers column ordered after Brands, before Date

1.9.3

  • Fix variable product variation attributes: recover parent attribute options from child variation post meta when deferred terms are empty
  • Convert non-variation parent attributes to global WooCommerce taxonomy-based attributes
  • Fix brand not assigned to variable products: propagate brand from child variations to parent
  • Fix categories not assigned to variable products: propagate categories from child variations to parent

1.9.2

  • Remove legacy pa_variant migration code (no live installs to migrate)
  • Fix simple product attributes: save as global WooCommerce taxonomy-based attributes instead of custom text attributes, so they appear in layered navigation and product filters

1.9.1

  • Remove legacy pre-1.8.0 Action Scheduler cleanup code (old slug reference)

1.9.0

  • Move remaining inline event handlers (onchange, onclick) to enqueued inline script for WordPress.org compliance
  • Fix stale debug log path in admin help text

1.8.4

  • Add non-variation ETIM and custom class attributes to parent variable products during sync

1.8.3

  • Fix empty variation attribute dropdowns on variable products by deferring parent attribute term updates to a single batch flush after all variations are processed

1.8.2

  • Replace all inline <script> and <style> tags with proper wp_enqueue_script/wp_add_inline_script/wp_add_inline_style calls
  • Rename plugin display name to “Skwirrel PIM sync for WooCommerce”

1.8.1

  • Fix variation attribute labels showing raw ETIM codes (e.g. “EF002671”) instead of human-readable names
  • Add missing include_etim_translations and include_languages to getGroupedProducts API call

1.8.0

  • Rename plugin slug from skwirrel-pim-wp-sync to skwirrel-pim-sync (WordPress.org restricts “wp” in plugin slugs)
  • Update text domain, Action Scheduler group, logger source, and admin page slug
  • Rename main plugin file and all language files to match new slug
  • Add activation cleanup for old Action Scheduler group from pre-1.8.0
  • Existing settings, synced products, and translations are fully preserved

1.7.1

  • Remove deprecated load_plugin_textdomain() call (WordPress 4.6+ auto-loads translations)
  • Fix unescaped SQL parameters in purge handler with proper $wpdb->prepare() placeholders
  • Fix direct database query caching warning in taxonomy manager
  • WordPress Plugin Check compliance improvements

1.7.0

  • Slug settings moved to Settings Permalinks page (alongside WooCommerce product permalinks)
  • New “Update slug on re-sync” option: update existing product slugs during sync
  • Sync history: new “Trigger” column showing Manual, Scheduled, or Purge
  • Purge (delete all) now adds an entry to sync history and preserves last sync status
  • Backward compatible slug settings migration from plugin settings to Permalinks page
  • New unit tests for slug resolver (16 tests)
  • Updated translations (all languages)

1.6.0

  • Product slug configuration: choose slug source field (product name, SKU, manufacturer code, external ID, Skwirrel ID)
  • Slug suffix on duplicate: configurable fallback field appended when slug already exists
  • New class: Slug Resolver for deterministic product URL slugs

1.5.0

  • Major refactoring: SyncService split from ~2200 lines into focused sub-classes
  • New classes: ProductUpserter, ProductLookup, SyncHistory, PurgeHandler, CategorySync, BrandSync, TaxonomyManager, EtimExtractor, CustomClassExtractor, AttachmentHandler
  • SyncService reduced to ~480 lines (pure orchestrator)
  • ProductMapper reduced to ~460 lines (delegates to focused sub-classes)
  • All existing public APIs preserved — no breaking changes

1.4.0

  • Brand sync: Skwirrel brands synced into WooCommerce product_brand taxonomy
  • Category tree sync: sync full category tree from a configurable super category ID
  • Sync progress indicator: spinning icon on menu item, blue status bar with auto-refresh
  • Sync button disabled while sync is in progress
  • Heartbeat mechanism: sync status auto-expires after 60s without activity
  • Purge: danger zone now also deletes product brands
  • Settings save clears sync-in-progress state
  • i18n: all UI strings switched to English source text
  • Updated translation files (POT + nl_NL, nl_BE, de_DE, fr_FR, fr_BE, en_US, en_GB)

1.3.2

  • i18n: all UI strings switched to English source text
  • Updated translation files (POT + nl_NL, nl_BE, de_DE, fr_FR, fr_BE, en_US, en_GB)
  • Added new translation entries for tabbed UI, custom classes, danger zone and delete protection
  • Recompiled all .mo binary translation files

1.3.1

  • Deep category tree sync: full ancestor chain from nested _parent_category (unlimited depth)
  • Custom Class sync: product-level and trade-item-level custom classes as WooCommerce attributes
  • Custom Class feature types: A (alphanumeric), M (multi), L (logical), N (numeric), R (range), D (date), I (internationalized)
  • Custom Class text types T and B stored as product meta (skwirrel_cc* prefix)
  • Whitelist/blacklist filtering on custom class ID or code
  • New settings: sync_custom_classes, sync_trade_item_custom_classes, custom_class_filter_mode, custom_class_filter_ids

1.3.0

  • Admin UI: tabbed layout (Sync Products, Instellingen, Logs)
  • Sync status and history now shown on the default Sync Products tab
  • Sync button moved to page title, visible on all tabs
  • Logs and variation debug instructions on dedicated Logs tab
  • Fixed GitHub release workflow: version is read from plugin file, no more auto-incrementing

1.2.3

  • WordPress Plugin Check compliance: translators comments, ordered placeholders, escape output
  • WordPress Plugin Check compliance: phpcs:ignore for direct DB queries, non-prefixed WooCommerce globals, nonce verification
  • Use WordPress alternative functions (wp_parse_url, wp_delete_file, wp_is_writable)
  • Translate readme.txt to English

1.2.2

  • Version bump in preparation for release

1.2.1

  • Update text domain and constants for Skwirrel PIM Sync rebranding

1.2.0

  • Rebranded to Skwirrel PIM Sync
  • Added unit tests for MediaImporter, ProductMapper, and related components
  • Added WordPress.org auto-deploy workflow
  • Added automated versioning, tagging, and release workflow

1.1.2

  • Version bump
  • Fix duplicate products during sync: 3-step lookup chain + SKU conflict prevention

1.1.1

  • Delete protection: warning banners on Skwirrel-managed products and categories
  • Purge stale products and categories after full sync
  • Category sync with parent-child hierarchy support
  • Collection ID filter for selective synchronisation
  • Translation files (POT + nl_NL, nl_BE, en_US, en_GB, de_DE, fr_FR, fr_BE)
  • New settings: purge_stale_products, show_delete_warning, collection_ids, sync_categories, include_languages, image_language
  • PHPStan, PHP_CodeSniffer, and Pest PHP test framework
  • WooCommerce 10.5 compatibility

1.0.0

  • Initial release
  • Full product synchronisation
  • Variable products with ETIM variation axes
  • Image and document import
  • Delta synchronisation support