PluginProbe
ShopBuilder – WooCommerce Builder For Elementor / 3.2.1
ShopBuilder – WooCommerce Builder For Elementor v3.2.1
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 / Render / Render.php

Render.php in ShopBuilder – WooCommerce Builder For Elementor 3.2.1, at app/Elementor/Render/Render.php

1,529 lines 45.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 Class.
4 *
5 * This class contains render logics & output. It utilizes WC_Products_Query().
6 *
7 * @package RadiusTheme\SB
8 */
9
10 namespace RadiusTheme\SB\Elementor\Render;
11
12 use WC_Query;
13 use WP_Query;
14 use WC_Product;
15 use Elementor\Utils;
16 use WC_Product_Query;
17 use RadiusTheme\SB\Helpers\Fns;
18 use RadiusTheme\SB\Models\QueryArgs;
19 use RadiusTheme\SB\Helpers\BuilderFns;
20 use RadiusTheme\SB\Traits\SingletonTrait;
21 use RadiusTheme\SB\Traits\ActionBtnTraits;
22 use RadiusTheme\SB\Elementor\Helper\RenderHelpers;
23
24 // Do not allow directly accessing this file.
25 if ( ! defined( 'ABSPATH' ) ) {
26 exit( 'This script cannot be accessed directly.' );
27 }
28
29 /**
30 * Elementor Render Class.
31 */
32 class Render {
33 /**
34 * Singleton Trait.
35 */
36 use SingletonTrait;
37
38 /**
39 * Action button trait.
40 */
41 use ActionBtnTraits;
42
43 /**
44 * HTML.
45 *
46 * @var string
47 */
48 private $html = null;
49
50 /**
51 * Settings array
52 *
53 * @var array
54 */
55 public $get_settings;
56
57 /**
58 * Element attributes.
59 *
60 * @var array
61 */
62 private $attributes = [];
63
64 /**
65 * Get elementor settings.
66 *
67 * @return array
68 */
69 public function get_settings_for_display() {
70 return $this->get_settings;
71 }
72
73 /**
74 * Container Wrapper HTML.
75 *
76 * @param string $content The content.
77 * @param array $settings Settings.
78 * @param string $class Class.
79 * @param string $template Template name.
80 * @param bool $fullwidth Fullwidth check.
81 * @param bool $ajax Ajax attributes.
82 *
83 * @return string
84 */
85 public function container( $content, $settings, $class = '', $template = '', $fullwidth = false, $ajax = true ) {
86 $rand = wp_rand();
87 $layout_id = 'rtsb-container-' . $rand;
88 $metas = RenderHelpers::meta_dataset( $settings, $template );
89 $raw_layout = esc_html( $metas['layout'] );
90 $layout = RenderHelpers::filter_layout( $raw_layout, $template );
91 $style_attr = '--rtsb-default-columns: ' . esc_attr( RenderHelpers::default_columns( $layout ) );
92 $view_mode = $settings['view_mode'] ?? 'grid';
93 $view_mode = isset( $_GET['displayview'] ) ? sanitize_text_field( wp_unslash( $_GET['displayview'] ) ) : $view_mode; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
94 $tooltip_attr = 'list' == $view_mode && ! empty( $metas['tooltip_position_list'] ) ? esc_attr( $metas['tooltip_position_list'] ) : $metas['tooltip_position'];
95 $has_pro = rtsb()->has_pro();
96 $has_filters = $has_pro && ! empty( $settings['tax_filter'] );
97 $grid_style = $settings['grid_style'] ?? 'even';
98
99 // Set default layout.
100 if ( ! $has_pro && ! in_array( $layout, array_keys( Fns::free_layouts() ), true ) ) {
101 $layout = RenderHelpers::set_default_layout( $layout );
102 }
103
104 if ( $has_pro && 'masonry' === $grid_style ) {
105 $style_attr .= '; --rtsb-masonry-columns: ' . esc_attr( RenderHelpers::default_columns( $layout ) );
106 $style_attr .= '; --rtsb-masonry-list-columns: ' . esc_attr( RenderHelpers::default_columns( $layout ) );
107 }
108
109 // Container classes.
110 $container_classes = RenderHelpers::prepare_container_classes( $metas, $settings, $class );
111
112 // Pagination attributes.
113 $pagination_attributes = RenderHelpers::prepare_pagination_attr( $layout, $has_filters, $template, $settings, $ajax );
114
115 // Attributes for the container.
116 $container_attributes = [
117 'id' => $layout_id,
118 'class' => $container_classes,
119 'style' => apply_filters( 'rtsb/product/container/attr', $style_attr, $settings ),
120 'data-layout' => $layout,
121 'data-tooltip-position' => $tooltip_attr,
122 ];
123
124 if ( ! empty( $pagination_attributes ) ) {
125 $container_attributes['data-rtsb-ajax'] = $pagination_attributes;
126 }
127
128 // Adding render attributes.
129 $this->add_attribute( 'rtsb_container_attr_' . $rand, $container_attributes );
130
131 // Start rendering.
132 $this->html = '<div ' . $this->get_attribute_string( 'rtsb_container_attr_' . $rand ) . '>';
133
134 // Section title.
135 $this->html .= $this->render_section_title( $settings );
136
137 // Content.
138 $this->html .= $content;
139
140 // End rendering.
141 $this->html .= '</div><!-- .rtsb-elementor-container -->';
142
143 // Action button icon reset.
144 $this->action_button_icon_set_default();
145
146 // Script generator.
147 $script_generator = [];
148 $script_generator['layout'] = $layout_id;
149 $script_generator['rand'] = absint( $rand );
150 $script_generator['isCarousel'] = preg_match( '/slider/', $layout );
151 $script_generator['isMasonry'] = $has_pro && 'masonry' === $grid_style;
152
153 // Register scripts in the wp_footer action.
154 add_action(
155 'wp_footer',
156 static function () use ( $script_generator ) {
157 RenderHelpers::register_scripts( $script_generator );
158 }
159 );
160
161 return $this->html;
162 }
163
164 /**
165 * Row Wrapper HTML.
166 *
167 * @param string $content The content.
168 * @param array $settings Settings.
169 * @param string $hook_html Hook HTML.
170 * @param array $raw_settings Raw Settings.
171 *
172 * @return string
173 */
174 public function row( $content, $settings, $hook_html = '', $raw_settings = [] ) {
175 $raw_layout = esc_html( $settings['layout'] );
176 $layout = RenderHelpers::filter_layout( $raw_layout );
177
178 $rand = wp_rand();
179 $is_carousel = preg_match( '/slider/', $raw_layout );
180
181 if ( preg_match( '/category/', $raw_layout ) && ( ! empty( $settings['active_cat_slider'] ) ) ) {
182 $is_carousel = true;
183 }
184
185 // Row classes.
186 $row_classes = RenderHelpers::prepare_row_classes( $settings );
187
188 // Adding render attributes.
189 $this->add_attribute( 'rtsb_row_attr_' . $rand, 'class', $row_classes );
190
191 // Start rendering.
192 $row_wrapper = '<div ' . $this->get_attribute_string( 'rtsb_row_attr_' . $rand ) . '>';
193 $start_wrapper = apply_filters( 'rtsb/elementor/row/start', $row_wrapper, $raw_settings );
194
195 $this->html = ! empty( $settings['show_filter'] ) ? $hook_html . $start_wrapper : $start_wrapper;
196
197 if ( $is_carousel ) {
198 $slider_data = RenderHelpers::slider_data( $settings, $raw_settings );
199 // Adding slider render attributes.
200 $this->add_attribute(
201 'rtsb_slider_attr_' . $rand,
202 [
203 'class' => 'rtsb-col-grid ' . $slider_data['class'],
204 'data-options' => $slider_data['data'],
205 ]
206 );
207
208 $this->html .= '<div ' . $this->get_attribute_string( 'rtsb_slider_attr_' . $rand ) . '>';
209 $this->html .= '<div class="swiper-wrapper">';
210 }
211
212 $this->html .= $content;
213
214 if ( $is_carousel ) {
215 $this->html .= '</div><!-- .swiper-wrapper -->';
216
217 // Slider buttons.
218 $this->html .= $this->render_slider_buttons( $settings );
219 $this->html .= '</div><!-- .rtsb-carousel-slider -->';
220 }
221
222 $row_end = '</div><!-- .rtsb-row -->';
223
224 $this->html .= apply_filters( 'rtsb/elementor/row/end', $row_end );
225
226 // Preloader.
227 $this->html .= RenderHelpers::pre_loader( $layout );
228
229 return $this->html;
230 }
231
232 /**
233 * Product loop.
234 *
235 * @param object $products Products.
236 * @param array $settings Settings.
237 * @param string $template Template.
238 *
239 * @return string|null
240 */
241 public function product_loop( $products, $settings, $template ) {
242 $html = null;
243
244 if ( empty( $products ) ) {
245 return null;
246 }
247
248 $i = 0;
249
250 global $product;
251
252 $isGlobalProduct = false;
253
254 if ( $product instanceof WC_Product ) {
255 $isGlobalProduct = $product;
256 }
257
258 $layout = RenderHelpers::filter_layout( esc_html( $settings['layout'] ), $template );
259
260 foreach ( $products as $_product ) {
261 $i++;
262 $GLOBALS['product'] = $_product;
263
264 /**
265 * Before product template render hook.
266 */
267 do_action( 'rtsb_before_product_template_render' );
268
269 // Loop arg.
270 $arg = $this->arg_dataset( $settings, $_product, $settings['lazy_load'], $i );
271
272 // Get template.
273 $html .= Fns::load_template( $template . $layout, $arg, true );
274
275 /**
276 * After product template render hook.
277 */
278 do_action( 'rtsb_after_product_template_render' );
279
280 unset( $GLOBALS['product'] );
281 }
282 if ( $isGlobalProduct ) {
283 $GLOBALS['product'] = $isGlobalProduct;
284 }
285 return $html;
286 }
287
288 /**
289 * WC Product loop.
290 *
291 * @param object $query The query.
292 * @param array $settings Settings.
293 * @param string $template Template.
294 *
295 * @return string|null
296 */
297 public function wc_product_loop( $query, $settings, $template ) {
298 $html = null;
299
300 if ( empty( $query ) ) {
301 return null;
302 }
303
304 $i = 0;
305 $layout = RenderHelpers::filter_layout( esc_html( $settings['layout'] ), $template );
306
307 while ( $query->have_posts() ) {
308 $query->the_post();
309 $i++;
310 global $product;
311 // Loop arg.
312 $arg = $this->arg_dataset( $settings, $product, $settings['lazy_load'], $i );
313
314 // Get template.
315 $html .= Fns::load_template( $template . $layout, $arg, true );
316 }
317
318 return $html;
319 }
320
321 /**
322 * Category loop.
323 *
324 * @param object $query The query.
325 * @param array $settings Settings.
326 * @param string $template Template.
327 * @param bool $is_single Single category.
328 *
329 * @return string|null
330 */
331 public function category_loop( $query, $settings, $template, $is_single = false ) {
332 $html = null;
333 $category_loop = false;
334
335 if ( 'category-layout3' === $settings['raw_settings']['layout'] || 'category-single-layout2' === $settings['raw_settings']['layout'] ) {
336 $category_loop = true;
337 }
338
339 if ( $is_single ) {
340 // Loop arg.
341 $arg = $this->cat_arg_dataset( $settings, $query, true, false, $category_loop );
342
343 // Get template.
344 $html .= Fns::load_template( $template . $settings['layout'], $arg, true );
345 } else {
346 foreach ( $query as $term ) {
347 // Loop arg.
348 $arg = $this->cat_arg_dataset( $settings, $term, false, false, $category_loop );
349
350 // Get template.
351 $html .= Fns::load_template( $template . $settings['layout'], $arg, true );
352 }
353 }
354
355 return $html;
356 }
357
358 /**
359 * Render Product View.
360 *
361 * @param string $template Template name.
362 * @param array $settings Control settings.
363 *
364 * @return string
365 */
366 public function product_view( $template, $settings ) {
367 $this->get_settings = $settings;
368
369 $metas = RenderHelpers::meta_dataset( $settings, $template, $this->get_settings_for_display() );
370 $is_carousel = preg_match( '/slider/', $metas['layout'] );
371 $pagination = $metas['pagination'];
372 $hook_html = '';
373
374 // Query Args.
375 $args = ( new QueryArgs() )->buildArgs( $metas, 'product', $is_carousel );
376
377 $temp_args = $args;
378 $default_args = [
379 'visibility' => 'visible',
380 'status' => 'publish',
381 ];
382 $custom_args = apply_filters( 'rtsb/elementor/args_before_loop', $args, $metas );
383 $args = wp_parse_args( $custom_args, $default_args );
384
385 /**
386 * Before Product query hook.
387 *
388 * @hooked RadiusTheme\SB\Controllers\Hooks\ActionHooks::modify_wc_query_args 10
389 */
390 do_action( 'rtsb/elements/render/before_query', $settings, $args );
391
392 // Query.
393 $product_query = new WC_Product_Query( $args );
394
395 /**
396 * Product query hook.
397 */
398 do_action( 'rtsb/elements/render/product_query', $product_query, $this );
399
400 $products = $pagination ? $product_query->get_products()->products : $product_query->get_products();
401
402 if ( ! empty( $products ) ) {
403 // Start rendering.
404 $hook_html .= $this->render_start( $metas, $settings, $temp_args );
405
406 // Row.
407 $this->row( $this->product_loop( $products, $metas, $template ), $metas, $hook_html, $settings );
408
409 // Pagination.
410 if ( $pagination ) {
411 $pagination_args = [
412 'query' => $product_query,
413 'meta' => $metas,
414 'limit' => $settings['posts_limit'],
415 'per_page' => $settings['pagination_per_page'],
416 ];
417
418 $this->html .= $this->render_products_pagination( $pagination_args );
419 }
420
421 // End rendering.
422 $this->html .= $this->render_end( $metas, $settings, $products );
423 } else {
424 $this->no_products_msg( $metas );
425 }
426
427 wp_reset_postdata();
428
429 unset( $args['tax_query'] );
430
431 /**
432 * After Product query hook.
433 *
434 * @hooked RadiusTheme\SB\Controllers\Hooks\ActionHooks::reset_wc_query_args 10
435 */
436 do_action( 'rtsb/elements/render/after_query', $settings, $args );
437
438 // Container.
439 $this->container( $this->html, $settings, 'rtsb-products-container', $template, $is_carousel );
440
441 return $this->html;
442 }
443
444 /**
445 * Render Product View.
446 *
447 * @param string $template Template name.
448 * @param array $settings Control settings.
449 * @param object $widget Reference widget.
450 *
451 * @return string
452 */
453 public function wc_loop_for_product_view( $template, $settings, $widget ) {
454 global $wp_query, $post;
455
456 $this->get_settings = $settings;
457 $rand = wp_rand();
458 $metas = RenderHelpers::meta_dataset( $settings, $template, $widget->get_settings_for_display() );
459
460 $settings['rtsb_order'] = ! empty( $wp_query->query_vars['order'] ) ? $wp_query->query_vars['order'] : 'ASC';
461 $settings['rtsb_orderby'] = ! empty( $wp_query->query_vars['orderby'] ) ? $wp_query->query_vars['orderby'] : 'menu_order';
462
463 $pagination = $metas['pagination'];
464
465 $page_type = BuilderFns::builder_type( get_the_ID() );
466 $is_preview = BuilderFns::is_builder_preview() && array_key_exists( $page_type, BuilderFns::builder_page_types() );
467 $posts_per_page = apply_filters( 'loop_shop_per_page', wc_get_default_products_per_row() * wc_get_default_product_rows_per_page() );
468 if ( $is_preview ) {
469 $main_query = clone $wp_query;
470 $main_post = clone $post;
471
472 $ordering = ( new WC_Query() )->get_catalog_ordering_args();
473 $wp_query_args = [
474 'post_type' => 'product',
475 'posts_per_page' => $posts_per_page,
476 ];
477
478 if ( ! empty( $ordering['orderby'] ) ) {
479 $wp_query_args['orderby'] = $ordering['orderby'];
480 }
481
482 if ( ! empty( $ordering['order'] ) ) {
483 $wp_query_args['order'] = $ordering['order'];
484 }
485
486 $wp_query = new WP_Query( $wp_query_args ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
487
488 wc_set_loop_prop( 'total', $wp_query->found_posts );
489 wc_set_loop_prop( 'total_pages', $wp_query->max_num_pages );
490 }
491
492 if ( Fns::product_has_applied_filters( 'shop' ) || Fns::product_has_applied_filters( 'archive' ) || 'rtsb_builder' === get_post_type( get_the_ID() ) ) {
493 echo '<div class="rtsb-active-filters-wrapper"></div>';
494 }
495
496 // column_per_row.
497 if ( woocommerce_product_loop() ) {
498 if ( wc_get_loop_prop( 'total' ) ) {
499
500 // Row.
501 $this->row( $this->wc_product_loop( $wp_query, $metas, $template ), $metas, '', $settings );
502 }
503
504 // Pagination.
505 $this->html .= '<div class="rtsb-archive-pagination-wrap">';
506
507 if ( $pagination ) {
508 if ( Fns::product_filters_has_ajax( apply_filters( 'rtsb/builder/set/current/page/type', '' ) ) ) {
509 if ( $wp_query->max_num_pages > 1 ) {
510 $this->add_attribute(
511 'rtsb_load_more_btn_attr_' . $rand,
512 [
513 'data-paged' => '2',
514 'data-max-page' => $wp_query->max_num_pages,
515 ]
516 );
517
518 $this->html .=
519 "<div class='rtsb-load-more rtsb-pos-r'>
520 <button {$this->get_attribute_string( 'rtsb_load_more_btn_attr_' . $rand )}>
521 <span>" . esc_html__( 'Load More', 'shopbuilder' ) . "</span>
522 </button>
523 <div class='rtsb-loadmore-loading rtsb-ball-clip-rotate'><div></div></div>
524 </div>";
525 }
526 } else {
527 $pagination_args = [
528 'query' => $wp_query,
529 'meta' => $metas,
530 'limit' => wc_get_loop_prop( 'total' ),
531 'per_page' => $posts_per_page,
532 ];
533
534 $this->html .= $this->render_products_pagination( $pagination_args, 'wp' );
535 }
536 }
537
538 $this->html .= '</div>';
539 } else {
540 $this->no_products_msg( $metas );
541 }
542
543 if ( $is_preview ) {
544 // phpcs:disable
545 $wp_query = $main_query;
546 $post = $main_post;
547
548 wp_reset_query();
549 wp_reset_postdata();
550 // phpcs:enable
551 }
552
553 // Container.
554 $this->container( $this->html, $settings, 'rtsb-products-container', $template );
555
556 return $this->html;
557 }
558
559 /**
560 * Loop Setup for Product View.
561 *
562 * @param string $template Template name.
563 * @param array $settings Control settings.
564 * @param object $widget Widget object.
565 *
566 * @return string
567 */
568 public function setup_loop_for_product_view( $template, $settings, $widget ) {
569 if ( empty( $settings['query_products'] ) ) {
570 return $this->html;
571 }
572
573 $html = null;
574
575 $metas = RenderHelpers::meta_dataset( $settings, '', $widget->get_settings_for_display() );
576
577 foreach ( $settings['query_products'] as $_product ) {
578 $post_object = get_post( $_product->get_id() );
579
580 setup_postdata( $GLOBALS['post'] =& $post_object ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited, Squiz.PHP.DisallowMultipleAssignments.Found
581
582 // Loop arg.
583 $arg = $this->arg_dataset( $metas, $_product );
584 // Get template.
585 $html .= Fns::load_template( $template . $settings['layout'], $arg, true );
586
587 $this->row( $html, $metas, '', $settings );
588 }
589
590 // Container.
591 $this->container( $this->html, $settings, 'rtsb-products-container', '', false, false );
592
593 wp_reset_postdata();
594
595 return $this->html;
596 }
597
598 /**
599 * Render Category View.
600 *
601 * @param string $template Template name.
602 * @param array $settings Control settings.
603 * @param bool $is_single Single category.
604 *
605 * @return string
606 */
607 public function category_view( $template, $settings, $is_single = false ) {
608 $this->get_settings = $settings;
609
610 $metas = RenderHelpers::meta_dataset( $settings, $template, $this->get_settings_for_display() );
611 $taxonomy = $metas['category_source'];
612 $term = $metas['category_term'];
613
614 if ( rtsb()->has_pro() && ( ! empty( $metas['tag_term'] ) ) ) {
615 $term = $metas['tag_term'];
616 }
617 if ( rtsb()->has_pro() && ( ! empty( $metas['brand_term'] ) ) ) {
618 $term = $metas['brand_term'];
619 }
620
621 // Query.
622 if ( $is_single ) {
623 if ( empty( $term ) ) {
624 return '<p>' . esc_html__( 'Please select a category.', 'shopbuilder' ) . '</p>';
625 }
626
627 $taxonomy_query = get_term( $term, $taxonomy );
628 } else {
629 // Query Args.
630 $args = ( new QueryArgs() )->buildArgs( $metas, 'category' );
631
632 $taxonomy_query = get_terms( $args );
633 }
634
635 if ( ! is_wp_error( $taxonomy_query ) && ! empty( $taxonomy_query ) ) {
636 // Row.
637 $this->row( $this->category_loop( $taxonomy_query, $metas, $template, $is_single ), $metas );
638 } else {
639 $this->html .= '<p>' . esc_html__( 'No categories found', 'shopbuilder' ) . '</p>';
640 }
641
642 // Container.
643 $this->container( $this->html, $settings, 'rtsb-categories-container', $is_single );
644
645 return $this->html;
646 }
647
648 /**
649 * Render Category View.
650 *
651 * @param string $template Template name.
652 * @param array $settings Control settings.
653 *
654 * @return string
655 */
656 public function social_share_view( $template, $settings ) {
657 $this->get_settings = $settings;
658 $share_items = [];
659
660 foreach ( $settings['share_platforms'] as $platform ) {
661 $share_items[] = [
662 'share_items' => $platform['share_items'],
663 'share_text' => $platform['share_text'],
664 ];
665 }
666
667 if ( ! empty( $share_items ) && is_array( $share_items ) ) {
668 $arg['p_id'] = get_the_ID();
669 $arg['share_items'] = $share_items;
670 $arg['preset'] = $settings['layout'];
671 $arg['direction'] = ! empty( $settings['layout_direction'] ) ? $settings['layout_direction'] : 'horizontal';
672 $arg['show_icon'] = ! empty( $settings['show_share_icon'] );
673 $arg['show_text'] = ! empty( $settings['show_share_text'] );
674 $arg['show_pre_text'] = ! empty( $settings['show_share_pre_text'] );
675 $arg['share_pre_text'] = ! empty( $settings['share_pre_text'] ) ? $settings['share_pre_text'] : '';
676 $arg['raw_settings'] = $this->get_settings_for_display();
677 $this->html = Fns::load_template( $template, $arg, true );
678 } else {
679 $this->html = '<p>' . esc_html__( 'Please select a social sharing platform.', 'shopbuilder' ) . '</p>';
680 }
681
682 return $this->html;
683 }
684
685 /**
686 * Builds an array with meta values.
687 *
688 * @param array $meta Meta values.
689 * @param object $product Product object.
690 * @param bool $lazy_load Lazy Load.
691 * @param int $i Integer.
692 *
693 * @return array
694 */
695 public function arg_dataset( array $meta, object $product, bool $lazy_load = false, $i = 0 ) {
696 $post_id = $product->get_id();
697 $arg = $this->build_product_arg(
698 [
699 'product' => $product,
700 'id' => $product->get_id(),
701 'type' => $product->get_type(),
702 'meta' => $meta,
703 'lazy_load' => $lazy_load,
704 'i' => $i,
705 ]
706 );
707
708 return apply_filters( 'rtsb/elementor/render/arg_dataset', $arg, $post_id, $meta, $lazy_load );
709 }
710
711 /**
712 * Builds an array with meta values for WP_Query.
713 *
714 * @param array $meta Meta values.
715 * @param int $post_id Post ID.
716 * @param bool $lazy_load Lazy Load.
717 * @param int $i Integer.
718 *
719 * @return array
720 */
721 public function wp_arg_dataset( array $meta, int $post_id, bool $lazy_load = false, $i = 0 ) {
722 // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict
723 if ( ( empty( $meta ) && ! $post_id ) || ! in_array( get_post_type( $post_id ), [ 'product_variation', 'product' ] ) ) {
724 return [];
725 }
726
727 global $product;
728
729 if ( ! $product instanceof WC_Product && $post_id ) {
730 $product = wc_get_product( $post_id );
731 }
732
733 $post_id = $product->get_id();
734 $arg = $this->build_product_arg(
735 [
736 'product' => $product,
737 'id' => $post_id,
738 'type' => $product->get_type(),
739 'meta' => $meta,
740 'lazy_load' => $lazy_load,
741 'i' => $i,
742 ]
743 );
744
745 return apply_filters( 'rtsb/elementor/render/arg_dataset', $arg, $post_id, $meta, $lazy_load );
746 }
747
748 /**
749 * Builds an array with meta values.
750 *
751 * @param array $meta Meta values.
752 * @param Object $query Category object.
753 * @param bool $fullwidth Fullwidth check.
754 * @param bool $lazy_load Lazy Load.
755 * @param bool $category_loop Category loop.
756 *
757 * @return array
758 */
759 public function cat_arg_dataset( $meta, $query = null, $fullwidth = false, $lazy_load = false, $category_loop = false ) {
760 if ( empty( $meta ) ) {
761 return [];
762 }
763
764 $arg = [];
765
766 $products = null;
767 $arg['class'] = null;
768 $arg['grid'] = null;
769 $arg['anchor_class'] = null;
770 $arg['p_sale'] = null;
771 $arg['count'] = max( $query->count, 0 );
772 $arg['count_position'] = $meta['count_position'];
773 $arg['excerpt_position'] = $meta['cat_excerpt_position'];
774 $arg['cat_link'] = get_term_link( $query );
775 $arg['target'] = '_self';
776 $arg['raw_settings'] = ! empty( $meta['raw_settings'] ) ? $meta['raw_settings'] : [];
777
778 if ( ! empty( $meta['tag_term'] ) ) {
779 $arg['tag_term'] = $meta['tag_term'];
780 }
781
782 if ( ! empty( $meta['custom_link']['url'] ) ) {
783 $arg['cat_link'] = $meta['custom_link']['url'];
784 $arg['target'] = $meta['custom_link']['is_external'] ? '_blank' : '_self';
785 }
786
787 $title = $query->name;
788 $excerpt = $query->description;
789
790 if ( in_array( 'badges', $meta['visibility'], true ) ) {
791 $arg['p_sale'] = $meta['custom_badge_text'];
792 }
793
794 $arg['badge_class'] = RenderHelpers::badge_class( $meta['badge_preset'] );
795
796 if ( $arg['count'] >= 0 ) {
797 $prefix = ! empty( $meta['before_count'] ) ? $meta['before_count'] : '';
798 $suffix = ! empty( $meta['after_count'] ) ? $meta['after_count'] : '';
799
800 $arg['count'] = $prefix . $arg['count'] . $suffix;
801 }
802
803 if ( $meta['show_custom_title'] && ! empty( $meta['cat_custom_title'] ) ) {
804 $title = $meta['cat_custom_title'];
805 }
806
807 if ( $meta['show_custom_excerpt'] && ! empty( $meta['cat_custom_excerpt'] ) ) {
808 $excerpt = $meta['cat_custom_excerpt'];
809 }
810
811 $arg['c_img'] = $meta['c_img'];
812 $arg['items'] = $meta['visibility'];
813 $arg['title_tag'] = $meta['title_tag'];
814 $arg['title_class'] = 'category-title rtsb-text-limit limit-' . $meta['title_limit'] . ( ! $meta['title_link'] ? ' no-link' : '' );
815 $arg['title'] = Fns::text_truncation( $title, $meta['title_limit_custom'] );
816 $arg['excerpt_limit'] = $meta['excerpt_limit'];
817 $arg['excerpt'] = wpautop( Fns::text_truncation( $excerpt, $meta['excerpt_limit_custom'] ) );
818 $arg['title_link'] = $meta['title_link'];
819 $arg['image_link'] = $meta['image_link'];
820 $arg['grid'] .= 'rtsb-col-grid ';
821
822 if ( ! $fullwidth ) {
823 if ( 'masonry' === $meta['grid_type'] ) {
824 $arg['class'] .= 'masonry-grid-item ';
825 } else {
826 $arg['class'] .= 'even-grid-item ';
827 }
828 }
829
830 if ( ! empty( $meta['active_cat_slider'] ) ) {
831 $arg['class'] .= ' rtsb-slide-item swiper-slide animated rtFadeIn ';
832 }
833
834 $arg['class'] .= 'rtsb-category-grid';
835
836 ob_start();
837 do_action( 'rtsb/categories/category_image_override', $meta, $query->term_id );
838 $override_category_image = ob_get_clean();
839
840 $arg['img_html'] = $meta['c_img'] ? Fns::get_product_image_html(
841 'category',
842 $query->term_id,
843 $meta['f_img_size'],
844 $meta['default_img_id'],
845 $meta['custom_img_size'],
846 $lazy_load,
847 false,
848 false,
849 $override_category_image
850 ) : null;
851
852 if ( $meta['show_custom_image'] && ! empty( $meta['custom_image'] ) ) {
853 $arg['img_html'] = Fns::get_product_image_html(
854 'custom',
855 null,
856 $meta['f_img_size'],
857 $meta['custom_image'],
858 $meta['custom_img_size'],
859 $lazy_load
860 );
861 }
862
863 $arg['excerpt_class'] = 'category-info' . ( 'block' === $meta['count_position'] ? ' block-count' : ' inline-count' ) . ( 'above' === $meta['cat_excerpt_position'] ? ' excerpt-above' : '' ) . ( empty( $arg['excerpt'] ) ? ' no-excerpt' : '' );
864
865 if ( $category_loop ) {
866 $query_args = [
867 'product_category_id' => $query->term_id,
868 'limit' => -1,
869 'order' => 'DESC',
870 'orderby' => 'date',
871 ];
872
873 $products_query = new WC_Product_Query( $query_args );
874 $products = $products_query->get_products();
875 }
876
877 return apply_filters( 'rtsb/elementor/render/cat_arg_dataset', $arg, $meta, $query, $fullwidth, $lazy_load, $products );
878 }
879
880 /**
881 * Building product args.
882 *
883 * @param array $args Args.
884 * @return array
885 */
886 public function build_product_arg( $args ) {
887 list(
888 'product' => $product,
889 'id' => $post_id,
890 'type' => $p_type,
891 'meta' => $meta,
892 'lazy_load' => $lazy_load,
893 'i' => $i,
894 ) = $args;
895
896 $arg = [];
897 $arg['class'] = null;
898 $arg['grid'] = null;
899 $arg['anchor_class'] = null;
900 $arg['hover_img_html'] = null;
901 $arg['s_link'] = [];
902 $arg['add_to_cart'] = '';
903 $arg['p_counter'] = absint( $i );
904 $arg['p_id'] = $post_id;
905 $arg['product'] = $product;
906 $arg['p_link'] = $product->get_permalink();
907 $arg['p_sale'] = '';
908 $arg['rating_args'] = [];
909 $arg['has_attributes'] = $product->is_type( 'variable' ) && $product->get_attributes();
910 $arg['raw_settings'] = ! empty( $meta['raw_settings'] ) ? $meta['raw_settings'] : [];
911 $cart_icon_html = '';
912 $layout = $meta['layout'];
913
914 // Building the arg.
915 $arg['target'] = '_self';
916 $arg['f_img'] = $meta['f_img'];
917 $arg['items'] = $meta['visibility'];
918 $arg['title_tag'] = $meta['title_tag'];
919 $arg['title_class'] = 'product-title rtsb-text-limit limit-' . $meta['title_limit'] . ( ! $meta['title_link'] ? ' no-link' : '' );
920 $arg['title'] = Fns::text_truncation( $product->get_title(), $meta['title_limit_custom'] );
921 $arg['excerpt_limit'] = $meta['excerpt_limit'];
922 $arg['excerpt'] = wpautop( Fns::text_truncation( do_shortcode( get_post_field( 'post_excerpt', $post_id ) ), $meta['excerpt_limit_custom'] ) );
923 $arg['title_link'] = $meta['title_link'];
924 $arg['image_link'] = $meta['image_link'];
925 $arg['out_of_stock'] = $meta['out_of_stock_badge_text'];
926 $arg['action_btn_preset'] = $meta['btn_preset'];
927 $arg['action_btn_position'] = $meta['btn_position'];
928 $arg['swatch_position'] = $meta['swatch_position'];
929 $arg['swatch_type'] = $meta['swatch_type'];
930 $arg['tooltip_position'] = $meta['tooltip_position'];
931
932 $is_list = preg_match( '/list/', $layout );
933 $is_carousel = preg_match( '/slider/', $layout );
934
935 if ( ! $is_carousel ) {
936 $arg['grid'] .= 'rtsb-col-grid ';
937
938 if ( 'masonry' === $meta['grid_type'] ) {
939 $arg['class'] .= 'masonry-grid-item ';
940 } else {
941 $arg['class'] .= 'even-grid-item ';
942 }
943 } else {
944 $arg['grid'] .= 'rtsb-col-grid rtsb-col-full rtsb-slide-item swiper-slide ';
945 }
946
947 $d_cols = 0 === $meta['d_cols'] ? RenderHelpers::default_columns( $layout ) : $meta['d_cols'];
948 $t_cols = 0 === $meta['t_cols'] ? 2 : $meta['t_cols'];
949 $m_cols = 0 === $meta['m_cols'] ? 1 : $meta['m_cols'];
950
951 $arg['grid'] .= 0 === $i % $d_cols ? ' row-last desktop-row-last' : '';
952 $arg['grid'] .= 0 === $i % $t_cols ? ' row-last tablet-row-last' : '';
953 $arg['grid'] .= 0 === $i % $m_cols ? ' row-last mobile-row-last' : '';
954
955 $arg['class'] .= 'rtsb-product';
956
957 if ( $is_list ) {
958 $arg['class'] .= ' rtsb-product-list';
959 }
960
961 $arg['class'] .= ' animated rtFadeIn';
962
963 if ( in_array( 'badges', $meta['visibility'], true ) ) {
964 $badge_module = ! empty( $meta['enable_badges_module'] );
965 $arg['p_sale'] = Fns::is_module_active( 'product_badges' ) && $badge_module ? 'rtsb_yes' : Fns::get_promo_badge(
966 $product,
967 $meta['sale_badge_type'],
968 $meta['sale_badge_text'],
969 apply_filters( 'rtsb/elementor/render/out_of_stock_badge_text', $meta['out_of_stock_badge_text'] )
970 );
971 }
972
973 $arg['badge_class'] = RenderHelpers::badge_class( $meta['badge_preset'] );
974
975 $meta['add_to_cart_text'] = apply_filters( 'rtsb/elementor/render/add_to_cart_text', $meta['add_to_cart_text'], $product );
976
977 if ( ! $meta['show_cart_text'] ) {
978 $meta['add_to_cart_text'] = '';
979 $meta['add_to_cart_success_text'] = '';
980 }
981
982 if ( in_array( 'add_to_cart', $meta['visibility'], true ) ) {
983 if ( ! empty( $meta['add_to_cart_icon'] ) ) {
984 $cart_icon_html .= Fns::icons_manager( $meta['add_to_cart_icon'], 'cart-icon' );
985 }
986
987 if ( ! empty( $meta['add_to_cart_success_icon'] ) ) {
988 $cart_icon_html .= Fns::icons_manager( $meta['add_to_cart_success_icon'], 'cart-success-icon' );
989 }
990
991 $cart_icon = ! empty( $cart_icon_html ) ? '<span class="icon">' . $cart_icon_html . '</span>' : '';
992
993 if ( $product->is_purchasable() || 'grouped' === $p_type || 'external' === $p_type ) {
994 if ( $product->is_in_stock() ) {
995 $arg['add_to_cart'] = $this->render_add_to_cart_button(
996 [
997 'link' => $product->get_permalink(),
998 'id' => $post_id,
999 'type' => $p_type,
1000 'text' => $meta['add_to_cart_text'],
1001 'success' => $meta['add_to_cart_success_text'],
1002 'icon_html' => $cart_icon,
1003 'alignment' => $meta['add_to_cart_alignment'],
1004 'product' => $product,
1005 ]
1006 );
1007 }
1008 }
1009 }
1010
1011 if ( in_array( 'quick_view', $meta['visibility'], true ) ) {
1012 add_filter( 'rtsb/module/quick_view/button_params', [ $this, 'button_classes' ] );
1013 add_filter( 'rtsb/module/quick_view/icon_html', [ $this, 'quick_view_icon' ] );
1014 }
1015
1016 if ( in_array( 'compare', $meta['visibility'], true ) ) {
1017 add_filter( 'rtsb/module/compare/button_params', [ $this, 'button_classes' ] );
1018 add_filter( 'rtsb/module/compare/icon_html', [ $this, 'compare_icon' ] );
1019 }
1020
1021 if ( in_array( 'wishlist', $meta['visibility'], true ) ) {
1022 add_filter( 'rtsb/module/wishlist/button_params', [ $this, 'button_classes' ] );
1023 add_filter( 'rtsb/module/wishlist/icon_html', [ $this, 'wishlist_icon' ] );
1024 }
1025
1026 $arg['img_html'] = $meta['f_img'] ? Fns::get_product_image_html(
1027 'product',
1028 $post_id,
1029 $meta['f_img_size'],
1030 $meta['default_img_id'],
1031 $meta['custom_img_size'],
1032 $lazy_load
1033 ) : null;
1034
1035 if ( ! ( function_exists( 'rtwpvsp' ) && $product->is_type( 'variable' ) && $product->get_attributes() ) ) {
1036 $gallery_ids = $product->get_gallery_image_ids();
1037
1038 if ( $meta['hover_img'] && ! empty( $gallery_ids ) ) {
1039 $arg['class'] .= ! $meta['gallery_img'] ? ' rtsb-double-img' : '';
1040
1041 if ( ! rtsb()->has_pro() ) {
1042 $arg['class'] .= ' rtsb-double-img';
1043 }
1044
1045 $arg['hover_img_html'] = Fns::get_product_image_html(
1046 'product',
1047 $gallery_ids[0],
1048 $meta['f_img_size'],
1049 null,
1050 $meta['custom_img_size'],
1051 $lazy_load,
1052 true
1053 );
1054 }
1055 }
1056
1057 $arg['hover_btn_text'] = $meta['hover_btn_text'];
1058
1059 if ( $meta['show_hover_btn_icon'] && ! empty( $meta['hover_btn_icon'] ) ) {
1060 $arg['hover_btn_icon'] = '<span class="icon">' . Fns::icons_manager( $meta['hover_btn_icon'], 'hover-btn-icon' ) . '</span>';
1061 }
1062
1063 $arg['img_args'] = [
1064 'p_id' => $arg['p_id'],
1065 'title' => get_the_title(),
1066 'p_link' => $arg['p_link'],
1067 'image_link' => $arg['image_link'],
1068 'img_html' => $arg['img_html'],
1069 'hover_img_html' => $arg['hover_img_html'],
1070 'meta' => $meta,
1071 ];
1072
1073 return $arg;
1074 }
1075
1076 /**
1077 * Renders add to cart button.
1078 *
1079 * @param array $args Cart args.
1080 *
1081 * @return string
1082 */
1083 public function render_add_to_cart_button( $args ) {
1084 list(
1085 'link' => $link,
1086 'id' => $id,
1087 'type' => $type,
1088 'text' => $text,
1089 'success' => $success,
1090 'icon_html' => $icon_html,
1091 'alignment' => $alignment,
1092 'product' => $product
1093 ) = $args;
1094
1095 $content = null;
1096 $rand = wp_rand();
1097 $ext_link = get_post_meta( $id, '_product_url', true );
1098 $btn_txt = get_post_meta( $id, '_button_text', true );
1099
1100 ob_start();
1101 do_action( 'rtsb/before/cart/button' );
1102 $content .= ob_get_clean();
1103
1104 $cart_attr = [
1105 'class' => 'rtsb-action-btn ' . $type . '-product tipsy icon-' . $alignment . ' ' . ( empty( $text ) ? 'no-text' : 'has-text' ),
1106 'href' => esc_url( $link ),
1107 'data-quantity' => '1',
1108 'data-product_id' => absint( $id ),
1109 'data-id' => absint( $id ),
1110 ];
1111
1112 if ( empty( $text ) ) {
1113 $tooltip_mapping = [
1114 'simple' => esc_attr__( 'Add to Cart', 'shopbuilder' ),
1115 'variable' => esc_attr__( 'Select Options', 'shopbuilder' ),
1116 'grouped' => esc_attr__( 'View Products', 'shopbuilder' ),
1117 'external' => ! empty( $btn_txt ) ? esc_html( $btn_txt ) : esc_html__( 'Buy Product', 'shopbuilder' ),
1118 'rtsb-gift-card' => esc_attr__( 'Select Gift Card', 'shopbuilder' ),
1119 ];
1120
1121 $cart_attr['title'] = $tooltip_mapping[ $type ];
1122 }
1123
1124 $content .= '<div class="rtsb-wc-add-to-cart-wrap">';
1125 if ( 'variable' === $type ) {
1126 $cart_attr['class'] = $cart_attr['class'] . ' rtsb_vs_add_to_cart ';
1127 }
1128 if ( 'variable' === $type || 'grouped' === $type ) {
1129 $cart_attr = apply_filters( 'rtsb/elementor/render/cart_attributes', $cart_attr, $product );
1130
1131 $this->add_attribute( 'rtsb_add_to_cart_button_' . $id . $rand, $cart_attr );
1132
1133 $text_btn = apply_filters(
1134 'rtsb/elementor/render/add_to_cart_text',
1135 'grouped' === $type ? __( 'View Products', 'shopbuilder' ) : __( 'Select Options', 'shopbuilder' ),
1136 $product,
1137 $type
1138 );
1139
1140 $content .= '<a ' . $this->get_attribute_string( 'rtsb_add_to_cart_button_' . $id . $rand ) . '>';
1141 $content .= $icon_html;
1142 $content .= '<span class="text ' . ( ! empty( $success ) ? 'has-success-text' : 'no-success-text' ) . '" data-success="' . esc_html( $success ) . '" data-cart-text="' . ( ! empty( $text ) ? esc_attr( $text ) : $tooltip_mapping['simple'] ) . '" data-variable-text="' . esc_attr( $text_btn ) . '">' . esc_html( $text_btn ) . '</span>';
1143 $content .= '<span></span>';
1144 } elseif ( 'external' === $type ) {
1145 if ( ! empty( $ext_link ) ) {
1146 $cart_attr['target'] = '_blank';
1147 }
1148
1149 $cart_attr['href'] = ! empty( $ext_link ) ? esc_url( $ext_link ) : esc_url( $link );
1150
1151 $this->add_attribute( 'rtsb_add_to_cart_button_' . $id . $rand, $cart_attr );
1152
1153 $content .= '<a ' . $this->get_attribute_string( 'rtsb_add_to_cart_button_' . $id . $rand ) . '>';
1154 $content .= $icon_html;
1155 $content .= '<span class="text">' . ( ! empty( $btn_txt ) ? esc_html( $btn_txt ) : esc_html__( 'Buy Product', 'shopbuilder' ) ) . '</span>';
1156 } else {
1157 $cart_attr['href'] = $cart_attr['href'] . '?add-to-cart=' . absint( $id );
1158 $cart_attr['class'] .= ' rtsb-add-to-cart-btn';
1159 $cart_attr['data-type'] = $type;
1160 $cart_attr = apply_filters( 'rtsb/elementor/render/cart_attributes', $cart_attr, $product );
1161
1162 $this->add_attribute( 'rtsb_add_to_cart_button_' . $id . $rand, $cart_attr );
1163
1164 $content .= '<a ' . $this->get_attribute_string( 'rtsb_add_to_cart_button_' . $id . $rand ) . '>';
1165 $content .= $icon_html;
1166 $content .= '<span class="text ' . ( ! empty( $success ) ? 'has-success-text' : 'no-success-text' ) . '" data-success="' . esc_html( $success ) . '">' . esc_html( $text ) . '</span>';
1167 $content .= '<span></span>';
1168 }
1169
1170 $content .= '</a>';
1171 $content .= '</div>';
1172
1173 ob_start();
1174 do_action( 'rtsb/after/cart/button' );
1175 $content .= ob_get_clean();
1176
1177 return apply_filters( 'rtsb/render/cart/button', $content, $args );
1178 }
1179
1180 /**
1181 * Button classes.
1182 *
1183 * @param array $params Button params.
1184 *
1185 * @return array
1186 */
1187 public function button_classes( $params ) {
1188 $params['classes'] = array_merge( $params['classes'], [ 'rtsb-action-btn', 'tipsy' ] );
1189
1190 return $params;
1191 }
1192
1193 /**
1194 * Render the section title.
1195 *
1196 * @param array $settings The Elementor settings.
1197 *
1198 * @return string
1199 */
1200 public function render_section_title( $settings ) {
1201 $html = '';
1202 $rand = wp_rand();
1203
1204 if ( empty( $settings['show_section_title'] ) || ( empty( $settings['query_products'] ) && 0 === count( $settings['query_products'] ) ) ) {
1205 return $html;
1206 }
1207
1208 // Set attributes for the section title.
1209 $this->add_attribute( 'rtsb_section_title_wrapper_' . $rand, 'class', 'rtsb-section-title-wrapper' );
1210 $this->add_attribute( 'rtsb_section_title_' . $rand, 'class', 'rtsb-section-title' );
1211
1212 // Start rendering.
1213 $html .= '<div ' . $this->get_attribute_string( 'rtsb_section_title_wrapper_' . $rand ) . '>';
1214 $html .= '<' . esc_attr( $settings['section_title_tag'] ) . ' ' . $this->get_attribute_string( 'rtsb_section_title_' . $rand ) . '>';
1215 $html .= esc_html( $settings['section_title_text'] );
1216 $html .= '</' . esc_attr( $settings['section_title_tag'] ) . '>';
1217 $html .= '</div>';
1218
1219 return $html;
1220 }
1221
1222 /**
1223 * Renders products pagination
1224 *
1225 * @param array $args Pagination args.
1226 * @param string $query_type Query type.
1227 *
1228 * @return string
1229 */
1230 public function render_products_pagination( $args, $query_type = 'wc' ) {
1231 list(
1232 'query' => $query,
1233 'meta' => $meta,
1234 'limit' => $limit,
1235 'per_page' => $per_page
1236 ) = $args;
1237
1238 $html_utility = null;
1239 $html = null;
1240 $ajax = false;
1241 $is_grid = preg_match( '/grid-layout/', $meta['layout'] ) || preg_match( '/list-layout/', $meta['layout'] );
1242 $posts_per_page = 'wp' === $query_type ? $query->query_vars['posts_per_page'] : null;
1243 $total_page = 'wp' === $query_type ? $query->max_num_pages : $query->get_products()->max_num_pages;
1244
1245 if ( $limit ) {
1246 if ( 'wc' === $query_type ) {
1247 $found_post = $query->get_products()->total;
1248 } elseif ( 'wp' === $query_type && ( empty( $wp_query->query['tax_query'] ) ) ) {
1249 $found_post = $query->found_posts;
1250 }
1251
1252 if ( $per_page && $found_post > $per_page ) {
1253 $found_post = $limit;
1254 $total_page = ceil( $found_post / $per_page );
1255 }
1256 }
1257
1258 $total_page = absint( $total_page );
1259
1260 $args = [
1261 'total_page' => $total_page,
1262 'posts_per_page' => 'wc' === $query_type ? $per_page : $posts_per_page,
1263 'ajax' => $ajax,
1264 ];
1265
1266 if ( 'pagination' === $meta['posts_loading_type'] ) {
1267 if ( $is_grid ) {
1268 $html_utility .= Fns::custom_pagination(
1269 $total_page,
1270 'wc' === $query_type ? $per_page : $posts_per_page
1271 );
1272 }
1273 } else {
1274 ob_start();
1275 do_action( 'rtsb/elementor/render/pagination', $meta, $query, $args );
1276 $html_utility .= ob_get_clean();
1277 }
1278
1279 if ( $html_utility ) {
1280 $rand = wp_rand();
1281
1282 // Adding pagination render attributes.
1283 $this->add_attribute(
1284 'rtsb_pagination_attr_' . $rand,
1285 [
1286 'class' => 'rtsb-pagination-wrap element-loading',
1287 'data-total-pages' => $total_page,
1288 'data-posts-per-page' => 'wc' === $query_type ? $per_page : $posts_per_page,
1289 'data-type' => $meta['posts_loading_type'],
1290 ]
1291 );
1292
1293 // Start rendering.
1294 $html = '<div ' . $this->get_attribute_string( 'rtsb_pagination_attr_' . $rand ) . '>';
1295 $html .= $html_utility;
1296 $html .= '</div><!-- .rtsb-pagination-wrap -->';
1297 }
1298
1299 return $html;
1300 }
1301
1302 /**
1303 * Renders slider buttons
1304 *
1305 * @param array $settings Settings array.
1306 *
1307 * @return string
1308 */
1309 public function render_slider_buttons( $settings ) {
1310 $html = '';
1311 $left_icon = $settings['left_arrow_icon'];
1312 $right_icon = $settings['right_arrow_icon'];
1313
1314 if ( ! empty( $settings['slider_nav'] ) ) {
1315 $html .=
1316 '<div class="swiper-nav">
1317 <div class="swiper-arrow swiper-button-next">
1318 ' . Fns::icons_manager( $right_icon ) . '
1319 </div>
1320 <div class="swiper-arrow swiper-button-prev">
1321 ' . Fns::icons_manager( $left_icon ) . '
1322 </div>
1323 </div>';
1324 }
1325
1326 $html .= ! empty( $settings['slider_dot'] ) ? '<div class="swiper-pagination rtsb-pos-s"></div>' : '';
1327
1328 return $html;
1329 }
1330
1331 /**
1332 * No products message.
1333 *
1334 * @param array $metas Meta data.
1335 *
1336 * @return void
1337 */
1338 public function no_products_msg( $metas ) {
1339 $content =
1340 '<div class="rtsb-notice woocommerce-no-products-found">
1341 <div class="woocommerce-info">' . esc_html__( 'No products were found matching your selection.', 'shopbuilder' ) . '</div>
1342 </div>';
1343
1344 $this->row( $content, $metas );
1345 }
1346
1347 /**
1348 * Start the rendering.
1349 *
1350 * @param array $metas Meta Data.
1351 * @param array $settings Widget settings.
1352 * @param array $temp_args Additional temporary arguments for rendering.
1353 *
1354 * @return string
1355 */
1356 public function render_start( $metas, $settings, $temp_args ) {
1357 ob_start();
1358
1359 /**
1360 * Before render row hook.
1361 */
1362 do_action( 'rtsb/elementor/render/before_row', $metas, $settings, $temp_args, $this );
1363
1364 return ob_get_clean();
1365 }
1366
1367 /**
1368 * End the rendering.
1369 *
1370 * @param array $metas Meta Data.
1371 * @param array $settings Widget settings.
1372 * @param array $products Array of products being rendered.
1373 *
1374 * @return string
1375 */
1376 public function render_end( $metas, $settings, $products ) {
1377 ob_start();
1378
1379 /**
1380 * After render row hook.
1381 */
1382 do_action( 'rtsb/elementor/render/after_row', $metas, $settings, $products );
1383
1384 return ob_get_clean();
1385 }
1386
1387 /**
1388 * Add render attribute.
1389 *
1390 * @param array|string $element The HTML element.
1391 * @param array|string $key Optional. Attribute key. Default is null.
1392 * @param array|string $value Optional. Attribute value. Default is null.
1393 * @param bool $overwrite Optional. Whether to overwrite existing.
1394 *
1395 * @return self
1396 */
1397 public function add_attribute( $element, $key = null, $value = null, $overwrite = false ) {
1398 if ( is_array( $element ) ) {
1399 foreach ( $element as $element_key => $attributes ) {
1400 $this->add_attribute( $element_key, $attributes, null, $overwrite );
1401 }
1402
1403 return $this;
1404 }
1405
1406 if ( is_array( $key ) ) {
1407 foreach ( $key as $attribute_key => $attributes ) {
1408 $this->add_attribute( $element, $attribute_key, $attributes, $overwrite );
1409 }
1410
1411 return $this;
1412 }
1413
1414 if ( empty( $this->attributes[ $element ][ $key ] ) ) {
1415 $this->attributes[ $element ][ $key ] = [];
1416 }
1417
1418 settype( $value, 'array' );
1419
1420 if ( $overwrite ) {
1421 $this->attributes[ $element ][ $key ] = $value;
1422 } else {
1423 $this->attributes[ $element ][ $key ] = array_merge( $this->attributes[ $element ][ $key ], $value );
1424 }
1425
1426 return $this;
1427 }
1428
1429 /**
1430 * Add link render attributes.
1431 *
1432 * @param array|string $element The HTML element.
1433 * @param array $url_control Array of link settings.
1434 * @param bool $overwrite Optional. Whether to overwrite existing
1435 * attribute. Default is false, not to overwrite.
1436 *
1437 * @return Render
1438 */
1439 public function add_link_attributes( $element, array $url_control, $overwrite = false ) {
1440 $attributes = [];
1441
1442 if ( ! empty( $url_control['url'] ) ) {
1443 $allowed_protocols = array_merge( wp_allowed_protocols(), [ 'skype', 'viber' ] );
1444
1445 $attributes['href'] = esc_url( $url_control['url'], $allowed_protocols );
1446 }
1447
1448 if ( ! empty( $url_control['is_external'] ) ) {
1449 $attributes['target'] = '_blank';
1450 }
1451
1452 if ( ! empty( $url_control['nofollow'] ) ) {
1453 $attributes['rel'] = 'nofollow';
1454 }
1455
1456 if ( ! empty( $url_control['custom_attributes'] ) ) {
1457 $attributes = array_merge( $attributes, Utils::parse_custom_attributes( $url_control['custom_attributes'] ) );
1458 }
1459
1460 if ( $attributes ) {
1461 $this->add_attribute( $element, $attributes, null, $overwrite );
1462 }
1463
1464 return $this;
1465 }
1466
1467 /**
1468 * Get render attribute string.
1469 *
1470 * @param string $element The element.
1471 *
1472 * @return string
1473 */
1474 public function get_attribute_string( $element ) {
1475 if ( empty( $this->attributes[ $element ] ) ) {
1476 return '';
1477 }
1478
1479 if ( class_exists( '\Elementor\Utils' ) ) {
1480 return Utils::render_html_attributes( $this->attributes[ $element ] );
1481 }
1482
1483 // Fallback.
1484 return $this->render_fallback_attributes( $this->attributes[ $element ] );
1485 }
1486
1487 /**
1488 * Function to render link attributes.
1489 *
1490 * @param string $id Element ID.
1491 * @param array $control_name Control name.
1492 * @param string $class_name Class name.
1493 *
1494 * @return string
1495 */
1496 public function render_link_attributes( $id, $control_name, $class_name = 'logo-img-link' ) {
1497 $this->add_attribute( $id, 'class', $class_name );
1498
1499 if ( ! empty( $control_name['url'] ) ) {
1500 $this->add_link_attributes( $id, $control_name );
1501 } else {
1502 $this->add_attribute( $id, 'role', 'button' );
1503 }
1504
1505 return $this->get_attribute_string( $id );
1506 }
1507
1508 /**
1509 * Render fallback attributes.
1510 *
1511 * @param array $attributes The attributes.
1512 *
1513 * @return string
1514 */
1515 private function render_fallback_attributes( $attributes ) {
1516 $rendered_attributes = [];
1517
1518 foreach ( $attributes as $attribute_key => $attribute_values ) {
1519 if ( is_array( $attribute_values ) ) {
1520 $attribute_values = implode( ' ', $attribute_values );
1521 }
1522
1523 $rendered_attributes[] = sprintf( '%1$s="%2$s"', $attribute_key, esc_attr( $attribute_values ) );
1524 }
1525
1526 return implode( ' ', $rendered_attributes );
1527 }
1528 }
1529