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