PluginProbe
ShopBuilder – WooCommerce Builder For Elementor / 2.6.5
ShopBuilder – WooCommerce Builder For Elementor v2.6.5
3.4.2 3.4.1 3.4.0 2.0.1 2.0.2 2.0.3 2.1.0 2.1.1 2.1.10 2.1.11 2.1.12 2.1.13 2.1.14 2.1.15 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 2.2.0 2.2.1 2.2.2 All 63 releases
← All changes | app/Elementor/Render/Render.php +812 -399 2.1.2 → 2.6.5 View file →
@@ -1,9 +1,9 @@
1 1 <?php
2 2 /**
3 3 * Elementor Render Class.
4 4 *
5 - * This class contains render logics & output.
5 + * This class contains render logics & output. It utilizes WC_Products_Query().
6 6 *
7 7 * @package RadiusTheme\SB
8 8 */
9 9
@@ -8,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,42 @@
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 );
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 + );
253 207
254 - $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 ) . '>';
255 209 $this->html .= '<div class="swiper-wrapper">';
256 210 }
257 211
258 212 $this->html .= $content;
@@ -259,32 +213,18 @@
259 213
260 214 if ( $is_carousel ) {
261 215 $this->html .= '</div><!-- .swiper-wrapper -->';
262 216
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 -
217 + // Slider buttons.
218 + $this->html .= $this->render_slider_buttons( $settings );
280 219 $this->html .= '</div><!-- .rtsb-carousel-slider -->';
281 220 }
282 221
283 222 $row_end = '</div><!-- .rtsb-row -->';
284 223
285 - $this->html .= apply_filters( 'rtsb/custom/layout/row/end', $row_end );
224 + $this->html .= apply_filters( 'rtsb/elementor/row/end', $row_end );
286 225
226 + // Preloader.
287 227 $this->html .= RenderHelpers::pre_loader( $layout );
288 228
289 229 return $this->html;
290 230 }
@@ -291,8 +231,61 @@
291 231
292 232 /**
293 233 * Product loop.
294 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 + *
295 288 * @param object $query The query.
296 289 * @param array $settings Settings.
297 290 * @param string $template Template.
298 291 *
@@ -297,9 +290,9 @@
297 290 * @param string $template Template.
298 291 *
299 292 * @return string|null
300 293 */
301 - public function product_loop( $query, $settings, $template ) {
294 + public function wc_product_loop( $query, $settings, $template ) {
302 295 $html = null;
303 296
304 297 if ( empty( $query ) ) {
305 298 return null;
@@ -309,11 +302,11 @@
309 302
310 303 while ( $query->have_posts() ) {
311 304 $query->the_post();
312 305 $i++;
313 -
306 + global $product;
314 307 // Loop arg.
315 - $arg = $this->arg_dataset( $settings, get_the_ID(), $settings['lazy_load'], $i );
308 + $arg = $this->arg_dataset( $settings, $product, $settings['lazy_load'], $i );
316 309
317 310 // Get template.
318 311 $html .= Fns::load_template( $template . $settings['layout'], $arg, true );
319 312 }
@@ -321,9 +314,9 @@
321 314 return $html;
322 315 }
323 316
324 317 /**
325 - * Product loop.
318 + * Category loop.
326 319 *
327 320 * @param object $query The query.
328 321 * @param array $settings Settings.
329 322 * @param string $template Template.
@@ -333,11 +326,13 @@
333 326 */
334 327 public function category_loop( $query, $settings, $template, $is_single = false ) {
335 328 $html = null;
336 329 $category_loop = false;
330 +
337 331 if ( 'category-layout3' === $settings['raw_settings']['layout'] || 'category-single-layout2' === $settings['raw_settings']['layout'] ) {
338 332 $category_loop = true;
339 333 }
334 +
340 335 if ( $is_single ) {
341 336 // Loop arg.
342 337 $arg = $this->cat_arg_dataset( $settings, $query, true, false, $category_loop );
343 338
@@ -374,42 +369,56 @@
374 369
375 370 // Query Args.
376 371 $args = ( new QueryArgs() )->buildArgs( $metas, 'product', $is_carousel );
377 372
378 - $temp_args = $args;
379 - $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 );
380 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 +
381 388 // Query.
382 - $product_query = new WP_Query( $args );
389 + $product_query = new WC_Product_Query( $args );
383 390
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();
391 + /**
392 + * Product query hook.
393 + */
394 + do_action( 'rtsb/elements/render/product_query', $product_query, $this );
389 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 +
390 402 // Row.
391 - $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 );
392 404
393 405 // Pagination.
394 406 if ( $pagination ) {
395 - $this->html .= RenderHelpers::render_pagination(
396 - $product_query,
397 - $metas,
398 - $settings['posts_limit'],
399 - $settings['pagination_per_page']
400 - );
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 );
401 415 }
402 416
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 -
417 + // End rendering.
418 + $this->html .= $this->render_end( $metas, $settings, $products );
408 419 } 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 );
420 + $this->no_products_msg( $metas );
412 421 }
413 422
414 423 wp_reset_postdata();
415 424
@@ -414,8 +423,15 @@
414 423 wp_reset_postdata();
415 424
416 425 unset( $args['tax_query'] );
417 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 +
418 434 // Container.
419 435 $this->container( $this->html, $settings, 'rtsb-products-container', $template, $is_carousel );
420 436
421 437 return $this->html;
@@ -420,9 +436,8 @@
420 436
421 437 return $this->html;
422 438 }
423 439
424 -
425 440 /**
426 441 * Render Product View.
427 442 *
428 443 * @param string $template Template name.
@@ -434,8 +449,9 @@
434 449 public function wc_loop_for_product_view( $template, $settings, $widget ) {
435 450 global $wp_query, $post;
436 451
437 452 $this->get_settings = $settings;
453 + $rand = wp_rand();
438 454 $metas = RenderHelpers::meta_dataset( $settings, $template, $widget->get_settings_for_display() );
439 455
440 456 $settings['rtsb_order'] = ! empty( $wp_query->query_vars['order'] ) ? $wp_query->query_vars['order'] : 'ASC';
441 457 $settings['rtsb_orderby'] = ! empty( $wp_query->query_vars['orderby'] ) ? $wp_query->query_vars['orderby'] : 'menu_order';
@@ -449,9 +465,9 @@
449 465 if ( $is_preview ) {
450 466 $main_query = clone $wp_query;
451 467 $main_post = clone $post;
452 468
453 - $ordering = ( new \WC_Query() )->get_catalog_ordering_args();
469 + $ordering = ( new WC_Query() )->get_catalog_ordering_args();
454 470 $wp_query_args = [
455 471 'post_type' => 'product',
456 472 'posts_per_page' => $posts_per_page,
457 473 ];
@@ -464,21 +480,23 @@
464 480 $wp_query_args['order'] = $ordering['order'];
465 481 }
466 482
467 483 $wp_query = new WP_Query( $wp_query_args ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
484 +
468 485 wc_set_loop_prop( 'total', $wp_query->found_posts );
469 486 wc_set_loop_prop( 'total_pages', $wp_query->max_num_pages );
470 487 }
471 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 +
472 493 // column_per_row.
473 494 if ( woocommerce_product_loop() ) {
474 495 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 496
479 497 // Row.
480 - $this->row( $this->product_loop( $wp_query, $metas, $template ), $metas );
498 + $this->row( $this->wc_product_loop( $wp_query, $metas, $template ), $metas, '', $settings );
481 499 }
482 500
483 501 // Pagination.
484 502 $this->html .= '<div class="rtsb-archive-pagination-wrap">';
@@ -483,40 +501,51 @@
483 501 // Pagination.
484 502 $this->html .= '<div class="rtsb-archive-pagination-wrap">';
485 503
486 504 if ( $pagination ) {
487 - 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', '' ) ) ) {
488 506 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 - ";
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>";
495 522 }
496 523 } else {
497 - $this->html .= RenderHelpers::render_pagination(
498 - $wp_query,
499 - $metas,
500 - wc_get_loop_prop( 'total' ),
501 - $posts_per_page
502 - );
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' );
503 532 }
504 533 }
505 534
506 535 $this->html .= '</div>';
507 536 } 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 );
537 + $this->no_products_msg( $metas );
511 538 }
512 539
513 540 if ( $is_preview ) {
541 + // phpcs:disable
514 542 $wp_query = $main_query;
515 543 $post = $main_post;
516 544
517 545 wp_reset_query();
518 546 wp_reset_postdata();
547 + // phpcs:enable
519 548 }
520 549
521 550 // Container.
522 551 $this->container( $this->html, $settings, 'rtsb-products-container', $template );
@@ -524,12 +553,13 @@
524 553 return $this->html;
525 554 }
526 555
527 556 /**
528 - * Render Product View.
557 + * Loop Setup for Product View.
529 558 *
530 559 * @param string $template Template name.
531 560 * @param array $settings Control settings.
561 + * @param object $widget Widget object.
532 562 *
533 563 * @return string
534 564 */
535 565 public function setup_loop_for_product_view( $template, $settings, $widget ) {
@@ -546,13 +576,13 @@
546 576
547 577 setup_postdata( $GLOBALS['post'] =& $post_object ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited, Squiz.PHP.DisallowMultipleAssignments.Found
548 578
549 579 // Loop arg.
550 - $arg = $this->arg_dataset( $metas, $_product->get_id() );
580 + $arg = $this->arg_dataset( $metas, $_product );
551 581 // Get template.
552 582 $html .= Fns::load_template( $template . $settings['layout'], $arg, true );
553 583
554 - $this->row( $html, $metas );
584 + $this->row( $html, $metas, '', $settings );
555 585 }
556 586
557 587 // Container.
558 588 $this->container( $this->html, $settings, 'rtsb-products-container', '', false, false );
@@ -580,8 +610,11 @@
580 610
581 611 if ( rtsb()->has_pro() && ( ! empty( $metas['tag_term'] ) ) ) {
582 612 $term = $metas['tag_term'];
583 613 }
614 + if ( rtsb()->has_pro() && ( ! empty( $metas['brand_term'] ) ) ) {
615 + $term = $metas['brand_term'];
616 + }
584 617
585 618 // Query.
586 619 if ( $is_single ) {
587 620 if ( empty( $term ) ) {
@@ -648,8 +681,34 @@
648 681
649 682 /**
650 683 * Builds an array with meta values.
651 684 *
685 + * @param array $meta Meta values.
686 + * @param object $product Product object.
687 + * @param bool $lazy_load Lazy Load.
688 + * @param int $i Integer.
689 + *
690 + * @return array
691 + */
692 + public function arg_dataset( array $meta, object $product, bool $lazy_load = false, $i = 0 ) {
693 + $post_id = $product->get_id();
694 + $arg = $this->build_product_arg(
695 + [
696 + 'product' => $product,
697 + 'id' => $product->get_id(),
698 + 'type' => $product->get_type(),
699 + 'meta' => $meta,
700 + 'lazy_load' => $lazy_load,
701 + 'i' => $i,
702 + ]
703 + );
704 +
705 + return apply_filters( 'rtsb/elementor/render/arg_dataset', $arg, $post_id, $meta, $lazy_load );
706 + }
707 +
708 + /**
709 + * Builds an array with meta values for WP_Query.
710 + *
652 711 * @param array $meta Meta values.
653 712 * @param int $post_id Post ID.
654 713 * @param bool $lazy_load Lazy Load.
655 714 * @param int $i Integer.
@@ -655,22 +714,183 @@
655 714 * @param int $i Integer.
656 715 *
657 716 * @return array
658 717 */
659 - public function arg_dataset( array $meta, int $post_id, bool $lazy_load = false, $i = 0 ) {
718 + public function wp_arg_dataset( array $meta, int $post_id, bool $lazy_load = false, $i = 0 ) {
660 719 if ( ( empty( $meta ) && ! $post_id ) || ! in_array( get_post_type( $post_id ), [ 'product_variation', 'product' ] ) ) {
661 720 return [];
662 721 }
663 722
664 - global $post, $product;
723 + global $product;
665 724
666 725 if ( ! $product instanceof WC_Product && $post_id ) {
667 726 $product = wc_get_product( $post_id );
668 727 }
669 728
670 - $arg = [];
671 - $p_type = $product->get_type();
729 + $post_id = $product->get_id();
730 + $arg = $this->build_product_arg(
731 + [
732 + 'product' => $product,
733 + 'id' => $post_id,
734 + 'type' => $product->get_type(),
735 + 'meta' => $meta,
736 + 'lazy_load' => $lazy_load,
737 + 'i' => $i,
738 + ]
739 + );
672 740
741 + return apply_filters( 'rtsb/elementor/render/arg_dataset', $arg, $post_id, $meta, $lazy_load );
742 + }
743 +
744 + /**
745 + * Builds an array with meta values.
746 + *
747 + * @param array $meta Meta values.
748 + * @param Object $query Category object.
749 + * @param bool $fullwidth Fullwidth check.
750 + * @param bool $lazy_load Lazy Load.
751 + * @param bool $category_loop Category loop.
752 + *
753 + * @return array
754 + */
755 + public function cat_arg_dataset( $meta, $query = null, $fullwidth = false, $lazy_load = false, $category_loop = false ) {
756 + if ( empty( $meta ) ) {
757 + return [];
758 + }
759 +
760 + $arg = [];
761 +
762 + $products = null;
763 + $arg['class'] = null;
764 + $arg['grid'] = null;
765 + $arg['anchor_class'] = null;
766 + $arg['p_sale'] = null;
767 + $arg['count'] = max( $query->count, 0 );
768 + $arg['count_position'] = $meta['count_position'];
769 + $arg['excerpt_position'] = $meta['cat_excerpt_position'];
770 + $arg['cat_link'] = get_term_link( $query );
771 + $arg['target'] = '_self';
772 + $arg['raw_settings'] = ! empty( $meta['raw_settings'] ) ? $meta['raw_settings'] : [];
773 +
774 + if ( ! empty( $meta['tag_term'] ) ) {
775 + $arg['tag_term'] = $meta['tag_term'];
776 + }
777 +
778 + if ( ! empty( $meta['custom_link']['url'] ) ) {
779 + $arg['cat_link'] = $meta['custom_link']['url'];
780 + $arg['target'] = $meta['custom_link']['is_external'] ? '_blank' : '_self';
781 + }
782 +
783 + $title = $query->name;
784 + $excerpt = $query->description;
785 +
786 + if ( in_array( 'badges', $meta['visibility'], true ) ) {
787 + $arg['p_sale'] = $meta['custom_badge_text'];
788 + }
789 +
790 + $arg['badge_class'] = RenderHelpers::badge_class( $meta['badge_preset'] );
791 +
792 + if ( $arg['count'] >= 0 ) {
793 + $prefix = ! empty( $meta['before_count'] ) ? $meta['before_count'] : '';
794 + $suffix = ! empty( $meta['after_count'] ) ? $meta['after_count'] : '';
795 +
796 + $arg['count'] = $prefix . $arg['count'] . $suffix;
797 + }
798 +
799 + if ( $meta['show_custom_title'] && ! empty( $meta['cat_custom_title'] ) ) {
800 + $title = $meta['cat_custom_title'];
801 + }
802 +
803 + if ( $meta['show_custom_excerpt'] && ! empty( $meta['cat_custom_excerpt'] ) ) {
804 + $excerpt = $meta['cat_custom_excerpt'];
805 + }
806 +
807 + $arg['c_img'] = $meta['c_img'];
808 + $arg['items'] = $meta['visibility'];
809 + $arg['title_tag'] = $meta['title_tag'];
810 + $arg['title_class'] = 'category-title rtsb-text-limit limit-' . $meta['title_limit'] . ( ! $meta['title_link'] ? ' no-link' : '' );
811 + $arg['title'] = Fns::text_truncation( $title, $meta['title_limit_custom'] );
812 + $arg['excerpt_limit'] = $meta['excerpt_limit'];
813 + $arg['excerpt'] = wpautop( Fns::text_truncation( $excerpt, $meta['excerpt_limit_custom'] ) );
814 + $arg['title_link'] = $meta['title_link'];
815 + $arg['image_link'] = $meta['image_link'];
816 + $arg['grid'] .= 'rtsb-col-grid ';
817 +
818 + if ( ! $fullwidth ) {
819 + if ( 'masonry' === $meta['grid_type'] ) {
820 + $arg['class'] .= 'masonry-grid-item ';
821 + } else {
822 + $arg['class'] .= 'even-grid-item ';
823 + }
824 + }
825 +
826 + if ( ! empty( $meta['active_cat_slider'] ) ) {
827 + $arg['class'] .= ' rtsb-slide-item swiper-slide animated rtFadeIn ';
828 + }
829 +
830 + $arg['class'] .= 'rtsb-category-grid';
831 +
832 + ob_start();
833 + do_action( 'rtsb/categories/category_image_override', $meta, $query->term_id );
834 + $override_category_image = ob_get_clean();
835 +
836 + $arg['img_html'] = $meta['c_img'] ? Fns::get_product_image_html(
837 + 'category',
838 + $query->term_id,
839 + $meta['f_img_size'],
840 + $meta['default_img_id'],
841 + $meta['custom_img_size'],
842 + $lazy_load,
843 + false,
844 + false,
845 + $override_category_image
846 + ) : null;
847 +
848 + if ( $meta['show_custom_image'] && ! empty( $meta['custom_image'] ) ) {
849 + $arg['img_html'] = Fns::get_product_image_html(
850 + 'custom',
851 + null,
852 + $meta['f_img_size'],
853 + $meta['custom_image'],
854 + $meta['custom_img_size'],
855 + $lazy_load
856 + );
857 + }
858 +
859 + $arg['excerpt_class'] = 'category-info' . ( 'block' === $meta['count_position'] ? ' block-count' : ' inline-count' ) . ( 'above' === $meta['cat_excerpt_position'] ? ' excerpt-above' : '' ) . ( empty( $arg['excerpt'] ) ? ' no-excerpt' : '' );
860 +
861 + if ( $category_loop ) {
862 + $query_args = [
863 + 'product_category_id' => $query->term_id,
864 + 'limit' => -1,
865 + 'order' => 'DESC',
866 + 'orderby' => 'date',
867 + ];
868 +
869 + $products_query = new WC_Product_Query( $query_args );
870 + $products = $products_query->get_products();
871 + }
872 +
873 + return apply_filters( 'rtsb/elementor/render/cat_arg_dataset', $arg, $meta, $query, $fullwidth, $lazy_load, $products );
874 + }
875 +
876 + /**
877 + * Building product args.
878 + *
879 + * @param array $args Args.
880 + * @return array
881 + */
882 + public function build_product_arg( $args ) {
883 + list(
884 + 'product' => $product,
885 + 'id' => $post_id,
886 + 'type' => $p_type,
887 + 'meta' => $meta,
888 + 'lazy_load' => $lazy_load,
889 + 'i' => $i,
890 + ) = $args;
891 +
892 + $arg = [];
673 893 $arg['class'] = null;
674 894 $arg['grid'] = null;
675 895 $arg['anchor_class'] = null;
676 896 $arg['hover_img_html'] = null;
@@ -675,26 +895,28 @@
675 895 $arg['anchor_class'] = null;
676 896 $arg['hover_img_html'] = null;
677 897 $arg['s_link'] = [];
678 898 $arg['add_to_cart'] = '';
899 + $arg['p_counter'] = absint( $i );
679 900 $arg['p_id'] = $post_id;
680 - $arg['p_link'] = get_permalink();
901 + $arg['product'] = $product;
902 + $arg['p_link'] = $product->get_permalink();
681 903 $arg['p_sale'] = '';
682 904 $arg['rating_args'] = [];
683 905 $arg['has_attributes'] = $product->is_type( 'variable' ) && $product->get_attributes();
684 906 $arg['raw_settings'] = ! empty( $meta['raw_settings'] ) ? $meta['raw_settings'] : [];
685 907 $cart_icon_html = '';
908 + $layout = $meta['layout'];
686 909
687 - $layout = $meta['layout'];
688 -
910 + // Building the arg.
689 911 $arg['target'] = '_self';
690 912 $arg['f_img'] = $meta['f_img'];
691 913 $arg['items'] = $meta['visibility'];
692 914 $arg['title_tag'] = $meta['title_tag'];
693 915 $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'] );
916 + $arg['title'] = Fns::text_truncation( $product->get_title(), $meta['title_limit_custom'] );
695 917 $arg['excerpt_limit'] = $meta['excerpt_limit'];
696 - $arg['excerpt'] = wpautop( Fns::text_truncation( do_shortcode( $post->post_excerpt ), $meta['excerpt_limit_custom'] ) );
918 + $arg['excerpt'] = wpautop( Fns::text_truncation( do_shortcode( get_post_field( 'post_excerpt', $post_id ) ), $meta['excerpt_limit_custom'] ) );
697 919 $arg['title_link'] = $meta['title_link'];
698 920 $arg['image_link'] = $meta['image_link'];
699 921 $arg['out_of_stock'] = $meta['out_of_stock_badge_text'];
700 922 $arg['action_btn_preset'] = $meta['btn_preset'];
@@ -706,10 +928,15 @@
706 928 $is_list = preg_match( '/list/', $layout );
707 929 $is_carousel = preg_match( '/slider/', $layout );
708 930
709 931 if ( ! $is_carousel ) {
710 - $arg['grid'] .= 'rtsb-col-grid ';
711 - $arg['class'] .= ' even-grid-item ';
932 + $arg['grid'] .= 'rtsb-col-grid ';
933 +
934 + if ( 'masonry' === $meta['grid_type'] ) {
935 + $arg['class'] .= 'masonry-grid-item ';
936 + } else {
937 + $arg['class'] .= 'even-grid-item ';
938 + }
712 939 } else {
713 940 $arg['grid'] .= 'rtsb-col-grid rtsb-col-full rtsb-slide-item swiper-slide ';
714 941 }
715 942
@@ -729,17 +956,21 @@
729 956
730 957 $arg['class'] .= ' animated rtFadeIn';
731 958
732 959 if ( in_array( 'badges', $meta['visibility'], true ) ) {
733 - $arg['p_sale'] = Fns::get_sale_badge(
960 + $badge_module = ! empty( $meta['enable_badges_module'] );
961 + $arg['p_sale'] = Fns::is_module_active( 'product_badges' ) && $badge_module ? 'rtsb_yes' : Fns::get_promo_badge(
962 + $product,
734 963 $meta['sale_badge_type'],
735 964 $meta['sale_badge_text'],
736 - $meta['out_of_stock_badge_text']
965 + apply_filters( 'rtsb/elementor/render/out_of_stock_badge_text', $meta['out_of_stock_badge_text'] )
737 966 );
738 967 }
739 968
740 969 $arg['badge_class'] = RenderHelpers::badge_class( $meta['badge_preset'] );
741 970
971 + $meta['add_to_cart_text'] = apply_filters( 'rtsb/elementor/render/add_to_cart_text', $meta['add_to_cart_text'], $product );
972 +
742 973 if ( ! $meta['show_cart_text'] ) {
743 974 $meta['add_to_cart_text'] = '';
744 975 $meta['add_to_cart_success_text'] = '';
745 976 }
@@ -752,20 +983,23 @@
752 983 if ( ! empty( $meta['add_to_cart_success_icon'] ) ) {
753 984 $cart_icon_html .= Fns::icons_manager( $meta['add_to_cart_success_icon'], 'cart-success-icon' );
754 985 }
755 986
756 - $cart_icon = '<span class="icon">' . $cart_icon_html . '</span>';
987 + $cart_icon = ! empty( $cart_icon_html ) ? '<span class="icon">' . $cart_icon_html . '</span>' : '';
757 988
758 989 if ( $product->is_purchasable() || 'grouped' === $p_type || 'external' === $p_type ) {
759 990 if ( $product->is_in_stock() ) {
760 991 $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']
992 + [
993 + 'link' => $product->get_permalink(),
994 + 'id' => $post_id,
995 + 'type' => $p_type,
996 + 'text' => $meta['add_to_cart_text'],
997 + 'success' => $meta['add_to_cart_success_text'],
998 + 'icon_html' => $cart_icon,
999 + 'alignment' => $meta['add_to_cart_alignment'],
1000 + 'product' => $product,
1001 + ]
768 1002 );
769 1003 }
770 1004 }
771 1005 }
@@ -831,249 +1065,428 @@
831 1065 'hover_img_html' => $arg['hover_img_html'],
832 1066 'meta' => $meta,
833 1067 ];
834 1068
835 - return apply_filters( 'rtsb/elementor/render/arg_dataset', $arg, $post_id, $meta, $lazy_load );
1069 + return $arg;
836 1070 }
837 1071
838 1072 /**
839 - * Builds an array with meta values.
1073 + * Renders add to cart button.
840 1074 *
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.
1075 + * @param array $args Cart args.
845 1076 *
846 - * @return array
1077 + * @return string
847 1078 */
848 - public function cat_arg_dataset( $meta, $query = null, $fullwidth = false, $lazy_load = false, $category_loop = false ) {
849 - if ( empty( $meta ) ) {
850 - return [];
1079 + public function render_add_to_cart_button( $args ) {
1080 + list(
1081 + 'link' => $link,
1082 + 'id' => $id,
1083 + 'type' => $type,
1084 + 'text' => $text,
1085 + 'success' => $success,
1086 + 'icon_html' => $icon_html,
1087 + 'alignment' => $alignment,
1088 + 'product' => $product
1089 + ) = $args;
1090 +
1091 + $content = null;
1092 + $rand = wp_rand();
1093 + $ext_link = get_post_meta( $id, '_product_url', true );
1094 + $btn_txt = get_post_meta( $id, '_button_text', true );
1095 +
1096 + ob_start();
1097 + do_action( 'rtsb/before/cart/button' );
1098 + $content .= ob_get_clean();
1099 +
1100 + $cart_attr = [
1101 + 'class' => 'rtsb-action-btn ' . $type . '-product tipsy icon-' . $alignment . ' ' . ( empty( $text ) ? 'no-text' : 'has-text' ),
1102 + 'href' => esc_url( $link ),
1103 + 'rel' => 'nofollow',
1104 + 'data-quantity' => '1',
1105 + 'data-product_id' => absint( $id ),
1106 + 'data-id' => absint( $id ),
1107 + ];
1108 +
1109 + if ( empty( $text ) ) {
1110 + $tooltip_mapping = [
1111 + 'simple' => esc_attr__( 'Add to Cart', 'shopbuilder' ),
1112 + 'variable' => esc_attr__( 'Select Options', 'shopbuilder' ),
1113 + 'grouped' => esc_attr__( 'View Products', 'shopbuilder' ),
1114 + 'external' => ! empty( $btn_txt ) ? esc_html( $btn_txt ) : esc_html__( 'Buy Product', 'shopbuilder' ),
1115 + 'rtsb-gift-card' => esc_attr__( 'Select Gift Card', 'shopbuilder' ),
1116 + ];
1117 +
1118 + $cart_attr['title'] = $tooltip_mapping[ $type ];
851 1119 }
852 1120
853 - $arg = [];
1121 + $content .= '<div class="rtsb-wc-add-to-cart-wrap">';
854 1122
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'] : [];
1123 + if ( 'variable' === $type || 'grouped' === $type ) {
1124 + $cart_attr = apply_filters( 'rtsb/elementor/render/cart_attributes', $cart_attr, $product );
866 1125
867 - if ( ! empty( $meta['tag_term'] ) ) {
868 - $arg['tag_term'] = $meta['tag_term'];
1126 + $this->add_attribute( 'rtsb_add_to_cart_button_' . $id . $rand, $cart_attr );
1127 +
1128 + $text_btn = 'grouped' === $type ? __( 'View Products', 'shopbuilder' ) : __( 'Select Options', 'shopbuilder' );
1129 +
1130 + $content .= '<a ' . $this->get_attribute_string( 'rtsb_add_to_cart_button_' . $id . $rand ) . '>';
1131 + $content .= $icon_html;
1132 + $content .= '<span class="text ' . ( ! empty( $success ) ? 'has-success-text' : 'no-success-text' ) . '" data-success="' . esc_html( $success ) . '" data-cart-text="' . ( ! empty( $text ) ? esc_attr( $text ) : $tooltip_mapping['simple'] ) . '" data-variable-text="' . esc_attr( $text_btn ) . '">' . esc_html( $text_btn ) . '</span>';
1133 + $content .= '<span></span>';
1134 + } elseif ( 'external' === $type ) {
1135 + if ( ! empty( $ext_link ) ) {
1136 + $cart_attr['target'] = '_blank';
1137 + }
1138 +
1139 + $cart_attr['href'] = ! empty( $ext_link ) ? esc_url( $ext_link ) : esc_url( $link );
1140 +
1141 + $this->add_attribute( 'rtsb_add_to_cart_button_' . $id . $rand, $cart_attr );
1142 +
1143 + $content .= '<a ' . $this->get_attribute_string( 'rtsb_add_to_cart_button_' . $id . $rand ) . '>';
1144 + $content .= $icon_html;
1145 + $content .= '<span class="text">' . ( ! empty( $btn_txt ) ? esc_html( $btn_txt ) : esc_html__( 'Buy Product', 'shopbuilder' ) ) . '</span>';
1146 + } else {
1147 + $cart_attr['href'] = $cart_attr['href'] . '?add-to-cart=' . absint( $id );
1148 + $cart_attr['class'] .= ' rtsb-add-to-cart-btn';
1149 + $cart_attr['data-type'] = $type;
1150 + $cart_attr = apply_filters( 'rtsb/elementor/render/cart_attributes', $cart_attr, $product );
1151 +
1152 + $this->add_attribute( 'rtsb_add_to_cart_button_' . $id . $rand, $cart_attr );
1153 +
1154 + $content .= '<a ' . $this->get_attribute_string( 'rtsb_add_to_cart_button_' . $id . $rand ) . '>';
1155 + $content .= $icon_html;
1156 + $content .= '<span class="text ' . ( ! empty( $success ) ? 'has-success-text' : 'no-success-text' ) . '" data-success="' . esc_html( $success ) . '">' . esc_html( $text ) . '</span>';
1157 + $content .= '<span></span>';
869 1158 }
870 1159
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';
1160 + $content .= '</a>';
1161 + $content .= '</div>';
1162 +
1163 + ob_start();
1164 + do_action( 'rtsb/after/cart/button' );
1165 + $content .= ob_get_clean();
1166 +
1167 + return apply_filters( 'rtsb/render/cart/button', $content, $args );
1168 + }
1169 +
1170 + /**
1171 + * Button classes.
1172 + *
1173 + * @param array $params Button params.
1174 + *
1175 + * @return array
1176 + */
1177 + public function button_classes( $params ) {
1178 + $params['classes'] = array_merge( $params['classes'], [ 'rtsb-action-btn', 'tipsy' ] );
1179 +
1180 + return $params;
1181 + }
1182 +
1183 + /**
1184 + * Render the section title.
1185 + *
1186 + * @param array $settings The Elementor settings.
1187 + *
1188 + * @return string
1189 + */
1190 + public function render_section_title( $settings ) {
1191 + $html = '';
1192 + $rand = wp_rand();
1193 +
1194 + if ( empty( $settings['show_section_title'] ) || ( empty( $settings['query_products'] ) && 0 === count( $settings['query_products'] ) ) ) {
1195 + return $html;
874 1196 }
875 1197
876 - $title = $query->name;
877 - $excerpt = $query->description;
1198 + // Set attributes for the section title.
1199 + $this->add_attribute( 'rtsb_section_title_wrapper_' . $rand, 'class', 'rtsb-section-title-wrapper' );
1200 + $this->add_attribute( 'rtsb_section_title_' . $rand, 'class', 'rtsb-section-title' );
878 1201
879 - if ( in_array( 'badges', $meta['visibility'], true ) ) {
880 - $arg['p_sale'] = $meta['custom_badge_text'];
881 - }
1202 + // Start rendering.
1203 + $html .= '<div ' . $this->get_attribute_string( 'rtsb_section_title_wrapper_' . $rand ) . '>';
1204 + $html .= '<' . esc_attr( $settings['section_title_tag'] ) . ' ' . $this->get_attribute_string( 'rtsb_section_title_' . $rand ) . '>';
1205 + $html .= esc_html( $settings['section_title_text'] );
1206 + $html .= '</' . esc_attr( $settings['section_title_tag'] ) . '>';
1207 + $html .= '</div>';
882 1208
883 - $arg['badge_class'] = RenderHelpers::badge_class( $meta['badge_preset'] );
1209 + return $html;
1210 + }
884 1211
885 - if ( $arg['count'] >= 0 ) {
886 - $prefix = ! empty( $meta['before_count'] ) ? $meta['before_count'] : '';
887 - $suffix = ! empty( $meta['after_count'] ) ? $meta['after_count'] : '';
1212 + /**
1213 + * Renders products pagination
1214 + *
1215 + * @param array $args Pagination args.
1216 + * @param string $query_type Query type.
1217 + *
1218 + * @return string
1219 + */
1220 + public function render_products_pagination( $args, $query_type = 'wc' ) {
1221 + list(
1222 + 'query' => $query,
1223 + 'meta' => $meta,
1224 + 'limit' => $limit,
1225 + 'per_page' => $per_page
1226 + ) = $args;
888 1227
889 - $arg['count'] = $prefix . $arg['count'] . $suffix;
890 - }
1228 + $html_utility = null;
1229 + $html = null;
1230 + $ajax = false;
1231 + $is_grid = preg_match( '/grid-layout/', $meta['layout'] ) || preg_match( '/list-layout/', $meta['layout'] );
1232 + $posts_per_page = 'wp' === $query_type ? $query->query_vars['posts_per_page'] : null;
1233 + $total_page = 'wp' === $query_type ? $query->max_num_pages : $query->get_products()->max_num_pages;
891 1234
892 - if ( $meta['show_custom_title'] && ! empty( $meta['cat_custom_title'] ) ) {
893 - $title = $meta['cat_custom_title'];
894 - }
1235 + if ( $limit ) {
1236 + if ( 'wc' === $query_type ) {
1237 + $found_post = $query->get_products()->total;
1238 + } elseif ( 'wp' === $query_type && ( empty( $wp_query->query['tax_query'] ) ) ) {
1239 + $found_post = $query->found_posts;
1240 + }
895 1241
896 - if ( $meta['show_custom_excerpt'] && ! empty( $meta['cat_custom_excerpt'] ) ) {
897 - $excerpt = $meta['cat_custom_excerpt'];
1242 + if ( $per_page && $found_post > $per_page ) {
1243 + $found_post = $limit;
1244 + $total_page = ceil( $found_post / $per_page );
1245 + }
898 1246 }
899 1247
900 - $layout = $meta['layout'];
1248 + $total_page = absint( $total_page );
901 1249
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'];
1250 + $args = [
1251 + 'total_page' => $total_page,
1252 + 'posts_per_page' => 'wc' === $query_type ? $per_page : $posts_per_page,
1253 + 'ajax' => $ajax,
1254 + ];
911 1255
912 - if ( ! $fullwidth ) {
913 - $arg['grid'] .= 'rtsb-col-grid ';
914 - $arg['class'] .= ' even-grid-item ';
1256 + if ( 'pagination' === $meta['posts_loading_type'] ) {
1257 + if ( $is_grid ) {
1258 + $html_utility .= Fns::custom_pagination(
1259 + $total_page,
1260 + 'wc' === $query_type ? $per_page : $posts_per_page
1261 + );
1262 + }
915 1263 } else {
916 - $arg['grid'] .= 'rtsb-col-grid ';
1264 + ob_start();
1265 + do_action( 'rtsb/elementor/render/pagination', $meta, $query, $args );
1266 + $html_utility .= ob_get_clean();
917 1267 }
918 1268
919 - $arg['class'] .= 'rtsb-category-grid';
1269 + if ( $html_utility ) {
1270 + $rand = wp_rand();
920 1271
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;
1272 + // Adding pagination render attributes.
1273 + $this->add_attribute(
1274 + 'rtsb_pagination_attr_' . $rand,
1275 + [
1276 + 'class' => 'rtsb-pagination-wrap element-loading',
1277 + 'data-total-pages' => $total_page,
1278 + 'data-posts-per-page' => 'wc' === $query_type ? $per_page : $posts_per_page,
1279 + 'data-type' => $meta['posts_loading_type'],
1280 + ]
1281 + );
929 1282
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 - );
1283 + // Start rendering.
1284 + $html = '<div ' . $this->get_attribute_string( 'rtsb_pagination_attr_' . $rand ) . '>';
1285 + $html .= $html_utility;
1286 + $html .= '</div><!-- .rtsb-pagination-wrap -->';
939 1287 }
940 1288
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' : '' );
1289 + return $html;
1290 + }
942 1291
943 - if ( $category_loop ) {
944 - $query_args = [
945 - 'product_category_id' => $query->term_id,
946 - 'limit' => -1,
947 - 'order' => 'DESC',
948 - 'orderby' => 'date',
949 - ];
1292 + /**
1293 + * Renders slider buttons
1294 + *
1295 + * @param array $settings Settings array.
1296 + *
1297 + * @return string
1298 + */
1299 + public function render_slider_buttons( $settings ) {
1300 + $html = '';
1301 + $left_icon = $settings['left_arrow_icon'];
1302 + $right_icon = $settings['right_arrow_icon'];
950 1303
951 - $products_query = new \WC_Product_Query( $query_args );
952 - $products = $products_query->get_products();
1304 + if ( ! empty( $settings['slider_nav'] ) ) {
1305 + $html .=
1306 + '<div class="swiper-nav">
1307 + <div class="swiper-arrow swiper-button-next">
1308 + ' . Fns::icons_manager( $right_icon ) . '
1309 + </div>
1310 + <div class="swiper-arrow swiper-button-prev">
1311 + ' . Fns::icons_manager( $left_icon ) . '
1312 + </div>
1313 + </div>';
953 1314 }
954 1315
955 - return apply_filters( 'rtsb/elementor/render/cat_arg_dataset', $arg, $meta, $query, $fullwidth, $lazy_load, $products );
1316 + $html .= ! empty( $settings['slider_dot'] ) ? '<div class="swiper-pagination rtsb-pos-s"></div>' : '';
1317 +
1318 + return $html;
956 1319 }
957 1320
958 1321 /**
959 - * Renders add to cart button.
1322 + * No products message.
960 1323 *
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.
1324 + * @param array $metas Meta data.
968 1325 *
1326 + * @return void
1327 + */
1328 + public function no_products_msg( $metas ) {
1329 + $content =
1330 + '<div class="rtsb-notice woocommerce-no-products-found">
1331 + <div class="woocommerce-info">' . esc_html__( 'No products were found matching your selection.', 'shopbuilder' ) . '</div>
1332 + </div>';
1333 +
1334 + $this->row( $content, $metas );
1335 + }
1336 +
1337 + /**
1338 + * Start the rendering.
1339 + *
1340 + * @param array $metas Meta Data.
1341 + * @param array $settings Widget settings.
1342 + * @param array $temp_args Additional temporary arguments for rendering.
1343 + *
969 1344 * @return string
970 1345 */
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 );
1346 + public function render_start( $metas, $settings, $temp_args ) {
1347 + ob_start();
976 1348
977 - if ( empty( $text ) ) {
978 - $tooltip_attr = ' title="' . esc_attr__( 'Add to Cart', 'shopbuilder' ) . '"';
1349 + /**
1350 + * Before render row hook.
1351 + */
1352 + do_action( 'rtsb/elementor/render/before_row', $metas, $settings, $temp_args, $this );
979 1353
980 - if ( 'variable' === $type ) {
981 - $tooltip_attr = ' title="' . esc_attr__( 'Select Options', 'shopbuilder' ) . '"';
1354 + return ob_get_clean();
1355 + }
1356 +
1357 + /**
1358 + * End the rendering.
1359 + *
1360 + * @param array $metas Meta Data.
1361 + * @param array $settings Widget settings.
1362 + * @param array $products Array of products being rendered.
1363 + *
1364 + * @return string
1365 + */
1366 + public function render_end( $metas, $settings, $products ) {
1367 + ob_start();
1368 +
1369 + /**
1370 + * After render row hook.
1371 + */
1372 + do_action( 'rtsb/elementor/render/after_row', $metas, $settings, $products );
1373 +
1374 + return ob_get_clean();
1375 + }
1376 +
1377 + /**
1378 + * Add render attribute.
1379 + *
1380 + * @param array|string $element The HTML element.
1381 + * @param array|string $key Optional. Attribute key. Default is null.
1382 + * @param array|string $value Optional. Attribute value. Default is null.
1383 + * @param bool $overwrite Optional. Whether to overwrite existing.
1384 + *
1385 + * @return self
1386 + */
1387 + public function add_attribute( $element, $key = null, $value = null, $overwrite = false ) {
1388 + if ( is_array( $element ) ) {
1389 + foreach ( $element as $element_key => $attributes ) {
1390 + $this->add_attribute( $element_key, $attributes, null, $overwrite );
982 1391 }
983 1392
984 - if ( 'grouped' === $type ) {
985 - $tooltip_attr = ' title="' . esc_attr__( 'View Products', 'shopbuilder' ) . '"';
1393 + return $this;
1394 + }
1395 +
1396 + if ( is_array( $key ) ) {
1397 + foreach ( $key as $attribute_key => $attributes ) {
1398 + $this->add_attribute( $element, $attribute_key, $attributes, $overwrite );
986 1399 }
987 1400
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 - }
1401 + return $this;
992 1402 }
993 1403
994 - ob_start();
995 - do_action( 'rtsb/before/cart/button' );
996 - $content .= ob_get_clean();
1404 + if ( empty( $this->attributes[ $element ][ $key ] ) ) {
1405 + $this->attributes[ $element ][ $key ] = [];
1406 + }
997 1407
998 - if ( 'variable' === $type ) {
999 - $data_cart_text = ! empty( $text ) ? esc_attr( $text ) : __( 'Add to Cart', 'shopbuilder' );
1000 - $variable_text = __( 'Select Options', 'shopbuilder' );
1408 + settype( $value, 'array' );
1001 1409
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 - );
1410 + if ( $overwrite ) {
1411 + $this->attributes[ $element ][ $key ] = $value;
1044 1412 } 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 - );
1413 + $this->attributes[ $element ][ $key ] = array_merge( $this->attributes[ $element ][ $key ], $value );
1058 1414 }
1059 1415
1060 - ob_start();
1061 - do_action( 'rtsb/after/cart/button' );
1062 - $content .= ob_get_clean();
1416 + return $this;
1417 + }
1063 1418
1064 - return $content;
1419 + /**
1420 + * Add link render attributes.
1421 + *
1422 + * @param array|string $element The HTML element.
1423 + * @param array $url_control Array of link settings.
1424 + * @param bool $overwrite Optional. Whether to overwrite existing
1425 + * attribute. Default is false, not to overwrite.
1426 + *
1427 + * @return Render
1428 + */
1429 + public function add_link_attributes( $element, array $url_control, $overwrite = false ) {
1430 + $attributes = [];
1431 +
1432 + if ( ! empty( $url_control['url'] ) ) {
1433 + $allowed_protocols = array_merge( wp_allowed_protocols(), [ 'skype', 'viber' ] );
1434 +
1435 + $attributes['href'] = esc_url( $url_control['url'], $allowed_protocols );
1436 + }
1437 +
1438 + if ( ! empty( $url_control['is_external'] ) ) {
1439 + $attributes['target'] = '_blank';
1440 + }
1441 +
1442 + if ( ! empty( $url_control['nofollow'] ) ) {
1443 + $attributes['rel'] = 'nofollow';
1444 + }
1445 +
1446 + if ( ! empty( $url_control['custom_attributes'] ) ) {
1447 + $attributes = array_merge( $attributes, Utils::parse_custom_attributes( $url_control['custom_attributes'] ) );
1448 + }
1449 +
1450 + if ( $attributes ) {
1451 + $this->add_attribute( $element, $attributes, null, $overwrite );
1452 + }
1453 +
1454 + return $this;
1065 1455 }
1066 1456
1067 1457 /**
1068 - * Button classes.
1458 + * Get render attribute string.
1069 1459 *
1070 - * @param array $params Button params.
1460 + * @param string $element The element.
1071 1461 *
1072 - * @return array
1462 + * @return string
1073 1463 */
1074 - public function button_classes( $params ) {
1075 - $params['classes'] = array_merge( $params['classes'], [ 'rtsb-action-btn', 'tipsy' ] );
1464 + public function get_attribute_string( $element ) {
1465 + if ( empty( $this->attributes[ $element ] ) ) {
1466 + return '';
1467 + }
1076 1468
1077 - return $params;
1469 + return Utils::render_html_attributes( $this->attributes[ $element ] );
1470 + }
1471 +
1472 + /**
1473 + * Function to render link attributes.
1474 + *
1475 + * @param string $id Element ID.
1476 + * @param array $control_name Control name.
1477 + * @param string $class_name Class name.
1478 + *
1479 + * @return string
1480 + */
1481 + public function render_link_attributes( $id, $control_name, $class_name = 'logo-img-link' ) {
1482 + $this->add_attribute( $id, 'class', $class_name );
1483 +
1484 + if ( ! empty( $control_name['url'] ) ) {
1485 + $this->add_link_attributes( $id, $control_name );
1486 + } else {
1487 + $this->add_attribute( $id, 'role', 'button' );
1488 + }
1489 +
1490 + return $this->get_attribute_string( $id );
1078 1491 }
1079 1492 }