PluginProbe
ShopBuilder – WooCommerce Builder For Elementor / 3.1.0
ShopBuilder – WooCommerce Builder For Elementor v3.1.0
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.1.0, at app/Elementor/Render/Render.php

1,528 lines 45.2 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 'rel' => 'nofollow',
1108 'data-quantity' => '1',
1109 'data-product_id' => absint( $id ),
1110 'data-id' => absint( $id ),
1111 ];
1112
1113 if ( empty( $text ) ) {
1114 $tooltip_mapping = [
1115 'simple' => esc_attr__( 'Add to Cart', 'shopbuilder' ),
1116 'variable' => esc_attr__( 'Select Options', 'shopbuilder' ),
1117 'grouped' => esc_attr__( 'View Products', 'shopbuilder' ),
1118 'external' => ! empty( $btn_txt ) ? esc_html( $btn_txt ) : esc_html__( 'Buy Product', 'shopbuilder' ),
1119 'rtsb-gift-card' => esc_attr__( 'Select Gift Card', 'shopbuilder' ),
1120 ];
1121
1122 $cart_attr['title'] = $tooltip_mapping[ $type ];
1123 }
1124
1125 $content .= '<div class="rtsb-wc-add-to-cart-wrap">';
1126
1127 if ( 'variable' === $type || 'grouped' === $type ) {
1128 $cart_attr = apply_filters( 'rtsb/elementor/render/cart_attributes', $cart_attr, $product );
1129
1130 $this->add_attribute( 'rtsb_add_to_cart_button_' . $id . $rand, $cart_attr );
1131
1132 $text_btn = apply_filters(
1133 'rtsb/elementor/render/add_to_cart_text',
1134 'grouped' === $type ? __( 'View Products', 'shopbuilder' ) : __( 'Select Options', 'shopbuilder' ),
1135 $product,
1136 $type
1137 );
1138
1139 $content .= '<a ' . $this->get_attribute_string( 'rtsb_add_to_cart_button_' . $id . $rand ) . '>';
1140 $content .= $icon_html;
1141 $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>';
1142 $content .= '<span></span>';
1143 } elseif ( 'external' === $type ) {
1144 if ( ! empty( $ext_link ) ) {
1145 $cart_attr['target'] = '_blank';
1146 }
1147
1148 $cart_attr['href'] = ! empty( $ext_link ) ? esc_url( $ext_link ) : esc_url( $link );
1149
1150 $this->add_attribute( 'rtsb_add_to_cart_button_' . $id . $rand, $cart_attr );
1151
1152 $content .= '<a ' . $this->get_attribute_string( 'rtsb_add_to_cart_button_' . $id . $rand ) . '>';
1153 $content .= $icon_html;
1154 $content .= '<span class="text">' . ( ! empty( $btn_txt ) ? esc_html( $btn_txt ) : esc_html__( 'Buy Product', 'shopbuilder' ) ) . '</span>';
1155 } else {
1156 $cart_attr['href'] = $cart_attr['href'] . '?add-to-cart=' . absint( $id );
1157 $cart_attr['class'] .= ' rtsb-add-to-cart-btn';
1158 $cart_attr['data-type'] = $type;
1159 $cart_attr = apply_filters( 'rtsb/elementor/render/cart_attributes', $cart_attr, $product );
1160
1161 $this->add_attribute( 'rtsb_add_to_cart_button_' . $id . $rand, $cart_attr );
1162
1163 $content .= '<a ' . $this->get_attribute_string( 'rtsb_add_to_cart_button_' . $id . $rand ) . '>';
1164 $content .= $icon_html;
1165 $content .= '<span class="text ' . ( ! empty( $success ) ? 'has-success-text' : 'no-success-text' ) . '" data-success="' . esc_html( $success ) . '">' . esc_html( $text ) . '</span>';
1166 $content .= '<span></span>';
1167 }
1168
1169 $content .= '</a>';
1170 $content .= '</div>';
1171
1172 ob_start();
1173 do_action( 'rtsb/after/cart/button' );
1174 $content .= ob_get_clean();
1175
1176 return apply_filters( 'rtsb/render/cart/button', $content, $args );
1177 }
1178
1179 /**
1180 * Button classes.
1181 *
1182 * @param array $params Button params.
1183 *
1184 * @return array
1185 */
1186 public function button_classes( $params ) {
1187 $params['classes'] = array_merge( $params['classes'], [ 'rtsb-action-btn', 'tipsy' ] );
1188
1189 return $params;
1190 }
1191
1192 /**
1193 * Render the section title.
1194 *
1195 * @param array $settings The Elementor settings.
1196 *
1197 * @return string
1198 */
1199 public function render_section_title( $settings ) {
1200 $html = '';
1201 $rand = wp_rand();
1202
1203 if ( empty( $settings['show_section_title'] ) || ( empty( $settings['query_products'] ) && 0 === count( $settings['query_products'] ) ) ) {
1204 return $html;
1205 }
1206
1207 // Set attributes for the section title.
1208 $this->add_attribute( 'rtsb_section_title_wrapper_' . $rand, 'class', 'rtsb-section-title-wrapper' );
1209 $this->add_attribute( 'rtsb_section_title_' . $rand, 'class', 'rtsb-section-title' );
1210
1211 // Start rendering.
1212 $html .= '<div ' . $this->get_attribute_string( 'rtsb_section_title_wrapper_' . $rand ) . '>';
1213 $html .= '<' . esc_attr( $settings['section_title_tag'] ) . ' ' . $this->get_attribute_string( 'rtsb_section_title_' . $rand ) . '>';
1214 $html .= esc_html( $settings['section_title_text'] );
1215 $html .= '</' . esc_attr( $settings['section_title_tag'] ) . '>';
1216 $html .= '</div>';
1217
1218 return $html;
1219 }
1220
1221 /**
1222 * Renders products pagination
1223 *
1224 * @param array $args Pagination args.
1225 * @param string $query_type Query type.
1226 *
1227 * @return string
1228 */
1229 public function render_products_pagination( $args, $query_type = 'wc' ) {
1230 list(
1231 'query' => $query,
1232 'meta' => $meta,
1233 'limit' => $limit,
1234 'per_page' => $per_page
1235 ) = $args;
1236
1237 $html_utility = null;
1238 $html = null;
1239 $ajax = false;
1240 $is_grid = preg_match( '/grid-layout/', $meta['layout'] ) || preg_match( '/list-layout/', $meta['layout'] );
1241 $posts_per_page = 'wp' === $query_type ? $query->query_vars['posts_per_page'] : null;
1242 $total_page = 'wp' === $query_type ? $query->max_num_pages : $query->get_products()->max_num_pages;
1243
1244 if ( $limit ) {
1245 if ( 'wc' === $query_type ) {
1246 $found_post = $query->get_products()->total;
1247 } elseif ( 'wp' === $query_type && ( empty( $wp_query->query['tax_query'] ) ) ) {
1248 $found_post = $query->found_posts;
1249 }
1250
1251 if ( $per_page && $found_post > $per_page ) {
1252 $found_post = $limit;
1253 $total_page = ceil( $found_post / $per_page );
1254 }
1255 }
1256
1257 $total_page = absint( $total_page );
1258
1259 $args = [
1260 'total_page' => $total_page,
1261 'posts_per_page' => 'wc' === $query_type ? $per_page : $posts_per_page,
1262 'ajax' => $ajax,
1263 ];
1264
1265 if ( 'pagination' === $meta['posts_loading_type'] ) {
1266 if ( $is_grid ) {
1267 $html_utility .= Fns::custom_pagination(
1268 $total_page,
1269 'wc' === $query_type ? $per_page : $posts_per_page
1270 );
1271 }
1272 } else {
1273 ob_start();
1274 do_action( 'rtsb/elementor/render/pagination', $meta, $query, $args );
1275 $html_utility .= ob_get_clean();
1276 }
1277
1278 if ( $html_utility ) {
1279 $rand = wp_rand();
1280
1281 // Adding pagination render attributes.
1282 $this->add_attribute(
1283 'rtsb_pagination_attr_' . $rand,
1284 [
1285 'class' => 'rtsb-pagination-wrap element-loading',
1286 'data-total-pages' => $total_page,
1287 'data-posts-per-page' => 'wc' === $query_type ? $per_page : $posts_per_page,
1288 'data-type' => $meta['posts_loading_type'],
1289 ]
1290 );
1291
1292 // Start rendering.
1293 $html = '<div ' . $this->get_attribute_string( 'rtsb_pagination_attr_' . $rand ) . '>';
1294 $html .= $html_utility;
1295 $html .= '</div><!-- .rtsb-pagination-wrap -->';
1296 }
1297
1298 return $html;
1299 }
1300
1301 /**
1302 * Renders slider buttons
1303 *
1304 * @param array $settings Settings array.
1305 *
1306 * @return string
1307 */
1308 public function render_slider_buttons( $settings ) {
1309 $html = '';
1310 $left_icon = $settings['left_arrow_icon'];
1311 $right_icon = $settings['right_arrow_icon'];
1312
1313 if ( ! empty( $settings['slider_nav'] ) ) {
1314 $html .=
1315 '<div class="swiper-nav">
1316 <div class="swiper-arrow swiper-button-next">
1317 ' . Fns::icons_manager( $right_icon ) . '
1318 </div>
1319 <div class="swiper-arrow swiper-button-prev">
1320 ' . Fns::icons_manager( $left_icon ) . '
1321 </div>
1322 </div>';
1323 }
1324
1325 $html .= ! empty( $settings['slider_dot'] ) ? '<div class="swiper-pagination rtsb-pos-s"></div>' : '';
1326
1327 return $html;
1328 }
1329
1330 /**
1331 * No products message.
1332 *
1333 * @param array $metas Meta data.
1334 *
1335 * @return void
1336 */
1337 public function no_products_msg( $metas ) {
1338 $content =
1339 '<div class="rtsb-notice woocommerce-no-products-found">
1340 <div class="woocommerce-info">' . esc_html__( 'No products were found matching your selection.', 'shopbuilder' ) . '</div>
1341 </div>';
1342
1343 $this->row( $content, $metas );
1344 }
1345
1346 /**
1347 * Start the rendering.
1348 *
1349 * @param array $metas Meta Data.
1350 * @param array $settings Widget settings.
1351 * @param array $temp_args Additional temporary arguments for rendering.
1352 *
1353 * @return string
1354 */
1355 public function render_start( $metas, $settings, $temp_args ) {
1356 ob_start();
1357
1358 /**
1359 * Before render row hook.
1360 */
1361 do_action( 'rtsb/elementor/render/before_row', $metas, $settings, $temp_args, $this );
1362
1363 return ob_get_clean();
1364 }
1365
1366 /**
1367 * End the rendering.
1368 *
1369 * @param array $metas Meta Data.
1370 * @param array $settings Widget settings.
1371 * @param array $products Array of products being rendered.
1372 *
1373 * @return string
1374 */
1375 public function render_end( $metas, $settings, $products ) {
1376 ob_start();
1377
1378 /**
1379 * After render row hook.
1380 */
1381 do_action( 'rtsb/elementor/render/after_row', $metas, $settings, $products );
1382
1383 return ob_get_clean();
1384 }
1385
1386 /**
1387 * Add render attribute.
1388 *
1389 * @param array|string $element The HTML element.
1390 * @param array|string $key Optional. Attribute key. Default is null.
1391 * @param array|string $value Optional. Attribute value. Default is null.
1392 * @param bool $overwrite Optional. Whether to overwrite existing.
1393 *
1394 * @return self
1395 */
1396 public function add_attribute( $element, $key = null, $value = null, $overwrite = false ) {
1397 if ( is_array( $element ) ) {
1398 foreach ( $element as $element_key => $attributes ) {
1399 $this->add_attribute( $element_key, $attributes, null, $overwrite );
1400 }
1401
1402 return $this;
1403 }
1404
1405 if ( is_array( $key ) ) {
1406 foreach ( $key as $attribute_key => $attributes ) {
1407 $this->add_attribute( $element, $attribute_key, $attributes, $overwrite );
1408 }
1409
1410 return $this;
1411 }
1412
1413 if ( empty( $this->attributes[ $element ][ $key ] ) ) {
1414 $this->attributes[ $element ][ $key ] = [];
1415 }
1416
1417 settype( $value, 'array' );
1418
1419 if ( $overwrite ) {
1420 $this->attributes[ $element ][ $key ] = $value;
1421 } else {
1422 $this->attributes[ $element ][ $key ] = array_merge( $this->attributes[ $element ][ $key ], $value );
1423 }
1424
1425 return $this;
1426 }
1427
1428 /**
1429 * Add link render attributes.
1430 *
1431 * @param array|string $element The HTML element.
1432 * @param array $url_control Array of link settings.
1433 * @param bool $overwrite Optional. Whether to overwrite existing
1434 * attribute. Default is false, not to overwrite.
1435 *
1436 * @return Render
1437 */
1438 public function add_link_attributes( $element, array $url_control, $overwrite = false ) {
1439 $attributes = [];
1440
1441 if ( ! empty( $url_control['url'] ) ) {
1442 $allowed_protocols = array_merge( wp_allowed_protocols(), [ 'skype', 'viber' ] );
1443
1444 $attributes['href'] = esc_url( $url_control['url'], $allowed_protocols );
1445 }
1446
1447 if ( ! empty( $url_control['is_external'] ) ) {
1448 $attributes['target'] = '_blank';
1449 }
1450
1451 if ( ! empty( $url_control['nofollow'] ) ) {
1452 $attributes['rel'] = 'nofollow';
1453 }
1454
1455 if ( ! empty( $url_control['custom_attributes'] ) ) {
1456 $attributes = array_merge( $attributes, Utils::parse_custom_attributes( $url_control['custom_attributes'] ) );
1457 }
1458
1459 if ( $attributes ) {
1460 $this->add_attribute( $element, $attributes, null, $overwrite );
1461 }
1462
1463 return $this;
1464 }
1465
1466 /**
1467 * Get render attribute string.
1468 *
1469 * @param string $element The element.
1470 *
1471 * @return string
1472 */
1473 public function get_attribute_string( $element ) {
1474 if ( empty( $this->attributes[ $element ] ) ) {
1475 return '';
1476 }
1477
1478 if ( class_exists( '\Elementor\Utils' ) ) {
1479 return Utils::render_html_attributes( $this->attributes[ $element ] );
1480 }
1481
1482 // Fallback.
1483 return $this->render_fallback_attributes( $this->attributes[ $element ] );
1484 }
1485
1486 /**
1487 * Function to render link attributes.
1488 *
1489 * @param string $id Element ID.
1490 * @param array $control_name Control name.
1491 * @param string $class_name Class name.
1492 *
1493 * @return string
1494 */
1495 public function render_link_attributes( $id, $control_name, $class_name = 'logo-img-link' ) {
1496 $this->add_attribute( $id, 'class', $class_name );
1497
1498 if ( ! empty( $control_name['url'] ) ) {
1499 $this->add_link_attributes( $id, $control_name );
1500 } else {
1501 $this->add_attribute( $id, 'role', 'button' );
1502 }
1503
1504 return $this->get_attribute_string( $id );
1505 }
1506
1507 /**
1508 * Render fallback attributes.
1509 *
1510 * @param array $attributes The attributes.
1511 *
1512 * @return string
1513 */
1514 private function render_fallback_attributes( $attributes ) {
1515 $rendered_attributes = [];
1516
1517 foreach ( $attributes as $attribute_key => $attribute_values ) {
1518 if ( is_array( $attribute_values ) ) {
1519 $attribute_values = implode( ' ', $attribute_values );
1520 }
1521
1522 $rendered_attributes[] = sprintf( '%1$s="%2$s"', $attribute_key, esc_attr( $attribute_values ) );
1523 }
1524
1525 return implode( ' ', $rendered_attributes );
1526 }
1527 }
1528