PluginProbe
ShopBuilder – WooCommerce Builder For Elementor / 2.6.2
ShopBuilder – WooCommerce Builder For Elementor v2.6.2
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
shopbuilder / app / Elementor / Helper / RenderHelpers.php

RenderHelpers.php in ShopBuilder – WooCommerce Builder For Elementor 2.6.2, at app/Elementor/Helper/RenderHelpers.php

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