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