| @@ -7,8 +7,14 @@ | ||
| 7 | 7 | */ |
| 8 | 8 | |
| 9 | 9 | namespace UltimateStoreKit\Templates; |
| 10 | 10 | |
| 11 | +if (! defined('ABSPATH')) { | |
| 12 | + exit; // Exit if accessed directly | |
| 13 | +} | |
| 14 | + | |
| 15 | +// phpcs:disable WordPress.NamingConventions.PrefixAllGlobals -- usk_ / BDTUSK_ / ultimate-store-kit- are this plugin's established public prefixes. Ultimate Store Kit Pro calls into these names, as does third-party integration code, so renaming them is a breaking change. Plugin Check only recognises prefixes derived verbatim from the slug and so reports them as unprefixed. | |
| 16 | + | |
| 11 | 17 | use UltimateStoreKit\Traits\Global_Widget_Template; |
| 12 | 18 | use UltimateStoreKit\Classes\Utils; |
| 13 | 19 | |
| 14 | 20 | class USK_Shiny_Grid_Template { |
| @@ -201,8 +207,9 @@ | ||
| 201 | 207 | } |
| 202 | 208 | } |
| 203 | 209 | |
| 204 | 210 | // Output the button |
| 211 | + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Returns markup by contract; the sprintf arguments below are escaped individually, and WooCommerce core echoes this same filter unescaped. | |
| 205 | 212 | echo \apply_filters( |
| 206 | 213 | 'woocommerce_loop_add_to_cart_link', |
| 207 | 214 | sprintf( |
| 208 | 215 | '<a href="%s" data-quantity="%s" class="%s" %s>%s <i class="button-icon usk-icon-arrow-right-8"></i></a>', |
| @@ -318,9 +325,9 @@ | ||
| 318 | 325 | <a href="<?php echo esc_url(get_permalink()); ?>"> |
| 319 | 326 | <img |
| 320 | 327 | class="img" |
| 321 | 328 | src="<?php echo esc_url($product_image); ?>" |
| 322 | - alt="<?php echo esc_html(get_the_title()); ?>"> | |
| 329 | + alt="<?php echo esc_attr(get_the_title()); ?>"> | |
| 323 | 330 | </a> |
| 324 | 331 | <?php $this->render_add_to_cart_button($product, $settings); ?> |
| 325 | 332 | |
| 326 | 333 | <div class="usk-shoping"> |
| @@ -400,12 +407,14 @@ | ||
| 400 | 407 | return; |
| 401 | 408 | } |
| 402 | 409 | |
| 403 | 410 | // Add sequential data attribute if enabled |
| 404 | - $sequential_attr = $sequential ? ' data-sequential="true"' : ''; | |
| 411 | + if ($sequential) { | |
| 412 | + echo '<div class="usk-variations-container" data-product-id="' . esc_attr($product_id) . '" data-variations-reset="true" data-sequential="true">'; | |
| 413 | + } else { | |
| 414 | + echo '<div class="usk-variations-container" data-product-id="' . esc_attr($product_id) . '" data-variations-reset="true">'; | |
| 415 | + } | |
| 405 | 416 | |
| 406 | - echo '<div class="usk-variations-container" data-product-id="' . esc_attr($product_id) . '" data-variations-reset="true"' . $sequential_attr . '>'; | |
| 407 | - | |
| 408 | 417 | foreach ($attributes as $attribute_name => $options) { |
| 409 | 418 | if (empty($options)) { |
| 410 | 419 | continue; |
| 411 | 420 | } |
| @@ -465,12 +474,14 @@ | ||
| 465 | 474 | return; |
| 466 | 475 | } |
| 467 | 476 | |
| 468 | 477 | // Add sequential data attribute if enabled |
| 469 | - $sequential_attr = $sequential ? ' data-sequential="true"' : ''; | |
| 478 | + if ($sequential) { | |
| 479 | + echo '<div class="usk-variations-container usk-pro-swatches" data-product-id="' . esc_attr($product_id) . '" data-variations-reset="true" data-sequential="true">'; | |
| 480 | + } else { | |
| 481 | + echo '<div class="usk-variations-container usk-pro-swatches" data-product-id="' . esc_attr($product_id) . '" data-variations-reset="true">'; | |
| 482 | + } | |
| 470 | 483 | |
| 471 | - echo '<div class="usk-variations-container usk-pro-swatches" data-product-id="' . esc_attr($product_id) . '" data-variations-reset="true"' . $sequential_attr . '>'; | |
| 472 | - | |
| 473 | 484 | // Loop through each product attribute |
| 474 | 485 | foreach ($attributes as $attribute_name => $options) { |
| 475 | 486 | if (empty($options)) { |
| 476 | 487 | continue; |
| @@ -483,11 +494,13 @@ | ||
| 483 | 494 | 'options' => $options, |
| 484 | 495 | 'product' => $product, |
| 485 | 496 | 'attribute' => $attribute_name, |
| 486 | 497 | 'name' => 'attribute_' . sanitize_title($attribute_name), |
| 498 | + // phpcs:disable WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Preselects a variation from the shop URL; wc_clean() is WooCommerce's own sanitizer. | |
| 487 | 499 | 'selected' => isset($_REQUEST['attribute_' . sanitize_title($attribute_name)]) |
| 488 | 500 | ? wc_clean(wp_unslash($_REQUEST['attribute_' . sanitize_title($attribute_name)])) |
| 489 | 501 | : $product->get_variation_default_attribute($attribute_name) |
| 502 | + // phpcs:enable WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized | |
| 490 | 503 | ); |
| 491 | 504 | |
| 492 | 505 | // Create a placeholder for the dropdown - this will be replaced with swatches |
| 493 | 506 | $dropdown_html = '<select id="' . esc_attr($args['name']) . '" class="' . esc_attr($args['name']) . '" name="' . esc_attr($args['name']) . '" data-attribute_name="' . esc_attr($args['name']) . '" data-show_option_none="yes" style="display:none;">'; |
| @@ -504,11 +517,11 @@ | ||
| 504 | 517 | // Apply the filter to transform the dropdown to swatches |
| 505 | 518 | if (class_exists('UltimateStoreKitPro\\VariationSwatches\\Swatches')) { |
| 506 | 519 | $swatches = \UltimateStoreKitPro\VariationSwatches\Swatches::instance(); |
| 507 | 520 | $swatches_html = $swatches->swatches_html($dropdown_html, $args); |
| 508 | - echo $swatches_html; | |
| 521 | + echo $swatches_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Markup built from the already-escaped $dropdown_html. | |
| 509 | 522 | } else { |
| 510 | - echo $dropdown_html; | |
| 523 | + echo $dropdown_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Built above entirely from esc_attr()/esc_html()ed values. | |
| 511 | 524 | } |
| 512 | 525 | |
| 513 | 526 | echo '</div>'; // Close .usk-variation-group |
| 514 | 527 | } |