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

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