PluginProbe
Ultimate Store Kit / 3.1.0
Ultimate Store Kit v3.1.0
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 2.0.6 All 92 releases
ultimate-store-kit / includes / helper.php

helper.php in Ultimate Store Kit 3.1.0, at includes/helper.php

1,546 lines 43.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 if (! defined('ABSPATH')) {
4 exit; // Exit if accessed directly
5 }
6
7 // 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.
8
9 use UltimateStoreKit\Ultimate_Store_Kit_Loader;
10 use Elementor\Plugin;
11
12 /**
13 * You can easily add white label branding for for extended license or multi site license.
14 * Don't try for regular license otherwise your license will be invalid.
15 * return white label
16 */
17
18 if (! defined('BDTUSK')) {
19 define('BDTUSK', '');
20 }
21
22 //Add prefix for all widgets <span class="usk-widget-badge"></span>
23 if (! defined('BDTUSK_CP')) {
24 define('BDTUSK_CP', '<span class="usk-widget-badge"></span>');
25 }
26
27 //Add prefix for all widgets <span class="usk-widget-badge"></span>
28 if (! defined('BDTUSK_NC')) {
29 define('BDTUSK_NC', '<span class="usk-new-control"></span>');
30 }
31
32 // if you have any custom style
33 if (! defined('BDTUSK_SLUG')) {
34 define('BDTUSK_SLUG', 'ultimate-store-kit');
35 }
36
37 // set your own alias
38 if (! defined('BDTUSK_VER')) {
39 define('BDTUSK_VER', 'Ultimate Store Kit');
40 }
41
42 // Set your own name for plugin
43
44 if (! defined('BDTUSK_NAME')) {
45 define('BDTUSK_NAME', 'Ultimate Store Kit');
46 }
47
48 if (_is_usk_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
60 /**
61 * Show any alert by this function
62 *
63 * @param mixed $message [description]
64 * @param class prefix $type [description]
65 * @param boolean $close [description]
66 *
67 * @return helper [description]
68 */
69
70
71 function ultimate_store_kit_is_edit() {
72 return Plugin::$instance->editor->is_edit_mode();
73 }
74
75 function ultimate_store_kit_is_preview() {
76 return Plugin::$instance->preview->is_preview_mode();
77 }
78
79 function ultimate_store_kit_alert($message, $type = 'warning', $close = true) {
80 ?>
81 <div class="usk-alert-<?php echo esc_attr('$type'); ?>" usk-alert>
82 <?php if ($close) : ?>
83 <a class="usk-alert-close" usk-close></a>
84 <?php endif; ?>
85 <?php echo wp_kses_post($message); ?>
86 </div>
87 <?php
88 }
89
90 function ultimate_store_kit_get_alert($message, $type = 'warning', $close = true) {
91
92 $output = '<div class="usk-alert-' . $type . '" usk-alert>';
93 if ($close) :
94 $output .= '<a class="usk-alert-close" usk-close></a>';
95 endif;
96 $output .= wp_kses_post($message);
97 $output .= '</div>';
98
99 return $output;
100 }
101
102 /**
103 * all array css classes will output as proper space
104 *
105 * @param array $classes shortcode css class as array
106 *
107 * @return proper string
108 */
109
110 function ultimate_store_kit_get_post_types($args = []) {
111
112 $post_type_args = [
113 'show_in_nav_menus' => true,
114 ];
115
116 if (! empty($args['post_type'])) {
117 $post_type_args['name'] = $args['post_type'];
118 }
119
120 $_post_types = get_post_types($post_type_args, 'objects');
121
122 $post_types = ['0' => esc_html__('Select Type', 'ultimate-store-kit')];
123
124 foreach ($_post_types as $post_type => $object) {
125 $post_types[$post_type] = $object->label;
126 }
127
128 return $post_types;
129 }
130
131 /**
132 * Add REST API support to an already registered post type.
133 */
134
135 // function bdt_custom_post_type_rest_support() {
136
137 // global $wp_post_types;
138
139 // $post_types = ultimate_store_kit_get_post_types();
140
141 // foreach( $post_types as $post_type ) {
142
143 // $post_type_name = $post_type;
144
145 // if( isset( $wp_post_types[ $post_type_name ] ) ) {
146
147 // $wp_post_types[$post_type_name]->show_in_rest = true;
148
149 // $wp_post_types[$post_type_name]->rest_base = $post_type_name;
150
151 // $wp_post_types[$post_type_name]->rest_controller_class = 'WP_REST_Posts_Controller';
152
153 // }
154
155 // }
156
157 // }
158
159 // add_action( 'init', 'bdt_custom_post_type_rest_support', 25 );
160
161 function ultimate_store_kit_allow_tags($tag = null) {
162 $tag_allowed = wp_kses_allowed_html('post');
163
164 $tag_allowed['input'] = [
165 'class' => [],
166 'id' => [],
167 'name' => [],
168 'value' => [],
169 'checked' => [],
170 'type' => [],
171 ];
172 $tag_allowed['select'] = [
173 'class' => [],
174 'id' => [],
175 'name' => [],
176 'value' => [],
177 'multiple' => [],
178 'type' => [],
179 ];
180 $tag_allowed['option'] = [
181 'value' => [],
182 'selected' => [],
183 ];
184
185 $tag_allowed['title'] = [
186 'a' => [
187 'href' => [],
188 'title' => [],
189 'class' => [],
190 ],
191 'br' => [],
192 'em' => [],
193 'strong' => [],
194 'hr' => [],
195 ];
196
197 $tag_allowed['text'] = [
198 'a' => [
199 'href' => [],
200 'title' => [],
201 'class' => [],
202 ],
203 'br' => [],
204 'em' => [],
205 'strong' => [],
206 'hr' => [],
207 'i' => [
208 'class' => [],
209 ],
210 'span' => [
211 'class' => [],
212 ],
213 ];
214
215 $tag_allowed['svg'] = [
216 'svg' => [
217 'version' => [],
218 'xmlns' => [],
219 'viewbox' => [],
220 'xml:space' => [],
221 'xmlns:xlink' => [],
222 'x' => [],
223 'y' => [],
224 'style' => [],
225 ],
226 'g' => [],
227 'path' => [
228 'class' => [],
229 'd' => [],
230 ],
231 'ellipse' => [
232 'class' => [],
233 'cx' => [],
234 'cy' => [],
235 'rx' => [],
236 'ry' => [],
237 ],
238 'circle' => [
239 'class' => [],
240 'cx' => [],
241 'cy' => [],
242 'r' => [],
243 ],
244 'rect' => [
245 'x' => [],
246 'y' => [],
247 'transform' => [],
248 'height' => [],
249 'width' => [],
250 'class' => [],
251 ],
252 'line' => [
253 'class' => [],
254 'x1' => [],
255 'x2' => [],
256 'y1' => [],
257 'y2' => [],
258 ],
259 'style' => [],
260
261 ];
262
263 if ($tag == null) {
264 return $tag_allowed;
265 } elseif (is_array($tag)) {
266 $new_tag_allow = [];
267
268 foreach ($tag as $_tag) {
269 $new_tag_allow[$_tag] = $tag_allowed[$_tag];
270 }
271
272 return $new_tag_allow;
273 } else {
274 return isset($tag_allowed[$tag]) ? $tag_allowed[$tag] : [];
275 }
276 }
277
278 /**
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
285 */
286 function ultimate_store_kit_get_pagination_link($page, $query_arg = '', $current_url = '') {
287 $page = absint($page);
288
289 if (! empty($query_arg)) {
290 if ($page <= 1) {
291 return remove_query_arg($query_arg, $current_url);
292 }
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
341 if ($wp_query->max_num_pages <= 1) {
342 return;
343 }
344
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;
364 } else {
365 $paged = absint(get_query_var('paged')) ?: 1;
366 }
367
368 $max = absint($wp_query->max_num_pages);
369 $links = [];
370
371 if ($paged >= 1) {
372 $links[] = $paged;
373 }
374
375 if ($paged >= 3) {
376 $links[] = $paged - 1;
377 $links[] = $paged - 2;
378 }
379
380 if (($paged + 2) <= $max) {
381 $links[] = $paged + 2;
382 $links[] = $paged + 1;
383 }
384
385 $current_url = '';
386
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 }
396 }
397
398 echo '<ul class="usk-pagination" aria-label="' . esc_attr__('Pagination', 'ultimate-store-kit') . '">' . "\n";
399
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 }
411
412 if (! in_array(1, $links, true)) {
413 $class = (1 === $paged) ? 'usk-active' : '';
414
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";
424 }
425 }
426
427 sort($links);
428
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 );
438 }
439
440 if (! in_array($max, $links, true)) {
441 if (! in_array($max - 1, $links, true)) {
442 echo '<li class="usk-pagination-dot-dot"><span>...</span></li>' . "\n";
443 }
444
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 );
453 }
454
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 );
465 }
466
467 echo '</ul>' . "\n";
468 }
469
470 function ultimate_store_kit_template_edit_link($template_id) {
471
472 if (Ultimate_Store_Kit_Loader::elementor()->editor->is_edit_mode()) {
473
474 $final_url = add_query_arg(['elementor' => ''], get_permalink($template_id));
475
476 $output = sprintf('<a class="usk-elementor-template-edit-link" href="%s" title="%s" target="_blank"><i class="usk-icon-edit"></i></a>', esc_url($final_url), esc_html__('Edit Template', 'ultimate-store-kit'));
477
478 return $output;
479 }
480 }
481
482 function ultimate_store_kit_iso_time($time) {
483 $current_offset = (float) get_option('gmt_offset');
484 $timezone_string = get_option('timezone_string');
485
486 // Create a UTC+- zone if no timezone string exists.
487
488 //if ( empty( $timezone_string ) ) {
489 if (0 === $current_offset) {
490 $timezone_string = '+00:00';
491 } elseif ($current_offset < 0) {
492 $timezone_string = $current_offset . ':00';
493 } else {
494 $timezone_string = '+0' . $current_offset . ':00';
495 }
496
497 //}
498
499 $sub_time = [];
500 $sub_time = explode(" ", $time);
501 $final_time = $sub_time[0] . 'T' . $sub_time[1] . ':00' . $timezone_string;
502
503 return $final_time;
504 }
505
506 /**
507 * @param $currency
508 * @param int $precision
509 *
510 * @return false|string
511 */
512 function ultimate_store_kit_currency_format($currency, $precision = 1) {
513
514 if ($currency > 0) {
515
516 if ($currency < 900) {
517 // 0 - 900
518 $currency_format = number_format($currency, $precision);
519 $suffix = '';
520 } else
521
522 if ($currency < 900000) {
523 // 0.9k-850k
524 $currency_format = number_format($currency / 1000, $precision);
525 $suffix = 'K';
526 } else
527
528 if ($currency < 900000000) {
529 // 0.9m-850m
530 $currency_format = number_format($currency / 1000000, $precision);
531 $suffix = 'M';
532 } else
533
534 if ($currency < 900000000000) {
535 // 0.9b-850b
536 $currency_format = number_format($currency / 1000000000, $precision);
537 $suffix = 'B';
538 } else {
539 // 0.9t+
540 $currency_format = number_format($currency / 1000000000000, $precision);
541 $suffix = 'T';
542 }
543
544 // Remove unecessary zeroes after decimal. "1.0" -> "1"; "1.00" -> "1"
545
546 // Intentionally does not affect partials, eg "1.50" -> "1.50"
547 if ($precision > 0) {
548 $dotzero = '.' . str_repeat('0', $precision);
549 $currency_format = str_replace($dotzero, '', $currency_format);
550 }
551
552 return $currency_format . $suffix;
553 }
554
555 return false;
556 }
557
558 /**
559 * @return array
560 */
561 function ultimate_store_kit_get_menu() {
562
563 $menus = wp_get_nav_menus();
564 $items = [0 => esc_html__('Select Menu', 'ultimate-store-kit')];
565 foreach ($menus as $menu) {
566 $items[$menu->slug] = $menu->name;
567 }
568
569 return $items;
570 }
571
572 /**
573 * default get_option() default value check
574 *
575 * @param string $option settings field name
576 * @param string $section the section name this field belongs to
577 * @param string $default default text if it's not found
578 *
579 * @return mixed
580 */
581 function ultimate_store_kit_option($option, $section, $default = '') {
582
583 $options = get_option($section);
584
585 if (isset($options[$option])) {
586 return $options[$option];
587 }
588
589 return $default;
590 }
591
592 /**
593 * @return array of elementor template
594 */
595 function ultimate_store_kit_et_options() {
596
597 $templates = Ultimate_Store_Kit_Loader::elementor()->templates_manager->get_source('local')->get_items();
598 $types = [];
599
600 if (empty($templates)) {
601 $template_options = ['0' => __('Template Not Found!', 'ultimate-store-kit')];
602 } else {
603 $template_options = ['0' => __('Select Template', 'ultimate-store-kit')];
604
605 foreach ($templates as $template) {
606 $template_options[$template['template_id']] = $template['title'] . ' (' . $template['type'] . ')';
607 $types[$template['template_id']] = $template['type'];
608 }
609 }
610
611 return $template_options;
612 }
613
614 /**
615 * @return array of wp default sidebars
616 */
617 function ultimate_store_kit_sidebar_options() {
618
619 global $wp_registered_sidebars;
620 $sidebar_options = [];
621
622 if (! $wp_registered_sidebars) {
623 $sidebar_options[0] = esc_html__('No sidebars were found', 'ultimate-store-kit');
624 } else {
625 $sidebar_options[0] = esc_html__('Select Sidebar', 'ultimate-store-kit');
626
627 foreach ($wp_registered_sidebars as $sidebar_id => $sidebar) {
628 $sidebar_options[$sidebar_id] = $sidebar['name'];
629 }
630 }
631
632 return $sidebar_options;
633 }
634
635 /**
636 * @param string category name
637 *
638 * @return array of category
639 */
640 function ultimate_store_kit_get_category($taxonomy = 'product_cat') {
641
642 $post_options = [];
643
644 $post_categories = get_terms([
645 'taxonomy' => $taxonomy,
646 'hide_empty' => false,
647 ]);
648
649 if (is_wp_error($post_categories)) {
650 return $post_options;
651 }
652
653 if (false !== $post_categories and is_array($post_categories)) {
654 foreach ($post_categories as $category) {
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 }
659 }
660 }
661
662 return $post_options;
663 }
664
665 /**
666 * @param string parent category name
667 * @return array of parent category
668 */
669 function ultimate_store_kit_get_only_parent_cats($taxonomy = 'category') {
670
671 $parent_categories = ['none' => __('None', 'ultimate-store-kit')];
672 $args = ['taxonomy' => $taxonomy, 'parent' => 0];
673 $parent_cats = get_terms($args);
674
675 foreach ($parent_cats as $parent_cat) {
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 }
680 }
681
682 return $parent_categories;
683 }
684
685 /**
686 * @param array all ajax posted array there
687 *
688 * @return array return all setting as array
689 */
690 function ultimate_store_kit_ajax_settings($settings) {
691
692 $required_settings = [
693 'show_date' => true,
694 'show_comment' => true,
695 'show_link' => true,
696 'show_meta' => true,
697 'show_title' => true,
698 'show_excerpt' => true,
699 'show_lightbox' => true,
700 'show_thumbnail' => true,
701 'show_category' => false,
702 'show_tags' => false,
703 ];
704
705 foreach ($settings as $key => $value) {
706 if (in_array($key, $required_settings)) {
707 $required_settings[$key] = $value;
708 }
709 }
710
711 return $required_settings;
712 }
713
714 /**
715 * @return array of all transition names
716 */
717 function ultimate_store_kit_transition_options() {
718
719 $transition_options = [
720 '' => esc_html__('None', 'ultimate-store-kit'),
721 'fade' => esc_html__('Fade', 'ultimate-store-kit'),
722 'scale-up' => esc_html__('Scale Up', 'ultimate-store-kit'),
723 'scale-down' => esc_html__('Scale Down', 'ultimate-store-kit'),
724 'slide-top' => esc_html__('Slide Top', 'ultimate-store-kit'),
725 'slide-bottom' => esc_html__('Slide Bottom', 'ultimate-store-kit'),
726 'slide-left' => esc_html__('Slide Left', 'ultimate-store-kit'),
727 'slide-right' => esc_html__('Slide Right', 'ultimate-store-kit'),
728 'slide-top-small' => esc_html__('Slide Top Small', 'ultimate-store-kit'),
729 'slide-bottom-small' => esc_html__('Slide Bottom Small', 'ultimate-store-kit'),
730 'slide-left-small' => esc_html__('Slide Left Small', 'ultimate-store-kit'),
731 'slide-right-small' => esc_html__('Slide Right Small', 'ultimate-store-kit'),
732 'slide-top-medium' => esc_html__('Slide Top Medium', 'ultimate-store-kit'),
733 'slide-bottom-medium' => esc_html__('Slide Bottom Medium', 'ultimate-store-kit'),
734 'slide-left-medium' => esc_html__('Slide Left Medium', 'ultimate-store-kit'),
735 'slide-right-medium' => esc_html__('Slide Right Medium', 'ultimate-store-kit'),
736 ];
737
738 return $transition_options;
739 }
740
741 // BDT Blend Type
742 function ultimate_store_kit_blend_options() {
743 $blend_options = [
744 'multiply' => esc_html__('Multiply', 'ultimate-store-kit'),
745 'screen' => esc_html__('Screen', 'ultimate-store-kit'),
746 'overlay' => esc_html__('Overlay', 'ultimate-store-kit'),
747 'darken' => esc_html__('Darken', 'ultimate-store-kit'),
748 'lighten' => esc_html__('Lighten', 'ultimate-store-kit'),
749 'color-dodge' => esc_html__('Color-Dodge', 'ultimate-store-kit'),
750 'color-burn' => esc_html__('Color-Burn', 'ultimate-store-kit'),
751 'hard-light' => esc_html__('Hard-Light', 'ultimate-store-kit'),
752 'soft-light' => esc_html__('Soft-Light', 'ultimate-store-kit'),
753 'difference' => esc_html__('Difference', 'ultimate-store-kit'),
754 'exclusion' => esc_html__('Exclusion', 'ultimate-store-kit'),
755 'hue' => esc_html__('Hue', 'ultimate-store-kit'),
756 'saturation' => esc_html__('Saturation', 'ultimate-store-kit'),
757 'color' => esc_html__('Color', 'ultimate-store-kit'),
758 'luminosity' => esc_html__('Luminosity', 'ultimate-store-kit'),
759 ];
760
761 return $blend_options;
762 }
763
764 // BDT Position
765 function ultimate_store_kit_position() {
766 $position_options = [
767 '' => esc_html__('Default', 'ultimate-store-kit'),
768 'top-left' => esc_html__('Top Left', 'ultimate-store-kit'),
769 'top-center' => esc_html__('Top Center', 'ultimate-store-kit'),
770 'top-right' => esc_html__('Top Right', 'ultimate-store-kit'),
771 'center' => esc_html__('Center', 'ultimate-store-kit'),
772 'center-left' => esc_html__('Center Left', 'ultimate-store-kit'),
773 'center-right' => esc_html__('Center Right', 'ultimate-store-kit'),
774 'bottom-left' => esc_html__('Bottom Left', 'ultimate-store-kit'),
775 'bottom-center' => esc_html__('Bottom Center', 'ultimate-store-kit'),
776 'bottom-right' => esc_html__('Bottom Right', 'ultimate-store-kit'),
777 ];
778
779 return $position_options;
780 }
781
782 // BDT Thumbnavs Position
783 function ultimate_store_kit_thumbnavs_position() {
784 $position_options = [
785 'top-left' => esc_html__('Top Left', 'ultimate-store-kit'),
786 'top-center' => esc_html__('Top Center', 'ultimate-store-kit'),
787 'top-right' => esc_html__('Top Right', 'ultimate-store-kit'),
788 'center-left' => esc_html__('Center Left', 'ultimate-store-kit'),
789 'center-right' => esc_html__('Center Right', 'ultimate-store-kit'),
790 'bottom-left' => esc_html__('Bottom Left', 'ultimate-store-kit'),
791 'bottom-center' => esc_html__('Bottom Center', 'ultimate-store-kit'),
792 'bottom-right' => esc_html__('Bottom Right', 'ultimate-store-kit'),
793 ];
794
795 return $position_options;
796 }
797
798 function ultimate_store_kit_navigation_position() {
799 $position_options = [
800 'top-left' => esc_html__('Top Left', 'ultimate-store-kit'),
801 'top-center' => esc_html__('Top Center', 'ultimate-store-kit'),
802 'top-right' => esc_html__('Top Right', 'ultimate-store-kit'),
803 'center' => esc_html__('Center', 'ultimate-store-kit'),
804 'bottom-left' => esc_html__('Bottom Left', 'ultimate-store-kit'),
805 'bottom-center' => esc_html__('Bottom Center', 'ultimate-store-kit'),
806 'bottom-right' => esc_html__('Bottom Right', 'ultimate-store-kit'),
807 ];
808
809 return $position_options;
810 }
811
812 function ultimate_store_kit_pagination_position() {
813 $position_options = [
814 'top-left' => esc_html__('Top Left', 'ultimate-store-kit'),
815 'top-center' => esc_html__('Top Center', 'ultimate-store-kit'),
816 'top-right' => esc_html__('Top Right', 'ultimate-store-kit'),
817 'bottom-left' => esc_html__('Bottom Left', 'ultimate-store-kit'),
818 'bottom-center' => esc_html__('Bottom Center', 'ultimate-store-kit'),
819 'bottom-right' => esc_html__('Bottom Right', 'ultimate-store-kit'),
820 ];
821
822 return $position_options;
823 }
824
825 // BDT Drop Position
826 function ultimate_store_kit_drop_position() {
827 $drop_position_options = [
828 'bottom-left' => esc_html__('Bottom Left', 'ultimate-store-kit'),
829 'bottom-center' => esc_html__('Bottom Center', 'ultimate-store-kit'),
830 'bottom-right' => esc_html__('Bottom Right', 'ultimate-store-kit'),
831 'bottom-justify' => esc_html__('Bottom Justify', 'ultimate-store-kit'),
832 'top-left' => esc_html__('Top Left', 'ultimate-store-kit'),
833 'top-center' => esc_html__('Top Center', 'ultimate-store-kit'),
834 'top-right' => esc_html__('Top Right', 'ultimate-store-kit'),
835 'top-justify' => esc_html__('Top Justify', 'ultimate-store-kit'),
836 'left-top' => esc_html__('Left Top', 'ultimate-store-kit'),
837 'left-center' => esc_html__('Left Center', 'ultimate-store-kit'),
838 'left-bottom' => esc_html__('Left Bottom', 'ultimate-store-kit'),
839 'right-top' => esc_html__('Right Top', 'ultimate-store-kit'),
840 'right-center' => esc_html__('Right Center', 'ultimate-store-kit'),
841 'right-bottom' => esc_html__('Right Bottom', 'ultimate-store-kit'),
842 ];
843
844 return $drop_position_options;
845 }
846
847 // Button Size
848 function ultimate_store_kit_button_sizes() {
849 $button_sizes = [
850 'xs' => esc_html__('Extra Small', 'ultimate-store-kit'),
851 'sm' => esc_html__('Small', 'ultimate-store-kit'),
852 'md' => esc_html__('Medium', 'ultimate-store-kit'),
853 'lg' => esc_html__('Large', 'ultimate-store-kit'),
854 'xl' => esc_html__('Extra Large', 'ultimate-store-kit'),
855 ];
856
857 return $button_sizes;
858 }
859
860 // Button Size
861 function ultimate_store_kit_heading_size() {
862 $heading_sizes = [
863 'h1' => 'H1',
864 'h2' => 'H2',
865 'h3' => 'H3',
866 'h4' => 'H4',
867 'h5' => 'H5',
868 'h6' => 'H6',
869 ];
870
871 return $heading_sizes;
872 }
873
874 // Title Tags
875 function ultimate_store_kit_title_tags() {
876 $title_tags = [
877 'h1' => 'H1',
878 'h2' => 'H2',
879 'h3' => 'H3',
880 'h4' => 'H4',
881 'h5' => 'H5',
882 'h6' => 'H6',
883 'div' => 'div',
884 'span' => 'span',
885 'p' => 'p',
886 ];
887
888 return $title_tags;
889 }
890
891 function ultimate_store_kit_mask_shapes() {
892 $path = BDTUSK_ASSETS_URL . 'images/mask/';
893 $shape_name = 'shape';
894 $extension = '.svg';
895 $list = [0 => esc_html__('Select Mask', 'ultimate-store-kit')];
896
897 for ($i = 1; $i <= 20; $i++) {
898 $list[$path . $shape_name . '-' . $i . $extension] = ucwords($shape_name . ' ' . $i);
899 }
900
901 return $list;
902 }
903
904 /**
905 * This is a svg file converter function which return a svg content
906 *
907 * @param svg file
908 *
909 * @return svg content
910 */
911 function ultimate_store_kit_svg_icon($icon) {
912
913 $icon_path = BDTUSK_ASSETS_PATH . "images/svg/{$icon}.svg";
914
915 if (! file_exists($icon_path)) {
916 return false;
917 }
918
919 ob_start();
920
921 include $icon_path;
922
923 $svg = ob_get_clean();
924
925 return $svg;
926 }
927
928 /**
929 * @param array CSV file data
930 * @param string $delimiter
931 * @param false $header
932 *
933 * @return string
934 */
935 function ultimate_store_kit_parse_csv($csv, $delimiter = ';', $header = true) {
936
937 if (! is_string($csv)) {
938 return '';
939 }
940
941 if (! function_exists('str_getcsv')) {
942 return $csv;
943 }
944
945 $html = '';
946 $rows = explode(PHP_EOL, $csv);
947 $headRow = 1;
948
949 foreach ($rows as $row) {
950
951 if ($headRow == 1 and $header) {
952 $html .= '<thead><tr>';
953 } else {
954 $html .= '<tr>';
955 }
956
957 foreach (str_getcsv($row, $delimiter) as $cell) {
958
959 $cell = trim($cell);
960
961 $html .= $header
962 ? '<th>' . $cell . '</th>'
963 : '<td>' . $cell . '</td>';
964 }
965
966 if ($headRow == 1 and $header) {
967 $html .= '</tr></thead><tbody>';
968 } else {
969 $html .= '</tr>';
970 }
971
972 $headRow++;
973 $header = false;
974 }
975
976 return '<table>' . $html . '</tbody></table>';
977 }
978
979 function ultimate_store_kit_dashboard_link($suffix = '') {
980 return add_query_arg(['page' => 'ultimate-store-kit' . $suffix], admin_url('admin.php'));
981 }
982
983 /**
984 * @param int $limit default limit is 25 word
985 * @param bool $strip_shortcode if you want to strip shortcode from excert text
986 * @param string $trail trail string default is ...
987 *
988 * @return string return custom limited excerpt text
989 */
990 function ultimate_store_kit_custom_excerpt($limit = 25, $strip_shortcode = false, $trail = '') {
991
992 $output = get_the_content();
993
994 if ($limit) {
995 $output = wp_trim_words($output, $limit, $trail);
996 }
997
998 if ($strip_shortcode) {
999 $output = strip_shortcodes($output);
1000 }
1001
1002 return wpautop($output);
1003 }
1004
1005 function usk_get_order_options() {
1006 $options = [
1007 'title' => __('Title', 'ultimate-store-kit'),
1008 'ID' => __('ID', 'ultimate-store-kit'),
1009 'date' => __('Date', 'ultimate-store-kit'),
1010 'rand' => __('Random', 'ultimate-store-kit'),
1011 '_price' => __('Product Price', 'ultimate-store-kit'),
1012 'total_sales' => __('Top Seller', 'ultimate-store-kit'),
1013 'comment_count' => __('Most Reviewed', 'ultimate-store-kit'),
1014 '_wc_average_rating' => __('Top Rated', 'ultimate-store-kit'),
1015 ];
1016
1017 return apply_filters('usk_order_options', $options);
1018 }
1019
1020 //wishlist
1021 function ultimate_store_kit_get_wishlist($user_id = 0) {
1022 $_wishlist_key = '_ultimate_store_kit_wishlist';
1023 $_wishlist = [];
1024
1025 if (isset($_COOKIE[$_wishlist_key])) {
1026 // The cookie is visitor-controlled, so it is sanitized before decoding and
1027 // every decoded entry is forced to a product id — nothing else is kept.
1028 $cookie_data = sanitize_text_field(wp_unslash($_COOKIE[$_wishlist_key]));
1029 $decoded_data = json_decode($cookie_data, true);
1030
1031 if (json_last_error() === JSON_ERROR_NONE && is_array($decoded_data)) {
1032 $_wishlist = array_values(array_filter(array_map('absint', array_filter($decoded_data, 'is_scalar'))));
1033 }
1034 }
1035
1036 return apply_filters('ultimate_store_kit_wishlist', array_unique($_wishlist));
1037 }
1038
1039 function usk_get_taxonomies() {
1040 $taxonomy_list = get_object_taxonomies('product');
1041 $taxonomies = [
1042 'search' => 'Search',
1043 'price' => 'Price',
1044 'orderby' => 'Orderby',
1045 'order' => 'Order',
1046 ];
1047
1048 foreach ($taxonomy_list as $_taxonomy) {
1049 $taxonomy = get_taxonomy($_taxonomy);
1050
1051 if ($taxonomy->show_ui) {
1052 $taxonomies[$_taxonomy] = $taxonomy->label;
1053 }
1054 }
1055
1056 return $taxonomies;
1057 }
1058
1059 function ultimate_store_kit_hide_on_class($selectors) {
1060 $element_hide_on = '';
1061
1062 if (! empty($selectors)) {
1063
1064 foreach ($selectors as $element) {
1065
1066 if ($element == 'desktop') {
1067 $element_hide_on .= ' usk-hide-desktop';
1068 }
1069
1070 if ($element == 'tablet') {
1071 $element_hide_on .= ' usk-hide-tablet';
1072 }
1073
1074 if ($element == 'mobile') {
1075 $element_hide_on .= ' usk-hide-mobile';
1076 }
1077 }
1078 }
1079
1080 return $element_hide_on;
1081 }
1082
1083 function ultimate_store_kit_wc_product_quick_view_content($product_id) {
1084 // This renders on an unauthenticated endpoint, so the gate is on the product
1085 // rather than on a nonce: nothing is written, and only a product the visitor
1086 // could already open on the shop is allowed through. The previous
1087 // wp_verify_nonce() call here had its arguments reversed and its result
1088 // discarded, so it checked nothing.
1089 if (! usk_is_public_product($product_id)) {
1090 return;
1091 }
1092
1093 global $woocommerce;
1094 global $post;
1095
1096 if (intval($product_id)) {
1097 $post = get_post($product_id);
1098 $next_post = get_next_post();
1099 $prev_post = get_previous_post();
1100
1101 wp('p=' . $product_id . '&post_type=product');
1102 ob_start();
1103 ?>
1104 <div class="usk-modal-page">
1105 <?php
1106 while (have_posts()) :
1107 the_post(); ?>
1108 <div class="usk-modal-product">
1109 <div id="product-<?php the_ID(); ?>" <?php post_class('product'); ?>>
1110 <div class="usk-modal-image-wrapper">
1111 <?php do_action('ultimate_store_kit_quick_florence_grid_view_product_images'); ?>
1112 <div class="usk-onsale">
1113 <?php do_action('ultimate_store_kit_wc_product_quick_view_product_sale_flash'); ?>
1114 </div>
1115 </div>
1116 <div class="usk-modal-content-box">
1117 <a href="<?php echo esc_url(get_permalink()); ?>" class="usk-product-title">
1118 <?php do_action('ultimate_store_kit_quick_view_product_title'); ?>
1119 </a>
1120 <div class="usk-rating">
1121 <?php do_action('ultimate_store_kit_quick_view_product_single_rating'); ?>
1122 </div>
1123 <div class="usk-product-price">
1124 <?php do_action('ultimate_store_kit_quick_view_product_single_price'); ?>
1125 </div>
1126 <div class="usk-product-meta">
1127 <div class="usk-sku-wrapper">
1128 <?php do_action('ultimate_store_kit_quick_view_product_single_meta'); ?>
1129 </div>
1130 </div>
1131 <div class="usk-product-desc">
1132 <?php do_action('ultimate_store_kit_quick_view_product_single_excerpt'); ?>
1133 </div>
1134
1135 <div class="usk-quick-action-wrap">
1136 <?php do_action('ultimate_store_kit_quick_view_product_single_add_to_cart'); ?>
1137 </div>
1138 </div>
1139 </div>
1140 </div>
1141 <?php endwhile; ?>
1142 </div>
1143
1144 <?php
1145 $content = ob_get_clean();
1146 echo wp_kses_post($content);
1147 ?>
1148 <script type="text/javascript">
1149 jQuery(document).ready(function($) {
1150 // Load required WooCommerce scripts
1151 var scripts = [
1152 '<?php echo esc_url(plugins_url('assets/js/prettyPhoto/jquery.prettyPhoto.init.js', WC_PLUGIN_FILE)); ?>',
1153 '<?php echo esc_url(plugins_url('assets/js/frontend/add-to-cart-variation.min.js', WC_PLUGIN_FILE)); ?>',
1154 '<?php echo esc_url(plugins_url('assets/js/frontend/add-to-cart.min.js', WC_PLUGIN_FILE)); ?>',
1155 '<?php echo esc_url(plugins_url('assets/js/frontend/single-product.min.js', WC_PLUGIN_FILE)); ?>'
1156 ];
1157
1158 // Load scripts sequentially
1159 function loadScript(index) {
1160 if (index >= scripts.length) {
1161 initializeWooCommerce();
1162 return;
1163 }
1164
1165 $.getScript(scripts[index], function() {
1166 loadScript(index + 1);
1167 });
1168 }
1169
1170 function initializeWooCommerce() {
1171 // Initialize WooCommerce add to cart functionality
1172 var wc_add_to_cart_variation_params = {
1173 "ajax_url": "<?php echo esc_url(admin_url('admin-ajax.php')); ?>",
1174 "i18n_view_cart": "<?php echo esc_js(__('View cart', 'ultimate-store-kit')); ?>",
1175 "cart_url": "<?php echo esc_url(wc_get_cart_url()); ?>",
1176 "is_cart": "<?php echo is_cart() ? '1' : '0'; ?>",
1177 "cart_redirect_after_add": "<?php echo get_option('woocommerce_cart_redirect_after_add') ? '1' : '0'; ?>"
1178 };
1179
1180 // Initialize variation forms
1181 $(document.body).trigger('wc_variation_form');
1182
1183 // Initialize add to cart buttons
1184 $(document.body).trigger('wc_fragments_refreshed');
1185 $(document.body).trigger('wc_fragments_loaded');
1186 $(document.body).trigger('added_to_cart');
1187 }
1188
1189 // Start loading scripts
1190 // loadScript(0);
1191 });
1192 </script>
1193 <?php
1194 exit();
1195 }
1196 }
1197
1198 function ultimate_store_kit_quick_view_product_images() {
1199 global $post, $product; ?>
1200 <div class="images">
1201 <?php
1202
1203 if (has_post_thumbnail()) {
1204 $attachment_count = count($product->get_gallery_image_ids());
1205 $gallery = $attachment_count > 0 ? '[product-gallery]' : '';
1206 $props = wc_get_product_attachment_props(get_post_thumbnail_id(), $post);
1207 $image = get_the_post_thumbnail($post->ID, apply_filters('single_product_large_thumbnail_size', 'shop_single'), [
1208 'title' => $props['title'],
1209 'alt' => $props['alt'],
1210 ]);
1211 echo wp_kses_post(apply_filters('woocommerce_single_product_image_html', sprintf('<a href="%s" itemprop="image" class="usk-product woocommerce-main-image zoom" title="%s" data-rel="prettyPhoto' . $gallery . '">%s</a>', $props['url'], $props['caption'], $image), $post->ID));
1212 } else {
1213 echo wp_kses_post(apply_filters('woocommerce_single_product_image_html', sprintf('<img src="%s" alt="%s" />', wc_placeholder_img_src(), esc_html__('Placeholder', 'ultimate-store-kit')), $post->ID));
1214 }
1215
1216 $attachment_ids = $product->get_gallery_image_ids();
1217
1218 if ($attachment_ids) :
1219 $loop = 0;
1220 $columns = apply_filters('woocommerce_product_thumbnails_columns', 3);
1221 ?>
1222 <div class="thumbnails columns-<?php echo esc_attr($columns); ?>">
1223 <?php
1224
1225 foreach ($attachment_ids as $attachment_id) {
1226 $classes = ['thumbnail'];
1227
1228 if ($loop === 0 || $loop % $columns === 0) {
1229 $classes[] = 'first';
1230 }
1231
1232 if (($loop + 1) % $columns === 0) {
1233 $classes[] = 'last';
1234 }
1235
1236 $image_link = wp_get_attachment_url($attachment_id);
1237
1238 if (! $image_link) {
1239 continue;
1240 }
1241
1242 $image_title = esc_attr(get_the_title($attachment_id));
1243 $image_caption = esc_attr(get_post_field('post_excerpt', $attachment_id));
1244 $image = wp_get_attachment_image($attachment_id, apply_filters('single_product_small_thumbnail_size', 'shop_thumbnail'), 0, $attr = [
1245 'title' => $image_title,
1246 'alt' => $image_title,
1247 ]);
1248 $image_class = esc_attr(implode(' ', $classes));
1249 echo wp_kses_post(apply_filters('woocommerce_single_product_image_thumbnail_html', sprintf('<a href="%s" class="%s" title="%s" >%s</a>', $image_link, $image_class, $image_caption, $image), $attachment_id, $post->ID, $image_class));
1250 $loop++;
1251 }
1252
1253 ?>
1254 </div>
1255 <?php endif; ?>
1256 </div>
1257 <?php
1258 }
1259
1260
1261 /**
1262 * License Validation
1263 */
1264 if (! function_exists('usk_license_validation')) {
1265 function usk_license_validation() {
1266
1267 if (function_exists('_is_usk_pro_activated') && false === _is_usk_pro_activated()) {
1268 return false;
1269 }
1270
1271 $license_key = trim(get_option('ultimate_store_kit_license_key'));
1272
1273 if (isset($license_key) && ! empty($license_key)) {
1274 return true;
1275 } else {
1276 return false;
1277 }
1278 return false;
1279 }
1280 }
1281
1282
1283
1284 if (! function_exists('usk_is_public_product')) {
1285 /**
1286 * Whether a product id may be read or acted on by the current request.
1287 *
1288 * wc_get_product() resolves an id regardless of post status, so any endpoint
1289 * reachable by untrusted users must run the id through here before it reads
1290 * product data or stores the id against the visitor.
1291 *
1292 * @param mixed $product_id Raw product id, typically straight off $_POST.
1293 * @return bool
1294 */
1295 function usk_is_public_product($product_id) {
1296 $product_id = absint($product_id);
1297
1298 // Fail closed when WooCommerce is absent — helper.php also loads on EDD-only sites.
1299 if (! $product_id || ! function_exists('wc_get_product')) {
1300 return false;
1301 }
1302
1303 $product = wc_get_product($product_id);
1304
1305 if (! $product) {
1306 return false;
1307 }
1308
1309 // Private, draft, pending and trashed products stay invisible unless the
1310 // current user could read the post anyway (shop managers, the editor).
1311 if ('publish' !== $product->get_status() && ! current_user_can('read_post', $product_id)) {
1312 return false;
1313 }
1314
1315 if (post_password_required($product_id)) {
1316 return false;
1317 }
1318
1319 return true;
1320 }
1321 }
1322
1323 /**
1324 * Maximum number of items kept in a wishlist or compare list.
1325 *
1326 * Both lists are written from unauthenticated endpoints, so they need an upper
1327 * bound — a cookie that outgrows ~4KB is silently dropped by the browser, and an
1328 * unbounded list against a logged-in user means unbounded user meta.
1329 */
1330 if (! function_exists('usk_get_list_item_limit')) {
1331 function usk_get_list_item_limit() {
1332 return (int) apply_filters('ultimate_store_kit_list_item_limit', 50);
1333 }
1334 }
1335
1336 function usk_get_compare_products($user_id = 0) {
1337 $_compare_products_key = '_ultimate_store_kit_compare_products';
1338 $_compare_products = [];
1339 if ($user_id != 0) {
1340 $_compare_products = get_user_meta($user_id, $_compare_products_key, true) ?: [];
1341 } elseif (isset($_COOKIE[$_compare_products_key])) {
1342 // Same treatment as the wishlist cookie: sanitize the visitor-supplied value
1343 // before decoding, then keep nothing but product ids.
1344 $cookie_value = sanitize_text_field(wp_unslash($_COOKIE[$_compare_products_key]));
1345 $_compare_products = json_decode($cookie_value, true);
1346
1347 // Check if JSON decoding failed
1348 if (! is_array($_compare_products)) {
1349 $_compare_products = [];
1350 } else {
1351 $_compare_products = array_values(array_filter(array_map('absint', array_filter($_compare_products, 'is_scalar'))));
1352 }
1353 }
1354
1355 return apply_filters('ultimate_store_kit_compare_products', array_unique($_compare_products));
1356 }
1357
1358 function usk_get_compare_products_count() {
1359 $count = 0;
1360 $user_id = get_current_user_id();
1361 $products = usk_get_compare_products($user_id);
1362 if (is_array($products)) {
1363 $count = count($products);
1364 }
1365 return $count;
1366 }
1367
1368 //if (!function_exists('ultimate_store_kit_get_compare_product_slug')) {
1369 // function ultimate_store_kit_compare_product_slug() {
1370 // return 'compare-products';
1371 // }
1372 //}
1373
1374 if (! function_exists('ultimate_store_kit_compare_product_page')) {
1375 function ultimate_store_kit_compare_product_page() {
1376 if ($postId = intval(get_option('bdt_usk_compare_products_page_id'))) {
1377 $post = get_post($postId);
1378 if ($post && $post->post_status == 'publish') {
1379 return $post->ID;
1380 }
1381 }
1382 }
1383 }
1384
1385 if (! function_exists('ultimate_store_kit_is_compare_product_page')) {
1386 function ultimate_store_kit_is_compare_product_page() {
1387 if ($page = ultimate_store_kit_compare_product_page()) {
1388 // return is_page( $page->ID );
1389 return is_page($page);
1390 }
1391 }
1392 }
1393
1394 /**
1395 * Helper function to check if variation swatches Pro is active
1396 *
1397 * @return bool
1398 */
1399 function usk_has_variation_swatches_support() {
1400 return class_exists('UltimateStoreKitPro\\VariationSwatches\\Swatches');
1401 }
1402
1403 /**
1404 * Helper function to load variation swatches scripts and styles
1405 */
1406 function usk_load_variation_swatches_assets() {
1407 // Always load the grid variations script for variation support
1408 wp_register_script('usk-grid-variations', BDTUSK_ASSETS_URL . 'js/modules/grid-variations.js', ['jquery'], BDTUSK_VER, true);
1409 wp_localize_script('usk-grid-variations', 'usk_vars', array(
1410 'ajax_url' => admin_url('admin-ajax.php'),
1411 'nonce' => wp_create_nonce('usk_variations')
1412 ));
1413 }
1414 add_action('wp_enqueue_scripts', 'usk_load_variation_swatches_assets', 20);
1415
1416 // Hook into AJAX variation selection to update product image
1417 function usk_ajax_variation_image_update() {
1418 // Public read-only endpoint returning a public product image. A nonce cannot
1419 // survive full-page caching for logged-out visitors, so authorisation is
1420 // enforced on the product itself further down instead.
1421 // phpcs:disable WordPress.Security.NonceVerification.Missing
1422 if (!isset($_POST['variation_id']) || !isset($_POST['product_id'])) {
1423 wp_send_json_error('Missing required parameters');
1424 return;
1425 }
1426
1427 $variation_id = absint($_POST['variation_id']);
1428 $product_id = absint($_POST['product_id']);
1429 // phpcs:enable WordPress.Security.NonceVerification.Missing
1430
1431 $variation = wc_get_product($variation_id);
1432 if (!$variation) {
1433 wp_send_json_error('Invalid variation');
1434 return;
1435 }
1436
1437 // wc_get_product() ignores post status, so guard the parent product the same way
1438 // the variations endpoint does before exposing anything about it.
1439 $parent = wc_get_product($product_id);
1440 if (!$parent) {
1441 wp_send_json_error('Invalid product');
1442 return;
1443 }
1444
1445 if ('publish' !== $parent->get_status() && !current_user_can('read_post', $product_id)) {
1446 wp_send_json_error('Product not available', 404);
1447 return;
1448 }
1449
1450 if (post_password_required($product_id)) {
1451 wp_send_json_error('Product not available', 403);
1452 return;
1453 }
1454
1455 // The variation must actually belong to the product that passed the check above,
1456 // otherwise the parent id is just a public decoy for an arbitrary variation.
1457 if ($variation->get_parent_id() !== $product_id) {
1458 wp_send_json_error('Invalid variation');
1459 return;
1460 }
1461
1462 $image_id = $variation->get_image_id();
1463 $image_url = '';
1464
1465 if ($image_id) {
1466 $image_url = wp_get_attachment_image_url($image_id, 'woocommerce_thumbnail');
1467 } else {
1468 // If variation has no image, use the parent product image
1469 $parent_image_id = $parent->get_image_id();
1470 if ($parent_image_id) {
1471 $image_url = wp_get_attachment_image_url($parent_image_id, 'woocommerce_thumbnail');
1472 }
1473 }
1474
1475 wp_send_json_success(array('image_url' => $image_url));
1476 }
1477 add_action('wp_ajax_usk_get_variation_image', 'usk_ajax_variation_image_update');
1478 add_action('wp_ajax_nopriv_usk_get_variation_image', 'usk_ajax_variation_image_update');
1479
1480 // Start: Add to cart quantity buttons conversion
1481 if (! function_exists('usk_display_quantity_minus')) {
1482 function usk_display_quantity_minus() {
1483 if (! is_product()) return;
1484 echo '<button type="button" class="bdt-add-to-cart-qty-minus" ><i class="usk-icon-minus3"></i></button>';
1485 }
1486 }
1487
1488 if (! function_exists('usk_display_quantity_plus')) {
1489 function usk_display_quantity_plus() {
1490 if (! is_product()) return;
1491 echo '<button type="button" class="bdt-add-to-cart-qty-plus" ><i class="usk-icon-plus3"></i></button>';
1492 }
1493 }
1494
1495 if (! function_exists('usk_add_cart_quantity_plus_minus')) {
1496 function usk_add_cart_quantity_plus_minus() {
1497
1498 echo '<style>
1499 input[type="number"]::-webkit-outer-spin-button,
1500 input[type="number"]::-webkit-inner-spin-button {
1501 -webkit-appearance: none;
1502 margin: 0;
1503 }
1504
1505 input[type="number"] {
1506 -moz-appearance: textfield; /* Firefox */
1507 }
1508 </style>';
1509
1510 wc_enqueue_js("
1511 $(document).off('click.bdtQtyHandler'); // Remove previous handler
1512 $(document).on('click.bdtQtyHandler', 'button.bdt-add-to-cart-qty-plus, button.bdt-add-to-cart-qty-minus', function(e) {
1513 e.preventDefault();
1514 var qty = $(this).closest('form.cart').find('.qty');
1515 var val = parseFloat(qty.val()) || 0;
1516 var max = parseFloat(qty.attr('max')) || Infinity;
1517 var min = parseFloat(qty.attr('min')) || 0;
1518 var step = parseFloat(qty.attr( 'step' ));
1519
1520 if ($(this).is('.bdt-add-to-cart-qty-plus')) {
1521 qty.val(Math.min(val + step, max)).trigger('change');
1522 } else {
1523 qty.val(Math.max(val - step, min)).trigger('change');
1524 }
1525 });
1526 ");
1527 }
1528 }
1529
1530 if (! function_exists('usk_setup_quantity_buttons')) {
1531 function usk_setup_quantity_buttons() {
1532 if (function_exists('is_product')) {
1533 // Remove the default version
1534 remove_all_actions('woocommerce_before_quantity_input_field');
1535 remove_all_actions('woocommerce_after_quantity_input_field');
1536 remove_all_actions('woocommerce_before_single_product');
1537
1538 // Add our version
1539 add_action('woocommerce_before_quantity_input_field', 'usk_display_quantity_minus');
1540 add_action('woocommerce_after_quantity_input_field', 'usk_display_quantity_plus');
1541 add_action('woocommerce_after_single_product', 'usk_add_cart_quantity_plus_minus');
1542 }
1543 }
1544 }
1545 // End: Add to cart quantity buttons conversion
1546