PluginProbe
ShopBuilder – WooCommerce Builder For Elementor / 2.6.2
ShopBuilder – WooCommerce Builder For Elementor v2.6.2
3.4.2 3.4.1 3.4.0 2.0.1 2.0.2 2.0.3 2.1.0 2.1.1 2.1.10 2.1.11 2.1.12 2.1.13 2.1.14 2.1.15 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 2.2.0 2.2.1 2.2.2 All 63 releases
← All changes | app/Elementor/Render/Render.php +808 -393 2.1.02.6.2 View file →
@@ -1,9 +1,9 @@
1 1 <?php
2 2 /**
3 3 * Elementor Render Class.
4 4 *
5 - * This class contains render logics & output.
5 + * This class contains render logics & output. It utilizes WC_Products_Query().
6 6 *
7 7 * @package RadiusTheme\SB
8 8 */
9 9
@@ -8,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;
@@ -311,9 +304,9 @@
311 304 $query->the_post();
312 305 $i++;
313 306
314 307 // Loop arg.
315 - $arg = $this->arg_dataset( $settings, get_the_ID(), $settings['lazy_load'], $i );
308 + $arg = $this->arg_dataset( $settings, wc_get_product( get_the_ID() ), $settings['lazy_load'], $i );
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-pro' ) . '</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-pro' ) . "</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-pro' ) . '</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 );
@@ -648,8 +678,34 @@
648 678
649 679 /**
650 680 * Builds an array with meta values.
651 681 *
682 + * @param array $meta Meta values.
683 + * @param object $product Product object.
684 + * @param bool $lazy_load Lazy Load.
685 + * @param int $i Integer.
686 + *
687 + * @return array
688 + */
689 + public function arg_dataset( array $meta, object $product, bool $lazy_load = false, $i = 0 ) {
690 + $post_id = $product->get_id();
691 + $arg = $this->build_product_arg(
692 + [
693 + 'product' => $product,
694 + 'id' => $product->get_id(),
695 + 'type' => $product->get_type(),
696 + 'meta' => $meta,
697 + 'lazy_load' => $lazy_load,
698 + 'i' => $i,
699 + ]
700 + );
701 +
702 + return apply_filters( 'rtsb/elementor/render/arg_dataset', $arg, $post_id, $meta, $lazy_load );
703 + }
704 +
705 + /**
706 + * Builds an array with meta values for WP_Query.
707 + *
652 708 * @param array $meta Meta values.
653 709 * @param int $post_id Post ID.
654 710 * @param bool $lazy_load Lazy Load.
655 711 * @param int $i Integer.
@@ -655,22 +711,183 @@
655 711 * @param int $i Integer.
656 712 *
657 713 * @return array
658 714 */
659 - public function arg_dataset( array $meta, int $post_id, bool $lazy_load = false, $i = 0 ) {
715 + public function wp_arg_dataset( array $meta, int $post_id, bool $lazy_load = false, $i = 0 ) {
660 716 if ( ( empty( $meta ) && ! $post_id ) || ! in_array( get_post_type( $post_id ), [ 'product_variation', 'product' ] ) ) {
661 717 return [];
662 718 }
663 719
664 - global $post, $product;
720 + global $product;
665 721
666 722 if ( ! $product instanceof WC_Product && $post_id ) {
667 723 $product = wc_get_product( $post_id );
668 724 }
669 725
670 - $arg = [];
671 - $p_type = $product->get_type();
726 + $post_id = $product->get_id();
727 + $arg = $this->build_product_arg(
728 + [
729 + 'product' => $product,
730 + 'id' => $post_id,
731 + 'type' => $product->get_type(),
732 + 'meta' => $meta,
733 + 'lazy_load' => $lazy_load,
734 + 'i' => $i,
735 + ]
736 + );
672 737
738 + return apply_filters( 'rtsb/elementor/render/arg_dataset', $arg, $post_id, $meta, $lazy_load );
739 + }
740 +
741 + /**
742 + * Builds an array with meta values.
743 + *
744 + * @param array $meta Meta values.
745 + * @param Object $query Category object.
746 + * @param bool $fullwidth Fullwidth check.
747 + * @param bool $lazy_load Lazy Load.
748 + * @param bool $category_loop Category loop.
749 + *
750 + * @return array
751 + */
752 + public function cat_arg_dataset( $meta, $query = null, $fullwidth = false, $lazy_load = false, $category_loop = false ) {
753 + if ( empty( $meta ) ) {
754 + return [];
755 + }
756 +
757 + $arg = [];
758 +
759 + $products = null;
760 + $arg['class'] = null;
761 + $arg['grid'] = null;
762 + $arg['anchor_class'] = null;
763 + $arg['p_sale'] = null;
764 + $arg['count'] = max( $query->count, 0 );
765 + $arg['count_position'] = $meta['count_position'];
766 + $arg['excerpt_position'] = $meta['cat_excerpt_position'];
767 + $arg['cat_link'] = get_term_link( $query );
768 + $arg['target'] = '_self';
769 + $arg['raw_settings'] = ! empty( $meta['raw_settings'] ) ? $meta['raw_settings'] : [];
770 +
771 + if ( ! empty( $meta['tag_term'] ) ) {
772 + $arg['tag_term'] = $meta['tag_term'];
773 + }
774 +
775 + if ( ! empty( $meta['custom_link']['url'] ) ) {
776 + $arg['cat_link'] = $meta['custom_link']['url'];
777 + $arg['target'] = $meta['custom_link']['is_external'] ? '_blank' : '_self';
778 + }
779 +
780 + $title = $query->name;
781 + $excerpt = $query->description;
782 +
783 + if ( in_array( 'badges', $meta['visibility'], true ) ) {
784 + $arg['p_sale'] = $meta['custom_badge_text'];
785 + }
786 +
787 + $arg['badge_class'] = RenderHelpers::badge_class( $meta['badge_preset'] );
788 +
789 + if ( $arg['count'] >= 0 ) {
790 + $prefix = ! empty( $meta['before_count'] ) ? $meta['before_count'] : '';
791 + $suffix = ! empty( $meta['after_count'] ) ? $meta['after_count'] : '';
792 +
793 + $arg['count'] = $prefix . $arg['count'] . $suffix;
794 + }
795 +
796 + if ( $meta['show_custom_title'] && ! empty( $meta['cat_custom_title'] ) ) {
797 + $title = $meta['cat_custom_title'];
798 + }
799 +
800 + if ( $meta['show_custom_excerpt'] && ! empty( $meta['cat_custom_excerpt'] ) ) {
801 + $excerpt = $meta['cat_custom_excerpt'];
802 + }
803 +
804 + $arg['c_img'] = $meta['c_img'];
805 + $arg['items'] = $meta['visibility'];
806 + $arg['title_tag'] = $meta['title_tag'];
807 + $arg['title_class'] = 'category-title rtsb-text-limit limit-' . $meta['title_limit'] . ( ! $meta['title_link'] ? ' no-link' : '' );
808 + $arg['title'] = Fns::text_truncation( $title, $meta['title_limit_custom'] );
809 + $arg['excerpt_limit'] = $meta['excerpt_limit'];
810 + $arg['excerpt'] = wpautop( Fns::text_truncation( $excerpt, $meta['excerpt_limit_custom'] ) );
811 + $arg['title_link'] = $meta['title_link'];
812 + $arg['image_link'] = $meta['image_link'];
813 + $arg['grid'] .= 'rtsb-col-grid ';
814 +
815 + if ( ! $fullwidth ) {
816 + if ( 'masonry' === $meta['grid_type'] ) {
817 + $arg['class'] .= 'masonry-grid-item ';
818 + } else {
819 + $arg['class'] .= 'even-grid-item ';
820 + }
821 + }
822 +
823 + if ( ! empty( $meta['active_cat_slider'] ) ) {
824 + $arg['class'] .= ' rtsb-slide-item swiper-slide animated rtFadeIn ';
825 + }
826 +
827 + $arg['class'] .= 'rtsb-category-grid';
828 +
829 + ob_start();
830 + do_action( 'rtsb/categories/category_image_override', $meta, $query->term_id );
831 + $override_category_image = ob_get_clean();
832 +
833 + $arg['img_html'] = $meta['c_img'] ? Fns::get_product_image_html(
834 + 'category',
835 + $query->term_id,
836 + $meta['f_img_size'],
837 + $meta['default_img_id'],
838 + $meta['custom_img_size'],
839 + $lazy_load,
840 + false,
841 + false,
842 + $override_category_image
843 + ) : null;
844 +
845 + if ( $meta['show_custom_image'] && ! empty( $meta['custom_image'] ) ) {
846 + $arg['img_html'] = Fns::get_product_image_html(
847 + 'custom',
848 + null,
849 + $meta['f_img_size'],
850 + $meta['custom_image'],
851 + $meta['custom_img_size'],
852 + $lazy_load
853 + );
854 + }
855 +
856 + $arg['excerpt_class'] = 'category-info' . ( 'block' === $meta['count_position'] ? ' block-count' : ' inline-count' ) . ( 'above' === $meta['cat_excerpt_position'] ? ' excerpt-above' : '' ) . ( empty( $arg['excerpt'] ) ? ' no-excerpt' : '' );
857 +
858 + if ( $category_loop ) {
859 + $query_args = [
860 + 'product_category_id' => $query->term_id,
861 + 'limit' => -1,
862 + 'order' => 'DESC',
863 + 'orderby' => 'date',
864 + ];
865 +
866 + $products_query = new WC_Product_Query( $query_args );
867 + $products = $products_query->get_products();
868 + }
869 +
870 + return apply_filters( 'rtsb/elementor/render/cat_arg_dataset', $arg, $meta, $query, $fullwidth, $lazy_load, $products );
871 + }
872 +
873 + /**
874 + * Building product args.
875 + *
876 + * @param array $args Args.
877 + * @return array
878 + */
879 + public function build_product_arg( $args ) {
880 + list(
881 + 'product' => $product,
882 + 'id' => $post_id,
883 + 'type' => $p_type,
884 + 'meta' => $meta,
885 + 'lazy_load' => $lazy_load,
886 + 'i' => $i,
887 + ) = $args;
888 +
889 + $arg = [];
673 890 $arg['class'] = null;
674 891 $arg['grid'] = null;
675 892 $arg['anchor_class'] = null;
676 893 $arg['hover_img_html'] = null;
@@ -675,26 +892,28 @@
675 892 $arg['anchor_class'] = null;
676 893 $arg['hover_img_html'] = null;
677 894 $arg['s_link'] = [];
678 895 $arg['add_to_cart'] = '';
896 + $arg['p_counter'] = absint( $i );
679 897 $arg['p_id'] = $post_id;
680 - $arg['p_link'] = get_permalink();
898 + $arg['product'] = $product;
899 + $arg['p_link'] = $product->get_permalink();
681 900 $arg['p_sale'] = '';
682 901 $arg['rating_args'] = [];
683 902 $arg['has_attributes'] = $product->is_type( 'variable' ) && $product->get_attributes();
684 903 $arg['raw_settings'] = ! empty( $meta['raw_settings'] ) ? $meta['raw_settings'] : [];
685 904 $cart_icon_html = '';
905 + $layout = $meta['layout'];
686 906
687 - $layout = $meta['layout'];
688 -
907 + // Building the arg.
689 908 $arg['target'] = '_self';
690 909 $arg['f_img'] = $meta['f_img'];
691 910 $arg['items'] = $meta['visibility'];
692 911 $arg['title_tag'] = $meta['title_tag'];
693 912 $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'] );
913 + $arg['title'] = Fns::text_truncation( $product->get_title(), $meta['title_limit_custom'] );
695 914 $arg['excerpt_limit'] = $meta['excerpt_limit'];
696 - $arg['excerpt'] = wpautop( Fns::text_truncation( do_shortcode( $post->post_excerpt ), $meta['excerpt_limit_custom'] ) );
915 + $arg['excerpt'] = wpautop( Fns::text_truncation( do_shortcode( get_post_field( 'post_excerpt', $post_id ) ), $meta['excerpt_limit_custom'] ) );
697 916 $arg['title_link'] = $meta['title_link'];
698 917 $arg['image_link'] = $meta['image_link'];
699 918 $arg['out_of_stock'] = $meta['out_of_stock_badge_text'];
700 919 $arg['action_btn_preset'] = $meta['btn_preset'];
@@ -706,10 +925,15 @@
706 925 $is_list = preg_match( '/list/', $layout );
707 926 $is_carousel = preg_match( '/slider/', $layout );
708 927
709 928 if ( ! $is_carousel ) {
710 - $arg['grid'] .= 'rtsb-col-grid ';
711 - $arg['class'] .= ' even-grid-item ';
929 + $arg['grid'] .= 'rtsb-col-grid ';
930 +
931 + if ( 'masonry' === $meta['grid_type'] ) {
932 + $arg['class'] .= 'masonry-grid-item ';
933 + } else {
934 + $arg['class'] .= 'even-grid-item ';
935 + }
712 936 } else {
713 937 $arg['grid'] .= 'rtsb-col-grid rtsb-col-full rtsb-slide-item swiper-slide ';
714 938 }
715 939
@@ -729,17 +953,21 @@
729 953
730 954 $arg['class'] .= ' animated rtFadeIn';
731 955
732 956 if ( in_array( 'badges', $meta['visibility'], true ) ) {
733 - $arg['p_sale'] = Fns::get_sale_badge(
957 + $badge_module = ! empty( $meta['enable_badges_module'] );
958 + $arg['p_sale'] = Fns::is_module_active( 'product_badges' ) && $badge_module ? 'rtsb_yes' : Fns::get_promo_badge(
959 + $product,
734 960 $meta['sale_badge_type'],
735 961 $meta['sale_badge_text'],
736 - $meta['out_of_stock_badge_text']
962 + apply_filters( 'rtsb/elementor/render/out_of_stock_badge_text', $meta['out_of_stock_badge_text'] )
737 963 );
738 964 }
739 965
740 966 $arg['badge_class'] = RenderHelpers::badge_class( $meta['badge_preset'] );
741 967
968 + $meta['add_to_cart_text'] = apply_filters( 'rtsb/elementor/render/add_to_cart_text', $meta['add_to_cart_text'], $product );
969 +
742 970 if ( ! $meta['show_cart_text'] ) {
743 971 $meta['add_to_cart_text'] = '';
744 972 $meta['add_to_cart_success_text'] = '';
745 973 }
@@ -752,20 +980,23 @@
752 980 if ( ! empty( $meta['add_to_cart_success_icon'] ) ) {
753 981 $cart_icon_html .= Fns::icons_manager( $meta['add_to_cart_success_icon'], 'cart-success-icon' );
754 982 }
755 983
756 - $cart_icon = '<span class="icon">' . $cart_icon_html . '</span>';
984 + $cart_icon = ! empty( $cart_icon_html ) ? '<span class="icon">' . $cart_icon_html . '</span>' : '';
757 985
758 986 if ( $product->is_purchasable() || 'grouped' === $p_type || 'external' === $p_type ) {
759 987 if ( $product->is_in_stock() ) {
760 988 $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']
989 + [
990 + 'link' => $product->get_permalink(),
991 + 'id' => $post_id,
992 + 'type' => $p_type,
993 + 'text' => $meta['add_to_cart_text'],
994 + 'success' => $meta['add_to_cart_success_text'],
995 + 'icon_html' => $cart_icon,
996 + 'alignment' => $meta['add_to_cart_alignment'],
997 + 'product' => $product,
998 + ]
768 999 );
769 1000 }
770 1001 }
771 1002 }
@@ -831,243 +1062,427 @@
831 1062 'hover_img_html' => $arg['hover_img_html'],
832 1063 'meta' => $meta,
833 1064 ];
834 1065
835 - return apply_filters( 'rtsb/elementor/render/arg_dataset', $arg, $post_id, $meta, $lazy_load );
1066 + return $arg;
836 1067 }
837 1068
838 1069 /**
839 - * Builds an array with meta values.
1070 + * Renders add to cart button.
840 1071 *
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.
1072 + * @param array $args Cart args.
845 1073 *
846 - * @return array
1074 + * @return string
847 1075 */
848 - public function cat_arg_dataset( $meta, $query = null, $fullwidth = false, $lazy_load = false, $category_loop = false ) {
849 - if ( empty( $meta ) ) {
850 - return [];
1076 + public function render_add_to_cart_button( $args ) {
1077 + list(
1078 + 'link' => $link,
1079 + 'id' => $id,
1080 + 'type' => $type,
1081 + 'text' => $text,
1082 + 'success' => $success,
1083 + 'icon_html' => $icon_html,
1084 + 'alignment' => $alignment,
1085 + 'product' => $product
1086 + ) = $args;
1087 +
1088 + $content = null;
1089 + $rand = wp_rand();
1090 + $ext_link = get_post_meta( $id, '_product_url', true );
1091 + $btn_txt = get_post_meta( $id, '_button_text', true );
1092 +
1093 + ob_start();
1094 + do_action( 'rtsb/before/cart/button' );
1095 + $content .= ob_get_clean();
1096 +
1097 + $cart_attr = [
1098 + 'class' => 'rtsb-action-btn ' . $type . '-product tipsy icon-' . $alignment . ' ' . ( empty( $text ) ? 'no-text' : 'has-text' ),
1099 + 'href' => esc_url( $link ),
1100 + 'rel' => 'nofollow',
1101 + 'data-quantity' => '1',
1102 + 'data-product_id' => absint( $id ),
1103 + 'data-id' => absint( $id ),
1104 + ];
1105 +
1106 + if ( empty( $text ) ) {
1107 + $tooltip_mapping = [
1108 + 'simple' => esc_attr__( 'Add to Cart', 'shopbuilder' ),
1109 + 'variable' => esc_attr__( 'Select Options', 'shopbuilder' ),
1110 + 'grouped' => esc_attr__( 'View Products', 'shopbuilder' ),
1111 + 'external' => ! empty( $btn_txt ) ? esc_html( $btn_txt ) : esc_html__( 'Buy Product', 'shopbuilder' ),
1112 + ];
1113 +
1114 + $cart_attr['title'] = $tooltip_mapping[ $type ];
851 1115 }
852 1116
853 - $arg = [];
1117 + $content .= '<div class="rtsb-wc-add-to-cart-wrap">';
854 1118
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'] : [];
1119 + if ( 'variable' === $type || 'grouped' === $type ) {
1120 + $cart_attr = apply_filters( 'rtsb/elementor/render/cart_attributes', $cart_attr, $product );
866 1121
867 - if ( ! empty( $meta['tag_term'] ) ) {
868 - $arg['tag_term'] = $meta['tag_term'];
1122 + $this->add_attribute( 'rtsb_add_to_cart_button_' . $id . $rand, $cart_attr );
1123 +
1124 + $text_btn = 'grouped' === $type ? __( 'View Products', 'shopbuilder' ) : __( 'Select Options', 'shopbuilder' );
1125 +
1126 + $content .= '<a ' . $this->get_attribute_string( 'rtsb_add_to_cart_button_' . $id . $rand ) . '>';
1127 + $content .= $icon_html;
1128 + $content .= '<span class="text ' . ( ! empty( $success ) ? 'has-success-text' : 'no-success-text' ) . '" data-success="' . esc_html( $success ) . '" data-cart-text="' . ( ! empty( $text ) ? esc_attr( $text ) : $tooltip_mapping['simple'] ) . '" data-variable-text="' . esc_attr( $text_btn ) . '">' . esc_html( $text_btn ) . '</span>';
1129 + $content .= '<span></span>';
1130 + } elseif ( 'external' === $type ) {
1131 + if ( ! empty( $ext_link ) ) {
1132 + $cart_attr['target'] = '_blank';
1133 + }
1134 +
1135 + $cart_attr['href'] = ! empty( $ext_link ) ? esc_url( $ext_link ) : esc_url( $link );
1136 +
1137 + $this->add_attribute( 'rtsb_add_to_cart_button_' . $id . $rand, $cart_attr );
1138 +
1139 + $content .= '<a ' . $this->get_attribute_string( 'rtsb_add_to_cart_button_' . $id . $rand ) . '>';
1140 + $content .= $icon_html;
1141 + $content .= '<span class="text">' . ( ! empty( $btn_txt ) ? esc_html( $btn_txt ) : esc_html__( 'Buy Product', 'shopbuilder' ) ) . '</span>';
1142 + } else {
1143 + $cart_attr['href'] = $cart_attr['href'] . '?add-to-cart=' . absint( $id );
1144 + $cart_attr['class'] .= ' rtsb-add-to-cart-btn';
1145 + $cart_attr['data-type'] = $type;
1146 + $cart_attr = apply_filters( 'rtsb/elementor/render/cart_attributes', $cart_attr, $product );
1147 +
1148 + $this->add_attribute( 'rtsb_add_to_cart_button_' . $id . $rand, $cart_attr );
1149 +
1150 + $content .= '<a ' . $this->get_attribute_string( 'rtsb_add_to_cart_button_' . $id . $rand ) . '>';
1151 + $content .= $icon_html;
1152 + $content .= '<span class="text ' . ( ! empty( $success ) ? 'has-success-text' : 'no-success-text' ) . '" data-success="' . esc_html( $success ) . '">' . esc_html( $text ) . '</span>';
1153 + $content .= '<span></span>';
869 1154 }
870 1155
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';
1156 + $content .= '</a>';
1157 + $content .= '</div>';
1158 +
1159 + ob_start();
1160 + do_action( 'rtsb/after/cart/button' );
1161 + $content .= ob_get_clean();
1162 +
1163 + return apply_filters( 'rtsb/render/cart/button', $content, $args );
1164 + }
1165 +
1166 + /**
1167 + * Button classes.
1168 + *
1169 + * @param array $params Button params.
1170 + *
1171 + * @return array
1172 + */
1173 + public function button_classes( $params ) {
1174 + $params['classes'] = array_merge( $params['classes'], [ 'rtsb-action-btn', 'tipsy' ] );
1175 +
1176 + return $params;
1177 + }
1178 +
1179 + /**
1180 + * Render the section title.
1181 + *
1182 + * @param array $settings The Elementor settings.
1183 + *
1184 + * @return string
1185 + */
1186 + public function render_section_title( $settings ) {
1187 + $html = '';
1188 + $rand = wp_rand();
1189 +
1190 + if ( empty( $settings['show_section_title'] ) || ( empty( $settings['query_products'] ) && 0 === count( $settings['query_products'] ) ) ) {
1191 + return $html;
874 1192 }
875 1193
876 - $title = $query->name;
877 - $excerpt = $query->description;
1194 + // Set attributes for the section title.
1195 + $this->add_attribute( 'rtsb_section_title_wrapper_' . $rand, 'class', 'rtsb-section-title-wrapper' );
1196 + $this->add_attribute( 'rtsb_section_title_' . $rand, 'class', 'rtsb-section-title' );
878 1197
879 - if ( in_array( 'badges', $meta['visibility'], true ) ) {
880 - $arg['p_sale'] = $meta['custom_badge_text'];
881 - }
1198 + // Start rendering.
1199 + $html .= '<div ' . $this->get_attribute_string( 'rtsb_section_title_wrapper_' . $rand ) . '>';
1200 + $html .= '<' . esc_attr( $settings['section_title_tag'] ) . ' ' . $this->get_attribute_string( 'rtsb_section_title_' . $rand ) . '>';
1201 + $html .= esc_html( $settings['section_title_text'] );
1202 + $html .= '</' . esc_attr( $settings['section_title_tag'] ) . '>';
1203 + $html .= '</div>';
882 1204
883 - $arg['badge_class'] = RenderHelpers::badge_class( $meta['badge_preset'] );
1205 + return $html;
1206 + }
884 1207
885 - if ( $arg['count'] >= 0 ) {
886 - $prefix = ! empty( $meta['before_count'] ) ? $meta['before_count'] : '';
887 - $suffix = ! empty( $meta['after_count'] ) ? $meta['after_count'] : '';
1208 + /**
1209 + * Renders products pagination
1210 + *
1211 + * @param array $args Pagination args.
1212 + * @param string $query_type Query type.
1213 + *
1214 + * @return string
1215 + */
1216 + public function render_products_pagination( $args, $query_type = 'wc' ) {
1217 + list(
1218 + 'query' => $query,
1219 + 'meta' => $meta,
1220 + 'limit' => $limit,
1221 + 'per_page' => $per_page
1222 + ) = $args;
888 1223
889 - $arg['count'] = $prefix . $arg['count'] . $suffix;
890 - }
1224 + $html_utility = null;
1225 + $html = null;
1226 + $ajax = false;
1227 + $is_grid = preg_match( '/grid-layout/', $meta['layout'] ) || preg_match( '/list-layout/', $meta['layout'] );
1228 + $posts_per_page = 'wp' === $query_type ? $query->query_vars['posts_per_page'] : null;
1229 + $total_page = 'wp' === $query_type ? $query->max_num_pages : $query->get_products()->max_num_pages;
891 1230
892 - if ( $meta['show_custom_title'] && ! empty( $meta['cat_custom_title'] ) ) {
893 - $title = $meta['cat_custom_title'];
894 - }
1231 + if ( $limit ) {
1232 + if ( 'wc' === $query_type ) {
1233 + $found_post = $query->get_products()->total;
1234 + } elseif ( 'wp' === $query_type && ( empty( $wp_query->query['tax_query'] ) ) ) {
1235 + $found_post = $query->found_posts;
1236 + }
895 1237
896 - if ( $meta['show_custom_excerpt'] && ! empty( $meta['cat_custom_excerpt'] ) ) {
897 - $excerpt = $meta['cat_custom_excerpt'];
1238 + if ( $per_page && $found_post > $per_page ) {
1239 + $found_post = $limit;
1240 + $total_page = ceil( $found_post / $per_page );
1241 + }
898 1242 }
899 1243
900 - $layout = $meta['layout'];
1244 + $total_page = absint( $total_page );
901 1245
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'];
1246 + $args = [
1247 + 'total_page' => $total_page,
1248 + 'posts_per_page' => 'wc' === $query_type ? $per_page : $posts_per_page,
1249 + 'ajax' => $ajax,
1250 + ];
911 1251
912 - if ( ! $fullwidth ) {
913 - $arg['grid'] .= 'rtsb-col-grid ';
914 - $arg['class'] .= ' even-grid-item ';
1252 + if ( 'pagination' === $meta['posts_loading_type'] ) {
1253 + if ( $is_grid ) {
1254 + $html_utility .= Fns::custom_pagination(
1255 + $total_page,
1256 + 'wc' === $query_type ? $per_page : $posts_per_page
1257 + );
1258 + }
915 1259 } else {
916 - $arg['grid'] .= 'rtsb-col-grid ';
1260 + ob_start();
1261 + do_action( 'rtsb/elementor/render/pagination', $meta, $query, $args );
1262 + $html_utility .= ob_get_clean();
917 1263 }
918 1264
919 - $arg['class'] .= 'rtsb-category-grid';
1265 + if ( $html_utility ) {
1266 + $rand = wp_rand();
920 1267
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;
1268 + // Adding pagination render attributes.
1269 + $this->add_attribute(
1270 + 'rtsb_pagination_attr_' . $rand,
1271 + [
1272 + 'class' => 'rtsb-pagination-wrap element-loading',
1273 + 'data-total-pages' => $total_page,
1274 + 'data-posts-per-page' => 'wc' === $query_type ? $per_page : $posts_per_page,
1275 + 'data-type' => $meta['posts_loading_type'],
1276 + ]
1277 + );
929 1278
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 - );
1279 + // Start rendering.
1280 + $html = '<div ' . $this->get_attribute_string( 'rtsb_pagination_attr_' . $rand ) . '>';
1281 + $html .= $html_utility;
1282 + $html .= '</div><!-- .rtsb-pagination-wrap -->';
939 1283 }
940 1284
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' : '' );
1285 + return $html;
1286 + }
942 1287
943 - if ( $category_loop ) {
944 - $query_args = [
945 - 'product_category_id' => $query->term_id,
946 - 'limit' => -1,
947 - 'order' => 'DESC',
948 - 'orderby' => 'date',
949 - ];
1288 + /**
1289 + * Renders slider buttons
1290 + *
1291 + * @param array $settings Settings array.
1292 + *
1293 + * @return string
1294 + */
1295 + public function render_slider_buttons( $settings ) {
1296 + $html = '';
1297 + $left_icon = $settings['left_arrow_icon'];
1298 + $right_icon = $settings['right_arrow_icon'];
950 1299
951 - $products_query = new \WC_Product_Query( $query_args );
952 - $products = $products_query->get_products();
1300 + if ( ! empty( $settings['slider_nav'] ) ) {
1301 + $html .=
1302 + '<div class="swiper-nav">
1303 + <div class="swiper-arrow swiper-button-next">
1304 + ' . Fns::icons_manager( $right_icon ) . '
1305 + </div>
1306 + <div class="swiper-arrow swiper-button-prev">
1307 + ' . Fns::icons_manager( $left_icon ) . '
1308 + </div>
1309 + </div>';
953 1310 }
954 1311
955 - return apply_filters( 'rtsb/elementor/render/cat_arg_dataset', $arg, $meta, $query, $fullwidth, $lazy_load, $products );
1312 + $html .= ! empty( $settings['slider_dot'] ) ? '<div class="swiper-pagination rtsb-pos-s"></div>' : '';
1313 +
1314 + return $html;
956 1315 }
957 1316
958 1317 /**
959 - * Renders add to cart button.
1318 + * No products message.
960 1319 *
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.
1320 + * @param array $metas Meta data.
968 1321 *
1322 + * @return void
1323 + */
1324 + public function no_products_msg( $metas ) {
1325 + $content =
1326 + '<div class="rtsb-notice woocommerce-no-products-found">
1327 + <div class="woocommerce-info">' . esc_html__( 'No products were found matching your selection.', 'shopbuilder' ) . '</div>
1328 + </div>';
1329 +
1330 + $this->row( $content, $metas );
1331 + }
1332 +
1333 + /**
1334 + * Start the rendering.
1335 + *
1336 + * @param array $metas Meta Data.
1337 + * @param array $settings Widget settings.
1338 + * @param array $temp_args Additional temporary arguments for rendering.
1339 + *
969 1340 * @return string
970 1341 */
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 );
1342 + public function render_start( $metas, $settings, $temp_args ) {
1343 + ob_start();
976 1344
977 - if ( empty( $text ) ) {
978 - $tooltip_attr = ' title="' . esc_attr__( 'Add to Cart', 'shopbuilder' ) . '"';
1345 + /**
1346 + * Before render row hook.
1347 + */
1348 + do_action( 'rtsb/elementor/render/before_row', $metas, $settings, $temp_args, $this );
979 1349
980 - if ( 'variable' === $type ) {
981 - $tooltip_attr = ' title="' . esc_attr__( 'Select Options', 'shopbuilder' ) . '"';
1350 + return ob_get_clean();
1351 + }
1352 +
1353 + /**
1354 + * End the rendering.
1355 + *
1356 + * @param array $metas Meta Data.
1357 + * @param array $settings Widget settings.
1358 + * @param array $products Array of products being rendered.
1359 + *
1360 + * @return string
1361 + */
1362 + public function render_end( $metas, $settings, $products ) {
1363 + ob_start();
1364 +
1365 + /**
1366 + * After render row hook.
1367 + */
1368 + do_action( 'rtsb/elementor/render/after_row', $metas, $settings, $products );
1369 +
1370 + return ob_get_clean();
1371 + }
1372 +
1373 + /**
1374 + * Add render attribute.
1375 + *
1376 + * @param array|string $element The HTML element.
1377 + * @param array|string $key Optional. Attribute key. Default is null.
1378 + * @param array|string $value Optional. Attribute value. Default is null.
1379 + * @param bool $overwrite Optional. Whether to overwrite existing.
1380 + *
1381 + * @return self
1382 + */
1383 + public function add_attribute( $element, $key = null, $value = null, $overwrite = false ) {
1384 + if ( is_array( $element ) ) {
1385 + foreach ( $element as $element_key => $attributes ) {
1386 + $this->add_attribute( $element_key, $attributes, null, $overwrite );
982 1387 }
983 1388
984 - if ( 'grouped' === $type ) {
985 - $tooltip_attr = ' title="' . esc_attr__( 'View Products', 'shopbuilder' ) . '"';
1389 + return $this;
1390 + }
1391 +
1392 + if ( is_array( $key ) ) {
1393 + foreach ( $key as $attribute_key => $attributes ) {
1394 + $this->add_attribute( $element, $attribute_key, $attributes, $overwrite );
986 1395 }
987 1396
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 - }
1397 + return $this;
992 1398 }
993 1399
994 - ob_start();
995 - do_action( 'rtsb/before/cart/button' );
996 - $content .= ob_get_clean();
1400 + if ( empty( $this->attributes[ $element ][ $key ] ) ) {
1401 + $this->attributes[ $element ][ $key ] = [];
1402 + }
997 1403
998 - if ( 'variable' === $type ) {
999 - $content .= sprintf(
1000 - '<div class="rtsb-wc-add-to-cart-wrap">
1001 - <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>
1002 - </div>',
1003 - esc_url( $link ),
1004 - esc_html__( 'Select Options', 'shopbuilder' ),
1005 - absint( $id ),
1006 - esc_attr( $alignment ),
1007 - empty( $text ) ? 'no-text' : 'has-text',
1008 - $tooltip_attr,
1009 - $icon_html,
1010 - );
1011 - } elseif ( 'grouped' === $type ) {
1012 - $content .= sprintf(
1013 - '<div class="rtsb-wc-add-to-cart-wrap">
1014 - <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>
1015 - </div>',
1016 - esc_url( $link ),
1017 - esc_html__( 'View Products', 'shopbuilder' ),
1018 - absint( $id ),
1019 - esc_attr( $alignment ),
1020 - empty( $text ) ? 'no-text' : 'has-text',
1021 - $tooltip_attr,
1022 - $icon_html,
1023 - );
1024 - } elseif ( 'external' === $type ) {
1025 - $content .= sprintf(
1026 - '<div class="rtsb-wc-add-to-cart-wrap">
1027 - <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>
1028 - </div>',
1029 - ! empty( $ext_link ) ? esc_url( $ext_link ) : esc_url( $link ),
1030 - ! empty( $btn_txt ) ? esc_html( $btn_txt ) : esc_html__( 'Buy Product', 'shopbuilder' ),
1031 - absint( $id ),
1032 - esc_attr( $alignment ),
1033 - empty( $text ) ? 'no-text' : 'has-text',
1034 - $tooltip_attr,
1035 - $icon_html,
1036 - ! empty( $ext_link ) ? ' target="_blank"' : ''
1037 - );
1404 + settype( $value, 'array' );
1405 +
1406 + if ( $overwrite ) {
1407 + $this->attributes[ $element ][ $key ] = $value;
1038 1408 } else {
1039 - $content .= sprintf(
1040 - '<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>',
1041 - esc_url( $link ),
1042 - absint( $id ),
1043 - esc_attr( $type ),
1044 - $icon_html,
1045 - esc_html( $text ),
1046 - esc_attr( $alignment ),
1047 - empty( $text ) ? 'no-text' : 'has-text',
1048 - $tooltip_attr,
1049 - esc_html( $success ),
1050 - ! empty( $success ) ? 'has-success-text' : 'no-success-text',
1051 - );
1409 + $this->attributes[ $element ][ $key ] = array_merge( $this->attributes[ $element ][ $key ], $value );
1052 1410 }
1053 1411
1054 - ob_start();
1055 - do_action( 'rtsb/after/cart/button' );
1056 - $content .= ob_get_clean();
1412 + return $this;
1413 + }
1057 1414
1058 - return $content;
1415 + /**
1416 + * Add link render attributes.
1417 + *
1418 + * @param array|string $element The HTML element.
1419 + * @param array $url_control Array of link settings.
1420 + * @param bool $overwrite Optional. Whether to overwrite existing
1421 + * attribute. Default is false, not to overwrite.
1422 + *
1423 + * @return Render
1424 + */
1425 + public function add_link_attributes( $element, array $url_control, $overwrite = false ) {
1426 + $attributes = [];
1427 +
1428 + if ( ! empty( $url_control['url'] ) ) {
1429 + $allowed_protocols = array_merge( wp_allowed_protocols(), [ 'skype', 'viber' ] );
1430 +
1431 + $attributes['href'] = esc_url( $url_control['url'], $allowed_protocols );
1432 + }
1433 +
1434 + if ( ! empty( $url_control['is_external'] ) ) {
1435 + $attributes['target'] = '_blank';
1436 + }
1437 +
1438 + if ( ! empty( $url_control['nofollow'] ) ) {
1439 + $attributes['rel'] = 'nofollow';
1440 + }
1441 +
1442 + if ( ! empty( $url_control['custom_attributes'] ) ) {
1443 + $attributes = array_merge( $attributes, Utils::parse_custom_attributes( $url_control['custom_attributes'] ) );
1444 + }
1445 +
1446 + if ( $attributes ) {
1447 + $this->add_attribute( $element, $attributes, null, $overwrite );
1448 + }
1449 +
1450 + return $this;
1059 1451 }
1060 1452
1061 1453 /**
1062 - * Button classes.
1454 + * Get render attribute string.
1063 1455 *
1064 - * @param array $params Button params.
1456 + * @param string $element The element.
1065 1457 *
1066 - * @return array
1458 + * @return string
1067 1459 */
1068 - public function button_classes( $params ) {
1069 - $params['classes'] = array_merge( $params['classes'], [ 'rtsb-action-btn', 'tipsy' ] );
1460 + public function get_attribute_string( $element ) {
1461 + if ( empty( $this->attributes[ $element ] ) ) {
1462 + return '';
1463 + }
1070 1464
1071 - return $params;
1465 + return Utils::render_html_attributes( $this->attributes[ $element ] );
1466 + }
1467 +
1468 + /**
1469 + * Function to render link attributes.
1470 + *
1471 + * @param string $id Element ID.
1472 + * @param array $control_name Control name.
1473 + * @param string $class_name Class name.
1474 + *
1475 + * @return string
1476 + */
1477 + public function render_link_attributes( $id, $control_name, $class_name = 'logo-img-link' ) {
1478 + $this->add_attribute( $id, 'class', $class_name );
1479 +
1480 + if ( ! empty( $control_name['url'] ) ) {
1481 + $this->add_link_attributes( $id, $control_name );
1482 + } else {
1483 + $this->add_attribute( $id, 'role', 'button' );
1484 + }
1485 +
1486 + return $this->get_attribute_string( $id );
1072 1487 }
1073 1488 }