PluginProbe
ShopBuilder – WooCommerce Builder For Elementor / 2.6.5
ShopBuilder – WooCommerce Builder For Elementor v2.6.5
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 +1180 -200 2.0.2 → 2.6.5 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.' );
@@ -20,11 +24,31 @@
20 24 /**
21 25 * BuilderFns class
22 26 */
23 27 class RenderHelpers {
28 + /**
29 + * @var array
30 + */
31 + private static $cache = [];
32 + /**
33 + * Get data.
34 + *
35 + * @param array|string $haystack The array or string to search for the value.
36 + * @param string $needle The key to look for in the array or the string itself.
37 + * @param mixed $default The default value to return if the key is not.
38 + *
39 + * @return mixed
40 + */
41 + public static function get_data( $haystack, $needle, $default = null ) {
42 + if ( is_array( $haystack ) && ! empty( $haystack[ $needle ] ) ) {
43 + return $haystack[ $needle ];
44 + } elseif ( is_string( $haystack ) && ! empty( $haystack ) ) {
45 + return $haystack;
46 + } else {
47 + return $default;
48 + }
49 + }
24 50
25 -
26 -
27 51 /**
28 52 * Builds an array with field values.
29 53 *
30 54 * @param array $meta Field values.
@@ -33,15 +57,15 @@
33 57 *
34 58 * @return array
35 59 */
36 60 public static function meta_dataset( array $meta, $template = '', $raw_settings = [] ) {
37 - $c_image_size = ! empty( $meta['image_custom_dimension'] ) ? $meta['image_custom_dimension'] : [];
38 - $c_image_size['crop'] = ! empty( $meta['image_crop'] ) ? $meta['image_crop'] : '';
61 + $c_image_size = self::get_data( $meta, 'image_custom_dimension', [] );
62 + $c_image_size['crop'] = self::get_data( $meta, 'image_crop', [] );
39 63
40 - if ( ( ! empty( $_GET['displayview'] ) && 'list' === $_GET['displayview'] ) || ( empty( $_GET['displayview'] ) && ( ! empty( $meta['view_mode'] ) && 'list' === $meta['view_mode'] ) ) ) {
41 - $meta['cols'] = ! empty( $meta['list_cols'] ) ? $meta['list_cols'] : 1;
42 - $meta['cols_tablet'] = ! empty( $meta['list_cols_tablet'] ) ? $meta['list_cols_tablet'] : $meta['cols'];
43 - $meta['cols_mobile'] = ! empty( $meta['list_cols_mobile'] ) ? $meta['list_cols_mobile'] : $meta['cols'];
64 + if ( ( ! empty( $_GET['displayview'] ) && 'list' === $_GET['displayview'] ) || ( empty( $_GET['displayview'] ) && ( ! empty( $meta['view_mode'] ) && 'list' === $meta['view_mode'] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
65 + $meta['cols'] = self::get_data( $meta, 'list_cols', 1 );
66 + $meta['cols_tablet'] = self::get_data( $meta, 'list_cols_tablet', $meta['cols'] );
67 + $meta['cols_mobile'] = self::get_data( $meta, 'list_cols_mobile', $meta['cols'] );
44 68 }
45 69
46 70 $data = apply_filters(
47 71 'rtsb/elementor/render/meta_dataset',
@@ -47,53 +71,54 @@
47 71 'rtsb/elementor/render/meta_dataset',
48 72 [
49 73 // Layout.
50 74 'widget' => 'custom',
51 - 'template' => ! empty( $template ) ? $template : '',
52 - 'raw_settings' => ! empty( $raw_settings ) ? $raw_settings : [],
53 - 'layout' => ! empty( $meta['layout'] ) ? $meta['layout'] : 'layout1',
54 - 'grid_layout' => ! empty( $raw_settings['layout'] ) ? $raw_settings['layout'] : 'grid-layout1',
55 - 'list_layout' => ! empty( $raw_settings['list_layout'] ) ? $raw_settings['list_layout'] : 'list-layout1',
56 - 'd_cols' => ! empty( $meta['cols'] ) ? $meta['cols'] : 0,
57 - 't_cols' => ! empty( $meta['cols_tablet'] ) ? $meta['cols_tablet'] : 2,
58 - 'm_cols' => ! empty( $meta['cols_mobile'] ) ? $meta['cols_mobile'] : 1,
59 - 'grid_type' => ! empty( $meta['grid_style'] ) ? $meta['grid_style'] : 'even',
75 + 'template' => self::get_data( $template, '', '' ),
76 + 'raw_settings' => $raw_settings,
77 + 'layout' => self::get_data( $meta, 'layout', 'layout1' ),
78 + 'grid_layout' => self::get_data( $raw_settings, 'layout', 'grid-layout1' ),
79 + 'list_layout' => self::get_data( $raw_settings, 'list_layout', 'list-layout1' ),
80 + 'd_cols' => self::get_data( $meta, 'cols', 0 ),
81 + 't_cols' => self::get_data( $meta, 'cols_tablet', 2 ),
82 + 'm_cols' => self::get_data( $meta, 'cols_mobile', 1 ),
83 + 'grid_type' => self::get_data( $meta, 'grid_style', 'even' ),
60 84
61 85 // Query.
62 - 'post_in' => ! empty( $meta['include_posts'] ) && is_array( $meta['include_posts'] ) ? $meta['include_posts'] : [],
63 - 'post_not_in' => ! empty( $meta['exclude_posts'] ) && is_array( $meta['exclude_posts'] ) ? $meta['exclude_posts'] : [],
86 + 'post_in' => self::get_data( $meta, 'include_posts', [] ),
87 + 'post_not_in' => self::get_data( $meta, 'exclude_posts', [] ),
64 88 'limit' => ( empty( $meta['posts_limit'] ) || '-1' === $meta['posts_limit'] ) ? 10000000 : $meta['posts_limit'],
65 - 'offset' => ! empty( $meta['posts_offset'] ) ? $meta['posts_offset'] : 0,
66 - 'order_by' => ! empty( $meta['posts_order_by'] ) ? $meta['posts_order_by'] : 'date',
67 - 'order' => ! empty( $meta['posts_order'] ) ? $meta['posts_order'] : 'DESC',
68 - 'author_in' => ! empty( $meta['filter_author'] ) && is_array( $meta['filter_author'] ) ? $meta['filter_author'] : [],
69 - 'display_by' => ! empty( $meta['products_filter'] ) ? $meta['products_filter'] : 'date',
70 - 'categories' => ! empty( $meta['filter_categories'] ) && is_array( $meta['filter_categories'] ) ? $meta['filter_categories'] : [],
71 - 'tags' => ! empty( $meta['filter_tags'] ) && is_array( $meta['filter_tags'] ) ? $meta['filter_tags'] : [],
72 - 'attributes' => ! empty( $meta['filter_attributes'] ) && is_array( $meta['filter_attributes'] ) ? $meta['filter_attributes'] : [],
73 - 'relation' => ! empty( $meta['tax_relation'] ) ? $meta['tax_relation'] : 'OR',
74 - 'category_source' => ! empty( $meta['select_source'] ) ? $meta['select_source'] : 'product_cat',
75 - 'category_term' => ! empty( $meta['select_cat'] ) ? $meta['select_cat'] : null,
76 - 'display_cat_by' => ! empty( $meta['display_cat_by'] ) ? $meta['display_cat_by'] : null,
77 - 'include_cats' => ! empty( $meta['include_cats'] ) && is_array( $meta['include_cats'] ) ? $meta['include_cats'] : [],
78 - 'exclude_cats' => ! empty( $meta['exclude_cats'] ) && is_array( $meta['exclude_cats'] ) ? $meta['exclude_cats'] : [],
79 - 'select_parent_cat' => ! empty( $meta['select_parent_cat'] ) ? $meta['select_parent_cat'] : null,
80 - 'select_cats' => ! empty( $meta['select_cats'] ) && is_array( $meta['select_cats'] ) ? $meta['select_cats'] : [],
81 - 'select_cat_ids' => ! empty( $meta['include_cat_ids'] ) ? $meta['include_cat_ids'] : '',
89 + 'offset' => self::get_data( $meta, 'posts_offset', 0 ),
90 + 'order_by' => self::get_data( $meta, 'posts_order_by', 'date' ),
91 + 'order' => self::get_data( $meta, 'posts_order', 'DESC' ),
92 + 'author_in' => self::get_data( $meta, 'filter_author', [] ),
93 + 'display_by' => self::get_data( $meta, 'products_filter', 'date' ),
94 + 'categories' => self::get_data( $meta, 'filter_categories', [] ),
95 + 'brands' => self::get_data( $meta, 'filter_brands', [] ),
96 + 'tags' => self::get_data( $meta, 'filter_tags', [] ),
97 + 'attributes' => self::get_data( $meta, 'filter_attributes', [] ),
98 + 'relation' => self::get_data( $meta, 'tax_relation', 'OR' ),
99 + 'category_source' => self::get_data( $meta, 'select_source', 'product_cat' ),
100 + 'category_term' => self::get_data( $meta, 'select_cat' ),
101 + 'display_cat_by' => self::get_data( $meta, 'display_cat_by' ),
102 + 'include_cats' => self::get_data( $meta, 'include_cats', [] ),
103 + 'exclude_cats' => self::get_data( $meta, 'exclude_cats', [] ),
104 + 'select_parent_cat' => self::get_data( $meta, 'select_parent_cat' ),
105 + 'select_cats' => self::get_data( $meta, 'select_cats', [] ),
106 + 'select_cat_ids' => self::get_data( $meta, 'include_cat_ids', '' ),
82 107 'cats_limit' => ( empty( $meta['cats_limit'] ) || '-1' === $meta['cats_limit'] ) ? 10000000 : $meta['cats_limit'],
83 108 'show_empty' => ! empty( $meta['show_empty'] ),
84 109 'show_uncategorized' => ! empty( $meta['show_uncategorized'] ),
85 110 'show_subcats' => ! empty( $meta['show_subcats'] ),
86 111 'show_top_level_cats' => ! empty( $meta['show_top_level_cats'] ),
87 - 'cats_order_by' => ! empty( $meta['cats_order_by'] ) ? $meta['cats_order_by'] : 'name',
88 - 'cats_order' => ! empty( $meta['cats_order'] ) ? $meta['cats_order'] : 'DESC',
89 - 'rtsb_order' => ! empty( $meta['rtsb_order'] ) ? $meta['rtsb_order'] : 'ASC',
90 - 'rtsb_orderby' => ! empty( $meta['rtsb_orderby'] ) ? $meta['rtsb_orderby'] : 'menu_order',
112 + 'cats_order_by' => self::get_data( $meta, 'cats_order_by', 'name' ),
113 + 'cats_order' => self::get_data( $meta, 'cats_order', 'DESC' ),
114 + 'rtsb_order' => self::get_data( $meta, 'rtsb_order', 'ASC' ),
115 + 'rtsb_orderby' => self::get_data( $meta, 'rtsb_orderby', 'menu_order' ),
91 116
92 117 // Pagination.
93 118 'pagination' => ! empty( $meta['show_pagination'] ),
94 - 'posts_loading_type' => ! empty( $meta['pagination_type'] ) ? $meta['pagination_type'] : 'pagination',
95 - 'posts_per_page' => ! empty( $meta['pagination_per_page'] ) ? $meta['pagination_per_page'] : '',
119 + 'posts_loading_type' => self::get_data( $meta, 'pagination_type', 'pagination' ),
120 + 'posts_per_page' => self::get_data( $meta, 'pagination_per_page', '' ),
96 121
97 122 // Image.
98 123 'f_img' => ! empty( $meta['show_featured_image'] ),
99 124 'gallery_img' => ! empty( $meta['show_product_gallery'] ),
@@ -98,13 +123,13 @@
98 123 'f_img' => ! empty( $meta['show_featured_image'] ),
99 124 'gallery_img' => ! empty( $meta['show_product_gallery'] ),
100 125 'c_img' => ! empty( $meta['show_cat_image'] ),
101 126 'hover_img' => ! empty( $meta['show_hover_image'] ),
102 - 'f_img_size' => ! empty( $meta['image'] ) ? $meta['image'] : 'medium',
127 + 'f_img_size' => self::get_data( $meta, 'image', 'medium' ),
103 128 'custom_img_size' => ! empty( $c_image_size ) && is_array( $c_image_size ) ? $c_image_size : [],
104 129 'default_img_id' => ! empty( $meta['default_preview_image']['id'] ) ? $meta['default_preview_image']['id'] : null,
105 130 'show_overlay' => ! empty( $meta['show_overlay'] ),
106 - 'hover_animation' => ! empty( $meta['image_hover_animation'] ) ? $meta['image_hover_animation'] : 'none',
131 + 'hover_animation' => self::get_data( $meta, 'image_hover_animation', 'none' ),
107 132 'show_custom_image' => ! empty( $meta['show_custom_image'] ),
108 133 'custom_image' => ! empty( $meta['custom_image']['id'] ) ? $meta['custom_image']['id'] : null,
109 134
110 135 // Visibility.
@@ -110,90 +135,96 @@
110 135 // Visibility.
111 136 'visibility' => ! empty( self::content_visibility( $meta ) ) ? self::content_visibility( $meta ) : [],
112 137
113 138 // Action Buttons.
114 - 'btn_preset' => ! empty( $meta['action_btn_preset'] ) ? $meta['action_btn_preset'] : 'preset1',
115 - 'btn_position' => ! empty( $meta['action_btn_position'] ) ? $meta['action_btn_position'] : 'above',
116 - 'tooltip_position' => ! empty( $meta['action_btn_tooltip_position'] ) ? $meta['action_btn_tooltip_position'] : 'top',
139 + 'btn_preset' => self::get_data( $meta, 'action_btn_preset', 'preset1' ),
140 + 'btn_position' => self::get_data( $meta, 'action_btn_position', 'above' ),
141 + 'tooltip_position' => self::get_data( $meta, 'action_btn_tooltip_position', 'top' ),
117 142
118 143 // Product Title.
119 - 'title_tag' => ! empty( $meta['title_tag'] ) ? $meta['title_tag'] : 'h3',
144 + 'title_tag' => self::get_data( $meta, 'title_tag', 'h3' ),
120 145 'title_hover' => ! empty( $meta['title_hover'] ),
121 - 'title_limit' => ! empty( $meta['title_limit'] ) ? $meta['title_limit'] : 'default',
122 - 'title_limit_custom' => ! empty( $meta['title_limit_custom'] ) ? $meta['title_limit_custom'] : null,
146 + 'title_limit' => self::get_data( $meta, 'title_limit', 'default' ),
147 + 'title_limit_custom' => self::get_data( $meta, 'title_limit_custom' ),
123 148 'show_custom_title' => ! empty( $meta['show_custom_title'] ),
124 - 'cat_custom_title' => ! empty( $meta['custom_title'] ) ? $meta['custom_title'] : '',
149 + 'cat_custom_title' => self::get_data( $meta, 'custom_title', '' ),
125 150
126 151 // Variation Swatches.
127 - 'swatch_position' => ! empty( $meta['swatch_position'] ) ? $meta['swatch_position'] : 'top',
128 - 'swatch_type' => ! empty( $meta['swatch_type'] ) ? $meta['swatch_type'] : 'circle',
152 + 'swatch_position' => self::get_data( $meta, 'swatch_position', 'top' ),
153 + 'swatch_type' => self::get_data( $meta, 'swatch_type', 'circle' ),
129 154
130 155 // Short Description.
131 156 'show_custom_excerpt' => ! empty( $meta['show_custom_description'] ),
132 - 'cat_custom_excerpt' => ! empty( $meta['custom_description'] ) ? $meta['custom_description'] : '',
133 - 'cat_excerpt_position' => ! empty( $meta['excerpt_position'] ) ? $meta['excerpt_position'] : 'below',
134 - 'excerpt_limit' => ! empty( $meta['excerpt_limit'] ) ? $meta['excerpt_limit'] : 'default',
135 - 'excerpt_limit_custom' => ! empty( $meta['excerpt_limit_custom'] ) ? $meta['excerpt_limit_custom'] : 200,
157 + 'cat_custom_excerpt' => self::get_data( $meta, 'custom_description', '' ),
158 + 'cat_excerpt_position' => self::get_data( $meta, 'excerpt_position', 'below' ),
159 + 'excerpt_limit' => self::get_data( $meta, 'excerpt_limit', 'default' ),
160 + 'excerpt_limit_custom' => self::get_data( $meta, 'excerpt_limit_custom', 200 ),
136 161
137 162 // Count.
138 - 'count_position' => ! empty( $meta['count_display_type'] ) ? $meta['count_display_type'] : 'flex',
139 - 'before_count' => ! empty( $meta['before_count'] ) ? $meta['before_count'] : '',
140 - 'after_count' => ! empty( $meta['after_count'] ) ? $meta['after_count'] : '',
163 + 'count_position' => self::get_data( $meta, 'count_display_type', 'flex' ),
164 + 'before_count' => self::get_data( $meta, 'before_count', '' ),
165 + 'after_count' => self::get_data( $meta, 'after_count', '' ),
141 166
142 167 // Add to cart.
143 168 'show_cart_icon' => ! empty( $meta['show_cart_icon'] ),
144 169 'show_cart_text' => ! empty( $meta['show_cart_text'] ),
145 - 'add_to_cart_icon' => ! empty( $meta['add_to_cart_icon'] ) ? $meta['add_to_cart_icon'] : [],
146 - 'add_to_cart_success_icon' => ! empty( $meta['add_to_cart_success_icon'] ) ? $meta['add_to_cart_success_icon'] : [],
147 - 'add_to_cart_text' => ! empty( $meta['add_to_cart_text'] ) ? $meta['add_to_cart_text'] : esc_html__( 'Add to Cart', 'shopbuilder' ),
148 - 'add_to_cart_success_text' => ! empty( $meta['add_to_cart_success_text'] ) ? $meta['add_to_cart_success_text'] : '',
149 - 'add_to_cart_alignment' => ! empty( $meta['cart_icon_alignment'] ) ? $meta['cart_icon_alignment'] : 'left',
170 + 'add_to_cart_icon' => self::get_data( $meta, 'add_to_cart_icon', [] ),
171 + 'add_to_cart_success_icon' => self::get_data( $meta, 'add_to_cart_success_icon', [] ),
172 + 'add_to_cart_text' => self::get_data( $meta, 'add_to_cart_text', esc_html__( 'Add to Cart', 'shopbuilder' ) ),
173 + 'add_to_cart_success_text' => self::get_data( $meta, 'add_to_cart_success_text', '' ),
174 + 'add_to_cart_alignment' => self::get_data( $meta, 'cart_icon_alignment', 'left' ),
150 175
151 176 // Action Button Icons.
152 - 'quick_view_icon' => ! empty( $meta['quick_view_icon'] ) ? $meta['quick_view_icon'] : [],
153 - 'comparison_icon' => ! empty( $meta['comparison_icon'] ) ? $meta['comparison_icon'] : [],
154 - 'comparison_icon_added' => ! empty( $meta['comparison_icon_added'] ) ? $meta['comparison_icon_added'] : [],
155 - 'wishlist_icon' => ! empty( $meta['wishlist_icon'] ) ? $meta['wishlist_icon'] : [],
156 - 'wishlist_icon_added' => ! empty( $meta['wishlist_icon_added'] ) ? $meta['wishlist_icon_added'] : [],
177 + 'quick_view_icon' => self::get_data( $meta, 'quick_view_icon', [] ),
178 + 'comparison_icon' => self::get_data( $meta, 'comparison_icon', [] ),
179 + 'comparison_icon_added' => self::get_data( $meta, 'comparison_icon_added', [] ),
180 + 'wishlist_icon' => self::get_data( $meta, 'wishlist_icon', [] ),
181 + 'wishlist_icon_added' => self::get_data( $meta, 'wishlist_icon_added', [] ),
157 182
158 183 // Badges.
159 - 'sale_badge_type' => ! empty( $meta['sale_badges_type'] ) ? $meta['sale_badges_type'] : 'percentage',
160 - 'sale_badge_text' => ! empty( $meta['sale_badges_text'] ) ? $meta['sale_badges_text'] : esc_html__( 'Sale', 'shopbuilder' ),
161 - 'custom_badge_text' => ! empty( $meta['custom_badge_text'] ) ? $meta['custom_badge_text'] : esc_html__( 'Sale', 'shopbuilder' ),
162 - 'out_of_stock_badge_text' => ! empty( $meta['stock_badges_text'] ) ? $meta['stock_badges_text'] : esc_html__( 'Out of Stock', 'shopbuilder' ),
163 - 'badge_position' => ! empty( $meta['badges_position'] ) ? $meta['badges_position'] : 'top',
164 - 'badge_alignment' => ! empty( $meta['badges_alignment'] ) ? $meta['badges_alignment'] : 'left',
165 - 'badge_preset' => ! empty( $meta['custom_badge_preset'] ) ? $meta['custom_badge_preset'] : 'preset-1',
184 + 'sale_badge_type' => self::get_data( $meta, 'sale_badges_type', 'percentage' ),
185 + 'sale_badge_text' => self::get_data( $meta, 'sale_badges_text', esc_html__( 'Sale', 'shopbuilder' ) ),
186 + 'custom_badge_text' => self::get_data( $meta, 'custom_badge_text', esc_html__( 'Sale', 'shopbuilder' ) ),
187 + 'out_of_stock_badge_text' => self::get_data( $meta, 'stock_badges_text', esc_html__( 'Out of Stock', 'shopbuilder' ) ),
188 + 'badge_position' => self::get_data( $meta, 'badges_position', 'top' ),
189 + 'badge_alignment' => self::get_data( $meta, 'badges_alignment', 'left' ),
190 + 'badge_preset' => self::get_data( $meta, 'custom_badge_preset', 'preset-1' ),
191 + 'enable_badges_module' => self::get_data( $meta, 'enable_badges_module', '' ),
166 192
167 193 // Link.
168 194 'image_link' => ! empty( $meta['image_link'] ),
169 195 'title_link' => ! empty( $meta['title_link'] ),
170 - 'hover_btn_text' => ! empty( $meta['hover_btn_text'] ) ? $meta['hover_btn_text'] : esc_html__( 'See Details', 'shopbuilder' ),
196 + 'hover_btn_text' => self::get_data( $meta, 'hover_btn_text', esc_html__( 'See Details', 'shopbuilder' ) ),
171 197 'show_hover_btn_icon' => ! empty( $meta['show_hover_btn_icon'] ),
172 - 'hover_btn_icon' => ! empty( $meta['hover_btn_icon'] ) ? $meta['hover_btn_icon'] : [],
173 - 'custom_link' => ! empty( $meta['custom_link'] ) ? $meta['custom_link'] : null,
198 + 'hover_btn_icon' => self::get_data( $meta, 'hover_btn_icon', [] ),
199 + 'custom_link' => self::get_data( $meta, 'custom_link' ),
174 200
175 201 // Slider.
176 - 'd_group' => ! empty( $meta['cols_group'] ) ? $meta['cols_group'] : 1,
177 - 't_group' => ! empty( $meta['cols_group_tablet'] ) ? $meta['cols_group_tablet'] : 1,
178 - 'm_group' => ! empty( $meta['cols_group_mobile'] ) ? $meta['cols_group_mobile'] : 1,
202 + 'd_group' => self::get_data( $meta, 'cols_group', 1 ),
203 + 't_group' => self::get_data( $meta, 'cols_group_tablet', 1 ),
204 + 'm_group' => self::get_data( $meta, 'cols_group_mobile', 1 ),
179 205 'auto_play' => ! empty( $meta['slide_autoplay'] ),
180 206 'stop_on_hover' => ! empty( $meta['pause_hover'] ),
181 207 'slider_nav' => ! empty( $meta['slider_nav'] ),
182 208 'slider_dot' => ! empty( $meta['slider_pagi'] ),
209 + 'slider_dynamic_dot' => ! empty( $meta['slider_dynamic_pagi'] ),
183 210 'loop' => ! empty( $meta['slider_loop'] ),
184 211 'lazy_load' => ! empty( $meta['slider_lazy_load'] ),
185 212 'auto_height' => ! empty( $meta['slider_auto_height'] ),
186 - 'speed' => ! empty( $meta['slide_speed'] ) ? $meta['slide_speed'] : 2000,
213 + 'speed' => self::get_data( $meta, 'slide_speed', 2000 ),
187 214 'space_between' => isset( $meta['grid_gap']['size'] ) && strlen( $meta['grid_gap']['size'] ) ? $meta['grid_gap']['size'] : 30,
188 - 'auto_play_timeout' => ! empty( $meta['autoplay_timeout'] ) ? $meta['autoplay_timeout'] : 5000,
189 - 'nav_position' => ! empty( $meta['slider_nav_position'] ) ? $meta['slider_nav_position'] : 'top',
190 - 'left_arrow_icon' => ! empty( $meta['slider_left_arrow_icon'] ) ? $meta['slider_left_arrow_icon'] : [],
191 - 'right_arrow_icon' => ! empty( $meta['slider_right_arrow_icon'] ) ? $meta['slider_right_arrow_icon'] : [],
215 + 'auto_play_timeout' => self::get_data( $meta, 'autoplay_timeout', 5000 ),
216 + 'nav_position' => self::get_data( $meta, 'slider_nav_position', 'top' ),
217 + 'left_arrow_icon' => self::get_data( $meta, 'slider_left_arrow_icon', [] ),
218 + 'right_arrow_icon' => self::get_data( $meta, 'slider_right_arrow_icon', [] ),
192 219 ],
193 220 $meta
194 221 );
195 222
223 + if ( ! empty( $meta['active_cat_slider'] ) ) {
224 + $data['active_cat_slider'] = true;
225 + }
226 +
196 227 if ( ! empty( $meta['cols_widescreen'] ) ) {
197 228 $data['w_cols'] = $meta['cols_widescreen'];
198 229 }
199 230
@@ -211,11 +242,11 @@
211 242
212 243 $queried_obj = get_queried_object();
213 244
214 245 if ( is_shop() || is_product_taxonomy() ) {
215 - $data['posts_per_page'] = wc_get_default_products_per_row() * wc_get_default_product_rows_per_page();
216 - $data['order_by'] = ! empty( $data['rtsb_orderby'] ) ? esc_html( $data['rtsb_orderby'] ) : $data['orderby'];
217 - $data['order'] = ! empty( $data['rtsb_order'] ) ? esc_html( $data['rtsb_order'] ) : $data['order'];
246 + $data['posts_per_page'] = self::get_products_per_page();
247 + $data['order_by'] = self::get_data( $data, 'rtsb_orderby', $data['order_by'] );
248 + $data['order'] = self::get_data( $data, 'rtsb_order', $data['order'] );
218 249 }
219 250
220 251 if ( ! is_shop() && is_product_taxonomy() ) {
221 252 if ( ! empty( $queried_obj->taxonomy ) ) {
@@ -238,19 +269,15 @@
238 269 *
239 270 * @return array
240 271 */
241 272 public static function archive_meta_dataset( array $meta, $template = '' ) {
242 - $products_row = absint( get_option( 'woocommerce_catalog_rows', 4 ) );
243 - $products_col = absint( get_option( 'woocommerce_catalog_columns', 4 ) );
244 - $products_per_page = apply_filters( 'rtsb/elementor/archive/products_per_page', $products_row * $products_col );
245 -
246 273 $data = apply_filters(
247 274 'rtsb/elementor/render/archive_meta_dataset',
248 275 [
249 276 // Layout.
250 277 'widget' => 'default',
251 - 'template' => ! empty( $template ) ? $template : '',
252 - 'view_mode' => ! empty( $meta['view_mode'] ) ? $meta['view_mode'] : 'grid',
278 + 'template' => self::get_data( $template, '', '' ),
279 + 'view_mode' => self::get_data( $meta, 'view_mode', 'grid' ),
253 280 'show_flash_sale' => ! empty( $meta['show_flash_sale'] ),
254 281 'wishlist_button' => ! empty( $meta['wishlist_button'] ),
255 282 'comparison_button' => ! empty( $meta['comparison_button'] ),
256 283 'quick_view_button' => ! empty( $meta['quick_view_button'] ),
@@ -255,20 +282,20 @@
255 282 'comparison_button' => ! empty( $meta['comparison_button'] ),
256 283 'quick_view_button' => ! empty( $meta['quick_view_button'] ),
257 284 'show_rating' => ! empty( $meta['show_rating'] ),
258 285 'show_pagination' => ! empty( $meta['show_pagination'] ),
259 - 'cart_icon' => ! empty( $meta['cart_icon'] ) ? $meta['cart_icon'] : [],
260 - 'wishlist_icon' => ! empty( $meta['wishlist_icon'] ) ? $meta['wishlist_icon'] : [],
261 - 'wishlist_icon_added' => ! empty( $meta['wishlist_icon_added'] ) ? $meta['wishlist_icon_added'] : [],
262 - 'comparison_icon' => ! empty( $meta['comparison_icon'] ) ? $meta['comparison_icon'] : [],
263 - 'comparison_icon_added' => ! empty( $meta['comparison_icon_added'] ) ? $meta['comparison_icon_added'] : [],
264 - 'quick_view_icon' => ! empty( $meta['quick_view_icon'] ) ? $meta['quick_view_icon'] : [],
265 - 'prev_icon' => ! empty( $meta['prev_icon'] ) ? $meta['prev_icon'] : [],
266 - 'next_icon' => ! empty( $meta['next_icon'] ) ? $meta['next_icon'] : [],
267 - 'posts_per_page' => ! empty( $products_per_page ) ? $products_per_page : 12,
268 - 'rtsb_order' => ! empty( $meta['rtsb_order'] ) ? $meta['rtsb_order'] : 'ASC',
269 - 'rtsb_orderby' => ! empty( $meta['rtsb_orderby'] ) ? $meta['rtsb_orderby'] : 'menu_order',
270 - 'tooltip_position' => ! empty( $meta['action_btn_tooltip_position'] ) ? $meta['action_btn_tooltip_position'] : 'top',
286 + 'cart_icon' => self::get_data( $meta, 'cart_icon', [] ),
287 + 'wishlist_icon' => self::get_data( $meta, 'wishlist_icon', [] ),
288 + 'wishlist_icon_added' => self::get_data( $meta, 'wishlist_icon_added', [] ),
289 + 'comparison_icon' => self::get_data( $meta, 'comparison_icon', [] ),
290 + 'comparison_icon_added' => self::get_data( $meta, 'comparison_icon_added', [] ),
291 + 'quick_view_icon' => self::get_data( $meta, 'quick_view_icon', [] ),
292 + 'prev_icon' => self::get_data( $meta, 'prev_icon', [] ),
293 + 'next_icon' => self::get_data( $meta, 'next_icon', [] ),
294 + 'posts_per_page' => self::get_products_per_page(),
295 + 'rtsb_order' => self::get_data( $meta, 'rtsb_order', 'ASC' ),
296 + 'rtsb_orderby' => self::get_data( $meta, 'rtsb_orderby', 'menu_order' ),
297 + 'tooltip_position' => self::get_data( $meta, 'action_btn_tooltip_position', 'top' ),
271 298 ],
272 299 $meta
273 300 );
274 301
@@ -323,9 +350,16 @@
323 350
324 351 if ( ! empty( $settings['single_category'] ) ) {
325 352 $visibility[] = 'single-cat';
326 353 }
354 + if ( ! empty( $settings['show_brands'] ) ) {
355 + $visibility[] = 'brands';
356 + }
327 357
358 + if ( ! empty( $settings['single_brand'] ) ) {
359 + $visibility[] = 'single-brand';
360 + }
361 +
328 362 if ( ! empty( $settings['show_swatches'] ) ) {
329 363 $visibility[] = 'swatches';
330 364 }
331 365
@@ -391,18 +425,47 @@
391 425 * @return int
392 426 */
393 427 public static function default_columns( $layout ) {
394 428 switch ( $layout ) {
429 + case 'rtsb-post-grid-layout1':
430 + case 'rtsb-post-grid-layout2':
431 + case 'rtsb-post-grid-layout3':
432 + case 'rtsb-team-layout1':
433 + case 'rtsb-team-layout2':
434 + case 'rtsb-testimonial-layout2':
395 435 case 'grid-layout2':
396 436 case 'slider-layout2':
437 + case 'rtsb-coupon-layout1':
438 + case 'rtsb-coupon-layout2':
439 + case 'rtsb-coupon-layout3':
397 440 $columns = 3;
398 441 break;
399 -
442 + case 'rtsb-post-list-layout1':
443 + case 'rtsb-post-list-layout2':
400 444 case 'list-layout1':
401 445 case 'list-layout2':
402 446 case 'list-layout3':
447 + case 'list-layout7':
448 + case 'list-layout5':
403 449 $columns = 1;
404 450 break;
451 + case 'rtsb-team-layout4':
452 + case 'rtsb-testimonial-layout1':
453 + case 'rtsb-testimonial-layout3':
454 + case 'rtsb-testimonial-layout4':
455 + case 'rtsb-testimonial-layout5':
456 + case 'list-layout4':
457 + case 'list-layout6':
458 + case 'slider-layout5':
459 + case 'slider-layout8':
460 + $columns = 2;
461 + break;
462 + case 'rtsb-logo-layout1':
463 + case 'rtsb-logo-layout2':
464 + case 'grid-layout6':
465 + case 'grid-layout9':
466 + $columns = 5;
467 + break;
405 468
406 469 case 'category-layout1':
407 470 $columns = 6;
408 471 break;
@@ -411,9 +474,9 @@
411 474 $columns = 4;
412 475 break;
413 476 }
414 477
415 - return $columns;
478 + return apply_filters( 'rtsb/element/general/default_columns', $columns, $layout );
416 479 }
417 480
418 481 /**
419 482 * Pagination JSON data.
@@ -425,12 +488,210 @@
425 488 */
426 489 public static function pagination_data( $metas, $template ) {
427 490 $el_data = self::meta_dataset( $metas, $template );
428 491
429 - return ' data-rtsb-ajax=\'' . esc_js( wp_json_encode( $el_data ) ) . '\'';
492 + return esc_js( wp_json_encode( $el_data, JSON_UNESCAPED_UNICODE ) );
430 493 }
431 494
432 495 /**
496 + * Container classes
497 + *
498 + * @param array $metas Meta data.
499 + * @param array $settings Settings array.
500 + * @param string $custom_class Custom class.
501 + *
502 + * @return mixed|null
503 + */
504 + public static function prepare_container_classes( $metas = [], $settings = [], $custom_class = '' ) {
505 + $classes = 'rtsb-elementor-container products rtsb-pos-r ';
506 + $classes .= $custom_class;
507 +
508 + $raw_layout = esc_html( $metas['layout'] );
509 + $layout = self::filter_layout( $raw_layout );
510 + $is_cat = preg_match( '/category/', $layout );
511 + $cart_txt_class = '';
512 +
513 + $animation = $metas['hover_animation'];
514 +
515 + if ( ! $is_cat ) {
516 + $cart_txt_class = $metas['show_cart_text'] ? ' has-cart-text' : ' no-cart-text';
517 + $animation .= ' gallery-hover-' . ( ! empty( $settings['gallery_hover_animation'] ) ? $settings['gallery_hover_animation'] : 'fade' );
518 + }
519 +
520 + $badge_class = [
521 + 'position' => $metas['badge_position'],
522 + 'alignment' => $metas['badge_alignment'],
523 + ];
524 +
525 + if ( ! in_array( 'clear-btn', $metas['visibility'], true ) ) {
526 + $classes .= ' no-clear-btn';
527 + }
528 +
529 + $classes .= ! empty( $metas['pagination'] ) ? ' rtsb-has-pagination' : ' rtsb-no-pagination';
530 + $classes .= ' badge-' . esc_attr( $badge_class['position'] ) . ' badge-' . esc_attr( $badge_class['alignment'] ) . esc_attr( $cart_txt_class ) . ' img-hover-' . esc_attr( $animation ) . ' excerpt-' . esc_attr( $metas['cat_excerpt_position'] );
531 +
532 + if ( $metas['show_overlay'] ) {
533 + $classes .= ' has-overlay';
534 + }
535 +
536 + if ( in_array( 'single-cat', $metas['visibility'], true ) ) {
537 + $classes .= ' show-single-cat';
538 + }
539 + if ( in_array( 'single-brand', $metas['visibility'], true ) ) {
540 + $classes .= ' show-single-brand';
541 + }
542 +
543 + if ( ! $is_cat ) {
544 + $action_btn_classes = [
545 + 'show_compare_text' => 'has-compare-text',
546 + 'show_quick_view_text' => 'has-quick-view-text',
547 + 'show_wishlist_text' => 'has-wishlist-text',
548 + 'show_compare_icon' => 'no-compare-icon',
549 + 'show_quick_view_icon' => 'no-quick-view-icon',
550 + 'show_wishlist_icon' => 'no-wishlist-icon',
551 + 'show_quick_checkout_icon' => 'no-quick_checkout-icon',
552 + ];
553 +
554 + foreach ( $action_btn_classes as $setting_key => $class ) {
555 + if ( strpos( $setting_key, 'icon' ) !== false ) {
556 + $classes .= empty( $settings[ $setting_key ] ) ? ' ' . $class : '';
557 + } else {
558 + $classes .= ! empty( $settings[ $setting_key ] ) ? ' ' . $class : '';
559 + }
560 + }
561 +
562 + if ( rtsb()->has_pro() && ! empty( $settings['custom_ordering'] ) ) {
563 + $classes .= ' has-custom-ordering';
564 + }
565 + }
566 + if ( rtsb()->has_pro() && ! empty( $settings['slider_slide_animation'] ) ) {
567 + $classes .= ' has-slide-animation';
568 + }
569 +
570 + return apply_filters( 'rtsb/product/container/class', $classes, $settings );
571 + }
572 +
573 + /**
574 + * Function to filter and validate the layout against allowed patterns.
575 + *
576 + * @param string $layout The layout string to be filtered.
577 + * @param string $template The template to be checked.
578 + *
579 + * @return string
580 + */
581 + public static function filter_layout( $layout, $template = '' ) {
582 + $allowed_patterns = apply_filters( 'rtsb/elementor/custom_layout', [ 'grid', 'list', 'slider', 'category', 'toyup', 'zilly', 'rtsb' ] );
583 + $layout_part = explode( '-', $layout );
584 + $default = 'grid-layout1';
585 +
586 + if ( empty( $layout_part[0] ) ) {
587 + return $default;
588 + }
589 +
590 + if ( in_array( $layout_part[0], $allowed_patterns, true ) ) {
591 + return $layout;
592 + } else {
593 + if ( ! empty( $template ) ) {
594 + $grid = preg_match( '/grid/', $template );
595 + $list = preg_match( '/list/', $template );
596 + $slider = preg_match( '/slider/', $template );
597 + $category = preg_match( '/category/', $template );
598 +
599 + if ( $grid ) {
600 + return $default;
601 + } elseif ( $list ) {
602 + return 'list-layout1';
603 + } elseif ( $slider ) {
604 + return 'slider-layout1';
605 + } elseif ( $category ) {
606 + return 'category-layout1';
607 + } else {
608 + return $default;
609 + }
610 + } else {
611 + return $default;
612 + }
613 + }
614 + }
615 +
616 + /**
617 + * Row classes
618 + *
619 + * @param array $settings Settings array.
620 + *
621 + * @return mixed|null
622 + */
623 + public static function prepare_row_classes( $settings = [] ) {
624 + $masonry_class = null;
625 + $classes = 'rtsb-row rtsb-content-loader element-loading';
626 + $loader_class = ' rtsb-pre-loader';
627 +
628 + $raw_layout = esc_html( $settings['layout'] );
629 + $layout = self::filter_layout( $raw_layout );
630 + $grid_type = $settings['grid_type'];
631 + $is_carousel = preg_match( '/slider/', $layout );
632 +
633 + if ( preg_match( '/category/', $layout ) && ! empty( $settings['active_cat_slider'] ) ) {
634 + $classes .= ' rtsb-slider-layout';
635 + }
636 +
637 + if ( 'equal' === $grid_type ) {
638 + $masonry_class = ' rtsb-equal';
639 + } elseif ( 'masonry' === $grid_type ) {
640 + $masonry_class = ' rtsb-masonry';
641 + } else {
642 + $masonry_class = ' rtsb-even';
643 + }
644 +
645 + if ( ! rtsb()->has_pro() || $is_carousel ) {
646 + $masonry_class = ' rtsb-even';
647 + }
648 +
649 + if ( $is_carousel && ! empty( $settings['raw_settings']['always_show_nav'] ) ) {
650 + $classes .= ' always-show-nav';
651 + }
652 +
653 + if ( ! empty( $settings['raw_settings']['inner_slider_always_show_nav'] ) ) {
654 + $classes .= ' inner-slider-always-show-nav';
655 + }
656 + if ( empty( $settings['raw_settings']['cols_mobile'] ) ) {
657 + $classes .= ' rtsb-mobile-flex-row';
658 + }
659 +
660 + $classes .= ' rtsb-' . $layout . $masonry_class . $loader_class;
661 +
662 + return apply_filters( 'rtsb/elementor/row/classes', $classes, $settings );
663 + }
664 +
665 + /**
666 + * Prepare pagination attributes.
667 + *
668 + * @param string $layout The layout type.
669 + * @param bool $has_filters Whether the widget has filters.
670 + * @param string $template The template name.
671 + * @param array $settings The widget settings.
672 + * @param bool $ajax Whether to enable AJAX pagination.
673 + *
674 + * @return string
675 + */
676 + public static function prepare_pagination_attr( $layout, $has_filters, $template, $settings, $ajax ) {
677 + $is_cat = preg_match( '/category/', $layout );
678 + $is_carousel = preg_match( '/slider/', $layout );
679 +
680 + if ( $is_carousel ) {
681 + return $has_filters ? self::pagination_data( $settings, $template ) : '';
682 + } elseif ( $is_cat ) {
683 + return '';
684 + } else {
685 + $ajax_pagination = ! empty( $settings['show_pagination'] ) && 'pagination' !== $settings['pagination_type'];
686 + $page = Fns::product_filters_has_ajax( apply_filters( 'rtsb/builder/set/current/page/type', '' ) );
687 + $condition = rtsb()->has_pro() && $ajax && ( $ajax_pagination || $has_filters || $page );
688 +
689 + return $condition ? self::pagination_data( $settings, $template ) : '';
690 + }
691 + }
692 +
693 + /**
433 694 * Archive JSON data.
434 695 *
435 696 * @param array $metas Data set.
436 697 * @param string $template Template name.
@@ -451,34 +712,35 @@
451 712 *
452 713 * @return array
453 714 */
454 715 public static function slider_data( array $meta, $settings = [] ) {
455 - $has_dots = $meta['slider_dot'] ? ' has-dot' : ' no-dot';
456 - $has_dots .= $meta['slider_nav'] ? ' has-nav' : ' no-nav';
457 - $d_col = 0 === $meta['d_cols'] ? self::default_columns( $meta['layout'] ) : $meta['d_cols'];
458 - $t_col = 0 === $meta['t_cols'] ? 2 : $meta['t_cols'];
459 - $m_col = 0 === $meta['m_cols'] ? 1 : $meta['m_cols'];
716 + $has_dots = $meta['slider_dot'] ? ' has-dot' : ' no-dot';
717 + $has_dots .= $meta['slider_nav'] ? ' has-nav' : ' no-nav';
718 + $has_dynamic_dots = $meta['slider_dynamic_dot'] ? true : false;
719 + $d_col = 0 === $meta['d_cols'] ? self::default_columns( $meta['layout'] ) : $meta['d_cols'];
720 + $t_col = 0 === $meta['t_cols'] ? 2 : $meta['t_cols'];
721 + $m_col = 0 === $meta['m_cols'] ? 1 : $meta['m_cols'];
460 722
461 - if ( \Elementor\Plugin::$instance->breakpoints->has_custom_breakpoints() ) {
723 + if ( Plugin::$instance->breakpoints->has_custom_breakpoints() ) {
462 724 // Widescreen.
463 - $w_col = ! empty( $settings['cols_widescreen'] ) ? $settings['cols_widescreen'] : null;
725 + $w_col = self::get_data( $settings, 'cols_widescreen' );
464 726 $w_col = 0 === $w_col ? self::default_columns( $meta['layout'] ) : $w_col;
465 - $w_group = ! empty( $settings['cols_group_widescreen'] ) ? $settings['cols_group_widescreen'] : 1;
727 + $w_group = self::get_data( $settings, 'cols_group_widescreen', 1 );
466 728
467 729 // Laptop.
468 - $l_col = ! empty( $settings['cols_laptop'] ) ? $settings['cols_laptop'] : null;
730 + $l_col = self::get_data( $settings, 'cols_laptop' );
469 731 $l_col = 0 === $l_col ? self::default_columns( $meta['layout'] ) : $l_col;
470 - $l_group = ! empty( $settings['cols_group_laptop'] ) ? $settings['cols_group_laptop'] : 1;
732 + $l_group = self::get_data( $settings, 'cols_group_laptop', 1 );
471 733
472 734 // Tablet Landscape.
473 - $te_col = ! empty( $settings['cols_tablet_extra'] ) ? $settings['cols_tablet_extra'] : null;
735 + $te_col = self::get_data( $settings, 'cols_tablet_extra' );
474 736 $te_col = 0 === $te_col ? 3 : $te_col;
475 - $te_group = ! empty( $settings['cols_group_tablet_extra'] ) ? $settings['cols_group_tablet_extra'] : 1;
737 + $te_group = self::get_data( $settings, 'cols_group_tablet_extra', 1 );
476 738
477 739 // Mobile Landscape.
478 - $me_col = ! empty( $settings['cols_mobile_extra'] ) ? $settings['cols_mobile_extra'] : null;
740 + $me_col = self::get_data( $settings, 'cols_mobile_extra' );
479 741 $me_col = 0 === $me_col ? 2 : $me_col;
480 - $me_group = ! empty( $settings['cols_group_mobile_extra'] ) ? $settings['cols_group_mobile_extra'] : 1;
742 + $me_group = self::get_data( $settings, 'cols_group_mobile_extra', 1 );
481 743
482 744 $el_breakpoints = Fns::get_elementor_breakpoints();
483 745
484 746 foreach ( $el_breakpoints as $key => $value ) {
@@ -491,9 +753,9 @@
491 753 0 => [
492 754 'slidesPerView' => absint( $m_col ),
493 755 'slidesPerGroup' => absint( $meta['m_group'] ),
494 756 'pagination' => [
495 - 'dynamicBullets' => true,
757 + 'dynamicBullets' => $has_dynamic_dots,
496 758 ],
497 759 ],
498 760 ];
499 761
@@ -518,16 +780,16 @@
518 780 }
519 781
520 782 foreach ( $el_breakpoints as $el_breakpoint => $value ) {
521 783 $breakpoint_value = ! empty( $value['value'] ) ? $value['value'] : $value['default_value'];
522 - $dynamic_bullets = false;
784 + $dynamic_bullets = $has_dynamic_dots;
523 785 $slides_per_view = $d_col;
524 786 $slides_per_group = $meta['d_group'];
525 787
526 788 switch ( $el_breakpoint ) {
527 789 case 'widescreen':
528 - $slides_per_view = $w_col;
529 - $slides_per_group = $w_group;
790 + $slides_per_view = $w_col;
791 + $slides_per_group = $w_group;
530 792
531 793 break;
532 794
533 795 case 'laptop':
@@ -536,10 +798,10 @@
536 798
537 799 break;
538 800
539 801 case 'tablet_extra':
540 - $slides_per_view = $l_col;
541 - $slides_per_group = $l_group;
802 + $slides_per_view = $l_col;
803 + $slides_per_group = $l_group;
542 804
543 805 if ( empty( $el_breakpoints['laptop'] ) ) {
544 806 $slides_per_view = $d_col;
545 807 $slides_per_group = $meta['d_group'];
@@ -564,11 +826,11 @@
564 826
565 827 break;
566 828
567 829 case 'mobile_extra':
568 - $slides_per_view = $t_col;
569 - $slides_per_group = $meta['t_group'];
570 - $dynamic_bullets = true;
830 + $slides_per_view = $t_col;
831 + $slides_per_group = $meta['t_group'];
832 + $dynamic_bullets = $has_dynamic_dots;
571 833
572 834 break;
573 835
574 836 case 'mobile':
@@ -573,9 +835,9 @@
573 835
574 836 case 'mobile':
575 837 $slides_per_view = $t_col;
576 838 $slides_per_group = $meta['t_group'];
577 - $dynamic_bullets = true;
839 + $dynamic_bullets = $has_dynamic_dots;
578 840
579 841 if ( ! empty( $el_breakpoints['mobile_extra'] ) ) {
580 842 $slides_per_view = $me_col;
581 843 $slides_per_group = $me_group;
@@ -601,9 +863,9 @@
601 863 0 => [
602 864 'slidesPerView' => absint( $m_col ),
603 865 'slidesPerGroup' => absint( $meta['m_group'] ),
604 866 'pagination' => [
605 - 'dynamicBullets' => true,
867 + 'dynamicBullets' => $has_dynamic_dots,
606 868 ],
607 869 ],
608 870 768 => [
609 871 'slidesPerView' => absint( $t_col ),
@@ -608,9 +870,9 @@
608 870 768 => [
609 871 'slidesPerView' => absint( $t_col ),
610 872 'slidesPerGroup' => absint( $meta['t_group'] ),
611 873 'pagination' => [
612 - 'dynamicBullets' => false,
874 + 'dynamicBullets' => $has_dynamic_dots,
613 875 ],
614 876 ],
615 877 1025 => [
616 878 'slidesPerView' => absint( $d_col ),
@@ -615,9 +877,9 @@
615 877 1025 => [
616 878 'slidesPerView' => absint( $d_col ),
617 879 'slidesPerGroup' => absint( $meta['d_group'] ),
618 880 'pagination' => [
619 - 'dynamicBullets' => false,
881 + 'dynamicBullets' => $has_dynamic_dots,
620 882 ],
621 883 ],
622 884 ];
623 885 }
@@ -632,8 +894,11 @@
632 894 'lazy' => $meta['lazy_load'],
633 895 'breakpoints' => $breakpoints,
634 896 ];
635 897
898 + if ( ! empty( $meta['raw_settings']['slider_slide_animation'] ) ) {
899 + $slider_options['watchSlidesProgress'] = true;
900 + }
636 901 if ( $meta['auto_play'] ) {
637 902 $slider_options['autoplay'] = [
638 903 'delay' => absint( $meta['auto_play_timeout'] ),
639 904 'pauseOnMouseEnter' => $meta['stop_on_hover'],
@@ -640,72 +905,18 @@
640 905 'disableOnInteraction' => false,
641 906 ];
642 907 }
643 908
644 - $carouselClass = 'rtsb-carousel-slider slider-loading rtsb-pos-s ' . $meta['nav_position'] . '-nav' . $has_dots;
909 + $slider_options = apply_filters( 'rtsb/elementor/render/slider_dataset', $slider_options, $meta );
910 + $carouselClass = 'rtsb-carousel-slider slider-loading rtsb-pos-s ' . $meta['nav_position'] . '-nav' . $has_dots;
645 911
646 912 return [
647 - 'data' => ' data-options=\'' . esc_js( wp_json_encode( $slider_options ) ) . '\'',
913 + 'data' => esc_js( wp_json_encode( $slider_options ) ),
648 914 'class' => $carouselClass,
649 915 ];
650 916 }
651 917
652 918 /**
653 - * Renders pagination
654 - *
655 - * @param object $wp_query WP_Query object.
656 - * @param array $meta Meta values.
657 - * @param int $limit Post limit.
658 - * @param int $per_page Posts per page.
659 - *
660 - * @return string
661 - */
662 - public static function render_pagination( $wp_query, $meta, $limit, $per_page ) {
663 - $html_utility = null;
664 - $html = null;
665 - $ajax = false;
666 - $is_grid = preg_match( '/grid-layout/', $meta['layout'] ) || preg_match( '/list-layout/', $meta['layout'] );
667 - $posts_per_page = $wp_query->query_vars['posts_per_page'];
668 - $total_page = $wp_query->max_num_pages;
669 -
670 - if ( $limit && ( empty( $wp_query->query['tax_query'] ) ) ) {
671 - $found_post = $wp_query->found_posts;
672 -
673 - if ( $per_page && $found_post > $per_page ) {
674 - $found_post = $limit;
675 - $total_page = ceil( $found_post / $per_page );
676 - }
677 - }
678 -
679 - $total_page = absint( $total_page );
680 -
681 - $args = [
682 - 'total_page' => $total_page,
683 - 'posts_per_page' => $posts_per_page,
684 - 'ajax' => $ajax,
685 - ];
686 -
687 - if ( 'pagination' === $meta['posts_loading_type'] ) {
688 - if ( $is_grid ) {
689 - $html_utility .= Fns::custom_pagination(
690 - $total_page,
691 - $posts_per_page
692 - );
693 - }
694 - } else {
695 - ob_start();
696 - do_action( 'rtsb/elementor/render/pagination', $meta, $wp_query, $args );
697 - $html_utility .= ob_get_clean();
698 - }
699 -
700 - if ( $html_utility ) {
701 - $html .= '<div class="rtsb-pagination-wrap element-loading" data-total-pages="' . absint( $total_page ) . '" data-posts-per-page="' . absint( $posts_per_page ) . '" data-type="' . esc_attr( $meta['posts_loading_type'] ) . '">' . $html_utility . '</div>';
702 - }
703 -
704 - return $html;
705 - }
706 -
707 - /**
708 919 * Preloader.
709 920 *
710 921 * @param string $layout Layout.
711 922 *
@@ -761,10 +972,11 @@
761 972 *
762 973 * @return void
763 974 */
764 975 public static function register_scripts( $scripts ) {
765 - $caro = false;
766 - $script = [];
976 + $caro = false;
977 + $masonry = false;
978 + $script = [];
767 979
768 980 $script[] = 'jquery';
769 981
770 982 foreach ( $scripts as $sc => $value ) {
@@ -771,8 +983,12 @@
771 983 if ( ! empty( $sc ) ) {
772 984 if ( 'isCarousel' === $sc ) {
773 985 $caro = $value;
774 986 }
987 +
988 + if ( 'isMasonry' === $sc ) {
989 + $masonry = $value;
990 + }
775 991 }
776 992 }
777 993
778 994 if ( count( $scripts ) ) {
@@ -782,12 +998,18 @@
782 998 if ( $caro ) {
783 999 $script[] = 'swiper';
784 1000 }
785 1001
1002 + if ( $masonry ) {
1003 + $script[] = 'masonry';
1004 + }
1005 +
786 1006 $script[] = 'rtsb-imagesloaded';
787 1007 $script[] = 'rtsb-public';
788 1008
789 - wp_enqueue_script( $script );
1009 + foreach ( $script as $sc ) {
1010 + wp_enqueue_script( $sc );
1011 + }
790 1012 }
791 1013 }
792 1014
793 1015 /**
@@ -813,6 +1035,764 @@
813 1035 return true;
814 1036 }
815 1037
816 1038 return false;
1039 + }
1040 +
1041 + /**
1042 + * Get the number of products per page based on WooCommerce settings.
1043 + *
1044 + * @return int
1045 + */
1046 + public static function get_products_per_page() {
1047 + $products_row = absint( get_option( 'woocommerce_catalog_rows', 4 ) );
1048 + $products_col = absint( get_option( 'woocommerce_catalog_columns', 4 ) );
1049 + $products_per_page = apply_filters( 'rtsb/elementor/archive/products_per_page', $products_row * $products_col );
1050 +
1051 + return ! empty( $products_per_page ) ? $products_per_page : 12;
1052 + }
1053 + /**
1054 + * Render Filters View.
1055 + *
1056 + * @param array $templates Template name.
1057 + * @param array $settings Control settings.
1058 + *
1059 + * @return string
1060 + */
1061 + public static function filters_view( $templates, $settings ) {
1062 + global $wp;
1063 + $filters = [];
1064 + $html = null;
1065 + $reset = ! empty( $settings['reset_btn'] );
1066 + $reset_mode = 'show' === $settings['reset_btn_behavior'] ? ' show-reset' : ' ondemand-reset';
1067 + $reset_text = ! empty( $settings['reset_btn_text'] ) ? $settings['reset_btn_text'] : esc_html__( 'Reset Filter', 'shopbuilder' );
1068 + $scroll_mode = ! empty( $settings['enable_scroll'] ) ? ' has-scroll' : ' no-scroll';
1069 + $scroll_height = ! empty( $settings['enable_scroll'] ) ? $settings['scroll_height']['size'] : 300;
1070 + $has_mobile_toggle = ! empty( $settings['filter_mobile_toggle'] );
1071 + $toggle_class = $has_mobile_toggle ? ' default-filter-has-toggle' : ' default-filter-no-toggle';
1072 + $mobile_toggle_text = ! empty( $settings['filter_mobile_toggle_text'] ) ? $settings['filter_mobile_toggle_text'] : esc_html__( 'Click to Filter', 'shopbuilder' );
1073 + $scroll_attr = '';
1074 +
1075 + if ( ! empty( $settings['enable_scroll'] ) ) {
1076 + $scroll_attr = ' style="--rtsb-filter-scroll-height: ' . absint( $scroll_height ) . 'px;"';
1077 + }
1078 +
1079 + foreach ( $settings['filter_types'] as $key => $item ) {
1080 + if ( ! defined( 'RTWPVS_PLUGIN_FILE' ) && in_array( $item['input_type_all'], [ 'color', 'button', 'image' ], true ) ) {
1081 + $item['input_type_all'] = 'checkbox';
1082 + }
1083 + $filters[] = [
1084 + 'filter_title' => $item['filter_title'],
1085 + 'filter_items' => $item['filter_items'],
1086 + 'filter_attr' => $item['filter_attr'],
1087 + 'input_type' => 'product_attr' === $item['filter_items'] ? $item['input_type_all'] : $item['input_type'],
1088 + ];
1089 +
1090 + if ( 'rating_filter' === $item['filter_items'] ) {
1091 + $filters[ $key ]['template'] = $templates['rating'];
1092 + } elseif ( 'price_filter' === $item['filter_items'] ) {
1093 +
1094 + $filters[ $key ]['input_type'] = 'slider';
1095 + $filters[ $key ]['template'] = $templates['price'];
1096 + } else {
1097 + $filters[ $key ]['template'] = 'product_attr' === $item['filter_items'] ? $templates[ $item['input_type_all'] ] : $templates[ $item['input_type'] ];
1098 + }
1099 + }
1100 +
1101 + if ( '' === get_option( 'permalink_structure' ) ) {
1102 + $form_action = remove_query_arg( [ 'page', 'paged', 'product-page' ], add_query_arg( $wp->query_string, '', home_url( $wp->request ) ) );
1103 + } else {
1104 + $form_action = preg_replace( '%\/page/[0-9]+%', '', home_url( trailingslashit( $wp->request ) ) );
1105 + }
1106 + $args = [
1107 + 'filters' => $filters,
1108 + 'scroll_mode' => $scroll_mode,
1109 + 'reset_mode' => $reset_mode,
1110 + 'reset' => $reset,
1111 + 'scroll_attr' => $scroll_attr,
1112 + 'toggle_class' => $toggle_class,
1113 + 'reset_text' => $reset_text,
1114 + 'settings' => $settings,
1115 + 'form_action' => $form_action,
1116 + ];
1117 +
1118 + $html .= '<div class="rtsb-default-archive-filters' . esc_attr( $toggle_class ) . '">';
1119 +
1120 + if ( $has_mobile_toggle ) {
1121 + $html .= '<div class="rtsb-filter-mobile-toggle">
1122 + <button class="product-filter-toggle">
1123 + <span class="icon"><i aria-hidden="true" class="toggle-icon rtsb-icon rtsb-icon-filter"></i></span>
1124 + <span class="text reset-text">' . esc_html( $mobile_toggle_text ) . '</span>
1125 + <span></span>
1126 + </button>
1127 + </div>';
1128 + }
1129 +
1130 + $html .= Fns::load_template( $templates['layout'], $args, true );
1131 + $html .= '</div>';
1132 + return $html;
1133 + }
1134 + /**
1135 + * Get taxonomy type.
1136 + *
1137 + * @param string $tax_type Taxonomy type.
1138 + * @param string $attr_type Attribute type.
1139 + *
1140 + * @return string
1141 + */
1142 + public static function get_product_filters_tax_type( $tax_type, $attr_type = '' ) {
1143 + if ( 'product_attr' !== $tax_type ) {
1144 + return $tax_type;
1145 + }
1146 +
1147 + if ( empty( $attr_type ) ) {
1148 + return $tax_type;
1149 + }
1150 +
1151 + return $attr_type;
1152 + }
1153 + /**
1154 + * Get attribute terms.
1155 + *
1156 + * @param string $tax Taxonomy type.
1157 + * @return int[]|string|string[]|\WP_Error|\WP_Term[]
1158 + */
1159 + public static function get_product_filters_attribute_terms( $tax ) {
1160 + $cache_key = 'get_default_filter_attribute_terms_' . $tax;
1161 + if ( isset( self::$cache[ $cache_key ] ) ) {
1162 + return self::$cache[ $cache_key ];
1163 + }
1164 +
1165 + $attributes = wc_get_attribute_taxonomies();
1166 + $att_name = str_replace( 'pa_', '', $tax );
1167 + $exist = array_search( $att_name, array_column( $attributes, 'attribute_name' ), true );
1168 + if ( false === $exist ) {
1169 + self::$cache[ $cache_key ] = [];
1170 + return self::$cache[ $cache_key ];
1171 + }
1172 + $attribute = $tax;
1173 +
1174 + if ( ! is_shop() && is_product_taxonomy() ) {
1175 + $term = get_queried_object();
1176 +
1177 + if ( ! $term instanceof WP_Term ) {
1178 + return [];
1179 + }
1180 +
1181 + $args = [
1182 + 'status' => 'publish',
1183 + 'limit' => -1,
1184 + 'return' => 'ids',
1185 + ];
1186 +
1187 + if ( 'product_cat' === $term->taxonomy ) {
1188 + $args['category'] = [ $term->slug ];
1189 + }
1190 +
1191 + if ( 'product_tag' === $term->taxonomy ) {
1192 + $args['tag'] = [ $term->slug ];
1193 + }
1194 +
1195 + if ( strpos( $term->taxonomy, 'pa_' ) !== false ) {
1196 + $args['tax_query'] = [ // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
1197 + [
1198 + 'taxonomy' => $term->taxonomy,
1199 + 'field' => 'term_id',
1200 + 'terms' => $term->term_id,
1201 + 'operator' => 'IN',
1202 + ],
1203 + ];
1204 + }
1205 +
1206 + $query = new WC_Product_Query( $args );
1207 + $products = $query->get_products();
1208 + $attr_terms = [];
1209 +
1210 + foreach ( $products as $product_id ) {
1211 + $product_attr = wp_get_post_terms( $product_id, $attribute );
1212 +
1213 + foreach ( $product_attr as $attr ) {
1214 + $attr_terms[] = $attr;
1215 + }
1216 + }
1217 + self::$cache[ $cache_key ] = self::filter_products_array_unique( $attr_terms );
1218 + return self::$cache[ $cache_key ];
1219 + } else {
1220 + self::$cache[ $cache_key ] = get_terms(
1221 + [
1222 + 'taxonomy' => $attribute,
1223 + 'hide_empty' => false,
1224 + ]
1225 + );
1226 + return self::$cache[ $cache_key ];
1227 + }
1228 + }
1229 + /**
1230 + * Remove duplicate values from an array.
1231 + *
1232 + * @param array $array The input array.
1233 + * @param bool $keep_key_assoc Whether to keep the key associations after removing duplicates.
1234 + * @return array The array with duplicates removed.
1235 + */
1236 + public static function filter_products_array_unique( $array, $keep_key_assoc = false ) {
1237 + $duplicate_keys = [];
1238 + $tmp = [];
1239 +
1240 + foreach ( $array as $key => $val ) {
1241 + // convert objects to arrays, in_array() does not support objects.
1242 + if ( is_object( $val ) ) {
1243 + $val = (array) $val;
1244 + }
1245 +
1246 + if ( ! in_array( $val, $tmp, true ) ) {
1247 + $tmp[] = $val;
1248 + } else {
1249 + $duplicate_keys[] = $key;
1250 + }
1251 + }
1252 +
1253 + foreach ( $duplicate_keys as $key ) {
1254 + unset( $array[ $key ] );
1255 + }
1256 +
1257 + return $keep_key_assoc ? $array : array_values( $array );
1258 + }
1259 + /**
1260 + * Retrieve product categories (including hierarchical support)
1261 + *
1262 + * @param string $taxonomy Taxonomy name.
1263 + * @return int[]|string|string[]|\WP_Error|\WP_Term[]
1264 + */
1265 + public static function get_products( $taxonomy ) {
1266 + $args = [
1267 + 'taxonomy' => esc_html( $taxonomy ),
1268 + 'hide_empty' => false,
1269 + 'pad_counts' => true,
1270 + 'hierarchical' => true,
1271 + ];
1272 +
1273 + $cache_key = 'rtsb_get_terms_filter_products' . $taxonomy;
1274 + $taxonomy_terms = wp_cache_get( $cache_key, 'shopbuilder' );
1275 +
1276 + if ( false === $taxonomy_terms ) {
1277 + $queried_object = get_queried_object();
1278 +
1279 + if ( 'archive' === apply_filters( 'rtsb/builder/set/current/page/type', '' ) && $queried_object instanceof WP_Term && $queried_object->taxonomy === $taxonomy ) {
1280 + $term_id = $queried_object->term_id;
1281 + $taxonomy = $queried_object->taxonomy;
1282 + $archive_cache_key = 'rtsb_get_terms_filter_products_' . $taxonomy . $term_id;
1283 + $taxonomy_terms = wp_cache_get( $archive_cache_key, 'shopbuilder' );
1284 + if ( false === $taxonomy_terms ) {
1285 + $taxonomy_terms = [ $queried_object ];
1286 + $child_args = [
1287 + 'taxonomy' => $taxonomy,
1288 + 'child_of' => $term_id,
1289 + 'hide_empty' => false,
1290 + ];
1291 +
1292 + $child_terms = get_terms( $child_args );
1293 +
1294 + if ( ! empty( $child_terms ) ) {
1295 + $taxonomy_terms = array_merge( $taxonomy_terms, $child_terms );
1296 + }
1297 +
1298 + wp_cache_set( $archive_cache_key, $taxonomy_terms, 'shopbuilder' );
1299 + Cache::set_data_cache_key( $archive_cache_key );
1300 + }
1301 + } else {
1302 + $taxonomy_terms = get_terms( $args );
1303 + }
1304 +
1305 + wp_cache_set( $cache_key, $taxonomy_terms, 'shopbuilder' );
1306 + Cache::set_data_cache_key( $cache_key );
1307 + }
1308 +
1309 + return $taxonomy_terms;
1310 + }
1311 + /**
1312 + * Generates an error message block.
1313 + *
1314 + * @param array $title The title array of the error message block.
1315 + * @param string $wrapper Additional CSS class for the wrapper div.
1316 + *
1317 + * @return string The HTML representation of the error message block.
1318 + */
1319 + public static function filter_error_message( $title, $wrapper ) {
1320 + $html = '<div class="rtsb-product-default-filters ' . esc_attr( $wrapper ) . '">';
1321 + $html .= self::get_default_filter_title( $title );
1322 + $html .= '<div class="default-filter-content">';
1323 + $html .= '<p class="no-filter">' . esc_html__( 'Nothing found.', 'shopbuilder' ) . '</p>';
1324 + $html .= '</div>';
1325 + $html .= '</div>';
1326 +
1327 + return $html;
1328 + }
1329 + /**
1330 + * Get filter title.
1331 + *
1332 + * @param array $title Filter title.
1333 + * @return string
1334 + */
1335 + public static function get_default_filter_title( $title ) {
1336 + $html = '';
1337 +
1338 + if ( empty( $title['title'] ) ) {
1339 + return $html;
1340 + }
1341 +
1342 + $html .= '<div class="default-filter-title-wrapper">';
1343 +
1344 + $html .= '<h3 class="widget-title rtsb-d-flex rtsb-align-items-center"><span class="title">' . $title['title'] . '</span></h3>';
1345 +
1346 + $html .= '</div>';
1347 +
1348 + return apply_filters( 'rtsb/elementor/default/filter/title', $html, $title );
1349 + }
1350 + /**
1351 + * Recursive function to generate the filter list with hierarchy.
1352 + *
1353 + * @param array $data Array of required data.
1354 + * @param string $input Type of input field for the filter (e.g., checkbox).
1355 + * @param array $additional_data Data for additional filtering.
1356 + * @param int $parent ID of the parent term (default: 0).
1357 + *
1358 + * @return string The generated HTML for the product filter list.
1359 + */
1360 + public static function get_product_default_filter_list_html( $data, $input = 'checkbox', $additional_data = [], $parent = 0 ) {
1361 +
1362 + $html = '';
1363 + $default_tax = [];
1364 + $is_attribute = ! empty( $data['is_attribute'] );
1365 + $active_option = isset( $_GET[ $additional_data['taxonomy'] ] ) ? explode( ',', wc_clean( wp_unslash( $_GET[ $additional_data['taxonomy'] ] ) ) ) : []; // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
1366 + $counter = 0;
1367 +
1368 + if ( $is_attribute ) {
1369 + list(
1370 + 'filter_name' => $filter_name,
1371 + 'base_link' => $base_link
1372 + ) = self::get_product_filters_attribute_term_info( $additional_data['taxonomy'] );
1373 + }
1374 +
1375 + $html .= '<ul class="product-default-filters input-type-' . esc_attr( $input ) . '">';
1376 +
1377 + foreach ( $data['taxonomy'] as $tax ) {
1378 + if ( $tax->parent !== $parent ) {
1379 + continue;
1380 + }
1381 +
1382 + if ( $is_attribute ) {
1383 + $tax_link = remove_query_arg( $filter_name, $base_link );
1384 + $tax_link = add_query_arg( $filter_name, $tax->slug, $tax_link );
1385 + $active_option = isset( $_GET[ $filter_name ] ) ? explode( ',', sanitize_text_field( wp_unslash( $_GET[ $filter_name ] ) ) ) : []; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1386 + } else {
1387 + $tax_link = get_term_link( $tax );
1388 + }
1389 +
1390 + $unique_id = substr( uniqid(), -4 );
1391 +
1392 + $active_tax = in_array( $tax->slug, $active_option, true ) ? ' checked' : '';
1393 + $active_tax_parent = in_array( $tax->slug, $active_option, true ) ? ' active' : '';
1394 + $term_children = in_array( $tax->taxonomy, [ 'product_cat', 'product_brand' ], true ) ? get_term_children( $tax->term_id, $tax->taxonomy ) : [];
1395 + $taxonomy = $tax->taxonomy;
1396 + $product_count = $tax->count;
1397 +
1398 + // Show count.
1399 + if ( 'archive' === apply_filters( 'rtsb/builder/set/current/page/type', '' ) && ! empty( $data['count_display'] ) && 'block' === $data['count_display'] ) {
1400 + $product_count = self::count_tax_data( $tax, $tax->count );
1401 + }
1402 +
1403 + // Show Empty Categories.
1404 + if ( empty( $data['show_empty_terms'] ) && 0 === $product_count ) {
1405 + continue;
1406 + }
1407 +
1408 + $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' : '' ) . '">
1409 + <div class="rtsb-default-filter-group' . esc_attr( $active_tax_parent ) . '">
1410 + <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 ) . '>
1411 + <label class="rtsb-default-' . esc_attr( $input ) . '-filter-label" for="rtsb-default-term-filter-' . $unique_id . absint( $tax->term_id ) . '">
1412 + <span class="name">' . esc_html( $tax->name ) . '</span>
1413 + </label>
1414 + <div class="rtsb-product-count">(' . esc_html( $product_count ) . ')</div>
1415 + ' . ( ! empty( $term_children ) && $data['show_child'] ? '<i class="rtsb-plus-icon"></i>' : '' ) . '
1416 + </div>';
1417 +
1418 + // Show Sub-categories.
1419 + if ( $data['show_child'] ) {
1420 + $children = self::get_product_default_filter_list_html( $data, $input, $additional_data, $tax->term_id );
1421 +
1422 + if ( ! empty( $children ) ) {
1423 + $html .= '<div class="filter-child">' . $children . '</div>';
1424 + }
1425 + }
1426 +
1427 + $counter++;
1428 +
1429 + $html .= '</li>';
1430 + }
1431 +
1432 + $html .= '</ul>';
1433 +
1434 + // Check if the output contains any child elements.
1435 + $has_children = strpos( $html, '<li class="rtsb-default-filter-term-item' ) !== false;
1436 +
1437 + if ( ! $has_children ) {
1438 + // Remove the outer <ul> if there are no child elements.
1439 + $html = '';
1440 + }
1441 + return $html;
1442 + }
1443 + /**
1444 + * Get attribute term info.
1445 + *
1446 + * @param string $tax Taxonomy type.
1447 + * @return array
1448 + */
1449 + public static function get_product_filters_attribute_term_info( $tax ) {
1450 + $result = [];
1451 + $attributes = wc_get_attribute_taxonomies();
1452 +
1453 + foreach ( $attributes as $attr ) {
1454 + if ( str_replace( 'pa_', '', $tax ) === $attr->attribute_name ) {
1455 + $term_type = $attr->attribute_type;
1456 + $attribute_slug = $attr->attribute_name;
1457 + $attribute = wc_attribute_taxonomy_name( $attr->attribute_name );
1458 + break;
1459 + }
1460 + }
1461 +
1462 + $filter_name = 'filter_' . wc_attribute_taxonomy_slug( $attribute_slug );
1463 +
1464 + $result['attribute'] = $attribute ?? '';
1465 + $result['term_type'] = $term_type ?? '';
1466 + $result['attribute_slug'] = $attribute_slug ?? '';
1467 + $result['filter_name'] = $filter_name;
1468 + $result['base_link'] = self::get_base_url();
1469 +
1470 + return $result;
1471 + }
1472 + /**
1473 + * Get base URL.
1474 + *
1475 + * @return string|null
1476 + */
1477 + public static function get_base_url() {
1478 + global $wp;
1479 + return home_url( add_query_arg( [], $wp->request ) );
1480 + }
1481 + /**
1482 + * Count the number of occurrences for a specific term in a taxonomy for the current queried object.
1483 + *
1484 + * @param object|array $tax The term object for the taxonomy.
1485 + * @param int $count The initial count value.
1486 + *
1487 + * @return int The updated count value.
1488 + */
1489 + public static function count_tax_data( $tax, $count ) {
1490 + $page = get_queried_object();
1491 +
1492 + if ( is_array( $tax ) && strpos( $tax['taxonomy'], 'attribute_' ) !== false ) {
1493 + $taxonomy = str_replace( 'attribute_', '', $tax['taxonomy'] );
1494 + $term_id = $tax['term_id'];
1495 + } else {
1496 + $taxonomy = $tax->taxonomy;
1497 + $term_id = $tax->term_id;
1498 + }
1499 +
1500 + if ( strpos( $page->taxonomy, 'pa_' ) !== false ) {
1501 + $args['status'] = 'publish';
1502 + $args['limit'] = -1;
1503 + $args['return'] = 'ids';
1504 + $args['tax_query'] = [ // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
1505 + 'relation' => 'AND',
1506 + [
1507 + 'taxonomy' => $page->taxonomy,
1508 + 'field' => 'term_id',
1509 + 'terms' => $page->term_id,
1510 + 'operator' => 'IN',
1511 + ],
1512 + [
1513 + 'taxonomy' => $tax->taxonomy,
1514 + 'field' => 'term_id',
1515 + 'terms' => $tax->term_id,
1516 + 'operator' => 'IN',
1517 + ],
1518 + ];
1519 +
1520 + $query = new WC_Product_Query( $args );
1521 + $products = $query->get_products();
1522 +
1523 + return count( $products );
1524 + }
1525 +
1526 + if ( $taxonomy !== $page->taxonomy ) {
1527 + $args = [
1528 + 'limit' => -1,
1529 + 'return' => 'ids',
1530 + 'status' => 'publish',
1531 + 'tax_query' => [ // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
1532 + [
1533 + 'taxonomy' => $taxonomy,
1534 + 'field' => 'term_id',
1535 + 'terms' => $term_id,
1536 + ],
1537 + ],
1538 + ];
1539 +
1540 + if ( 'product_cat' === $page->taxonomy ) {
1541 + $args['category'] = [ $page->slug ];
1542 + }
1543 +
1544 + if ( 'product_tag' === $page->taxonomy ) {
1545 + $args['tag'] = [ $page->slug ];
1546 + }
1547 +
1548 + $query = new WC_Product_Query( $args );
1549 + $products = $query->get_products();
1550 +
1551 + $count = count( $products );
1552 + }
1553 +
1554 + return $count;
1555 + }
1556 + /**
1557 + * Get filter HTML.
1558 + *
1559 + * @param string $filter_type Filter type.
1560 + * @param array $options Options.
1561 + * @param array $additional_data Data for additional filtering.
1562 + * @param string $input Input type.
1563 + *
1564 + * @return string The generated HTML for the attribute filter list.
1565 + */
1566 + public static function get_default_filter_html( $filter_type, $options, $additional_data = [], $input = 'checkbox' ) {
1567 + $html = '<ul class="product-filters input-type-' . esc_attr( $input ) . '" ';
1568 + $active_option = isset( $_GET[ $filter_type ] ) ? explode( ',', wc_clean( wp_unslash( $_GET[ $filter_type ] ) ) ) : []; // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
1569 + $option_link = self::get_base_url() . '/' . $filter_type . '/';
1570 +
1571 + foreach ( $options as $option => $option_name ) {
1572 + $unique_id = substr( uniqid(), -6 );
1573 + $active_tax = in_array( $option, $active_option, true ) ? ' checked' : '';
1574 + $active_group = in_array( $option, $active_option, true ) ? ' active' : '';
1575 +
1576 + $html .= '
1577 + <li class="rtsb-term-item">
1578 + <div class="rtsb-default-filter-group' . esc_attr( $active_group ) . '">
1579 + <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 . '>
1580 + <label class="rtsb-default-' . esc_attr( $input ) . '-filter-label" for="rtsb-term-default-filter-' . $unique_id . '">
1581 + <span class="name">' . esc_html( $option_name ) . '</span>
1582 + </label>
1583 + </div>
1584 + </li>';
1585 + }
1586 +
1587 + $html .= '</ul>';
1588 +
1589 + return $html;
1590 + }
1591 + /**
1592 + * Get the count of products with a specific star rating.
1593 + *
1594 + * @param int $star_rating The star rating value.
1595 + *
1596 + * @return int
1597 + */
1598 + public static function get_product_rating_count( $star_rating ) {
1599 + $args = [
1600 + 'status' => 'publish',
1601 + 'limit' => -1,
1602 + 'product_rating' => sprintf( '%.1f', $star_rating ),
1603 + ];
1604 +
1605 + if ( is_product_taxonomy() ) {
1606 + $term = get_queried_object();
1607 + $term_type = 'product_cat' === $term->taxonomy ? 'product_category_id' : 'product_tag_id';
1608 +
1609 + if ( $term instanceof WP_Term ) {
1610 + $args[ $term_type ] = [ $term->term_id ];
1611 + }
1612 + }
1613 +
1614 + $query = new WC_Product_Query( $args );
1615 + $products = $query->get_products();
1616 +
1617 + return count( $products );
1618 + }
1619 + /**
1620 + * Get attribute filter HTML.
1621 + *
1622 + * @param array $data Array of required data.
1623 + * @param string $input Type of input field for the filter (e.g., color).
1624 + * @param array $additional_data Data for additional filtering.
1625 + *
1626 + * @return string The generated HTML for the attribute filter list.
1627 + */
1628 + public static function get_attribute_filter_list_html( $data, $input = 'color', $additional_data = [] ) {
1629 + if ( ! function_exists( 'rtwpvs' ) ) {
1630 + return '';
1631 + }
1632 +
1633 + $terms = $data['terms'];
1634 + $term_info = $data['term_info'];
1635 + $show_tooltips = $data['show_tooltips'];
1636 + $show_empty_terms = $data['show_empty_terms'];
1637 + $counter = 0;
1638 +
1639 + $html = '<ul class="product-default-filters rtsb-terms-wrapper ' . esc_attr( $term_info['type'] ) . '-variable-wrapper' . esc_attr( $data['show_label'] ) . '">';
1640 +
1641 + foreach ( $terms as $attr_term ) {
1642 + $count = $attr_term->count;
1643 + $term_link = remove_query_arg( $term_info['filter_name'], $term_info['base_link'] );
1644 + $term_link = add_query_arg( $term_info['filter_name'], $attr_term->slug, $term_link );
1645 + $unique_id = substr( uniqid(), -6 );
1646 + $name = 'term-' . wc_variation_attribute_name( $term_info['attribute'] ) . '-' . $unique_id;
1647 + $selected_class = in_array( $attr_term->slug, $term_info['current_filter'], true ) ? ' selected' : '';
1648 + $selected_item = in_array( $attr_term->slug, $term_info['current_filter'], true ) ? ' checked' : '';
1649 + $active_class = in_array( $attr_term->slug, $term_info['current_filter'], true ) ? ' active' : '';
1650 +
1651 + $args = [
1652 + 'id' => $name,
1653 + 'name' => $attr_term->name,
1654 + 'link' => $term_link,
1655 + 'type' => $term_info['type'],
1656 + 'term_id' => $attr_term->term_id,
1657 + 'term_slug' => $attr_term->slug,
1658 + 'taxonomy' => wc_variation_attribute_name( $term_info['attribute'] ),
1659 + 'tooltips' => $show_tooltips,
1660 + 'attribute_name' => $term_info['attribute'],
1661 + 'selected_item' => $selected_item,
1662 + ];
1663 +
1664 + if ( 'archive' === apply_filters( 'rtsb/builder/set/current/page/type', '' ) && ! empty( $data['count_display'] ) && 'block' === $data['count_display'] ) {
1665 + $count = self::count_tax_data( $args, $attr_term->count );
1666 + }
1667 +
1668 + $args['count'] = $count;
1669 +
1670 + if ( ! $show_empty_terms && 0 === $count ) {
1671 + continue;
1672 + }
1673 +
1674 + $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 ) . '">';
1675 + $html .= '<div class="rtsb-default-filter-group' . esc_attr( $active_class ) . '">';
1676 + $html .= self::get_input_type_html( $input, $args );
1677 +
1678 + $counter++;
1679 +
1680 + $html .= '</div>';
1681 + $html .= '</li>';
1682 + }
1683 +
1684 + $html .= '</ul>';
1685 +
1686 + return $html;
1687 + }
1688 + /**
1689 + * Get input type HTML.
1690 + *
1691 + * @param string $input Input type.
1692 + * @param array $args Args.
1693 + * @return mixed|string|null
1694 + */
1695 + public static function get_input_type_html( $input, $args ) {
1696 + $html = '';
1697 + $count = $args['count'];
1698 +
1699 + switch ( $input ) {
1700 + case 'color':
1701 + $color = sanitize_hex_color( get_term_meta( $args['term_id'], 'product_attribute_color', true ) );
1702 +
1703 + $html .= sprintf(
1704 + '<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>',
1705 + esc_attr( $args['id'] ),
1706 + esc_attr( $args['type'] ),
1707 + $args['tooltips'] ? esc_attr( ' tipsy' ) : '',
1708 + esc_attr( $color ),
1709 + esc_html( $args['name'] ),
1710 + absint( $count )
1711 + );
1712 + break;
1713 +
1714 + case 'image':
1715 + if ( ! function_exists( 'rtwpvs' ) ) {
1716 + return $html;
1717 + }
1718 +
1719 + $attachment_id = absint( get_term_meta( $args['term_id'], 'product_attribute_image', true ) );
1720 + $image_size = rtwpvs()->get_option( 'attribute_image_size' );
1721 + $image_url = wp_get_attachment_image_url( $attachment_id, apply_filters( 'rtwpvs_product_attribute_image_size', $image_size ) );
1722 +
1723 + $html .= sprintf(
1724 + '<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>',
1725 + esc_attr( $args['id'] ),
1726 + esc_attr( $args['type'] ),
1727 + $args['tooltips'] ? esc_attr( ' tipsy' ) : '',
1728 + esc_attr( $args['name'] ),
1729 + esc_url( $image_url )
1730 + );
1731 + break;
1732 +
1733 + case 'button':
1734 + $html .= sprintf(
1735 + '<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>',
1736 + esc_attr( $args['id'] ),
1737 + esc_attr( $args['type'] ),
1738 + $args['tooltips'] ? esc_attr( ' tipsy' ) : '',
1739 + esc_html( $args['name'] ),
1740 + absint( $count )
1741 + );
1742 + break;
1743 +
1744 + default:
1745 + }
1746 +
1747 + return apply_filters( 'rtsb/elementor/default/filter/get_input_type_html', $html );
1748 + }
1749 + /**
1750 + * Get reset button.
1751 + *
1752 + * @param string $btn_text Button text.
1753 + * @param array $settings Settings array.
1754 + *
1755 + * @return string
1756 + */
1757 + public static function get_default_filter_reset_button( $btn_text, $settings ) {
1758 + $html = '';
1759 +
1760 + ob_start();
1761 +
1762 + $active = ! empty( $_SERVER['QUERY_STRING'] ) ? ' active' : '';
1763 + $reset = ! empty( $settings['reset_btn'] );
1764 + $apply_text = ! empty( $settings['apply_filters_btn_text'] ) ? $settings['apply_filters_btn_text'] : esc_html__( 'Apply Filters', 'shopbuilder' );
1765 + $apply_icon = ! empty( $settings['apply_filters_btn_icon'] ) ? $settings['apply_filters_btn_icon'] : [];
1766 + echo '<div class="default-filter-btn-wrapper">';
1767 + ?>
1768 + <?php if ( $settings['show_filter_btn'] ) { ?>
1769 + <div class="rtsb-product-default-filters rtsb-apply-filters-btn">
1770 + <div class="reset-btn-item">
1771 + <button class="rtsb-apply-filters init">
1772 + <span class="icon"><?php Fns::print_html( Fns::icons_manager( $apply_icon, 'icon-default' ) ); ?></span>
1773 + <span class="text reset-text"><?php echo esc_html( $apply_text ); ?></span>
1774 + <span></span>
1775 + </button>
1776 + </div>
1777 + </div>
1778 + <?php } ?>
1779 + <?php
1780 + if ( $reset ) {
1781 + ?>
1782 + <div class="rtsb-product-default-filters rtsb-reset<?php echo esc_attr( $active ); ?>">
1783 + <div class="reset-btn-item">
1784 + <button class="product-default-filter-reset">
1785 + <span class="text reset-text"><?php echo esc_html( $btn_text ); ?></span>
1786 + <span></span>
1787 + </button>
1788 + </div>
1789 + </div>
1790 + <?php
1791 + }
1792 + echo '</div>';
1793 +
1794 + $html .= ob_get_clean();
1795 +
1796 + return $html;
817 1797 }
818 1798 }