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