Kaynağa Gözat

Fix: escape-stripped price in card variant <select> — v1.5.0 -> v1.5.1

The upload-card's variant <select> was showing literal "<span
class="woocommerce-Price-amount amount">...</span>" inside the option
labels. Root cause: buildVariantSelect() called formatPrice() (which
returns HTML span wrappers for inline display) and then wrote the
result via $opt.text(), which escaped the markup into visible text.

Fix: add a formatPricePlain() that applies the same decimal/thousand
separator + priceFormat rules but strips any span wrappers, and use it
for <option> labels. formatPriceHtml() stays for inline price cells
inside cards, where the span hooks are actually useful for theme CSS.
Also tidied the <select> construction: jQuery .text() all the way so
user-provided variant labels can't inject markup.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Dalibor Votruba 2 ay önce
ebeveyn
işleme
65e9fbbbb6

+ 1 - 1
studiou-wc-free-photo-product/CLAUDE.md

@@ -2,7 +2,7 @@
 
 ## Project Overview
 
-WordPress/WooCommerce plugin called **studiou-wc-free-photo-product** (QDR - Studiou WC Free Photo Product) v1.5.0.
+WordPress/WooCommerce plugin called **studiou-wc-free-photo-product** (QDR - Studiou WC Free Photo Product) v1.5.1.
 Allows publishing special variable products where customers upload custom raw images to be printed for a price.
 
 ## Initial Requirements

+ 26 - 4
studiou-wc-free-photo-product/assets/js/frontend.js

@@ -105,16 +105,21 @@
 
         function buildVariantSelect(initialVariationId) {
             var $select = $('<select class="studiou-fpp-card-variant"></select>');
-            $select.append('<option value="0">' + escHtml(i18n.selectVariant || 'Select variant') + '</option>');
+            $select.append($('<option></option>').val(0).text(i18n.selectVariant || 'Select variant'));
             for (var i = 0; i < variants.length; i++) {
                 var v = variants[i];
-                var priceLabel = v.base_price ? ' — ' + formatPrice(v.base_price) : '';
+                // <option> text nodes cannot render HTML, so use a plain-text price formatter
+                var priceLabel = v.base_price ? ' — ' + formatPricePlain(v.base_price) : '';
+                var label = v.label + priceLabel;
+                if (!v.in_stock) {
+                    label += ' — ' + (i18n.outOfStock || 'out of stock');
+                }
                 var $opt = $('<option></option>')
                     .val(v.id)
-                    .text(v.label + priceLabel)
+                    .text(label)
                     .attr('data-base-price', v.base_price);
                 if (!v.in_stock) {
-                    $opt.prop('disabled', true).text(v.label + priceLabel + ' — ' + (i18n.outOfStock || 'out of stock'));
+                    $opt.prop('disabled', true);
                 }
                 $select.append($opt);
             }
@@ -623,6 +628,23 @@
             return formatPriceHtml(amount);
         }
 
+        // Plain-text price (no HTML) — used for <option> labels where spans aren't rendered
+        function formatPricePlain(amount) {
+            var c = studiouWcfppFront.currency || {};
+            var decimals = parseInt(c.decimals, 10); if (isNaN(decimals)) decimals = 2;
+            var decSep = c.decimalSeparator || '.';
+            var thouSep = c.thousandSeparator || ',';
+            var fmt = c.priceFormat || '%1$s%2$s';
+            var symbol = c.symbol || '';
+            var fixed = parseFloat(amount).toFixed(decimals);
+            var parts = fixed.split('.');
+            var intPart = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, thouSep);
+            var decPart = parts.length > 1 ? parts[1] : '';
+            var numStr = decPart ? intPart + decSep + decPart : intPart;
+            // strip any HTML tags that might arrive from priceFormat (defensive)
+            return fmt.replace('%1$s', symbol).replace('%2$s', numStr).replace(/<[^>]*>/g, '');
+        }
+
         function formatPriceHtml(amount) {
             var c = studiouWcfppFront.currency || {};
             var decimals = parseInt(c.decimals, 10); if (isNaN(decimals)) decimals = 2;

+ 1 - 1
studiou-wc-free-photo-product/languages/studiou-wc-free-photo-product-cs_CZ.po

@@ -3,7 +3,7 @@
 # This file is distributed under the GPL v2 or later.
 msgid ""
 msgstr ""
-"Project-Id-Version: QDR - Studiou WC Free Photo Product 1.5.0\n"
+"Project-Id-Version: QDR - Studiou WC Free Photo Product 1.5.1\n"
 "Report-Msgid-Bugs-To: https://www.quadarax.com\n"
 "POT-Creation-Date: 2026-04-02 00:00+0000\n"
 "PO-Revision-Date: 2026-04-02 00:00+0000\n"

+ 1 - 1
studiou-wc-free-photo-product/languages/studiou-wc-free-photo-product.pot

@@ -2,7 +2,7 @@
 # This file is distributed under the GPL v2 or later.
 msgid ""
 msgstr ""
-"Project-Id-Version: QDR - Studiou WC Free Photo Product 1.5.0\n"
+"Project-Id-Version: QDR - Studiou WC Free Photo Product 1.5.1\n"
 "Report-Msgid-Bugs-To: https://www.quadarax.com\n"
 "POT-Creation-Date: 2026-04-02 00:00+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"

+ 2 - 2
studiou-wc-free-photo-product/studiou-wc-free-photo-product.php

@@ -3,7 +3,7 @@
  * Plugin Name: QDR - Studiou WC Free Photo Product
  * Plugin URI: https://www.quadarax.com/plugins/studiou-wc-free-photo-product
  * Description: Allows publishing special WooCommerce products with variants and custom raw image upload for photo printing
- * Version: 1.5.0
+ * Version: 1.5.1
  * Requires at least: 6.9.4
  * Requires PHP: 8.2
  * Requires Plugins: woocommerce, product-variant-table-for-woocommerce
@@ -22,7 +22,7 @@ if (!defined('WPINC')) {
     die;
 }
 
-if (!defined('STUDIOU_WCFPP_VERSION')) define('STUDIOU_WCFPP_VERSION', '1.5.0');
+if (!defined('STUDIOU_WCFPP_VERSION')) define('STUDIOU_WCFPP_VERSION', '1.5.1');
 if (!defined('STUDIOU_WCFPP_PLUGIN_DIR')) define('STUDIOU_WCFPP_PLUGIN_DIR', plugin_dir_path(__FILE__));
 if (!defined('STUDIOU_WCFPP_PLUGIN_URL')) define('STUDIOU_WCFPP_PLUGIN_URL', plugin_dir_url(__FILE__));
 if (!defined('STUDIOU_WCFPP_PLUGIN_BASENAME')) define('STUDIOU_WCFPP_PLUGIN_BASENAME', plugin_basename(__FILE__));