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

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