PluginProbe
ShopBuilder – WooCommerce Builder For Elementor / 3.2.6
ShopBuilder – WooCommerce Builder For Elementor v3.2.6
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 +222 -46 2.1.133.2.6 View file →
@@ -8,8 +8,9 @@
8 8 */
9 9
10 10 namespace RadiusTheme\SB\Elementor\Render;
11 11
12 +use RadiusTheme\SBPRO\Modules\CatalogMode\CatalogModeFns;
12 13 use WC_Query;
13 14 use WP_Query;
14 15 use WC_Product;
15 16 use Elementor\Utils;
@@ -93,8 +94,9 @@
93 94 $view_mode = isset( $_GET['displayview'] ) ? sanitize_text_field( wp_unslash( $_GET['displayview'] ) ) : $view_mode; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
94 95 $tooltip_attr = 'list' == $view_mode && ! empty( $metas['tooltip_position_list'] ) ? esc_attr( $metas['tooltip_position_list'] ) : $metas['tooltip_position'];
95 96 $has_pro = rtsb()->has_pro();
96 97 $has_filters = $has_pro && ! empty( $settings['tax_filter'] );
98 + $grid_style = $settings['grid_style'] ?? 'even';
97 99
98 100 // Set default layout.
99 101 if ( ! $has_pro && ! in_array( $layout, array_keys( Fns::free_layouts() ), true ) ) {
100 102 $layout = RenderHelpers::set_default_layout( $layout );
@@ -99,8 +101,13 @@
99 101 if ( ! $has_pro && ! in_array( $layout, array_keys( Fns::free_layouts() ), true ) ) {
100 102 $layout = RenderHelpers::set_default_layout( $layout );
101 103 }
102 104
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 ) );
108 + }
109 +
103 110 // Container classes.
104 111 $container_classes = RenderHelpers::prepare_container_classes( $metas, $settings, $class );
105 112
106 113 // Pagination attributes.
@@ -109,9 +116,9 @@
109 116 // Attributes for the container.
110 117 $container_attributes = [
111 118 'id' => $layout_id,
112 119 'class' => $container_classes,
113 - 'style' => $style_attr,
120 + 'style' => apply_filters( 'rtsb/product/container/attr', $style_attr, $settings ),
114 121 'data-layout' => $layout,
115 122 'data-tooltip-position' => $tooltip_attr,
116 123 ];
117 124
@@ -141,8 +148,9 @@
141 148 $script_generator = [];
142 149 $script_generator['layout'] = $layout_id;
143 150 $script_generator['rand'] = absint( $rand );
144 151 $script_generator['isCarousel'] = preg_match( '/slider/', $layout );
152 + $script_generator['isMasonry'] = $has_pro && 'masonry' === $grid_style;
145 153
146 154 // Register scripts in the wp_footer action.
147 155 add_action(
148 156 'wp_footer',
@@ -188,9 +196,8 @@
188 196 $this->html = ! empty( $settings['show_filter'] ) ? $hook_html . $start_wrapper : $start_wrapper;
189 197
190 198 if ( $is_carousel ) {
191 199 $slider_data = RenderHelpers::slider_data( $settings, $raw_settings );
192 -
193 200 // Adding slider render attributes.
194 201 $this->add_attribute(
195 202 'rtsb_slider_attr_' . $rand,
196 203 [
@@ -240,13 +247,21 @@
240 247 }
241 248
242 249 $i = 0;
243 250
244 - foreach ( $products as $product ) {
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 ) {
245 262 $i++;
246 - $GLOBALS['product'] = $product;
247 - $layout = RenderHelpers::filter_layout( esc_html( $settings['layout'] ), $template );
248 -
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.
249 264 /**
250 265 * Before product template render hook.
251 266 */
252 267 do_action( 'rtsb_before_product_template_render' );
@@ -251,9 +266,9 @@
251 266 */
252 267 do_action( 'rtsb_before_product_template_render' );
253 268
254 269 // Loop arg.
255 - $arg = $this->arg_dataset( $settings, $product, $settings['lazy_load'], $i );
270 + $arg = $this->arg_dataset( $settings, $_product, $settings['lazy_load'], $i );
256 271
257 272 // Get template.
258 273 $html .= Fns::load_template( $template . $layout, $arg, true );
259 274
@@ -263,9 +278,11 @@
263 278 do_action( 'rtsb_after_product_template_render' );
264 279
265 280 unset( $GLOBALS['product'] );
266 281 }
267 -
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 + }
268 285 return $html;
269 286 }
270 287
271 288 /**
@@ -283,19 +300,20 @@
283 300 if ( empty( $query ) ) {
284 301 return null;
285 302 }
286 303
287 - $i = 0;
304 + $i = 0;
305 + $layout = RenderHelpers::filter_layout( esc_html( $settings['layout'] ), $template );
288 306
289 307 while ( $query->have_posts() ) {
290 308 $query->the_post();
291 309 $i++;
292 -
310 + global $product;
293 311 // Loop arg.
294 - $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 );
295 313
296 314 // Get template.
297 - $html .= Fns::load_template( $template . $settings['layout'], $arg, true );
315 + $html .= Fns::load_template( $template . $layout, $arg, true );
298 316 }
299 317
300 318 return $html;
301 319 }
@@ -368,14 +386,20 @@
368 386 * Before Product query hook.
369 387 *
370 388 * @hooked RadiusTheme\SB\Controllers\Hooks\ActionHooks::modify_wc_query_args 10
371 389 */
390 +
372 391 do_action( 'rtsb/elements/render/before_query', $settings, $args );
373 -
374 392 // Query.
375 393 $product_query = new WC_Product_Query( $args );
376 - $products = $pagination ? $product_query->get_products()->products : $product_query->get_products();
377 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 +
378 402 if ( ! empty( $products ) ) {
379 403 // Start rendering.
380 404 $hook_html .= $this->render_start( $metas, $settings, $temp_args );
381 405
@@ -432,17 +456,17 @@
432 456 $this->get_settings = $settings;
433 457 $rand = wp_rand();
434 458 $metas = RenderHelpers::meta_dataset( $settings, $template, $widget->get_settings_for_display() );
435 459
436 - $settings['rtsb_order'] = ! empty( $wp_query->query_vars['order'] ) ? $wp_query->query_vars['order'] : 'ASC';
437 - $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';
438 463
439 464 $pagination = $metas['pagination'];
440 465
441 466 $page_type = BuilderFns::builder_type( get_the_ID() );
442 467 $is_preview = BuilderFns::is_builder_preview() && array_key_exists( $page_type, BuilderFns::builder_page_types() );
443 - $posts_per_page = wc_get_default_products_per_row() * wc_get_default_product_rows_per_page();
444 -
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.
445 469 if ( $is_preview ) {
446 470 $main_query = clone $wp_query;
447 471 $main_post = clone $post;
448 472
@@ -483,23 +507,39 @@
483 507
484 508 if ( $pagination ) {
485 509 if ( Fns::product_filters_has_ajax( apply_filters( 'rtsb/builder/set/current/page/type', '' ) ) ) {
486 510 if ( $wp_query->max_num_pages > 1 ) {
487 - $this->add_attribute(
488 - 'rtsb_load_more_btn_attr_' . $rand,
489 - [
490 - 'data-paged' => '2',
491 - 'data-max-page' => $wp_query->max_num_pages,
492 - ]
493 - );
511 + $posts_loading_type = ! empty( $metas['posts_loading_type'] ) ? $metas['posts_loading_type'] : 'pagination';
494 512
495 - $this->html .=
496 - "<div class='rtsb-load-more rtsb-pos-r'>
497 - <button {$this->get_attribute_string( 'rtsb_load_more_btn_attr_' . $rand )}>
498 - <span>" . esc_html__( 'Load More', 'shopbuilder' ) . "</span>
499 - </button>
500 - <div class='rtsb-loadmore-loading rtsb-ball-clip-rotate'><div></div></div>
501 - </div>";
513 + if ( 'pagination_ajax' === $posts_loading_type ) {
514 + $this->html .= Fns::custom_pagination( $wp_query->max_num_pages, $posts_per_page, true );
515 + } elseif ( 'pagination' === $posts_loading_type ) {
516 + $this->html .= Fns::custom_pagination( $wp_query->max_num_pages, $posts_per_page, false );
517 + } elseif ( 'load_on_scroll' === $posts_loading_type ) {
518 + $this->html .= "<div class='rtsb-infinite-scroll' data-trigger='1' data-paged='2' data-max-page='{$wp_query->max_num_pages}'>
519 + <div class='rtsb-infinite-action'>
520 + <div class='rtsb-loadmore-loading rtsb-ball-clip-rotate'><div></div></div>
521 + </div>
522 + </div>";
523 + } else {
524 + $load_more_text = ! empty( $metas['load_more_text'] ) ? $metas['load_more_text'] : esc_html__( 'Load More', 'shopbuilder' );
525 +
526 + $this->add_attribute(
527 + 'rtsb_load_more_btn_attr_' . $rand,
528 + [
529 + 'data-paged' => '2',
530 + 'data-max-page' => $wp_query->max_num_pages,
531 + ]
532 + );
533 +
534 + $this->html .=
535 + "<div class='rtsb-load-more rtsb-pos-r'>
536 + <button {$this->get_attribute_string( 'rtsb_load_more_btn_attr_' . $rand )}>
537 + <span>" . esc_html( $load_more_text ) . "</span>
538 + </button>
539 + <div class='rtsb-loadmore-loading rtsb-ball-clip-rotate'><div></div></div>
540 + </div>";
541 + }
502 542 }
503 543 } else {
504 544 $pagination_args = [
505 545 'query' => $wp_query,
@@ -590,8 +630,11 @@
590 630
591 631 if ( rtsb()->has_pro() && ( ! empty( $metas['tag_term'] ) ) ) {
592 632 $term = $metas['tag_term'];
593 633 }
634 + if ( rtsb()->has_pro() && ( ! empty( $metas['brand_term'] ) ) ) {
635 + $term = $metas['brand_term'];
636 + }
594 637
595 638 // Query.
596 639 if ( $is_single ) {
597 640 if ( empty( $term ) ) {
@@ -692,8 +735,9 @@
692 735 *
693 736 * @return array
694 737 */
695 738 public function wp_arg_dataset( array $meta, int $post_id, bool $lazy_load = false, $i = 0 ) {
739 + // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict
696 740 if ( ( empty( $meta ) && ! $post_id ) || ! in_array( get_post_type( $post_id ), [ 'product_variation', 'product' ] ) ) {
697 741 return [];
698 742 }
699 743
@@ -699,9 +743,9 @@
699 743
700 744 global $product;
701 745
702 746 if ( ! $product instanceof WC_Product && $post_id ) {
703 - $product = wc_get_product( $post_id );
747 + $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.
704 748 }
705 749
706 750 $post_id = $product->get_id();
707 751 $arg = $this->build_product_arg(
@@ -792,9 +836,13 @@
792 836 $arg['image_link'] = $meta['image_link'];
793 837 $arg['grid'] .= 'rtsb-col-grid ';
794 838
795 839 if ( ! $fullwidth ) {
796 - $arg['class'] .= ' even-grid-item ';
840 + if ( 'masonry' === $meta['grid_type'] ) {
841 + $arg['class'] .= 'masonry-grid-item ';
842 + } else {
843 + $arg['class'] .= 'even-grid-item ';
844 + }
797 845 }
798 846
799 847 if ( ! empty( $meta['active_cat_slider'] ) ) {
800 848 $arg['class'] .= ' rtsb-slide-item swiper-slide animated rtFadeIn ';
@@ -868,8 +916,9 @@
868 916 $arg['anchor_class'] = null;
869 917 $arg['hover_img_html'] = null;
870 918 $arg['s_link'] = [];
871 919 $arg['add_to_cart'] = '';
920 + $arg['p_counter'] = absint( $i );
872 921 $arg['p_id'] = $post_id;
873 922 $arg['product'] = $product;
874 923 $arg['p_link'] = $product->get_permalink();
875 924 $arg['p_sale'] = '';
@@ -900,10 +949,15 @@
900 949 $is_list = preg_match( '/list/', $layout );
901 950 $is_carousel = preg_match( '/slider/', $layout );
902 951
903 952 if ( ! $is_carousel ) {
904 - $arg['grid'] .= 'rtsb-col-grid ';
905 - $arg['class'] .= ' even-grid-item ';
953 + $arg['grid'] .= 'rtsb-col-grid ';
954 +
955 + if ( 'masonry' === $meta['grid_type'] ) {
956 + $arg['class'] .= 'masonry-grid-item ';
957 + } else {
958 + $arg['class'] .= 'even-grid-item ';
959 + }
906 960 } else {
907 961 $arg['grid'] .= 'rtsb-col-grid rtsb-col-full rtsb-slide-item swiper-slide ';
908 962 }
909 963
@@ -934,8 +988,10 @@
934 988 }
935 989
936 990 $arg['badge_class'] = RenderHelpers::badge_class( $meta['badge_preset'] );
937 991
992 + $meta['add_to_cart_text'] = apply_filters( 'rtsb/elementor/render/add_to_cart_text', $meta['add_to_cart_text'], $product );
993 +
938 994 if ( ! $meta['show_cart_text'] ) {
939 995 $meta['add_to_cart_text'] = '';
940 996 $meta['add_to_cart_success_text'] = '';
941 997 }
@@ -961,12 +1017,28 @@
961 1017 'text' => $meta['add_to_cart_text'],
962 1018 'success' => $meta['add_to_cart_success_text'],
963 1019 'icon_html' => $cart_icon,
964 1020 'alignment' => $meta['add_to_cart_alignment'],
1021 + 'product' => $product,
965 1022 ]
966 1023 );
967 1024 }
968 1025 }
1026 + if ( Fns::is_catalog_mode() && empty( $arg['add_to_cart'] ) ) {
1027 + if ( ! empty( $meta['add_to_cart_icon'] ) ) {
1028 + $cart_icon_html = Fns::icons_manager( $meta['add_to_cart_icon'], 'cart-icon' );
1029 + }
1030 + $cart_icon = ! empty( $cart_icon_html ) ? '<span class="icon">' . $cart_icon_html . '</span>' : '';
1031 + $arg['add_to_cart'] = CatalogModeFns::render_elementor_widget_catalog_mode_button(
1032 + [
1033 + 'id' => $post_id,
1034 + 'type' => $p_type,
1035 + 'text' => $meta['add_to_cart_text'],
1036 + 'icon_html' => $cart_icon,
1037 + 'alignment' => $meta['add_to_cart_alignment'],
1038 + ]
1039 + );
1040 + }
969 1041 }
970 1042
971 1043 if ( in_array( 'quick_view', $meta['visibility'], true ) ) {
972 1044 add_filter( 'rtsb/module/quick_view/button_params', [ $this, 'button_classes' ] );
@@ -1031,9 +1103,8 @@
1031 1103 ];
1032 1104
1033 1105 return $arg;
1034 1106 }
1035 -
1036 1107 /**
1037 1108 * Renders add to cart button.
1038 1109 *
1039 1110 * @param array $args Cart args.
@@ -1047,9 +1118,10 @@
1047 1118 'type' => $type,
1048 1119 'text' => $text,
1049 1120 'success' => $success,
1050 1121 'icon_html' => $icon_html,
1051 - 'alignment' => $alignment
1122 + 'alignment' => $alignment,
1123 + 'product' => $product
1052 1124 ) = $args;
1053 1125
1054 1126 $content = null;
1055 1127 $rand = wp_rand();
@@ -1062,9 +1134,8 @@
1062 1134
1063 1135 $cart_attr = [
1064 1136 'class' => 'rtsb-action-btn ' . $type . '-product tipsy icon-' . $alignment . ' ' . ( empty( $text ) ? 'no-text' : 'has-text' ),
1065 1137 'href' => esc_url( $link ),
1066 - 'rel' => 'nofollow',
1067 1138 'data-quantity' => '1',
1068 1139 'data-product_id' => absint( $id ),
1069 1140 'data-id' => absint( $id ),
1070 1141 ];
@@ -1070,12 +1141,13 @@
1070 1141 ];
1071 1142
1072 1143 if ( empty( $text ) ) {
1073 1144 $tooltip_mapping = [
1074 - 'simple' => esc_attr__( 'Add to Cart', 'shopbuilder' ),
1075 - 'variable' => esc_attr__( 'Select Options', 'shopbuilder' ),
1076 - 'grouped' => esc_attr__( 'View Products', 'shopbuilder' ),
1077 - 'external' => ! empty( $btn_txt ) ? esc_html( $btn_txt ) : esc_html__( 'Buy Product', 'shopbuilder' ),
1145 + 'simple' => esc_attr__( 'Add to Cart', 'shopbuilder' ),
1146 + 'variable' => esc_attr__( 'Select Options', 'shopbuilder' ),
1147 + 'grouped' => esc_attr__( 'View Products', 'shopbuilder' ),
1148 + 'external' => ! empty( $btn_txt ) ? esc_html( $btn_txt ) : esc_html__( 'Buy Product', 'shopbuilder' ),
1149 + 'rtsb-gift-card' => esc_attr__( 'Select Gift Card', 'shopbuilder' ),
1078 1150 ];
1079 1151
1080 1152 $cart_attr['title'] = $tooltip_mapping[ $type ];
1081 1153 }
@@ -1080,13 +1152,22 @@
1080 1152 $cart_attr['title'] = $tooltip_mapping[ $type ];
1081 1153 }
1082 1154
1083 1155 $content .= '<div class="rtsb-wc-add-to-cart-wrap">';
1156 + if ( 'variable' === $type ) {
1157 + $cart_attr['class'] = $cart_attr['class'] . ' rtsb_vs_add_to_cart ';
1158 + }
1159 + if ( 'variable' === $type || 'grouped' === $type ) {
1160 + $cart_attr = apply_filters( 'rtsb/elementor/render/cart_attributes', $cart_attr, $product );
1084 1161
1085 - if ( 'variable' === $type || 'grouped' === $type ) {
1086 1162 $this->add_attribute( 'rtsb_add_to_cart_button_' . $id . $rand, $cart_attr );
1087 1163
1088 - $text_btn = 'grouped' === $type ? __( 'View Products', 'shopbuilder' ) : __( 'Select Options', 'shopbuilder' );
1164 + $text_btn = apply_filters(
1165 + 'rtsb/elementor/render/add_to_cart_text',
1166 + 'grouped' === $type ? __( 'View Products', 'shopbuilder' ) : __( 'Select Options', 'shopbuilder' ),
1167 + $product,
1168 + $type
1169 + );
1089 1170
1090 1171 $content .= '<a ' . $this->get_attribute_string( 'rtsb_add_to_cart_button_' . $id . $rand ) . '>';
1091 1172 $content .= $icon_html;
1092 1173 $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>';
@@ -1106,8 +1187,9 @@
1106 1187 } else {
1107 1188 $cart_attr['href'] = $cart_attr['href'] . '?add-to-cart=' . absint( $id );
1108 1189 $cart_attr['class'] .= ' rtsb-add-to-cart-btn';
1109 1190 $cart_attr['data-type'] = $type;
1191 + $cart_attr = apply_filters( 'rtsb/elementor/render/cart_attributes', $cart_attr, $product );
1110 1192
1111 1193 $this->add_attribute( 'rtsb_add_to_cart_button_' . $id . $rand, $cart_attr );
1112 1194
1113 1195 $content .= '<a ' . $this->get_attribute_string( 'rtsb_add_to_cart_button_' . $id . $rand ) . '>';
@@ -1218,8 +1300,17 @@
1218 1300 $total_page,
1219 1301 'wc' === $query_type ? $per_page : $posts_per_page
1220 1302 );
1221 1303 }
1304 + } elseif ( 'pagination_ajax' === $meta['posts_loading_type'] && ! rtsb()->has_pro() ) {
1305 + // Free version fallback: render non-AJAX number pagination so filter
1306 + // params in the URL are preserved via get_pagenum_link().
1307 + if ( $is_grid ) {
1308 + $html_utility .= Fns::custom_pagination(
1309 + $total_page,
1310 + 'wc' === $query_type ? $per_page : $posts_per_page
1311 + );
1312 + }
1222 1313 } else {
1223 1314 ob_start();
1224 1315 do_action( 'rtsb/elementor/render/pagination', $meta, $query, $args );
1225 1316 $html_utility .= ob_get_clean();
@@ -1375,8 +1466,46 @@
1375 1466 return $this;
1376 1467 }
1377 1468
1378 1469 /**
1470 + * Add link render attributes.
1471 + *
1472 + * @param array|string $element The HTML element.
1473 + * @param array $url_control Array of link settings.
1474 + * @param bool $overwrite Optional. Whether to overwrite existing
1475 + * attribute. Default is false, not to overwrite.
1476 + *
1477 + * @return Render
1478 + */
1479 + public function add_link_attributes( $element, array $url_control, $overwrite = false ) {
1480 + $attributes = [];
1481 +
1482 + if ( ! empty( $url_control['url'] ) ) {
1483 + $allowed_protocols = array_merge( wp_allowed_protocols(), [ 'skype', 'viber' ] );
1484 +
1485 + $attributes['href'] = esc_url( $url_control['url'], $allowed_protocols );
1486 + }
1487 +
1488 + if ( ! empty( $url_control['is_external'] ) ) {
1489 + $attributes['target'] = '_blank';
1490 + }
1491 +
1492 + if ( ! empty( $url_control['nofollow'] ) ) {
1493 + $attributes['rel'] = 'nofollow';
1494 + }
1495 +
1496 + if ( ! empty( $url_control['custom_attributes'] ) ) {
1497 + $attributes = array_merge( $attributes, Utils::parse_custom_attributes( $url_control['custom_attributes'] ) );
1498 + }
1499 +
1500 + if ( $attributes ) {
1501 + $this->add_attribute( $element, $attributes, null, $overwrite );
1502 + }
1503 +
1504 + return $this;
1505 + }
1506 +
1507 + /**
1379 1508 * Get render attribute string.
1380 1509 *
1381 1510 * @param string $element The element.
1382 1511 *
@@ -1386,7 +1515,54 @@
1386 1515 if ( empty( $this->attributes[ $element ] ) ) {
1387 1516 return '';
1388 1517 }
1389 1518
1390 - return Utils::render_html_attributes( $this->attributes[ $element ] );
1519 + if ( class_exists( '\Elementor\Utils' ) ) {
1520 + return Utils::render_html_attributes( $this->attributes[ $element ] );
1521 + }
1522 +
1523 + // Fallback.
1524 + return $this->render_fallback_attributes( $this->attributes[ $element ] );
1525 + }
1526 +
1527 + /**
1528 + * Function to render link attributes.
1529 + *
1530 + * @param string $id Element ID.
1531 + * @param array $control_name Control name.
1532 + * @param string $class_name Class name.
1533 + *
1534 + * @return string
1535 + */
1536 + public function render_link_attributes( $id, $control_name, $class_name = 'logo-img-link' ) {
1537 + $this->add_attribute( $id, 'class', $class_name );
1538 +
1539 + if ( ! empty( $control_name['url'] ) ) {
1540 + $this->add_link_attributes( $id, $control_name );
1541 + } else {
1542 + $this->add_attribute( $id, 'role', 'button' );
1543 + }
1544 +
1545 + return $this->get_attribute_string( $id );
1546 + }
1547 +
1548 + /**
1549 + * Render fallback attributes.
1550 + *
1551 + * @param array $attributes The attributes.
1552 + *
1553 + * @return string
1554 + */
1555 + private function render_fallback_attributes( $attributes ) {
1556 + $rendered_attributes = [];
1557 +
1558 + foreach ( $attributes as $attribute_key => $attribute_values ) {
1559 + if ( is_array( $attribute_values ) ) {
1560 + $attribute_values = implode( ' ', $attribute_values );
1561 + }
1562 +
1563 + $rendered_attributes[] = sprintf( '%1$s="%2$s"', $attribute_key, esc_attr( $attribute_values ) );
1564 + }
1565 +
1566 + return implode( ' ', $rendered_attributes );
1391 1567 }
1392 1568 }