PluginProbe
ShopBuilder – WooCommerce Builder For Elementor / 3.2.6
ShopBuilder – WooCommerce Builder For Elementor v3.2.6
3.4.2 3.4.1 3.4.0 2.0.1 2.0.2 2.0.3 2.1.0 2.1.1 2.1.10 2.1.11 2.1.12 2.1.13 2.1.14 2.1.15 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 2.2.0 2.2.1 2.2.2 All 63 releases
← All changes | app/Elementor/Helper/RenderHelpers.php +990 -38 2.1.53.2.6 View file →
@@ -8,10 +8,14 @@
8 8 */
9 9
10 10 namespace RadiusTheme\SB\Elementor\Helper;
11 11
12 +use Elementor\Plugin;
13 +use RadiusTheme\SB\Helpers\Cache;
14 +use RadiusTheme\SB\Helpers\Fns;
12 15 use RadiusTheme\SB\Helpers\BuilderFns;
13 -use RadiusTheme\SB\Helpers\Fns;
16 +use WC_Product_Query;
17 +use WP_Term;
14 18
15 19 // Do not allow directly accessing this file.
16 20 if ( ! defined( 'ABSPATH' ) ) {
17 21 exit( 'This script cannot be accessed directly.' );
@@ -21,8 +25,33 @@
21 25 * BuilderFns class
22 26 */
23 27 class RenderHelpers {
24 28 /**
29 + * @var array
30 + */
31 + private static $cache = [];
32 + /**
33 + * @param string $order_by value.
34 + * @return string
35 + */
36 + public static function escaping_product_orderby( $order_by ) {
37 + $catalog_orderby_options = apply_filters(
38 + 'woocommerce_catalog_orderby',
39 + [
40 + 'menu_order' => __( 'Default sorting', 'shopbuilder' ),
41 + 'popularity' => __( 'Sort by popularity', 'shopbuilder' ),
42 + 'rating' => __( 'Sort by average rating', 'shopbuilder' ),
43 + 'date' => __( 'Sort by latest', 'shopbuilder' ),
44 + 'price' => __( 'Sort by price: low to high', 'shopbuilder' ),
45 + 'price-desc' => __( 'Sort by price: high to low', 'shopbuilder' ),
46 + ]
47 + );
48 + if ( in_array( $order_by, array_keys( $catalog_orderby_options ), true ) ) {
49 + return $order_by;
50 + }
51 + return 'menu_order';
52 + }
53 + /**
25 54 * Get data.
26 55 *
27 56 * @param array|string $haystack The array or string to search for the value.
28 57 * @param string $needle The key to look for in the array or the string itself.
@@ -83,8 +112,9 @@
83 112 'order' => self::get_data( $meta, 'posts_order', 'DESC' ),
84 113 'author_in' => self::get_data( $meta, 'filter_author', [] ),
85 114 'display_by' => self::get_data( $meta, 'products_filter', 'date' ),
86 115 'categories' => self::get_data( $meta, 'filter_categories', [] ),
116 + 'brands' => self::get_data( $meta, 'filter_brands', [] ),
87 117 'tags' => self::get_data( $meta, 'filter_tags', [] ),
88 118 'attributes' => self::get_data( $meta, 'filter_attributes', [] ),
89 119 'relation' => self::get_data( $meta, 'tax_relation', 'OR' ),
90 120 'category_source' => self::get_data( $meta, 'select_source', 'product_cat' ),
@@ -196,8 +226,9 @@
196 226 'auto_play' => ! empty( $meta['slide_autoplay'] ),
197 227 'stop_on_hover' => ! empty( $meta['pause_hover'] ),
198 228 'slider_nav' => ! empty( $meta['slider_nav'] ),
199 229 'slider_dot' => ! empty( $meta['slider_pagi'] ),
230 + 'slider_dynamic_dot' => ! empty( $meta['slider_dynamic_pagi'] ),
200 231 'loop' => ! empty( $meta['slider_loop'] ),
201 232 'lazy_load' => ! empty( $meta['slider_lazy_load'] ),
202 233 'auto_height' => ! empty( $meta['slider_auto_height'] ),
203 234 'speed' => self::get_data( $meta, 'slide_speed', 2000 ),
@@ -247,8 +278,16 @@
247 278 $data['queried_term'] = esc_html( $queried_obj->term_id );
248 279 }
249 280 }
250 281
282 + if ( is_search() ) {
283 + $search_query = get_search_query();
284 +
285 + if ( $search_query ) {
286 + $data['search_query'] = sanitize_text_field( $search_query );
287 + }
288 + }
289 +
251 290 return apply_filters( 'rtsb/elementor/render/meta_dataset_final', $data, $meta, $raw_settings );
252 291 }
253 292
254 293 /**
@@ -300,8 +339,16 @@
300 339 $data['queried_term'] = esc_html( $queried_obj->term_id );
301 340 }
302 341 }
303 342
343 + if ( is_search() ) {
344 + $search_query = get_search_query();
345 +
346 + if ( $search_query ) {
347 + $data['search_query'] = sanitize_text_field( $search_query );
348 + }
349 + }
350 +
304 351 return apply_filters( 'rtsb/elementor/render/archive_meta_dataset_final', $data, $meta );
305 352 }
306 353
307 354 /**
@@ -340,9 +387,16 @@
340 387
341 388 if ( ! empty( $settings['single_category'] ) ) {
342 389 $visibility[] = 'single-cat';
343 390 }
391 + if ( ! empty( $settings['show_brands'] ) ) {
392 + $visibility[] = 'brands';
393 + }
344 394
395 + if ( ! empty( $settings['single_brand'] ) ) {
396 + $visibility[] = 'single-brand';
397 + }
398 +
345 399 if ( ! empty( $settings['show_swatches'] ) ) {
346 400 $visibility[] = 'swatches';
347 401 }
348 402
@@ -408,13 +462,23 @@
408 462 * @return int
409 463 */
410 464 public static function default_columns( $layout ) {
411 465 switch ( $layout ) {
466 + case 'rtsb-post-grid-layout1':
467 + case 'rtsb-post-grid-layout2':
468 + case 'rtsb-post-grid-layout3':
469 + case 'rtsb-team-layout1':
470 + case 'rtsb-team-layout2':
471 + case 'rtsb-testimonial-layout2':
412 472 case 'grid-layout2':
413 473 case 'slider-layout2':
474 + case 'rtsb-coupon-layout1':
475 + case 'rtsb-coupon-layout2':
476 + case 'rtsb-coupon-layout3':
414 477 $columns = 3;
415 478 break;
416 -
479 + case 'rtsb-post-list-layout1':
480 + case 'rtsb-post-list-layout2':
417 481 case 'list-layout1':
418 482 case 'list-layout2':
419 483 case 'list-layout3':
420 484 case 'list-layout7':
@@ -420,9 +484,13 @@
420 484 case 'list-layout7':
421 485 case 'list-layout5':
422 486 $columns = 1;
423 487 break;
424 -
488 + case 'rtsb-team-layout4':
489 + case 'rtsb-testimonial-layout1':
490 + case 'rtsb-testimonial-layout3':
491 + case 'rtsb-testimonial-layout4':
492 + case 'rtsb-testimonial-layout5':
425 493 case 'list-layout4':
426 494 case 'list-layout6':
427 495 case 'slider-layout5':
428 496 case 'slider-layout8':
@@ -427,10 +495,12 @@
427 495 case 'slider-layout5':
428 496 case 'slider-layout8':
429 497 $columns = 2;
430 498 break;
431 -
499 + case 'rtsb-logo-layout1':
500 + case 'rtsb-logo-layout2':
432 501 case 'grid-layout6':
502 + case 'grid-layout9':
433 503 $columns = 5;
434 504 break;
435 505
436 506 case 'category-layout1':
@@ -471,9 +541,10 @@
471 541 public static function prepare_container_classes( $metas = [], $settings = [], $custom_class = '' ) {
472 542 $classes = 'rtsb-elementor-container products rtsb-pos-r ';
473 543 $classes .= $custom_class;
474 544
475 - $layout = $metas['layout'];
545 + $raw_layout = esc_html( $metas['layout'] );
546 + $layout = self::filter_layout( $raw_layout );
476 547 $is_cat = preg_match( '/category/', $layout );
477 548 $cart_txt_class = '';
478 549
479 550 $animation = $metas['hover_animation'];
@@ -501,8 +572,11 @@
501 572
502 573 if ( in_array( 'single-cat', $metas['visibility'], true ) ) {
503 574 $classes .= ' show-single-cat';
504 575 }
576 + if ( in_array( 'single-brand', $metas['visibility'], true ) ) {
577 + $classes .= ' show-single-brand';
578 + }
505 579
506 580 if ( ! $is_cat ) {
507 581 $action_btn_classes = [
508 582 'show_compare_text' => 'has-compare-text',
@@ -533,8 +607,36 @@
533 607 return apply_filters( 'rtsb/product/container/class', $classes, $settings );
534 608 }
535 609
536 610 /**
611 + * Function to filter and validate the layout against allowed patterns.
612 + *
613 + * @param string $layout The layout string to be filtered.
614 + * @param string $template The template to be checked.
615 + *
616 + * @return string
617 + */
618 + public static function filter_layout( $layout, $template = '' ) {
619 + $allowed_patterns = apply_filters( 'rtsb/elementor/custom_layout', [ 'grid', 'list', 'slider', 'category', 'toyup', 'zilly', 'rtsb' ] );
620 + $layout_part = explode( '-', $layout );
621 + $default = 'grid-layout1';
622 +
623 + if ( empty( $layout_part[0] ) ) {
624 + return $default;
625 + }
626 +
627 + if ( in_array( $layout_part[0], $allowed_patterns, true ) ) {
628 + if ( ! rtsb()->has_pro() && ! in_array( $layout, array_keys( Fns::free_layouts( $layout ) ), true ) ) {
629 + $layout = self::set_default_layout( $layout );
630 + }
631 +
632 + return $layout;
633 + } else {
634 + return ! empty( $template ) ? self::set_default_layout( $layout ) : $default;
635 + }
636 + }
637 +
638 + /**
537 639 * Row classes
538 640 *
539 641 * @param array $settings Settings array.
540 642 *
@@ -542,11 +644,15 @@
542 644 */
543 645 public static function prepare_row_classes( $settings = [] ) {
544 646 $masonry_class = null;
545 647 $classes = 'rtsb-row rtsb-content-loader element-loading';
546 - $loader_class = ' rtsb-pre-loader';
547 648
548 - $layout = $settings['layout'];
649 + $loader_class = '';
650 + if ( Fns::enable_loader() ) {
651 + $loader_class = ' rtsb-pre-loader';
652 + }
653 + $raw_layout = esc_html( $settings['layout'] );
654 + $layout = self::filter_layout( $raw_layout );
549 655 $grid_type = $settings['grid_type'];
550 656 $is_carousel = preg_match( '/slider/', $layout );
551 657
552 658 if ( preg_match( '/category/', $layout ) && ! empty( $settings['active_cat_slider'] ) ) {
@@ -552,21 +658,31 @@
552 658 if ( preg_match( '/category/', $layout ) && ! empty( $settings['active_cat_slider'] ) ) {
553 659 $classes .= ' rtsb-slider-layout';
554 660 }
555 661
556 - if ( 'even' === $grid_type ) {
557 - $masonry_class = ' rtsb-even';
662 + if ( 'equal' === $grid_type ) {
663 + $masonry_class = ' rtsb-equal';
558 664 } elseif ( 'masonry' === $grid_type ) {
559 665 $masonry_class = ' rtsb-masonry';
666 + } else {
667 + $masonry_class = ' rtsb-even';
560 668 }
561 669
562 670 if ( ! rtsb()->has_pro() || $is_carousel ) {
563 671 $masonry_class = ' rtsb-even';
564 672 }
673 +
565 674 if ( $is_carousel && ! empty( $settings['raw_settings']['always_show_nav'] ) ) {
566 675 $classes .= ' always-show-nav';
567 676 }
568 677
678 + if ( ! empty( $settings['raw_settings']['inner_slider_always_show_nav'] ) ) {
679 + $classes .= ' inner-slider-always-show-nav';
680 + }
681 + if ( empty( $settings['raw_settings']['cols_mobile'] ) ) {
682 + $classes .= ' rtsb-mobile-flex-row';
683 + }
684 +
569 685 $classes .= ' rtsb-' . $layout . $masonry_class . $loader_class;
570 686
571 687 return apply_filters( 'rtsb/elementor/row/classes', $classes, $settings );
572 688 }
@@ -573,13 +689,13 @@
573 689
574 690 /**
575 691 * Prepare pagination attributes.
576 692 *
577 - * @param string $layout The layout type.
693 + * @param string $layout The layout type.
578 694 * @param bool $has_filters Whether the widget has filters.
579 - * @param string $template The template name.
580 - * @param array $settings The widget settings.
581 - * @param bool $ajax Whether to enable AJAX pagination.
695 + * @param string $template The template name.
696 + * @param array $settings The widget settings.
697 + * @param bool $ajax Whether to enable AJAX pagination.
582 698 *
583 699 * @return string
584 700 */
585 701 public static function prepare_pagination_attr( $layout, $has_filters, $template, $settings, $ajax ) {
@@ -591,9 +707,9 @@
591 707 } elseif ( $is_cat ) {
592 708 return '';
593 709 } else {
594 710 $ajax_pagination = ! empty( $settings['show_pagination'] ) && 'pagination' !== $settings['pagination_type'];
595 - $page = Fns::product_filters_has_ajax( 'shop' ) || Fns::product_filters_has_ajax( 'archive' );
711 + $page = Fns::product_filters_has_ajax( apply_filters( 'rtsb/builder/set/current/page/type', '' ) );
596 712 $condition = rtsb()->has_pro() && $ajax && ( $ajax_pagination || $has_filters || $page );
597 713
598 714 return $condition ? self::pagination_data( $settings, $template ) : '';
599 715 }
@@ -621,15 +737,16 @@
621 737 *
622 738 * @return array
623 739 */
624 740 public static function slider_data( array $meta, $settings = [] ) {
625 - $has_dots = $meta['slider_dot'] ? ' has-dot' : ' no-dot';
626 - $has_dots .= $meta['slider_nav'] ? ' has-nav' : ' no-nav';
627 - $d_col = 0 === $meta['d_cols'] ? self::default_columns( $meta['layout'] ) : $meta['d_cols'];
628 - $t_col = 0 === $meta['t_cols'] ? 2 : $meta['t_cols'];
629 - $m_col = 0 === $meta['m_cols'] ? 1 : $meta['m_cols'];
741 + $has_dots = $meta['slider_dot'] ? ' has-dot' : ' no-dot';
742 + $has_dots .= $meta['slider_nav'] ? ' has-nav' : ' no-nav';
743 + $has_dynamic_dots = $meta['slider_dynamic_dot'] ? true : false;
744 + $d_col = 0 === $meta['d_cols'] ? self::default_columns( $meta['layout'] ) : $meta['d_cols'];
745 + $t_col = 0 === $meta['t_cols'] ? 2 : $meta['t_cols'];
746 + $m_col = 0 === $meta['m_cols'] ? 1 : $meta['m_cols'];
630 747
631 - if ( \Elementor\Plugin::$instance->breakpoints->has_custom_breakpoints() ) {
748 + if ( Plugin::$instance->breakpoints->has_custom_breakpoints() ) {
632 749 // Widescreen.
633 750 $w_col = self::get_data( $settings, 'cols_widescreen' );
634 751 $w_col = 0 === $w_col ? self::default_columns( $meta['layout'] ) : $w_col;
635 752 $w_group = self::get_data( $settings, 'cols_group_widescreen', 1 );
@@ -660,10 +777,13 @@
660 777 $breakpoints = [
661 778 0 => [
662 779 'slidesPerView' => absint( $m_col ),
663 780 'slidesPerGroup' => absint( $meta['m_group'] ),
781 + 'grid' => [
782 + 'rows' => 1,
783 + ],
664 784 'pagination' => [
665 - 'dynamicBullets' => true,
785 + 'dynamicBullets' => $has_dynamic_dots,
666 786 ],
667 787 ],
668 788 ];
669 789
@@ -688,16 +808,16 @@
688 808 }
689 809
690 810 foreach ( $el_breakpoints as $el_breakpoint => $value ) {
691 811 $breakpoint_value = ! empty( $value['value'] ) ? $value['value'] : $value['default_value'];
692 - $dynamic_bullets = false;
812 + $dynamic_bullets = $has_dynamic_dots;
693 813 $slides_per_view = $d_col;
694 814 $slides_per_group = $meta['d_group'];
695 815
696 816 switch ( $el_breakpoint ) {
697 817 case 'widescreen':
698 - $slides_per_view = $w_col;
699 - $slides_per_group = $w_group;
818 + $slides_per_view = $w_col;
819 + $slides_per_group = $w_group;
700 820
701 821 break;
702 822
703 823 case 'laptop':
@@ -706,10 +826,10 @@
706 826
707 827 break;
708 828
709 829 case 'tablet_extra':
710 - $slides_per_view = $l_col;
711 - $slides_per_group = $l_group;
830 + $slides_per_view = $l_col;
831 + $slides_per_group = $l_group;
712 832
713 833 if ( empty( $el_breakpoints['laptop'] ) ) {
714 834 $slides_per_view = $d_col;
715 835 $slides_per_group = $meta['d_group'];
@@ -734,11 +854,11 @@
734 854
735 855 break;
736 856
737 857 case 'mobile_extra':
738 - $slides_per_view = $t_col;
739 - $slides_per_group = $meta['t_group'];
740 - $dynamic_bullets = true;
858 + $slides_per_view = $t_col;
859 + $slides_per_group = $meta['t_group'];
860 + $dynamic_bullets = $has_dynamic_dots;
741 861
742 862 break;
743 863
744 864 case 'mobile':
@@ -743,9 +863,9 @@
743 863
744 864 case 'mobile':
745 865 $slides_per_view = $t_col;
746 866 $slides_per_group = $meta['t_group'];
747 - $dynamic_bullets = true;
867 + $dynamic_bullets = $has_dynamic_dots;
748 868
749 869 if ( ! empty( $el_breakpoints['mobile_extra'] ) ) {
750 870 $slides_per_view = $me_col;
751 871 $slides_per_group = $me_group;
@@ -771,9 +891,9 @@
771 891 0 => [
772 892 'slidesPerView' => absint( $m_col ),
773 893 'slidesPerGroup' => absint( $meta['m_group'] ),
774 894 'pagination' => [
775 - 'dynamicBullets' => true,
895 + 'dynamicBullets' => $has_dynamic_dots,
776 896 ],
777 897 ],
778 898 768 => [
779 899 'slidesPerView' => absint( $t_col ),
@@ -778,9 +898,9 @@
778 898 768 => [
779 899 'slidesPerView' => absint( $t_col ),
780 900 'slidesPerGroup' => absint( $meta['t_group'] ),
781 901 'pagination' => [
782 - 'dynamicBullets' => false,
902 + 'dynamicBullets' => $has_dynamic_dots,
783 903 ],
784 904 ],
785 905 1025 => [
786 906 'slidesPerView' => absint( $d_col ),
@@ -785,9 +905,9 @@
785 905 1025 => [
786 906 'slidesPerView' => absint( $d_col ),
787 907 'slidesPerGroup' => absint( $meta['d_group'] ),
788 908 'pagination' => [
789 - 'dynamicBullets' => false,
909 + 'dynamicBullets' => $has_dynamic_dots,
790 910 ],
791 911 ],
792 912 ];
793 913 }
@@ -837,9 +957,11 @@
837 957
838 958 if ( $is_carousel ) {
839 959 $loader_class = ' full-op';
840 960 }
841 -
961 + if ( ! Fns::enable_loader() ) {
962 + return '';
963 + }
842 964 return '<div class="rtsb-elements-loading rtsb-ball-clip-rotate"><div></div></div>';
843 965 }
844 966
845 967 /**
@@ -880,10 +1002,11 @@
880 1002 *
881 1003 * @return void
882 1004 */
883 1005 public static function register_scripts( $scripts ) {
884 - $caro = false;
885 - $script = [];
1006 + $caro = false;
1007 + $masonry = false;
1008 + $script = [];
886 1009
887 1010 $script[] = 'jquery';
888 1011
889 1012 foreach ( $scripts as $sc => $value ) {
@@ -890,8 +1013,12 @@
890 1013 if ( ! empty( $sc ) ) {
891 1014 if ( 'isCarousel' === $sc ) {
892 1015 $caro = $value;
893 1016 }
1017 +
1018 + if ( 'isMasonry' === $sc ) {
1019 + $masonry = $value;
1020 + }
894 1021 }
895 1022 }
896 1023
897 1024 if ( count( $scripts ) ) {
@@ -901,11 +1028,15 @@
901 1028 if ( $caro ) {
902 1029 $script[] = 'swiper';
903 1030 }
904 1031
905 - $script[] = 'rtsb-imagesloaded';
906 - $script[] = 'rtsb-public';
1032 + if ( $masonry ) {
1033 + $script[] = 'masonry';
1034 + }
907 1035
1036 + $script[] = 'imagesloaded';
1037 + $script[] = Fns::optimized_handle( 'rtsb-public' );
1038 +
908 1039 foreach ( $script as $sc ) {
909 1040 wp_enqueue_script( $sc );
910 1041 }
911 1042 }
@@ -947,6 +1078,827 @@
947 1078 $products_col = absint( get_option( 'woocommerce_catalog_columns', 4 ) );
948 1079 $products_per_page = apply_filters( 'rtsb/elementor/archive/products_per_page', $products_row * $products_col );
949 1080
950 1081 return ! empty( $products_per_page ) ? $products_per_page : 12;
1082 + }
1083 + /**
1084 + * Render Filters View.
1085 + *
1086 + * @param array $templates Template name.
1087 + * @param array $settings Control settings.
1088 + *
1089 + * @return string
1090 + */
1091 + public static function filters_view( $templates, $settings ) {
1092 + global $wp;
1093 + $filters = [];
1094 + $html = null;
1095 + $reset = ! empty( $settings['reset_btn'] );
1096 + $reset_mode = 'show' === $settings['reset_btn_behavior'] ? ' show-reset' : ' ondemand-reset';
1097 + $reset_text = ! empty( $settings['reset_btn_text'] ) ? $settings['reset_btn_text'] : esc_html__( 'Reset Filter', 'shopbuilder' );
1098 + $scroll_mode = ! empty( $settings['enable_scroll'] ) ? ' has-scroll' : ' no-scroll';
1099 + $scroll_height = ! empty( $settings['enable_scroll'] ) ? $settings['scroll_height']['size'] : 300;
1100 + $has_mobile_toggle = ! empty( $settings['filter_mobile_toggle'] );
1101 + $toggle_class = $has_mobile_toggle ? ' default-filter-has-toggle' : ' default-filter-no-toggle';
1102 + $mobile_toggle_text = ! empty( $settings['filter_mobile_toggle_text'] ) ? $settings['filter_mobile_toggle_text'] : esc_html__( 'Click to Filter', 'shopbuilder' );
1103 + $scroll_attr = '';
1104 +
1105 + if ( ! empty( $settings['enable_scroll'] ) ) {
1106 + $scroll_attr = ' style="--rtsb-filter-scroll-height: ' . absint( $scroll_height ) . 'px;"';
1107 + }
1108 +
1109 + foreach ( $settings['filter_types'] as $key => $item ) {
1110 + if ( ! defined( 'RTWPVS_PLUGIN_FILE' ) && in_array( $item['input_type_all'], [ 'color', 'button', 'image' ], true ) ) {
1111 + $item['input_type_all'] = 'checkbox';
1112 + }
1113 + $filters[] = [
1114 + 'filter_title' => $item['filter_title'],
1115 + 'filter_items' => $item['filter_items'],
1116 + 'filter_attr' => $item['filter_attr'],
1117 + 'input_type' => 'product_attr' === $item['filter_items'] ? $item['input_type_all'] : $item['input_type'],
1118 + ];
1119 +
1120 + if ( 'rating_filter' === $item['filter_items'] ) {
1121 + $filters[ $key ]['template'] = $templates['rating'];
1122 + } elseif ( 'price_filter' === $item['filter_items'] ) {
1123 +
1124 + $filters[ $key ]['input_type'] = 'slider';
1125 + $filters[ $key ]['template'] = $templates['price'];
1126 + } else {
1127 + $filters[ $key ]['template'] = 'product_attr' === $item['filter_items'] ? $templates[ $item['input_type_all'] ] : $templates[ $item['input_type'] ];
1128 + }
1129 + }
1130 +
1131 + if ( '' === get_option( 'permalink_structure' ) ) {
1132 + $form_action = remove_query_arg( [ 'page', 'paged', 'product-page' ], add_query_arg( $wp->query_string, '', home_url( $wp->request ) ) );
1133 + } else {
1134 + $form_action = preg_replace( '%\/page/[0-9]+%', '', home_url( trailingslashit( $wp->request ) ) );
1135 + }
1136 + $args = [
1137 + 'filters' => $filters,
1138 + 'scroll_mode' => $scroll_mode,
1139 + 'reset_mode' => $reset_mode,
1140 + 'reset' => $reset,
1141 + 'scroll_attr' => $scroll_attr,
1142 + 'toggle_class' => $toggle_class,
1143 + 'reset_text' => $reset_text,
1144 + 'settings' => $settings,
1145 + 'form_action' => $form_action,
1146 + ];
1147 +
1148 + $html .= '<div class="rtsb-default-archive-filters' . esc_attr( $toggle_class ) . '">';
1149 +
1150 + if ( $has_mobile_toggle ) {
1151 + $html .= '<div class="rtsb-filter-mobile-toggle">
1152 + <button class="product-filter-toggle">
1153 + <span class="icon"><i aria-hidden="true" class="toggle-icon rtsb-icon rtsb-icon-filter"></i></span>
1154 + <span class="text reset-text">' . esc_html( $mobile_toggle_text ) . '</span>
1155 + <span></span>
1156 + </button>
1157 + </div>';
1158 + }
1159 +
1160 + $html .= Fns::load_template( $templates['layout'], $args, true );
1161 + $html .= '</div>';
1162 + return $html;
1163 + }
1164 + /**
1165 + * Get taxonomy type.
1166 + *
1167 + * @param string $tax_type Taxonomy type.
1168 + * @param string $attr_type Attribute type.
1169 + *
1170 + * @return string
1171 + */
1172 + public static function get_product_filters_tax_type( $tax_type, $attr_type = '' ) {
1173 + if ( 'product_attr' !== $tax_type ) {
1174 + return $tax_type;
1175 + }
1176 +
1177 + if ( empty( $attr_type ) ) {
1178 + return $tax_type;
1179 + }
1180 +
1181 + return $attr_type;
1182 + }
1183 + /**
1184 + * Get attribute terms.
1185 + *
1186 + * @param string $tax Taxonomy type.
1187 + * @return int[]|string|string[]|\WP_Error|\WP_Term[]
1188 + */
1189 + public static function get_product_filters_attribute_terms( $tax ) {
1190 + $cache_key = 'get_default_filter_attribute_terms_' . $tax;
1191 + if ( isset( self::$cache[ $cache_key ] ) ) {
1192 + return self::$cache[ $cache_key ];
1193 + }
1194 +
1195 + $attributes = wc_get_attribute_taxonomies();
1196 + $att_name = str_replace( 'pa_', '', $tax );
1197 + $exist = array_search( $att_name, array_column( $attributes, 'attribute_name' ), true );
1198 + if ( false === $exist ) {
1199 + self::$cache[ $cache_key ] = [];
1200 + return self::$cache[ $cache_key ];
1201 + }
1202 + $attribute = $tax;
1203 +
1204 + if ( ! is_shop() && is_product_taxonomy() ) {
1205 + $term = get_queried_object();
1206 +
1207 + if ( ! $term instanceof WP_Term ) {
1208 + return [];
1209 + }
1210 +
1211 + $args = [
1212 + 'status' => 'publish',
1213 + 'limit' => -1,
1214 + 'return' => 'ids',
1215 + ];
1216 +
1217 + if ( 'product_cat' === $term->taxonomy ) {
1218 + $args['category'] = [ $term->slug ];
1219 + }
1220 +
1221 + if ( 'product_tag' === $term->taxonomy ) {
1222 + $args['tag'] = [ $term->slug ];
1223 + }
1224 +
1225 + if ( strpos( $term->taxonomy, 'pa_' ) !== false ) {
1226 + $args['tax_query'] = [ // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
1227 + [
1228 + 'taxonomy' => $term->taxonomy,
1229 + 'field' => 'term_id',
1230 + 'terms' => $term->term_id,
1231 + 'operator' => 'IN',
1232 + ],
1233 + ];
1234 + }
1235 +
1236 + $query = new WC_Product_Query( $args );
1237 + $products = $query->get_products();
1238 + $attr_terms = [];
1239 +
1240 + foreach ( $products as $product_id ) {
1241 + $product_attr = wp_get_post_terms( $product_id, $attribute );
1242 +
1243 + foreach ( $product_attr as $attr ) {
1244 + $attr_terms[] = $attr;
1245 + }
1246 + }
1247 + self::$cache[ $cache_key ] = self::filter_products_array_unique( $attr_terms );
1248 + return self::$cache[ $cache_key ];
1249 + } else {
1250 + self::$cache[ $cache_key ] = get_terms(
1251 + [
1252 + 'taxonomy' => $attribute,
1253 + 'hide_empty' => false,
1254 + ]
1255 + );
1256 + return self::$cache[ $cache_key ];
1257 + }
1258 + }
1259 + /**
1260 + * Remove duplicate values from an array.
1261 + *
1262 + * @param array $array The input array.
1263 + * @param bool $keep_key_assoc Whether to keep the key associations after removing duplicates.
1264 + * @return array The array with duplicates removed.
1265 + */
1266 + public static function filter_products_array_unique( $array, $keep_key_assoc = false ) {
1267 + $duplicate_keys = [];
1268 + $tmp = [];
1269 +
1270 + foreach ( $array as $key => $val ) {
1271 + // convert objects to arrays, in_array() does not support objects.
1272 + if ( is_object( $val ) ) {
1273 + $val = (array) $val;
1274 + }
1275 +
1276 + if ( ! in_array( $val, $tmp, true ) ) {
1277 + $tmp[] = $val;
1278 + } else {
1279 + $duplicate_keys[] = $key;
1280 + }
1281 + }
1282 +
1283 + foreach ( $duplicate_keys as $key ) {
1284 + unset( $array[ $key ] );
1285 + }
1286 +
1287 + return $keep_key_assoc ? $array : array_values( $array );
1288 + }
1289 + /**
1290 + * Retrieve product categories (including hierarchical support)
1291 + *
1292 + * @param string $taxonomy Taxonomy name.
1293 + * @return int[]|string|string[]|\WP_Error|\WP_Term[]
1294 + */
1295 + public static function get_all_terms( $taxonomy ) {
1296 + $args = [
1297 + 'taxonomy' => $taxonomy,
1298 + 'suppress_filter' => true,
1299 + 'hide_empty' => false,
1300 + 'pad_counts' => true,
1301 + 'hierarchical' => true,
1302 + ];
1303 +
1304 + $cache_key = 'rtsb_get_terms_filter_products' . $taxonomy;
1305 + $taxonomy_terms = wp_cache_get( $cache_key, 'shopbuilder' );
1306 +
1307 + if ( false === $taxonomy_terms ) {
1308 + $queried_object = get_queried_object();
1309 +
1310 + if ( 'archive' === apply_filters( 'rtsb/builder/set/current/page/type', '' ) && $queried_object instanceof WP_Term && $queried_object->taxonomy === $taxonomy ) {
1311 + $term_id = $queried_object->term_id;
1312 + $taxonomy = $queried_object->taxonomy;
1313 + $archive_cache_key = 'rtsb_get_terms_filter_products_' . $taxonomy . $term_id;
1314 + $taxonomy_terms = wp_cache_get( $archive_cache_key, 'shopbuilder' );
1315 + if ( false === $taxonomy_terms ) {
1316 + $taxonomy_terms = [ $queried_object ];
1317 + $child_args = [
1318 + 'taxonomy' => $taxonomy,
1319 + 'child_of' => $term_id,
1320 + 'hide_empty' => false,
1321 + ];
1322 +
1323 + $child_terms = get_terms( $child_args );
1324 +
1325 + if ( ! empty( $child_terms ) ) {
1326 + $taxonomy_terms = array_merge( $taxonomy_terms, $child_terms );
1327 + }
1328 +
1329 + wp_cache_set( $archive_cache_key, $taxonomy_terms, 'shopbuilder' );
1330 + Cache::set_data_cache_key( $archive_cache_key );
1331 + }
1332 + } else {
1333 + $taxonomy_terms = get_terms( $args );
1334 + }
1335 +
1336 + wp_cache_set( $cache_key, $taxonomy_terms, 'shopbuilder' );
1337 + Cache::set_data_cache_key( $cache_key );
1338 + }
1339 +
1340 + return $taxonomy_terms;
1341 + }
1342 + /**
1343 + * Generates an error message block.
1344 + *
1345 + * @param array $title The title array of the error message block.
1346 + * @param string $wrapper Additional CSS class for the wrapper div.
1347 + *
1348 + * @return string The HTML representation of the error message block.
1349 + */
1350 + public static function filter_error_message( $title, $wrapper ) {
1351 + $html = '<div class="rtsb-product-default-filters ' . esc_attr( $wrapper ) . '">';
1352 + $html .= self::get_default_filter_title( $title );
1353 + $html .= '<div class="default-filter-content">';
1354 + $html .= '<p class="no-filter">' . esc_html__( 'Nothing found.', 'shopbuilder' ) . '</p>';
1355 + $html .= '</div>';
1356 + $html .= '</div>';
1357 +
1358 + return $html;
1359 + }
1360 + /**
1361 + * Get filter title.
1362 + *
1363 + * @param array $title Filter title.
1364 + * @return string
1365 + */
1366 + public static function get_default_filter_title( $title ) {
1367 + $html = '';
1368 +
1369 + if ( empty( $title['title'] ) ) {
1370 + return $html;
1371 + }
1372 +
1373 + $html .= '<div class="default-filter-title-wrapper">';
1374 +
1375 + $html .= '<h3 class="widget-title rtsb-d-flex rtsb-align-items-center"><span class="title">' . $title['title'] . '</span></h3>';
1376 +
1377 + $html .= '</div>';
1378 +
1379 + return apply_filters( 'rtsb/elementor/default/filter/title', $html, $title );
1380 + }
1381 + /**
1382 + * Recursive function to generate the filter list with hierarchy.
1383 + *
1384 + * @param array $data Array of required data.
1385 + * @param string $input Type of input field for the filter (e.g., checkbox).
1386 + * @param array $additional_data Data for additional filtering.
1387 + * @param int $parent ID of the parent term (default: 0).
1388 + *
1389 + * @return string The generated HTML for the product filter list.
1390 + */
1391 + public static function get_product_default_filter_list_html( $data, $input = 'checkbox', $additional_data = [], $parent = 0 ) {
1392 +
1393 + $html = '';
1394 + $default_tax = [];
1395 + $is_attribute = ! empty( $data['is_attribute'] );
1396 +
1397 + $param_key = isset( $additional_data['taxonomy'] ) ? $additional_data['taxonomy'] : '';
1398 + if ( $param_key && 0 === strpos( $param_key, 'pa_' ) ) {
1399 + $param_key = 'filter_' . substr( $param_key, 3 );
1400 + }
1401 +
1402 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
1403 + $raw_values = ( $param_key && isset( $_GET[ $param_key ] ) ) ? wc_clean( wp_unslash( $_GET[ $param_key ] ) ) : '';
1404 + $active_option = $raw_values ? array_map( 'urldecode', explode( ',', $raw_values ) ) : [];
1405 + $counter = 0;
1406 +
1407 + if ( $is_attribute ) {
1408 + list(
1409 + 'filter_name' => $filter_name,
1410 + 'base_link' => $base_link
1411 + ) = self::get_product_filters_attribute_term_info( $additional_data['taxonomy'] );
1412 + }
1413 +
1414 + $html .= '<ul class="product-default-filters input-type-' . esc_attr( $input ) . '">';
1415 +
1416 + foreach ( $data['taxonomy'] as $tax ) {
1417 + if ( $tax->parent !== $parent ) {
1418 + continue;
1419 + }
1420 +
1421 + if ( $is_attribute ) {
1422 + $tax_link = remove_query_arg( $filter_name, $base_link );
1423 + $tax_link = add_query_arg( $filter_name, $tax->slug, $tax_link );
1424 + } else {
1425 + $tax_link = get_term_link( $tax );
1426 + }
1427 +
1428 + $unique_id = substr( uniqid(), -4 );
1429 +
1430 + $decoded_slug = urldecode( $tax->slug );
1431 + $is_active = in_array( $decoded_slug, $active_option, true ) || in_array( $tax->slug, $active_option, true );
1432 + $active_tax = $is_active ? ' checked' : '';
1433 + $active_tax_parent = $is_active ? ' active' : '';
1434 + $term_children = in_array( $tax->taxonomy, [ 'product_cat', 'product_brand' ], true ) ? get_term_children( $tax->term_id, $tax->taxonomy ) : [];
1435 + $taxonomy = $tax->taxonomy;
1436 + $product_count = self::get_visible_term_count( $tax->taxonomy, $tax->term_id );
1437 +
1438 + // Show count.
1439 + if ( 'archive' === apply_filters( 'rtsb/builder/set/current/page/type', '' ) && ! empty( $data['count_display'] ) && 'block' === $data['count_display'] ) {
1440 + $product_count = self::count_tax_data( $tax, $product_count );
1441 + }
1442 +
1443 + // Show Empty Categories.
1444 + if ( empty( $data['show_empty_terms'] ) && 0 === $product_count ) {
1445 + continue;
1446 + }
1447 +
1448 + $html .= '<li class="rtsb-default-filter-term-item term-item-' . absint( $tax->term_id ) . esc_attr( $term_children ? ' term-has-children' : '' ) . ( ! $data['show_child'] ? ' child-terms-hidden' : '' ) . '">
1449 + <div class="rtsb-default-filter-group' . esc_attr( $active_tax_parent ) . '">
1450 + <input type="' . esc_attr( $input ) . '" class="rtsb-default-filter-trigger rtsb-' . esc_attr( $input . '-filter rtsb-term-' . $tax->slug . $active_tax ) . '" name="rtsb-filter-' . esc_attr( $taxonomy ) . '[]" id="rtsb-default-term-filter-' . $unique_id . absint( $tax->term_id ) . '" value="' . esc_attr( $tax->slug ) . '" ' . esc_attr( $active_tax ) . '>
1451 + <label class="rtsb-default-' . esc_attr( $input ) . '-filter-label" for="rtsb-default-term-filter-' . $unique_id . absint( $tax->term_id ) . '">
1452 + <span class="name">' . esc_html( $tax->name ) . '</span>
1453 + </label>
1454 + <div class="rtsb-product-count">(' . esc_html( $product_count ) . ')</div>
1455 + ' . ( ! empty( $term_children ) && $data['show_child'] ? '<i class="rtsb-plus-icon"></i>' : '' ) . '
1456 + </div>';
1457 +
1458 + // Show Sub-categories.
1459 + if ( $data['show_child'] ) {
1460 + $children = self::get_product_default_filter_list_html( $data, $input, $additional_data, $tax->term_id );
1461 +
1462 + if ( ! empty( $children ) ) {
1463 + $html .= '<div class="filter-child">' . $children . '</div>';
1464 + }
1465 + }
1466 +
1467 + $counter++;
1468 +
1469 + $html .= '</li>';
1470 + }
1471 +
1472 + $html .= '</ul>';
1473 +
1474 + // Check if the output contains any child elements.
1475 + $has_children = strpos( $html, '<li class="rtsb-default-filter-term-item' ) !== false;
1476 +
1477 + if ( ! $has_children ) {
1478 + // Remove the outer <ul> if there are no child elements.
1479 + $html = '';
1480 + }
1481 + return $html;
1482 + }
1483 + /**
1484 + * Get attribute term info.
1485 + *
1486 + * @param string $tax Taxonomy type.
1487 + * @return array
1488 + */
1489 + public static function get_product_filters_attribute_term_info( $tax ) {
1490 + $result = [];
1491 + $attributes = wc_get_attribute_taxonomies();
1492 +
1493 + foreach ( $attributes as $attr ) {
1494 + if ( str_replace( 'pa_', '', $tax ) === $attr->attribute_name ) {
1495 + $term_type = $attr->attribute_type;
1496 + $attribute_slug = $attr->attribute_name;
1497 + $attribute = wc_attribute_taxonomy_name( $attr->attribute_name );
1498 + break;
1499 + }
1500 + }
1501 +
1502 + $filter_name = 'filter_' . wc_attribute_taxonomy_slug( $attribute_slug );
1503 +
1504 + $result['attribute'] = $attribute ?? '';
1505 + $result['term_type'] = $term_type ?? '';
1506 + $result['attribute_slug'] = $attribute_slug ?? '';
1507 + $result['filter_name'] = $filter_name;
1508 + $result['base_link'] = self::get_base_url();
1509 +
1510 + return $result;
1511 + }
1512 + /**
1513 + * Get base URL.
1514 + *
1515 + * @return string|null
1516 + */
1517 + public static function get_base_url() {
1518 + global $wp;
1519 + return home_url( add_query_arg( [], $wp->request ) );
1520 + }
1521 + /**
1522 + * Count the number of occurrences for a specific term in a taxonomy for the current queried object.
1523 + *
1524 + * @param object|array $tax The term object for the taxonomy.
1525 + * @param int $count The initial count value.
1526 + *
1527 + * @return int The updated count value.
1528 + */
1529 + public static function count_tax_data( $tax, $count ) {
1530 + $page = get_queried_object();
1531 +
1532 + if ( is_array( $tax ) && strpos( $tax['taxonomy'], 'attribute_' ) !== false ) {
1533 + $taxonomy = str_replace( 'attribute_', '', $tax['taxonomy'] );
1534 + $term_id = $tax['term_id'];
1535 + } else {
1536 + $taxonomy = $tax->taxonomy;
1537 + $term_id = $tax->term_id;
1538 + }
1539 +
1540 + if ( strpos( $page->taxonomy, 'pa_' ) !== false ) {
1541 + $args['status'] = 'publish';
1542 + $args['limit'] = -1;
1543 + $args['return'] = 'ids';
1544 + $args['tax_query'] = [ // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
1545 + 'relation' => 'AND',
1546 + [
1547 + 'taxonomy' => $page->taxonomy,
1548 + 'field' => 'term_id',
1549 + 'terms' => $page->term_id,
1550 + 'operator' => 'IN',
1551 + ],
1552 + [
1553 + 'taxonomy' => $tax->taxonomy,
1554 + 'field' => 'term_id',
1555 + 'terms' => $tax->term_id,
1556 + 'operator' => 'IN',
1557 + ],
1558 + [
1559 + 'taxonomy' => 'product_visibility',
1560 + 'terms' => [ 'exclude-from-catalog' ],
1561 + 'field' => 'name',
1562 + 'operator' => 'NOT IN',
1563 + ],
1564 + ];
1565 +
1566 + $query = new WC_Product_Query( $args );
1567 + $products = $query->get_products();
1568 +
1569 + return count( $products );
1570 + }
1571 +
1572 + if ( $taxonomy !== $page->taxonomy ) {
1573 + $args = [
1574 + 'limit' => -1,
1575 + 'return' => 'ids',
1576 + 'status' => 'publish',
1577 + 'tax_query' => [ // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
1578 + 'relation' => 'AND',
1579 + [
1580 + 'taxonomy' => $taxonomy,
1581 + 'field' => 'term_id',
1582 + 'terms' => $term_id,
1583 + ],
1584 + [
1585 + 'taxonomy' => 'product_visibility',
1586 + 'terms' => [ 'exclude-from-catalog' ],
1587 + 'field' => 'name',
1588 + 'operator' => 'NOT IN',
1589 + ],
1590 + ],
1591 + ];
1592 +
1593 + if ( 'product_cat' === $page->taxonomy ) {
1594 + $args['category'] = [ $page->slug ];
1595 + }
1596 +
1597 + if ( 'product_tag' === $page->taxonomy ) {
1598 + $args['tag'] = [ $page->slug ];
1599 + }
1600 +
1601 + $query = new WC_Product_Query( $args );
1602 + $products = $query->get_products();
1603 +
1604 + $count = count( $products );
1605 + }
1606 +
1607 + return $count;
1608 + }
1609 +
1610 + /**
1611 + * Get the count of visible (non-hidden) products for a given taxonomy term.
1612 + *
1613 + * Excludes products with WooCommerce visibility set to 'Hidden'
1614 + * (products assigned the 'exclude-from-catalog' term).
1615 + *
1616 + * @param string $taxonomy The taxonomy name.
1617 + * @param int $term_id The term ID.
1618 + *
1619 + * @return int Visible product count.
1620 + */
1621 + public static function get_visible_term_count( $taxonomy, $term_id ) {
1622 + static $cache = [];
1623 +
1624 + $cache_key = $taxonomy . '_' . $term_id;
1625 +
1626 + if ( isset( $cache[ $cache_key ] ) ) {
1627 + return $cache[ $cache_key ];
1628 + }
1629 +
1630 + $args = [
1631 + 'limit' => -1,
1632 + 'return' => 'ids',
1633 + 'status' => 'publish',
1634 + 'tax_query' => [ // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
1635 + 'relation' => 'AND',
1636 + [
1637 + 'taxonomy' => $taxonomy,
1638 + 'field' => 'term_id',
1639 + 'terms' => $term_id,
1640 + ],
1641 + [
1642 + 'taxonomy' => 'product_visibility',
1643 + 'terms' => [ 'exclude-from-catalog' ],
1644 + 'field' => 'name',
1645 + 'operator' => 'NOT IN',
1646 + ],
1647 + ],
1648 + ];
1649 +
1650 + $query = new WC_Product_Query( $args );
1651 + $count = count( $query->get_products() );
1652 +
1653 + $cache[ $cache_key ] = $count;
1654 +
1655 + return $count;
1656 + }
1657 +
1658 + /**
1659 + * Get filter HTML.
1660 + *
1661 + * @param string $filter_type Filter type.
1662 + * @param array $options Options.
1663 + * @param array $additional_data Data for additional filtering.
1664 + * @param string $input Input type.
1665 + *
1666 + * @return string The generated HTML for the attribute filter list.
1667 + */
1668 + public static function get_default_filter_html( $filter_type, $options, $additional_data = [], $input = 'checkbox' ) {
1669 + $html = '<ul class="product-filters input-type-' . esc_attr( $input ) . '" ';
1670 + $active_option = isset( $_GET[ $filter_type ] ) ? explode( ',', wc_clean( wp_unslash( $_GET[ $filter_type ] ) ) ) : []; // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
1671 + $option_link = self::get_base_url() . '/' . $filter_type . '/';
1672 +
1673 + foreach ( $options as $option => $option_name ) {
1674 + $unique_id = substr( uniqid(), -6 );
1675 + $active_tax = in_array( $option, $active_option, true ) ? ' checked' : '';
1676 + $active_group = in_array( $option, $active_option, true ) ? ' active' : '';
1677 +
1678 + $html .= '
1679 + <li class="rtsb-term-item">
1680 + <div class="rtsb-default-filter-group' . esc_attr( $active_group ) . '">
1681 + <input type="' . esc_attr( $input ) . '" class="rtsb-default-filter-trigger rtsb-' . esc_attr( $input ) . '-filter rtsb-term-' . esc_attr( $option ) . esc_attr( $active_tax ) . '" name="rtsb-filter-' . esc_attr( $filter_type ) . '[]" id="rtsb-term-default-filter-' . $unique_id . '" value="' . esc_attr( $option ) . '" ' . $active_tax . '>
1682 + <label class="rtsb-default-' . esc_attr( $input ) . '-filter-label" for="rtsb-term-default-filter-' . $unique_id . '">
1683 + <span class="name">' . esc_html( $option_name ) . '</span>
1684 + </label>
1685 + </div>
1686 + </li>';
1687 + }
1688 +
1689 + $html .= '</ul>';
1690 +
1691 + return $html;
1692 + }
1693 + /**
1694 + * Get the count of products with a specific star rating.
1695 + *
1696 + * @param int $star_rating The star rating value.
1697 + *
1698 + * @return int
1699 + */
1700 + public static function get_product_rating_count( $star_rating ) {
1701 + $args = [
1702 + 'status' => 'publish',
1703 + 'limit' => -1,
1704 + 'product_rating' => sprintf( '%.1f', $star_rating ),
1705 + ];
1706 +
1707 + if ( is_product_taxonomy() ) {
1708 + $term = get_queried_object();
1709 + $term_type = 'product_cat' === $term->taxonomy ? 'product_category_id' : 'product_tag_id';
1710 +
1711 + if ( $term instanceof WP_Term ) {
1712 + $args[ $term_type ] = [ $term->term_id ];
1713 + }
1714 + }
1715 +
1716 + $query = new WC_Product_Query( $args );
1717 + $products = $query->get_products();
1718 +
1719 + return count( $products );
1720 + }
1721 + /**
1722 + * Get attribute filter HTML.
1723 + *
1724 + * @param array $data Array of required data.
1725 + * @param string $input Type of input field for the filter (e.g., color).
1726 + *
1727 + * @return string The generated HTML for the attribute filter list.
1728 + */
1729 + public static function get_attribute_filter_list_html( $data, $input = 'color' ) {
1730 + if ( ! function_exists( 'rtwpvs' ) ) {
1731 + return '';
1732 + }
1733 +
1734 + $terms = $data['terms'];
1735 + $term_info = $data['term_info'];
1736 + $show_tooltips = $data['show_tooltips'];
1737 + $show_empty_terms = $data['show_empty_terms'];
1738 + $counter = 0;
1739 +
1740 + $html = '<ul class="product-default-filters rtsb-terms-wrapper ' . esc_attr( $term_info['type'] ) . '-variable-wrapper' . esc_attr( $data['show_label'] ) . '">';
1741 +
1742 + foreach ( $terms as $attr_term ) {
1743 + $count = self::get_visible_term_count( $attr_term->taxonomy, $attr_term->term_id );
1744 + $term_link = remove_query_arg( $term_info['filter_name'], $term_info['base_link'] );
1745 + $term_link = add_query_arg( $term_info['filter_name'], $attr_term->slug, $term_link );
1746 + $unique_id = substr( uniqid(), -6 );
1747 + $name = 'term-' . wc_variation_attribute_name( $term_info['attribute'] ) . '-' . $unique_id;
1748 + $decoded_slug = urldecode( $attr_term->slug );
1749 + $is_selected = in_array( $decoded_slug, $term_info['current_filter'], true ) || in_array( $attr_term->slug, $term_info['current_filter'], true );
1750 + $selected_class = $is_selected ? ' selected' : '';
1751 + $selected_item = $is_selected ? ' checked' : '';
1752 + $active_class = $is_selected ? ' active' : '';
1753 +
1754 + $args = [
1755 + 'id' => $name,
1756 + 'name' => $attr_term->name,
1757 + 'link' => $term_link,
1758 + 'type' => $term_info['type'],
1759 + 'term_id' => $attr_term->term_id,
1760 + 'term_slug' => $attr_term->slug,
1761 + 'taxonomy' => wc_variation_attribute_name( $term_info['attribute'] ),
1762 + 'tooltips' => $show_tooltips,
1763 + 'attribute_name' => $term_info['attribute'],
1764 + 'selected_item' => $selected_item,
1765 + ];
1766 +
1767 + if ( 'archive' === apply_filters( 'rtsb/builder/set/current/page/type', '' ) && ! empty( $data['count_display'] ) && 'block' === $data['count_display'] ) {
1768 + $count = self::count_tax_data( $args, $count );
1769 + }
1770 +
1771 + $args['count'] = $count;
1772 +
1773 + if ( ! $show_empty_terms && 0 === $count ) {
1774 + continue;
1775 + }
1776 +
1777 + $html .= '<li class="rtsb-default-filter-term-item term-item-' . esc_attr( $attr_term->term_id ) . ' rtsb-term rtsb-default-' . esc_attr( $input ) . '-term ' . esc_attr( $term_info['type'] ) . '-variable-term-' . esc_attr( $attr_term->slug ) . esc_attr( $selected_class ) . '">';
1778 + $html .= '<div class="rtsb-default-filter-group' . esc_attr( $active_class ) . '">';
1779 + $html .= self::get_input_type_html( $input, $args );
1780 +
1781 + $counter++;
1782 +
1783 + $html .= '</div>';
1784 + $html .= '</li>';
1785 + }
1786 +
1787 + $html .= '</ul>';
1788 +
1789 + return $html;
1790 + }
1791 + /**
1792 + * Get input type HTML.
1793 + *
1794 + * @param string $input Input type.
1795 + * @param array $args Args.
1796 + * @return mixed|string|null
1797 + */
1798 + public static function get_input_type_html( $input, $args ) {
1799 + $html = '';
1800 + $count = $args['count'];
1801 +
1802 + switch ( $input ) {
1803 + case 'color':
1804 + $color = sanitize_hex_color( get_term_meta( $args['term_id'], 'product_attribute_color', true ) );
1805 +
1806 + $html .= sprintf(
1807 + // phpcs:ignore Generic.Strings.UnnecessaryStringConcat.Found
1808 + '<input type="checkbox" id="' . esc_attr( $args['id'] ) . '" value="' . esc_attr( $args['term_slug'] ) . '" class="rtsb-attr-hidden-field" name="rtsb-filter-' . esc_attr( $args['attribute_name'] ) . '[]' . '" ' . esc_attr( $args['selected_item'] ) . '/><label for="%s" title="' . esc_attr( $args['name'] ) . '" class="rtsb-default-filter-trigger rtsb-attr-filter rtsb-color-filter rtsb-term-span rtsb-term-span-%s%s"><span class="default-filter-attr-color" style="background-color:%s;"></span><span class="default-filter-attr-name">%s</span></label><div class="rtsb-count">(%s)</div>',
1809 + esc_attr( $args['id'] ),
1810 + esc_attr( $args['type'] ),
1811 + $args['tooltips'] ? esc_attr( ' tipsy' ) : '',
1812 + esc_attr( $color ),
1813 + esc_html( $args['name'] ),
1814 + absint( $count )
1815 + );
1816 + break;
1817 +
1818 + case 'image':
1819 + if ( ! function_exists( 'rtwpvs' ) ) {
1820 + return $html;
1821 + }
1822 +
1823 + $attachment_id = absint( get_term_meta( $args['term_id'], 'product_attribute_image', true ) );
1824 + $image_size = rtwpvs()->get_option( 'attribute_image_size' );
1825 + $image_url = wp_get_attachment_image_url( $attachment_id, apply_filters( 'rtwpvs_product_attribute_image_size', $image_size ) );
1826 +
1827 + $html .= sprintf(
1828 + // phpcs:ignore Generic.Strings.UnnecessaryStringConcat.Found
1829 + '<input type="checkbox" id="' . esc_attr( $args['id'] ) . '" value="' . esc_attr( $args['term_slug'] ) . '" class="rtsb-attr-hidden-field" name="rtsb-filter-' . esc_attr( $args['attribute_name'] ) . '[]' . '" ' . esc_attr( $args['selected_item'] ) . '/><label for="%s" title="' . esc_attr( $args['name'] ) . '" class="rtsb-default-filter-trigger rtsb-image-filter rtsb-term-span rtsb-term-span-%s%s"><img class="rtsb-default-attr-filter" alt="%s" src="%s" /></label>',
1830 + esc_attr( $args['id'] ),
1831 + esc_attr( $args['type'] ),
1832 + $args['tooltips'] ? esc_attr( ' tipsy' ) : '',
1833 + esc_attr( $args['name'] ),
1834 + esc_url( $image_url )
1835 + );
1836 + break;
1837 +
1838 + case 'button':
1839 + $html .= sprintf(
1840 + // phpcs:ignore Generic.Strings.UnnecessaryStringConcat.Found
1841 + '<input id="' . esc_attr( $args['id'] ) . '" type="checkbox" value="' . esc_attr( $args['term_slug'] ) . '" class="rtsb-attr-hidden-field" name="rtsb-filter-' . esc_attr( $args['attribute_name'] ) . '[]' . '" ' . esc_attr( $args['selected_item'] ) . '/><label for="%s" title="' . esc_attr( $args['name'] ) . '" class="rtsb-default-filter-trigger rtsb-attr-filter rtsb-button-filter rtsb-term-span rtsb-term-span-%s%s"><span class="default-filter-attr-name">%s</span><div class="rtsb-count">(%s)</div></label>',
1842 + esc_attr( $args['id'] ),
1843 + esc_attr( $args['type'] ),
1844 + $args['tooltips'] ? esc_attr( ' tipsy' ) : '',
1845 + esc_html( $args['name'] ),
1846 + absint( $count )
1847 + );
1848 + break;
1849 +
1850 + default:
1851 + }
1852 +
1853 + return apply_filters( 'rtsb/elementor/default/filter/get_input_type_html', $html );
1854 + }
1855 + /**
1856 + * Get reset button.
1857 + *
1858 + * @param string $btn_text Button text.
1859 + * @param array $settings Settings array.
1860 + *
1861 + * @return string
1862 + */
1863 + public static function get_default_filter_reset_button( $btn_text, $settings ) {
1864 + $html = '';
1865 +
1866 + ob_start();
1867 +
1868 + $active = ! empty( $_SERVER['QUERY_STRING'] ) ? ' active' : '';
1869 + $reset = ! empty( $settings['reset_btn'] );
1870 + $apply_text = ! empty( $settings['apply_filters_btn_text'] ) ? $settings['apply_filters_btn_text'] : esc_html__( 'Apply Filters', 'shopbuilder' );
1871 + $apply_icon = ! empty( $settings['apply_filters_btn_icon'] ) ? $settings['apply_filters_btn_icon'] : [];
1872 + echo '<div class="default-filter-btn-wrapper">';
1873 + ?>
1874 + <?php if ( $settings['show_filter_btn'] ) { ?>
1875 + <div class="rtsb-product-default-filters rtsb-apply-filters-btn">
1876 + <div class="reset-btn-item">
1877 + <button class="rtsb-apply-filters init">
1878 + <span class="icon"><?php Fns::print_html( Fns::icons_manager( $apply_icon, 'icon-default' ) ); ?></span>
1879 + <span class="text reset-text"><?php echo esc_html( $apply_text ); ?></span>
1880 + <span></span>
1881 + </button>
1882 + </div>
1883 + </div>
1884 + <?php } ?>
1885 + <?php
1886 + if ( $reset ) {
1887 + ?>
1888 + <div class="rtsb-product-default-filters rtsb-reset<?php echo esc_attr( $active ); ?>">
1889 + <div class="reset-btn-item">
1890 + <button class="product-default-filter-reset">
1891 + <span class="text reset-text"><?php echo esc_html( $btn_text ); ?></span>
1892 + <span></span>
1893 + </button>
1894 + </div>
1895 + </div>
1896 + <?php
1897 + }
1898 + echo '</div>';
1899 +
1900 + $html .= ob_get_clean();
1901 +
1902 + return $html;
951 1903 }
952 1904 }