PluginProbe
ShopBuilder – WooCommerce Builder For Elementor / 3.2.0
ShopBuilder – WooCommerce Builder For Elementor v3.2.0
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 +176 -35 2.1.43.2.0 View file →
@@ -85,9 +85,10 @@
85 85 public function container( $content, $settings, $class = '', $template = '', $fullwidth = false, $ajax = true ) {
86 86 $rand = wp_rand();
87 87 $layout_id = 'rtsb-container-' . $rand;
88 88 $metas = RenderHelpers::meta_dataset( $settings, $template );
89 - $layout = $metas['layout'];
89 + $raw_layout = esc_html( $metas['layout'] );
90 + $layout = RenderHelpers::filter_layout( $raw_layout, $template );
90 91 $style_attr = '--rtsb-default-columns: ' . esc_attr( RenderHelpers::default_columns( $layout ) );
91 92 $view_mode = $settings['view_mode'] ?? 'grid';
92 93 $view_mode = isset( $_GET['displayview'] ) ? sanitize_text_field( wp_unslash( $_GET['displayview'] ) ) : $view_mode; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
93 94 $tooltip_attr = 'list' == $view_mode && ! empty( $metas['tooltip_position_list'] ) ? esc_attr( $metas['tooltip_position_list'] ) : $metas['tooltip_position'];
@@ -92,8 +93,9 @@
92 93 $view_mode = isset( $_GET['displayview'] ) ? sanitize_text_field( wp_unslash( $_GET['displayview'] ) ) : $view_mode; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
93 94 $tooltip_attr = 'list' == $view_mode && ! empty( $metas['tooltip_position_list'] ) ? esc_attr( $metas['tooltip_position_list'] ) : $metas['tooltip_position'];
94 95 $has_pro = rtsb()->has_pro();
95 96 $has_filters = $has_pro && ! empty( $settings['tax_filter'] );
97 + $grid_style = $settings['grid_style'] ?? 'even';
96 98
97 99 // Set default layout.
98 100 if ( ! $has_pro && ! in_array( $layout, array_keys( Fns::free_layouts() ), true ) ) {
99 101 $layout = RenderHelpers::set_default_layout( $layout );
@@ -98,8 +100,13 @@
98 100 if ( ! $has_pro && ! in_array( $layout, array_keys( Fns::free_layouts() ), true ) ) {
99 101 $layout = RenderHelpers::set_default_layout( $layout );
100 102 }
101 103
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 ) );
107 + }
108 +
102 109 // Container classes.
103 110 $container_classes = RenderHelpers::prepare_container_classes( $metas, $settings, $class );
104 111
105 112 // Pagination attributes.
@@ -108,9 +115,9 @@
108 115 // Attributes for the container.
109 116 $container_attributes = [
110 117 'id' => $layout_id,
111 118 'class' => $container_classes,
112 - 'style' => $style_attr,
119 + 'style' => apply_filters( 'rtsb/product/container/attr', $style_attr, $settings ),
113 120 'data-layout' => $layout,
114 121 'data-tooltip-position' => $tooltip_attr,
115 122 ];
116 123
@@ -140,8 +147,9 @@
140 147 $script_generator = [];
141 148 $script_generator['layout'] = $layout_id;
142 149 $script_generator['rand'] = absint( $rand );
143 150 $script_generator['isCarousel'] = preg_match( '/slider/', $layout );
151 + $script_generator['isMasonry'] = $has_pro && 'masonry' === $grid_style;
144 152
145 153 // Register scripts in the wp_footer action.
146 154 add_action(
147 155 'wp_footer',
@@ -163,13 +171,15 @@
163 171 *
164 172 * @return string
165 173 */
166 174 public function row( $content, $settings, $hook_html = '', $raw_settings = [] ) {
167 - $layout = $settings['layout'];
175 + $raw_layout = esc_html( $settings['layout'] );
176 + $layout = RenderHelpers::filter_layout( $raw_layout );
177 +
168 178 $rand = wp_rand();
169 - $is_carousel = preg_match( '/slider/', $layout );
179 + $is_carousel = preg_match( '/slider/', $raw_layout );
170 180
171 - if ( preg_match( '/category/', $layout ) && ( ! empty( $settings['active_cat_slider'] ) ) ) {
181 + if ( preg_match( '/category/', $raw_layout ) && ( ! empty( $settings['active_cat_slider'] ) ) ) {
172 182 $is_carousel = true;
173 183 }
174 184
175 185 // Row classes.
@@ -185,9 +195,8 @@
185 195 $this->html = ! empty( $settings['show_filter'] ) ? $hook_html . $start_wrapper : $start_wrapper;
186 196
187 197 if ( $is_carousel ) {
188 198 $slider_data = RenderHelpers::slider_data( $settings, $raw_settings );
189 -
190 199 // Adding slider render attributes.
191 200 $this->add_attribute(
192 201 'rtsb_slider_attr_' . $rand,
193 202 [
@@ -237,11 +246,21 @@
237 246 }
238 247
239 248 $i = 0;
240 249
241 - foreach ( $products as $product ) {
250 + global $product;
251 +
252 + $isGlobalProduct = false;
253 +
254 + if ( $product instanceof WC_Product ) {
255 + $isGlobalProduct = $product;
256 + }
257 +
258 + $layout = RenderHelpers::filter_layout( esc_html( $settings['layout'] ), $template );
259 +
260 + foreach ( $products as $_product ) {
242 261 $i++;
243 - $GLOBALS['product'] = $product;
262 + $GLOBALS['product'] = $_product;
244 263
245 264 /**
246 265 * Before product template render hook.
247 266 */
@@ -247,12 +266,12 @@
247 266 */
248 267 do_action( 'rtsb_before_product_template_render' );
249 268
250 269 // Loop arg.
251 - $arg = $this->arg_dataset( $settings, $product, $settings['lazy_load'], $i );
270 + $arg = $this->arg_dataset( $settings, $_product, $settings['lazy_load'], $i );
252 271
253 272 // Get template.
254 - $html .= Fns::load_template( $template . $settings['layout'], $arg, true );
273 + $html .= Fns::load_template( $template . $layout, $arg, true );
255 274
256 275 /**
257 276 * After product template render hook.
258 277 */
@@ -259,9 +278,11 @@
259 278 do_action( 'rtsb_after_product_template_render' );
260 279
261 280 unset( $GLOBALS['product'] );
262 281 }
263 -
282 + if ( $isGlobalProduct ) {
283 + $GLOBALS['product'] = $isGlobalProduct;
284 + }
264 285 return $html;
265 286 }
266 287
267 288 /**
@@ -279,19 +300,20 @@
279 300 if ( empty( $query ) ) {
280 301 return null;
281 302 }
282 303
283 - $i = 0;
304 + $i = 0;
305 + $layout = RenderHelpers::filter_layout( esc_html( $settings['layout'] ), $template );
284 306
285 307 while ( $query->have_posts() ) {
286 308 $query->the_post();
287 309 $i++;
288 -
310 + global $product;
289 311 // Loop arg.
290 - $arg = $this->arg_dataset( $settings, wc_get_product( get_the_ID() ), $settings['lazy_load'], $i );
312 + $arg = $this->arg_dataset( $settings, $product, $settings['lazy_load'], $i );
291 313
292 314 // Get template.
293 - $html .= Fns::load_template( $template . $settings['layout'], $arg, true );
315 + $html .= Fns::load_template( $template . $layout, $arg, true );
294 316 }
295 317
296 318 return $html;
297 319 }
@@ -368,10 +390,16 @@
368 390 do_action( 'rtsb/elements/render/before_query', $settings, $args );
369 391
370 392 // Query.
371 393 $product_query = new WC_Product_Query( $args );
372 - $products = $pagination ? $product_query->get_products()->products : $product_query->get_products();
373 394
395 + /**
396 + * Product query hook.
397 + */
398 + do_action( 'rtsb/elements/render/product_query', $product_query, $this );
399 +
400 + $products = $pagination ? $product_query->get_products()->products : $product_query->get_products();
401 +
374 402 if ( ! empty( $products ) ) {
375 403 // Start rendering.
376 404 $hook_html .= $this->render_start( $metas, $settings, $temp_args );
377 405
@@ -435,10 +463,9 @@
435 463 $pagination = $metas['pagination'];
436 464
437 465 $page_type = BuilderFns::builder_type( get_the_ID() );
438 466 $is_preview = BuilderFns::is_builder_preview() && array_key_exists( $page_type, BuilderFns::builder_page_types() );
439 - $posts_per_page = wc_get_default_products_per_row() * wc_get_default_product_rows_per_page();
440 -
467 + $posts_per_page = apply_filters( 'loop_shop_per_page', wc_get_default_products_per_row() * wc_get_default_product_rows_per_page() );
441 468 if ( $is_preview ) {
442 469 $main_query = clone $wp_query;
443 470 $main_post = clone $post;
444 471
@@ -477,9 +504,9 @@
477 504 // Pagination.
478 505 $this->html .= '<div class="rtsb-archive-pagination-wrap">';
479 506
480 507 if ( $pagination ) {
481 - if ( Fns::product_filters_has_ajax( 'shop' ) || Fns::product_filters_has_ajax( 'archive' ) ) {
508 + if ( Fns::product_filters_has_ajax( apply_filters( 'rtsb/builder/set/current/page/type', '' ) ) ) {
482 509 if ( $wp_query->max_num_pages > 1 ) {
483 510 $this->add_attribute(
484 511 'rtsb_load_more_btn_attr_' . $rand,
485 512 [
@@ -489,9 +516,9 @@
489 516 );
490 517
491 518 $this->html .=
492 519 "<div class='rtsb-load-more rtsb-pos-r'>
493 - <button '{$this->get_attribute_string( 'rtsb_load_more_btn_attr_' . $rand )}'>
520 + <button {$this->get_attribute_string( 'rtsb_load_more_btn_attr_' . $rand )}>
494 521 <span>" . esc_html__( 'Load More', 'shopbuilder' ) . "</span>
495 522 </button>
496 523 <div class='rtsb-loadmore-loading rtsb-ball-clip-rotate'><div></div></div>
497 524 </div>";
@@ -586,8 +613,11 @@
586 613
587 614 if ( rtsb()->has_pro() && ( ! empty( $metas['tag_term'] ) ) ) {
588 615 $term = $metas['tag_term'];
589 616 }
617 + if ( rtsb()->has_pro() && ( ! empty( $metas['brand_term'] ) ) ) {
618 + $term = $metas['brand_term'];
619 + }
590 620
591 621 // Query.
592 622 if ( $is_single ) {
593 623 if ( empty( $term ) ) {
@@ -688,8 +718,9 @@
688 718 *
689 719 * @return array
690 720 */
691 721 public function wp_arg_dataset( array $meta, int $post_id, bool $lazy_load = false, $i = 0 ) {
722 + // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict
692 723 if ( ( empty( $meta ) && ! $post_id ) || ! in_array( get_post_type( $post_id ), [ 'product_variation', 'product' ] ) ) {
693 724 return [];
694 725 }
695 726
@@ -788,9 +819,13 @@
788 819 $arg['image_link'] = $meta['image_link'];
789 820 $arg['grid'] .= 'rtsb-col-grid ';
790 821
791 822 if ( ! $fullwidth ) {
792 - $arg['class'] .= ' even-grid-item ';
823 + if ( 'masonry' === $meta['grid_type'] ) {
824 + $arg['class'] .= 'masonry-grid-item ';
825 + } else {
826 + $arg['class'] .= 'even-grid-item ';
827 + }
793 828 }
794 829
795 830 if ( ! empty( $meta['active_cat_slider'] ) ) {
796 831 $arg['class'] .= ' rtsb-slide-item swiper-slide animated rtFadeIn ';
@@ -864,8 +899,9 @@
864 899 $arg['anchor_class'] = null;
865 900 $arg['hover_img_html'] = null;
866 901 $arg['s_link'] = [];
867 902 $arg['add_to_cart'] = '';
903 + $arg['p_counter'] = absint( $i );
868 904 $arg['p_id'] = $post_id;
869 905 $arg['product'] = $product;
870 906 $arg['p_link'] = $product->get_permalink();
871 907 $arg['p_sale'] = '';
@@ -896,10 +932,15 @@
896 932 $is_list = preg_match( '/list/', $layout );
897 933 $is_carousel = preg_match( '/slider/', $layout );
898 934
899 935 if ( ! $is_carousel ) {
900 - $arg['grid'] .= 'rtsb-col-grid ';
901 - $arg['class'] .= ' even-grid-item ';
936 + $arg['grid'] .= 'rtsb-col-grid ';
937 +
938 + if ( 'masonry' === $meta['grid_type'] ) {
939 + $arg['class'] .= 'masonry-grid-item ';
940 + } else {
941 + $arg['class'] .= 'even-grid-item ';
942 + }
902 943 } else {
903 944 $arg['grid'] .= 'rtsb-col-grid rtsb-col-full rtsb-slide-item swiper-slide ';
904 945 }
905 946
@@ -919,18 +960,21 @@
919 960
920 961 $arg['class'] .= ' animated rtFadeIn';
921 962
922 963 if ( in_array( 'badges', $meta['visibility'], true ) ) {
923 - $arg['p_sale'] = Fns::get_promo_badge(
964 + $badge_module = ! empty( $meta['enable_badges_module'] );
965 + $arg['p_sale'] = Fns::is_module_active( 'product_badges' ) && $badge_module ? 'rtsb_yes' : Fns::get_promo_badge(
924 966 $product,
925 967 $meta['sale_badge_type'],
926 968 $meta['sale_badge_text'],
927 - $meta['out_of_stock_badge_text']
969 + apply_filters( 'rtsb/elementor/render/out_of_stock_badge_text', $meta['out_of_stock_badge_text'] )
928 970 );
929 971 }
930 972
931 973 $arg['badge_class'] = RenderHelpers::badge_class( $meta['badge_preset'] );
932 974
975 + $meta['add_to_cart_text'] = apply_filters( 'rtsb/elementor/render/add_to_cart_text', $meta['add_to_cart_text'], $product );
976 +
933 977 if ( ! $meta['show_cart_text'] ) {
934 978 $meta['add_to_cart_text'] = '';
935 979 $meta['add_to_cart_success_text'] = '';
936 980 }
@@ -956,8 +1000,9 @@
956 1000 'text' => $meta['add_to_cart_text'],
957 1001 'success' => $meta['add_to_cart_success_text'],
958 1002 'icon_html' => $cart_icon,
959 1003 'alignment' => $meta['add_to_cart_alignment'],
1004 + 'product' => $product,
960 1005 ]
961 1006 );
962 1007 }
963 1008 }
@@ -1042,9 +1087,10 @@
1042 1087 'type' => $type,
1043 1088 'text' => $text,
1044 1089 'success' => $success,
1045 1090 'icon_html' => $icon_html,
1046 - 'alignment' => $alignment
1091 + 'alignment' => $alignment,
1092 + 'product' => $product
1047 1093 ) = $args;
1048 1094
1049 1095 $content = null;
1050 1096 $rand = wp_rand();
@@ -1057,9 +1103,8 @@
1057 1103
1058 1104 $cart_attr = [
1059 1105 'class' => 'rtsb-action-btn ' . $type . '-product tipsy icon-' . $alignment . ' ' . ( empty( $text ) ? 'no-text' : 'has-text' ),
1060 1106 'href' => esc_url( $link ),
1061 - 'rel' => 'nofollow',
1062 1107 'data-quantity' => '1',
1063 1108 'data-product_id' => absint( $id ),
1064 1109 'data-id' => absint( $id ),
1065 1110 ];
@@ -1065,12 +1110,13 @@
1065 1110 ];
1066 1111
1067 1112 if ( empty( $text ) ) {
1068 1113 $tooltip_mapping = [
1069 - 'simple' => esc_attr__( 'Add to Cart', 'shopbuilder' ),
1070 - 'variable' => esc_attr__( 'Select Options', 'shopbuilder' ),
1071 - 'grouped' => esc_attr__( 'View Products', 'shopbuilder' ),
1072 - 'external' => ! empty( $btn_txt ) ? esc_html( $btn_txt ) : esc_html__( 'Buy Product', 'shopbuilder' ),
1114 + 'simple' => esc_attr__( 'Add to Cart', 'shopbuilder' ),
1115 + 'variable' => esc_attr__( 'Select Options', 'shopbuilder' ),
1116 + 'grouped' => esc_attr__( 'View Products', 'shopbuilder' ),
1117 + 'external' => ! empty( $btn_txt ) ? esc_html( $btn_txt ) : esc_html__( 'Buy Product', 'shopbuilder' ),
1118 + 'rtsb-gift-card' => esc_attr__( 'Select Gift Card', 'shopbuilder' ),
1073 1119 ];
1074 1120
1075 1121 $cart_attr['title'] = $tooltip_mapping[ $type ];
1076 1122 }
@@ -1075,13 +1121,22 @@
1075 1121 $cart_attr['title'] = $tooltip_mapping[ $type ];
1076 1122 }
1077 1123
1078 1124 $content .= '<div class="rtsb-wc-add-to-cart-wrap">';
1125 + if ( 'variable' === $type ) {
1126 + $cart_attr['class'] = $cart_attr['class'] . ' rtsb_vs_add_to_cart ';
1127 + }
1128 + if ( 'variable' === $type || 'grouped' === $type ) {
1129 + $cart_attr = apply_filters( 'rtsb/elementor/render/cart_attributes', $cart_attr, $product );
1079 1130
1080 - if ( 'variable' === $type || 'grouped' === $type ) {
1081 1131 $this->add_attribute( 'rtsb_add_to_cart_button_' . $id . $rand, $cart_attr );
1082 1132
1083 - $text_btn = 'grouped' === $type ? __( 'View Products', 'shopbuilder' ) : __( 'Select Options', 'shopbuilder' );
1133 + $text_btn = apply_filters(
1134 + 'rtsb/elementor/render/add_to_cart_text',
1135 + 'grouped' === $type ? __( 'View Products', 'shopbuilder' ) : __( 'Select Options', 'shopbuilder' ),
1136 + $product,
1137 + $type
1138 + );
1084 1139
1085 1140 $content .= '<a ' . $this->get_attribute_string( 'rtsb_add_to_cart_button_' . $id . $rand ) . '>';
1086 1141 $content .= $icon_html;
1087 1142 $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>';
@@ -1101,8 +1156,9 @@
1101 1156 } else {
1102 1157 $cart_attr['href'] = $cart_attr['href'] . '?add-to-cart=' . absint( $id );
1103 1158 $cart_attr['class'] .= ' rtsb-add-to-cart-btn';
1104 1159 $cart_attr['data-type'] = $type;
1160 + $cart_attr = apply_filters( 'rtsb/elementor/render/cart_attributes', $cart_attr, $product );
1105 1161
1106 1162 $this->add_attribute( 'rtsb_add_to_cart_button_' . $id . $rand, $cart_attr );
1107 1163
1108 1164 $content .= '<a ' . $this->get_attribute_string( 'rtsb_add_to_cart_button_' . $id . $rand ) . '>';
@@ -1280,9 +1336,9 @@
1280 1336 * @return void
1281 1337 */
1282 1338 public function no_products_msg( $metas ) {
1283 1339 $content =
1284 - '<div class="woocommerce-no-products-found">
1340 + '<div class="rtsb-notice woocommerce-no-products-found">
1285 1341 <div class="woocommerce-info">' . esc_html__( 'No products were found matching your selection.', 'shopbuilder' ) . '</div>
1286 1342 </div>';
1287 1343
1288 1344 $this->row( $content, $metas );
@@ -1370,8 +1426,46 @@
1370 1426 return $this;
1371 1427 }
1372 1428
1373 1429 /**
1430 + * Add link render attributes.
1431 + *
1432 + * @param array|string $element The HTML element.
1433 + * @param array $url_control Array of link settings.
1434 + * @param bool $overwrite Optional. Whether to overwrite existing
1435 + * attribute. Default is false, not to overwrite.
1436 + *
1437 + * @return Render
1438 + */
1439 + public function add_link_attributes( $element, array $url_control, $overwrite = false ) {
1440 + $attributes = [];
1441 +
1442 + if ( ! empty( $url_control['url'] ) ) {
1443 + $allowed_protocols = array_merge( wp_allowed_protocols(), [ 'skype', 'viber' ] );
1444 +
1445 + $attributes['href'] = esc_url( $url_control['url'], $allowed_protocols );
1446 + }
1447 +
1448 + if ( ! empty( $url_control['is_external'] ) ) {
1449 + $attributes['target'] = '_blank';
1450 + }
1451 +
1452 + if ( ! empty( $url_control['nofollow'] ) ) {
1453 + $attributes['rel'] = 'nofollow';
1454 + }
1455 +
1456 + if ( ! empty( $url_control['custom_attributes'] ) ) {
1457 + $attributes = array_merge( $attributes, Utils::parse_custom_attributes( $url_control['custom_attributes'] ) );
1458 + }
1459 +
1460 + if ( $attributes ) {
1461 + $this->add_attribute( $element, $attributes, null, $overwrite );
1462 + }
1463 +
1464 + return $this;
1465 + }
1466 +
1467 + /**
1374 1468 * Get render attribute string.
1375 1469 *
1376 1470 * @param string $element The element.
1377 1471 *
@@ -1381,7 +1475,54 @@
1381 1475 if ( empty( $this->attributes[ $element ] ) ) {
1382 1476 return '';
1383 1477 }
1384 1478
1385 - return Utils::render_html_attributes( $this->attributes[ $element ] );
1479 + if ( class_exists( '\Elementor\Utils' ) ) {
1480 + return Utils::render_html_attributes( $this->attributes[ $element ] );
1481 + }
1482 +
1483 + // Fallback.
1484 + return $this->render_fallback_attributes( $this->attributes[ $element ] );
1485 + }
1486 +
1487 + /**
1488 + * Function to render link attributes.
1489 + *
1490 + * @param string $id Element ID.
1491 + * @param array $control_name Control name.
1492 + * @param string $class_name Class name.
1493 + *
1494 + * @return string
1495 + */
1496 + public function render_link_attributes( $id, $control_name, $class_name = 'logo-img-link' ) {
1497 + $this->add_attribute( $id, 'class', $class_name );
1498 +
1499 + if ( ! empty( $control_name['url'] ) ) {
1500 + $this->add_link_attributes( $id, $control_name );
1501 + } else {
1502 + $this->add_attribute( $id, 'role', 'button' );
1503 + }
1504 +
1505 + return $this->get_attribute_string( $id );
1506 + }
1507 +
1508 + /**
1509 + * Render fallback attributes.
1510 + *
1511 + * @param array $attributes The attributes.
1512 + *
1513 + * @return string
1514 + */
1515 + private function render_fallback_attributes( $attributes ) {
1516 + $rendered_attributes = [];
1517 +
1518 + foreach ( $attributes as $attribute_key => $attribute_values ) {
1519 + if ( is_array( $attribute_values ) ) {
1520 + $attribute_values = implode( ' ', $attribute_values );
1521 + }
1522 +
1523 + $rendered_attributes[] = sprintf( '%1$s="%2$s"', $attribute_key, esc_attr( $attribute_values ) );
1524 + }
1525 +
1526 + return implode( ' ', $rendered_attributes );
1386 1527 }
1387 1528 }