Beskrivelse
Woo Webalive Remote Stock lets a WooCommerce shop track and display stock levels that live at distributors / suppliers, alongside the shop's own local stock.
Ships with:
- An adapter interface (
WWRS_Source_Adapter) so any XML / JSON / REST / CSV feed can be plugged in without touching core. - A first built-in adapter for XML feeds with HTTP Basic auth.
- An admin screen under WooCommerce → Remote stock to configure sources and trigger imports manually.
- A read filter (
wwrs_get_remote_stock) for themes and other plugins (including Webalive Stock Status) to render remote stock next to local stock.
Mapping to Woo products:
- Simple product: matched by SKU == remote
model. - Variation: matched either by the variation's own SKU, or by a per-variation "Remote option value id" bound to the supplier's
option_values_id.
Ændringslog
1.6.16
- Broader scrub for supplier text fields: on top of stripping 4-byte UTF-8 the plugin now (a) re-encodes any malformed UTF-8 byte sequences with the Unicode replacement character (via
mb_convert_encodingUTF-8 -> UTF-8 withmb_substitute_character(0xFFFD)), and (b) removes C0 control characters other than tab / LF / CR (NULL bytes, backspace, form feed, escape). These are the values MySQL rejects with the generic "field failed" error even on tables with correct charsets.
1.6.15
- Fixed WordPress DB insert failure when the extracted supplier description contained 4-byte UTF-8 characters (emojis, rare CJK, some math symbols) and the shop's DB tables were still on the legacy 3-byte
utf8collation.title,h1,description,description_fullare now pre-scrubbed of 4-byte codepoints (replaced with U+FFFD replacement) before insert/update, so 4-byte chars in supplier catalogues no longer crash the preview save.
1.6.14
- Fixed off-by-one in
SupplierItemRepository::upsert()INSERT / UPDATE format arrays (13 fields but 14 formats on INSERT, 11 fields but 12 formats on UPDATE). The extra%swas silently misaligning the placeholder mapping, so DB writes could fail on some rows. - Upsert now throws a
RuntimeExceptionwhen$wpdb->insert/$wpdb->updatereturnfalseand includes$wpdb->last_error, so the AJAX endpoint surfaces the real DB error instead of a misleading "template-empty" message.
1.6.13
- When
preview_for()returns null, the AJAX error message now includes an explicit reason (source-not-found,empty-supplier-item-id,template-empty, etc.) with the actual source_id and template values inline, so it's obvious why the URL couldn't be resolved.
1.6.12
- All AJAX endpoints now return HTTP 200 with
{success:false,data:{message:...}}for handled errors (previously they used 400/403/404/500). Non-200 codes make jQuery's$.ajaxskip.done()and jump straight to.fail(), which was swallowing our specific error messages and always showing the generic "Kunne ikke hente forhåndsvisning". This is the standard WordPress AJAX contract.
1.6.11
- AJAX preview endpoint wraps the extractor in
try / catchand returns the exception's message (plus file / line for admins withWP_DEBUGon) instead of letting a fatal error surface as a generic "Kunne ikke hente forhåndsvisning". Errors are alsoerror_log'd so hosters can see them without hitting the browser again.
1.6.10
- Fixed AJAX preview timing out ("Could not fetch preview.") on products with 3+ images. Image mirror timeout reduced from 15 s to 5 s and skipped image files are silently left as remote URLs. Also aborts image mirroring once the whole request has spent 25 s so the AJAX call always returns within PHP's default 30 s ceiling.
1.6.9
- Fixed frontend badge position setting being ignored on single product pages. The
woocommerce_get_stock_htmlfilter was appending the badge to WooCommerce's<p class="stock">element (which lives at the top of the summary) and the dedupe guard then blocked the position-controlled action hook. On single-product pages the filter is now skipped for the main product so the position setting is respected, and the badge is wrapped in a block-level<p class="wwrs-badge-remote-wrap">so it takes its own line without pushing the add-to-cart form sideways.
1.6.8
- Frontend badge placement is now configurable in Settings. Choices: Above price (priority 4), Below price (12), Above add-to-cart (25 — previous default), Below add-to-cart (35), In product meta (45).
- Removed the debug
<!-- wwrs ... summary: ... -->HTML comment from single product pages. Nothing traceable is emitted in production output any more.
1.6.7
- Every mapping is now also written as a repeatable
_wwrs_supplier_linkpostmeta on the Woo product / variation post. Value format:source_id|supplier_item_id|supplier_variation_key. Visible in wp-admin custom fields, WP All Export, wp-cli, and any code that queries products by supplier reference. - Delete / unlink also removes the matching postmeta value.
- Upgrade path: existing rows in
wp_wwrs_mapping(from earlier versions) are back-filled to postmeta on nextadmin_init. - Frontend badge now reads directly from postmeta first (fallback to mapping table), so a product linked via WP All Import (postmeta only) also fires the badge without touching the plugin admin.
1.6.6
- Diagnostic HTML comment renamed the ambiguous
qtyfield toremote_qtyand added the product's SKU so it's easier to see whether an SKU fallback lookup should have matched a suppliermodel.
1.6.5
- Badge trigger widened: any stock status other than
instockcounts as unavailable (previouslyonbackorderwas silently accepted as available). So products on backorder / out-of-stock / unknown status now all get the badge when the shop has remote stock. - Detection also still fires on positive
manage_stockwith qty <= 0 (regardless of backorder flag).
1.6.4
- Badge trigger no longer relies solely on
WC_Product::is_in_stock(). That returns TRUE for products with_manage_stock = 'no'even when their explicit stock quantity is 0, which meant the badge never fired for shops that manage stock via the stock_status flag while a numeric quantity of 0 still lives on the product. New trigger: badge shows when EITHERis_in_stock()is false OR the product manages stock andget_stock_quantity() <= 0. - Debug HTML comment now includes
stock_status,stock_qtyandmanage_stockso it's obvious what Woo believes about a product's local availability.
1.6.3
- Frontend badge injects an HTML comment on single-product pages regardless of whether a badge is emitted, so you can inspect the source to see exactly why the badge did or did not render (in stock? remote qty? feature disabled? not mapped?).
- Inline CSS gained a
/* WWRS <version> */marker so it's trivial to confirm the deployed version matches the code you expect.
1.6.2
- Frontend badge now also renders via a direct
woocommerce_single_product_summaryaction (priority 25) so it appears on single-product pages even when the theme skips the standardwc_get_stock_htmloutput for out-of-stock products. - Same product won't render the badge twice: the filter path and the action path share a per-request "already-rendered" guard.
- Frontend inline CSS is now enqueued on every non-admin page (small cost), so the badge always looks styled regardless of where WooCommerce injects the html.
1.6.1
- Converted price now floors to the nearest whole unit of the target currency (e.g.
1699.53→1699.00) while still formatting with two decimals so amounts render consistently. Applies to same-currency VAT conversions too. - Fixed preview modal price row layout: label "Supplier" / "Your store" now sits on its own line above the value instead of colliding with the amount.
1.6.0
- Frontend remote-stock badge: when a Woo product would otherwise render as "out of stock", WWRS now injects an orange badge with the total remote quantity (summed across every mapped supplier item). Text and colour are configurable in Settings. When the remote quantity is below the
Only-threshold (default 5), the alternative wording (defaultOnly %d in remote stock) is used to convey scarcity. - Rendering hooks:
woocommerce_get_stock_htmlon single product + shop loop, so it appears wherever Woo would otherwise print "Out of stock". - Consumers can override via
wwrs_stock_badge_htmlandwwrs_stock_badge_qtyfilters. - No effect on WWSS or other display plugins — the badge is standalone.
1.5.0
- Full text description: preview now grabs the entire body copy from the supplier product page (
<div class="product_info_description">on tubs.parts, generic fallbacks otherwise), not just the short meta description. - Every product image: JSON-LD image array +
.main_image+.sub_imageare all collected, de-duplicated and mirrored towp-content/uploads/wwrs-cache/. Modal renders the primary big image plus a horizontal thumbnail strip. - EUR → DKK currency conversion: preview shows both original supplier price and a converted target-currency price. Formula strips source VAT, converts via a configurable rate, then adds target VAT. Defaults: base DKK, base VAT 25 %, rate EUR = 7.45. Per-source
source_vat_pctconfig field (default 20 for tubs). - New Settings tab on the Remote stock page: edit base currency, base VAT %, and currency rates in one place.
- Schema bumped to 1.5.0 with new
images(JSON) anddescription_fullcolumns onwp_wwrs_supplier_item; the cache is truncated automatically on upgrade so every item re-fetches.
1.4.0
- Dashboard header on the Remote stock page: shows total supplier items, linked, ignored, unmapped, and configured sources at a glance. Numbers are live counts from the DB.
- Auto-link button on the Mapping tab: iterates every unmapped supplier stock row, runs the source's SKU transform (prefix strip + wildcard + ignore-separators + candidate regex), and creates a mapping when there is exactly one Woo SKU match. Ambiguous rows (multiple candidates) and rows with no candidate are skipped so nothing risky is auto-linked.
- Auto-link is scoped by the current Mapping tab filters (source / status / search), so you can preview “unmapped” rows, then click Auto-link to sweep them.
- Result summary after a run: linked / ambiguous / no candidates / already mapped.
1.3.0
- Product edit page → Remote stock metabox is now a full two-way mapping manager:
- Shows every explicit mapping currently linked to this product / variations (source, supplier item id, variation, live qty, unlink button).
- Search-as-you-type widget queries the plugin's supplier item + stock tables so merchants can pick a supplier item to link straight from the product edit page. Matches on
supplier_item_id, model, cached title and description. - Falls back to legacy "remote stock rows matched by SKU" section when no explicit mapping exists so nothing is hidden.
- New AJAX endpoint
wwrs_search_supplier_items(nonce +manage_woocommerceprotected). - Modal image now
loading="lazy"and hooked up to the mirrored local URL introduced in 1.2.15.
1.2.15
- Supplier images are now mirrored to
wp-content/uploads/wwrs-cache/and served from your own domain. Tubs.parts (and many other shops) block hot-linking of images — embedding<img src="https://www.tubs.parts/...">on your admin page returned nothing / a broken image. Mirroring bypasses this and also makes previews instant on repeat views. - Referer header is forwarded to the supplier so their hot-link protection accepts the fetch.
- Only whitelisted image extensions (png / jpg / jpeg / gif / webp) are cached. Files are named
{sha1(url)}.{ext}and served directly by the web server (no PHP wrapper). - Falls back to the remote URL if caching fails for any reason (permissions, disk full, ...).
1.2.14
- Upgrade path: on schema bump to 1.4.0 the
wp_wwrs_supplier_itemtable is truncated automatically. This wipes stale phpThumb / broken image URLs that were stored by 1.2.4-1.2.9 before the extractor was fixed. Next time any supplier item's modal is opened, it fetches fresh data.
1.2.13
- Critical fix: the modal's Refresh button was silently a no-op. The JS was sending
force=1to the AJAX endpoint, but the PHP handler ignored the parameter and always returned the cached DB row. Since the 24 h TTL means we never re-fetch on view, this meant every subsequent image/price improvement was invisible on already-viewed items until either the row expired or was manually deleted. - Now the AJAX endpoint honours
force=1— clicking Refresh actually refetches the supplier page and updates the persisted row. This is what unblocks the image + price improvements from 1.2.5-1.2.12 for items that were viewed under older versions.
1.2.12
- Image extractor now targets tubs.parts' actual product gallery classes: images inside
.main_image(primary) and.sub_image(extra) are picked up directly. Runs before the click-to-enlarge / id-in-URL fallbacks so we never guess when there's a solid signal. - Works whether the class is on the
<img>itself or on a wrapping<div>/<a>/<figure>.
1.2.11
- Image extractor no longer requires the supplier item id to be in the image filename. Tubs.parts and similar osCommerce shops routinely name product images descriptively (
Dessin sans titre (18).jpg,BoutonThermostat H30-H20.jpg, ...). New strategy:- Any
<img>or<a href>whosealt/title/anchor text contains “Click to enlarge”, “enlarge”, “click to zoom”, “view larger”, “full size” wins. - Then the previous id-in-URL fallback (still useful when ids ARE present).
- Icons / logos / spacers are filtered out (based on filename + explicit small width/height).
- Any
1.2.10
- Image extractor now unwraps phpThumb / TimThumb URLs:
phpThumb.php?src=images/4224.png&w=100becomesimages/4224.png. That means every product on tubs.parts gets a real image (either the direct<a href>if present, or the underlying source of the phpThumb thumbnail). - Fewer situations where we silently return no image — previously we rejected all phpThumb candidates and if none other existed the modal was blank.
1.2.9
- Image extractor now scores candidates and rejects known-bad thumbnail generators (phpThumb, TimThumb,
cache/,resize=query args,_thumb/thumb_in filename). Fixes the tubs.parts modal previously showing the phpThumb debug "error messages disabled" image. - Direct
<a href>links to full-size images are preferred over<img src>when both are present. - Clean URLs (no query string) beat URLs with parameters.
1.2.8
- Fixed price scanner:
\bword boundary after non-ASCII currency symbols like€never matched, causing tubs.parts style prices6,30€to be silently dropped. Replaced with a Unicode-safe(?![\w])lookahead. - Price scanner tuned for osCommerce / xtCommerce output (which is what tubs.parts runs).
1.2.7
- Preview extractor gained supplier-id-aware fallbacks so it works with old-school osCommerce / xtCommerce shops (e.g. tubs.parts) that don't ship JSON-LD or OpenGraph:
- Image: falls back to any
<a>/<img>whose URL contains/images/{supplier_item_id}*.{png,jpg,jpeg,gif,webp}. - Price: falls back to scanning body text for currency patterns (
6,30€,1.999,50 DKK,$4.99,£4.50,4,99 kr, ...). First match wins; currency is derived from the matched symbol / code.
- Image: falls back to any
1.2.6
- New per-source option Ignore separators when matching SKUs. When enabled, dashes, underscores and spaces are stripped from BOTH the Woo SKU (after prefix removal) and the supplier model before comparing — e.g. Woo
581-01211090-25now matches tubs model0121109025out of the box. - Wildcard tokens still work in combination:
PF-XX-1N22normalizes toPFXX1N22, wildcard is expanded, and matches Woo582-PF-30-1N22->PF301N22. - Source form now supports
checkboxconfig field type.
1.2.5
- Supplier preview now extracts price, currency and a much better image. Extraction pipeline: JSON-LD (schema.org Product) first, then OpenGraph / Twitter meta, then microdata
itemprop="image"/itemprop="price". Relative image URLs are resolved to absolute. - Preview card gained a highlighted price line with currency badge.
wp_wwrs_supplier_itemtable gainedpriceandcurrencycolumns (schema upgraded automatically, DB version 1.3.0).
1.2.4
- Supplier preview modal no longer relies on iframes (blocked by
X-Frame-Optionson tubs.parts and many others). Instead the plugin now fetches the supplier page server-side, extracts image, title, description and h1, and renders them as an inline preview card. "Open at supplier ↗" button remains for the full page. - Preview data is cached in a transient for 24 h per supplier item.
- New AJAX endpoint
wwrs_supplier_preview(nonce +manage_woocommerceprotected).
1.2.3
- Fixed URL template being mangled on save:
{id}placeholder was stripped byesc_url_raw().product_url_templateis now a text field that preserves placeholders. - SKU prefix stripping now supports multiple regex patterns, one per line — e.g. put
^581-,^582-,^511-on separate lines if you don't want the default^\d{3}-to match every three-digit prefix. First matching pattern wins. - Source form now supports
textareaconfig fields.
1.2.2
- Supplier item
#NNNNis now ALWAYS clickable when we know how to build a URL. Auto-detects tubs.parts sources from the feed URL host and uses the built-inhttps://www.tubs.parts/en/-p-{id}.htmlpattern — no more required manual URL template config. - Same auto-detection for
www.spafix.dkmirror hosts. product_url_templateconfig field still overrides the built-in patterns when set.
1.2.1
- Fixed plugin row "Settings" link pointing at the old
page=wwrs-sourcesslug — now correctly openspage=wwrs. - Supplier items on the Mapping tab are now clickable and open the supplier product page in an in-admin modal (iframe) so merchants can preview WHAT a supplier item like
#2161actually is without leaving WooCommerce. Falls back to opening in a new tab when the supplier blocks framing.
1.2.0
- Unified admin surface: one Remote stock menu item under WooCommerce with WP-standard tabs (Sources, Mapping, Log).
- Mapping: manual link field became a search-as-you-type input matching Woo SKU or title, showing up to 10 candidates. No more copy-pasting numeric IDs.
- Mapping: every supplier row now shows a View at supplier ↗ link built from a per-source URL template (
product_url_template, defaulthttps://www.tubs.parts/en/-p-{id}.html). - Mapping: candidate view fetches the supplier page's
<title>on demand (24 h cached) so items like#449show what they actually are. - Refreshed admin CSS — cards, spacing, status badges, matches the look-and-feel of WWSMP / WWSS.
- New AJAX search endpoint
wwrs_search_products(nonce +manage_woocommerceprotected).
1.1.0
- Explicit mapping between supplier items and Woo products/variations, stored in a new
wp_wwrs_mappingtable (N:N: one Woo product can be linked to items from multiple sources). - New admin sub-page WooCommerce → Remote stock mapping with filters (source / status / free text search), pagination and per-row actions (Find candidates, Ignore, Unlink).
- Auto-suggest engine: per-source SKU transform (prefix regex + wildcard token) that turns supplier models like
PF-XX-1N22into Woo SKU candidate patterns like^\d{3}-PF-[A-Z0-9]{2}-1N22$. - XML source config gained three new fields: SKU strip prefix, wildcard token and wildcard regex (defaults suit
NNN-prefix +XXwildcard). wwrs_get_remote_stocknow consults the mapping table first and falls back to the previous SKU==model behaviour when no explicit mapping exists.- New
linked/ignored/import_candidatestatuses so unwanted supplier items disappear from the mapping list without being deleted.
1.0.2
- XML source: auto-recover from feeds that declare
encoding="UTF-8"but actually contain Windows-1252 / Latin-1 bytes (e.g. rawéas 0xE9). Body is transcoded to UTF-8 and the XML declaration is rewritten before parsing.
1.0.1
- XML source: much better parse-error diagnostics (libxml errors, root element name, first bytes of response body, content-type).
- XML source: strip UTF-8 BOM and leading whitespace before parsing.
- XML source: detect HTML responses (e.g. supplier login page) and surface a clear error instead of a generic parse failure.
- XML source:
<item>elements are now located via//itemXPath, so they are found regardless of wrapper element depth.
1.0.0
- Initial release.
- XML basic-auth source adapter.
- Manual "Import now" from the admin sources screen.
wp_wwrs_stocktable with atomic per-source replacement.wwrs_get_remote_stockread filter.- Product metabox + variation field for mapping.