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