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

1,493 lines 44.3 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 global $product;
307 // Loop arg.
308 $arg = $this->arg_dataset( $settings, $product, $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 if ( rtsb()->has_pro() && ( ! empty( $metas['brand_term'] ) ) ) {
615 $term = $metas['brand_term'];
616 }
617
618 // Query.
619 if ( $is_single ) {
620 if ( empty( $term ) ) {
621 return '<p>' . esc_html__( 'Please select a category.', 'shopbuilder' ) . '</p>';
622 }
623
624 $taxonomy_query = get_term( $term, $taxonomy );
625 } else {
626 // Query Args.
627 $args = ( new QueryArgs() )->buildArgs( $metas, 'category' );
628
629 $taxonomy_query = get_terms( $args );
630 }
631
632 if ( ! is_wp_error( $taxonomy_query ) && ! empty( $taxonomy_query ) ) {
633 // Row.
634 $this->row( $this->category_loop( $taxonomy_query, $metas, $template, $is_single ), $metas );
635 } else {
636 $this->html .= '<p>' . esc_html__( 'No categories found', 'shopbuilder' ) . '</p>';
637 }
638
639 // Container.
640 $this->container( $this->html, $settings, 'rtsb-categories-container', $is_single );
641
642 return $this->html;
643 }
644
645 /**
646 * Render Category View.
647 *
648 * @param string $template Template name.
649 * @param array $settings Control settings.
650 *
651 * @return string
652 */
653 public function social_share_view( $template, $settings ) {
654 $this->get_settings = $settings;
655 $share_items = [];
656
657 foreach ( $settings['share_platforms'] as $platform ) {
658 $share_items[] = [
659 'share_items' => $platform['share_items'],
660 'share_text' => $platform['share_text'],
661 ];
662 }
663
664 if ( ! empty( $share_items ) && is_array( $share_items ) ) {
665 $arg['p_id'] = get_the_ID();
666 $arg['share_items'] = $share_items;
667 $arg['preset'] = $settings['layout'];
668 $arg['direction'] = ! empty( $settings['layout_direction'] ) ? $settings['layout_direction'] : 'horizontal';
669 $arg['show_icon'] = ! empty( $settings['show_share_icon'] );
670 $arg['show_text'] = ! empty( $settings['show_share_text'] );
671 $arg['show_pre_text'] = ! empty( $settings['show_share_pre_text'] );
672 $arg['share_pre_text'] = ! empty( $settings['share_pre_text'] ) ? $settings['share_pre_text'] : '';
673 $arg['raw_settings'] = $this->get_settings_for_display();
674 $this->html = Fns::load_template( $template, $arg, true );
675 } else {
676 $this->html = '<p>' . esc_html__( 'Please select a social sharing platform.', 'shopbuilder' ) . '</p>';
677 }
678
679 return $this->html;
680 }
681
682 /**
683 * Builds an array with meta values.
684 *
685 * @param array $meta Meta values.
686 * @param object $product Product object.
687 * @param bool $lazy_load Lazy Load.
688 * @param int $i Integer.
689 *
690 * @return array
691 */
692 public function arg_dataset( array $meta, object $product, bool $lazy_load = false, $i = 0 ) {
693 $post_id = $product->get_id();
694 $arg = $this->build_product_arg(
695 [
696 'product' => $product,
697 'id' => $product->get_id(),
698 'type' => $product->get_type(),
699 'meta' => $meta,
700 'lazy_load' => $lazy_load,
701 'i' => $i,
702 ]
703 );
704
705 return apply_filters( 'rtsb/elementor/render/arg_dataset', $arg, $post_id, $meta, $lazy_load );
706 }
707
708 /**
709 * Builds an array with meta values for WP_Query.
710 *
711 * @param array $meta Meta values.
712 * @param int $post_id Post ID.
713 * @param bool $lazy_load Lazy Load.
714 * @param int $i Integer.
715 *
716 * @return array
717 */
718 public function wp_arg_dataset( array $meta, int $post_id, bool $lazy_load = false, $i = 0 ) {
719 if ( ( empty( $meta ) && ! $post_id ) || ! in_array( get_post_type( $post_id ), [ 'product_variation', 'product' ] ) ) {
720 return [];
721 }
722
723 global $product;
724
725 if ( ! $product instanceof WC_Product && $post_id ) {
726 $product = wc_get_product( $post_id );
727 }
728
729 $post_id = $product->get_id();
730 $arg = $this->build_product_arg(
731 [
732 'product' => $product,
733 'id' => $post_id,
734 'type' => $product->get_type(),
735 'meta' => $meta,
736 'lazy_load' => $lazy_load,
737 'i' => $i,
738 ]
739 );
740
741 return apply_filters( 'rtsb/elementor/render/arg_dataset', $arg, $post_id, $meta, $lazy_load );
742 }
743
744 /**
745 * Builds an array with meta values.
746 *
747 * @param array $meta Meta values.
748 * @param Object $query Category object.
749 * @param bool $fullwidth Fullwidth check.
750 * @param bool $lazy_load Lazy Load.
751 * @param bool $category_loop Category loop.
752 *
753 * @return array
754 */
755 public function cat_arg_dataset( $meta, $query = null, $fullwidth = false, $lazy_load = false, $category_loop = false ) {
756 if ( empty( $meta ) ) {
757 return [];
758 }
759
760 $arg = [];
761
762 $products = null;
763 $arg['class'] = null;
764 $arg['grid'] = null;
765 $arg['anchor_class'] = null;
766 $arg['p_sale'] = null;
767 $arg['count'] = max( $query->count, 0 );
768 $arg['count_position'] = $meta['count_position'];
769 $arg['excerpt_position'] = $meta['cat_excerpt_position'];
770 $arg['cat_link'] = get_term_link( $query );
771 $arg['target'] = '_self';
772 $arg['raw_settings'] = ! empty( $meta['raw_settings'] ) ? $meta['raw_settings'] : [];
773
774 if ( ! empty( $meta['tag_term'] ) ) {
775 $arg['tag_term'] = $meta['tag_term'];
776 }
777
778 if ( ! empty( $meta['custom_link']['url'] ) ) {
779 $arg['cat_link'] = $meta['custom_link']['url'];
780 $arg['target'] = $meta['custom_link']['is_external'] ? '_blank' : '_self';
781 }
782
783 $title = $query->name;
784 $excerpt = $query->description;
785
786 if ( in_array( 'badges', $meta['visibility'], true ) ) {
787 $arg['p_sale'] = $meta['custom_badge_text'];
788 }
789
790 $arg['badge_class'] = RenderHelpers::badge_class( $meta['badge_preset'] );
791
792 if ( $arg['count'] >= 0 ) {
793 $prefix = ! empty( $meta['before_count'] ) ? $meta['before_count'] : '';
794 $suffix = ! empty( $meta['after_count'] ) ? $meta['after_count'] : '';
795
796 $arg['count'] = $prefix . $arg['count'] . $suffix;
797 }
798
799 if ( $meta['show_custom_title'] && ! empty( $meta['cat_custom_title'] ) ) {
800 $title = $meta['cat_custom_title'];
801 }
802
803 if ( $meta['show_custom_excerpt'] && ! empty( $meta['cat_custom_excerpt'] ) ) {
804 $excerpt = $meta['cat_custom_excerpt'];
805 }
806
807 $arg['c_img'] = $meta['c_img'];
808 $arg['items'] = $meta['visibility'];
809 $arg['title_tag'] = $meta['title_tag'];
810 $arg['title_class'] = 'category-title rtsb-text-limit limit-' . $meta['title_limit'] . ( ! $meta['title_link'] ? ' no-link' : '' );
811 $arg['title'] = Fns::text_truncation( $title, $meta['title_limit_custom'] );
812 $arg['excerpt_limit'] = $meta['excerpt_limit'];
813 $arg['excerpt'] = wpautop( Fns::text_truncation( $excerpt, $meta['excerpt_limit_custom'] ) );
814 $arg['title_link'] = $meta['title_link'];
815 $arg['image_link'] = $meta['image_link'];
816 $arg['grid'] .= 'rtsb-col-grid ';
817
818 if ( ! $fullwidth ) {
819 if ( 'masonry' === $meta['grid_type'] ) {
820 $arg['class'] .= 'masonry-grid-item ';
821 } else {
822 $arg['class'] .= 'even-grid-item ';
823 }
824 }
825
826 if ( ! empty( $meta['active_cat_slider'] ) ) {
827 $arg['class'] .= ' rtsb-slide-item swiper-slide animated rtFadeIn ';
828 }
829
830 $arg['class'] .= 'rtsb-category-grid';
831
832 ob_start();
833 do_action( 'rtsb/categories/category_image_override', $meta, $query->term_id );
834 $override_category_image = ob_get_clean();
835
836 $arg['img_html'] = $meta['c_img'] ? Fns::get_product_image_html(
837 'category',
838 $query->term_id,
839 $meta['f_img_size'],
840 $meta['default_img_id'],
841 $meta['custom_img_size'],
842 $lazy_load,
843 false,
844 false,
845 $override_category_image
846 ) : null;
847
848 if ( $meta['show_custom_image'] && ! empty( $meta['custom_image'] ) ) {
849 $arg['img_html'] = Fns::get_product_image_html(
850 'custom',
851 null,
852 $meta['f_img_size'],
853 $meta['custom_image'],
854 $meta['custom_img_size'],
855 $lazy_load
856 );
857 }
858
859 $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' : '' );
860
861 if ( $category_loop ) {
862 $query_args = [
863 'product_category_id' => $query->term_id,
864 'limit' => -1,
865 'order' => 'DESC',
866 'orderby' => 'date',
867 ];
868
869 $products_query = new WC_Product_Query( $query_args );
870 $products = $products_query->get_products();
871 }
872
873 return apply_filters( 'rtsb/elementor/render/cat_arg_dataset', $arg, $meta, $query, $fullwidth, $lazy_load, $products );
874 }
875
876 /**
877 * Building product args.
878 *
879 * @param array $args Args.
880 * @return array
881 */
882 public function build_product_arg( $args ) {
883 list(
884 'product' => $product,
885 'id' => $post_id,
886 'type' => $p_type,
887 'meta' => $meta,
888 'lazy_load' => $lazy_load,
889 'i' => $i,
890 ) = $args;
891
892 $arg = [];
893 $arg['class'] = null;
894 $arg['grid'] = null;
895 $arg['anchor_class'] = null;
896 $arg['hover_img_html'] = null;
897 $arg['s_link'] = [];
898 $arg['add_to_cart'] = '';
899 $arg['p_counter'] = absint( $i );
900 $arg['p_id'] = $post_id;
901 $arg['product'] = $product;
902 $arg['p_link'] = $product->get_permalink();
903 $arg['p_sale'] = '';
904 $arg['rating_args'] = [];
905 $arg['has_attributes'] = $product->is_type( 'variable' ) && $product->get_attributes();
906 $arg['raw_settings'] = ! empty( $meta['raw_settings'] ) ? $meta['raw_settings'] : [];
907 $cart_icon_html = '';
908 $layout = $meta['layout'];
909
910 // Building the arg.
911 $arg['target'] = '_self';
912 $arg['f_img'] = $meta['f_img'];
913 $arg['items'] = $meta['visibility'];
914 $arg['title_tag'] = $meta['title_tag'];
915 $arg['title_class'] = 'product-title rtsb-text-limit limit-' . $meta['title_limit'] . ( ! $meta['title_link'] ? ' no-link' : '' );
916 $arg['title'] = Fns::text_truncation( $product->get_title(), $meta['title_limit_custom'] );
917 $arg['excerpt_limit'] = $meta['excerpt_limit'];
918 $arg['excerpt'] = wpautop( Fns::text_truncation( do_shortcode( get_post_field( 'post_excerpt', $post_id ) ), $meta['excerpt_limit_custom'] ) );
919 $arg['title_link'] = $meta['title_link'];
920 $arg['image_link'] = $meta['image_link'];
921 $arg['out_of_stock'] = $meta['out_of_stock_badge_text'];
922 $arg['action_btn_preset'] = $meta['btn_preset'];
923 $arg['action_btn_position'] = $meta['btn_position'];
924 $arg['swatch_position'] = $meta['swatch_position'];
925 $arg['swatch_type'] = $meta['swatch_type'];
926 $arg['tooltip_position'] = $meta['tooltip_position'];
927
928 $is_list = preg_match( '/list/', $layout );
929 $is_carousel = preg_match( '/slider/', $layout );
930
931 if ( ! $is_carousel ) {
932 $arg['grid'] .= 'rtsb-col-grid ';
933
934 if ( 'masonry' === $meta['grid_type'] ) {
935 $arg['class'] .= 'masonry-grid-item ';
936 } else {
937 $arg['class'] .= 'even-grid-item ';
938 }
939 } else {
940 $arg['grid'] .= 'rtsb-col-grid rtsb-col-full rtsb-slide-item swiper-slide ';
941 }
942
943 $d_cols = 0 === $meta['d_cols'] ? RenderHelpers::default_columns( $layout ) : $meta['d_cols'];
944 $t_cols = 0 === $meta['t_cols'] ? 2 : $meta['t_cols'];
945 $m_cols = 0 === $meta['m_cols'] ? 1 : $meta['m_cols'];
946
947 $arg['grid'] .= 0 === $i % $d_cols ? ' row-last desktop-row-last' : '';
948 $arg['grid'] .= 0 === $i % $t_cols ? ' row-last tablet-row-last' : '';
949 $arg['grid'] .= 0 === $i % $m_cols ? ' row-last mobile-row-last' : '';
950
951 $arg['class'] .= 'rtsb-product';
952
953 if ( $is_list ) {
954 $arg['class'] .= ' rtsb-product-list';
955 }
956
957 $arg['class'] .= ' animated rtFadeIn';
958
959 if ( in_array( 'badges', $meta['visibility'], true ) ) {
960 $badge_module = ! empty( $meta['enable_badges_module'] );
961 $arg['p_sale'] = Fns::is_module_active( 'product_badges' ) && $badge_module ? 'rtsb_yes' : Fns::get_promo_badge(
962 $product,
963 $meta['sale_badge_type'],
964 $meta['sale_badge_text'],
965 apply_filters( 'rtsb/elementor/render/out_of_stock_badge_text', $meta['out_of_stock_badge_text'] )
966 );
967 }
968
969 $arg['badge_class'] = RenderHelpers::badge_class( $meta['badge_preset'] );
970
971 $meta['add_to_cart_text'] = apply_filters( 'rtsb/elementor/render/add_to_cart_text', $meta['add_to_cart_text'], $product );
972
973 if ( ! $meta['show_cart_text'] ) {
974 $meta['add_to_cart_text'] = '';
975 $meta['add_to_cart_success_text'] = '';
976 }
977
978 if ( in_array( 'add_to_cart', $meta['visibility'], true ) ) {
979 if ( ! empty( $meta['add_to_cart_icon'] ) ) {
980 $cart_icon_html .= Fns::icons_manager( $meta['add_to_cart_icon'], 'cart-icon' );
981 }
982
983 if ( ! empty( $meta['add_to_cart_success_icon'] ) ) {
984 $cart_icon_html .= Fns::icons_manager( $meta['add_to_cart_success_icon'], 'cart-success-icon' );
985 }
986
987 $cart_icon = ! empty( $cart_icon_html ) ? '<span class="icon">' . $cart_icon_html . '</span>' : '';
988
989 if ( $product->is_purchasable() || 'grouped' === $p_type || 'external' === $p_type ) {
990 if ( $product->is_in_stock() ) {
991 $arg['add_to_cart'] = $this->render_add_to_cart_button(
992 [
993 'link' => $product->get_permalink(),
994 'id' => $post_id,
995 'type' => $p_type,
996 'text' => $meta['add_to_cart_text'],
997 'success' => $meta['add_to_cart_success_text'],
998 'icon_html' => $cart_icon,
999 'alignment' => $meta['add_to_cart_alignment'],
1000 'product' => $product,
1001 ]
1002 );
1003 }
1004 }
1005 }
1006
1007 if ( in_array( 'quick_view', $meta['visibility'], true ) ) {
1008 add_filter( 'rtsb/module/quick_view/button_params', [ $this, 'button_classes' ] );
1009 add_filter( 'rtsb/module/quick_view/icon_html', [ $this, 'quick_view_icon' ] );
1010 }
1011
1012 if ( in_array( 'compare', $meta['visibility'], true ) ) {
1013 add_filter( 'rtsb/module/compare/button_params', [ $this, 'button_classes' ] );
1014 add_filter( 'rtsb/module/compare/icon_html', [ $this, 'compare_icon' ] );
1015 }
1016
1017 if ( in_array( 'wishlist', $meta['visibility'], true ) ) {
1018 add_filter( 'rtsb/module/wishlist/button_params', [ $this, 'button_classes' ] );
1019 add_filter( 'rtsb/module/wishlist/icon_html', [ $this, 'wishlist_icon' ] );
1020 }
1021
1022 $arg['img_html'] = $meta['f_img'] ? Fns::get_product_image_html(
1023 'product',
1024 $post_id,
1025 $meta['f_img_size'],
1026 $meta['default_img_id'],
1027 $meta['custom_img_size'],
1028 $lazy_load
1029 ) : null;
1030
1031 if ( ! ( function_exists( 'rtwpvsp' ) && $product->is_type( 'variable' ) && $product->get_attributes() ) ) {
1032 $gallery_ids = $product->get_gallery_image_ids();
1033
1034 if ( $meta['hover_img'] && ! empty( $gallery_ids ) ) {
1035 $arg['class'] .= ! $meta['gallery_img'] ? ' rtsb-double-img' : '';
1036
1037 if ( ! rtsb()->has_pro() ) {
1038 $arg['class'] .= ' rtsb-double-img';
1039 }
1040
1041 $arg['hover_img_html'] = Fns::get_product_image_html(
1042 'product',
1043 $gallery_ids[0],
1044 $meta['f_img_size'],
1045 null,
1046 $meta['custom_img_size'],
1047 $lazy_load,
1048 true
1049 );
1050 }
1051 }
1052
1053 $arg['hover_btn_text'] = $meta['hover_btn_text'];
1054
1055 if ( $meta['show_hover_btn_icon'] && ! empty( $meta['hover_btn_icon'] ) ) {
1056 $arg['hover_btn_icon'] = '<span class="icon">' . Fns::icons_manager( $meta['hover_btn_icon'], 'hover-btn-icon' ) . '</span>';
1057 }
1058
1059 $arg['img_args'] = [
1060 'p_id' => $arg['p_id'],
1061 'title' => get_the_title(),
1062 'p_link' => $arg['p_link'],
1063 'image_link' => $arg['image_link'],
1064 'img_html' => $arg['img_html'],
1065 'hover_img_html' => $arg['hover_img_html'],
1066 'meta' => $meta,
1067 ];
1068
1069 return $arg;
1070 }
1071
1072 /**
1073 * Renders add to cart button.
1074 *
1075 * @param array $args Cart args.
1076 *
1077 * @return string
1078 */
1079 public function render_add_to_cart_button( $args ) {
1080 list(
1081 'link' => $link,
1082 'id' => $id,
1083 'type' => $type,
1084 'text' => $text,
1085 'success' => $success,
1086 'icon_html' => $icon_html,
1087 'alignment' => $alignment,
1088 'product' => $product
1089 ) = $args;
1090
1091 $content = null;
1092 $rand = wp_rand();
1093 $ext_link = get_post_meta( $id, '_product_url', true );
1094 $btn_txt = get_post_meta( $id, '_button_text', true );
1095
1096 ob_start();
1097 do_action( 'rtsb/before/cart/button' );
1098 $content .= ob_get_clean();
1099
1100 $cart_attr = [
1101 'class' => 'rtsb-action-btn ' . $type . '-product tipsy icon-' . $alignment . ' ' . ( empty( $text ) ? 'no-text' : 'has-text' ),
1102 'href' => esc_url( $link ),
1103 'rel' => 'nofollow',
1104 'data-quantity' => '1',
1105 'data-product_id' => absint( $id ),
1106 'data-id' => absint( $id ),
1107 ];
1108
1109 if ( empty( $text ) ) {
1110 $tooltip_mapping = [
1111 'simple' => esc_attr__( 'Add to Cart', 'shopbuilder' ),
1112 'variable' => esc_attr__( 'Select Options', 'shopbuilder' ),
1113 'grouped' => esc_attr__( 'View Products', 'shopbuilder' ),
1114 'external' => ! empty( $btn_txt ) ? esc_html( $btn_txt ) : esc_html__( 'Buy Product', 'shopbuilder' ),
1115 'rtsb-gift-card' => esc_attr__( 'Select Gift Card', 'shopbuilder' ),
1116 ];
1117
1118 $cart_attr['title'] = $tooltip_mapping[ $type ];
1119 }
1120
1121 $content .= '<div class="rtsb-wc-add-to-cart-wrap">';
1122
1123 if ( 'variable' === $type || 'grouped' === $type ) {
1124 $cart_attr = apply_filters( 'rtsb/elementor/render/cart_attributes', $cart_attr, $product );
1125
1126 $this->add_attribute( 'rtsb_add_to_cart_button_' . $id . $rand, $cart_attr );
1127
1128 $text_btn = 'grouped' === $type ? __( 'View Products', 'shopbuilder' ) : __( 'Select Options', 'shopbuilder' );
1129
1130 $content .= '<a ' . $this->get_attribute_string( 'rtsb_add_to_cart_button_' . $id . $rand ) . '>';
1131 $content .= $icon_html;
1132 $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>';
1133 $content .= '<span></span>';
1134 } elseif ( 'external' === $type ) {
1135 if ( ! empty( $ext_link ) ) {
1136 $cart_attr['target'] = '_blank';
1137 }
1138
1139 $cart_attr['href'] = ! empty( $ext_link ) ? esc_url( $ext_link ) : esc_url( $link );
1140
1141 $this->add_attribute( 'rtsb_add_to_cart_button_' . $id . $rand, $cart_attr );
1142
1143 $content .= '<a ' . $this->get_attribute_string( 'rtsb_add_to_cart_button_' . $id . $rand ) . '>';
1144 $content .= $icon_html;
1145 $content .= '<span class="text">' . ( ! empty( $btn_txt ) ? esc_html( $btn_txt ) : esc_html__( 'Buy Product', 'shopbuilder' ) ) . '</span>';
1146 } else {
1147 $cart_attr['href'] = $cart_attr['href'] . '?add-to-cart=' . absint( $id );
1148 $cart_attr['class'] .= ' rtsb-add-to-cart-btn';
1149 $cart_attr['data-type'] = $type;
1150 $cart_attr = apply_filters( 'rtsb/elementor/render/cart_attributes', $cart_attr, $product );
1151
1152 $this->add_attribute( 'rtsb_add_to_cart_button_' . $id . $rand, $cart_attr );
1153
1154 $content .= '<a ' . $this->get_attribute_string( 'rtsb_add_to_cart_button_' . $id . $rand ) . '>';
1155 $content .= $icon_html;
1156 $content .= '<span class="text ' . ( ! empty( $success ) ? 'has-success-text' : 'no-success-text' ) . '" data-success="' . esc_html( $success ) . '">' . esc_html( $text ) . '</span>';
1157 $content .= '<span></span>';
1158 }
1159
1160 $content .= '</a>';
1161 $content .= '</div>';
1162
1163 ob_start();
1164 do_action( 'rtsb/after/cart/button' );
1165 $content .= ob_get_clean();
1166
1167 return apply_filters( 'rtsb/render/cart/button', $content, $args );
1168 }
1169
1170 /**
1171 * Button classes.
1172 *
1173 * @param array $params Button params.
1174 *
1175 * @return array
1176 */
1177 public function button_classes( $params ) {
1178 $params['classes'] = array_merge( $params['classes'], [ 'rtsb-action-btn', 'tipsy' ] );
1179
1180 return $params;
1181 }
1182
1183 /**
1184 * Render the section title.
1185 *
1186 * @param array $settings The Elementor settings.
1187 *
1188 * @return string
1189 */
1190 public function render_section_title( $settings ) {
1191 $html = '';
1192 $rand = wp_rand();
1193
1194 if ( empty( $settings['show_section_title'] ) || ( empty( $settings['query_products'] ) && 0 === count( $settings['query_products'] ) ) ) {
1195 return $html;
1196 }
1197
1198 // Set attributes for the section title.
1199 $this->add_attribute( 'rtsb_section_title_wrapper_' . $rand, 'class', 'rtsb-section-title-wrapper' );
1200 $this->add_attribute( 'rtsb_section_title_' . $rand, 'class', 'rtsb-section-title' );
1201
1202 // Start rendering.
1203 $html .= '<div ' . $this->get_attribute_string( 'rtsb_section_title_wrapper_' . $rand ) . '>';
1204 $html .= '<' . esc_attr( $settings['section_title_tag'] ) . ' ' . $this->get_attribute_string( 'rtsb_section_title_' . $rand ) . '>';
1205 $html .= esc_html( $settings['section_title_text'] );
1206 $html .= '</' . esc_attr( $settings['section_title_tag'] ) . '>';
1207 $html .= '</div>';
1208
1209 return $html;
1210 }
1211
1212 /**
1213 * Renders products pagination
1214 *
1215 * @param array $args Pagination args.
1216 * @param string $query_type Query type.
1217 *
1218 * @return string
1219 */
1220 public function render_products_pagination( $args, $query_type = 'wc' ) {
1221 list(
1222 'query' => $query,
1223 'meta' => $meta,
1224 'limit' => $limit,
1225 'per_page' => $per_page
1226 ) = $args;
1227
1228 $html_utility = null;
1229 $html = null;
1230 $ajax = false;
1231 $is_grid = preg_match( '/grid-layout/', $meta['layout'] ) || preg_match( '/list-layout/', $meta['layout'] );
1232 $posts_per_page = 'wp' === $query_type ? $query->query_vars['posts_per_page'] : null;
1233 $total_page = 'wp' === $query_type ? $query->max_num_pages : $query->get_products()->max_num_pages;
1234
1235 if ( $limit ) {
1236 if ( 'wc' === $query_type ) {
1237 $found_post = $query->get_products()->total;
1238 } elseif ( 'wp' === $query_type && ( empty( $wp_query->query['tax_query'] ) ) ) {
1239 $found_post = $query->found_posts;
1240 }
1241
1242 if ( $per_page && $found_post > $per_page ) {
1243 $found_post = $limit;
1244 $total_page = ceil( $found_post / $per_page );
1245 }
1246 }
1247
1248 $total_page = absint( $total_page );
1249
1250 $args = [
1251 'total_page' => $total_page,
1252 'posts_per_page' => 'wc' === $query_type ? $per_page : $posts_per_page,
1253 'ajax' => $ajax,
1254 ];
1255
1256 if ( 'pagination' === $meta['posts_loading_type'] ) {
1257 if ( $is_grid ) {
1258 $html_utility .= Fns::custom_pagination(
1259 $total_page,
1260 'wc' === $query_type ? $per_page : $posts_per_page
1261 );
1262 }
1263 } else {
1264 ob_start();
1265 do_action( 'rtsb/elementor/render/pagination', $meta, $query, $args );
1266 $html_utility .= ob_get_clean();
1267 }
1268
1269 if ( $html_utility ) {
1270 $rand = wp_rand();
1271
1272 // Adding pagination render attributes.
1273 $this->add_attribute(
1274 'rtsb_pagination_attr_' . $rand,
1275 [
1276 'class' => 'rtsb-pagination-wrap element-loading',
1277 'data-total-pages' => $total_page,
1278 'data-posts-per-page' => 'wc' === $query_type ? $per_page : $posts_per_page,
1279 'data-type' => $meta['posts_loading_type'],
1280 ]
1281 );
1282
1283 // Start rendering.
1284 $html = '<div ' . $this->get_attribute_string( 'rtsb_pagination_attr_' . $rand ) . '>';
1285 $html .= $html_utility;
1286 $html .= '</div><!-- .rtsb-pagination-wrap -->';
1287 }
1288
1289 return $html;
1290 }
1291
1292 /**
1293 * Renders slider buttons
1294 *
1295 * @param array $settings Settings array.
1296 *
1297 * @return string
1298 */
1299 public function render_slider_buttons( $settings ) {
1300 $html = '';
1301 $left_icon = $settings['left_arrow_icon'];
1302 $right_icon = $settings['right_arrow_icon'];
1303
1304 if ( ! empty( $settings['slider_nav'] ) ) {
1305 $html .=
1306 '<div class="swiper-nav">
1307 <div class="swiper-arrow swiper-button-next">
1308 ' . Fns::icons_manager( $right_icon ) . '
1309 </div>
1310 <div class="swiper-arrow swiper-button-prev">
1311 ' . Fns::icons_manager( $left_icon ) . '
1312 </div>
1313 </div>';
1314 }
1315
1316 $html .= ! empty( $settings['slider_dot'] ) ? '<div class="swiper-pagination rtsb-pos-s"></div>' : '';
1317
1318 return $html;
1319 }
1320
1321 /**
1322 * No products message.
1323 *
1324 * @param array $metas Meta data.
1325 *
1326 * @return void
1327 */
1328 public function no_products_msg( $metas ) {
1329 $content =
1330 '<div class="rtsb-notice woocommerce-no-products-found">
1331 <div class="woocommerce-info">' . esc_html__( 'No products were found matching your selection.', 'shopbuilder' ) . '</div>
1332 </div>';
1333
1334 $this->row( $content, $metas );
1335 }
1336
1337 /**
1338 * Start the rendering.
1339 *
1340 * @param array $metas Meta Data.
1341 * @param array $settings Widget settings.
1342 * @param array $temp_args Additional temporary arguments for rendering.
1343 *
1344 * @return string
1345 */
1346 public function render_start( $metas, $settings, $temp_args ) {
1347 ob_start();
1348
1349 /**
1350 * Before render row hook.
1351 */
1352 do_action( 'rtsb/elementor/render/before_row', $metas, $settings, $temp_args, $this );
1353
1354 return ob_get_clean();
1355 }
1356
1357 /**
1358 * End the rendering.
1359 *
1360 * @param array $metas Meta Data.
1361 * @param array $settings Widget settings.
1362 * @param array $products Array of products being rendered.
1363 *
1364 * @return string
1365 */
1366 public function render_end( $metas, $settings, $products ) {
1367 ob_start();
1368
1369 /**
1370 * After render row hook.
1371 */
1372 do_action( 'rtsb/elementor/render/after_row', $metas, $settings, $products );
1373
1374 return ob_get_clean();
1375 }
1376
1377 /**
1378 * Add render attribute.
1379 *
1380 * @param array|string $element The HTML element.
1381 * @param array|string $key Optional. Attribute key. Default is null.
1382 * @param array|string $value Optional. Attribute value. Default is null.
1383 * @param bool $overwrite Optional. Whether to overwrite existing.
1384 *
1385 * @return self
1386 */
1387 public function add_attribute( $element, $key = null, $value = null, $overwrite = false ) {
1388 if ( is_array( $element ) ) {
1389 foreach ( $element as $element_key => $attributes ) {
1390 $this->add_attribute( $element_key, $attributes, null, $overwrite );
1391 }
1392
1393 return $this;
1394 }
1395
1396 if ( is_array( $key ) ) {
1397 foreach ( $key as $attribute_key => $attributes ) {
1398 $this->add_attribute( $element, $attribute_key, $attributes, $overwrite );
1399 }
1400
1401 return $this;
1402 }
1403
1404 if ( empty( $this->attributes[ $element ][ $key ] ) ) {
1405 $this->attributes[ $element ][ $key ] = [];
1406 }
1407
1408 settype( $value, 'array' );
1409
1410 if ( $overwrite ) {
1411 $this->attributes[ $element ][ $key ] = $value;
1412 } else {
1413 $this->attributes[ $element ][ $key ] = array_merge( $this->attributes[ $element ][ $key ], $value );
1414 }
1415
1416 return $this;
1417 }
1418
1419 /**
1420 * Add link render attributes.
1421 *
1422 * @param array|string $element The HTML element.
1423 * @param array $url_control Array of link settings.
1424 * @param bool $overwrite Optional. Whether to overwrite existing
1425 * attribute. Default is false, not to overwrite.
1426 *
1427 * @return Render
1428 */
1429 public function add_link_attributes( $element, array $url_control, $overwrite = false ) {
1430 $attributes = [];
1431
1432 if ( ! empty( $url_control['url'] ) ) {
1433 $allowed_protocols = array_merge( wp_allowed_protocols(), [ 'skype', 'viber' ] );
1434
1435 $attributes['href'] = esc_url( $url_control['url'], $allowed_protocols );
1436 }
1437
1438 if ( ! empty( $url_control['is_external'] ) ) {
1439 $attributes['target'] = '_blank';
1440 }
1441
1442 if ( ! empty( $url_control['nofollow'] ) ) {
1443 $attributes['rel'] = 'nofollow';
1444 }
1445
1446 if ( ! empty( $url_control['custom_attributes'] ) ) {
1447 $attributes = array_merge( $attributes, Utils::parse_custom_attributes( $url_control['custom_attributes'] ) );
1448 }
1449
1450 if ( $attributes ) {
1451 $this->add_attribute( $element, $attributes, null, $overwrite );
1452 }
1453
1454 return $this;
1455 }
1456
1457 /**
1458 * Get render attribute string.
1459 *
1460 * @param string $element The element.
1461 *
1462 * @return string
1463 */
1464 public function get_attribute_string( $element ) {
1465 if ( empty( $this->attributes[ $element ] ) ) {
1466 return '';
1467 }
1468
1469 return Utils::render_html_attributes( $this->attributes[ $element ] );
1470 }
1471
1472 /**
1473 * Function to render link attributes.
1474 *
1475 * @param string $id Element ID.
1476 * @param array $control_name Control name.
1477 * @param string $class_name Class name.
1478 *
1479 * @return string
1480 */
1481 public function render_link_attributes( $id, $control_name, $class_name = 'logo-img-link' ) {
1482 $this->add_attribute( $id, 'class', $class_name );
1483
1484 if ( ! empty( $control_name['url'] ) ) {
1485 $this->add_link_attributes( $id, $control_name );
1486 } else {
1487 $this->add_attribute( $id, 'role', 'button' );
1488 }
1489
1490 return $this->get_attribute_string( $id );
1491 }
1492 }
1493