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

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