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 +180 -1459 3.4.22.6.3 View file →
@@ -11,24 +11,15 @@
11 11 if ( ! defined( 'ABSPATH' ) ) {
12 12 exit( 'This script cannot be accessed directly.' );
13 13 }
14 14
15 -
16 -use RadiusTheme\SB\Dependencies\CodesVault\Howdyqb\DB;
17 -use DateTime;
18 -use WP_Query;
19 -use WP_Styles;
20 15 use WC_Product;
21 -use Elementor\Plugin;
22 16 use WC_Product_Query;
23 17 use Elementor\Icons_Manager;
24 18 use RadiusTheme\SB\Models\ReSizer;
25 -use RadiusTheme\SB\Models\Settings;
26 19 use RadiusTheme\SB\Models\DataModel;
27 20 use RadiusTheme\SB\Models\ModuleList;
28 -use RadiusTheme\SB\Models\GeneralList;
29 -use RadiusTheme\SB\Models\ElementList;
30 -use RadiusTheme\SB\Controllers\AssetRegistry;
21 +use RadiusTheme\SB\Models\Settings;
31 22
32 23 /**
33 24 * Fns class
34 25 */
@@ -39,89 +30,8 @@
39 30 */
40 31 private static $cache = [];
41 32
42 33 /**
43 - * Whether asset bundling failed during this request.
44 - *
45 - * Set by AssetRegistry when AssetBundler::build() cannot produce a file, for
46 - * example on the first request after a plugin upgrade while the autoloader
47 - * still points at the removed files, or when the cache directory turns out
48 - * not to be writable. Once set, is_optimization_enabled() reports false for
49 - * the remainder of the request so the callers register and enqueue the plain
50 - * unbundled assets instead of a stylesheet handle with an empty source.
51 - *
52 - * Request scoped on purpose: it must not be persisted, the next request
53 - * retries the build.
54 - *
55 - * @var bool
56 - */
57 - private static $optimization_unavailable = false;
58 -
59 - /**
60 - * Flag that a bundle could not be produced during this request.
61 - *
62 - * @return void
63 - */
64 - public static function mark_optimization_unavailable() {
65 - self::$optimization_unavailable = true;
66 - }
67 -
68 - /**
69 - * Whether bundling has been marked unavailable for this request.
70 - *
71 - * @return bool
72 - */
73 - public static function is_optimization_unavailable() {
74 - return self::$optimization_unavailable;
75 - }
76 -
77 - /**
78 - * Check if the stored license is valid. Its Only Show Pro Label.
79 - *
80 - * @return bool
81 - */
82 - public static function has_valid_license() {
83 - static $cached_result = null;
84 - // Return cached result if already calculated in this request.
85 - if ( null !== $cached_result ) {
86 - return $cached_result;
87 - }
88 - $license_status = rtsb()->has_pro() ? rtsbpro()->get_license( 'license_status' ) : '';
89 - return 'valid' === $license_status;
90 - }
91 -
92 - /**
93 - * Check if license is valid or bypassed by theme,
94 - *
95 - * @return bool
96 - */
97 - public static function is_pro_authorized() {
98 - static $cached_result = null;
99 - if ( null !== $cached_result ) {
100 - return $cached_result;
101 - }
102 - // ❗ Step 2: If Pro not installed → no access.
103 - if ( ! function_exists( 'rtsbpro' ) ) {
104 - $cached_result = false;
105 - return $cached_result;
106 - }
107 - $current_theme = wp_get_theme()->get_stylesheet(); // Child theme And Parent Theme Need Check work or not.
108 - /**
109 - * Allowed themes (bypass license)
110 - *
111 - * @param array $themes
112 - */
113 - $allowed_themes = apply_filters( 'rt_pro_access_allowed_themes', [] );
114 - // ✅ Step 1: Theme bypass (no license check).
115 - if ( in_array( $current_theme, $allowed_themes, true ) ) {
116 - $cached_result = true;
117 - return true;
118 - }
119 - // ✅ Step 3: Normal license validation
120 - $license_status = rtsbpro()->get_license( 'license_status' );
121 - return ( 'valid' === $license_status );
122 - }
123 - /**
124 34 * Verify nonce.
125 35 *
126 36 * @return bool
127 37 */
@@ -134,36 +44,8 @@
134 44
135 45 return false;
136 46 }
137 47
138 - /**
139 - * Get nonce.
140 - *
141 - * @return string|null
142 - */
143 - public static function enable_loader() {
144 - return 'on' !== self::get_option( 'general', 'optimization', 'remove_pre_loader', '' );
145 - }
146 - /**
147 - * Get nonce.
148 - *
149 - * @return string|null
150 - */
151 - public static function content_invisible() {
152 - $wp_rocket_compatibility = 'on' !== self::get_option( 'general', 'optimization', 'wp_rocket_compatibility', '' );
153 - if ( $wp_rocket_compatibility ) {
154 - return true;
155 - }
156 - if ( defined( 'WP_ROCKET_VERSION' ) ) {
157 - return false;
158 - }
159 - return true;
160 - }
161 - /**
162 - * Get nonce.
163 - *
164 - * @return string|null
165 - */
166 48 public static function get_nonce() {
167 49 // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
168 50 return isset( $_REQUEST[ rtsb()->nonceId ] ) ? sanitize_text_field( $_REQUEST[ rtsb()->nonceId ] ) : null;
169 51 }
@@ -168,12 +50,12 @@
168 50 return isset( $_REQUEST[ rtsb()->nonceId ] ) ? sanitize_text_field( $_REQUEST[ rtsb()->nonceId ] ) : null;
169 51 }
170 52
171 53 /**
172 - * Set Session
54 + * Set Cookie or Session
173 55 *
174 - * @param string $name Name.
175 - * @param mixed $value Value.
56 + * @param $name
57 + * @param $value
176 58 */
177 59 public static function setSession( $name, $value ) {
178 60 if ( ! headers_sent() && session_status() == PHP_SESSION_NONE ) {
179 61 session_start();
@@ -181,13 +63,12 @@
181 63 } else {
182 64 $_SESSION[ $name ] = $value;
183 65 }
184 66 }
185 -
186 67 /**
187 68 * Get Cookie or Session
188 69 *
189 - * @param string $name Name.
70 + * @param $name
190 71 *
191 72 * @return bool
192 73 */
193 74 public static function getSession( $name ) {
@@ -208,43 +89,12 @@
208 89 } else {
209 90 unset( $_SESSION[ $name ] );
210 91 }
211 92 }
93 +
212 94 /**
213 - * Gets the current timestamp in WordPress timezone.
95 + * @param $plugin_slug
214 96 *
215 - * @return int Current timestamp.
216 - */
217 - public static function currentTimestampUTC() {
218 - $wp_timezone = wp_timezone();
219 - $now = new DateTime( 'now', $wp_timezone ); // Current time in WP timezone.
220 - return $now->getTimestamp();
221 - }
222 - /**
223 - * Action.
224 - *
225 - * @param string $action action.
226 - * @return void
227 - */
228 - public static function add_to_scheduled_hook_list( $action ) {
229 - if ( empty( $action ) ) {
230 - return;
231 - }
232 - $schedule = get_option( 'rtsb_cron_schedule_free', [] );
233 - $schedule[] = $action;
234 - update_option( 'rtsb_cron_schedule_free', array_unique( $schedule ) );
235 - }
236 - /**
237 - * @return DB
238 - */
239 - public static function DB() {
240 - return new DB( 'wpdb' );
241 - }
242 - /**
243 - * Check if a plugin is installed.
244 - *
245 - * @param string $plugin_slug Plugin slug.
246 - *
247 97 * @return bool
248 98 */
249 99 public static function check_plugin_installed( $plugin_slug ): bool {
250 100 $installed_plugins = get_plugins();
@@ -252,12 +102,10 @@
252 102 return array_key_exists( $plugin_slug, $installed_plugins ) || in_array( $plugin_slug, $installed_plugins, true );
253 103 }
254 104
255 105 /**
256 - * Check if a plugin is active.
106 + * @param $plugin_slug
257 107 *
258 - * @param string $plugin_slug Plugin slug.
259 - *
260 108 * @return bool
261 109 */
262 110 public static function check_plugin_active( $plugin_slug ): bool {
263 111 if ( is_plugin_active( $plugin_slug ) ) {
@@ -268,9 +116,9 @@
268 116 }
269 117 /**
270 118 * Get all user roles.
271 119 *
272 - * @return array
120 + * @return array|false|mixed
273 121 */
274 122 public static function get_current_user_roles() {
275 123 $current_user = wp_get_current_user();
276 124 return $current_user->roles;
@@ -280,20 +128,10 @@
280 128 *
281 129 * @return array|false|mixed
282 130 */
283 131 public static function get_all_user_roles() {
284 - // Memoized because the settings schema asks for the role list several
285 - // times per request, and the miss path require_once()s an admin-only
286 - // WordPress file. The static also covers the legitimate empty-result
287 - // case, which wp_cache_get() alone cannot distinguish from a miss.
288 - static $memo = null;
289 -
290 - if ( null !== $memo ) {
291 - return $memo;
292 - }
293 -
294 132 $cache_key = 'rtsb_get_user_roles';
295 - $roles = wp_cache_get( $cache_key, Cache::group() );
133 + $roles = wp_cache_get( $cache_key, 'shopbuilder' );
296 134
297 135 if ( empty( $roles ) ) {
298 136 if ( ! function_exists( 'get_editable_roles' ) ) {
299 137 require_once ABSPATH . 'wp-admin/includes/user.php';
@@ -307,45 +145,26 @@
307 145 continue;
308 146 }
309 147 $roles[ $key ] = $role['name'];
310 148 }
311 -
312 - // Only prime the object cache on a miss. Re-writing it on every hit
313 - // also re-appended the key to the tracked data-cache index.
314 - wp_cache_set( $cache_key, $roles, Cache::group(), 12 * HOUR_IN_SECONDS );
315 - Cache::set_data_cache_key( $cache_key );
316 149 }
317 -
318 - $memo = $roles;
319 -
320 - return $memo;
150 + wp_cache_set( $cache_key, $roles, 'shopbuilder' );
151 + Cache::set_data_cache_key( $cache_key );
152 + return $roles;
321 153 }
322 154
323 155 /**
324 - * Stripslashes value.
156 + * @param $data
325 157 *
326 - * @param mixed $data Data.
327 - *
328 158 * @return mixed|string
329 159 */
330 160 public static function stripslashes_value( $data ) {
331 - if ( is_array( $data ) ) {
161 + if ( is_string( $data ) && strpos( $data, '\\' ) !== false ) {
162 + return stripslashes( $data );
163 + } elseif ( is_array( $data ) ) {
332 164 foreach ( $data as $key => $value ) {
333 165 $data[ $key ] = self::stripslashes_value( $value );
334 166 }
335 - } elseif ( is_string( $data ) ) {
336 - $trimmed = trim( $data );
337 - // Try to decode JSON.
338 - $json = json_decode( $trimmed, true );
339 - if ( json_last_error() === JSON_ERROR_NONE && is_array( $json ) ) {
340 - // Recursively stripslashes on decoded array.
341 - $json = self::stripslashes_value( $json );
342 - return wp_json_encode( $json, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE );
343 - }
344 - // Not valid JSON, just stripslashes if needed.
345 - if ( strpos( $data, '\\' ) !== false ) {
346 - return stripslashes( $data );
347 - }
348 167 }
349 168
350 169 return $data;
351 170 }
@@ -350,12 +169,10 @@
350 169 return $data;
351 170 }
352 171
353 172 /**
354 - * Multiselect settings field value
173 + * @param $string
355 174 *
356 - * @param string $string String.
357 - *
358 175 * @return array
359 176 */
360 177 public static function multiselect_settings_field_value( $string ) {
361 178
@@ -375,10 +192,8 @@
375 192 return $values;
376 193 }
377 194
378 195 /**
379 - * Check if is block theme
380 - *
381 196 * @return bool
382 197 */
383 198 public static function check_is_block_theme(): bool {
384 199 global $wp_version;
@@ -386,10 +201,8 @@
386 201 return version_compare( $wp_version, '5.9', '>=' ) && function_exists( 'wp_is_block_theme' ) && wp_is_block_theme();
387 202 }
388 203
389 204 /**
390 - * Locate template
391 - *
392 205 * @param string $template_name Template name.
393 206 * @param string $template_path Template path. (default: '').
394 207 * @param string $plugin_path Plugin path. (default: ''). fallback file from plugin.
395 208 *
@@ -420,12 +233,12 @@
420 233 )
421 234 );
422 235
423 236 if ( ! $located ) {
424 - if ( $pro_plugin_path && rtsb()->has_pro() && file_exists( $pro_plugin_path ) ) {
237 + if ( file_exists( $plugin_path ) ) {
238 + return apply_filters( 'rtsb/core/locate_template', $plugin_path, $template_name );
239 + } elseif ( $pro_plugin_path && rtsb()->has_pro() && file_exists( $pro_plugin_path ) ) {
425 240 return apply_filters( 'rtsb/core/locate_template', $pro_plugin_path, $template_name );
426 - } elseif ( file_exists( $plugin_path ) ) {
427 - return apply_filters( 'rtsb/core/locate_template', $plugin_path, $template_name );
428 241 }
429 242 }
430 243
431 244 /**
@@ -462,11 +275,11 @@
462 275 * @param string $plugin_path Fallback path from where file will load if fail to load from template. (default: '').
463 276 *
464 277 * @return false|string|void
465 278 */
466 - public static function load_template( $template_name, $args = null, $return = false, $template_path = '', $plugin_path = '' ) {
279 + public static function load_template( $template_name, array $args = null, $return = false, $template_path = '', $plugin_path = '' ) {
467 280 $cache_key = sanitize_key( implode( '-', [ 'template', $template_name, $template_path ] ) );
468 - $located = (string) wp_cache_get( $cache_key, Cache::group() );
281 + $located = (string) wp_cache_get( $cache_key, 'shopbuilder' );
469 282 if ( ! $located ) {
470 283 $located = self::locate_template( $template_name, $template_path, $plugin_path );
471 284 // Don't cache the absolute path so that it can be shared between web servers with different paths.
472 285 $cache_path = wc_tokenize_path( $located, wc_get_path_define_tokens() );
@@ -475,14 +288,10 @@
475 288 // Make sure that the absolute path to the template is resolved.
476 289 $located = wc_untokenize_path( $located, wc_get_path_define_tokens() );
477 290 }
478 291 if ( ! file_exists( $located ) ) {
479 - // $located is empty when the template could not be found anywhere,
480 - // so report the requested name instead of an empty path.
481 - $reported = $located ? $located : $template_name;
482 -
483 292 // translators: %s template.
484 - self::doing_it_wrong( __FUNCTION__, sprintf( __( '%s does not exist.', 'shopbuilder' ), '<code>' . esc_html( $reported ) . '</code>' ), '1.0' );
293 + self::doing_it_wrong( __FUNCTION__, sprintf( __( '%s does not exist.', 'shopbuilder' ), '<code>' . $located . '</code>' ), '1.0' );
485 294
486 295 return;
487 296 }
488 297
@@ -604,9 +413,9 @@
604 413 // Return the Builder Template id.
605 414
606 415 if ( get_post_type( get_the_ID() ) == BuilderFns::$post_type_tb ) {
607 416 $product_id = get_post_meta( get_the_ID(), BuilderFns::$product_template_meta, true );
608 - if ( $product_id && 'product' === get_post_type( $product_id ) && get_post_status( $product_id ) ) {
417 + if ( $product_id && get_post_status( $product_id ) ) {
609 418 return $product_id;
610 419 }
611 420 }
612 421
@@ -611,15 +420,15 @@
611 420 }
612 421
613 422 global $wpdb;
614 423 $cache_key = 'rtsb_prepared_product_id';
615 - $_post_id = wp_cache_get( $cache_key, Cache::group() );
424 + $_post_id = wp_cache_get( $cache_key, 'shopbuilder' );
616 425 if ( false === $_post_id || 'publish' !== get_post_status( $_post_id ) ) {
617 426 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
618 427 $_post_id = $wpdb->get_var(
619 428 $wpdb->prepare( "SELECT MAX(ID) FROM {$wpdb->prefix}posts WHERE post_type = %s AND post_status IN('publish', 'draft')", 'product' )
620 429 );
621 - wp_cache_set( $cache_key, $_post_id, Cache::group(), 12 * HOUR_IN_SECONDS );
430 + wp_cache_set( $cache_key, $_post_id, 'shopbuilder', 12 * HOUR_IN_SECONDS );
622 431 Cache::set_data_cache_key( $cache_key );
623 432 }
624 433
625 434 return $_post_id;
@@ -633,8 +442,9 @@
633 442 public static function get_product() {
634 443 global $product;
635 444
636 445 if ( is_singular( 'product' ) && $product instanceof WC_Product ) {
446 + // do_action( 'rtsb_before_product_template_render' );.
637 447 return $product;
638 448 }
639 449 $cache_key = 'prepared_product_for_preview';
640 450 if ( isset( self::$cache[ $cache_key ] ) ) {
@@ -664,17 +474,8 @@
664 474 /**
665 475 * @return array
666 476 */
667 477 public static function get_pages() {
668 - // Memoized because this only feeds admin select fields, yet the settings
669 - // schema is built on every request and asks for it several times. Each
670 - // miss runs a full get_pages() query and hydrates every page object.
671 - static $memo = null;
672 -
673 - if ( null !== $memo ) {
674 - return $memo;
675 - }
676 -
677 478 $pages = [];
678 479 $rawPages = get_pages();
679 480 if ( ! empty( $rawPages ) ) {
680 481 foreach ( $rawPages as $page ) {
@@ -681,20 +482,11 @@
681 482 $pages[ $page->ID ] = $page->post_title;
682 483 }
683 484 }
684 485
685 - $memo = $pages;
686 -
687 - return $memo;
486 + return $pages;
688 487 }
689 488
690 - /**
691 - * Get section items
692 - *
693 - * @param string $section_id Section ID.
694 - *
695 - * @return array
696 - */
697 489 public static function get_section_items( $section_id ) {
698 490 if ( ! $section_id ) {
699 491 return [];
700 492 }
@@ -701,16 +493,8 @@
701 493
702 494 return DataModel::source()->get_option( $section_id, [] );
703 495 }
704 496
705 - /**
706 - * Get options items
707 - *
708 - * @param string $section_id Section ID.
709 - * @param string $item_id Item ID.
710 - *
711 - * @return array
712 - */
713 497 public static function get_options( $section_id, $item_id ) {
714 498 if ( ! $section_id || ! $item_id ) {
715 499 return [];
716 500 }
@@ -721,11 +505,11 @@
721 505
722 506 /**
723 507 * Get options value with default value if options doesn't exist.
724 508 *
725 - * @param array $group Group.
726 - * @param string $option_key Option key.
727 - * @param string $default_value Default value.
509 + * @param $group
510 + * @param $option_key
511 + * @param $default_value
728 512 *
729 513 * @return mixed|string
730 514 */
731 515 public static function get_options_by_default_val( $group, $option_key, $default_value = '' ) {
@@ -737,34 +521,32 @@
737 521 return $group[ $option_key ];
738 522 }
739 523
740 524 /**
741 - * Get option.
525 + * @param string $section_id
526 + * @param string $item_id
527 + * @param string $option_id
528 + * @param null $default EXCEPT multi_checkbox you can provide default value if given option does not set any value
529 + * @param null $type checkbox, multi_checkbox, number
742 530 *
743 - * @param string $section_id Section ID.
744 - * @param string $item_id Item ID.
745 - * @param string $option_id Option ID.
746 - * @param null $default EXCEPT multi_checkbox you can provide default value if given option does not set any value.
747 - * @param null $type checkbox, multi_checkbox, number.
748 - *
749 531 * @return bool|int|mixed|null
750 532 */
751 533 public static function get_option( $section_id, $item_id, $option_id, $default = null, $type = null ) {
752 534 $options = self::get_options( $section_id, $item_id );
753 535
754 - if ( 'checkbox' === $type ) {
536 + if ( $type === 'checkbox' ) {
755 537 if ( isset( $options[ $option_id ] ) ) {
756 - return 'on' === $options[ $option_id ];
538 + return $options[ $option_id ] === 'on';
757 539 }
758 540
759 541 return $default;
760 - } elseif ( 'multi_checkbox' === $type ) {
761 - return isset( $options[ $option_id ] ) && is_array( $options[ $option_id ] ) && in_array( $default, $options[ $option_id ] ); // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict
762 - } elseif ( 'number' === $type ) {
542 + } elseif ( $type === 'multi_checkbox' ) {
543 + return isset( $options[ $option_id ] ) && is_array( $options[ $option_id ] ) && in_array( $default, $options[ $option_id ] );
544 + } elseif ( $type === 'number' ) {
763 545 return isset( $options[ $option_id ] ) ? absint( $options[ $option_id ] ) : absint( $default );
764 546 }
765 547
766 - return ! empty( $options[ $option_id ] ) ? $options[ $option_id ] : $default;
548 + return isset( $options[ $option_id ] ) && ! empty( $options[ $option_id ] ) ? $options[ $option_id ] : $default;
767 549 }
768 550
769 551
770 552 /**
@@ -891,13 +673,8 @@
891 673
892 674 return $page_id;
893 675 }
894 676
895 - /**
896 - * Get allowed HTML tags.
897 - *
898 - * @return array
899 - */
900 677 public static function get_kses_array() {
901 678 return [
902 679 'a' => [
903 680 'class' => [],
@@ -996,14 +773,13 @@
996 773 ];
997 774 }
998 775
999 776
1000 - /**
777 + /*
1001 778 * Escape output of wishlist icon
1002 779 *
1003 780 * @param string $data Data to escape.
1004 - *
1005 - * @return void
781 + * @return string Escaped data
1006 782 */
1007 783 public static function print_icon( $data ) {
1008 784 /**
1009 785 * APPLY_FILTERS: rtsb/core/allowed_icon_html
@@ -1062,16 +838,8 @@
1062 838
1063 839 /**
1064 840 * Prints HTMl.
1065 841 *
1066 - * The full-markup branch is echoed as-is. It used to run stripslashes_deep(),
1067 - * which corrupts already-rendered markup: product loop templates embed JSON in
1068 - * attributes (e.g. the Variation Swatches `data-product_variations` payload),
1069 - * and stripping backslashes turns `\"` into `"` and `\n` into `n`, so the JSON
1070 - * no longer parses and every swatch renders disabled. Rendered HTML never
1071 - * carries WordPress slashes — values that do are unslashed where they are read
1072 - * (see stripslashes_value()), not at output time.
1073 - *
1074 842 * @param string $html HTML.
1075 843 * @param bool $allHtml All HTML.
1076 844 *
1077 845 * @return void
@@ -1080,9 +848,9 @@
1080 848 if ( ! $html ) {
1081 849 return;
1082 850 }
1083 851 if ( $allHtml ) {
1084 - echo $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
852 + echo stripslashes_deep( $html ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
1085 853 } else {
1086 854 echo wp_kses_post( stripslashes_deep( $html ) );
1087 855 }
1088 856 }
@@ -1095,9 +863,9 @@
1095 863 * @return mixed
1096 864 */
1097 865 public static function allowedHtml( $level = 'basic' ) {
1098 866 $allowed_html = [];
1099 -
867 + // TODO:: Need Optimize.
1100 868 switch ( $level ) {
1101 869 case 'basic':
1102 870 $allowed_html = [
1103 871 'b' => [
@@ -1311,18 +1079,8 @@
1311 1079 *
1312 1080 * @return array
1313 1081 */
1314 1082 public static function get_image_sizes() {
1315 - // Memoized: called repeatedly while the settings schema is built, and each
1316 - // call loops every registered size doing three get_option() lookups for
1317 - // the core ones. Image sizes are registered on after_setup_theme, long
1318 - // before anything here runs, so the list cannot change mid-request.
1319 - static $memo = null;
1320 -
1321 - if ( null !== $memo ) {
1322 - return $memo;
1323 - }
1324 -
1325 1083 global $_wp_additional_image_sizes;
1326 1084
1327 1085 $sizes = [];
1328 1086
@@ -1342,31 +1100,23 @@
1342 1100
1343 1101 $imgSize = [];
1344 1102
1345 1103 foreach ( $sizes as $key => $img ) {
1346 - $imgSize[ $key ] = esc_html( $key ) . " ({$img['width']}*{$img['height']})";
1104 + $imgSize[ $key ] = ucfirst( $key ) . " ({$img['width']}*{$img['height']})";
1347 1105 }
1348 1106
1349 1107 $imgSize['full'] = esc_html__( 'Full size', 'shopbuilder' );
1350 1108 $imgSize['rtsb_custom'] = esc_html__( 'Custom image size', 'shopbuilder' );
1351 1109
1352 - $memo = $imgSize;
1353 -
1354 - return $memo;
1110 + return $imgSize;
1355 1111 }
1356 1112
1357 1113 /**
1358 1114 * Free Layouts
1359 1115 *
1360 - * @param string $layout Layout.
1361 - *
1362 1116 * @return array
1363 1117 */
1364 - public static function free_layouts( $layout = '' ) {
1365 - if ( ! empty( $layout ) && false !== strpos( $layout, 'rtsb-' ) ) {
1366 - return [ $layout => esc_html__( 'Addon Layout', 'shopbuilder' ) ];
1367 - }
1368 -
1118 + public static function free_layouts() {
1369 1119 $layouts = [
1370 1120 'grid-layout1' => esc_html__( 'Grid Layout 1', 'shopbuilder' ),
1371 1121 'grid-layout2' => esc_html__( 'Grid Layout 2', 'shopbuilder' ),
1372 1122 'list-layout1' => esc_html__( 'List Layout 1', 'shopbuilder' ),
@@ -1420,25 +1170,8 @@
1420 1170 }
1421 1171 self::$cache[ $cache_key ] = $terms;
1422 1172 return $terms;
1423 1173 }
1424 - /**
1425 - * Get all product attribute taxonomy by id
1426 - *
1427 - * @param array $term_ids Term id.
1428 - *
1429 - * @return array
1430 - */
1431 - public static function tax_filter_attr_taxonomy( $term_ids ) {
1432 - $taxonomies = [];
1433 - foreach ( $term_ids as $id ) {
1434 - $term = get_term( $id );
1435 - if ( ! is_wp_error( $term ) && $term ) {
1436 - $taxonomies[] = $term->taxonomy;
1437 - }
1438 - }
1439 - return array_unique( $taxonomies );
1440 - }
1441 1174
1442 1175 /**
1443 1176 * Get all terms by attributes
1444 1177 *
@@ -1444,17 +1177,8 @@
1444 1177 *
1445 1178 * @return array
1446 1179 */
1447 1180 public static function get_all_attributes() {
1448 - // Memoize per request: this runs once per product widget while Elementor
1449 - // builds the editor control config, firing a get_terms() query per
1450 - // attribute taxonomy each time. Caching avoids the repeated N queries.
1451 - static $cached = null;
1452 -
1453 - if ( null !== $cached ) {
1454 - return $cached;
1455 - }
1456 -
1457 1181 $terms = [];
1458 1182 $termList = [];
1459 1183
1460 1184 $attributes = wc_get_attribute_taxonomies();
@@ -1474,10 +1198,8 @@
1474 1198 }
1475 1199 }
1476 1200 }
1477 1201
1478 - $cached = $terms;
1479 -
1480 1202 return $terms;
1481 1203 }
1482 1204
1483 1205 /**
@@ -1561,8 +1283,9 @@
1561 1283 if ( ! is_admin() ) {
1562 1284 return [];
1563 1285 }
1564 1286
1287 + // TODO:: Return Slug always true for all settings.
1565 1288 $term_list = [];
1566 1289 $args = [
1567 1290 'taxonomy' => $taxonomy,
1568 1291 'hide_empty' => false,
@@ -1659,13 +1382,8 @@
1659 1382
1660 1383 self::print_html( $html, true );
1661 1384 }
1662 1385
1663 - /**
1664 - * Get product simple rating.
1665 - *
1666 - * @return void
1667 - */
1668 1386 public static function get_product_simple_rating_html() {
1669 1387 global $product;
1670 1388 $html = null;
1671 1389 $rating_count = $product->get_rating_count();
@@ -1777,59 +1495,25 @@
1777 1495
1778 1496 <?php
1779 1497 self::print_html( ob_get_clean() );
1780 1498 }
1781 - /**
1782 - * Brands HTML
1783 - *
1784 - * @param int $id Product ID.
1785 - * @param string $class Custom class.
1786 - *
1787 - * @return void|string
1788 - */
1789 - public static function get_brands_list( $id, $class = 'rtsb-brand-outline' ) {
1790 - $terms = get_the_terms( $id, 'product_brand' );
1791 - if ( empty( $id ) || empty( $terms ) || is_wp_error( $terms ) ) {
1792 - return '';
1793 - }
1794 - ob_start();
1795 - ?>
1796 1499
1797 - <ul class="rtsb-brand-list <?php echo esc_attr( $class ); ?>">
1798 - <?php
1799 - $brand_list = get_the_term_list(
1800 - $id,
1801 - 'product_brand',
1802 - '<li class="rtsb-brand-list-item">',
1803 - '</li><li class="rtsb-brand-list-item">',
1804 - '</li>'
1805 - );
1806 -
1807 - self::print_html( $brand_list );
1808 - ?>
1809 - </ul>
1810 -
1811 - <?php
1812 - self::print_html( ob_get_clean() );
1813 - }
1814 -
1815 1500 /**
1816 1501 * Get Featured Image HTML.
1817 1502 *
1818 - * @param string $type Image type.
1819 - * @param int $post_id Post ID.
1820 - * @param string $f_img_size Image size.
1821 - * @param null $default_img_id Default image ID.
1822 - * @param array $custom_img_size Custom image size.
1823 - * @param bool $lazy Lazy load check.
1824 - * @param bool $hover Hover image.
1825 - * @param bool $gallery Gallery image.
1826 - * @param bool $custom_image_id Custom category image ID.
1827 - * @param bool|string $priority Eager/priority hint: 'high', 'eager', or false (default lazy).
1503 + * @param string $type Image type.
1504 + * @param int $post_id Post ID.
1505 + * @param string $f_img_size Image size.
1506 + * @param null $default_img_id Default image ID.
1507 + * @param array $custom_img_size Custom image size.
1508 + * @param bool $lazy Lazy load check.
1509 + * @param bool $hover Hover image.
1510 + * @param bool $gallery Gallery image.
1511 + * @param bool $custom_image_id Custom category image ID.
1828 1512 *
1829 1513 * @return string|null
1830 1514 */
1831 - public static function get_product_image_html( $type = 'product', $post_id = null, $f_img_size = 'medium', $default_img_id = null, $custom_img_size = [], $lazy = false, $hover = false, $gallery = false, $custom_image_id = 0, $priority = false ) {
1515 + public static function get_product_image_html( $type = 'product', $post_id = null, $f_img_size = 'medium', $default_img_id = null, $custom_img_size = [], $lazy = false, $hover = false, $gallery = false, $custom_image_id = 0 ) {
1832 1516 $img_html = null;
1833 1517 $attachment_id = null;
1834 1518 $c_size = false;
1835 1519 $hover_class = '';
@@ -1868,19 +1552,8 @@
1868 1552 'class' => 'img-responsive rtsb-product-image' . $lazy_class . $hover_class,
1869 1553 'alt' => $alt_tag,
1870 1554 ];
1871 1555
1872 - // Eager-load above-the-fold images so the LCP candidate is fetched
1873 - // immediately instead of being lazy-loaded after render-blocking CSS.
1874 - // Only the first image is marked fetchpriority=high to avoid competing hints.
1875 - if ( $priority ) {
1876 - $attr['loading'] = 'eager';
1877 -
1878 - if ( 'high' === $priority ) {
1879 - $attr['fetchpriority'] = 'high';
1880 - }
1881 - }
1882 -
1883 1556 if ( $a_id ) {
1884 1557 $img_html = wp_get_attachment_image( $a_id, $f_img_size, false, $attr );
1885 1558 $attachment_id = $a_id;
1886 1559 }
@@ -1927,9 +1600,9 @@
1927 1600
1928 1601 if ( ! $img_html ) {
1929 1602 $hwstring = image_hwstring( 160, 160 );
1930 1603 $attr = isset( $attr['src'] ) ? apply_filters( 'wp_get_attachment_image_attributes', $attr, false, $f_img_size ) : [];
1931 - $attr['class'] = 'default-img ' . $hover_class;
1604 + $attr['class'] = 'default-img';
1932 1605 $attr['src'] = esc_url( rtsb()->get_assets_uri( 'images/demo.png' ) );
1933 1606 $attr['alt'] = esc_html__( 'Default Image', 'shopbuilder' );
1934 1607 $img_html = rtrim( "<img $hwstring" );
1935 1608
@@ -2020,9 +1693,9 @@
2020 1693 if ( empty( $control['value'] ) ) {
2021 1694 return '';
2022 1695 }
2023 1696 if ( is_array( $control['value'] ) ) {
2024 - $cache_key = 'icons_managet_' . str_replace( ' ', '', md5( serialize( $control['value'] ) ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize
1697 + $cache_key = 'icons_managet_' . str_replace( ' ', '', md5( serialize( $control['value'] ) ) );
2025 1698 } else {
2026 1699 $cache_key = 'icons_managet_' . str_replace( ' ', '', $control['value'] );
2027 1700 }
2028 1701 if ( isset( self::$cache[ $cache_key ] ) ) {
@@ -2058,25 +1731,9 @@
2058 1731 public static function get_product_swatches( $type ) {
2059 1732 ?>
2060 1733 <div class="rtsb-swatches <?php echo esc_attr( $type ); ?>-layout">
2061 1734 <?php
2062 - /*
2063 - * Hand off to the Variation Swatches plugin's own archive renderer when it
2064 - * is listening, otherwise fall back to the built-in module.
2065 - *
2066 - * Detected with has_action() rather than a class name on purpose: this used
2067 - * to test `class_exists( 'Rtwpvsp' )`, but the plugin moved its classes into
2068 - * a namespace (`Rtwpvsp\Rtwpvsp`), so that check silently started failing.
2069 - * The else branch then fired the built-in module's action — which is never
2070 - * hooked while the plugin is active, because the module deliberately stands
2071 - * down — leaving this wrapper rendered but empty. Asking whether anything is
2072 - * actually listening is both the correct question and rename-proof.
2073 - */
2074 - if ( has_action( 'rtwpvs_show_archive_variation' ) ) {
2075 - do_action( 'rtwpvs_show_archive_variation' );
2076 - } else {
2077 - do_action( 'rtsb/vs/showcase/variation' );
2078 - }
1735 + do_action( 'rtwpvs_show_archive_variation' );
2079 1736 ?>
2080 1737 </div>
2081 1738 <?php
2082 1739 }
@@ -2100,18 +1757,20 @@
2100 1757 if ( ! $product->is_on_sale() ) {
2101 1758 return '';
2102 1759 }
2103 1760
1761 + $badge_text = '';
1762 + $percentage = self::calculate_sale_percentage( $product );
1763 +
1764 + if ( $percentage > 0 ) {
1765 + $badge_text = '-' . round( $percentage ) . '%';
1766 + }
1767 +
2104 1768 if ( 'text' === $type ) {
2105 - return $text;
1769 + $badge_text = $text;
2106 1770 }
2107 1771
2108 - $percentage = self::calculate_sale_percentage( $product );
2109 -
2110 - // The product is on sale (checked above). When a percentage can be derived show
2111 - // it; otherwise (e.g. some variable products where variation prices aren't
2112 - // primed) fall back to the sale text so an on-sale product always shows a badge.
2113 - return $percentage > 0 ? '-' . round( $percentage ) . '%' : $text;
1772 + return $badge_text;
2114 1773 }
2115 1774
2116 1775 /**
2117 1776 * Get sale badge
@@ -2146,18 +1805,20 @@
2146 1805 if ( ! $product->is_on_sale() ) {
2147 1806 return '';
2148 1807 }
2149 1808
1809 + $badge_text = '';
1810 + $percentage = self::calculate_sale_percentage( $product );
1811 +
1812 + if ( $percentage > 0 ) {
1813 + $badge_text = '-' . round( $percentage ) . '%';
1814 + }
1815 +
2150 1816 if ( 'text' === $type ) {
2151 - return $text;
1817 + $badge_text = $text;
2152 1818 }
2153 1819
2154 - $percentage = self::calculate_sale_percentage( $product );
2155 -
2156 - // The product is on sale (checked above). When a percentage can be derived show
2157 - // it; otherwise (e.g. some variable products where variation prices aren't
2158 - // primed) fall back to the sale text so an on-sale product always shows a badge.
2159 - return $percentage > 0 ? '-' . round( $percentage ) . '%' : $text;
1820 + return $badge_text;
2160 1821 }
2161 1822
2162 1823 /**
2163 1824 * Calculate Sale Percentage
@@ -2169,74 +1830,72 @@
2169 1830 public static function calculate_sale_percentage( $product = null ) {
2170 1831 if ( is_null( $product ) ) {
2171 1832 global $product;
2172 1833 }
1834 +
2173 1835 if ( ! $product instanceof WC_Product ) {
2174 1836 return '';
2175 1837 }
1838 +
2176 1839 if ( ! $product->is_on_sale() ) {
2177 1840 return '';
2178 1841 }
2179 - $max_percentage = 0;
1842 +
1843 + $percentage = null;
1844 + $max_percentage = '';
1845 +
2180 1846 if ( $product->is_type( 'simple' ) ) {
2181 - $regular_price = (float) $product->get_regular_price();
2182 - $sale_price = (float) $product->get_sale_price();
2183 - if ( $regular_price > 0 && $sale_price > 0 ) {
2184 - $max_percentage = ( ( $regular_price - $sale_price ) / $regular_price ) * 100;
2185 - }
1847 + $max_percentage = $product->get_sale_price() ? ( ( $product->get_regular_price() - $product->get_sale_price() ) / $product->get_regular_price() ) * 100 : 0;
2186 1848 } elseif ( $product->is_type( 'variable' ) ) {
2187 - $prices = $product->get_variation_prices( true );
2188 - if ( ! empty( $prices['regular_price'] ) && ! empty( $prices['sale_price'] ) ) {
2189 - foreach ( $prices['regular_price'] as $key => $regular_price ) {
2190 - $sale_price = $prices['sale_price'][ $key ];
1849 + $max_percentage = 0;
2191 1850
2192 - if ( $regular_price > 0 && $sale_price > 0 && $regular_price > $sale_price ) {
2193 - $percentage = ( ( $regular_price - $sale_price ) / $regular_price ) * 100;
2194 - $max_percentage = max( $max_percentage, $percentage );
2195 - }
1851 + foreach ( $product->get_children() as $child_id ) {
1852 +
1853 + $variation = wc_get_product( $child_id );
1854 +
1855 + $price = $variation->get_regular_price();
1856 + $sale = $variation->get_sale_price();
1857 +
1858 + if ( 0 !== $price && ! empty( $sale ) ) {
1859 + $percentage = ( $price - $sale ) / $price * 100;
2196 1860 }
1861 +
1862 + if ( $percentage > $max_percentage ) {
1863 + $max_percentage = $percentage;
1864 + }
2197 1865 }
2198 1866 }
2199 - return $max_percentage > 0 ? round( $max_percentage ) : 0;
1867 +
1868 + if ( $max_percentage > 0 ) {
1869 + return round( $max_percentage );
1870 + }
1871 +
1872 + return 0;
2200 1873 }
2201 1874
2202 1875 /**
2203 1876 * Pagination
2204 1877 *
2205 - * @param string $pages Pages.
2206 - * @param integer $per_page Per-page count (unused; kept for positional compatibility).
2207 - * @param boolean $ajax Ajax.
2208 - * @param int|null $visible_range Page links to show on each side of the current
2209 - * page (from the widget's "Page Number Range"
2210 - * control). Falls back to the filter default.
1878 + * @param string $pages Pages.
1879 + * @param integer $range Range.
1880 + * @param boolean $ajax Ajax.
2211 1881 *
2212 1882 * @return string
2213 1883 */
2214 - public static function custom_pagination( $pages = '', $per_page = 4, $ajax = false, $visible_range = null ) {
2215 - $html = null;
1884 + public static function custom_pagination( $pages = '', $range = 4, $ajax = false ) {
1885 + $html = null;
1886 + $visible_range = apply_filters( 'rtsb/elements/pagination/visible_range', ( $range * 2 ) + 1 );
2216 1887
2217 - // How many page links to show on each side of the current page. Driven by
2218 - // the widget's "Page Number Range" control; the filter is the fallback
2219 - // default. Keeping it small truncates long paginations (« ‹ 1 2 3 › »)
2220 - // with jump-to-first/last arrows instead of listing every page number.
2221 - // (The 2nd argument callers pass is the per-page count, not a window).
2222 - if ( null === $visible_range ) {
2223 - $visible_range = apply_filters( 'rtsb/elements/pagination/visible_range', 5 );
2224 - }
2225 -
2226 - $visible_range = max( 1, (int) $visible_range );
2227 -
2228 1888 global $paged;
2229 1889
2230 - // Prefer `paged` (archive pagination), fall back to `page` (paginated singular posts).
2231 - // This handles the case where the shop/archive is set as the front page — `is_front_page()`
2232 - // alone would force `page` and miss the archive `paged` value.
2233 - $paged = get_query_var( 'paged' ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
2234 - if ( ! $paged ) {
2235 - $paged = get_query_var( 'page' ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
1890 + if ( is_front_page() ) {
1891 + $paged = ( get_query_var( 'page' ) ) ? get_query_var( 'page' ) : 1;
1892 + } else {
1893 + $paged = ( get_query_var( 'paged' ) ) ? get_query_var( 'paged' ) : 1;
2236 1894 }
2237 - if ( ! $paged ) {
2238 - $paged = 1; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
1895 +
1896 + if ( empty( $paged ) ) {
1897 + $paged = 1;
2239 1898 }
2240 1899
2241 1900 if ( '' === $pages ) {
2242 1901 global $wp_query;
@@ -2280,9 +1939,9 @@
2280 1939 $p = $paged + 1;
2281 1940 $html .= "<li><a data-paged='{$p}' href=\"" . get_pagenum_link( $paged + 1 ) . "\" aria-label='Next'>&rsaquo;</a></li>";
2282 1941 }
2283 1942
2284 - if ( $paged < $pages - 1 && $paged + $visible_range < $pages && $visible_range < $pages ) {
1943 + if ( $paged < $pages - 1 && $paged + $range - 1 < $pages && $visible_range < $pages ) {
2285 1944 $html .= "<li><a data-paged='{$pages}' href='" . get_pagenum_link( $pages ) . "' aria-label='Last'>&raquo;</a></li>";
2286 1945 }
2287 1946
2288 1947 $html .= '</ul>';
@@ -2394,8 +2053,10 @@
2394 2053 } else {
2395 2054 $class .= ' rtsb-' . esc_attr( str_replace( '_', '-', $type ) );
2396 2055 }
2397 2056
2057 + $html .= '<' . esc_attr( $wrapper ) . ' class="' . esc_attr( $class ) . '">';
2058 +
2398 2059 if ( ( 'add_to_cart' === $type ) && ( ! empty( $cart_html ) ) ) {
2399 2060 $html .= $cart_html;
2400 2061 } else {
2401 2062 $html .= shortcode_exists( 'rtsb_' . $type . '_button' ) ? do_shortcode( '[rtsb_' . $type . '_button]' ) : null;
@@ -2400,11 +2061,9 @@
2400 2061 } else {
2401 2062 $html .= shortcode_exists( 'rtsb_' . $type . '_button' ) ? do_shortcode( '[rtsb_' . $type . '_button]' ) : null;
2402 2063 }
2403 2064
2404 - if ( ! empty( $html ) ) {
2405 - $html = '<' . esc_attr( $wrapper ) . ' class="' . esc_attr( $class ) . '">' . $html . '</' . esc_attr( $wrapper ) . '>';
2406 - }
2065 + $html .= '</' . esc_attr( $wrapper ) . '>';
2407 2066
2408 2067 return apply_filters( 'rtsb/elements/elementor/get_action_button_by_type', $html );
2409 2068 }
2410 2069
@@ -2505,9 +2164,9 @@
2505 2164 $link['social_network'] = 'Facebook';
2506 2165 $link['social_action'] = 'Share';
2507 2166 break;
2508 2167 case 'twitter':
2509 - $link['link'] = esc_url( 'https://x.com/intent/tweet?text=' . htmlspecialchars( rawurlencode( html_entity_decode( $link['title'], ENT_COMPAT, 'UTF-8' ) ), ENT_COMPAT, 'UTF-8' ) . '&url=' . $link['url'] );
2168 + $link['link'] = esc_url( 'https://twitter.com/intent/tweet?text=' . htmlspecialchars( rawurlencode( html_entity_decode( $link['title'], ENT_COMPAT, 'UTF-8' ) ), ENT_COMPAT, 'UTF-8' ) . '&url=' . $link['url'] );
2510 2169 $link['icon'] = '<svg width="24" height="24" viewBox="0 0 1200 1227" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M714.163 519.284L1160.89 0H1055.03L667.137 450.887L357.328 0H0L468.492 681.821L0 1226.37H105.866L515.491 750.218L842.672 1226.37H1200L714.137 519.284H714.163ZM569.165 687.828L521.697 619.934L144.011 79.6944H306.615L611.412 515.685L658.88 583.579L1055.08 1150.3H892.476L569.165 687.854V687.828Z" /></svg>';
2511 2170 $link['attr_title'] = esc_html__( 'Share on Twitter', 'shopbuilder' );
2512 2171 $link['social_network'] = 'Twitter';
2513 2172 $link['social_action'] = 'Tweet';
@@ -2631,83 +2290,27 @@
2631 2290
2632 2291 /**
2633 2292 * Social Share Platforms.
2634 2293 *
2635 - * @param string $module Module.
2636 - *
2637 - * @return true|void
2294 + * @return mixed|null
2638 2295 */
2639 2296 public static function is_module_active( $module ) {
2640 - $modulelist = self::get_modules_list();
2641 -
2297 + $modulelist = ModuleList::instance()->get_data();
2642 2298 if ( ! empty( $modulelist[ $module ]['active'] ) ) {
2643 2299 return true;
2644 2300 }
2645 2301 }
2646 - /**
2647 - * Checks if catalog pro is active
2648 - *
2649 - * @return boolean
2650 - */
2651 - public static function is_catalog_mode() {
2652 - return function_exists( 'rtsbpro' ) && self::is_module_active( 'catalog_mode' );
2653 - }
2654 - /**
2655 - * Checks if back in stock notifier is active
2656 - *
2657 - * @return boolean
2658 - */
2659 - public static function is_back_in_stock_notifier_enable() {
2660 - return function_exists( 'rtsbpro' ) && self::is_module_active( 'back_in_stock_notifier' );
2661 - }
2662 2302
2663 - /**
2664 - * Elementor Widget Active.
2665 - *
2666 - * @param string $widget Elementor Widget.
2667 - *
2668 - * @return true|void
2669 - */
2670 - public static function is_elementor_widget_active( $widget ) {
2671 - $element_list = self::get_widgets_list();
2672 -
2673 - if ( ! empty( $element_list[ $widget ]['active'] ) ) {
2674 - return true;
2675 - }
2676 - }
2677 -
2678 - /**
2679 - * Sanitize a media (fileupload) control value into an { id, source } array.
2680 - *
2681 - * The control value is posted as an array (or a JSON string). Using it as a
2682 - * `sanitize_fn` avoids it being flattened to an empty string by the default
2683 - * sanitize_text_field() path in set_options().
2684 - *
2685 - * @param mixed $raw_value Raw posted value.
2686 - *
2687 - * @return array|string
2688 - */
2689 - public static function sanitize_fileupload_value( $raw_value ) {
2690 - $decoded = is_string( $raw_value ) && '' !== $raw_value ? json_decode( wp_unslash( $raw_value ), true ) : $raw_value;
2691 - if ( ! is_array( $decoded ) ) {
2692 - return '';
2693 - }
2694 - return [
2695 - 'id' => isset( $decoded['id'] ) ? absint( $decoded['id'] ) : 0,
2696 - 'source' => isset( $decoded['source'] ) ? esc_url_raw( $decoded['source'] ) : '',
2697 - ];
2698 - }
2699 -
2700 2303 /***
2701 2304 * Save Settings data
2702 2305 *
2703 - * @param string $section_id Section ID.
2704 - * @param string $block_id Block ID.
2705 - * @param array $rawOptions Raw Options.
2306 + * @param $section_id
2307 + * @param $block_id
2308 + * @param $rawOptions
2706 2309 *
2707 2310 * @return array
2708 2311 */
2709 - public static function set_options( $section_id = '', $block_id = '', $rawOptions = [] ) { // phpcs:ignore Generic.Metrics.NestingLevel.TooHigh
2312 + public static function set_options( $section_id = '', $block_id = '', $rawOptions = [] ) {
2710 2313 $section_id = ! empty( $section_id ) ? sanitize_text_field( wp_unslash( $section_id ) ) : '';
2711 2314 $block_id = ! empty( $block_id ) ? sanitize_text_field( wp_unslash( $block_id ) ) : '';
2712 2315 $rawOptions = ! empty( $rawOptions ) ? $rawOptions : [];
2713 2316 $results = [
@@ -2733,9 +2336,9 @@
2733 2336 $fields = [];
2734 2337 if ( isset( $sections[ $section_id ]['list'][ $block_id ]['fields'] ) && ! empty( $sections[ $section_id ]['list'][ $block_id ]['fields'] ) ) {
2735 2338 $fields = $sections[ $section_id ]['list'][ $block_id ]['fields'];
2736 2339 }
2737 - do_action( 'rtsb/before/save/options', $section_id, $block_id, $rawOptions );
2340 +
2738 2341 if ( empty( $fields ) ) {
2739 2342 if ( isset( $rawOptions['active'] ) ) {
2740 2343 $changed = true;
2741 2344 $options[ $block_id ]['active'] = 'on' === $rawOptions['active'] ? 'on' : '';
@@ -2741,15 +2344,15 @@
2741 2344 $options[ $block_id ]['active'] = 'on' === $rawOptions['active'] ? 'on' : '';
2742 2345 }
2743 2346 } else {
2744 2347 foreach ( $rawOptions as $raw_option_key => $raw_value ) {
2745 - if ( 'active' === $raw_option_key ) {
2348 + if ( $raw_option_key === 'active' ) {
2746 2349 $changed = true;
2747 - $options[ $block_id ]['active'] = $sections[ $section_id ]['list'][ $block_id ]['active'] = 'on' === $raw_value ? 'on' : ''; // phpcs:ignore Squiz.PHP.DisallowMultipleAssignments.Found
2350 + $options[ $block_id ]['active'] = $sections[ $section_id ]['list'][ $block_id ]['active'] = 'on' === $raw_value ? 'on' : '';
2748 2351 } else {
2749 2352 if ( isset( $fields[ $raw_option_key ] ) ) {
2750 2353 $field = $fields[ $raw_option_key ];
2751 - if ( 'switch' === $field['type'] ) {
2354 + if ( $field['type'] === 'switch' ) {
2752 2355 $value = 'on' === $raw_value ? 'on' : '';
2753 2356 } elseif ( 'repeaters' === $field['type'] ) {
2754 2357 $the_value = [];
2755 2358 $rep_title = [];
@@ -2769,21 +2372,21 @@
2769 2372 }
2770 2373 } elseif ( ! empty( $raw_value ) && is_string( $raw_value ) ) {
2771 2374 $the_value = $raw_value;
2772 2375 }
2773 - $value = wp_json_encode( $the_value, JSON_UNESCAPED_UNICODE );
2774 - } elseif ( in_array( $field['type'], [ 'product_addons_special_settings', 'checkout_fields' ] ) ) { // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict
2376 + $value = wp_json_encode( $the_value );
2377 + } elseif ( in_array( $field['type'], [ 'product_addons_special_settings', 'checkout_fields' ] ) ) {
2775 2378 $manual_field_value = [];
2776 2379 if ( is_array( $raw_value ) ) {
2777 2380 foreach ( $raw_value as $key => $value ) {
2778 2381 $manual_field_value[] = json_decode( stripslashes( $value ), true );
2779 2382 }
2780 - $value = wp_json_encode( $manual_field_value, JSON_UNESCAPED_UNICODE );
2383 + $value = wp_json_encode( $manual_field_value );
2781 2384 } elseif ( is_string( $raw_value ) ) {
2782 2385 $value = $raw_value;
2783 2386 }
2784 2387 } else {
2785 - if ( ! empty( $field['multiple'] ) || in_array( $field['type'], [ 'checkbox', 'search_and_multi_select' ] ) ) { // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict
2388 + if ( ! empty( $field['multiple'] ) || in_array( $field['type'], [ 'checkbox', 'search_and_multi_select' ] ) ) {
2786 2389 if ( isset( $raw_value ) && is_array( $raw_value ) ) {
2787 2390 if ( ! empty( $field['sanitize_fn'] ) && is_callable( $field['sanitize_fn'] ) ) {
2788 2391 $value = array_map( $field['sanitize_fn'], $raw_value );
2789 2392 } else {
@@ -2805,9 +2408,9 @@
2805 2408 $value = sanitize_text_field( $raw_value );
2806 2409 }
2807 2410 }
2808 2411 }
2809 - $options[ $block_id ][ $raw_option_key ] = $sections[ $section_id ]['list'][ $block_id ]['fields'][ $raw_option_key ]['value'] = $value ?? null; // phpcs:ignore Squiz.PHP.DisallowMultipleAssignments.Found
2412 + $options[ $block_id ][ $raw_option_key ] = $sections[ $section_id ]['list'][ $block_id ]['fields'][ $raw_option_key ]['value'] = $value ?? null;
2810 2413 $changed = true;
2811 2414 }
2812 2415 }
2813 2416 }
@@ -2827,52 +2430,28 @@
2827 2430
2828 2431 return $results;
2829 2432 }
2830 2433
2831 - /**
2832 - * Count published products matching a tax query.
2434 + /***
2435 + * @param $meta_key
2436 + * @param $meta_value
2833 2437 *
2834 - * Filter badges must agree with the number of products the same filter lists.
2835 - * The archive (and the Ajax filter response) runs a plain WP_Query, while
2836 - * WC_Product_Query additionally restricts results to the registered
2837 - * `product_type` terms — so a product imported without a `product_type` term
2838 - * is listed on the archive but silently dropped from the count, making the
2839 - * badge read lower than the result set (e.g. "Cat1 (4)" for 10 results).
2438 + * @return mixed|void
2840 2439 *
2841 - * @param array $tax_query Tax query clauses.
2842 2440 *
2843 - * @return int Number of matching products.
2441 + * public static function get_post_id_by_meta_key_and_value( $meta_key, $meta_value ) {
2442 + * if( ! $meta_key || ! $meta_value ){
2443 + * return;
2444 + * }
2445 + * global $wpdb;
2446 + * $prepare_guery = $wpdb->prepare( "SELECT post_id FROM $wpdb->postmeta WHERE meta_key = '%s' and meta_value = '%d'", $meta_key, $meta_value );
2447 + * $the_products = $wpdb->get_col( $prepare_guery );
2448 + * if( count( $the_products ) > 0 ){
2449 + * return $the_products[0];
2450 + * }
2451 + * return;
2452 + * }
2844 2453 */
2845 - public static function count_products_by_tax_query( array $tax_query ) {
2846 - if ( empty( $tax_query ) ) {
2847 - return 0;
2848 - }
2849 -
2850 - $query = new WP_Query(
2851 - [
2852 - 'post_type' => 'product',
2853 - 'post_status' => 'publish',
2854 - 'posts_per_page' => -1,
2855 - 'fields' => 'ids',
2856 - 'ignore_sticky_posts' => true,
2857 - 'no_found_rows' => true,
2858 - 'update_post_meta_cache' => false,
2859 - 'update_post_term_cache' => false,
2860 - 'tax_query' => $tax_query, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
2861 - ]
2862 - );
2863 -
2864 - return count( $query->posts );
2865 - }
2866 -
2867 - /**
2868 - * Count products by taxonomies.
2869 - *
2870 - * @param array $terms Terms.
2871 - * @param string $taxonomy Taxonomy.
2872 - *
2873 - * @return int|void
2874 - */
2875 2454 public static function count_products_by_taxonomies( $terms, $taxonomy = 'product_cat' ) {
2876 2455 if ( empty( $terms ) || ! is_array( $terms ) ) {
2877 2456 return;
2878 2457 }
@@ -2883,73 +2462,17 @@
2883 2462 ];
2884 2463
2885 2464 if ( 'product_cat' === $taxonomy ) {
2886 2465 $args['product_category_id'] = $terms;
2887 - } elseif ( 'product_brand' === $taxonomy ) {
2888 - $args['product_brand_id'] = $terms;
2889 2466 } else {
2890 2467 $args['product_tag_id'] = $terms;
2891 2468 }
2892 2469
2893 2470 $query = new WC_Product_Query( $args );
2894 -
2895 2471 return ! empty( $query->get_products() ) ? count( $query->get_products() ) : 0;
2896 2472 }
2897 2473
2898 2474 /**
2899 - * Count products by attribute terms.
2900 - *
2901 - * @param array $term_ids Term IDs.
2902 - * @param string $relation Relation.
2903 - *
2904 - * @return int
2905 - */
2906 - public static function count_products_by_attribute_terms( $term_ids, $relation = 'AND' ) {
2907 - if ( empty( $term_ids ) || ! is_array( $term_ids ) ) {
2908 - return 0;
2909 - }
2910 -
2911 - $terms_by_taxonomy = [];
2912 -
2913 - foreach ( $term_ids as $term_id ) {
2914 - $term = get_term( $term_id );
2915 - if ( ! is_wp_error( $term ) && $term ) {
2916 - if ( ! isset( $terms_by_taxonomy[ $term->taxonomy ] ) ) {
2917 - $terms_by_taxonomy[ $term->taxonomy ] = [];
2918 - }
2919 - $terms_by_taxonomy[ $term->taxonomy ][] = $term_id;
2920 - }
2921 - }
2922 -
2923 - if ( empty( $terms_by_taxonomy ) ) {
2924 - return 0;
2925 - }
2926 -
2927 - $args = [
2928 - 'status' => 'publish',
2929 - 'limit' => -1,
2930 - 'return' => 'ids',
2931 - 'tax_query' => [ // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
2932 - 'relation' => strtoupper( $relation ),
2933 - ],
2934 - ];
2935 -
2936 - foreach ( $terms_by_taxonomy as $taxonomy => $terms ) {
2937 - $args['tax_query'][] = [
2938 - 'taxonomy' => $taxonomy,
2939 - 'field' => 'term_id',
2940 - 'terms' => $terms,
2941 - 'operator' => 'IN',
2942 - ];
2943 - }
2944 -
2945 - $query = new WC_Product_Query( $args );
2946 - $products = $query->get_products();
2947 -
2948 - return count( $products );
2949 - }
2950 -
2951 - /**
2952 2475 * Check if product filters widget has ajax.
2953 2476 *
2954 2477 * @param string $page Page name.
2955 2478 *
@@ -2983,233 +2506,8 @@
2983 2506 return ! empty( $ajax[0] );
2984 2507 }
2985 2508
2986 2509 /**
2987 - * Build the active filter chips HTML from the current request query string.
2988 - *
2989 - * Mirrors the markup produced by the JS `displayAppliedFilter()` method so
2990 - * the chips are visible on initial page load (before AJAX hydration).
2991 - *
2992 - * @return string HTML for the active filter chips, or empty string if none.
2993 - */
2994 - public static function get_active_filters_html() {
2995 - // phpcs:disable WordPress.Security.NonceVerification.Recommended -- Read-only query string parsing for display.
2996 - if ( empty( $_GET ) ) {
2997 - return '';
2998 - }
2999 -
3000 - $skip_keys = [
3001 - 'displayview',
3002 - 'orderby',
3003 - 'paged',
3004 - 'page',
3005 - 'product-page',
3006 - 'rtsb_archive_page',
3007 - 'rtsb_grid_columns',
3008 - 'min_price',
3009 - 'max_price',
3010 - '_pjax',
3011 - ];
3012 -
3013 - // When the "Product Categories" widget is already showing the active
3014 - // category navigation, suppress the duplicate product_cat chip group.
3015 - if ( self::is_product_categories_widget_active() ) {
3016 - $skip_keys[] = 'product_cat';
3017 - }
3018 -
3019 - $groups = [];
3020 -
3021 - foreach ( $_GET as $raw_key => $raw_value ) {
3022 - $key = sanitize_key( $raw_key );
3023 -
3024 - if ( '' === $key || in_array( $key, $skip_keys, true ) ) {
3025 - continue;
3026 - }
3027 -
3028 - if ( 0 === strpos( $key, 'query_type_' ) ) {
3029 - continue;
3030 - }
3031 -
3032 - if ( is_array( $raw_value ) ) {
3033 - continue;
3034 - }
3035 -
3036 - $value = sanitize_text_field( wp_unslash( $raw_value ) );
3037 -
3038 - if ( '' === $value ) {
3039 - continue;
3040 - }
3041 -
3042 - $taxonomy = $key;
3043 - if ( 0 === strpos( $taxonomy, 'filter_' ) ) {
3044 - $taxonomy = 'pa_' . substr( $taxonomy, 7 );
3045 - }
3046 -
3047 - $items = [];
3048 - foreach ( explode( ',', $value ) as $slug ) {
3049 - $slug = trim( $slug );
3050 - if ( '' === $slug ) {
3051 - continue;
3052 - }
3053 - $items[] = [
3054 - 'slug' => $slug,
3055 - 'name' => self::get_active_filter_term_label( $taxonomy, $slug, $key ),
3056 - ];
3057 - }
3058 -
3059 - if ( empty( $items ) ) {
3060 - continue;
3061 - }
3062 -
3063 - $groups[ $key ] = [
3064 - 'label' => self::get_active_filter_group_label( $taxonomy, $key ),
3065 - 'items' => $items,
3066 - 'class' => $key,
3067 - ];
3068 - }
3069 -
3070 - // Price range as a single combined chip.
3071 - if ( isset( $_GET['min_price'] ) || isset( $_GET['max_price'] ) ) {
3072 - $min = isset( $_GET['min_price'] ) ? sanitize_text_field( wp_unslash( $_GET['min_price'] ) ) : '';
3073 - $max = isset( $_GET['max_price'] ) ? sanitize_text_field( wp_unslash( $_GET['max_price'] ) ) : '';
3074 -
3075 - if ( '' !== $min || '' !== $max ) {
3076 - $symbol = function_exists( 'get_woocommerce_currency_symbol' ) ? get_woocommerce_currency_symbol() : '';
3077 - $groups['price_filter'] = [
3078 - 'label' => esc_html__( 'Price', 'shopbuilder' ),
3079 - 'class' => 'price_filter',
3080 - 'items' => [
3081 - [
3082 - 'slug' => $min . ',' . $max,
3083 - 'name' => $symbol . $min . ' - ' . $symbol . $max,
3084 - 'extra_class' => ' remove-price',
3085 - ],
3086 - ],
3087 - ];
3088 - }
3089 - }
3090 -
3091 - if ( empty( $groups ) ) {
3092 - return '';
3093 - }
3094 -
3095 - $html = '<div class="rtsb-active-filters">';
3096 -
3097 - foreach ( $groups as $key => $group ) {
3098 - $html .= '<div class="active-filter ' . esc_attr( $group['class'] ) . '">';
3099 -
3100 - if ( ! empty( $group['label'] ) ) {
3101 - $html .= '<div class="filter-name">' . esc_html( $group['label'] ) . ': </div>';
3102 - }
3103 -
3104 - $html .= '<div class="filter-item-container">';
3105 -
3106 - foreach ( $group['items'] as $item ) {
3107 - $extra = isset( $item['extra_class'] ) ? $item['extra_class'] : '';
3108 - $html .= '<div class="filter-item">' . esc_html( $item['name'] );
3109 - $html .= '<span class="remove-filter' . esc_attr( $extra ) . '"';
3110 - $html .= ' title="' . esc_attr( sprintf( /* translators: %s: filter term name */ __( 'Remove %s', 'shopbuilder' ), $item['name'] ) ) . '"';
3111 - $html .= ' data-filter-name="' . esc_attr( $key ) . '"';
3112 - $html .= ' data-filter-value="' . esc_attr( $item['slug'] ) . '">';
3113 - $html .= '<i class="rtsb-icon rtsb-icon-delete"></i></span>';
3114 - $html .= '</div>';
3115 - }
3116 -
3117 - $html .= '</div></div>';
3118 - }
3119 -
3120 - $html .= '<a href="#" class="rtsb-clear-filters"><span class="icon-wrap"><i class="eicon-trash-o"></i></span><span>' . esc_html__( 'Reset Filters', 'shopbuilder' ) . '</span></a>';
3121 - $html .= '</div>';
3122 -
3123 - return $html;
3124 - // phpcs:enable
3125 - }
3126 -
3127 - /**
3128 - * Determine whether the "Product Categories" Elementor widget is present
3129 - * on the current shop/archive layout. Used to avoid duplicating the
3130 - * product_cat chip group when the widget already shows the active term.
3131 - *
3132 - * @return bool
3133 - */
3134 - private static function is_product_categories_widget_active() {
3135 - $elmap = ElementorDataMap::instance();
3136 -
3137 - foreach ( [ 'shop', 'archive' ] as $page ) {
3138 - $id = BuilderFns::is_builder_preview() ? get_the_ID() : BuilderFns::builder_page_id_by_type( $page );
3139 -
3140 - if ( ! $id ) {
3141 - continue;
3142 - }
3143 -
3144 - $widgets = $elmap->get_widget_data( 'rtsb-product-categories-general', [], $id );
3145 -
3146 - if ( ! empty( $widgets ) ) {
3147 - return true;
3148 - }
3149 - }
3150 -
3151 - return false;
3152 - }
3153 -
3154 - /**
3155 - * Resolve the display label for an active filter group (taxonomy heading).
3156 - *
3157 - * @param string $taxonomy Resolved taxonomy slug (e.g. product_cat, pa_color).
3158 - * @param string $raw_key Original URL key (e.g. product_cat, filter_color).
3159 - *
3160 - * @return string
3161 - */
3162 - private static function get_active_filter_group_label( $taxonomy, $raw_key ) {
3163 - if ( 's' === $raw_key || 'search' === $raw_key ) {
3164 - return esc_html__( 'Search', 'shopbuilder' );
3165 - }
3166 - if ( 'rating_filter' === $raw_key ) {
3167 - return esc_html__( 'Ratings', 'shopbuilder' );
3168 - }
3169 - if ( 'sale_filter' === $raw_key ) {
3170 - return esc_html__( 'Sale Filter', 'shopbuilder' );
3171 - }
3172 -
3173 - if ( 0 === strpos( $taxonomy, 'pa_' ) && function_exists( 'wc_attribute_label' ) ) {
3174 - return wc_attribute_label( $taxonomy );
3175 - }
3176 -
3177 - if ( taxonomy_exists( $taxonomy ) ) {
3178 - $tax_obj = get_taxonomy( $taxonomy );
3179 - if ( $tax_obj && ! empty( $tax_obj->labels->name ) ) {
3180 - return $tax_obj->labels->name;
3181 - }
3182 - }
3183 -
3184 - return ucwords( str_replace( [ '_', '-' ], ' ', $raw_key ) );
3185 - }
3186 -
3187 - /**
3188 - * Resolve a term slug to its display name; fall back to a humanised slug.
3189 - *
3190 - * @param string $taxonomy Resolved taxonomy slug.
3191 - * @param string $slug Raw slug from query string.
3192 - * @param string $raw_key Original URL key.
3193 - *
3194 - * @return string
3195 - */
3196 - private static function get_active_filter_term_label( $taxonomy, $slug, $raw_key ) {
3197 - if ( 's' === $raw_key || 'search' === $raw_key || 'rating_filter' === $raw_key ) {
3198 - return rawurldecode( $slug );
3199 - }
3200 -
3201 - if ( taxonomy_exists( $taxonomy ) ) {
3202 - $term = get_term_by( 'slug', $slug, $taxonomy );
3203 - if ( $term && ! is_wp_error( $term ) ) {
3204 - return $term->name;
3205 - }
3206 - }
3207 -
3208 - return ucwords( str_replace( [ '-', '_' ], ' ', rawurldecode( $slug ) ) );
3209 - }
3210 -
3211 - /**
3212 2510 * Check if product has applied filter.
3213 2511 *
3214 2512 * @param string $page Page name.
3215 2513 *
@@ -3230,15 +2528,8 @@
3230 2528 $ajax[] = ! isset( $data['settings']['active_filter'] );
3231 2529
3232 2530 }
3233 2531
3234 - // Also account for the free, non-AJAX "Product Filters" widget so that
3235 - // the active filter chips wrapper renders for it on initial page load.
3236 - foreach ( $elmap->get_widget_data( 'rtsb-product-filters', [], $id ) as $data ) {
3237 - $ajax[] = true;
3238 - unset( $data );
3239 - }
3240 -
3241 2532 return ! empty( $ajax[0] );
3242 2533 }
3243 2534
3244 2535 /**
@@ -3252,14 +2543,14 @@
3252 2543 if ( ! is_admin() ) {
3253 2544 return [];
3254 2545 }
3255 2546
3256 - $cache_key = 'rtsb_product_categories_' . md5( serialize( $search_query ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize
2547 + $cache_key = 'rtsb_product_categories_' . md5( serialize( $search_query ) );
3257 2548
3258 2549 if ( isset( self::$cache[ $cache_key ] ) ) {
3259 2550 return self::$cache[ $cache_key ];
3260 2551 }
3261 - $results = wp_cache_get( $cache_key, Cache::group() );
2552 + $results = wp_cache_get( $cache_key, 'shopbuilder' );
3262 2553 if ( ! $results ) {
3263 2554 global $wpdb;
3264 2555 $sql = "SELECT t.term_id, t.name
3265 2556 FROM {$wpdb->terms} t
@@ -3274,9 +2565,9 @@
3274 2565 }
3275 2566
3276 2567 $results = $wpdb->get_results( $prepared_sql ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.PreparedSQL.NotPrepared
3277 2568 // Cache the results in the object cache for future use.
3278 - wp_cache_set( $cache_key, $results, Cache::group(), 12 * HOUR_IN_SECONDS );
2569 + wp_cache_set( $cache_key, $results, 'shopbuilder' ); // Adjust the expiration time as needed.
3279 2570 Cache::set_data_cache_key( $cache_key );
3280 2571 }
3281 2572
3282 2573 $cats = [];
@@ -3308,14 +2599,14 @@
3308 2599 if ( ! is_admin() ) {
3309 2600 return [];
3310 2601 }
3311 2602
3312 - $cache_key = 'rtsb_product_tags_' . md5( serialize( $search_query ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize
2603 + $cache_key = 'rtsb_product_tags_' . md5( serialize( $search_query ) );
3313 2604
3314 2605 if ( isset( self::$cache[ $cache_key ] ) ) {
3315 2606 return self::$cache[ $cache_key ];
3316 2607 }
3317 - $results = wp_cache_get( $cache_key, Cache::group() );
2608 + $results = wp_cache_get( $cache_key, 'shopbuilder' );
3318 2609 if ( ! $results ) {
3319 2610 global $wpdb;
3320 2611 $sql = "SELECT t.term_id, t.name
3321 2612 FROM {$wpdb->terms} t
@@ -3330,9 +2621,9 @@
3330 2621 }
3331 2622
3332 2623 $results = $wpdb->get_results( $prepared_sql ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.PreparedSQL.NotPrepared
3333 2624 // Cache the results in the object cache for future use.
3334 - wp_cache_set( $cache_key, $results, Cache::group(), 12 * HOUR_IN_SECONDS );
2625 + wp_cache_set( $cache_key, $results, 'shopbuilder' ); // Adjust the expiration time as needed.
3335 2626 Cache::set_data_cache_key( $cache_key );
3336 2627 }
3337 2628
3338 2629 $cats = [];
@@ -3412,9 +2703,8 @@
3412 2703 /**
3413 2704 * Get Post Types.
3414 2705 *
3415 2706 * @param string $search_query Search query.
3416 - * @param array $args Arguments.
3417 2707 *
3418 2708 * @return array
3419 2709 */
3420 2710 public static function get_post_types( $search_query = null, $args = [] ) {
@@ -3425,8 +2715,9 @@
3425 2715 'post_type' => 'any',
3426 2716 'posts_per_page' => 20,
3427 2717 'orderby' => 'ID',
3428 2718 'order' => 'DESC',
2719 + // 'suppress_filters' => false, // WPML Support, Remove Others Languages.
3429 2720 ]
3430 2721 );
3431 2722
3432 2723 if ( 'archive' !== $args['post_type'] ) {
@@ -3484,9 +2775,9 @@
3484 2775 public static function renderView( $viewName, $args = [], $return = false ) {
3485 2776 $viewName = str_replace( '.', '/', $viewName );
3486 2777
3487 2778 if ( ! empty( $args ) && is_array( $args ) ) {
3488 - extract( $args ); // phpcs:ignore WordPress.PHP.DontExtract.extract_extract
2779 + extract( $args );
3489 2780 }
3490 2781
3491 2782 $view_file = rtsb()->plugin_path() . '/resources/' . $viewName . '.php';
3492 2783
@@ -3508,9 +2799,9 @@
3508 2799
3509 2800 /**
3510 2801 * Best selling product query.
3511 2802 *
3512 - * @param int $minimum_sale Minimum sale.
2803 + * @param int $minimum_sale Minimum sale/
3513 2804 * @param int $limit Total limit.
3514 2805 *
3515 2806 * @return array|mixed
3516 2807 */
@@ -3603,9 +2894,12 @@
3603 2894
3604 2895 /**
3605 2896 * Checks if a feature is disabled for guest users based on the provided option.
3606 2897 *
3607 - * @return void
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
3608 2902 */
3609 2903 public static function woocommerce_output_all_notices() {
3610 2904 if ( function_exists( 'wc_print_notices' ) ) :
3611 2905 echo '<div class="rtsb-notice">';
@@ -3724,8 +3018,9 @@
3724 3018 /**
3725 3019 * Generates HTML markup for displaying an endpoint icon.
3726 3020 *
3727 3021 * @param array $data The endpoint data containing icon information.
3022 + * @param string $endpoint The endpoint identifier.
3728 3023 * @param boolean $wrapper Whether to wrap the icon in a span element.
3729 3024 *
3730 3025 * @return string
3731 3026 */
@@ -3808,18 +3103,12 @@
3808 3103
3809 3104 /**
3810 3105 * Social Share Platforms.
3811 3106 *
3812 - * @param string $section_id Section ID.
3813 - * @param string $block_id Block ID.
3814 - * @param string $option_key Option key.
3815 - * @param string $compare_key Compare key.
3816 - * @param string $compare_value Compare value.
3817 - * @param array $values Values.
3818 - *
3819 - * @return void
3107 + * @return mixed|null
3820 3108 */
3821 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 );
3822 3111 $modules = DataModel::source()->get_option( $section_id, [], false );
3823 3112 if ( empty( $modules[ $block_id ] ) ) {
3824 3113 return;
3825 3114 }
@@ -3865,14 +3154,14 @@
3865 3154 *
3866 3155 * @param array $options CSS options to apply (e.g. padding, margin).
3867 3156 * @param string $cache_key Cache key for the CSS.
3868 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.
3869 3159 *
3870 3160 * @return void
3871 3161 */
3872 - public static function dynamic_styles( $options, $cache_key, $css_properties ) {
3873 - $cached_css = wp_cache_get( $cache_key, Cache::group() );
3874 - $style_handle = self::optimized_handle( 'rtsb-frontend' );
3162 + public static function dynamic_styles( $options, $cache_key, $css_properties, $style_handle = 'rtsb-frontend' ) {
3163 + $cached_css = wp_cache_get( $cache_key, 'shopbuilder' );
3875 3164
3876 3165 if ( false !== $cached_css ) {
3877 3166 wp_add_inline_style( $style_handle, $cached_css );
3878 3167 return;
@@ -3907,9 +3196,9 @@
3907 3196 }
3908 3197
3909 3198 $dynamic_css = implode( ' ', $css_rules );
3910 3199
3911 - wp_cache_set( $cache_key, $dynamic_css, Cache::group(), 12 * HOUR_IN_SECONDS );
3200 + wp_cache_set( $cache_key, $dynamic_css, 'shopbuilder', 12 * HOUR_IN_SECONDS );
3912 3201 Cache::set_data_cache_key( $cache_key );
3913 3202
3914 3203 if ( ! empty( $dynamic_css ) ) {
3915 3204 wp_add_inline_style( $style_handle, $dynamic_css );
@@ -3919,11 +3208,8 @@
3919 3208 /**
3920 3209 * Pro version notice.
3921 3210 *
3922 3211 * @param string $ver Pro Version.
3923 - * @param string $tab Tab.
3924 - * @param string $name Name.
3925 - * @param bool $enable_free_Link Enable free link.
3926 3212 *
3927 3213 * @return array[]
3928 3214 */
3929 3215 public static function pro_version_notice( $ver, $tab = 'billing_fields', $name = 'ShopBuilder', $enable_free_Link = true ) {
@@ -3972,9 +3258,9 @@
3972 3258 if ( isset( self::$cache[ $cache_key ] ) ) {
3973 3259 return self::$cache[ $cache_key ];
3974 3260 }
3975 3261
3976 - $cached_result = wp_cache_get( $cache_key, Cache::group() );
3262 + $cached_result = wp_cache_get( $cache_key, 'shopbuilder' );
3977 3263
3978 3264 if ( false !== $cached_result ) {
3979 3265 self::$cache[ $cache_key ] = $cached_result;
3980 3266
@@ -3983,575 +3269,10 @@
3983 3269
3984 3270 $gallery_ids = $product->get_gallery_image_ids();
3985 3271 self::$cache[ $cache_key ] = $gallery_ids;
3986 3272
3987 - wp_cache_set( $cache_key, $gallery_ids, Cache::group(), 12 * HOUR_IN_SECONDS );
3273 + wp_cache_set( $cache_key, $gallery_ids, 'shopbuilder', 12 * HOUR_IN_SECONDS );
3988 3274 Cache::set_data_cache_key( $cache_key );
3989 3275
3990 3276 return $gallery_ids;
3991 - }
3992 -
3993 - /**
3994 - * Check if optimization setting is turned on in the database.
3995 - *
3996 - * This only checks the user's setting value, without validating
3997 - * whether the cache directory is writable.
3998 - *
3999 - * @return bool
4000 - */
4001 - public static function is_optimization_setting_on() {
4002 - $has_pro = rtsb()->has_pro();
4003 - $pro_ver = defined( 'RTSBPRO_VERSION' ) ? RTSBPRO_VERSION : 0;
4004 -
4005 - if ( $has_pro && version_compare( $pro_ver, '2.0.0', '<' ) ) {
4006 - return false;
4007 - }
4008 -
4009 - $generalList = GeneralList::instance()->get_data();
4010 -
4011 - return 'on' === ( $generalList['optimization']['enable_optimization'] ?? '' );
4012 - }
4013 -
4014 - /**
4015 - * Check if optimization is enabled and the cache directory is writable.
4016 - *
4017 - * @return bool
4018 - */
4019 - public static function is_optimization_enabled() {
4020 - // A bundle could not be built during this request, fall back to the
4021 - // unbundled assets rather than emitting handles with an empty source.
4022 - if ( self::$optimization_unavailable ) {
4023 - return false;
4024 - }
4025 -
4026 - if ( ! self::is_optimization_setting_on() ) {
4027 - return false;
4028 - }
4029 -
4030 - $upload = wp_upload_dir();
4031 - $cache_dir = trailingslashit( $upload['basedir'] ) . 'shopbuilder_uploads/cache/';
4032 -
4033 - if ( ! wp_is_writable( $cache_dir ) ) {
4034 - return false;
4035 - }
4036 -
4037 - return true;
4038 - }
4039 -
4040 - /**
4041 - * Get the optimized handle.
4042 - *
4043 - * @param string $handle Base handle used for script/style IDs.
4044 - *
4045 - * @return string
4046 - */
4047 - public static function optimized_handle( $handle ) {
4048 - $use_optimization = self::is_optimization_enabled();
4049 -
4050 - if ( $use_optimization ) {
4051 - $handle = self::is_contextual_loading() ? self::get_optimized_handle_by_context() : 'rtsb-bundled';
4052 - }
4053 -
4054 - return $handle;
4055 - }
4056 -
4057 - /**
4058 - * Get the optimized handle by context.
4059 - *
4060 - * @return string
4061 - */
4062 - public static function get_optimized_handle_by_context() {
4063 - $context = self::detect_context();
4064 -
4065 - if ( 'account' === $context ) {
4066 - return 'rtsb-bundled-global';
4067 - }
4068 -
4069 - return "rtsb-bundled-$context";
4070 - }
4071 -
4072 - /**
4073 - * Check if Elementor page.
4074 - *
4075 - * @param int $post_id Post ID.
4076 - *
4077 - * @return bool
4078 - */
4079 - public static function is_elementor_page( $post_id = null ) {
4080 - if ( ! defined( 'ELEMENTOR_VERSION' ) ) {
4081 - return false;
4082 - }
4083 -
4084 - $post_id = $post_id ?: get_the_ID();
4085 -
4086 - if ( ! $post_id ) {
4087 - return true;
4088 - }
4089 -
4090 - return Plugin::$instance->documents->get( $post_id )->is_built_with_elementor();
4091 - }
4092 -
4093 - /**
4094 - * Check if shop or archive.
4095 - *
4096 - * @return bool
4097 - */
4098 - public static function is_shop_or_archive() {
4099 - return is_shop() || is_product_category() || is_product_tag() || is_post_type_archive( 'product' ) || BuilderFns::is_archive() || BuilderFns::is_shop() || is_tax( 'product_brand' );
4100 - }
4101 -
4102 - /**
4103 - * Detect context.
4104 - *
4105 - * @return string
4106 - */
4107 - public static function detect_context() {
4108 - switch ( true ) {
4109 - case is_product() || BuilderFns::is_product():
4110 - $context = 'product';
4111 - break;
4112 -
4113 - case is_cart() || BuilderFns::is_cart():
4114 - $context = 'cart';
4115 - break;
4116 -
4117 - case is_checkout() || BuilderFns::is_checkout():
4118 - $context = 'checkout';
4119 - break;
4120 -
4121 - case self::is_shop_or_archive():
4122 - $context = 'shop';
4123 - break;
4124 -
4125 - default:
4126 - $context = 'global';
4127 - break;
4128 - }
4129 -
4130 - return apply_filters( 'rtsb/optimizer/context_detect', $context );
4131 - }
4132 -
4133 - /**
4134 - * Enqueue optimized assets.
4135 - *
4136 - * @return void
4137 - */
4138 - public static function enqueue_optimized_assets() {
4139 - $asset_registry = AssetRegistry::instance();
4140 - $bundled_assets = $asset_registry->get_bundled_assets();
4141 - $context = self::detect_context();
4142 - $contextual_loading = self::is_contextual_loading();
4143 - $theme_handle = self::find_theme_stylesheet_handle();
4144 -
4145 - if ( $contextual_loading ) {
4146 - // Enqueue JS.
4147 - if ( ! empty( $bundled_assets['js'] ) ) {
4148 - $js_context = isset( $bundled_assets['js'][ $context ] ) ? $context : 'global';
4149 -
4150 - if ( $js_context ) {
4151 - wp_enqueue_script( $bundled_assets['js'][ $js_context ]['handle'] );
4152 - }
4153 - }
4154 -
4155 - // Enqueue CSS.
4156 - if ( ! empty( $bundled_assets['css'] ) ) {
4157 - $css_context = isset( $bundled_assets['css'][ $context ] ) ? $context : 'global';
4158 -
4159 - if ( $css_context ) {
4160 - $handle = $bundled_assets['css'][ $css_context ]['handle'];
4161 -
4162 - wp_enqueue_style( $handle );
4163 -
4164 - if ( ! empty( $theme_handle ) ) {
4165 - self::set_theme_dependency( $theme_handle, $handle );
4166 - }
4167 - }
4168 - }
4169 - } else {
4170 - // Enqueue JS.
4171 - if ( ! empty( $bundled_assets['js'] ) ) {
4172 - wp_enqueue_script( $bundled_assets['js']['handle'] );
4173 - }
4174 -
4175 - // Enqueue CSS.
4176 - if ( ! empty( $bundled_assets['css'] ) ) {
4177 - $handle = $bundled_assets['css']['handle'];
4178 -
4179 - wp_enqueue_style( $handle );
4180 -
4181 - if ( ! empty( $theme_handle ) ) {
4182 - self::set_theme_dependency( $theme_handle, $handle );
4183 - }
4184 - }
4185 - }
4186 - }
4187 -
4188 - /**
4189 - * Find theme stylesheet handle.
4190 - *
4191 - * @return string|false
4192 - */
4193 - private static function find_theme_stylesheet_handle() {
4194 - static $cached_handle = null;
4195 -
4196 - if ( null !== $cached_handle ) {
4197 - return $cached_handle;
4198 - }
4199 -
4200 - global $wp_styles;
4201 -
4202 - if ( ! $wp_styles instanceof WP_Styles ) {
4203 - return false;
4204 - }
4205 -
4206 - $stylesheet_uri = get_stylesheet_uri();
4207 - $theme_directory_uri = get_stylesheet_directory_uri();
4208 -
4209 - $hash = md5( $stylesheet_uri );
4210 - $transient_key = 'rtsb_theme_css_handle_' . $hash;
4211 -
4212 - $transient = get_transient( $transient_key );
4213 -
4214 - if ( false !== $transient ) {
4215 - $cached_handle = $transient;
4216 -
4217 - return $transient;
4218 - }
4219 -
4220 - foreach ( $wp_styles->registered as $handle => $style ) {
4221 - $src = $style->src;
4222 -
4223 - if (
4224 - ( $src === $stylesheet_uri || strpos( $src, $theme_directory_uri ) !== false ) &&
4225 - strpos( $src, 'style.css' ) !== false
4226 - ) {
4227 - set_transient( $transient_key, $handle, DAY_IN_SECONDS );
4228 - $cached_handle = $handle;
4229 -
4230 - return $handle;
4231 - }
4232 - }
4233 -
4234 - $filtered_handle = apply_filters( 'rtsb/optimizer/theme_stylesheet_handle', false );
4235 -
4236 - if ( is_string( $filtered_handle ) && ! empty( $filtered_handle ) ) {
4237 - set_transient( $transient_key, $filtered_handle, DAY_IN_SECONDS );
4238 - $cached_handle = $filtered_handle;
4239 -
4240 - return $filtered_handle;
4241 - }
4242 -
4243 - set_transient( $transient_key, false, DAY_IN_SECONDS );
4244 - $cached_handle = false;
4245 -
4246 - return false;
4247 - }
4248 -
4249 - /**
4250 - * Set theme dependency.
4251 - *
4252 - * @param string $theme_handle Theme handle.
4253 - * @param string $our_handle Our handle.
4254 - *
4255 - * @return void
4256 - */
4257 - private static function set_theme_dependency( $theme_handle, $our_handle ) {
4258 - global $wp_styles;
4259 -
4260 - if ( ! isset( $wp_styles->registered[ $theme_handle ] ) ) {
4261 - return;
4262 - }
4263 -
4264 - $theme_style = $wp_styles->registered[ $theme_handle ];
4265 -
4266 - // Add our handle as a dependency if it's not already there.
4267 - if ( ! in_array( $our_handle, $theme_style->deps, true ) ) {
4268 - $theme_style->deps[] = $our_handle;
4269 - }
4270 - }
4271 -
4272 - /**
4273 - * Check if Elementor scripts should be loaded.
4274 - *
4275 - * @return bool
4276 - */
4277 - public static function should_load_elementor_scripts() {
4278 - $data = GeneralList::instance()->get_data()['optimization'] ?? [];
4279 -
4280 - $enable_optimization = $data['enable_optimization'] ?? 'on';
4281 - $load_elementor_scripts = $data['load_elementor_scripts'] ?? 'on';
4282 -
4283 - if ( 'on' !== $enable_optimization ) {
4284 - return true;
4285 - }
4286 -
4287 - return 'on' === $load_elementor_scripts;
4288 - }
4289 -
4290 - /**
4291 - * Locate asset.
4292 - *
4293 - * @param string $relative_path Relative path.
4294 - *
4295 - * @return string|null
4296 - */
4297 - public static function locate_asset( $relative_path ) {
4298 - $free_context = rtsb();
4299 - $pro_context = function_exists( 'rtsbpro' ) && rtsb()->has_pro() ? rtsbpro() : null;
4300 -
4301 - $paths = [];
4302 -
4303 - if ( $pro_context ) {
4304 - $paths[] = $pro_context->get_assets_path( $relative_path );
4305 - }
4306 -
4307 - $paths[] = $free_context->get_assets_path( $relative_path );
4308 -
4309 - foreach ( $paths as $path ) {
4310 - if ( file_exists( $path ) ) {
4311 - return $path;
4312 - }
4313 - }
4314 -
4315 - return null;
4316 - }
4317 -
4318 - /**
4319 - * Enqueue module assets.
4320 - *
4321 - * @param string $handle Asset handle.
4322 - * @param string $module_name Module name.
4323 - * @param array $options Options array: ['type' => 'css|js|both', 'deps' => [], 'context' => null].
4324 - *
4325 - * @return string
4326 - */
4327 - public static function enqueue_module_assets( $handle, $module_name, $options = [] ) {
4328 - $use_optimization = self::is_optimization_enabled();
4329 - $handle = self::optimized_handle( $handle );
4330 -
4331 - if ( $use_optimization ) {
4332 - return $handle;
4333 - }
4334 -
4335 - $defaults = [
4336 - 'type' => 'both',
4337 - 'deps' => [ 'jquery', 'rtsb-public' ],
4338 - 'context' => null,
4339 - 'version' => RTSB_VERSION,
4340 - ];
4341 -
4342 - $config = array_merge( $defaults, $options );
4343 - $rtl_suffix = is_rtl() ? '-rtl' : '';
4344 - $rtl_dir = is_rtl() ? trailingslashit( 'rtl' ) : trailingslashit( 'css' );
4345 - $context = $config['context'] ?: rtsb();
4346 - $load_css = in_array( $config['type'], [ 'css', 'both' ], true );
4347 - $load_js = in_array( $config['type'], [ 'js', 'both' ], true );
4348 -
4349 - // Register CSS if enabled.
4350 - if ( $load_css ) {
4351 - wp_register_style(
4352 - $handle,
4353 - $context->get_assets_uri( $rtl_dir . 'modules/' . $module_name . $rtl_suffix . '.css' ),
4354 - [],
4355 - $config['version']
4356 - );
4357 - }
4358 -
4359 - // Register JS if enabled.
4360 - if ( $load_js ) {
4361 - wp_register_script(
4362 - $handle,
4363 - $context->get_assets_uri( 'js/modules/' . $module_name . '.js' ),
4364 - $config['deps'],
4365 - $config['version'],
4366 - true
4367 - );
4368 - }
4369 -
4370 - if ( $load_css ) {
4371 - wp_enqueue_style( $handle );
4372 -
4373 - $theme_handle = self::find_theme_stylesheet_handle();
4374 -
4375 - if ( ! empty( $theme_handle ) ) {
4376 - self::set_theme_dependency( $theme_handle, $handle );
4377 - }
4378 - }
4379 -
4380 - if ( $load_js ) {
4381 - wp_enqueue_script( $handle );
4382 - }
4383 -
4384 - return $handle;
4385 - }
4386 -
4387 - /**
4388 - * Check if contextual loading is enabled.
4389 - *
4390 - * @return bool
4391 - */
4392 - public static function is_contextual_loading() {
4393 - $data = GeneralList::instance()->get_data()['optimization'] ?? [];
4394 -
4395 - return ! empty( $data['context_asset_loading'] ) && 'on' === $data['context_asset_loading'];
4396 - }
4397 -
4398 - /**
4399 - * Get Modules list with cache support.
4400 - *
4401 - * @return array
4402 - */
4403 - public static function get_modules_list() {
4404 - static $cached_modules = null;
4405 -
4406 - if ( null !== $cached_modules ) {
4407 - return $cached_modules;
4408 - }
4409 -
4410 - $cache_key = 'rtsb_module_list';
4411 - $cache_group = Cache::group();
4412 -
4413 - $cached = wp_cache_get( $cache_key, $cache_group );
4414 -
4415 - if ( false !== $cached ) {
4416 - $cached_modules = $cached;
4417 -
4418 - return $cached;
4419 - }
4420 -
4421 - $modules = ModuleList::instance()->get_data();
4422 -
4423 - wp_cache_set( $cache_key, $modules, $cache_group, 12 * HOUR_IN_SECONDS );
4424 - Cache::set_data_cache_key( $cache_key );
4425 -
4426 - $cached_modules = $modules;
4427 -
4428 - return $modules;
4429 - }
4430 -
4431 - /**
4432 - * Get Elementor widgets list with cache support.
4433 - *
4434 - * @return array
4435 - */
4436 - public static function get_widgets_list() {
4437 - static $cached_widgets = null;
4438 -
4439 - if ( null !== $cached_widgets ) {
4440 - return $cached_widgets;
4441 - }
4442 -
4443 - $cache_key = 'rtsb_elementor_widget_list';
4444 - $cache_group = Cache::group();
4445 -
4446 - $cached = wp_cache_get( $cache_key, $cache_group );
4447 -
4448 - if ( false !== $cached ) {
4449 - $cached_widgets = $cached;
4450 -
4451 - return $cached;
4452 - }
4453 -
4454 - $widgets = ElementList::instance()->get_list();
4455 -
4456 - wp_cache_set( $cache_key, $widgets, $cache_group, 12 * HOUR_IN_SECONDS );
4457 - Cache::set_data_cache_key( $cache_key );
4458 -
4459 - $cached_widgets = $widgets;
4460 -
4461 - return $widgets;
4462 - }
4463 -
4464 - /**
4465 - * Convert the given price to the active currency.
4466 - *
4467 - * @param float $price The original price.
4468 - * @param Object||null $product Product.
4469 - *
4470 - * @return float
4471 - */
4472 - public static function get_currency_base_price( $price, $product = null ) {
4473 - return apply_filters( 'rtsb/convert/currency/price', $price, $product );
4474 - }
4475 - /**
4476 - * Generate a signed URL with a payload.
4477 - *
4478 - * @param array $payload Associative data to encode.
4479 - * @param string $base_url Base URL to append ?key=... (e.g. wc_get_checkout_url()).
4480 - * @param string $key Key name to use in the URL.
4481 - * @return string Signed URL with key parameter.
4482 - */
4483 - public static function generate_signed_url( array $payload, string $base_url, $key = 'key' ) {
4484 - if ( empty( $key ) ) {
4485 - $key = 'key';
4486 - }
4487 - // Convert payload to JSON.
4488 - $data = wp_json_encode( $payload );
4489 - // Create signature.
4490 - $signature = wp_hash( $data );
4491 - // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
4492 - $encode_key = base64_encode( $data . '::' . $signature );
4493 - // Return full URL with key param.
4494 - return add_query_arg( [ $key => rawurlencode( $encode_key ) ], $base_url );
4495 - }
4496 -
4497 - /**
4498 - * Decode and verify a signed URL key.
4499 - *
4500 - * @param string $key Encoded key from URL.
4501 - * @return array|false Decoded payload array on success, false on failure.
4502 - */
4503 - public static function decode_signed_key( string $key ) {
4504 - if ( empty( $key ) ) {
4505 - return false;
4506 - }
4507 - $key = rawurldecode( wp_unslash( $key ) );
4508 - // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
4509 - $raw = base64_decode( sanitize_text_field( $key ) );
4510 - if ( ! $raw ) {
4511 - return false;
4512 - }
4513 - $parts = explode( '::', $raw, 2 );
4514 - if ( count( $parts ) !== 2 ) {
4515 - return false;
4516 - }
4517 - list( $data_json, $signature ) = $parts;
4518 - // Validate signature.
4519 - if ( ! hash_equals( wp_hash( $data_json ), $signature ) ) {
4520 - return false;
4521 - }
4522 - $payload = json_decode( $data_json, true );
4523 - return is_array( $payload ) ? $payload : false;
4524 - }
4525 -
4526 - /**
4527 - * Get Loco Translate MO file path for a plugin.
4528 - *
4529 - * @param string $textdomain Plugin textdomain.
4530 - * @return void|false
4531 - */
4532 - public static function load_loco_textdomain( $textdomain ) {
4533 - if ( ! function_exists( 'loco_plugin_version' ) ) {
4534 - return false;
4535 - }
4536 - $lang = WP_LANG_DIR;
4537 - $path = $lang . '/plugins/' . $textdomain . '-' . get_locale() . '.mo';
4538 - if ( ! file_exists( $path ) && defined( 'LOCO_LANG_DIR' ) ) {
4539 - $lang = LOCO_LANG_DIR;
4540 - $path = $lang . '/plugins/' . $textdomain . '-' . get_locale() . '.mo';
4541 - }
4542 - if ( ! file_exists( $path ) ) {
4543 - if ( 'shopbuilder' === $textdomain ) {
4544 - $plugin_root = dirname( RTSB_FILE );
4545 - } elseif ( 'shopbuilder-pro' === $textdomain ) {
4546 - $plugin_root = dirname( RTSBPRO_FILE );
4547 - } else {
4548 - return false;
4549 - }
4550 - $path = $plugin_root . '/languages/' . $textdomain . '-' . get_locale() . '.mo';
4551 - }
4552 - if ( ! file_exists( $path ) ) {
4553 - return false;
4554 - }
4555 - load_textdomain( $textdomain, $path );
4556 3277 }
4557 3278 }