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

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