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

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