PluginProbe
Ultimate Store Kit – Store Builder Addons for Elementor, WooCommerce Store Builder, EDD Store Builder / 3.1.4
Ultimate Store Kit – Store Builder Addons for Elementor, WooCommerce Store Builder, EDD Store Builder v3.1.4
3.1.4 3.0.8 3.0.9 3.1.0 3.1.2 3.1.3 3.0.7 3.0.5 3.0.4 3.0.3 3.0.2 trunk 1.5.0 1.5.1 1.5.2 1.6.1 1.6.2 1.6.3 1.6.4 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 All 93 releases
← All changes | includes/helper.php +250 -263 3.0.33.1.4 View file →
@@ -1,6 +1,12 @@
1 1 <?php
2 2
3 +if (! defined('ABSPATH')) {
4 + exit; // Exit if accessed directly
5 +}
6 +
7 +// phpcs:disable WordPress.NamingConventions.PrefixAllGlobals -- BDTUSK_ / ultimate_store_kit_ / ultimate-store-kit- are this plugin's established public prefixes.
8 +
3 9 use UltimateStoreKit\Ultimate_Store_Kit_Loader;
4 10 use Elementor\Plugin;
5 11
6 12 /**
@@ -38,9 +44,9 @@
38 44 if (! defined('BDTUSK_NAME')) {
39 45 define('BDTUSK_NAME', 'Ultimate Store Kit');
40 46 }
41 47
42 -if (_is_usk_pro_activated()) {
48 +if (ultimate_store_kit_is_pro_activated()) {
43 49 if (! defined('BDTUSK_PC')) {
44 50 define('BDTUSK_PC', '');
45 51 } // pro control badge
46 52 define('BDTUSK_IS_PC', '');
@@ -269,136 +275,104 @@
269 275 }
270 276 }
271 277
272 278 /**
273 - * post pagination
279 + * Build a pagination link for widget grids or standard WordPress queries.
280 + *
281 + * @param int $page Page number.
282 + * @param string $query_arg Optional custom query arg (e.g. product-page).
283 + * @param string $current_url Base URL when using a custom query arg.
284 + * @return string
274 285 */
275 -function ultimate_store_kit_post_pagination($wp_query) {
286 +function ultimate_store_kit_get_pagination_link($page, $query_arg = '', $current_url = '') {
287 + $page = absint($page);
276 288
277 - /** Stop execution if there's only 1 page */
289 + if (! empty($query_arg)) {
290 + if ($page <= 1) {
291 + return remove_query_arg($query_arg, $current_url);
292 + }
278 293
279 - if ($wp_query->max_num_pages <= 1) {
280 - return;
294 + return add_query_arg($query_arg, $page, $current_url);
281 295 }
282 296
283 - if (is_front_page()) {
284 - $paged = (get_query_var('page')) ? get_query_var('page') : 1;
285 - } else {
286 - $paged = (get_query_var('paged')) ? get_query_var('paged') : 1;
287 - }
297 + return get_pagenum_link($page);
298 +}
288 299
289 - $max = intval($wp_query->max_num_pages);
290 -
291 - /** Add current page to the array */
292 -
293 - if ($paged >= 1) {
294 - $links[] = $paged;
300 +/**
301 + * Whether pagination should use the product-page query arg.
302 + *
303 + * WooCommerce product grids read the page from ?product-page= so widget
304 + * pagination does not conflict with WordPress paged/page on embedded pages.
305 + *
306 + * @param WP_Query|object $wp_query Query object.
307 + * @return bool
308 + */
309 +function ultimate_store_kit_pagination_uses_product_page($wp_query) {
310 + if (! $wp_query instanceof \WP_Query) {
311 + return false;
295 312 }
296 313
297 - /** Add the pages around the current page to the array */
314 + $post_type = $wp_query->get('post_type');
298 315
299 - if ($paged >= 3) {
300 - $links[] = $paged - 1;
301 - $links[] = $paged - 2;
316 + if (empty($post_type) && isset($wp_query->query_vars['post_type'])) {
317 + $post_type = $wp_query->query_vars['post_type'];
302 318 }
303 319
304 - if (($paged + 2) <= $max) {
305 - $links[] = $paged + 2;
306 - $links[] = $paged + 1;
320 + if (is_array($post_type)) {
321 + return in_array('product', $post_type, true);
307 322 }
308 323
309 - echo '<ul class="usk-pagination" aria-label="' . esc_attr__('Pagination', 'ultimate-store-kit') . '">' . "\n";
324 + return 'product' === $post_type;
325 +}
310 326
311 - /** Previous Post Link */
327 +/**
328 + * Post pagination.
329 + *
330 + * Auto-detects WooCommerce product grids and uses the product-page query arg.
331 + * EDD grids, product reviews, and other queries use standard WordPress pagination.
332 + *
333 + * @param WP_Query|object $wp_query Query object with max_num_pages.
334 + * @param array $args Optional. 'query_arg' => 'product-page' to force custom arg mode.
335 + */
336 +function ultimate_store_kit_post_pagination($wp_query, $args = []) {
337 + $args = wp_parse_args($args, [
338 + 'query_arg' => '',
339 + ]);
312 340
313 - if ($paged > 1) {
314 - $prev_link = get_pagenum_link($paged - 1);
315 - printf(
316 - '<li class="usk-pagination-previous">
317 - <a href="%s" aria-label="' . esc_attr__('Previous Page', 'ultimate-store-kit') . '">
318 - <span class="usk-icon-arrow-left-5" aria-hidden="true"></span>
319 - </a>
320 - </li>' . "\n",
321 - esc_url($prev_link)
322 - );
341 + if ($wp_query->max_num_pages <= 1) {
342 + return;
323 343 }
324 344
325 - /** Link to first page, plus ellipses if necessary */
345 + $query_arg = sanitize_key($args['query_arg']);
326 346
327 - if (! in_array(1, $links)) {
328 - $class = 1 == $paged ? ' class="current"' : '';
329 -
330 - printf('<li%s><a href="%s" target="_self">%s</a></li>' . "\n", wp_kses_post($class), esc_url(get_pagenum_link(1)), '1');
331 -
332 - if (! in_array(2, $links)) {
333 - echo '<li class="usk-pagination-dot-dot"><span>...</span></li>';
334 - }
347 + if ('' === $query_arg && ultimate_store_kit_pagination_uses_product_page($wp_query)) {
348 + $query_arg = 'product-page';
335 349 }
336 350
337 - /** Link to current page, plus 2 pages in either direction if necessary */
338 - sort($links);
351 + $use_custom_arg = ! empty($query_arg);
339 352
340 - foreach ((array) $links as $link) {
341 - $class = $paged == $link ? ' class="usk-active"' : '';
342 - printf(
343 - '<li%s><a href="%s" target="_self">%s</a></li>' . "\n",
344 - wp_kses_post($class),
345 - esc_url(get_pagenum_link($link)),
346 - esc_html($link)
347 - );
348 - }
353 + if ($use_custom_arg) {
354 + $page = max(1, absint(get_query_var('paged')), absint(get_query_var('page')));
355 + $custom_page = filter_input(INPUT_GET, $query_arg, FILTER_VALIDATE_INT);
349 356
350 - /** Link to last page, plus ellipses if necessary */
351 -
352 - if (! in_array($max, $links)) {
353 -
354 - if (! in_array($max - 1, $links)) {
355 - echo '<li class="usk-pagination-dot-dot"><span>...</span></li>' . "\n";
357 + if ($custom_page && $custom_page >= 1) {
358 + $page = absint($custom_page);
356 359 }
357 360
358 - $class = $paged == $max ? ' class="usk-active"' : '';
359 - printf(
360 - '<li%s><a href="%s" target="_self">%s</a></li>' . "\n",
361 - wp_kses_post($class),
362 - esc_url(get_pagenum_link($max)),
363 - esc_html($max)
364 - );
361 + $paged = absint($page);
362 + } elseif (is_front_page()) {
363 + $paged = absint(get_query_var('page')) ?: 1;
364 + } else {
365 + $paged = absint(get_query_var('paged')) ?: 1;
365 366 }
366 367
367 - /** Next Post Link */
368 + $max = absint($wp_query->max_num_pages);
369 + $links = [];
368 370
369 - if ($paged < $max) {
370 - $next_link = get_pagenum_link($paged + 1);
371 - printf(
372 - '<li class="usk-pagination-next">
373 - <a href="%s" aria-label="' . esc_attr__('Next Page', 'ultimate-store-kit') . '">
374 - <span class="usk-icon-arrow-right-5" aria-hidden="true"></span>
375 - </a>
376 - </li>' . "\n",
377 - esc_url($next_link)
378 - );
379 - }
380 -
381 - echo '</ul>' . "\n";
382 -}
383 -function ultimate_store_kit_post_pagination__new($wp_query) {
384 - $page = max(1, get_query_var('paged'), get_query_var('page'));
385 - $page = absint(empty($_GET['product-page']) ? $page : $_GET['product-page']);
386 - $paged = absint($page);
387 -
388 - /** Stop execution if there's only 1 page */
389 - if ($wp_query->max_num_pages <= 1) {
390 - return;
391 - }
392 -
393 - $max = intval($wp_query->max_num_pages);
394 -
395 - /** Add current page to the array */
396 371 if ($paged >= 1) {
397 372 $links[] = $paged;
398 373 }
399 374
400 - /** Add the pages around the current page to the array */
401 375 if ($paged >= 3) {
402 376 $links[] = $paged - 1;
403 377 $links[] = $paged - 2;
404 378 }
@@ -407,85 +381,87 @@
407 381 $links[] = $paged + 2;
408 382 $links[] = $paged + 1;
409 383 }
410 384
411 - // Get the current URL without any pagination parameters
412 - $current_url = remove_query_arg(['paged', 'page', 'product-page']);
385 + $current_url = '';
413 386
414 - // If we're on a custom post type archive or taxonomy page, preserve the base URL
415 - if (is_post_type_archive() || is_tax()) {
416 - $current_url = get_pagenum_link(1, false);
417 - $current_url = remove_query_arg(['paged', 'page', 'product-page'], $current_url);
387 + if ($use_custom_arg) {
388 + $current_url = remove_query_arg(['paged', 'page', $query_arg]);
389 +
390 + if (is_post_type_archive() || is_tax()) {
391 + $current_url = remove_query_arg(
392 + ['paged', 'page', $query_arg],
393 + get_pagenum_link(1, false)
394 + );
395 + }
418 396 }
419 397
420 398 echo '<ul class="usk-pagination" aria-label="' . esc_attr__('Pagination', 'ultimate-store-kit') . '">' . "\n";
421 399
422 - /** Previous Post Link */
423 400 if ($paged > 1) {
424 - $prev_page = $paged - 1;
425 - if ($prev_page < 1) {
426 - return;
427 - }
428 - $class = $paged == $prev_page ? ' class="current"' : '';
429 401 printf(
430 - '<li%s><a href="%s" target="_self" aria-label="' . esc_attr__('Previous Page', 'ultimate-store-kit') . '">%s</a></li>' . "\n",
431 - wp_kses_post($class),
432 - esc_url(add_query_arg('product-page', $prev_page, $current_url)),
433 - '<span class="usk-icon-arrow-left-5"></span>'
402 + '<li class="usk-pagination-previous">
403 + <a href="%s" aria-label="%s">
404 + <span class="usk-icon-arrow-left-5" aria-hidden="true"></span>
405 + </a>
406 + </li>' . "\n",
407 + esc_url(ultimate_store_kit_get_pagination_link($paged - 1, $query_arg, $current_url)),
408 + esc_attr__('Previous Page', 'ultimate-store-kit')
434 409 );
435 410 }
436 411
437 - /** Link to first page, plus ellipses if necessary */
438 - if (! in_array(1, $links)) {
439 - $class = 1 == $paged ? ' class="current"' : '';
412 + if (! in_array(1, $links, true)) {
413 + $class = (1 === $paged) ? 'usk-active' : '';
414 +
440 415 printf(
441 - '<li%s><a href="%s" target="_self">%s</a></li>' . "\n",
442 - wp_kses_post($class),
443 - esc_url(add_query_arg('product-page', '1', $current_url)),
444 - '1'
416 + '<li class="%s"><a href="%s" target="_self">%s</a></li>' . "\n",
417 + esc_attr($class),
418 + esc_url(ultimate_store_kit_get_pagination_link(1, $query_arg, $current_url)),
419 + esc_html('1')
445 420 );
446 - if (! in_array(2, $links)) {
447 - echo '<li class="usk-pagination-dot-dot"><span>...</span></li>';
421 +
422 + if (! in_array(2, $links, true)) {
423 + echo '<li class="usk-pagination-dot-dot"><span>...</span></li>' . "\n";
448 424 }
449 425 }
450 426
451 - /** Link to current page, plus 2 pages in either direction if necessary */
452 427 sort($links);
453 - foreach ((array) $links as $link) {
454 - $class = $paged == $link ? ' class="usk-active"' : '';
428 +
429 + foreach ($links as $link) {
430 + $class = ($paged === (int) $link) ? 'usk-active' : '';
431 +
455 432 printf(
456 - '<li%s><a href="%s" target="_self">%s</a></li>' . "\n",
457 - wp_kses_post($class),
458 - esc_url(add_query_arg('product-page', $link, $current_url)),
433 + '<li class="%s"><a href="%s" target="_self">%s</a></li>' . "\n",
434 + esc_attr($class),
435 + esc_url(ultimate_store_kit_get_pagination_link($link, $query_arg, $current_url)),
459 436 esc_html($link)
460 437 );
461 438 }
462 439
463 - /** Link to last page, plus ellipses if necessary */
464 - if (! in_array($max, $links)) {
465 - if (! in_array($max - 1, $links)) {
440 + if (! in_array($max, $links, true)) {
441 + if (! in_array($max - 1, $links, true)) {
466 442 echo '<li class="usk-pagination-dot-dot"><span>...</span></li>' . "\n";
467 443 }
468 - $class = $paged == $max ? ' class="usk-active"' : '';
444 +
445 + $class = ($paged === $max) ? 'usk-active' : '';
446 +
469 447 printf(
470 - '<li%s><a href="%s" target="_self">%s</a></li>' . "\n",
471 - wp_kses_post($class),
472 - esc_url(add_query_arg('product-page', $max, $current_url)),
448 + '<li class="%s"><a href="%s" target="_self">%s</a></li>' . "\n",
449 + esc_attr($class),
450 + esc_url(ultimate_store_kit_get_pagination_link($max, $query_arg, $current_url)),
473 451 esc_html($max)
474 452 );
475 453 }
476 454
477 - /** Next Post Link */
478 455 if ($paged < $max) {
479 - $next_page = $paged + 1;
480 - if ($next_page > $max) {
481 - return;
482 - }
483 456 printf(
484 - '<li%s><a href="%s" target="_self" aria-label="' . esc_attr__('Next Page', 'ultimate-store-kit') . '">%s</a></li>' . "\n",
485 - wp_kses_post($class),
486 - esc_url(add_query_arg('product-page', $next_page, $current_url)),
487 - '<span class="usk-icon-arrow-right-5"></span>'
457 + '<li class="usk-pagination-next">
458 + <a href="%s" aria-label="%s">
459 + <span class="usk-icon-arrow-right-5" aria-hidden="true"></span>
460 + </a>
461 + </li>' . "\n",
462 + esc_url(ultimate_store_kit_get_pagination_link($paged + 1, $query_arg, $current_url)),
463 + esc_attr__('Next Page', 'ultimate-store-kit')
488 464 );
489 465 }
490 466
491 467 echo '</ul>' . "\n";
@@ -692,10 +668,10 @@
692 668 */
693 669 function ultimate_store_kit_get_only_parent_cats($taxonomy = 'category') {
694 670
695 671 $parent_categories = ['none' => __('None', 'ultimate-store-kit')];
696 - $args = ['parent' => 0];
697 - $parent_cats = get_terms($taxonomy, $args);
672 + $args = ['taxonomy' => $taxonomy, 'parent' => 0];
673 + $parent_cats = get_terms($args);
698 674
699 675 foreach ($parent_cats as $parent_cat) {
700 676 // Ensure $parent_cat is an object, not an array
701 677 if (is_object($parent_cat) && isset($parent_cat->term_id, $parent_cat->name)) {
@@ -1025,23 +1001,8 @@
1025 1001
1026 1002 return wpautop($output);
1027 1003 }
1028 1004
1029 -function usk_get_order_options() {
1030 - $options = [
1031 - 'title' => __('Title', 'ultimate-store-kit'),
1032 - 'ID' => __('ID', 'ultimate-store-kit'),
1033 - 'date' => __('Date', 'ultimate-store-kit'),
1034 - 'rand' => __('Random', 'ultimate-store-kit'),
1035 - '_price' => __('Product Price', 'ultimate-store-kit'),
1036 - 'total_sales' => __('Top Seller', 'ultimate-store-kit'),
1037 - 'comment_count' => __('Most Reviewed', 'ultimate-store-kit'),
1038 - '_wc_average_rating' => __('Top Rated', 'ultimate-store-kit'),
1039 - ];
1040 -
1041 - return apply_filters('usk_order_options', $options);
1042 -}
1043 -
1044 1005 //wishlist
1045 1006 function ultimate_store_kit_get_wishlist($user_id = 0) {
1046 1007 $_wishlist_key = '_ultimate_store_kit_wishlist';
1047 1008 $_wishlist = [];
@@ -1046,13 +1007,15 @@
1046 1007 $_wishlist_key = '_ultimate_store_kit_wishlist';
1047 1008 $_wishlist = [];
1048 1009
1049 1010 if (isset($_COOKIE[$_wishlist_key])) {
1050 - $cookie_data = stripslashes($_COOKIE[$_wishlist_key]);
1011 + // The cookie is visitor-controlled, so it is sanitized before decoding and
1012 + // every decoded entry is forced to a product id — nothing else is kept.
1013 + $cookie_data = sanitize_text_field(wp_unslash($_COOKIE[$_wishlist_key]));
1051 1014 $decoded_data = json_decode($cookie_data, true);
1052 1015
1053 1016 if (json_last_error() === JSON_ERROR_NONE && is_array($decoded_data)) {
1054 - $_wishlist = $decoded_data;
1017 + $_wishlist = array_values(array_filter(array_map('absint', array_filter($decoded_data, 'is_scalar'))));
1055 1018 }
1056 1019 }
1057 1020
1058 1021 return apply_filters('ultimate_store_kit_wishlist', array_unique($_wishlist));
@@ -1057,28 +1020,8 @@
1057 1020
1058 1021 return apply_filters('ultimate_store_kit_wishlist', array_unique($_wishlist));
1059 1022 }
1060 1023
1061 -function usk_get_taxonomies() {
1062 - $taxonomy_list = get_object_taxonomies('product');
1063 - $taxonomies = [
1064 - 'search' => 'Search',
1065 - 'price' => 'Price',
1066 - 'orderby' => 'Orderby',
1067 - 'order' => 'Order',
1068 - ];
1069 -
1070 - foreach ($taxonomy_list as $_taxonomy) {
1071 - $taxonomy = get_taxonomy($_taxonomy);
1072 -
1073 - if ($taxonomy->show_ui) {
1074 - $taxonomies[$_taxonomy] = $taxonomy->label;
1075 - }
1076 - }
1077 -
1078 - return $taxonomies;
1079 -}
1080 -
1081 1024 function ultimate_store_kit_hide_on_class($selectors) {
1082 1025 $element_hide_on = '';
1083 1026
1084 1027 if (! empty($selectors)) {
@@ -1102,9 +1045,17 @@
1102 1045 return $element_hide_on;
1103 1046 }
1104 1047
1105 1048 function ultimate_store_kit_wc_product_quick_view_content($product_id) {
1106 - wp_verify_nonce('ajax-usk-quick-view-nonce', 'usk-quick-view-modal-sc');
1049 + // This renders on an unauthenticated endpoint, so the gate is on the product
1050 + // rather than on a nonce: nothing is written, and only a product the visitor
1051 + // could already open on the shop is allowed through. The previous
1052 + // wp_verify_nonce() call here had its arguments reversed and its result
1053 + // discarded, so it checked nothing.
1054 + if (! ultimate_store_kit_is_public_product($product_id)) {
1055 + return;
1056 + }
1057 +
1107 1058 global $woocommerce;
1108 1059 global $post;
1109 1060
1110 1061 if (intval($product_id)) {
@@ -1183,9 +1134,9 @@
1183 1134
1184 1135 function initializeWooCommerce() {
1185 1136 // Initialize WooCommerce add to cart functionality
1186 1137 var wc_add_to_cart_variation_params = {
1187 - "ajax_url": "<?php echo admin_url('admin-ajax.php'); ?>",
1138 + "ajax_url": "<?php echo esc_url(admin_url('admin-ajax.php')); ?>",
1188 1139 "i18n_view_cart": "<?php echo esc_js(__('View cart', 'ultimate-store-kit')); ?>",
1189 1140 "cart_url": "<?php echo esc_url(wc_get_cart_url()); ?>",
1190 1141 "is_cart": "<?php echo is_cart() ? '1' : '0'; ?>",
1191 1142 "cart_redirect_after_add": "<?php echo get_option('woocommerce_cart_redirect_after_add') ? '1' : '0'; ?>"
@@ -1274,12 +1225,12 @@
1274 1225
1275 1226 /**
1276 1227 * License Validation
1277 1228 */
1278 -if (! function_exists('usk_license_validation')) {
1279 - function usk_license_validation() {
1229 +if (! function_exists('ultimate_store_kit_license_validation')) {
1230 + function ultimate_store_kit_license_validation() {
1280 1231
1281 - if (function_exists('_is_usk_pro_activated') && false === _is_usk_pro_activated()) {
1232 + if (function_exists('ultimate_store_kit_is_pro_activated') && false === ultimate_store_kit_is_pro_activated()) {
1282 1233 return false;
1283 1234 }
1284 1235
1285 1236 $license_key = trim(get_option('ultimate_store_kit_license_key'));
@@ -1294,22 +1245,77 @@
1294 1245 }
1295 1246
1296 1247
1297 1248
1298 -function usk_get_compare_products($user_id = 0) {
1249 +
1250 +if (! function_exists('ultimate_store_kit_is_public_product')) {
1251 + /**
1252 + * Whether a product id may be read or acted on by the current request.
1253 + *
1254 + * wc_get_product() resolves an id regardless of post status, so any endpoint
1255 + * reachable by untrusted users must run the id through here before it reads
1256 + * product data or stores the id against the visitor.
1257 + *
1258 + * @param mixed $product_id Raw product id, typically straight off $_POST.
1259 + * @return bool
1260 + */
1261 + function ultimate_store_kit_is_public_product($product_id) {
1262 + $product_id = absint($product_id);
1263 +
1264 + // Fail closed when WooCommerce is absent — helper.php also loads on EDD-only sites.
1265 + if (! $product_id || ! function_exists('wc_get_product')) {
1266 + return false;
1267 + }
1268 +
1269 + $product = wc_get_product($product_id);
1270 +
1271 + if (! $product) {
1272 + return false;
1273 + }
1274 +
1275 + // Private, draft, pending and trashed products stay invisible unless the
1276 + // current user could read the post anyway (shop managers, the editor).
1277 + if ('publish' !== $product->get_status() && ! current_user_can('read_post', $product_id)) {
1278 + return false;
1279 + }
1280 +
1281 + if (post_password_required($product_id)) {
1282 + return false;
1283 + }
1284 +
1285 + return true;
1286 + }
1287 +}
1288 +
1289 +/**
1290 + * Maximum number of items kept in a wishlist or compare list.
1291 + *
1292 + * Both lists are written from unauthenticated endpoints, so they need an upper
1293 + * bound — a cookie that outgrows ~4KB is silently dropped by the browser, and an
1294 + * unbounded list against a logged-in user means unbounded user meta.
1295 + */
1296 +if (! function_exists('ultimate_store_kit_get_list_item_limit')) {
1297 + function ultimate_store_kit_get_list_item_limit() {
1298 + return (int) apply_filters('ultimate_store_kit_list_item_limit', 50);
1299 + }
1300 +}
1301 +
1302 +function ultimate_store_kit_get_compare_products($user_id = 0) {
1299 1303 $_compare_products_key = '_ultimate_store_kit_compare_products';
1300 1304 $_compare_products = [];
1301 1305 if ($user_id != 0) {
1302 1306 $_compare_products = get_user_meta($user_id, $_compare_products_key, true) ?: [];
1303 1307 } elseif (isset($_COOKIE[$_compare_products_key])) {
1304 - //$_compare_products = unserialize(stripslashes($_COOKIE[sanitize_text_field($_compare_products_key)]));
1308 + // Same treatment as the wishlist cookie: sanitize the visitor-supplied value
1309 + // before decoding, then keep nothing but product ids.
1310 + $cookie_value = sanitize_text_field(wp_unslash($_COOKIE[$_compare_products_key]));
1311 + $_compare_products = json_decode($cookie_value, true);
1305 1312
1306 - $cookie_value = sanitize_text_field($_COOKIE[$_compare_products_key]);
1307 - $_compare_products = json_decode(stripslashes($cookie_value), true);
1308 -
1309 1313 // Check if JSON decoding failed
1310 1314 if (! is_array($_compare_products)) {
1311 1315 $_compare_products = [];
1316 + } else {
1317 + $_compare_products = array_values(array_filter(array_map('absint', array_filter($_compare_products, 'is_scalar'))));
1312 1318 }
1313 1319 }
1314 1320
1315 1321 return apply_filters('ultimate_store_kit_compare_products', array_unique($_compare_products));
@@ -1314,17 +1320,8 @@
1314 1320
1315 1321 return apply_filters('ultimate_store_kit_compare_products', array_unique($_compare_products));
1316 1322 }
1317 1323
1318 -function usk_get_compare_products_count() {
1319 - $count = 0;
1320 - $user_id = get_current_user_id();
1321 - $products = usk_get_compare_products($user_id);
1322 - if (is_array($products)) {
1323 - $count = count($products);
1324 - }
1325 - return $count;
1326 -}
1327 1324
1328 1325 //if (!function_exists('ultimate_store_kit_get_compare_product_slug')) {
1329 1326 // function ultimate_store_kit_compare_product_slug() {
1330 1327 // return 'compare-products';
@@ -1330,11 +1327,39 @@
1330 1327 // return 'compare-products';
1331 1328 // }
1332 1329 //}
1333 1330
1331 +if (! function_exists('ultimate_store_kit_get_compare_page_option')) {
1332 + /**
1333 + * Read the compare-products page id, migrating it off the old "bdt_"-prefixed
1334 + * option key the first time it is seen.
1335 + *
1336 + * The option is written by Ultimate Store Kit Pro on activation and read by
1337 + * both plugins, so it is stored user data: a bare rename would detach an
1338 + * existing site's compare page.
1339 + *
1340 + * @return int Page id, or 0 when unset.
1341 + */
1342 + function ultimate_store_kit_get_compare_page_option() {
1343 + $value = get_option('ultimate_store_kit_compare_products_page_id', null);
1344 +
1345 + if (null === $value) {
1346 + $legacy = get_option('bdt_usk_compare_products_page_id', null);
1347 +
1348 + if (null !== $legacy) {
1349 + update_option('ultimate_store_kit_compare_products_page_id', $legacy, true);
1350 + delete_option('bdt_usk_compare_products_page_id');
1351 + $value = $legacy;
1352 + }
1353 + }
1354 +
1355 + return intval($value);
1356 + }
1357 +}
1358 +
1334 1359 if (! function_exists('ultimate_store_kit_compare_product_page')) {
1335 1360 function ultimate_store_kit_compare_product_page() {
1336 - if ($postId = intval(get_option('bdt_usk_compare_products_page_id'))) {
1361 + if ($postId = ultimate_store_kit_get_compare_page_option()) {
1337 1362 $post = get_post($postId);
1338 1363 if ($post && $post->post_status == 'publish') {
1339 1364 return $post->ID;
1340 1365 }
@@ -1350,21 +1375,13 @@
1350 1375 }
1351 1376 }
1352 1377 }
1353 1378
1354 -/**
1355 - * Helper function to check if variation swatches Pro is active
1356 - *
1357 - * @return bool
1358 - */
1359 -function usk_has_variation_swatches_support() {
1360 - return class_exists('UltimateStoreKitPro\\VariationSwatches\\Swatches');
1361 -}
1362 1379
1363 1380 /**
1364 1381 * Helper function to load variation swatches scripts and styles
1365 1382 */
1366 -function usk_load_variation_swatches_assets() {
1383 +function ultimate_store_kit_load_variation_swatches_assets() {
1367 1384 // Always load the grid variations script for variation support
1368 1385 wp_register_script('usk-grid-variations', BDTUSK_ASSETS_URL . 'js/modules/grid-variations.js', ['jquery'], BDTUSK_VER, true);
1369 1386 wp_localize_script('usk-grid-variations', 'usk_vars', array(
1370 1387 'ajax_url' => admin_url('admin-ajax.php'),
@@ -1370,62 +1387,28 @@
1370 1387 'ajax_url' => admin_url('admin-ajax.php'),
1371 1388 'nonce' => wp_create_nonce('usk_variations')
1372 1389 ));
1373 1390 }
1374 -add_action('wp_enqueue_scripts', 'usk_load_variation_swatches_assets', 20);
1391 +add_action('wp_enqueue_scripts', 'ultimate_store_kit_load_variation_swatches_assets', 20);
1375 1392
1376 -// Hook into AJAX variation selection to update product image
1377 -function usk_ajax_variation_image_update() {
1378 - if (!isset($_POST['variation_id']) || !isset($_POST['product_id'])) {
1379 - wp_send_json_error('Missing required parameters');
1380 - return;
1381 - }
1382 1393
1383 - $variation_id = absint($_POST['variation_id']);
1384 - $product_id = absint($_POST['product_id']);
1385 -
1386 - $variation = wc_get_product($variation_id);
1387 - if (!$variation) {
1388 - wp_send_json_error('Invalid variation');
1389 - return;
1390 - }
1391 -
1392 - $image_id = $variation->get_image_id();
1393 - $image_url = '';
1394 -
1395 - if ($image_id) {
1396 - $image_url = wp_get_attachment_image_url($image_id, 'woocommerce_thumbnail');
1397 - } else {
1398 - // If variation has no image, use the parent product image
1399 - $parent = wc_get_product($product_id);
1400 - $parent_image_id = $parent->get_image_id();
1401 - if ($parent_image_id) {
1402 - $image_url = wp_get_attachment_image_url($parent_image_id, 'woocommerce_thumbnail');
1403 - }
1404 - }
1405 -
1406 - wp_send_json_success(array('image_url' => $image_url));
1407 -}
1408 -add_action('wp_ajax_usk_get_variation_image', 'usk_ajax_variation_image_update');
1409 -add_action('wp_ajax_nopriv_usk_get_variation_image', 'usk_ajax_variation_image_update');
1410 -
1411 1394 // Start: Add to cart quantity buttons conversion
1412 -if (! function_exists('usk_display_quantity_minus')) {
1413 - function usk_display_quantity_minus() {
1395 +if (! function_exists('ultimate_store_kit_display_quantity_minus')) {
1396 + function ultimate_store_kit_display_quantity_minus() {
1414 1397 if (! is_product()) return;
1415 1398 echo '<button type="button" class="bdt-add-to-cart-qty-minus" ><i class="usk-icon-minus3"></i></button>';
1416 1399 }
1417 1400 }
1418 1401
1419 -if (! function_exists('usk_display_quantity_plus')) {
1420 - function usk_display_quantity_plus() {
1402 +if (! function_exists('ultimate_store_kit_display_quantity_plus')) {
1403 + function ultimate_store_kit_display_quantity_plus() {
1421 1404 if (! is_product()) return;
1422 1405 echo '<button type="button" class="bdt-add-to-cart-qty-plus" ><i class="usk-icon-plus3"></i></button>';
1423 1406 }
1424 1407 }
1425 1408
1426 -if (! function_exists('usk_add_cart_quantity_plus_minus')) {
1427 - function usk_add_cart_quantity_plus_minus() {
1409 +if (! function_exists('ultimate_store_kit_add_cart_quantity_plus_minus')) {
1410 + function ultimate_store_kit_add_cart_quantity_plus_minus() {
1428 1411
1429 1412 echo '<style>
1430 1413 input[type="number"]::-webkit-outer-spin-button,
1431 1414 input[type="number"]::-webkit-inner-spin-button {
@@ -1457,20 +1440,24 @@
1457 1440 ");
1458 1441 }
1459 1442 }
1460 1443
1461 -if (! function_exists('usk_setup_quantity_buttons')) {
1462 - function usk_setup_quantity_buttons() {
1444 +if (! function_exists('ultimate_store_kit_setup_quantity_buttons')) {
1445 + function ultimate_store_kit_setup_quantity_buttons() {
1463 1446 if (function_exists('is_product')) {
1464 - // Remove the default version
1465 - remove_all_actions('woocommerce_before_quantity_input_field');
1466 - remove_all_actions('woocommerce_after_quantity_input_field');
1467 - remove_all_actions('woocommerce_before_single_product');
1447 + // Element Pack renders its own quantity buttons on these hooks. Remove
1448 + // only those callbacks: the previous remove_all_actions() also stripped
1449 + // WooCommerce core's own handlers on woocommerce_before_single_product
1450 + // (notably woocommerce_output_all_notices), suppressing store notices.
1451 + remove_action('woocommerce_before_quantity_input_field', 'ep_display_quantity_minus');
1452 + remove_action('woocommerce_after_quantity_input_field', 'ep_display_quantity_plus');
1453 + remove_action('woocommerce_before_single_product', 'ep_add_cart_quantity_plus_minus');
1468 1454
1469 1455 // Add our version
1470 - add_action('woocommerce_before_quantity_input_field', 'usk_display_quantity_minus');
1471 - add_action('woocommerce_after_quantity_input_field', 'usk_display_quantity_plus');
1472 - add_action('woocommerce_after_single_product', 'usk_add_cart_quantity_plus_minus');
1456 + add_action('woocommerce_before_quantity_input_field', 'ultimate_store_kit_display_quantity_minus');
1457 + add_action('woocommerce_after_quantity_input_field', 'ultimate_store_kit_display_quantity_plus');
1458 + add_action('woocommerce_after_single_product', 'ultimate_store_kit_add_cart_quantity_plus_minus');
1473 1459 }
1474 1460 }
1475 1461 }
1462 +
1476 1463 // End: Add to cart quantity buttons conversion