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 +457 -136 1.6.23.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 /**
@@ -8,38 +14,50 @@
8 14 * Don't try for regular license otherwise your license will be invalid.
9 15 * return white label
10 16 */
11 17
12 -if (!defined('BDTUSK')) {
18 +if (! defined('BDTUSK')) {
13 19 define('BDTUSK', '');
14 20 }
15 21
16 22 //Add prefix for all widgets <span class="usk-widget-badge"></span>
17 -if (!defined('BDTUSK_CP')) {
23 +if (! defined('BDTUSK_CP')) {
18 24 define('BDTUSK_CP', '<span class="usk-widget-badge"></span>');
19 25 }
20 26
21 27 //Add prefix for all widgets <span class="usk-widget-badge"></span>
22 -if (!defined('BDTUSK_NC')) {
28 +if (! defined('BDTUSK_NC')) {
23 29 define('BDTUSK_NC', '<span class="usk-new-control"></span>');
24 30 }
25 31
26 32 // if you have any custom style
27 -if (!defined('BDTUSK_SLUG')) {
33 +if (! defined('BDTUSK_SLUG')) {
28 34 define('BDTUSK_SLUG', 'ultimate-store-kit');
29 35 }
30 36
31 37 // set your own alias
32 -if (!defined('BDTUSK_VER')) {
38 +if (! defined('BDTUSK_VER')) {
33 39 define('BDTUSK_VER', 'Ultimate Store Kit');
34 40 }
35 41
36 42 // Set your own name for plugin
37 43
38 -if (!defined('BDTUSK_NAME')) {
44 +if (! defined('BDTUSK_NAME')) {
39 45 define('BDTUSK_NAME', 'Ultimate Store Kit');
40 46 }
41 47
48 +if (ultimate_store_kit_is_pro_activated()) {
49 + if (! defined('BDTUSK_PC')) {
50 + define('BDTUSK_PC', '');
51 + } // pro control badge
52 + define('BDTUSK_IS_PC', '');
53 +} else {
54 + if (! defined('BDTUSK_PC')) {
55 + define('BDTUSK_PC', '<span class="usk-pro-control"></span>');
56 + } // pro control badge
57 + define('BDTUSK_IS_PC', 'usk-disabled-control');
58 +}
59 +
42 60 /**
43 61 * Show any alert by this function
44 62 *
45 63 * @param mixed $message [description]
@@ -94,9 +112,9 @@
94 112 $post_type_args = [
95 113 'show_in_nav_menus' => true,
96 114 ];
97 115
98 - if (!empty($args['post_type'])) {
116 + if (! empty($args['post_type'])) {
99 117 $post_type_args['name'] = $args['post_type'];
100 118 }
101 119
102 120 $_post_types = get_post_types($post_type_args, 'objects');
@@ -142,9 +160,9 @@
142 160
143 161 function ultimate_store_kit_allow_tags($tag = null) {
144 162 $tag_allowed = wp_kses_allowed_html('post');
145 163
146 - $tag_allowed['input'] = [
164 + $tag_allowed['input'] = [
147 165 'class' => [],
148 166 'id' => [],
149 167 'name' => [],
150 168 'value' => [],
@@ -257,34 +275,104 @@
257 275 }
258 276 }
259 277
260 278 /**
261 - * 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
262 285 */
263 -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);
264 288
265 - /** 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 + }
266 293
294 + return add_query_arg($query_arg, $page, $current_url);
295 + }
296 +
297 + return get_pagenum_link($page);
298 +}
299 +
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;
312 + }
313 +
314 + $post_type = $wp_query->get('post_type');
315 +
316 + if (empty($post_type) && isset($wp_query->query_vars['post_type'])) {
317 + $post_type = $wp_query->query_vars['post_type'];
318 + }
319 +
320 + if (is_array($post_type)) {
321 + return in_array('product', $post_type, true);
322 + }
323 +
324 + return 'product' === $post_type;
325 +}
326 +
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 + ]);
340 +
267 341 if ($wp_query->max_num_pages <= 1) {
268 342 return;
269 343 }
270 344
271 - if (is_front_page()) {
272 - $paged = (get_query_var('page')) ? get_query_var('page') : 1;
345 + $query_arg = sanitize_key($args['query_arg']);
346 +
347 + if ('' === $query_arg && ultimate_store_kit_pagination_uses_product_page($wp_query)) {
348 + $query_arg = 'product-page';
349 + }
350 +
351 + $use_custom_arg = ! empty($query_arg);
352 +
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);
356 +
357 + if ($custom_page && $custom_page >= 1) {
358 + $page = absint($custom_page);
359 + }
360 +
361 + $paged = absint($page);
362 + } elseif (is_front_page()) {
363 + $paged = absint(get_query_var('page')) ?: 1;
273 364 } else {
274 - $paged = (get_query_var('paged')) ? get_query_var('paged') : 1;
365 + $paged = absint(get_query_var('paged')) ?: 1;
275 366 }
276 367
277 - $max = intval($wp_query->max_num_pages);
368 + $max = absint($wp_query->max_num_pages);
369 + $links = [];
278 370
279 - /** Add current page to the array */
280 -
281 371 if ($paged >= 1) {
282 372 $links[] = $paged;
283 373 }
284 374
285 - /** Add the pages around the current page to the array */
286 -
287 375 if ($paged >= 3) {
288 376 $links[] = $paged - 1;
289 377 $links[] = $paged - 2;
290 378 }
@@ -293,52 +381,88 @@
293 381 $links[] = $paged + 2;
294 382 $links[] = $paged + 1;
295 383 }
296 384
297 - echo '<ul class="usk-pagination">' . "\n";
385 + $current_url = '';
298 386
299 - /** Previous Post Link */
387 + if ($use_custom_arg) {
388 + $current_url = remove_query_arg(['paged', 'page', $query_arg]);
300 389
301 - if (get_previous_posts_link()) {
302 - printf('<li>%s</li>' . "\n", wp_kses_post(get_previous_posts_link('<span class="usk-icon-arrow-left-5"></span>')));
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 + }
303 396 }
304 397
305 - /** Link to first page, plus ellipses if necessary */
398 + echo '<ul class="usk-pagination" aria-label="' . esc_attr__('Pagination', 'ultimate-store-kit') . '">' . "\n";
306 399
307 - if (!in_array(1, $links)) {
308 - $class = 1 == $paged ? ' class="current"' : '';
400 + if ($paged > 1) {
401 + printf(
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')
409 + );
410 + }
309 411
310 - printf('<li%s><a href="%s" target="_self">%s</a></li>' . "\n", wp_kses_post($class), esc_url(get_pagenum_link(1)), '1');
412 + if (! in_array(1, $links, true)) {
413 + $class = (1 === $paged) ? 'usk-active' : '';
311 414
312 - if (!in_array(2, $links)) {
313 - echo '<li class="usk-pagination-dot-dot"><span>...</span></li>';
415 + printf(
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')
420 + );
421 +
422 + if (! in_array(2, $links, true)) {
423 + echo '<li class="usk-pagination-dot-dot"><span>...</span></li>' . "\n";
314 424 }
315 425 }
316 426
317 - /** Link to current page, plus 2 pages in either direction if necessary */
318 427 sort($links);
319 428
320 - foreach ((array) $links as $link) {
321 - $class = $paged == $link ? ' class="usk-active"' : '';
322 - printf('<li%s><a href="%s" target="_self">%s</a></li>' . "\n", wp_kses_post($class), esc_url(get_pagenum_link($link)), esc_html($link));
429 + foreach ($links as $link) {
430 + $class = ($paged === (int) $link) ? 'usk-active' : '';
431 +
432 + printf(
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)),
436 + esc_html($link)
437 + );
323 438 }
324 439
325 - /** Link to last page, plus ellipses if necessary */
326 -
327 - if (!in_array($max, $links)) {
328 -
329 - if (!in_array($max - 1, $links)) {
440 + if (! in_array($max, $links, true)) {
441 + if (! in_array($max - 1, $links, true)) {
330 442 echo '<li class="usk-pagination-dot-dot"><span>...</span></li>' . "\n";
331 443 }
332 444
333 - $class = $paged == $max ? ' class="usk-active"' : '';
334 - printf('<li%s><a href="%s" target="_self">%s</a></li>' . "\n", wp_kses_post($class), esc_url(get_pagenum_link($max)), esc_html($max));
445 + $class = ($paged === $max) ? 'usk-active' : '';
446 +
447 + printf(
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)),
451 + esc_html($max)
452 + );
335 453 }
336 454
337 - /** Next Post Link */
338 -
339 - if (get_next_posts_link()) {
340 - printf('<li>%s</li>' . "\n", wp_kses_post(get_next_posts_link('<span class="usk-icon-arrow-right-1"></span>')));
455 + if ($paged < $max) {
456 + printf(
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')
464 + );
341 465 }
342 466
343 467 echo '</ul>' . "\n";
344 468 }
@@ -394,21 +518,21 @@
394 518 $currency_format = number_format($currency, $precision);
395 519 $suffix = '';
396 520 } else
397 521
398 - if ($currency < 900000) {
522 + if ($currency < 900000) {
399 523 // 0.9k-850k
400 524 $currency_format = number_format($currency / 1000, $precision);
401 525 $suffix = 'K';
402 526 } else
403 527
404 - if ($currency < 900000000) {
528 + if ($currency < 900000000) {
405 529 // 0.9m-850m
406 530 $currency_format = number_format($currency / 1000000, $precision);
407 531 $suffix = 'M';
408 532 } else
409 533
410 - if ($currency < 900000000000) {
534 + if ($currency < 900000000000) {
411 535 // 0.9b-850b
412 536 $currency_format = number_format($currency / 1000000000, $precision);
413 537 $suffix = 'B';
414 538 } else {
@@ -494,9 +618,9 @@
494 618
495 619 global $wp_registered_sidebars;
496 620 $sidebar_options = [];
497 621
498 - if (!$wp_registered_sidebars) {
622 + if (! $wp_registered_sidebars) {
499 623 $sidebar_options[0] = esc_html__('No sidebars were found', 'ultimate-store-kit');
500 624 } else {
501 625 $sidebar_options[0] = esc_html__('Select Sidebar', 'ultimate-store-kit');
502 626
@@ -527,9 +651,12 @@
527 651 }
528 652
529 653 if (false !== $post_categories and is_array($post_categories)) {
530 654 foreach ($post_categories as $category) {
531 - $post_options[$category->term_id] = $category->name;
655 + // Ensure $category is an object, not an array
656 + if (is_object($category) && isset($category->term_id, $category->name)) {
657 + $post_options[$category->term_id] = $category->name;
658 + }
532 659 }
533 660 }
534 661
535 662 return $post_options;
@@ -540,14 +667,17 @@
540 667 * @return array of parent category
541 668 */
542 669 function ultimate_store_kit_get_only_parent_cats($taxonomy = 'category') {
543 670
544 - $parent_categories = ['none' => __('None', 'bdthemes-element-pack')];
545 - $args = ['parent' => 0];
546 - $parent_cats = get_terms($taxonomy, $args);
671 + $parent_categories = ['none' => __('None', 'ultimate-store-kit')];
672 + $args = ['taxonomy' => $taxonomy, 'parent' => 0];
673 + $parent_cats = get_terms($args);
547 674
548 675 foreach ($parent_cats as $parent_cat) {
549 - $parent_categories[$parent_cat->term_id] = ucfirst($parent_cat->name);
676 + // Ensure $parent_cat is an object, not an array
677 + if (is_object($parent_cat) && isset($parent_cat->term_id, $parent_cat->name)) {
678 + $parent_categories[$parent_cat->term_id] = ucfirst($parent_cat->name);
679 + }
550 680 }
551 681
552 682 return $parent_categories;
553 683 }
@@ -781,9 +911,9 @@
781 911 function ultimate_store_kit_svg_icon($icon) {
782 912
783 913 $icon_path = BDTUSK_ASSETS_PATH . "images/svg/{$icon}.svg";
784 914
785 - if (!file_exists($icon_path)) {
915 + if (! file_exists($icon_path)) {
786 916 return false;
787 917 }
788 918
789 919 ob_start();
@@ -803,13 +933,13 @@
803 933 * @return string
804 934 */
805 935 function ultimate_store_kit_parse_csv($csv, $delimiter = ';', $header = true) {
806 936
807 - if (!is_string($csv)) {
937 + if (! is_string($csv)) {
808 938 return '';
809 939 }
810 940
811 - if (!function_exists('str_getcsv')) {
941 + if (! function_exists('str_getcsv')) {
812 942 return $csv;
813 943 }
814 944
815 945 $html = '';
@@ -845,10 +975,10 @@
845 975
846 976 return '<table>' . $html . '</tbody></table>';
847 977 }
848 978
849 -function ultimate_store_kit_dashboard_link($suffix = '#welcome') {
850 - return add_query_arg(['page' => 'ultimate_store_kit_options' . $suffix], admin_url('admin.php'));
979 +function ultimate_store_kit_dashboard_link($suffix = '') {
980 + return add_query_arg(['page' => 'ultimate-store-kit' . $suffix], admin_url('admin.php'));
851 981 }
852 982
853 983 /**
854 984 * @param int $limit default limit is 25 word
@@ -871,58 +1001,31 @@
871 1001
872 1002 return wpautop($output);
873 1003 }
874 1004
875 -function usk_get_order_options() {
876 - $options = [
877 - 'title' => __('Title', 'ultimate-store-kit'),
878 - 'ID' => __('ID', 'ultimate-store-kit'),
879 - 'date' => __('Date', 'ultimate-store-kit'),
880 - 'rand' => __('Random', 'ultimate-store-kit'),
881 - '_price' => __('Product Price', 'ultimate-store-kit'),
882 - 'total_sales' => __('Top Seller', 'ultimate-store-kit'),
883 - 'comment_count' => __('Most Reviewed', 'ultimate-store-kit'),
884 - '_wc_average_rating' => __('Top Rated', 'ultimate-store-kit'),
885 - ];
886 -
887 - return apply_filters('usk_order_options', $options);
888 -}
889 -
890 1005 //wishlist
891 -
892 1006 function ultimate_store_kit_get_wishlist($user_id = 0) {
893 1007 $_wishlist_key = '_ultimate_store_kit_wishlist';
894 1008 $_wishlist = [];
1009 +
895 1010 if (isset($_COOKIE[$_wishlist_key])) {
896 - $_wishlist = unserialize(stripslashes($_COOKIE[sanitize_text_field($_wishlist_key)]));
897 - }
898 - return apply_filters('ultimate_store_kit_wishlist', array_unique($_wishlist));
899 -}
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]));
1014 + $decoded_data = json_decode($cookie_data, true);
900 1015
901 -function usk_get_taxonomies() {
902 - $taxonomy_list = get_object_taxonomies('product');
903 - $taxonomies = [
904 - 'search' => 'Search',
905 - 'price' => 'Price',
906 - 'orderby' => 'Orderby',
907 - 'order' => 'Order',
908 - ];
909 -
910 - foreach ($taxonomy_list as $_taxonomy) {
911 - $taxonomy = get_taxonomy($_taxonomy);
912 -
913 - if ($taxonomy->show_ui) {
914 - $taxonomies[$_taxonomy] = $taxonomy->label;
1016 + if (json_last_error() === JSON_ERROR_NONE && is_array($decoded_data)) {
1017 + $_wishlist = array_values(array_filter(array_map('absint', array_filter($decoded_data, 'is_scalar'))));
915 1018 }
916 1019 }
917 1020
918 - return $taxonomies;
1021 + return apply_filters('ultimate_store_kit_wishlist', array_unique($_wishlist));
919 1022 }
920 1023
921 1024 function ultimate_store_kit_hide_on_class($selectors) {
922 1025 $element_hide_on = '';
923 1026
924 - if (!empty($selectors)) {
1027 + if (! empty($selectors)) {
925 1028
926 1029 foreach ($selectors as $element) {
927 1030
928 1031 if ($element == 'desktop') {
@@ -942,9 +1045,17 @@
942 1045 return $element_hide_on;
943 1046 }
944 1047
945 1048 function ultimate_store_kit_wc_product_quick_view_content($product_id) {
946 - 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 +
947 1058 global $woocommerce;
948 1059 global $post;
949 1060
950 1061 if (intval($product_id)) {
@@ -956,19 +1067,10 @@
956 1067 ob_start();
957 1068 ?>
958 1069 <div class="usk-modal-page">
959 1070 <?php
960 -
961 - while (have_posts()) : the_post(); ?>
962 -
963 - <script>
964 - var url = '<?php echo esc_url(plugins_url('assets/js/prettyPhoto/jquery.prettyPhoto.init.js', WC_PLUGIN_FILE)); ?>';
965 - jQuery.getScript(url);
966 - var wc_add_to_cart_variation_params = {
967 - "ajax_url": "\/wp-admin\/admin-ajax.php"
968 - };
969 - jQuery.getScript("<?php echo esc_url($woocommerce->plugin_url()); ?> '/assets/js/frontend/add-to-cart-variation.min.js'");
970 - </script>
1071 + while (have_posts()) :
1072 + the_post(); ?>
971 1073 <div class="usk-modal-product">
972 1074 <div id="product-<?php the_ID(); ?>" <?php post_class('product'); ?>>
973 1075 <div class="usk-modal-image-wrapper">
974 1076 <?php do_action('ultimate_store_kit_quick_florence_grid_view_product_images'); ?>
@@ -1003,9 +1105,59 @@
1003 1105 </div>
1004 1106 <?php endwhile; ?>
1005 1107 </div>
1006 1108
1007 - <?php echo ob_get_clean(); exit();
1109 + <?php
1110 + $content = ob_get_clean();
1111 + echo wp_kses_post($content);
1112 + ?>
1113 + <script type="text/javascript">
1114 + jQuery(document).ready(function($) {
1115 + // Load required WooCommerce scripts
1116 + var scripts = [
1117 + '<?php echo esc_url(plugins_url('assets/js/prettyPhoto/jquery.prettyPhoto.init.js', WC_PLUGIN_FILE)); ?>',
1118 + '<?php echo esc_url(plugins_url('assets/js/frontend/add-to-cart-variation.min.js', WC_PLUGIN_FILE)); ?>',
1119 + '<?php echo esc_url(plugins_url('assets/js/frontend/add-to-cart.min.js', WC_PLUGIN_FILE)); ?>',
1120 + '<?php echo esc_url(plugins_url('assets/js/frontend/single-product.min.js', WC_PLUGIN_FILE)); ?>'
1121 + ];
1122 +
1123 + // Load scripts sequentially
1124 + function loadScript(index) {
1125 + if (index >= scripts.length) {
1126 + initializeWooCommerce();
1127 + return;
1128 + }
1129 +
1130 + $.getScript(scripts[index], function() {
1131 + loadScript(index + 1);
1132 + });
1133 + }
1134 +
1135 + function initializeWooCommerce() {
1136 + // Initialize WooCommerce add to cart functionality
1137 + var wc_add_to_cart_variation_params = {
1138 + "ajax_url": "<?php echo esc_url(admin_url('admin-ajax.php')); ?>",
1139 + "i18n_view_cart": "<?php echo esc_js(__('View cart', 'ultimate-store-kit')); ?>",
1140 + "cart_url": "<?php echo esc_url(wc_get_cart_url()); ?>",
1141 + "is_cart": "<?php echo is_cart() ? '1' : '0'; ?>",
1142 + "cart_redirect_after_add": "<?php echo get_option('woocommerce_cart_redirect_after_add') ? '1' : '0'; ?>"
1143 + };
1144 +
1145 + // Initialize variation forms
1146 + $(document.body).trigger('wc_variation_form');
1147 +
1148 + // Initialize add to cart buttons
1149 + $(document.body).trigger('wc_fragments_refreshed');
1150 + $(document.body).trigger('wc_fragments_loaded');
1151 + $(document.body).trigger('added_to_cart');
1152 + }
1153 +
1154 + // Start loading scripts
1155 + // loadScript(0);
1156 + });
1157 + </script>
1158 + <?php
1159 + exit();
1008 1160 }
1009 1161 }
1010 1162
1011 1163 function ultimate_store_kit_quick_view_product_images() {
@@ -1047,9 +1199,9 @@
1047 1199 }
1048 1200
1049 1201 $image_link = wp_get_attachment_url($attachment_id);
1050 1202
1051 - if (!$image_link) {
1203 + if (! $image_link) {
1052 1204 continue;
1053 1205 }
1054 1206
1055 1207 $image_title = esc_attr(get_the_title($attachment_id));
@@ -1073,18 +1225,18 @@
1073 1225
1074 1226 /**
1075 1227 * License Validation
1076 1228 */
1077 -if (!function_exists('usk_license_validation')) {
1078 - function usk_license_validation() {
1229 +if (! function_exists('ultimate_store_kit_license_validation')) {
1230 + function ultimate_store_kit_license_validation() {
1079 1231
1080 - 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()) {
1081 1233 return false;
1082 1234 }
1083 1235
1084 - $license_key = trim(get_option('ultimate_post_kit_license_key'));
1236 + $license_key = trim(get_option('ultimate_store_kit_license_key'));
1085 1237
1086 - if (isset($license_key) && !empty($license_key)) {
1238 + if (isset($license_key) && ! empty($license_key)) {
1087 1239 return true;
1088 1240 } else {
1089 1241 return false;
1090 1242 }
@@ -1093,29 +1245,83 @@
1093 1245 }
1094 1246
1095 1247
1096 1248
1097 -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) {
1098 1303 $_compare_products_key = '_ultimate_store_kit_compare_products';
1099 1304 $_compare_products = [];
1100 1305 if ($user_id != 0) {
1101 1306 $_compare_products = get_user_meta($user_id, $_compare_products_key, true) ?: [];
1102 1307 } elseif (isset($_COOKIE[$_compare_products_key])) {
1103 - $_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);
1312 +
1313 + // Check if JSON decoding failed
1314 + if (! is_array($_compare_products)) {
1315 + $_compare_products = [];
1316 + } else {
1317 + $_compare_products = array_values(array_filter(array_map('absint', array_filter($_compare_products, 'is_scalar'))));
1318 + }
1104 1319 }
1105 1320
1106 1321 return apply_filters('ultimate_store_kit_compare_products', array_unique($_compare_products));
1107 1322 }
1108 1323
1109 -function usk_get_compare_products_count() {
1110 - $count = 0;
1111 - $user_id = get_current_user_id();
1112 - $products = usk_get_compare_products($user_id);
1113 - if (is_array($products)) {
1114 - $count = count($products);
1115 - }
1116 - return $count;
1117 -}
1118 1324
1119 1325 //if (!function_exists('ultimate_store_kit_get_compare_product_slug')) {
1120 1326 // function ultimate_store_kit_compare_product_slug() {
1121 1327 // return 'compare-products';
@@ -1121,22 +1327,137 @@
1121 1327 // return 'compare-products';
1122 1328 // }
1123 1329 //}
1124 1330
1125 -if (!function_exists('ultimate_store_kit_compare_product_page')) {
1126 - function ultimate_store_kit_compare_product_page() {
1127 - if($postId = intval(get_option( 'bdt_usk_compare_products_page_id' ))){
1128 - $post = get_post($postId);
1129 - if($post->post_status == 'publish'){
1130 - return $post->ID;
1131 - }
1132 - }
1133 - }
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 + }
1134 1357 }
1135 1358
1136 -if (!function_exists('ultimate_store_kit_is_compare_product_page')) {
1359 +if (! function_exists('ultimate_store_kit_compare_product_page')) {
1360 + function ultimate_store_kit_compare_product_page() {
1361 + if ($postId = ultimate_store_kit_get_compare_page_option()) {
1362 + $post = get_post($postId);
1363 + if ($post && $post->post_status == 'publish') {
1364 + return $post->ID;
1365 + }
1366 + }
1367 + }
1368 +}
1369 +
1370 +if (! function_exists('ultimate_store_kit_is_compare_product_page')) {
1137 1371 function ultimate_store_kit_is_compare_product_page() {
1138 - if ($page = ultimate_store_kit_compare_product_page() ) {
1139 - return is_page( $page->ID );
1372 + if ($page = ultimate_store_kit_compare_product_page()) {
1373 + // return is_page( $page->ID );
1374 + return is_page($page);
1140 1375 }
1141 1376 }
1142 1377 }
1378 +
1379 +
1380 +/**
1381 + * Helper function to load variation swatches scripts and styles
1382 + */
1383 +function ultimate_store_kit_load_variation_swatches_assets() {
1384 + // Always load the grid variations script for variation support
1385 + wp_register_script('usk-grid-variations', BDTUSK_ASSETS_URL . 'js/modules/grid-variations.js', ['jquery'], BDTUSK_VER, true);
1386 + wp_localize_script('usk-grid-variations', 'usk_vars', array(
1387 + 'ajax_url' => admin_url('admin-ajax.php'),
1388 + 'nonce' => wp_create_nonce('usk_variations')
1389 + ));
1390 +}
1391 +add_action('wp_enqueue_scripts', 'ultimate_store_kit_load_variation_swatches_assets', 20);
1392 +
1393 +
1394 +// Start: Add to cart quantity buttons conversion
1395 +if (! function_exists('ultimate_store_kit_display_quantity_minus')) {
1396 + function ultimate_store_kit_display_quantity_minus() {
1397 + if (! is_product()) return;
1398 + echo '<button type="button" class="bdt-add-to-cart-qty-minus" ><i class="usk-icon-minus3"></i></button>';
1399 + }
1400 +}
1401 +
1402 +if (! function_exists('ultimate_store_kit_display_quantity_plus')) {
1403 + function ultimate_store_kit_display_quantity_plus() {
1404 + if (! is_product()) return;
1405 + echo '<button type="button" class="bdt-add-to-cart-qty-plus" ><i class="usk-icon-plus3"></i></button>';
1406 + }
1407 +}
1408 +
1409 +if (! function_exists('ultimate_store_kit_add_cart_quantity_plus_minus')) {
1410 + function ultimate_store_kit_add_cart_quantity_plus_minus() {
1411 +
1412 + echo '<style>
1413 + input[type="number"]::-webkit-outer-spin-button,
1414 + input[type="number"]::-webkit-inner-spin-button {
1415 + -webkit-appearance: none;
1416 + margin: 0;
1417 + }
1418 +
1419 + input[type="number"] {
1420 + -moz-appearance: textfield; /* Firefox */
1421 + }
1422 + </style>';
1423 +
1424 + wc_enqueue_js("
1425 + $(document).off('click.bdtQtyHandler'); // Remove previous handler
1426 + $(document).on('click.bdtQtyHandler', 'button.bdt-add-to-cart-qty-plus, button.bdt-add-to-cart-qty-minus', function(e) {
1427 + e.preventDefault();
1428 + var qty = $(this).closest('form.cart').find('.qty');
1429 + var val = parseFloat(qty.val()) || 0;
1430 + var max = parseFloat(qty.attr('max')) || Infinity;
1431 + var min = parseFloat(qty.attr('min')) || 0;
1432 + var step = parseFloat(qty.attr( 'step' ));
1433 +
1434 + if ($(this).is('.bdt-add-to-cart-qty-plus')) {
1435 + qty.val(Math.min(val + step, max)).trigger('change');
1436 + } else {
1437 + qty.val(Math.max(val - step, min)).trigger('change');
1438 + }
1439 + });
1440 + ");
1441 + }
1442 +}
1443 +
1444 +if (! function_exists('ultimate_store_kit_setup_quantity_buttons')) {
1445 + function ultimate_store_kit_setup_quantity_buttons() {
1446 + if (function_exists('is_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');
1454 +
1455 + // Add our version
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');
1459 + }
1460 + }
1461 +}
1462 +
1463 +// End: Add to cart quantity buttons conversion