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