PluginProbe
ShopBuilder – WooCommerce Builder For Elementor / 3.2.1
ShopBuilder – WooCommerce Builder For Elementor v3.2.1
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 +29 -361 3.4.13.2.1 View file →
@@ -12,9 +12,9 @@
12 12 exit( 'This script cannot be accessed directly.' );
13 13 }
14 14
15 15
16 -use RadiusTheme\SB\Dependencies\CodesVault\Howdyqb\DB;
16 +use CodesVault\Howdyqb\DB;
17 17 use DateTime;
18 18 use WP_Styles;
19 19 use WC_Product;
20 20 use Elementor\Plugin;
@@ -37,9 +37,9 @@
37 37 * @var array
38 38 */
39 39 private static $cache = [];
40 40 /**
41 - * Check if the stored license is valid. Its Only Show Pro Label.
41 + * Check if the stored license is valid.
42 42 *
43 43 * @return bool
44 44 */
45 45 public static function has_valid_license() {
@@ -52,39 +52,8 @@
52 52 return 'valid' === $license_status;
53 53 }
54 54
55 55 /**
56 - * Check if license is valid or bypassed by theme,
57 - *
58 - * @return bool
59 - */
60 - public static function is_pro_authorized() {
61 - static $cached_result = null;
62 - if ( null !== $cached_result ) {
63 - return $cached_result;
64 - }
65 - // ❗ Step 2: If Pro not installed → no access.
66 - if ( ! function_exists( 'rtsbpro' ) ) {
67 - $cached_result = false;
68 - return $cached_result;
69 - }
70 - $current_theme = wp_get_theme()->get_stylesheet(); // Child theme And Parent Theme Need Check work or not.
71 - /**
72 - * Allowed themes (bypass license)
73 - *
74 - * @param array $themes
75 - */
76 - $allowed_themes = apply_filters( 'rt_pro_access_allowed_themes', [] );
77 - // ✅ Step 1: Theme bypass (no license check).
78 - if ( in_array( $current_theme, $allowed_themes, true ) ) {
79 - $cached_result = true;
80 - return true;
81 - }
82 - // ✅ Step 3: Normal license validation
83 - $license_status = rtsbpro()->get_license( 'license_status' );
84 - return ( 'valid' === $license_status );
85 - }
86 - /**
87 56 * Verify nonce.
88 57 *
89 58 * @return bool
90 59 */
@@ -1256,9 +1225,9 @@
1256 1225
1257 1226 $imgSize = [];
1258 1227
1259 1228 foreach ( $sizes as $key => $img ) {
1260 - $imgSize[ $key ] = esc_html( $key ) . " ({$img['width']}*{$img['height']})";
1229 + $imgSize[ $key ] = ucfirst( $key ) . " ({$img['width']}*{$img['height']})";
1261 1230 }
1262 1231
1263 1232 $imgSize['full'] = esc_html__( 'Full size', 'shopbuilder' );
1264 1233 $imgSize['rtsb_custom'] = esc_html__( 'Custom image size', 'shopbuilder' );
@@ -1356,17 +1325,8 @@
1356 1325 *
1357 1326 * @return array
1358 1327 */
1359 1328 public static function get_all_attributes() {
1360 - // Memoize per request: this runs once per product widget while Elementor
1361 - // builds the editor control config, firing a get_terms() query per
1362 - // attribute taxonomy each time. Caching avoids the repeated N queries.
1363 - static $cached = null;
1364 -
1365 - if ( null !== $cached ) {
1366 - return $cached;
1367 - }
1368 -
1369 1329 $terms = [];
1370 1330 $termList = [];
1371 1331
1372 1332 $attributes = wc_get_attribute_taxonomies();
@@ -1386,10 +1346,8 @@
1386 1346 }
1387 1347 }
1388 1348 }
1389 1349
1390 - $cached = $terms;
1391 -
1392 1350 return $terms;
1393 1351 }
1394 1352
1395 1353 /**
@@ -1988,18 +1946,20 @@
1988 1946 if ( ! $product->is_on_sale() ) {
1989 1947 return '';
1990 1948 }
1991 1949
1950 + $badge_text = '';
1951 + $percentage = self::calculate_sale_percentage( $product );
1952 +
1953 + if ( $percentage > 0 ) {
1954 + $badge_text = '-' . round( $percentage ) . '%';
1955 + }
1956 +
1992 1957 if ( 'text' === $type ) {
1993 - return $text;
1958 + $badge_text = $text;
1994 1959 }
1995 1960
1996 - $percentage = self::calculate_sale_percentage( $product );
1997 -
1998 - // The product is on sale (checked above). When a percentage can be derived show
1999 - // it; otherwise (e.g. some variable products where variation prices aren't
2000 - // primed) fall back to the sale text so an on-sale product always shows a badge.
2001 - return $percentage > 0 ? '-' . round( $percentage ) . '%' : $text;
1961 + return $badge_text;
2002 1962 }
2003 1963
2004 1964 /**
2005 1965 * Get sale badge
@@ -2034,18 +1994,20 @@
2034 1994 if ( ! $product->is_on_sale() ) {
2035 1995 return '';
2036 1996 }
2037 1997
1998 + $badge_text = '';
1999 + $percentage = self::calculate_sale_percentage( $product );
2000 +
2001 + if ( $percentage > 0 ) {
2002 + $badge_text = '-' . round( $percentage ) . '%';
2003 + }
2004 +
2038 2005 if ( 'text' === $type ) {
2039 - return $text;
2006 + $badge_text = $text;
2040 2007 }
2041 2008
2042 - $percentage = self::calculate_sale_percentage( $product );
2043 -
2044 - // The product is on sale (checked above). When a percentage can be derived show
2045 - // it; otherwise (e.g. some variable products where variation prices aren't
2046 - // primed) fall back to the sale text so an on-sale product always shows a badge.
2047 - return $percentage > 0 ? '-' . round( $percentage ) . '%' : $text;
2009 + return $badge_text;
2048 2010 }
2049 2011
2050 2012 /**
2051 2013 * Calculate Sale Percentage
@@ -2101,16 +2063,15 @@
2101 2063 $visible_range = apply_filters( 'rtsb/elements/pagination/visible_range', ( $range * 2 ) + 1 );
2102 2064
2103 2065 global $paged;
2104 2066
2105 - // Prefer `paged` (archive pagination), fall back to `page` (paginated singular posts).
2106 - // This handles the case where the shop/archive is set as the front page — `is_front_page()`
2107 - // alone would force `page` and miss the archive `paged` value.
2108 - $paged = get_query_var( 'paged' ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
2109 - if ( ! $paged ) {
2110 - $paged = get_query_var( 'page' ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
2067 + if ( is_front_page() ) {
2068 + $paged = ( get_query_var( 'page' ) ) ? get_query_var( 'page' ) : 1; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
2069 + } else {
2070 + $paged = ( get_query_var( 'paged' ) ) ? get_query_var( 'paged' ) : 1; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
2111 2071 }
2112 - if ( ! $paged ) {
2072 +
2073 + if ( empty( $paged ) ) {
2113 2074 $paged = 1; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
2114 2075 }
2115 2076
2116 2077 if ( '' === $pages ) {
@@ -2517,24 +2478,8 @@
2517 2478 if ( ! empty( $modulelist[ $module ]['active'] ) ) {
2518 2479 return true;
2519 2480 }
2520 2481 }
2521 - /**
2522 - * Checks if catalog pro is active
2523 - *
2524 - * @return boolean
2525 - */
2526 - public static function is_catalog_mode() {
2527 - return function_exists( 'rtsbpro' ) && self::is_module_active( 'catalog_mode' );
2528 - }
2529 - /**
2530 - * Checks if back in stock notifier is active
2531 - *
2532 - * @return boolean
2533 - */
2534 - public static function is_back_in_stock_notifier_enable() {
2535 - return function_exists( 'rtsbpro' ) && self::is_module_active( 'back_in_stock_notifier' );
2536 - }
2537 2482
2538 2483 /**
2539 2484 * Elementor Widget Active.
2540 2485 *
@@ -2549,30 +2494,8 @@
2549 2494 return true;
2550 2495 }
2551 2496 }
2552 2497
2553 - /**
2554 - * Sanitize a media (fileupload) control value into an { id, source } array.
2555 - *
2556 - * The control value is posted as an array (or a JSON string). Using it as a
2557 - * `sanitize_fn` avoids it being flattened to an empty string by the default
2558 - * sanitize_text_field() path in set_options().
2559 - *
2560 - * @param mixed $raw_value Raw posted value.
2561 - *
2562 - * @return array|string
2563 - */
2564 - public static function sanitize_fileupload_value( $raw_value ) {
2565 - $decoded = is_string( $raw_value ) && '' !== $raw_value ? json_decode( wp_unslash( $raw_value ), true ) : $raw_value;
2566 - if ( ! is_array( $decoded ) ) {
2567 - return '';
2568 - }
2569 - return [
2570 - 'id' => isset( $decoded['id'] ) ? absint( $decoded['id'] ) : 0,
2571 - 'source' => isset( $decoded['source'] ) ? esc_url_raw( $decoded['source'] ) : '',
2572 - ];
2573 - }
2574 -
2575 2498 /***
2576 2499 * Save Settings data
2577 2500 *
2578 2501 * @param string $section_id Section ID.
@@ -2822,233 +2745,8 @@
2822 2745 return ! empty( $ajax[0] );
2823 2746 }
2824 2747
2825 2748 /**
2826 - * Build the active filter chips HTML from the current request query string.
2827 - *
2828 - * Mirrors the markup produced by the JS `displayAppliedFilter()` method so
2829 - * the chips are visible on initial page load (before AJAX hydration).
2830 - *
2831 - * @return string HTML for the active filter chips, or empty string if none.
2832 - */
2833 - public static function get_active_filters_html() {
2834 - // phpcs:disable WordPress.Security.NonceVerification.Recommended -- Read-only query string parsing for display.
2835 - if ( empty( $_GET ) ) {
2836 - return '';
2837 - }
2838 -
2839 - $skip_keys = [
2840 - 'displayview',
2841 - 'orderby',
2842 - 'paged',
2843 - 'page',
2844 - 'product-page',
2845 - 'rtsb_archive_page',
2846 - 'rtsb_grid_columns',
2847 - 'min_price',
2848 - 'max_price',
2849 - '_pjax',
2850 - ];
2851 -
2852 - // When the "Product Categories" widget is already showing the active
2853 - // category navigation, suppress the duplicate product_cat chip group.
2854 - if ( self::is_product_categories_widget_active() ) {
2855 - $skip_keys[] = 'product_cat';
2856 - }
2857 -
2858 - $groups = [];
2859 -
2860 - foreach ( $_GET as $raw_key => $raw_value ) {
2861 - $key = sanitize_key( $raw_key );
2862 -
2863 - if ( '' === $key || in_array( $key, $skip_keys, true ) ) {
2864 - continue;
2865 - }
2866 -
2867 - if ( 0 === strpos( $key, 'query_type_' ) ) {
2868 - continue;
2869 - }
2870 -
2871 - if ( is_array( $raw_value ) ) {
2872 - continue;
2873 - }
2874 -
2875 - $value = sanitize_text_field( wp_unslash( $raw_value ) );
2876 -
2877 - if ( '' === $value ) {
2878 - continue;
2879 - }
2880 -
2881 - $taxonomy = $key;
2882 - if ( 0 === strpos( $taxonomy, 'filter_' ) ) {
2883 - $taxonomy = 'pa_' . substr( $taxonomy, 7 );
2884 - }
2885 -
2886 - $items = [];
2887 - foreach ( explode( ',', $value ) as $slug ) {
2888 - $slug = trim( $slug );
2889 - if ( '' === $slug ) {
2890 - continue;
2891 - }
2892 - $items[] = [
2893 - 'slug' => $slug,
2894 - 'name' => self::get_active_filter_term_label( $taxonomy, $slug, $key ),
2895 - ];
2896 - }
2897 -
2898 - if ( empty( $items ) ) {
2899 - continue;
2900 - }
2901 -
2902 - $groups[ $key ] = [
2903 - 'label' => self::get_active_filter_group_label( $taxonomy, $key ),
2904 - 'items' => $items,
2905 - 'class' => $key,
2906 - ];
2907 - }
2908 -
2909 - // Price range as a single combined chip.
2910 - if ( isset( $_GET['min_price'] ) || isset( $_GET['max_price'] ) ) {
2911 - $min = isset( $_GET['min_price'] ) ? sanitize_text_field( wp_unslash( $_GET['min_price'] ) ) : '';
2912 - $max = isset( $_GET['max_price'] ) ? sanitize_text_field( wp_unslash( $_GET['max_price'] ) ) : '';
2913 -
2914 - if ( '' !== $min || '' !== $max ) {
2915 - $symbol = function_exists( 'get_woocommerce_currency_symbol' ) ? get_woocommerce_currency_symbol() : '';
2916 - $groups['price_filter'] = [
2917 - 'label' => esc_html__( 'Price', 'shopbuilder' ),
2918 - 'class' => 'price_filter',
2919 - 'items' => [
2920 - [
2921 - 'slug' => $min . ',' . $max,
2922 - 'name' => $symbol . $min . ' - ' . $symbol . $max,
2923 - 'extra_class' => ' remove-price',
2924 - ],
2925 - ],
2926 - ];
2927 - }
2928 - }
2929 -
2930 - if ( empty( $groups ) ) {
2931 - return '';
2932 - }
2933 -
2934 - $html = '<div class="rtsb-active-filters">';
2935 -
2936 - foreach ( $groups as $key => $group ) {
2937 - $html .= '<div class="active-filter ' . esc_attr( $group['class'] ) . '">';
2938 -
2939 - if ( ! empty( $group['label'] ) ) {
2940 - $html .= '<div class="filter-name">' . esc_html( $group['label'] ) . ': </div>';
2941 - }
2942 -
2943 - $html .= '<div class="filter-item-container">';
2944 -
2945 - foreach ( $group['items'] as $item ) {
2946 - $extra = isset( $item['extra_class'] ) ? $item['extra_class'] : '';
2947 - $html .= '<div class="filter-item">' . esc_html( $item['name'] );
2948 - $html .= '<span class="remove-filter' . esc_attr( $extra ) . '"';
2949 - $html .= ' title="' . esc_attr( sprintf( /* translators: %s: filter term name */ __( 'Remove %s', 'shopbuilder' ), $item['name'] ) ) . '"';
2950 - $html .= ' data-filter-name="' . esc_attr( $key ) . '"';
2951 - $html .= ' data-filter-value="' . esc_attr( $item['slug'] ) . '">';
2952 - $html .= '<i class="rtsb-icon rtsb-icon-delete"></i></span>';
2953 - $html .= '</div>';
2954 - }
2955 -
2956 - $html .= '</div></div>';
2957 - }
2958 -
2959 - $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>';
2960 - $html .= '</div>';
2961 -
2962 - return $html;
2963 - // phpcs:enable
2964 - }
2965 -
2966 - /**
2967 - * Determine whether the "Product Categories" Elementor widget is present
2968 - * on the current shop/archive layout. Used to avoid duplicating the
2969 - * product_cat chip group when the widget already shows the active term.
2970 - *
2971 - * @return bool
2972 - */
2973 - private static function is_product_categories_widget_active() {
2974 - $elmap = ElementorDataMap::instance();
2975 -
2976 - foreach ( [ 'shop', 'archive' ] as $page ) {
2977 - $id = BuilderFns::is_builder_preview() ? get_the_ID() : BuilderFns::builder_page_id_by_type( $page );
2978 -
2979 - if ( ! $id ) {
2980 - continue;
2981 - }
2982 -
2983 - $widgets = $elmap->get_widget_data( 'rtsb-product-categories-general', [], $id );
2984 -
2985 - if ( ! empty( $widgets ) ) {
2986 - return true;
2987 - }
2988 - }
2989 -
2990 - return false;
2991 - }
2992 -
2993 - /**
2994 - * Resolve the display label for an active filter group (taxonomy heading).
2995 - *
2996 - * @param string $taxonomy Resolved taxonomy slug (e.g. product_cat, pa_color).
2997 - * @param string $raw_key Original URL key (e.g. product_cat, filter_color).
2998 - *
2999 - * @return string
3000 - */
3001 - private static function get_active_filter_group_label( $taxonomy, $raw_key ) {
3002 - if ( 's' === $raw_key || 'search' === $raw_key ) {
3003 - return esc_html__( 'Search', 'shopbuilder' );
3004 - }
3005 - if ( 'rating_filter' === $raw_key ) {
3006 - return esc_html__( 'Ratings', 'shopbuilder' );
3007 - }
3008 - if ( 'sale_filter' === $raw_key ) {
3009 - return esc_html__( 'Sale Filter', 'shopbuilder' );
3010 - }
3011 -
3012 - if ( 0 === strpos( $taxonomy, 'pa_' ) && function_exists( 'wc_attribute_label' ) ) {
3013 - return wc_attribute_label( $taxonomy );
3014 - }
3015 -
3016 - if ( taxonomy_exists( $taxonomy ) ) {
3017 - $tax_obj = get_taxonomy( $taxonomy );
3018 - if ( $tax_obj && ! empty( $tax_obj->labels->name ) ) {
3019 - return $tax_obj->labels->name;
3020 - }
3021 - }
3022 -
3023 - return ucwords( str_replace( [ '_', '-' ], ' ', $raw_key ) );
3024 - }
3025 -
3026 - /**
3027 - * Resolve a term slug to its display name; fall back to a humanised slug.
3028 - *
3029 - * @param string $taxonomy Resolved taxonomy slug.
3030 - * @param string $slug Raw slug from query string.
3031 - * @param string $raw_key Original URL key.
3032 - *
3033 - * @return string
3034 - */
3035 - private static function get_active_filter_term_label( $taxonomy, $slug, $raw_key ) {
3036 - if ( 's' === $raw_key || 'search' === $raw_key || 'rating_filter' === $raw_key ) {
3037 - return rawurldecode( $slug );
3038 - }
3039 -
3040 - if ( taxonomy_exists( $taxonomy ) ) {
3041 - $term = get_term_by( 'slug', $slug, $taxonomy );
3042 - if ( $term && ! is_wp_error( $term ) ) {
3043 - return $term->name;
3044 - }
3045 - }
3046 -
3047 - return ucwords( str_replace( [ '-', '_' ], ' ', rawurldecode( $slug ) ) );
3048 - }
3049 -
3050 - /**
3051 2749 * Check if product has applied filter.
3052 2750 *
3053 2751 * @param string $page Page name.
3054 2752 *
@@ -3069,15 +2767,8 @@
3069 2767 $ajax[] = ! isset( $data['settings']['active_filter'] );
3070 2768
3071 2769 }
3072 2770
3073 - // Also account for the free, non-AJAX "Product Filters" widget so that
3074 - // the active filter chips wrapper renders for it on initial page load.
3075 - foreach ( $elmap->get_widget_data( 'rtsb-product-filters', [], $id ) as $data ) {
3076 - $ajax[] = true;
3077 - unset( $data );
3078 - }
3079 -
3080 2771 return ! empty( $ajax[0] );
3081 2772 }
3082 2773
3083 2774 /**
@@ -3829,16 +3520,13 @@
3829 3520 return $gallery_ids;
3830 3521 }
3831 3522
3832 3523 /**
3833 - * Check if optimization setting is turned on in the database.
3524 + * Check if optimization settings are enabled.
3834 3525 *
3835 - * This only checks the user's setting value, without validating
3836 - * whether the cache directory is writable.
3837 - *
3838 3526 * @return bool
3839 3527 */
3840 - public static function is_optimization_setting_on() {
3528 + public static function is_optimization_enabled() {
3841 3529 $has_pro = rtsb()->has_pro();
3842 3530 $pro_ver = defined( 'RTSBPRO_VERSION' ) ? RTSBPRO_VERSION : 0;
3843 3531
3844 3532 if ( $has_pro && version_compare( $pro_ver, '2.0.0', '<' ) ) {
@@ -3847,28 +3535,8 @@
3847 3535
3848 3536 $generalList = GeneralList::instance()->get_data();
3849 3537
3850 3538 return 'on' === ( $generalList['optimization']['enable_optimization'] ?? '' );
3851 - }
3852 -
3853 - /**
3854 - * Check if optimization is enabled and the cache directory is writable.
3855 - *
3856 - * @return bool
3857 - */
3858 - public static function is_optimization_enabled() {
3859 - if ( ! self::is_optimization_setting_on() ) {
3860 - return false;
3861 - }
3862 -
3863 - $upload = wp_upload_dir();
3864 - $cache_dir = trailingslashit( $upload['basedir'] ) . 'shopbuilder_uploads/cache/';
3865 -
3866 - if ( ! wp_is_writable( $cache_dir ) ) {
3867 - return false;
3868 - }
3869 -
3870 - return true;
3871 3539 }
3872 3540
3873 3541 /**
3874 3542 * Get the optimized handle.