PluginProbe
ShopBuilder – WooCommerce Builder For Elementor / 3.2.3
ShopBuilder – WooCommerce Builder For Elementor v3.2.3
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 +150 -28 2.2.13.2.3 View file →
@@ -93,8 +93,9 @@
93 93 $view_mode = isset( $_GET['displayview'] ) ? sanitize_text_field( wp_unslash( $_GET['displayview'] ) ) : $view_mode; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
94 94 $tooltip_attr = 'list' == $view_mode && ! empty( $metas['tooltip_position_list'] ) ? esc_attr( $metas['tooltip_position_list'] ) : $metas['tooltip_position'];
95 95 $has_pro = rtsb()->has_pro();
96 96 $has_filters = $has_pro && ! empty( $settings['tax_filter'] );
97 + $grid_style = $settings['grid_style'] ?? 'even';
97 98
98 99 // Set default layout.
99 100 if ( ! $has_pro && ! in_array( $layout, array_keys( Fns::free_layouts() ), true ) ) {
100 101 $layout = RenderHelpers::set_default_layout( $layout );
@@ -99,8 +100,13 @@
99 100 if ( ! $has_pro && ! in_array( $layout, array_keys( Fns::free_layouts() ), true ) ) {
100 101 $layout = RenderHelpers::set_default_layout( $layout );
101 102 }
102 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 +
103 109 // Container classes.
104 110 $container_classes = RenderHelpers::prepare_container_classes( $metas, $settings, $class );
105 111
106 112 // Pagination attributes.
@@ -141,8 +147,9 @@
141 147 $script_generator = [];
142 148 $script_generator['layout'] = $layout_id;
143 149 $script_generator['rand'] = absint( $rand );
144 150 $script_generator['isCarousel'] = preg_match( '/slider/', $layout );
151 + $script_generator['isMasonry'] = $has_pro && 'masonry' === $grid_style;
145 152
146 153 // Register scripts in the wp_footer action.
147 154 add_action(
148 155 'wp_footer',
@@ -188,9 +195,8 @@
188 195 $this->html = ! empty( $settings['show_filter'] ) ? $hook_html . $start_wrapper : $start_wrapper;
189 196
190 197 if ( $is_carousel ) {
191 198 $slider_data = RenderHelpers::slider_data( $settings, $raw_settings );
192 -
193 199 // Adding slider render attributes.
194 200 $this->add_attribute(
195 201 'rtsb_slider_attr_' . $rand,
196 202 [
@@ -240,13 +246,21 @@
240 246 }
241 247
242 248 $i = 0;
243 249
244 - 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 ) {
245 261 $i++;
246 - $GLOBALS['product'] = $product;
247 - $layout = RenderHelpers::filter_layout( esc_html( $settings['layout'] ), $template );
248 -
262 + $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.
249 263 /**
250 264 * Before product template render hook.
251 265 */
252 266 do_action( 'rtsb_before_product_template_render' );
@@ -251,9 +265,9 @@
251 265 */
252 266 do_action( 'rtsb_before_product_template_render' );
253 267
254 268 // Loop arg.
255 - $arg = $this->arg_dataset( $settings, $product, $settings['lazy_load'], $i );
269 + $arg = $this->arg_dataset( $settings, $_product, $settings['lazy_load'], $i );
256 270
257 271 // Get template.
258 272 $html .= Fns::load_template( $template . $layout, $arg, true );
259 273
@@ -263,9 +277,11 @@
263 277 do_action( 'rtsb_after_product_template_render' );
264 278
265 279 unset( $GLOBALS['product'] );
266 280 }
267 -
281 + if ( $isGlobalProduct ) {
282 + $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.
283 + }
268 284 return $html;
269 285 }
270 286
271 287 /**
@@ -283,19 +299,20 @@
283 299 if ( empty( $query ) ) {
284 300 return null;
285 301 }
286 302
287 - $i = 0;
303 + $i = 0;
304 + $layout = RenderHelpers::filter_layout( esc_html( $settings['layout'] ), $template );
288 305
289 306 while ( $query->have_posts() ) {
290 307 $query->the_post();
291 308 $i++;
292 -
309 + global $product;
293 310 // Loop arg.
294 - $arg = $this->arg_dataset( $settings, wc_get_product( get_the_ID() ), $settings['lazy_load'], $i );
311 + $arg = $this->arg_dataset( $settings, $product, $settings['lazy_load'], $i );
295 312
296 313 // Get template.
297 - $html .= Fns::load_template( $template . $settings['layout'], $arg, true );
314 + $html .= Fns::load_template( $template . $layout, $arg, true );
298 315 }
299 316
300 317 return $html;
301 318 }
@@ -368,10 +385,10 @@
368 385 * Before Product query hook.
369 386 *
370 387 * @hooked RadiusTheme\SB\Controllers\Hooks\ActionHooks::modify_wc_query_args 10
371 388 */
389 +
372 390 do_action( 'rtsb/elements/render/before_query', $settings, $args );
373 -
374 391 // Query.
375 392 $product_query = new WC_Product_Query( $args );
376 393
377 394 /**
@@ -438,17 +455,17 @@
438 455 $this->get_settings = $settings;
439 456 $rand = wp_rand();
440 457 $metas = RenderHelpers::meta_dataset( $settings, $template, $widget->get_settings_for_display() );
441 458
442 - $settings['rtsb_order'] = ! empty( $wp_query->query_vars['order'] ) ? $wp_query->query_vars['order'] : 'ASC';
443 - $settings['rtsb_orderby'] = ! empty( $wp_query->query_vars['orderby'] ) ? $wp_query->query_vars['orderby'] : 'menu_order';
459 + $order = strtoupper( $wp_query->query_vars['order'] ?? 'ASC' );
460 + $settings['rtsb_orderby'] = RenderHelpers::escaping_product_orderby( $wp_query->query_vars['orderby'] ?? 'menu_order' );
461 + $settings['rtsb_order'] = in_array( $order, [ 'ASC','DESC' ], true ) ? $order : 'ASC';
444 462
445 463 $pagination = $metas['pagination'];
446 464
447 465 $page_type = BuilderFns::builder_type( get_the_ID() );
448 466 $is_preview = BuilderFns::is_builder_preview() && array_key_exists( $page_type, BuilderFns::builder_page_types() );
449 - $posts_per_page = wc_get_default_products_per_row() * wc_get_default_product_rows_per_page();
450 -
467 + $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.
451 468 if ( $is_preview ) {
452 469 $main_query = clone $wp_query;
453 470 $main_post = clone $post;
454 471
@@ -596,8 +613,11 @@
596 613
597 614 if ( rtsb()->has_pro() && ( ! empty( $metas['tag_term'] ) ) ) {
598 615 $term = $metas['tag_term'];
599 616 }
617 + if ( rtsb()->has_pro() && ( ! empty( $metas['brand_term'] ) ) ) {
618 + $term = $metas['brand_term'];
619 + }
600 620
601 621 // Query.
602 622 if ( $is_single ) {
603 623 if ( empty( $term ) ) {
@@ -698,8 +718,9 @@
698 718 *
699 719 * @return array
700 720 */
701 721 public function wp_arg_dataset( array $meta, int $post_id, bool $lazy_load = false, $i = 0 ) {
722 + // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict
702 723 if ( ( empty( $meta ) && ! $post_id ) || ! in_array( get_post_type( $post_id ), [ 'product_variation', 'product' ] ) ) {
703 724 return [];
704 725 }
705 726
@@ -705,9 +726,9 @@
705 726
706 727 global $product;
707 728
708 729 if ( ! $product instanceof WC_Product && $post_id ) {
709 - $product = wc_get_product( $post_id );
730 + $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.
710 731 }
711 732
712 733 $post_id = $product->get_id();
713 734 $arg = $this->build_product_arg(
@@ -798,9 +819,13 @@
798 819 $arg['image_link'] = $meta['image_link'];
799 820 $arg['grid'] .= 'rtsb-col-grid ';
800 821
801 822 if ( ! $fullwidth ) {
802 - $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 + }
803 828 }
804 829
805 830 if ( ! empty( $meta['active_cat_slider'] ) ) {
806 831 $arg['class'] .= ' rtsb-slide-item swiper-slide animated rtFadeIn ';
@@ -907,10 +932,15 @@
907 932 $is_list = preg_match( '/list/', $layout );
908 933 $is_carousel = preg_match( '/slider/', $layout );
909 934
910 935 if ( ! $is_carousel ) {
911 - $arg['grid'] .= 'rtsb-col-grid ';
912 - $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 + }
913 943 } else {
914 944 $arg['grid'] .= 'rtsb-col-grid rtsb-col-full rtsb-slide-item swiper-slide ';
915 945 }
916 946
@@ -1073,9 +1103,8 @@
1073 1103
1074 1104 $cart_attr = [
1075 1105 'class' => 'rtsb-action-btn ' . $type . '-product tipsy icon-' . $alignment . ' ' . ( empty( $text ) ? 'no-text' : 'has-text' ),
1076 1106 'href' => esc_url( $link ),
1077 - 'rel' => 'nofollow',
1078 1107 'data-quantity' => '1',
1079 1108 'data-product_id' => absint( $id ),
1080 1109 'data-id' => absint( $id ),
1081 1110 ];
@@ -1081,12 +1110,13 @@
1081 1110 ];
1082 1111
1083 1112 if ( empty( $text ) ) {
1084 1113 $tooltip_mapping = [
1085 - 'simple' => esc_attr__( 'Add to Cart', 'shopbuilder' ),
1086 - 'variable' => esc_attr__( 'Select Options', 'shopbuilder' ),
1087 - 'grouped' => esc_attr__( 'View Products', 'shopbuilder' ),
1088 - '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' ),
1089 1119 ];
1090 1120
1091 1121 $cart_attr['title'] = $tooltip_mapping[ $type ];
1092 1122 }
@@ -1091,15 +1121,22 @@
1091 1121 $cart_attr['title'] = $tooltip_mapping[ $type ];
1092 1122 }
1093 1123
1094 1124 $content .= '<div class="rtsb-wc-add-to-cart-wrap">';
1095 -
1125 + if ( 'variable' === $type ) {
1126 + $cart_attr['class'] = $cart_attr['class'] . ' rtsb_vs_add_to_cart ';
1127 + }
1096 1128 if ( 'variable' === $type || 'grouped' === $type ) {
1097 1129 $cart_attr = apply_filters( 'rtsb/elementor/render/cart_attributes', $cart_attr, $product );
1098 1130
1099 1131 $this->add_attribute( 'rtsb_add_to_cart_button_' . $id . $rand, $cart_attr );
1100 1132
1101 - $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 + );
1102 1139
1103 1140 $content .= '<a ' . $this->get_attribute_string( 'rtsb_add_to_cart_button_' . $id . $rand ) . '>';
1104 1141 $content .= $icon_html;
1105 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>';
@@ -1389,8 +1426,46 @@
1389 1426 return $this;
1390 1427 }
1391 1428
1392 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 + /**
1393 1468 * Get render attribute string.
1394 1469 *
1395 1470 * @param string $element The element.
1396 1471 *
@@ -1400,7 +1475,54 @@
1400 1475 if ( empty( $this->attributes[ $element ] ) ) {
1401 1476 return '';
1402 1477 }
1403 1478
1404 - 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 );
1405 1527 }
1406 1528 }