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