PluginProbe
ShopBuilder – WooCommerce Builder For Elementor / 2.6.3
ShopBuilder – WooCommerce Builder For Elementor v2.6.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/Helpers/Fns.php +721 -78 2.1.42.6.3 View file →
@@ -44,9 +44,55 @@
44 44
45 45 return false;
46 46 }
47 47
48 + public static function get_nonce() {
49 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
50 + return isset( $_REQUEST[ rtsb()->nonceId ] ) ? sanitize_text_field( $_REQUEST[ rtsb()->nonceId ] ) : null;
51 + }
52 +
48 53 /**
54 + * Set Cookie or Session
55 + *
56 + * @param $name
57 + * @param $value
58 + */
59 + public static function setSession( $name, $value ) {
60 + if ( ! headers_sent() && session_status() == PHP_SESSION_NONE ) {
61 + session_start();
62 + $_SESSION[ $name ] = $value;
63 + } else {
64 + $_SESSION[ $name ] = $value;
65 + }
66 + }
67 + /**
68 + * Get Cookie or Session
69 + *
70 + * @param $name
71 + *
72 + * @return bool
73 + */
74 + public static function getSession( $name ) {
75 + if ( ! headers_sent() && session_status() == PHP_SESSION_NONE ) {
76 + session_start();
77 + }
78 + return $_SESSION[ $name ] ?? null; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
79 + }
80 + /**
81 + * Remove Session Variable
82 + *
83 + * @param string $name The name of the session variable to remove.
84 + */
85 + public static function removeSession( $name ) {
86 + if ( ! headers_sent() && session_status() == PHP_SESSION_NONE ) {
87 + session_start();
88 + unset( $_SESSION[ $name ] );
89 + } else {
90 + unset( $_SESSION[ $name ] );
91 + }
92 + }
93 +
94 + /**
49 95 * @param $plugin_slug
50 96 *
51 97 * @return bool
52 98 */
@@ -67,9 +113,46 @@
67 113 }
68 114
69 115 return false;
70 116 }
117 + /**
118 + * Get all user roles.
119 + *
120 + * @return array|false|mixed
121 + */
122 + public static function get_current_user_roles() {
123 + $current_user = wp_get_current_user();
124 + return $current_user->roles;
125 + }
126 + /**
127 + * Get all user roles.
128 + *
129 + * @return array|false|mixed
130 + */
131 + public static function get_all_user_roles() {
132 + $cache_key = 'rtsb_get_user_roles';
133 + $roles = wp_cache_get( $cache_key, 'shopbuilder' );
71 134
135 + if ( empty( $roles ) ) {
136 + if ( ! function_exists( 'get_editable_roles' ) ) {
137 + require_once ABSPATH . 'wp-admin/includes/user.php';
138 + }
139 +
140 + $user_roles = \get_editable_roles();
141 + $roles = [];
142 +
143 + foreach ( $user_roles as $key => $role ) {
144 + if ( empty( $role['capabilities'] ) ) {
145 + continue;
146 + }
147 + $roles[ $key ] = $role['name'];
148 + }
149 + }
150 + wp_cache_set( $cache_key, $roles, 'shopbuilder' );
151 + Cache::set_data_cache_key( $cache_key );
152 + return $roles;
153 + }
154 +
72 155 /**
73 156 * @param $data
74 157 *
75 158 * @return mixed|string
@@ -120,14 +203,15 @@
120 203
121 204 /**
122 205 * @param string $template_name Template name.
123 206 * @param string $template_path Template path. (default: '').
124 - * @param string $plugin_path Plugin path. (default: ''). fallback file from plugin
207 + * @param string $plugin_path Plugin path. (default: ''). fallback file from plugin.
125 208 *
126 209 * @return mixed|void
127 210 */
128 211 public static function locate_template( $template_name, $template_path = '', $plugin_path = '' ) {
129 - $template_name = $template_name . '.php';
212 + $template_name = self::sanitize_file_name( $template_name ) . '.php';
213 +
130 214 if ( ! $template_path ) {
131 215 $template_path = rtsb()->get_template_path();
132 216 }
133 217 if ( ! $plugin_path ) {
@@ -170,8 +254,19 @@
170 254 return apply_filters( 'rtsb/core/locate_template', $located, $template_name );
171 255 }
172 256
173 257 /**
258 + * Remove any character that is not alphanumeric, /, _, or -.
259 + *
260 + * @param string $name Name to sanitize.
261 + *
262 + * @return array|string|string[]|null
263 + */
264 + private static function sanitize_file_name( $name ) {
265 + return preg_replace( '/[^a-zA-Z0-9\/_\-]/', '', $name );
266 + }
267 +
268 + /**
174 269 * Template Content
175 270 *
176 271 * @param string $template_name Template name.
177 272 * @param array $args Arguments. (default: array).
@@ -181,10 +276,19 @@
181 276 *
182 277 * @return false|string|void
183 278 */
184 279 public static function load_template( $template_name, array $args = null, $return = false, $template_path = '', $plugin_path = '' ) {
185 - $located = self::locate_template( $template_name, $template_path, $plugin_path );
186 -
280 + $cache_key = sanitize_key( implode( '-', [ 'template', $template_name, $template_path ] ) );
281 + $located = (string) wp_cache_get( $cache_key, 'shopbuilder' );
282 + if ( ! $located ) {
283 + $located = self::locate_template( $template_name, $template_path, $plugin_path );
284 + // Don't cache the absolute path so that it can be shared between web servers with different paths.
285 + $cache_path = wc_tokenize_path( $located, wc_get_path_define_tokens() );
286 + Cache::set_template_cache( $cache_key, $cache_path );
287 + } else {
288 + // Make sure that the absolute path to the template is resolved.
289 + $located = wc_untokenize_path( $located, wc_get_path_define_tokens() );
290 + }
187 291 if ( ! file_exists( $located ) ) {
188 292 // translators: %s template.
189 293 self::doing_it_wrong( __FUNCTION__, sprintf( __( '%s does not exist.', 'shopbuilder' ), '<code>' . $located . '</code>' ), '1.0' );
190 294
@@ -192,9 +296,9 @@
192 296 }
193 297
194 298 if ( ! empty( $args ) && is_array( $args ) ) {
195 299 $atts = $args;
196 - extract( $args ); // @codingStandardsIgnoreLine
300 + extract( $args ); // @codingStandardsIgnoreLine
197 301 }
198 302
199 303 // Allow 3rd party plugin filter template file from their plugin.
200 304 $located = apply_filters( 'rtsb/core/get_template', $located, $template_name, $args );
@@ -309,9 +413,9 @@
309 413 // Return the Builder Template id.
310 414
311 415 if ( get_post_type( get_the_ID() ) == BuilderFns::$post_type_tb ) {
312 416 $product_id = get_post_meta( get_the_ID(), BuilderFns::$product_template_meta, true );
313 - if ( $product_id ) {
417 + if ( $product_id && get_post_status( $product_id ) ) {
314 418 return $product_id;
315 419 }
316 420 }
317 421
@@ -316,15 +420,16 @@
316 420 }
317 421
318 422 global $wpdb;
319 423 $cache_key = 'rtsb_prepared_product_id';
320 - $_post_id = wp_cache_get( $cache_key );
321 -
424 + $_post_id = wp_cache_get( $cache_key, 'shopbuilder' );
322 425 if ( false === $_post_id || 'publish' !== get_post_status( $_post_id ) ) {
426 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
323 427 $_post_id = $wpdb->get_var(
324 428 $wpdb->prepare( "SELECT MAX(ID) FROM {$wpdb->prefix}posts WHERE post_type = %s AND post_status IN('publish', 'draft')", 'product' )
325 429 );
326 - wp_cache_set( $cache_key, $_post_id, '', 12 * HOUR_IN_SECONDS );
430 + wp_cache_set( $cache_key, $_post_id, 'shopbuilder', 12 * HOUR_IN_SECONDS );
431 + Cache::set_data_cache_key( $cache_key );
327 432 }
328 433
329 434 return $_post_id;
330 435 }
@@ -337,9 +442,9 @@
337 442 public static function get_product() {
338 443 global $product;
339 444
340 445 if ( is_singular( 'product' ) && $product instanceof WC_Product ) {
341 - do_action( 'rtsb_before_product_template_render' );
446 + // do_action( 'rtsb_before_product_template_render' );.
342 447 return $product;
343 448 }
344 449 $cache_key = 'prepared_product_for_preview';
345 450 if ( isset( self::$cache[ $cache_key ] ) ) {
@@ -344,9 +449,8 @@
344 449 $cache_key = 'prepared_product_for_preview';
345 450 if ( isset( self::$cache[ $cache_key ] ) ) {
346 451 return self::$cache[ $cache_key ];
347 452 }
348 -
349 453 $product = wc_get_product( self::get_prepared_product_id() );
350 454 self::$cache[ $cache_key ] = $product;
351 455 do_action( 'rtsb_before_product_template_render' );
352 456 return $product;
@@ -361,10 +465,11 @@
361 465 *
362 466 * @return void
363 467 */
364 468 public static function doing_it_wrong( $function, $message, $version ) {
469 + // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_wp_debug_backtrace_summary
365 470 $message .= ' Backtrace: ' . wp_debug_backtrace_summary();
366 - _doing_it_wrong( $function, $message, $version );
471 + _doing_it_wrong( esc_html( $function ), wp_kses_post( $message ), esc_html( $version ) );
367 472 }
368 473
369 474 /**
370 475 * @return array
@@ -495,13 +600,14 @@
495 600 }
496 601
497 602 if ( strlen( $page_content ) > 0 ) {
498 603 // Search for an existing page with the specified page content (typically a shortcode).
499 - $shortcode = str_replace( [ '<!-- wp:shortcode -->', '<!-- /wp:shortcode -->' ], '', $page_content );
604 + $shortcode = str_replace( [ '<!-- wp:shortcode -->', '<!-- /wp:shortcode -->' ], '', $page_content );
605 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
500 606 $valid_page_found = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_type='page' AND post_status NOT IN ( 'pending', 'trash', 'future', 'auto-draft' ) AND post_content LIKE %s LIMIT 1;", "%{$shortcode}%" ) );
501 607 } else {
502 608 // Search for an existing page with the specified page slug.
503 - $valid_page_found = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_type='page' AND post_status NOT IN ( 'pending', 'trash', 'future', 'auto-draft' ) AND post_name = %s LIMIT 1;", $slug ) );
609 + $valid_page_found = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_type='page' AND post_status NOT IN ( 'pending', 'trash', 'future', 'auto-draft' ) AND post_name = %s LIMIT 1;", $slug ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
504 610 }
505 611
506 612 $valid_page_found = apply_filters( 'rtsb/core/create_page_id', $valid_page_found, $slug, $page_content, $options );
507 613
@@ -523,12 +629,12 @@
523 629
524 630 // Search for a matching valid trashed page.
525 631 if ( strlen( $page_content ) > 0 ) {
526 632 // Search for an existing page with the specified page content (typically a shortcode).
527 - $trashed_page_found = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_type='page' AND post_status = 'trash' AND post_content LIKE %s LIMIT 1;", "%{$page_content}%" ) );
633 + $trashed_page_found = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_type='page' AND post_status = 'trash' AND post_content LIKE %s LIMIT 1;", "%{$page_content}%" ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
528 634 } else {
529 635 // Search for an existing page with the specified page slug.
530 - $trashed_page_found = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_type='page' AND post_status = 'trash' AND post_name = %s LIMIT 1;", $slug ) );
636 + $trashed_page_found = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_type='page' AND post_status = 'trash' AND post_name = %s LIMIT 1;", $slug ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
531 637 }
532 638
533 639 if ( $trashed_page_found ) {
534 640 $page_id = $trashed_page_found;
@@ -899,13 +1005,15 @@
899 1005 return $allowed_html;
900 1006 }
901 1007
902 1008 /**
903 - * Safe print a validated HTML tag.
1009 + * Safe get a validated HTML tag.
904 1010 *
905 - * @param string $tag
1011 + * @param string $tag HTML tag.
1012 + *
1013 + * @return string
906 1014 */
907 - public static function print_validated_html_tag( $tag ) {
1015 + public static function get_validated_html_tag( $tag ) {
908 1016 $allowed_html_wrapper_tags = [
909 1017 'a',
910 1018 'article',
911 1019 'aside',
@@ -925,12 +1033,23 @@
925 1033 'section',
926 1034 'span',
927 1035 ];
928 1036
929 - self::print_html( in_array( strtolower( $tag ), $allowed_html_wrapper_tags, true ) ? $tag : 'div' );
1037 + return in_array( strtolower( $tag ), $allowed_html_wrapper_tags, true ) ? $tag : 'div';
930 1038 }
931 1039
932 1040 /**
1041 + * Safe print a validated HTML tag.
1042 + *
1043 + * @param string $tag HTML tag.
1044 + *
1045 + * @return void
1046 + */
1047 + public static function print_validated_html_tag( $tag ) {
1048 + self::print_html( self::get_validated_html_tag( $tag ) );
1049 + }
1050 +
1051 + /**
933 1052 * Insert Some array element
934 1053 *
935 1054 * @param [type] $key The elements will insert nearby this key.
936 1055 * @param [type] $main_array Original array.
@@ -1030,9 +1149,14 @@
1030 1149 if ( isset( self::$cache[ $cache_key ] ) ) {
1031 1150 return self::$cache[ $cache_key ];
1032 1151 }
1033 1152
1034 - $termList = get_terms( [ $taxonomy ], [ 'hide_empty' => 0 ] );
1153 + $termList = get_terms(
1154 + [
1155 + 'taxonomy' => $taxonomy,
1156 + 'hide_empty' => false,
1157 + ]
1158 + );
1035 1159
1036 1160 if ( is_array( $termList ) && ! empty( $termList ) && empty( $termList['errors'] ) ) {
1037 1161 if ( $placeholder ) {
1038 1162 $terms = [
@@ -1061,9 +1185,9 @@
1061 1185
1062 1186 if ( $attributes ) {
1063 1187 foreach ( $attributes as $tax ) {
1064 1188 if ( taxonomy_exists( wc_attribute_taxonomy_name( $tax->attribute_name ) ) ) {
1065 - $termList[ $tax->attribute_name ] = get_terms( wc_attribute_taxonomy_name( $tax->attribute_name ), 'orderby=name&hide_empty=0' );
1189 + $termList[ $tax->attribute_name ] = get_terms( wc_attribute_taxonomy_name( $tax->attribute_name ) );
1066 1190 }
1067 1191 }
1068 1192 }
1069 1193
@@ -1231,9 +1355,9 @@
1231 1355
1232 1356 if ( $args['show_rating_count'] ) {
1233 1357 $count .= '<div class="rtsb-count">';
1234 1358 $count .= sprintf(
1235 - /* translators: %s is the number of reviews */
1359 + /* translators: %s is the number of reviews */
1236 1360 _n( '%s Review', '%s Reviews', $rating_count, 'shopbuilder' ),
1237 1361 $rating_count
1238 1362 );
1239 1363 $count .= '</div>';
@@ -1258,8 +1382,23 @@
1258 1382
1259 1383 self::print_html( $html, true );
1260 1384 }
1261 1385
1386 + public static function get_product_simple_rating_html() {
1387 + global $product;
1388 + $html = null;
1389 + $rating_count = $product->get_rating_count();
1390 + $average_rating = $product->get_average_rating();
1391 + $html .= '<div class="product-rating">';
1392 + $html .= wc_get_rating_html( $average_rating, $rating_count );
1393 + $html .= ! empty( $html ) ? '<span class="rtsb-count">(' . $average_rating . ')</span>' : '';
1394 + $html .= '</div>';
1395 +
1396 + self::print_html( $html, true );
1397 + }
1398 +
1399 +
1400 +
1262 1401 /**
1263 1402 * Text truncation.
1264 1403 *
1265 1404 * @param string $text_to_truncate Text.
@@ -1302,8 +1441,14 @@
1302 1441 *
1303 1442 * @return void
1304 1443 */
1305 1444 public static function get_badge_html( $text, $class = 'fill' ) {
1445 + if ( self::is_module_active( 'product_badges' ) && 'rtsb_yes' === $text ) {
1446 + do_action( 'rtsb/modules/product_badges/frontend/display' );
1447 +
1448 + return;
1449 + }
1450 +
1306 1451 if ( empty( $text ) ) {
1307 1452 return;
1308 1453 }
1309 1454 ob_start();
@@ -1310,10 +1455,9 @@
1310 1455 ?>
1311 1456
1312 1457 <ul class="rtsb-promotion-list">
1313 1458 <li class="rtsb-promotion-list-item">
1314 - <span
1315 - class="rtsb-tag-<?php echo ! empty( $class ) ? esc_attr( $class ) : ''; ?>"><?php echo esc_html( $text ); ?></span>
1459 + <span class="rtsb-tag-<?php echo ! empty( $class ) ? esc_attr( $class ) : ''; ?>"><?php echo esc_html( $text ); ?></span>
1316 1460 </li>
1317 1461 </ul>
1318 1462
1319 1463 <?php
@@ -1691,13 +1835,17 @@
1691 1835 if ( ! $product instanceof WC_Product ) {
1692 1836 return '';
1693 1837 }
1694 1838
1839 + if ( ! $product->is_on_sale() ) {
1840 + return '';
1841 + }
1842 +
1695 1843 $percentage = null;
1696 1844 $max_percentage = '';
1697 1845
1698 1846 if ( $product->is_type( 'simple' ) ) {
1699 - $max_percentage = ( ( $product->get_regular_price() - $product->get_sale_price() ) / $product->get_regular_price() ) * 100;
1847 + $max_percentage = $product->get_sale_price() ? ( ( $product->get_regular_price() - $product->get_sale_price() ) / $product->get_regular_price() ) * 100 : 0;
1700 1848 } elseif ( $product->is_type( 'variable' ) ) {
1701 1849 $max_percentage = 0;
1702 1850
1703 1851 foreach ( $product->get_children() as $child_id ) {
@@ -1733,10 +1881,10 @@
1733 1881 *
1734 1882 * @return string
1735 1883 */
1736 1884 public static function custom_pagination( $pages = '', $range = 4, $ajax = false ) {
1737 - $html = null;
1738 - $showitems = ( $range * 2 ) + 1;
1885 + $html = null;
1886 + $visible_range = apply_filters( 'rtsb/elements/pagination/visible_range', ( $range * 2 ) + 1 );
1739 1887
1740 1888 global $paged;
1741 1889
1742 1890 if ( is_front_page() ) {
@@ -1764,8 +1912,9 @@
1764 1912
1765 1913 if ( $ajax ) {
1766 1914 $ajaxClass = ' rtsb-pagination-ajax';
1767 1915 $dataAttr = "data-paged='1'";
1916 + $dataAttr .= ' data-visible-range="' . esc_attr( $visible_range ) . '"';
1768 1917 }
1769 1918
1770 1919 if ( 1 !== $pages ) {
1771 1920 $html .= '<div class="rtsb-pagination' . $ajaxClass . '" ' . $dataAttr . '>';
@@ -1770,29 +1919,29 @@
1770 1919 if ( 1 !== $pages ) {
1771 1920 $html .= '<div class="rtsb-pagination' . $ajaxClass . '" ' . $dataAttr . '>';
1772 1921 $html .= '<ul class="pagination-list">';
1773 1922
1774 - if ( $paged > 2 && $paged > $range + 1 && $showitems < $pages ) {
1923 + if ( $paged > 2 && $paged > $visible_range + 1 && $visible_range < $pages ) {
1775 1924 $html .= "<li><a data-paged='1' href='" . get_pagenum_link( 1 ) . "' aria-label='First'>&laquo;</a></li>";
1776 1925 }
1777 1926
1778 - if ( $paged > 1 && $showitems < $pages ) {
1927 + if ( $paged > 1 && $visible_range < $pages ) {
1779 1928 $p = $paged - 1;
1780 1929 $html .= "<li><a data-paged='{$p}' href='" . get_pagenum_link( $p ) . "' aria-label='Previous'>&lsaquo;</a></li>";
1781 1930 }
1782 1931
1783 1932 for ( $i = 1; $i <= $pages; $i++ ) {
1784 - if ( 1 != $pages && ( ! ( $i >= $paged + $range + 1 || $i <= $paged - $range - 1 ) || $pages <= $showitems ) ) {
1933 + if ( 1 != $pages && ( ! ( $i >= $paged + $visible_range + 1 || $i <= $paged - $visible_range - 1 ) || $pages <= $visible_range ) ) {
1785 1934 $html .= ( $paged == $i ) ? '<li class="active"><span>' . $i . '</span></li>' : "<li><a data-paged='{$i}' href='" . get_pagenum_link( $i ) . "'>" . $i . '</a></li>';
1786 1935 }
1787 1936 }
1788 1937
1789 - if ( $paged < $pages && $showitems < $pages ) {
1938 + if ( $paged < $pages && $visible_range < $pages ) {
1790 1939 $p = $paged + 1;
1791 1940 $html .= "<li><a data-paged='{$p}' href=\"" . get_pagenum_link( $paged + 1 ) . "\" aria-label='Next'>&rsaquo;</a></li>";
1792 1941 }
1793 1942
1794 - if ( $paged < $pages - 1 && $paged + $range - 1 < $pages && $showitems < $pages ) {
1943 + if ( $paged < $pages - 1 && $paged + $range - 1 < $pages && $visible_range < $pages ) {
1795 1944 $html .= "<li><a data-paged='{$pages}' href='" . get_pagenum_link( $pages ) . "' aria-label='Last'>&raquo;</a></li>";
1796 1945 }
1797 1946
1798 1947 $html .= '</ul>';
@@ -2051,9 +2200,9 @@
2051 2200 $link['social_action'] = 'Share';
2052 2201 break;
2053 2202 case 'reddit':
2054 2203 $link['link'] = esc_url( 'https://reddit.com/submit?url=' . $link['url'] . '&title=' . $link['title'] );
2055 - $link['icon'] = '<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><title>ionicons-v5_logos</title><path d="M324,256a36,36,0,1,0,36,36A36,36,0,0,0,324,256Z"/><circle cx="188" cy="292" r="36" transform="translate(-97.43 94.17) rotate(-22.5)"/><path d="M496,253.77c0-31.19-25.14-56.56-56-56.56a55.72,55.72,0,0,0-35.61,12.86c-35-23.77-80.78-38.32-129.65-41.27l22-79L363.15,103c1.9,26.48,24,47.49,50.65,47.49,28,0,50.78-23,50.78-51.21S441,48,413,48c-19.53,0-36.31,11.19-44.85,28.77l-90-17.89L247.05,168.4l-4.63.13c-50.63,2.21-98.34,16.93-134.77,41.53A55.38,55.38,0,0,0,72,197.21c-30.89,0-56,25.37-56,56.56a56.43,56.43,0,0,0,28.11,49.06,98.65,98.65,0,0,0-.89,13.34c.11,39.74,22.49,77,63,105C146.36,448.77,199.51,464,256,464s109.76-15.23,149.83-42.89c40.53-28,62.85-65.27,62.85-105.06a109.32,109.32,0,0,0-.84-13.3A56.32,56.32,0,0,0,496,253.77ZM414,75a24,24,0,1,1-24,24A24,24,0,0,1,414,75ZM42.72,253.77a29.6,29.6,0,0,1,29.42-29.71,29,29,0,0,1,13.62,3.43c-15.5,14.41-26.93,30.41-34.07,47.68A30.23,30.23,0,0,1,42.72,253.77ZM390.82,399c-35.74,24.59-83.6,38.14-134.77,38.14S157,423.61,121.29,399c-33-22.79-51.24-52.26-51.24-83A78.5,78.5,0,0,1,75,288.72c5.68-15.74,16.16-30.48,31.15-43.79a155.17,155.17,0,0,1,14.76-11.53l.3-.21,0,0,.24-.17c35.72-24.52,83.52-38,134.61-38s98.9,13.51,134.62,38l.23.17.34.25A156.57,156.57,0,0,1,406,244.92c15,13.32,25.48,28.05,31.16,43.81a85.44,85.44,0,0,1,4.31,17.67,77.29,77.29,0,0,1,.6,9.65C442.06,346.77,423.86,376.24,390.82,399Zm69.6-123.92c-7.13-17.28-18.56-33.29-34.07-47.72A29.09,29.09,0,0,1,440,224a29.59,29.59,0,0,1,29.41,29.71A30.07,30.07,0,0,1,460.42,275.1Z"/><path d="M323.23,362.22c-.25.25-25.56,26.07-67.15,26.27-42-.2-66.28-25.23-67.31-26.27h0a4.14,4.14,0,0,0-5.83,0l-13.7,13.47a4.15,4.15,0,0,0,0,5.89h0c3.4,3.4,34.7,34.23,86.78,34.45,51.94-.22,83.38-31.05,86.78-34.45h0a4.16,4.16,0,0,0,0-5.9l-13.71-13.47a4.13,4.13,0,0,0-5.81,0Z"/></svg>';
2204 + $link['icon'] = '<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><path d="M324,256a36,36,0,1,0,36,36A36,36,0,0,0,324,256Z"/><circle cx="188" cy="292" r="36" transform="translate(-97.43 94.17) rotate(-22.5)"/><path d="M496,253.77c0-31.19-25.14-56.56-56-56.56a55.72,55.72,0,0,0-35.61,12.86c-35-23.77-80.78-38.32-129.65-41.27l22-79L363.15,103c1.9,26.48,24,47.49,50.65,47.49,28,0,50.78-23,50.78-51.21S441,48,413,48c-19.53,0-36.31,11.19-44.85,28.77l-90-17.89L247.05,168.4l-4.63.13c-50.63,2.21-98.34,16.93-134.77,41.53A55.38,55.38,0,0,0,72,197.21c-30.89,0-56,25.37-56,56.56a56.43,56.43,0,0,0,28.11,49.06,98.65,98.65,0,0,0-.89,13.34c.11,39.74,22.49,77,63,105C146.36,448.77,199.51,464,256,464s109.76-15.23,149.83-42.89c40.53-28,62.85-65.27,62.85-105.06a109.32,109.32,0,0,0-.84-13.3A56.32,56.32,0,0,0,496,253.77ZM414,75a24,24,0,1,1-24,24A24,24,0,0,1,414,75ZM42.72,253.77a29.6,29.6,0,0,1,29.42-29.71,29,29,0,0,1,13.62,3.43c-15.5,14.41-26.93,30.41-34.07,47.68A30.23,30.23,0,0,1,42.72,253.77ZM390.82,399c-35.74,24.59-83.6,38.14-134.77,38.14S157,423.61,121.29,399c-33-22.79-51.24-52.26-51.24-83A78.5,78.5,0,0,1,75,288.72c5.68-15.74,16.16-30.48,31.15-43.79a155.17,155.17,0,0,1,14.76-11.53l.3-.21,0,0,.24-.17c35.72-24.52,83.52-38,134.61-38s98.9,13.51,134.62,38l.23.17.34.25A156.57,156.57,0,0,1,406,244.92c15,13.32,25.48,28.05,31.16,43.81a85.44,85.44,0,0,1,4.31,17.67,77.29,77.29,0,0,1,.6,9.65C442.06,346.77,423.86,376.24,390.82,399Zm69.6-123.92c-7.13-17.28-18.56-33.29-34.07-47.72A29.09,29.09,0,0,1,440,224a29.59,29.59,0,0,1,29.41,29.71A30.07,30.07,0,0,1,460.42,275.1Z"/><path d="M323.23,362.22c-.25.25-25.56,26.07-67.15,26.27-42-.2-66.28-25.23-67.31-26.27h0a4.14,4.14,0,0,0-5.83,0l-13.7,13.47a4.15,4.15,0,0,0,0,5.89h0c3.4,3.4,34.7,34.23,86.78,34.45,51.94-.22,83.38-31.05,86.78-34.45h0a4.16,4.16,0,0,0,0-5.9l-13.71-13.47a4.13,4.13,0,0,0-5.81,0Z"/></svg>';
2056 2205 $link['attr_title'] = esc_html__( 'Share on Reddit', 'shopbuilder' );
2057 2206 $link['social_network'] = 'Reddit';
2058 2207 $link['social_action'] = 'Share';
2059 2208 break;
@@ -2150,9 +2299,8 @@
2150 2299 return true;
2151 2300 }
2152 2301 }
2153 2302
2154 -
2155 2303 /***
2156 2304 * Save Settings data
2157 2305 *
2158 2306 * @param $section_id
@@ -2164,10 +2312,9 @@
2164 2312 public static function set_options( $section_id = '', $block_id = '', $rawOptions = [] ) {
2165 2313 $section_id = ! empty( $section_id ) ? sanitize_text_field( wp_unslash( $section_id ) ) : '';
2166 2314 $block_id = ! empty( $block_id ) ? sanitize_text_field( wp_unslash( $block_id ) ) : '';
2167 2315 $rawOptions = ! empty( $rawOptions ) ? $rawOptions : [];
2168 -
2169 - $results = [
2316 + $results = [
2170 2317 'status' => true,
2171 2318 'message' => '',
2172 2319 ];
2173 2320 if ( ! $section_id || ! $block_id ) {
@@ -2183,9 +2330,9 @@
2183 2330
2184 2331 return $results;
2185 2332 }
2186 2333
2187 - $options = DataModel::source()->get_option( $section_id, [] );
2334 + $options = DataModel::source()->get_option( $section_id, [], false );
2188 2335 $changed = false;
2189 2336 $fields = [];
2190 2337 if ( isset( $sections[ $section_id ]['list'][ $block_id ]['fields'] ) && ! empty( $sections[ $section_id ]['list'][ $block_id ]['fields'] ) ) {
2191 2338 $fields = $sections[ $section_id ]['list'][ $block_id ]['fields'];
@@ -2206,12 +2353,40 @@
2206 2353 $field = $fields[ $raw_option_key ];
2207 2354 if ( $field['type'] === 'switch' ) {
2208 2355 $value = 'on' === $raw_value ? 'on' : '';
2209 2356 } elseif ( 'repeaters' === $field['type'] ) {
2210 - $value = stripslashes_deep( $raw_value );
2357 + $the_value = [];
2358 + $rep_title = [];
2359 + if ( ! empty( $raw_value ) && is_array( $raw_value ) ) {
2360 + foreach ( $raw_value as $key => $value ) {
2361 + if ( is_string( $value ) ) {
2362 + $the_value_decoded = json_decode( stripslashes_deep( $value ) );
2363 + } else {
2364 + $the_value_decoded = $value;
2365 + }
2366 + $cr = $the_value_decoded->title ?? '';
2367 + if ( in_array( $cr, $rep_title, true ) ) {
2368 + continue;
2369 + }
2370 + $rep_title[] = $cr;
2371 + $the_value[] = $the_value_decoded;
2372 + }
2373 + } elseif ( ! empty( $raw_value ) && is_string( $raw_value ) ) {
2374 + $the_value = $raw_value;
2375 + }
2376 + $value = wp_json_encode( $the_value );
2377 + } elseif ( in_array( $field['type'], [ 'product_addons_special_settings', 'checkout_fields' ] ) ) {
2378 + $manual_field_value = [];
2379 + if ( is_array( $raw_value ) ) {
2380 + foreach ( $raw_value as $key => $value ) {
2381 + $manual_field_value[] = json_decode( stripslashes( $value ), true );
2382 + }
2383 + $value = wp_json_encode( $manual_field_value );
2384 + } elseif ( is_string( $raw_value ) ) {
2385 + $value = $raw_value;
2386 + }
2211 2387 } else {
2212 2388 if ( ! empty( $field['multiple'] ) || in_array( $field['type'], [ 'checkbox', 'search_and_multi_select' ] ) ) {
2213 -
2214 2389 if ( isset( $raw_value ) && is_array( $raw_value ) ) {
2215 2390 if ( ! empty( $field['sanitize_fn'] ) && is_callable( $field['sanitize_fn'] ) ) {
2216 2391 $value = array_map( $field['sanitize_fn'], $raw_value );
2217 2392 } else {
@@ -2220,10 +2395,16 @@
2220 2395 } else {
2221 2396 $value = [];
2222 2397 }
2223 2398 } else {
2224 - if ( ! empty( $field['sanitize_fn'] ) && is_callable( $field['sanitize_fn'] ) ) {
2225 - $value = $field['sanitize_fn']( $raw_value );
2399 + if ( ! empty( $field['sanitize_fn'] ) ) {
2400 + if ( is_callable( $field['sanitize_fn'] ) ) {
2401 + $value = $field['sanitize_fn']( $raw_value );
2402 + } elseif ( 'pass_all' === $field['sanitize_fn'] ) {
2403 + $value = $raw_value;
2404 + } else {
2405 + $value = $field['sanitize_fn']( $raw_value );
2406 + }
2226 2407 } else {
2227 2408 $value = sanitize_text_field( $raw_value );
2228 2409 }
2229 2410 }
@@ -2269,9 +2450,8 @@
2269 2450 * }
2270 2451 * return;
2271 2452 * }
2272 2453 */
2273 -
2274 2454 public static function count_products_by_taxonomies( $terms, $taxonomy = 'product_cat' ) {
2275 2455 if ( empty( $terms ) || ! is_array( $terms ) ) {
2276 2456 return;
2277 2457 }
@@ -2287,9 +2467,8 @@
2287 2467 $args['product_tag_id'] = $terms;
2288 2468 }
2289 2469
2290 2470 $query = new WC_Product_Query( $args );
2291 -
2292 2471 return ! empty( $query->get_products() ) ? count( $query->get_products() ) : 0;
2293 2472 }
2294 2473
2295 2474 /**
@@ -2307,9 +2486,12 @@
2307 2486 if ( 'page' === get_post_type() ) {
2308 2487 return false;
2309 2488 }
2310 2489
2311 - $id = BuilderFns::is_builder_preview() ? get_the_ID() : BuilderFns::builder_page_id_by_type( $page );
2490 + $id = BuilderFns::is_builder_preview() ? get_the_ID() : BuilderFns::builder_page_id_by_type( $page );
2491 + if ( ! $id ) {
2492 + return false;
2493 + }
2312 2494 $cache_key = 'product_filters_has_ajax_' . $id;
2313 2495 if ( isset( self::$cache[ $cache_key ] ) ) {
2314 2496 return self::$cache[ $cache_key ];
2315 2497 }
@@ -2318,8 +2500,9 @@
2318 2500
2319 2501 foreach ( $elmap->get_widget_data( 'rtsb-ajax-product-filters', [], $id ) as $data ) {
2320 2502 $ajax[] = isset( $data['settings']['ajax_mode'] ) ? false : true;
2321 2503 }
2504 +
2322 2505 self::$cache[ $cache_key ] = ! empty( $ajax[0] );
2323 2506 return ! empty( $ajax[0] );
2324 2507 }
2325 2508
@@ -2337,9 +2520,11 @@
2337 2520
2338 2521 $id = BuilderFns::is_builder_preview() ? get_the_ID() : BuilderFns::builder_page_id_by_type( $page );
2339 2522 $elmap = ElementorDataMap::instance();
2340 2523 $ajax = [];
2341 -
2524 + if ( ! $id ) {
2525 + return false;
2526 + }
2342 2527 foreach ( $elmap->get_widget_data( 'rtsb-ajax-product-filters', [], $id ) as $data ) {
2343 2528 $ajax[] = ! isset( $data['settings']['active_filter'] );
2344 2529
2345 2530 }
@@ -2363,28 +2548,83 @@
2363 2548
2364 2549 if ( isset( self::$cache[ $cache_key ] ) ) {
2365 2550 return self::$cache[ $cache_key ];
2366 2551 }
2552 + $results = wp_cache_get( $cache_key, 'shopbuilder' );
2553 + if ( ! $results ) {
2554 + global $wpdb;
2555 + $sql = "SELECT t.term_id, t.name
2556 + FROM {$wpdb->terms} t
2557 + JOIN {$wpdb->term_taxonomy} tt ON t.term_id = tt.term_id
2558 + WHERE tt.taxonomy = 'product_cat'";
2367 2559
2368 - $results = wp_cache_get( $cache_key );
2560 + if ( $search_query ) {
2561 + $sql .= ' AND t.name LIKE %s';
2562 + $prepared_sql = $wpdb->prepare( $sql, '%' . $wpdb->esc_like( $search_query ) . '%' ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
2563 + } else {
2564 + $prepared_sql = $sql; // No need for placeholders.
2565 + }
2369 2566
2567 + $results = $wpdb->get_results( $prepared_sql ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.PreparedSQL.NotPrepared
2568 + // Cache the results in the object cache for future use.
2569 + wp_cache_set( $cache_key, $results, 'shopbuilder' ); // Adjust the expiration time as needed.
2570 + Cache::set_data_cache_key( $cache_key );
2571 + }
2572 +
2573 + $cats = [];
2574 + if ( ! is_array( $results ) && ! count( $results ) ) {
2575 + return $cats;
2576 + }
2577 + foreach ( $results as $row ) {
2578 + $category_id = $row->term_id;
2579 + $category_title = $row->name;
2580 +
2581 + $cats[] = [
2582 + 'value' => $category_id,
2583 + 'label' => $category_title,
2584 + ];
2585 + }
2586 +
2587 + // Cache the results in a static variable for the next call.
2588 + self::$cache[ $cache_key ] = $cats;
2589 + return $cats;
2590 + }
2591 + /**
2592 + * Get WooCommerce product categories.
2593 + *
2594 + * @param string|null $search_query The search string for category names.
2595 + *
2596 + * @return array An array of product categories with 'value' and 'label' keys.
2597 + */
2598 + public static function products_tags_query( $search_query = null ) {
2599 + if ( ! is_admin() ) {
2600 + return [];
2601 + }
2602 +
2603 + $cache_key = 'rtsb_product_tags_' . md5( serialize( $search_query ) );
2604 +
2605 + if ( isset( self::$cache[ $cache_key ] ) ) {
2606 + return self::$cache[ $cache_key ];
2607 + }
2608 + $results = wp_cache_get( $cache_key, 'shopbuilder' );
2370 2609 if ( ! $results ) {
2371 2610 global $wpdb;
2372 2611 $sql = "SELECT t.term_id, t.name
2373 2612 FROM {$wpdb->terms} t
2374 2613 JOIN {$wpdb->term_taxonomy} tt ON t.term_id = tt.term_id
2375 - WHERE tt.taxonomy = 'product_cat'";
2614 + WHERE tt.taxonomy = 'product_tag'";
2376 2615
2377 2616 if ( $search_query ) {
2378 2617 $sql .= ' AND t.name LIKE %s';
2379 - $prepared_sql = $wpdb->prepare( $sql, '%' . $wpdb->esc_like( $search_query ) . '%' ); // Escaping and wildcard
2618 + $prepared_sql = $wpdb->prepare( $sql, '%' . $wpdb->esc_like( $search_query ) . '%' ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
2380 2619 } else {
2381 2620 $prepared_sql = $sql; // No need for placeholders.
2382 2621 }
2383 2622
2384 - $results = $wpdb->get_results( $prepared_sql );
2623 + $results = $wpdb->get_results( $prepared_sql ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.PreparedSQL.NotPrepared
2385 2624 // Cache the results in the object cache for future use.
2386 - wp_cache_set( $cache_key, $results ); // Adjust the expiration time as needed.
2625 + wp_cache_set( $cache_key, $results, 'shopbuilder' ); // Adjust the expiration time as needed.
2626 + Cache::set_data_cache_key( $cache_key );
2387 2627 }
2388 2628
2389 2629 $cats = [];
2390 2630 if ( ! is_array( $results ) && ! count( $results ) ) {
@@ -2475,8 +2715,9 @@
2475 2715 'post_type' => 'any',
2476 2716 'posts_per_page' => 20,
2477 2717 'orderby' => 'ID',
2478 2718 'order' => 'DESC',
2719 + // 'suppress_filters' => false, // WPML Support, Remove Others Languages.
2479 2720 ]
2480 2721 );
2481 2722
2482 2723 if ( 'archive' !== $args['post_type'] ) {
@@ -2487,11 +2728,12 @@
2487 2728 $query_results = get_posts( $args );
2488 2729 foreach ( $query_results as $post ) {
2489 2730 $posts[] = [
2490 2731 'value' => $post->ID,
2491 - 'label' => $post->post_title,
2732 + 'label' => $post->post_title . ' (ID#' . $post->ID . ')',
2492 2733 ];
2493 2734 }
2735 +
2494 2736 if ( $search_query ) {
2495 2737 if ( strpos( '-error 404', $search_query ) ) {
2496 2738 $posts[] = [
2497 2739 'value' => 'error',
@@ -2498,12 +2740,14 @@
2498 2740 'label' => __( '404 Error', 'shopbuilder' ),
2499 2741 ];
2500 2742 }
2501 2743 } else {
2502 - $posts[] = [
2503 - 'value' => 'error',
2504 - 'label' => __( '404 Error', 'shopbuilder' ),
2505 - ];
2744 + if ( 'page' === $args['post_type'] ) {
2745 + $posts[] = [
2746 + 'value' => 'error',
2747 + 'label' => __( '404 Error', 'shopbuilder' ),
2748 + ];
2749 + }
2506 2750 }
2507 2751 } else {
2508 2752 $posts = self::get_registered_post_types( $search_query );
2509 2753 }
@@ -2551,35 +2795,48 @@
2551 2795 } else {
2552 2796 include $view_file;
2553 2797 }
2554 2798 }
2555 - /**
2556 - * @param int $limit Best-selling Base on product Limit.
2557 - * @param string $return_type id|Object.
2558 - *
2559 - * @return int
2560 - */
2561 - public static function best_selling_products( $limit = 10, $return_type = 'id' ) {
2562 - $cache_key = 'best_selling_products' . $return_type . $limit;
2563 2799
2800 + /**
2801 + * Best selling product query.
2802 + *
2803 + * @param int $minimum_sale Minimum sale/
2804 + * @param int $limit Total limit.
2805 + *
2806 + * @return array|mixed
2807 + */
2808 + public static function best_selling_products_ids( $minimum_sale = 1, $limit = 10 ) {
2809 + $cache_key = 'rtsb_best_selling_products_' . $minimum_sale . '_' . $limit;
2564 2810 // Check if the data is in the cache.
2811 + if ( isset( self::$cache[ $cache_key ] ) ) {
2812 + return self::$cache[ $cache_key ];
2813 + }
2565 2814 $cached_data = get_transient( $cache_key );
2566 2815 if ( $cached_data ) {
2816 + self::$cache[ $cache_key ] = $cached_data;
2567 2817 return $cached_data;
2568 2818 }
2569 -
2570 - $args = [
2571 - 'limit' => $limit,
2572 - 'orderby' => 'total_sales',
2573 - ];
2574 - if ( 'id' === strtolower( $return_type ) ) {
2575 - $args['return'] = 'ids';
2576 - }
2577 - $best_selling = wc_get_products( $args );
2578 -
2819 + global $wpdb;
2820 + $sql = $wpdb->prepare(
2821 + "
2822 + SELECT ID
2823 + FROM {$wpdb->posts} AS p
2824 + LEFT JOIN {$wpdb->postmeta} AS pm ON p.ID = pm.post_id
2825 + WHERE p.post_type = 'product'
2826 + AND p.post_status = 'publish'
2827 + AND pm.meta_key = 'total_sales'
2828 + AND pm.meta_value >= %d
2829 + ORDER BY pm.meta_value + 0 DESC
2830 + LIMIT %d
2831 + ",
2832 + $minimum_sale,
2833 + $limit
2834 + );
2835 + $best_selling = $wpdb->get_col( $sql ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared
2579 2836 // Cache the result for future use.
2580 2837 set_transient( $cache_key, $best_selling, DAY_IN_SECONDS );
2581 -
2838 + self::$cache[ $cache_key ] = $best_selling;
2582 2839 return $best_selling;
2583 2840 }
2584 2841
2585 2842 /**
@@ -2602,12 +2859,14 @@
2602 2859
2603 2860 if ( isset( self::$cache[ $cache_key ] ) ) {
2604 2861 return self::$cache[ $cache_key ];
2605 2862 }
2863 + $date_created = $product->get_date_created();
2864 + if ( ! $date_created ) {
2865 + return false;
2866 + }
2867 + $publish_timestamp = strtotime( $date_created->date_i18n( 'Y-m-d H:i:s' ) );
2606 2868
2607 - // Get the product publish date.
2608 - $publish_timestamp = strtotime( $product->get_date_created()->date_i18n( 'Y-m-d H:i:s' ) );
2609 -
2610 2869 // Calculate the difference in days.
2611 2870 $days_difference = abs( time() - $publish_timestamp ) / ( 60 * 60 * 24 );
2612 2871
2613 2872 // Check if the product is considered new.
@@ -2630,6 +2889,390 @@
2630 2889 public static function is_guest_feature_disabled( $option, $default ) {
2631 2890 $disabled = self::get_option( 'general', 'guest_user', $option, $default );
2632 2891
2633 2892 return ! is_user_logged_in() && $disabled;
2893 + }
2894 +
2895 + /**
2896 + * Checks if a feature is disabled for guest users based on the provided option.
2897 + *
2898 + * @param string $option The option to retrieve from the item.
2899 + * @param mixed $default The default value if the option is not found.
2900 + *
2901 + * @return bool
2902 + */
2903 + public static function woocommerce_output_all_notices() {
2904 + if ( function_exists( 'wc_print_notices' ) ) :
2905 + echo '<div class="rtsb-notice">';
2906 + woocommerce_output_all_notices();
2907 + echo '</div>';
2908 + endif;
2909 + }
2910 +
2911 + /**
2912 + * @param object $product Product.
2913 + * @return bool
2914 + */
2915 + public static function is_visible_qty_input( $product = null ) {
2916 + $is_visible_qty = true;
2917 + if ( $product instanceof \WC_Product ) {
2918 + if ( $product->is_sold_individually() ) {
2919 + $is_visible_qty = false;
2920 + } elseif ( $product->managing_stock() ) {
2921 + if ( in_array( $product->get_backorders(), [ 'notify' , 'yes' ], true ) ) {
2922 + $is_visible_qty = true;
2923 + } elseif ( $product->get_stock_quantity() < 2 ) {
2924 + $is_visible_qty = false;
2925 + }
2926 + }
2927 + }
2928 + return $is_visible_qty;
2929 + }
2930 +
2931 + /**
2932 + * @param int $object_id Object id.
2933 + * @param string $element_type element type.
2934 + * @param string|null $current_lang null for current language, default for default languages.
2935 + * @return false|mixed|null
2936 + */
2937 + public static function wpml_object_id( $object_id, $element_type, $current_lang = 'default' ) {
2938 + if ( ! defined( 'ICL_SITEPRESS_VERSION' ) ) {
2939 + return $object_id;
2940 + }
2941 + if ( ! $object_id || ! $element_type ) {
2942 + return $object_id;
2943 + }
2944 + if ( 'default' === $current_lang ) {
2945 + $lang = apply_filters( 'wpml_default_language', null );
2946 + } else {
2947 + $lang = null;
2948 + }
2949 + return apply_filters( 'wpml_object_id', $object_id, $element_type, false, $lang );
2950 + }
2951 +
2952 + /**
2953 + * @return string
2954 + */
2955 + public static function wpml_current_language() {
2956 + $current = apply_filters( 'wpml_current_language', null );
2957 + $default = apply_filters( 'wpml_default_language', null );
2958 + return $default !== $current ? $current : null;
2959 + }
2960 +
2961 + /**
2962 + * Custom icons.
2963 + *
2964 + * @return string[]
2965 + */
2966 + public static function get_custom_icon_names() {
2967 + return [
2968 + 'heart-empty',
2969 + 'heart',
2970 + 'eye',
2971 + 'exchange',
2972 + 'plus',
2973 + 'minus',
2974 + 'avatar',
2975 + 'pay',
2976 + 'share',
2977 + 'clock',
2978 + 'check-alt',
2979 + 'check',
2980 + 'delete',
2981 + 'marker',
2982 + 'list',
2983 + 'list-2',
2984 + 'power',
2985 + 'cart',
2986 + 'cart-2',
2987 + 'cart-3',
2988 + 'downloads',
2989 + 'zoom',
2990 + 'user-edit',
2991 + 'grid',
2992 + 'filter',
2993 + 'billing',
2994 + 'login',
2995 + 'payment',
2996 + 'search',
2997 + 'edit',
2998 + 'coupon',
2999 + 'arrows-cw',
3000 + 'trash-empty',
3001 + ];
3002 + }
3003 +
3004 + /**
3005 + * Retrieve an array of custom icons with their HTML representation.
3006 + *
3007 + * @return array
3008 + */
3009 + public static function get_icons() {
3010 + $icon = self::get_custom_icon_names();
3011 + $icons_array = [];
3012 + foreach ( $icon as $value ) {
3013 + $icons_array[ $value ] = '<i class="rtsb-icon rtsb-icon-' . $value . '"></i> <span class="icon-name">' . ucfirst( str_replace( '-', ' ', $value ) ) . '</span>';
3014 + }
3015 + return $icons_array;
3016 + }
3017 +
3018 + /**
3019 + * Generates HTML markup for displaying an endpoint icon.
3020 + *
3021 + * @param array $data The endpoint data containing icon information.
3022 + * @param string $endpoint The endpoint identifier.
3023 + * @param boolean $wrapper Whether to wrap the icon in a span element.
3024 + *
3025 + * @return string
3026 + */
3027 + public static function get_icon_html( $data, $wrapper = true ) {
3028 + if ( empty( $data ) || 'none' === $data['icon_source'] ) {
3029 + return '';
3030 + }
3031 + $endpoint = '';
3032 + $html = $wrapper ? '<span class="icon ' . esc_attr( $endpoint ) . '_icon">' : '';
3033 +
3034 + if ( 'select_icon' === $data['icon_source'] ) {
3035 + $html .= '<i class="rtsb-icon rtsb-icon-' . esc_attr( $data['custom_icon'] ) . '"></i>';
3036 + } else {
3037 + $icon_html = '';
3038 + $icon_url = $data['image_icon']['source'] ?? '';
3039 + $icon_id = $data['image_icon']['id'] ?? 0;
3040 + $extension = ! empty( $icon_url ) ? strtolower( substr( strrchr( $data['image_icon']['source'], '.' ), 1 ) ) : '';
3041 +
3042 + if ( 'svg' === $extension ) {
3043 + $content = file_get_contents( $icon_url ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
3044 + $is_svg = strpos( $content, '<svg' );
3045 +
3046 + if ( false !== $is_svg ) {
3047 + $icon_html = substr( $content, $is_svg );
3048 + }
3049 + } else {
3050 + $attr = [
3051 + 'class' => 'rtsb-icon-img',
3052 + 'alt' => esc_html( ucfirst( $endpoint ) ) . esc_html__( ' Icon', 'shopbuilder' ),
3053 + ];
3054 +
3055 + $icon_html = wp_get_attachment_image( absint( $icon_id ), 'full', true, $attr );
3056 + }
3057 +
3058 + $html .= $icon_html;
3059 + }
3060 +
3061 + $html .= $wrapper ? '</span>' : '';
3062 +
3063 + return $html;
3064 + }
3065 + /**
3066 + * Flushes rewrite rules.
3067 + *
3068 + * @return void
3069 + */
3070 + public static function maybe_flush_rewrite_rules() {
3071 + add_action( 'wp_loaded', [ __CLASS__, 'flush_rewrite_rules' ] );
3072 + add_action( 'shutdown', [ __CLASS__, 'flush_rewrite_rules_once_more' ] );
3073 + }
3074 +
3075 + /**
3076 + * Flushes rewrite rules if needed.
3077 + *
3078 + * @return void
3079 + */
3080 + public static function flush_rewrite_rules() {
3081 + if ( get_option( 'rtsb_permalinks_need_flush' ) === 'yes' ) {
3082 + flush_rewrite_rules();
3083 + }
3084 +
3085 + if ( get_option( 'rtsb_permalinks_flushed' ) === 'yes' ) {
3086 + flush_rewrite_rules();
3087 + delete_option( 'rtsb_permalinks_flushed' );
3088 + }
3089 + }
3090 +
3091 + /**
3092 + * Flushes rewrite rules if needed for second time.
3093 + *
3094 + * @return void
3095 + */
3096 + public static function flush_rewrite_rules_once_more() {
3097 + if ( get_option( 'rtsb_permalinks_need_flush' ) === 'yes' ) {
3098 + flush_rewrite_rules();
3099 + update_option( 'rtsb_permalinks_flushed', 'yes' );
3100 + delete_option( 'rtsb_permalinks_need_flush' );
3101 + }
3102 + }
3103 +
3104 + /**
3105 + * Social Share Platforms.
3106 + *
3107 + * @return mixed|null
3108 + */
3109 + public static function set_repeater_options( $section_id, $block_id, $option_key, $compare_key, $compare_value, $values ) {
3110 + // $options = self::get_options( $section_id, $block_id );
3111 + $modules = DataModel::source()->get_option( $section_id, [], false );
3112 + if ( empty( $modules[ $block_id ] ) ) {
3113 + return;
3114 + }
3115 + $options = $modules[ $block_id ];
3116 + if ( empty( $options[ $option_key ] ) ) {
3117 + return;
3118 + }
3119 + $repeater_options = json_decode( $options[ $option_key ], true );
3120 + // Check if options are not empty and are in array format.
3121 + if ( ! is_array( $repeater_options ) ) {
3122 + return;
3123 + }
3124 + // Use array_column to get an array of values from $compare_key.
3125 + $column_values = array_column( $repeater_options, $compare_key );
3126 + // Use array_search to find the index of the $compare_value.
3127 + $index = array_search( $compare_value, $column_values, true );
3128 + if ( false === $index ) {
3129 + return;
3130 + }
3131 + $repeater_options[ $index ] = wp_parse_args( $values, $repeater_options[ $index ] );
3132 + $options[ $option_key ] = wp_json_encode( $repeater_options );
3133 + $modules[ $block_id ] = $options;
3134 + DataModel::source()->set_option( $section_id, $modules );
3135 + }
3136 +
3137 +
3138 + /**
3139 + * @param array $ids products id.
3140 + * @return array
3141 + */
3142 + public static function get_available_products_by_ids( $ids = [] ) {
3143 + $ids = array_filter(
3144 + $ids,
3145 + function ( $id ) {
3146 + return 'product' === get_post_type( $id ) && 'publish' === get_post_status( $id );
3147 + }
3148 + );
3149 + return $ids;
3150 + }
3151 +
3152 + /**
3153 + * Generate and cache dynamic CSS styles.
3154 + *
3155 + * @param array $options CSS options to apply (e.g. padding, margin).
3156 + * @param string $cache_key Cache key for the CSS.
3157 + * @param array $css_properties An array of CSS properties with selectors and properties.
3158 + * @param string $style_handle The handle for the inline styles.
3159 + *
3160 + * @return void
3161 + */
3162 + public static function dynamic_styles( $options, $cache_key, $css_properties, $style_handle = 'rtsb-frontend' ) {
3163 + $cached_css = wp_cache_get( $cache_key, 'shopbuilder' );
3164 +
3165 + if ( false !== $cached_css ) {
3166 + wp_add_inline_style( $style_handle, $cached_css );
3167 + return;
3168 + }
3169 +
3170 + $css_rules = [];
3171 + $grouped_css_rules = [];
3172 +
3173 + foreach ( $css_properties as $option => $details ) {
3174 + if ( ! empty( $options[ $option ] ) ) {
3175 + $selector = $details['selector'] ?? '';
3176 + $property = $details['property'] ?? '';
3177 + $unit = $details['unit'] ?? '';
3178 + $value = $options[ $option ];
3179 +
3180 + if ( empty( $grouped_css_rules[ $selector ] ) ) {
3181 + $grouped_css_rules[ $selector ] = [];
3182 + }
3183 +
3184 + if ( is_array( $property ) ) {
3185 + foreach ( $property as $prop ) {
3186 + $grouped_css_rules[ $selector ][] = $prop . ': ' . $value . $unit . ' !important';
3187 + }
3188 + } else {
3189 + $grouped_css_rules[ $selector ][] = $property . ': ' . $value . $unit . ' !important';
3190 + }
3191 + }
3192 + }
3193 +
3194 + foreach ( $grouped_css_rules as $selector => $properties ) {
3195 + $css_rules[] = $selector . ' {' . implode( '; ', $properties ) . '}';
3196 + }
3197 +
3198 + $dynamic_css = implode( ' ', $css_rules );
3199 +
3200 + wp_cache_set( $cache_key, $dynamic_css, 'shopbuilder', 12 * HOUR_IN_SECONDS );
3201 + Cache::set_data_cache_key( $cache_key );
3202 +
3203 + if ( ! empty( $dynamic_css ) ) {
3204 + wp_add_inline_style( $style_handle, $dynamic_css );
3205 + }
3206 + }
3207 +
3208 + /**
3209 + * Pro version notice.
3210 + *
3211 + * @param string $ver Pro Version.
3212 + *
3213 + * @return array[]
3214 + */
3215 + public static function pro_version_notice( $ver, $tab = 'billing_fields', $name = 'ShopBuilder', $enable_free_Link = true ) {
3216 + return [
3217 + 'version_check' => [
3218 + 'id' => 'version_check',
3219 + 'type' => 'title',
3220 + 'label' => sprintf(
3221 + /* translators: 1: required version, 2: link to the Plugins page */
3222 + esc_html__(
3223 + 'To access all features and settings of this module, please ensure that "%1$s" is updated to version %2$s. %3$s',
3224 + 'shopbuilder'
3225 + ),
3226 + $name,
3227 + sprintf(
3228 + '<br /><u><b>%s</b></u>',
3229 + esc_html( $ver ?? '1.5.0' ) . ' or higher'
3230 + ),
3231 + $enable_free_Link
3232 + ? sprintf(
3233 + '%s <a class="pro-update-required" href="%s" target="_blank" title="%s">%s</a>',
3234 + esc_attr__( 'Update to the latest version', 'shopbuilder' ),
3235 + esc_url( admin_url( 'plugins.php' ) ),
3236 + esc_attr__( 'Go to the Plugin Page', 'shopbuilder' ),
3237 + esc_html__( 'from the Plugins page', 'shopbuilder' )
3238 + )
3239 + : ''
3240 + ),
3241 + 'tab' => $tab,
3242 + 'customClass' => 'checkout-notice',
3243 + ],
3244 + ];
3245 + }
3246 +
3247 + /**
3248 + * Get product gallery ids.
3249 + *
3250 + * @param object $product Product object.
3251 + *
3252 + * @return mixed
3253 + */
3254 + public static function get_cached_gallery_ids( $product ) {
3255 + $product_id = $product->get_id();
3256 + $cache_key = 'rtsb_product_gallery_ids_' . $product_id;
3257 +
3258 + if ( isset( self::$cache[ $cache_key ] ) ) {
3259 + return self::$cache[ $cache_key ];
3260 + }
3261 +
3262 + $cached_result = wp_cache_get( $cache_key, 'shopbuilder' );
3263 +
3264 + if ( false !== $cached_result ) {
3265 + self::$cache[ $cache_key ] = $cached_result;
3266 +
3267 + return $cached_result;
3268 + }
3269 +
3270 + $gallery_ids = $product->get_gallery_image_ids();
3271 + self::$cache[ $cache_key ] = $gallery_ids;
3272 +
3273 + wp_cache_set( $cache_key, $gallery_ids, 'shopbuilder', 12 * HOUR_IN_SECONDS );
3274 + Cache::set_data_cache_key( $cache_key );
3275 +
3276 + return $gallery_ids;
2634 3277 }
2635 3278 }