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

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