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 +97 -156 3.0.73.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', '');
@@ -403,13 +409,13 @@
403 409 );
404 410 }
405 411
406 412 if (! in_array(1, $links, true)) {
407 - $class = (1 === $paged) ? ' class="usk-active"' : '';
413 + $class = (1 === $paged) ? 'usk-active' : '';
408 414
409 415 printf(
410 - '<li%s><a href="%s" target="_self">%s</a></li>' . "\n",
411 - $class,
416 + '<li class="%s"><a href="%s" target="_self">%s</a></li>' . "\n",
417 + esc_attr($class),
412 418 esc_url(ultimate_store_kit_get_pagination_link(1, $query_arg, $current_url)),
413 419 esc_html('1')
414 420 );
415 421
@@ -420,13 +426,13 @@
420 426
421 427 sort($links);
422 428
423 429 foreach ($links as $link) {
424 - $class = ($paged === (int) $link) ? ' class="usk-active"' : '';
430 + $class = ($paged === (int) $link) ? 'usk-active' : '';
425 431
426 432 printf(
427 - '<li%s><a href="%s" target="_self">%s</a></li>' . "\n",
428 - $class,
433 + '<li class="%s"><a href="%s" target="_self">%s</a></li>' . "\n",
434 + esc_attr($class),
429 435 esc_url(ultimate_store_kit_get_pagination_link($link, $query_arg, $current_url)),
430 436 esc_html($link)
431 437 );
432 438 }
@@ -435,13 +441,13 @@
435 441 if (! in_array($max - 1, $links, true)) {
436 442 echo '<li class="usk-pagination-dot-dot"><span>...</span></li>' . "\n";
437 443 }
438 444
439 - $class = ($paged === $max) ? ' class="usk-active"' : '';
445 + $class = ($paged === $max) ? 'usk-active' : '';
440 446
441 447 printf(
442 - '<li%s><a href="%s" target="_self">%s</a></li>' . "\n",
443 - $class,
448 + '<li class="%s"><a href="%s" target="_self">%s</a></li>' . "\n",
449 + esc_attr($class),
444 450 esc_url(ultimate_store_kit_get_pagination_link($max, $query_arg, $current_url)),
445 451 esc_html($max)
446 452 );
447 453 }
@@ -662,10 +668,10 @@
662 668 */
663 669 function ultimate_store_kit_get_only_parent_cats($taxonomy = 'category') {
664 670
665 671 $parent_categories = ['none' => __('None', 'ultimate-store-kit')];
666 - $args = ['parent' => 0];
667 - $parent_cats = get_terms($taxonomy, $args);
672 + $args = ['taxonomy' => $taxonomy, 'parent' => 0];
673 + $parent_cats = get_terms($args);
668 674
669 675 foreach ($parent_cats as $parent_cat) {
670 676 // Ensure $parent_cat is an object, not an array
671 677 if (is_object($parent_cat) && isset($parent_cat->term_id, $parent_cat->name)) {
@@ -995,23 +1001,8 @@
995 1001
996 1002 return wpautop($output);
997 1003 }
998 1004
999 -function usk_get_order_options() {
1000 - $options = [
1001 - 'title' => __('Title', 'ultimate-store-kit'),
1002 - 'ID' => __('ID', 'ultimate-store-kit'),
1003 - 'date' => __('Date', 'ultimate-store-kit'),
1004 - 'rand' => __('Random', 'ultimate-store-kit'),
1005 - '_price' => __('Product Price', 'ultimate-store-kit'),
1006 - 'total_sales' => __('Top Seller', 'ultimate-store-kit'),
1007 - 'comment_count' => __('Most Reviewed', 'ultimate-store-kit'),
1008 - '_wc_average_rating' => __('Top Rated', 'ultimate-store-kit'),
1009 - ];
1010 -
1011 - return apply_filters('usk_order_options', $options);
1012 -}
1013 -
1014 1005 //wishlist
1015 1006 function ultimate_store_kit_get_wishlist($user_id = 0) {
1016 1007 $_wishlist_key = '_ultimate_store_kit_wishlist';
1017 1008 $_wishlist = [];
@@ -1016,13 +1007,15 @@
1016 1007 $_wishlist_key = '_ultimate_store_kit_wishlist';
1017 1008 $_wishlist = [];
1018 1009
1019 1010 if (isset($_COOKIE[$_wishlist_key])) {
1020 - $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]));
1021 1014 $decoded_data = json_decode($cookie_data, true);
1022 1015
1023 1016 if (json_last_error() === JSON_ERROR_NONE && is_array($decoded_data)) {
1024 - $_wishlist = $decoded_data;
1017 + $_wishlist = array_values(array_filter(array_map('absint', array_filter($decoded_data, 'is_scalar'))));
1025 1018 }
1026 1019 }
1027 1020
1028 1021 return apply_filters('ultimate_store_kit_wishlist', array_unique($_wishlist));
@@ -1027,28 +1020,8 @@
1027 1020
1028 1021 return apply_filters('ultimate_store_kit_wishlist', array_unique($_wishlist));
1029 1022 }
1030 1023
1031 -function usk_get_taxonomies() {
1032 - $taxonomy_list = get_object_taxonomies('product');
1033 - $taxonomies = [
1034 - 'search' => 'Search',
1035 - 'price' => 'Price',
1036 - 'orderby' => 'Orderby',
1037 - 'order' => 'Order',
1038 - ];
1039 -
1040 - foreach ($taxonomy_list as $_taxonomy) {
1041 - $taxonomy = get_taxonomy($_taxonomy);
1042 -
1043 - if ($taxonomy->show_ui) {
1044 - $taxonomies[$_taxonomy] = $taxonomy->label;
1045 - }
1046 - }
1047 -
1048 - return $taxonomies;
1049 -}
1050 -
1051 1024 function ultimate_store_kit_hide_on_class($selectors) {
1052 1025 $element_hide_on = '';
1053 1026
1054 1027 if (! empty($selectors)) {
@@ -1072,9 +1045,17 @@
1072 1045 return $element_hide_on;
1073 1046 }
1074 1047
1075 1048 function ultimate_store_kit_wc_product_quick_view_content($product_id) {
1076 - 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 +
1077 1058 global $woocommerce;
1078 1059 global $post;
1079 1060
1080 1061 if (intval($product_id)) {
@@ -1153,9 +1134,9 @@
1153 1134
1154 1135 function initializeWooCommerce() {
1155 1136 // Initialize WooCommerce add to cart functionality
1156 1137 var wc_add_to_cart_variation_params = {
1157 - "ajax_url": "<?php echo admin_url('admin-ajax.php'); ?>",
1138 + "ajax_url": "<?php echo esc_url(admin_url('admin-ajax.php')); ?>",
1158 1139 "i18n_view_cart": "<?php echo esc_js(__('View cart', 'ultimate-store-kit')); ?>",
1159 1140 "cart_url": "<?php echo esc_url(wc_get_cart_url()); ?>",
1160 1141 "is_cart": "<?php echo is_cart() ? '1' : '0'; ?>",
1161 1142 "cart_redirect_after_add": "<?php echo get_option('woocommerce_cart_redirect_after_add') ? '1' : '0'; ?>"
@@ -1244,12 +1225,12 @@
1244 1225
1245 1226 /**
1246 1227 * License Validation
1247 1228 */
1248 -if (! function_exists('usk_license_validation')) {
1249 - function usk_license_validation() {
1229 +if (! function_exists('ultimate_store_kit_license_validation')) {
1230 + function ultimate_store_kit_license_validation() {
1250 1231
1251 - 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()) {
1252 1233 return false;
1253 1234 }
1254 1235
1255 1236 $license_key = trim(get_option('ultimate_store_kit_license_key'));
@@ -1264,9 +1245,10 @@
1264 1245 }
1265 1246
1266 1247
1267 1248
1268 -if (! function_exists('usk_is_public_product')) {
1249 +
1250 +if (! function_exists('ultimate_store_kit_is_public_product')) {
1269 1251 /**
1270 1252 * Whether a product id may be read or acted on by the current request.
1271 1253 *
1272 1254 * wc_get_product() resolves an id regardless of post status, so any endpoint
@@ -1275,9 +1257,9 @@
1275 1257 *
1276 1258 * @param mixed $product_id Raw product id, typically straight off $_POST.
1277 1259 * @return bool
1278 1260 */
1279 - function usk_is_public_product($product_id) {
1261 + function ultimate_store_kit_is_public_product($product_id) {
1280 1262 $product_id = absint($product_id);
1281 1263
1282 1264 // Fail closed when WooCommerce is absent — helper.php also loads on EDD-only sites.
1283 1265 if (! $product_id || ! function_exists('wc_get_product')) {
@@ -1310,28 +1292,30 @@
1310 1292 * Both lists are written from unauthenticated endpoints, so they need an upper
1311 1293 * bound — a cookie that outgrows ~4KB is silently dropped by the browser, and an
1312 1294 * unbounded list against a logged-in user means unbounded user meta.
1313 1295 */
1314 -if (! function_exists('usk_get_list_item_limit')) {
1315 - function usk_get_list_item_limit() {
1296 +if (! function_exists('ultimate_store_kit_get_list_item_limit')) {
1297 + function ultimate_store_kit_get_list_item_limit() {
1316 1298 return (int) apply_filters('ultimate_store_kit_list_item_limit', 50);
1317 1299 }
1318 1300 }
1319 1301
1320 -function usk_get_compare_products($user_id = 0) {
1302 +function ultimate_store_kit_get_compare_products($user_id = 0) {
1321 1303 $_compare_products_key = '_ultimate_store_kit_compare_products';
1322 1304 $_compare_products = [];
1323 1305 if ($user_id != 0) {
1324 1306 $_compare_products = get_user_meta($user_id, $_compare_products_key, true) ?: [];
1325 1307 } elseif (isset($_COOKIE[$_compare_products_key])) {
1326 - //$_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);
1327 1312
1328 - $cookie_value = sanitize_text_field($_COOKIE[$_compare_products_key]);
1329 - $_compare_products = json_decode(stripslashes($cookie_value), true);
1330 -
1331 1313 // Check if JSON decoding failed
1332 1314 if (! is_array($_compare_products)) {
1333 1315 $_compare_products = [];
1316 + } else {
1317 + $_compare_products = array_values(array_filter(array_map('absint', array_filter($_compare_products, 'is_scalar'))));
1334 1318 }
1335 1319 }
1336 1320
1337 1321 return apply_filters('ultimate_store_kit_compare_products', array_unique($_compare_products));
@@ -1336,17 +1320,8 @@
1336 1320
1337 1321 return apply_filters('ultimate_store_kit_compare_products', array_unique($_compare_products));
1338 1322 }
1339 1323
1340 -function usk_get_compare_products_count() {
1341 - $count = 0;
1342 - $user_id = get_current_user_id();
1343 - $products = usk_get_compare_products($user_id);
1344 - if (is_array($products)) {
1345 - $count = count($products);
1346 - }
1347 - return $count;
1348 -}
1349 1324
1350 1325 //if (!function_exists('ultimate_store_kit_get_compare_product_slug')) {
1351 1326 // function ultimate_store_kit_compare_product_slug() {
1352 1327 // return 'compare-products';
@@ -1352,11 +1327,39 @@
1352 1327 // return 'compare-products';
1353 1328 // }
1354 1329 //}
1355 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 +
1356 1359 if (! function_exists('ultimate_store_kit_compare_product_page')) {
1357 1360 function ultimate_store_kit_compare_product_page() {
1358 - if ($postId = intval(get_option('bdt_usk_compare_products_page_id'))) {
1361 + if ($postId = ultimate_store_kit_get_compare_page_option()) {
1359 1362 $post = get_post($postId);
1360 1363 if ($post && $post->post_status == 'publish') {
1361 1364 return $post->ID;
1362 1365 }
@@ -1372,21 +1375,13 @@
1372 1375 }
1373 1376 }
1374 1377 }
1375 1378
1376 -/**
1377 - * Helper function to check if variation swatches Pro is active
1378 - *
1379 - * @return bool
1380 - */
1381 -function usk_has_variation_swatches_support() {
1382 - return class_exists('UltimateStoreKitPro\\VariationSwatches\\Swatches');
1383 -}
1384 1379
1385 1380 /**
1386 1381 * Helper function to load variation swatches scripts and styles
1387 1382 */
1388 -function usk_load_variation_swatches_assets() {
1383 +function ultimate_store_kit_load_variation_swatches_assets() {
1389 1384 // Always load the grid variations script for variation support
1390 1385 wp_register_script('usk-grid-variations', BDTUSK_ASSETS_URL . 'js/modules/grid-variations.js', ['jquery'], BDTUSK_VER, true);
1391 1386 wp_localize_script('usk-grid-variations', 'usk_vars', array(
1392 1387 'ajax_url' => admin_url('admin-ajax.php'),
@@ -1392,86 +1387,28 @@
1392 1387 'ajax_url' => admin_url('admin-ajax.php'),
1393 1388 'nonce' => wp_create_nonce('usk_variations')
1394 1389 ));
1395 1390 }
1396 -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);
1397 1392
1398 -// Hook into AJAX variation selection to update product image
1399 -function usk_ajax_variation_image_update() {
1400 - if (!isset($_POST['variation_id']) || !isset($_POST['product_id'])) {
1401 - wp_send_json_error('Missing required parameters');
1402 - return;
1403 - }
1404 1393
1405 - $variation_id = absint($_POST['variation_id']);
1406 - $product_id = absint($_POST['product_id']);
1407 -
1408 - $variation = wc_get_product($variation_id);
1409 - if (!$variation) {
1410 - wp_send_json_error('Invalid variation');
1411 - return;
1412 - }
1413 -
1414 - // wc_get_product() ignores post status, so guard the parent product the same way
1415 - // the variations endpoint does before exposing anything about it.
1416 - $parent = wc_get_product($product_id);
1417 - if (!$parent) {
1418 - wp_send_json_error('Invalid product');
1419 - return;
1420 - }
1421 -
1422 - if ('publish' !== $parent->get_status() && !current_user_can('read_post', $product_id)) {
1423 - wp_send_json_error('Product not available', 404);
1424 - return;
1425 - }
1426 -
1427 - if (post_password_required($product_id)) {
1428 - wp_send_json_error('Product not available', 403);
1429 - return;
1430 - }
1431 -
1432 - // The variation must actually belong to the product that passed the check above,
1433 - // otherwise the parent id is just a public decoy for an arbitrary variation.
1434 - if ($variation->get_parent_id() !== $product_id) {
1435 - wp_send_json_error('Invalid variation');
1436 - return;
1437 - }
1438 -
1439 - $image_id = $variation->get_image_id();
1440 - $image_url = '';
1441 -
1442 - if ($image_id) {
1443 - $image_url = wp_get_attachment_image_url($image_id, 'woocommerce_thumbnail');
1444 - } else {
1445 - // If variation has no image, use the parent product image
1446 - $parent_image_id = $parent->get_image_id();
1447 - if ($parent_image_id) {
1448 - $image_url = wp_get_attachment_image_url($parent_image_id, 'woocommerce_thumbnail');
1449 - }
1450 - }
1451 -
1452 - wp_send_json_success(array('image_url' => $image_url));
1453 -}
1454 -add_action('wp_ajax_usk_get_variation_image', 'usk_ajax_variation_image_update');
1455 -add_action('wp_ajax_nopriv_usk_get_variation_image', 'usk_ajax_variation_image_update');
1456 -
1457 1394 // Start: Add to cart quantity buttons conversion
1458 -if (! function_exists('usk_display_quantity_minus')) {
1459 - function usk_display_quantity_minus() {
1395 +if (! function_exists('ultimate_store_kit_display_quantity_minus')) {
1396 + function ultimate_store_kit_display_quantity_minus() {
1460 1397 if (! is_product()) return;
1461 1398 echo '<button type="button" class="bdt-add-to-cart-qty-minus" ><i class="usk-icon-minus3"></i></button>';
1462 1399 }
1463 1400 }
1464 1401
1465 -if (! function_exists('usk_display_quantity_plus')) {
1466 - function usk_display_quantity_plus() {
1402 +if (! function_exists('ultimate_store_kit_display_quantity_plus')) {
1403 + function ultimate_store_kit_display_quantity_plus() {
1467 1404 if (! is_product()) return;
1468 1405 echo '<button type="button" class="bdt-add-to-cart-qty-plus" ><i class="usk-icon-plus3"></i></button>';
1469 1406 }
1470 1407 }
1471 1408
1472 -if (! function_exists('usk_add_cart_quantity_plus_minus')) {
1473 - 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() {
1474 1411
1475 1412 echo '<style>
1476 1413 input[type="number"]::-webkit-outer-spin-button,
1477 1414 input[type="number"]::-webkit-inner-spin-button {
@@ -1503,20 +1440,24 @@
1503 1440 ");
1504 1441 }
1505 1442 }
1506 1443
1507 -if (! function_exists('usk_setup_quantity_buttons')) {
1508 - function usk_setup_quantity_buttons() {
1444 +if (! function_exists('ultimate_store_kit_setup_quantity_buttons')) {
1445 + function ultimate_store_kit_setup_quantity_buttons() {
1509 1446 if (function_exists('is_product')) {
1510 - // Remove the default version
1511 - remove_all_actions('woocommerce_before_quantity_input_field');
1512 - remove_all_actions('woocommerce_after_quantity_input_field');
1513 - 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');
1514 1454
1515 1455 // Add our version
1516 - add_action('woocommerce_before_quantity_input_field', 'usk_display_quantity_minus');
1517 - add_action('woocommerce_after_quantity_input_field', 'usk_display_quantity_plus');
1518 - 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');
1519 1459 }
1520 1460 }
1521 1461 }
1462 +
1522 1463 // End: Add to cart quantity buttons conversion