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

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