PluginProbe
Product Labels, Quick View, Buy Now, Pre-Orders, Frequently Bought Together & More for WooCommerce – Merchant / 2.3.0
Product Labels, Quick View, Buy Now, Pre-Orders, Frequently Bought Together & More for WooCommerce – Merchant v2.3.0
2.3.2 2.3.1 2.3.0 2.2.8 2.2.7 trunk 1.10.0 1.10.1 1.10.2 1.10.3 1.10.4 1.10.5 1.11.0 1.11.1 1.11.2 1.6 1.7 1.8 1.8.1 1.8.2 1.8.3 1.9.0 1.9.1 1.9.10 1.9.11 All 60 releases
merchant / inc / helpers.php

helpers.php in Product Labels, Quick View, Buy Now, Pre-Orders, Frequently Bought Together & More for WooCommerce – Merchant 2.3.0, at inc/helpers.php

1,157 lines 33.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Helper functions.
5 *
6 * @package Merchant
7 */
8
9 if ( ! defined( 'ABSPATH' ) ) {
10 exit; // Exit if accessed directly
11 }
12
13 /**
14 * Allowed tags general.
15 * Should be used to escape complex outputs like entire features html.
16 *
17 * @param array $extra
18 * @param bool $include_post_tags Whether to include post kses allowed tags or not. Default true.
19 *
20 * @return array $allowed_tags
21 */
22 if ( ! function_exists( 'merchant_kses_allowed_tags' ) ) {
23 function merchant_kses_allowed_tags( $extra = array(), $include_post_tags = true ) {
24 // Default
25 $allowed_tags = array_merge(
26 array(
27 // Meta
28 'meta' => array(
29 'name' => true,
30 'content' => true,
31 ),
32
33 // SVG Support
34 'svg' => array(
35 'class' => true,
36 'xmlns' => true,
37 'width' => true,
38 'height' => true,
39 'viewbox' => true,
40 'aria-hidden' => true,
41 'role' => true,
42 'focusable' => true,
43 'fill' => true,
44 'stroke' => true,
45 'stroke-linecap' => true,
46 'stroke-linejoin' => true,
47 'stroke-width' => true,
48 ),
49 'g' => array(
50 'id' => true,
51 'class' => true,
52 'clip-path' => true,
53 'style' => true,
54 'transform' => true,
55 ),
56 'path' => array(
57 'fill' => true,
58 'fill-rule' => true,
59 'd' => true,
60 'transform' => true,
61 'stroke' => true,
62 'stroke-width' => true,
63 'stroke-linejoin' => true,
64 'clip-rule' => true,
65 'opacity' => true,
66 ),
67 'polyline' => array(
68 'points' => true,
69 'fill' => true,
70 'fill-rule' => true,
71 'd' => true,
72 'transform' => true,
73 'stroke' => true,
74 'stroke-width' => true,
75 'stroke-linejoin' => true,
76 ),
77 'polygon' => array(
78 'fill' => true,
79 'fill-rule' => true,
80 'points' => true,
81 'transform' => true,
82 'focusable' => true,
83 'stroke' => true,
84 'stroke-width' => true,
85 ),
86 'rect' => array(
87 'x' => true,
88 'y' => true,
89 'rx' => true,
90 'width' => true,
91 'height' => true,
92 'transform' => true,
93 'fill' => true,
94 'stroke' => true,
95 'stroke-width' => true,
96 ),
97 'circle' => array(
98 'cx' => true,
99 'cy' => true,
100 'r' => true,
101 'width' => true,
102 'height' => true,
103 'transform' => true,
104 'fill' => true,
105 'stroke' => true,
106 'stroke-width' => true,
107 'stroke-linecap' => true,
108 'stroke-linejoin' => true,
109 ),
110 'line' => array(
111 'x1' => true,
112 'y1' => true,
113 'x2' => true,
114 'y2' => true,
115 'width' => true,
116 'height' => true,
117 'transform' => true,
118 'fill' => true,
119 'stroke' => true,
120 'stroke-width' => true,
121 'stroke-linecap' => true,
122 'stroke-linejoin' => true,
123 ),
124 'clipPath' => array(
125 'id' => true,
126 'class' => true,
127 'style' => true,
128 ),
129 'defs' => array(
130 'id' => true,
131 ),
132 'progress' => array(
133 'id' => true,
134 'class' => true,
135 'value' => true,
136 'max' => true,
137 ),
138 ),
139 $include_post_tags ? wp_kses_allowed_html( 'post' ) : array()
140 );
141
142 // Include schema markup tags
143 if ( in_array( 'schema_markup', $extra, true ) || in_array( 'all', $extra, true ) ) {
144 $tags = array( 'meta', 'nav', 'ul', 'li', 'a', 'span' );
145
146 foreach ( $tags as $tag ) {
147 if ( isset( $allowed_tags[ $tag ] ) ) {
148 $allowed_tags[ $tag ]['itemprop'] = true;
149 $allowed_tags[ $tag ]['itemscope'] = true;
150 $allowed_tags[ $tag ]['itemtype'] = true;
151 } else {
152 $allowed_tags[ $tag ] = array(
153 'itemprop' => true,
154 'itemscope' => true,
155 'itemtype' => true,
156 );
157 }
158 }
159 }
160
161 // Include iframe tags
162 if ( in_array( 'iframe', $extra, true ) || in_array( 'all', $extra, true ) ) {
163 $allowed_tags['iframe'] = array(
164 'src' => true,
165 'height' => true,
166 'width' => true,
167 'frameborder' => true,
168 'allowfullscreen' => true,
169 );
170 }
171
172 if ( in_array( 'wc_email', $extra, true ) || in_array( 'all', $extra, true ) ) {
173 $allowed_tags['body'] = array(
174 'class' => true,
175 'style' => true,
176 'leftmargin' => true,
177 'marginwidth' => true,
178 'topmargin' => true,
179 'marginheight' => true,
180 'offset' => true,
181 );
182
183 $allowed_tags['html'] = array(
184 'lang' => true,
185 );
186
187 $allowed_tags['style'] = array(
188 'type' => true,
189 );
190 $allowed_tags['head'] = array();
191 $allowed_tags['meta']['http-equiv'] = true;
192 }
193
194 // Include nonce tags
195 if ( in_array( 'nonce', $extra, true ) || in_array( 'all', $extra, true ) ) {
196 $allowed_tags['input'] = array(
197 'type' => true,
198 'id' => true,
199 'name' => true,
200 'value' => true,
201 'data-name' => true,
202 );
203 }
204
205 // Include bdi tag
206 if ( in_array( 'bdi', $extra, true ) || in_array( 'all', $extra, true ) ) {
207 $allowed_tags['bdi'] = array(
208 'class' => true,
209 'id' => true,
210 'style' => true,
211 );
212 }
213
214 // Include select2
215 if ( in_array( 'select2', $extra, true ) || in_array( 'all', $extra, true ) ) {
216 $allowed_tags['select'] = array(
217 'name' => true,
218 'class' => true,
219 'id' => true,
220 'style' => true,
221 'data-name' => true,
222 'data-source' => true,
223 'multiple' => true,
224 );
225
226 $allowed_tags['option'] = array(
227 'value' => true,
228 'selected' => true,
229 );
230
231 $allowed_tags['optgroup'] = array(
232 'label' => true,
233 );
234 }
235
236 // Include dd, dt tags
237 if ( in_array( 'dd', $extra, true ) || in_array( 'all', $extra, true ) ) {
238 $allowed_tags['dd'] = array(
239 'class' => true,
240 'id' => true,
241 'style' => true,
242 );
243
244 $allowed_tags['dt'] = array(
245 'class' => true,
246 'id' => true,
247 'style' => true,
248 );
249 }
250
251 // Include forms tags.
252 if ( in_array( 'forms', $extra, true ) || in_array( 'all', $extra, true ) ) {
253 $tags = array( 'form', 'input', 'select', 'option', 'textarea', 'a' );
254
255 foreach ( $tags as $tag ) {
256 $allowed_tags[ $tag ] = array(
257 'id' => true,
258 'class' => true,
259 'style' => true,
260 'name' => true,
261 'href' => true,
262 'target' => true,
263 'value' => true,
264 'type' => true,
265 'placeholder' => true,
266 'data' => '*',
267 'data-source' => true,
268 'data-product_id' => true,
269 'data-product_variations' => true,
270 'data-attribute_name' => true,
271 'data-show_option_none' => true,
272 'data-name' => true,
273 'data-allowed-types' => true,
274 'step' => true,
275 'min' => true,
276 'max' => true,
277 'selected' => true,
278 'checked' => true,
279 'onchange' => true,
280 'autocomplete' => true,
281 'required' => true,
282 'action' => true,
283 'method' => true,
284 'enctype' => true,
285 'size' => true,
286 'role' => true,
287 'inputmode' => true,
288 'aria-label' => true,
289 'multiple' => true,
290 );
291
292 if ( $tag === 'a' ) {
293 $allowed_tags[ $tag ]['href'] = true;
294 $allowed_tags[ $tag ]['title'] = true;
295 $allowed_tags[ $tag ]['target'] = true;
296 }
297 }
298 }
299
300
301 // Include script tag
302 if ( in_array( 'script', $extra, true ) ) {
303 $allowed_tags['script'] = array(
304 'id' => true,
305 'src' => true,
306 'type' => true,
307 'async' => true,
308 'defer' => true,
309 );
310 }
311
312 if ( in_array( 'div', $extra, true ) ) {
313 $tags = array( 'form', 'input', 'select', 'option', 'textarea', 'a', 'div' );
314
315 foreach ( $tags as $tag ) {
316 $allowed_tags[ $tag ] = array(
317 'id' => true,
318 'class' => true,
319 'style' => true,
320 'name' => true,
321 'href' => true,
322 'target' => true,
323 'value' => true,
324 'type' => true,
325 'placeholder' => true,
326 'data' => '*',
327 'data-source' => true,
328 'data-product_id' => true,
329 'data-product_variations' => true,
330 'data-attribute_name' => true,
331 'data-show_option_none' => true,
332 'data-name' => true,
333 'data-allowed-types' => true,
334 'step' => true,
335 'min' => true,
336 'max' => true,
337 'selected' => true,
338 'checked' => true,
339 'onchange' => true,
340 'autocomplete' => true,
341 'required' => true,
342 'action' => true,
343 'method' => true,
344 'enctype' => true,
345 'size' => true,
346 'role' => true,
347 'inputmode' => true,
348 'aria-label' => true,
349 'multiple' => true,
350 'title' => true,
351 'data-id' => true,
352 'data-product-id' => true,
353 );
354
355 if ( $tag === 'a' ) {
356 $allowed_tags[ $tag ]['href'] = true;
357 $allowed_tags[ $tag ]['title'] = true;
358 $allowed_tags[ $tag ]['target'] = true;
359 }
360 }
361 }
362
363 /**
364 * Filters the allowed tags.
365 *
366 * @since 1.2.5
367 */
368 return apply_filters( 'merchant_kses_allowed_tags', $allowed_tags );
369 }
370 }
371
372 /**
373 * Allowed tags for scripts.
374 *
375 * @return array $allowed_tags
376 */
377 if ( ! function_exists( 'merchant_kses_allowed_tags_for_code_snippets' ) ) {
378 function merchant_kses_allowed_tags_for_code_snippets() {
379 return array(
380 'script' => array(
381 'type' => true,
382 ),
383 );
384 }
385 }
386
387 /**
388 * Sanitize SVG markup.
389 *
390 * Uses the existing SVG allowlist from merchant_kses_allowed_tags()
391 * to preserve SVG elements while stripping unsafe content.
392 *
393 * @param mixed $value The raw SVG markup.
394 *
395 * @return string The sanitized SVG markup.
396 *
397 * @since 2.3.0
398 */
399 if ( ! function_exists( 'merchant_sanitize_svg' ) ) {
400 function merchant_sanitize_svg( $value ) {
401 return wp_kses( (string) $value, merchant_kses_allowed_tags( array(), false ) );
402 }
403 }
404
405 /**
406 * Format a price for admin preview display.
407 *
408 * Uses wc_price() when WooCommerce is available, otherwise falls back to a plain
409 * currency symbol + number_format string.
410 *
411 * @param float $price The price value.
412 * @param string $currency Currency symbol fallback (used only when WC is unavailable).
413 *
414 * @return string Formatted price HTML.
415 *
416 * @since 2.2.5
417 */
418 if ( ! function_exists( 'merchant_preview_price' ) ) {
419 function merchant_preview_price( $price, $currency = '$' ) {
420 if ( function_exists( 'wc_price' ) ) {
421 return wp_kses( wc_price( $price ), merchant_kses_allowed_tags( array( 'bdi' ) ) );
422 }
423
424 return esc_html( $currency ) . esc_html( number_format( (float) $price, 2 ) );
425 }
426 }
427
428 /**
429 * Check if WooCommerce checkout page is being rendered by block.
430 * Since WooCommerce 8.3.0 the checkout page is rendered by block.
431 *
432 * @return bool
433 */
434 if ( ! function_exists( 'merchant_is_checkout_block_layout' ) ) {
435 function merchant_is_checkout_block_layout() {
436 $checkout_page = wc_get_page_id( 'checkout' );
437
438 if ( empty( $checkout_page ) ) {
439 return false;
440 }
441
442 if ( function_exists( 'has_blocks' ) && has_blocks( $checkout_page ) ) {
443 $post = get_post( $checkout_page );
444 $blocks = parse_blocks( $post->post_content );
445
446 foreach ( $blocks as $block ) {
447 if ( 'woocommerce/checkout' === $block['blockName'] ) {
448 return true;
449 }
450 }
451 }
452
453 return false;
454 }
455 }
456
457 /**
458 * Check if WooCommerce cart page is being rendered by block.
459 * Since WooCommerce 8.3.0 the cart page is rendered by block.
460 *
461 * @return bool
462 */
463 if ( ! function_exists( 'merchant_is_cart_block_layout' ) ) {
464 function merchant_is_cart_block_layout() {
465 $cart_page = wc_get_page_id( 'cart' );
466
467 if ( empty( $cart_page ) ) {
468 return false;
469 }
470
471 if ( function_exists( 'has_blocks' ) && has_blocks( $cart_page ) ) {
472 $post = get_post( $cart_page );
473 $blocks = parse_blocks( $post->post_content );
474
475 foreach ( $blocks as $block ) {
476 if ( 'woocommerce/cart' === $block['blockName'] ) {
477 return true;
478 }
479 }
480 }
481
482 return false;
483 }
484 }
485
486 /**
487 * Get the product categories.
488 */
489 if ( ! function_exists( 'merchant_get_product_categories' ) ) {
490 function merchant_get_product_categories() {
491 $args = array(
492 'taxonomy' => 'product_cat',
493 'hide_empty' => false,
494 );
495
496 $categories = get_terms( $args );
497
498 $categories_tree = array();
499 $indexed_categories = array();
500
501 if ( ! empty( $categories ) && ! is_wp_error( $categories ) ) {
502 foreach ( $categories as $category ) {
503 $indexed_categories[ $category->term_id ] = array(
504 'id' => $category->term_id,
505 'slug' => $category->slug,
506 'name' => $category->name,
507 'parent' => $category->parent,
508 'children' => array(),
509 );
510 }
511
512 foreach ( $indexed_categories as &$cat ) {
513 if ( (int) $cat['parent'] === 0 ) {
514 $categories_tree[] = &$cat;
515 } else {
516 $indexed_categories[ $cat['parent'] ]['children'][] = &$cat;
517 }
518 }
519 }
520
521 return $categories_tree;
522 }
523 }
524
525 /**
526 * Get the product tags.
527 */
528 if ( ! function_exists( 'merchant_get_product_tags' ) ) {
529 function merchant_get_product_tags() {
530 $product_tags = get_terms( array(
531 'taxonomy' => 'product_tag',
532 'hide_empty' => false,
533 ) );
534
535 $tag_names = array();
536
537 if ( ! empty( $product_tags ) && ! is_wp_error( $product_tags ) ) {
538 foreach ( $product_tags as $tag ) {
539 $tag_names[ $tag->slug ] = $tag->name;
540 }
541 }
542
543 return $tag_names;
544 }
545 }
546
547 /**
548 * Convert array to css.
549 */
550 if ( ! function_exists( 'merchant_array_to_css' ) ) {
551 function merchant_array_to_css( $css_array ) {
552 $css = '';
553 if ( empty( $css_array ) ) {
554 return $css;
555 }
556 foreach ( $css_array as $key => $value ) {
557 $css .= $key . ':' . $value . ';';
558 }
559
560 return $css;
561 }
562 }
563
564 /**
565 * Get the share link data structure.
566 *
567 * @return array
568 */
569 if ( ! function_exists( 'merchant_get_share_link_data' ) ) {
570 function merchant_get_share_link_data() {
571 return array(
572 'facebook' => array(
573 'url' => 'https://www.facebook.com/sharer.php?u={{url}}',
574 'title' => __( 'Facebook', 'merchant' ),
575 ),
576 'twitter' => array(
577 'url' => 'https://twitter.com/intent/tweet?url={{url}}&text={{title}}',
578 'title' => __( 'X', 'merchant' ),
579 ),
580 'linkedin' => array(
581 'url' => 'https://www.linkedin.com/sharing/share-offsite/?url={{url}}',
582 'title' => __( 'LinkedIn', 'merchant' ),
583 ),
584 'reddit' => array(
585 'url' => 'https://reddit.com/submit?url={{url}}&title={{title}}',
586 'title' => __( 'Reddit', 'merchant' ),
587 ),
588 'whatsapp' => array(
589 'url' => 'https://api.whatsapp.com/send/?text={{url}}',
590 'title' => __( 'WhatsApp', 'merchant' ),
591 ),
592 'pinterest' => array(
593 'url' => 'http://pinterest.com/pin/create/link/?url={{url}}',
594 'title' => __( 'Pinterest', 'merchant' ),
595 ),
596 'telegram' => array(
597 'url' => 'https://t.me/share/url?url={{url}}&text={{title}}',
598 'title' => __( 'Telegram', 'merchant' ),
599 ),
600 'weibo' => array(
601 'url' => 'http://service.weibo.com/share/share.php?url={{url}}&appkey=&title={{title}}&pic=&ralateUid=',
602 'title' => __( 'Weibo', 'merchant' ),
603 ),
604 'vk' => array(
605 'url' => 'http://vk.com/share.php?url={{url}}&title={{title}}&comment={text}',
606 'title' => __( 'VK', 'merchant' ),
607 ),
608 'ok' => array(
609 'url' => 'https://connect.ok.ru/dk?st.cmd=WidgetSharePreview&st.shareUrl={{url}}',
610 'title' => __( 'OK', 'merchant' ),
611 ),
612 'xing' => array(
613 'url' => 'https://www.xing.com/spi/shares/new?url={{url}}',
614 'title' => __( 'Xing', 'merchant' ),
615 ),
616 'mail' => array(
617 'url' => 'mailto:?subject={{title}}&body={{url}}',
618 'title' => __( 'Mail', 'merchant' ),
619 ),
620 );
621 }
622 }
623
624 /**
625 * Get the share link url.
626 *
627 * @param string $social_network
628 *
629 * @return string
630 */
631 if ( ! function_exists( 'merchant_get_share_link_url' ) ) {
632 function merchant_get_share_link_url( $social_network, $url_to_share, $title_to_share = '' ) {
633 $share_link_data = merchant_get_share_link_data();
634
635 if ( ! isset( $share_link_data[ $social_network ] ) ) {
636 return '';
637 }
638
639 $share_link_url = str_replace(
640 array( '{{url}}', '{{title}}' ),
641 array( $url_to_share, $title_to_share ),
642 $share_link_data[ $social_network ]['url']
643 );
644
645 return $share_link_url;
646 }
647 }
648
649 /**
650 * Get the share link title.
651 *
652 * @param string $social_network
653 *
654 * @return string
655 */
656 if ( ! function_exists( 'merchant_get_share_link_title' ) ) {
657 function merchant_get_share_link_title( $social_network ) {
658 $share_link_data = merchant_get_share_link_data();
659
660 return isset( $share_link_data[ $social_network ] ) ? $share_link_data[ $social_network ]['title'] : '';
661 }
662 }
663
664 if ( ! function_exists( 'merchant_timezone' ) ) {
665 /**
666 * Get the WP timezone.
667 *
668 * @return string
669 */
670 function merchant_timezone() {
671 /**
672 * Filter the storewide sale timezone.
673 *
674 * @param string $timezone
675 *
676 * @since 1.9.9
677 */
678 return apply_filters(
679 'merchant_storewide_sale_timezone',
680 wp_timezone_string()
681 );
682 }
683 }
684
685 if ( ! function_exists( 'merchant_get_current_timestamp' ) ) {
686 /**
687 * Get the current timestamp.
688 *
689 * @return int|string
690 */
691 function merchant_get_current_timestamp() {
692 $timezone = new DateTimeZone( merchant_timezone() );
693
694 // Get the timestamp
695 return ( new DateTime( 'now', $timezone ) )->getTimestamp();
696 }
697 }
698
699 if ( ! function_exists( 'merchant_convert_date_to_timestamp' ) ) {
700 /**
701 * Convert date to timestamp.
702 *
703 * @param string $date The date to convert
704 * @param string $format The format of the date
705 *
706 * @return int The timestamp
707 */
708 function merchant_convert_date_to_timestamp( $date, $format = 'm-d-Y h:i A' ) {
709 $timezone = new DateTimeZone( merchant_timezone() );
710 $date_object = DateTime::createFromFormat( $format, $date, $timezone ); // Create DateTime object with specified format and timezone
711 if ( false === $date_object ) {
712 return 0;
713 }
714
715 return $date_object->getTimestamp(); // Output the timestamp
716 }
717 }
718
719 if ( ! function_exists( 'merchant_date_string_to_timestamp' ) ) {
720 /**
721 * Convert a date string to a Unix timestamp based on the specified format and timezone.
722 *
723 * This function signature matches Merchant Pro for compatibility.
724 *
725 * @param string $date The date string to convert.
726 * @param string|null $timezone_offset The timezone offset or timezone string. Defaults to WordPress timezone.
727 * @param string $format The format of the date string. Defaults to 'm-d-Y h:i A'.
728 *
729 * @return int The timestamp, or 0 on failure.
730 */
731 function merchant_date_string_to_timestamp( $date, $timezone_offset = null, $format = 'm-d-Y h:i A' ) {
732 try {
733 // If it's already numeric, return it as-is
734 if ( is_numeric( $date ) ) {
735 return (int) $date;
736 }
737
738 // Use WordPress timezone if not specified
739 $timezone_offset = $timezone_offset ?? wp_timezone_string();
740
741 // Create DateTime object from the specified format and timezone
742 $date_object = DateTime::createFromFormat( $format, $date, new DateTimeZone( $timezone_offset ) );
743
744 return $date_object ? $date_object->getTimestamp() : 0;
745 } catch ( Exception $e ) {
746 return 0;
747 }
748 }
749 }
750
751 /**
752 * Parses a list of product IDs
753 * @return array
754 */
755 if ( ! function_exists( 'merchant_parse_product_ids' ) ) {
756 function merchant_parse_product_ids( $product_ids ) {
757 $parsed_ids = $product_ids ?? array();
758
759 // Convert to array if it's not already an array
760 $parsed_ids = ! is_array( $parsed_ids ) ? explode( ',', $parsed_ids ) : $parsed_ids;
761
762 // Convert all elements to integers
763 $parsed_ids = array_map( 'intval', $parsed_ids );
764
765 return $parsed_ids;
766 }
767 }
768
769 /**
770 * Check if the user condition is passed.
771 *
772 * @param $args
773 *
774 * @return bool
775 */
776 if ( ! function_exists( 'merchant_is_user_condition_passed' ) ) {
777 function merchant_is_user_condition_passed( $args = array() ) {
778 $args = is_array( $args ) ? $args : array();
779
780 $is_logged_in = is_user_logged_in();
781 $current_user = $is_logged_in ? wp_get_current_user() : null;
782 $customer_id = is_object( $current_user ) && isset( $current_user->ID ) ? (int) $current_user->ID : 0;
783 $user_role = is_object( $current_user ) && isset( $current_user->roles ) && ! empty( $current_user->roles ) ? $current_user->roles[0] : '';
784
785 $condition = $args['user_condition'] ?? 'all';
786
787 $is_exclusion_enabled = $args['user_exclusion_enabled'] ?? false;
788 $exclude_users_raw = $args['exclude_users'] ?? array();
789 $excluded_customers = array_map( 'intval', is_array( $exclude_users_raw ) ? $exclude_users_raw : array() );
790 $excluded_roles_raw = $args['exclude_roles'] ?? array();
791 $excluded_roles = is_array( $excluded_roles_raw ) ? $excluded_roles_raw : array();
792
793 switch ( $condition ) {
794 case 'all':
795 case '':
796 if ( $is_exclusion_enabled ) {
797 if ( in_array( $customer_id, $excluded_customers, true ) ) {
798 return false;
799 }
800
801 if ( $condition === 'all' && in_array( $user_role, $excluded_roles, true ) ) {
802 return false;
803 }
804 }
805
806 return true;
807
808 case 'logged-in':
809 return $is_logged_in;
810
811 case 'roles':
812 if ( $is_exclusion_enabled && in_array( $customer_id, $excluded_customers, true ) ) {
813 return false;
814 }
815
816 $allowed_roles_raw = $args['user_condition_roles'] ?? array();
817 $allowed_roles = is_array( $allowed_roles_raw ) ? $allowed_roles_raw : array();
818 return in_array( $user_role, $allowed_roles, true );
819
820 case 'customers':
821 case 'users':
822 $allowed_users_raw = $args['user_condition_users'] ?? array();
823 $allowed_customers = array_map( 'intval', is_array( $allowed_users_raw ) ? $allowed_users_raw : array() );
824 return $is_logged_in && in_array( $customer_id, $allowed_customers, true );
825
826 default:
827 return false;
828 }
829 }
830 }
831
832 /**
833 * Check if a product is excluded based on given arguments.
834 *
835 * @param $product_id
836 * @param $args
837 *
838 * @return bool
839 */
840 if ( ! function_exists( 'merchant_is_product_excluded' ) ) {
841 function merchant_is_product_excluded( $product_id, $args = array() ) {
842 if ( empty( $args['exclusion_enabled'] ) ) {
843 return false;
844 }
845
846 $display_rule = $args['rules_to_display'] ?? $args['display_rules'] ?? $args['rules_to_apply'] ?? $args['trigger_on'] ?? 'products';
847
848 $rules = array(
849 'all',
850 'all_products',
851 'categories',
852 'category',
853 'by_category',
854 'tags',
855 'by_tags',
856 'featured_products',
857 'products_on_sale',
858 'new_products',
859 'out_of_stock',
860 'pre-order',
861 );
862
863 $product = wc_get_product( $product_id );
864 $_product_id = $product && $product->is_type( 'variation' ) ? $product->get_parent_id() : $product_id;
865
866 // Exclude products
867 if ( in_array( $display_rule, $rules, true ) ) {
868 $excluded_product_ids = $args['excluded_products'] ?? array();
869 $excluded_product_ids = merchant_parse_product_ids( $excluded_product_ids );
870
871 if ( in_array( (int) $product_id, $excluded_product_ids, true ) || in_array( (int) $_product_id, $excluded_product_ids, true ) ) {
872 return true;
873 }
874 }
875
876 // Exclude categories
877 if ( in_array( $display_rule, array( 'all', 'all_products', 'categories', 'category', 'by_category' ), true ) ) {
878 $excluded_categories_slugs = $args['excluded_categories'] ?? array();
879
880 if ( ! empty( $excluded_categories_slugs ) && has_term( $excluded_categories_slugs, 'product_cat', $_product_id ) ) {
881 return true;
882 }
883 }
884
885 // Exclude tags
886 if ( in_array( $display_rule, array( 'all', 'all_products', 'tags', 'by_tags' ), true ) ) {
887 $excluded_tags_slugs = $args['excluded_tags'] ?? array();
888
889 if ( ! empty( $excluded_tags_slugs ) && has_term( $excluded_tags_slugs, 'product_tag', $_product_id ) ) {
890 return true;
891 }
892 }
893
894 // Exclude brands
895 if ( in_array( $display_rule, array( 'all', 'all_products', 'brands', 'by_brands' ), true ) ) {
896 $excluded_brands_slugs = $args['excluded_brands'] ?? array();
897
898 if ( ! empty( $excluded_brands_slugs ) && has_term( $excluded_brands_slugs, 'product_brand', $_product_id ) ) {
899 return true;
900 }
901 }
902
903 // Exclude categories with toggle
904 if ( ! empty( $args['exclude_categories_toggle'] ) && in_array( $display_rule, array( 'all', 'all_products', 'categories', 'category', 'by_category' ), true ) ) {
905 $excluded_categories_slugs = $args['excluded_categories'] ?? array();
906
907 if ( ! empty( $excluded_categories_slugs ) && has_term( $excluded_categories_slugs, 'product_cat', $_product_id ) ) {
908 return true;
909 }
910 }
911
912 // Exclude tags with toggle
913 if ( ! empty( $args['exclude_tags_toggle'] ) && in_array( $display_rule, array( 'all', 'all_products', 'tags', 'by_tags' ), true ) ) {
914 $excluded_tags_slugs = $args['excluded_tags'] ?? array();
915
916 if ( ! empty( $excluded_tags_slugs ) && has_term( $excluded_tags_slugs, 'product_tag', $_product_id ) ) {
917 return true;
918 }
919 }
920
921 // Exclude brands with toggle
922 if ( ! empty( $args['exclude_brands_toggle'] ) && in_array( $display_rule, array( 'all', 'all_products', 'brands', 'by_brands' ), true ) ) {
923 $excluded_brands_slugs = $args['excluded_brands'] ?? array();
924
925 if ( ! empty( $excluded_brands_slugs ) && has_term( $excluded_brands_slugs, 'product_brand', $_product_id ) ) {
926 return true;
927 }
928 }
929
930 return false;
931 }
932 }
933
934 /**
935 * Check if a product or any of its variations is excluded based on the given offer.
936 *
937 * This method determines if a product (simple or variable) or any of its variations
938 * satisfies the merchant exclusion rules.
939 *
940 * @param int $product_id The ID of the product to check.
941 * @param array $offer The offer data to evaluate against exclusion rules.
942 *
943 * @return bool True if the product or any of its variations is excluded, false otherwise.
944 */
945 if ( ! function_exists( 'merchant_is_product_or_variation_excluded' ) ) {
946 function merchant_is_product_or_variation_excluded( $product_id, $offer = array() ) {
947 $product = wc_get_product( $product_id );
948
949 if ( ! $product ) {
950 return false;
951 }
952
953 // Get IDs to check for exclusion
954 $product_ids = $product->is_type( 'variable' ) ? $product->get_children() : array( $product_id );
955
956 foreach ( $product_ids as $id ) {
957 if ( merchant_is_product_excluded( $id, $offer ) ) {
958 return true; // Excluded if any ID matches
959 }
960 }
961
962 return false;
963 }
964 }
965
966 /**
967 * Get the label of the first active payment gateway in WooCommerce.
968 *
969 * @return string|null The label of the first active payment gateway, or null if none are found.
970 */
971 if ( ! function_exists( 'merchant_get_first_active_payment_gateway_label' ) ) {
972 function merchant_get_first_active_payment_gateway_label() {
973 // Get the available payment gateways
974 $available_gateways = WC()->payment_gateways->get_available_payment_gateways();
975
976 // Check if there are any active gateways
977 if ( ! empty( $available_gateways ) ) {
978 // Get the first active gateway
979 return reset( $available_gateways )->get_title();
980 }
981
982 // Return null if no active gateways are found
983 return null;
984 }
985 }
986
987 /**
988 * Get the review count of a product.
989 *
990 * @param int $product_id The ID of the product to get the review count for.
991 *
992 * @return int The review count of the product.
993 */
994 if ( ! function_exists( 'merchant_get_product_reviews_count' ) ) {
995 function merchant_get_product_reviews_count( $product_id ) {
996 $product = wc_get_product( $product_id );
997
998 return $product ? $product->get_review_count() : 0;
999 }
1000 }
1001
1002 if ( ! function_exists( 'merchant_get_modules_campaign_data' ) ) {
1003 /**
1004 * Retrieves campaign data for a specific module.
1005 *
1006 * This function fetches campaign data based on the provided module ID and processes it
1007 * into a standardized format. The campaigns are indexed by their `flexible_id` (expected to be a UUID v4)
1008 * and include details such as ID, title, and status.
1009 *
1010 * @param string $module_id The ID of the module for which to retrieve campaign data.
1011 * Expected values include constants like `Merchant_Buy_X_Get_Y::MODULE_ID`,
1012 * `Merchant_Pre_Orders::MODULE_ID`, and `Merchant_Product_Labels::MODULE_ID`.
1013 *
1014 * @return array An associative array of campaigns, where each key is the `flexible_id` (UUID v4)
1015 * of the campaign, and the value is an array containing the campaign's `id`, `title`,
1016 * and `status`.
1017 * Example:
1018 * [
1019 * '550e8400-e29b-41d4-a716-446655440000' => [
1020 * 'id' => '550e8400-e29b-41d4-a716-446655440000',
1021 * 'title' => 'Campaign Title',
1022 * 'status' => 'active',
1023 * ],
1024 * 'f47ac10b-58cc-4372-a567-0e02b2c3d479' => [
1025 * 'id' => 'f47ac10b-58cc-4372-a567-0e02b2c3d479',
1026 * 'title' => 'Another Campaign',
1027 * 'status' => 'inactive',
1028 * ],
1029 * ]
1030 */
1031 function merchant_get_module_campaigns_data( $module_id ) {
1032 $campaigns = array();
1033
1034 // Determine the key to use based on the module ID
1035 $key = '';
1036 if ( Merchant_Buy_X_Get_Y::MODULE_ID === $module_id || Merchant_Pre_Orders::MODULE_ID === $module_id ) {
1037 $key = 'rules';
1038 } elseif ( Merchant_Product_Labels::MODULE_ID === $module_id ) {
1039 $key = 'labels';
1040 } else {
1041 $key = 'offers';
1042 }
1043
1044 // Fetch the module campaigns
1045 $module_campaigns = Merchant_Admin_Options::get( $module_id, $key, array() );
1046 if ( empty( $module_campaigns ) ) {
1047 return $campaigns;
1048 }
1049
1050 // Process each campaign
1051 foreach ( $module_campaigns as $campaign ) {
1052 if ( empty( $campaign['flexible_id'] ) ) {
1053 continue;
1054 }
1055
1056 $campaigns[ $campaign['flexible_id'] ] = array(
1057 'campaign_key' => $key,
1058 'campaign_id' => $campaign['flexible_id'],
1059 'campaign_title' => $campaign['offer-title'] ?? $campaign['label-title'] ?? '',
1060 'campaign_status' => ( isset( $campaign['campaign_status'] ) && $campaign['campaign_status'] === 'inactive' ) ? 'inactive' : 'active',
1061 );
1062 }
1063
1064 return $campaigns;
1065 }
1066 }
1067
1068 if ( ! function_exists( 'merchant_get_modules_data' ) ) {
1069 /**
1070 * Get the data of all the modules.
1071 *
1072 * @return array
1073 */
1074 function merchant_get_modules_data() {
1075 $modules = array();
1076 $registered_modules = Merchant_Admin_Modules::get_modules();
1077 foreach ( $registered_modules as $module_category => $module_data ) {
1078 foreach ( $module_data['modules'] as $module_id => $module ) {
1079 if ( ! empty( $module ) ) {
1080 $modules[ $module_id ] = array(
1081 'id' => $module_id,
1082 'active' => Merchant_Modules::is_module_active( $module_id ),
1083 'name' => $module['title'],
1084 'desc' => $module['desc'],
1085 'category' => $module_category,
1086 'pro' => $module['pro'],
1087 );
1088
1089 $campaigns = merchant_get_module_campaigns_data( $module_id );
1090
1091 if ( ! empty( $campaigns ) ) {
1092 $modules[ $module_id ]['campaigns'] = $campaigns;
1093 }
1094 }
1095 }
1096 }
1097
1098 return $modules;
1099 }
1100 }
1101
1102 if ( ! function_exists( 'merchant_get_active_modules' ) ) {
1103 /**
1104 * Get the active modules.
1105 *
1106 * @return array
1107 */
1108 function merchant_get_active_modules() {
1109 $modules = array_keys( merchant_get_modules_data() );
1110
1111 return array_values( array_filter( $modules, static function ( $module_id ) {
1112 return Merchant_Modules::is_module_active( $module_id );
1113 } ) );
1114 }
1115 }
1116
1117 if ( ! function_exists( 'merchant_get_campaign_data' ) ) {
1118 /**
1119 * Get the data of a specific campaign.
1120 *
1121 * @param string $campaign_id The ID of the campaign.
1122 * @param string $module_id The ID of the module.
1123 *
1124 * @return array
1125 */
1126 function merchant_get_campaign_data( $campaign_id, $module_id ) {
1127 if ( Merchant_Buy_X_Get_Y::MODULE_ID === $module_id || Merchant_Pre_Orders::MODULE_ID === $module_id ) {
1128 $key = 'rules';
1129 } elseif ( Merchant_Product_Labels::MODULE_ID === $module_id ) {
1130 $key = 'labels';
1131 } elseif ( 'buy-now' === $module_id ) {
1132 $key = 'campaigns';
1133 } else {
1134 $key = 'offers';
1135 }
1136
1137 $all_modules = merchant_get_modules_data();
1138 $module_campaigns = Merchant_Admin_Options::get( $module_id, $key, array() );
1139 if ( ! empty( $module_campaigns ) ) {
1140 foreach ( $module_campaigns as $campaign ) {
1141 if ( isset( $all_modules[ $module_id ], $campaign['flexible_id'] ) && $campaign_id === $campaign['flexible_id'] ) {
1142 return array(
1143 'campaign_id' => $campaign['flexible_id'],
1144 'campaign_title' => $campaign['offer-title'] ?? $campaign['label-title'] ?? '',
1145 'campaign_status' => $campaign['status'] ?? 'active',
1146 'module_id' => $module_id,
1147 'module_name' => $all_modules[ $module_id ]['name'],
1148 'is_pro' => $all_modules[ $module_id ]['pro'],
1149 'is_module_active' => $all_modules[ $module_id ]['active'],
1150 );
1151 }
1152 }
1153 }
1154
1155 return array();
1156 }
1157 }