self::get_admin_settings_option( 'uagb_beta', 'no' ), 'uag_enable_legacy_blocks' => self::get_admin_settings_option( 'uag_enable_legacy_blocks' ), '_uagb_allow_file_generation' => self::get_admin_settings_option( '_uagb_allow_file_generation', 'enabled' ), 'uag_enable_templates_button' => self::get_admin_settings_option( 'uag_enable_templates_button', 'yes' ), 'uag_enable_on_page_css_button' => self::get_admin_settings_option( 'uag_enable_on_page_css_button', 'yes' ), 'uag_enable_block_condition' => self::get_admin_settings_option( 'uag_enable_block_condition', 'disabled' ), 'uag_enable_masonry_gallery' => self::get_admin_settings_option( 'uag_enable_masonry_gallery', 'enabled' ), 'uag_enable_quick_action_sidebar' => self::get_admin_settings_option( 'uag_enable_quick_action_sidebar', 'enabled' ), 'uag_enable_animations_extension' => self::get_admin_settings_option( 'uag_enable_animations_extension', 'enabled' ), 'uag_enable_gbs_extension' => self::get_admin_settings_option( 'uag_enable_gbs_extension', 'enabled' ), 'uag_enable_block_responsive' => self::get_admin_settings_option( 'uag_enable_block_responsive', 'enabled' ), 'uag_select_font_globally' => self::get_admin_settings_option( 'uag_select_font_globally', array() ), 'uag_load_select_font_globally' => self::get_admin_settings_option( 'uag_load_select_font_globally', 'disabled' ), 'uag_load_gfonts_locally' => self::get_admin_settings_option( 'uag_load_gfonts_locally', 'disabled' ), 'uag_collapse_panels' => self::get_admin_settings_option( 'uag_collapse_panels', 'enabled' ), 'uag_copy_paste' => self::get_admin_settings_option( 'uag_copy_paste', 'enabled' ), 'uag_preload_local_fonts' => self::get_admin_settings_option( 'uag_preload_local_fonts', 'disabled' ), 'uag_visibility_mode' => self::get_admin_settings_option( 'uag_visibility_mode', 'disabled' ), 'uag_container_global_padding' => self::get_admin_settings_option( 'uag_container_global_padding', 'default' ), 'uag_container_global_elements_gap' => self::get_admin_settings_option( 'uag_container_global_elements_gap', 20 ), 'uag_btn_inherit_from_theme' => self::get_admin_settings_option( 'uag_btn_inherit_from_theme', 'disabled' ), 'uag_blocks_editor_spacing' => apply_filters( 'uagb_default_blocks_editor_spacing', self::get_admin_settings_option( 'uag_blocks_editor_spacing', 0 ) ), 'uag_load_font_awesome_5' => self::get_admin_settings_option( 'uag_load_font_awesome_5' ), 'uag_auto_block_recovery' => self::get_admin_settings_option( 'uag_auto_block_recovery' ), 'uag_enable_bsf_analytics_option' => self::get_admin_settings_option( 'spectra_usage_optin', 'no' ), 'uag_content_width' => $content_width, 'spectra_core_blocks' => apply_filters( 'spectra_core_blocks', array( 'container', 'advanced-heading', 'image', 'icon', 'buttons', 'info-box', 'call-to-action', 'countdown', 'popup-builder', ) ), 'wp_is_block_theme' => self::is_block_theme(), 'zip_ai_modules' => $zip_ai_modules, ); return $options; } /** * Update all data from the admin settings page. * * @param array $data All settings of Admin. * @return mixed * @since 2.0.8 */ public static function update_admin_settings_shareable_data( $data = array() ) { foreach ( $data as $key => $value ) { self::update_admin_settings_option( $key, $value ); } } /** * Returns an option from the database for * the admin settings page. * * @param string $key The option key. * @param mixed $default Option default value if option is not available. * @param boolean $network_override Whether to allow the network admin setting to be overridden on subsites. * @return mixed Return the option value. * @since 0.0.1 */ public static function get_admin_settings_option( $key, $default = false, $network_override = false ) { // Get the site-wide option if we're in the network admin. return $network_override && is_multisite() ? get_site_option( $key, $default ) : get_option( $key, $default ); } /** * Deletes an option from the database for * the admin settings page. * * @param string $key The option key. * @param boolean $network_override Whether to allow the network admin setting to be overridden on subsites. * @since 2.8.0 * @return void Return the option value. */ public static function delete_admin_settings_option( $key, $network_override = false ) { // Get the site-wide option if we're in the network admin. if ( $network_override && is_multisite() ) { delete_site_option( $key ); } else { delete_option( $key ); } } /** * Provide Widget settings. * * @return array() * @since 0.0.1 */ public static function get_block_options() { $blocks = UAGB_Helper::$block_list; $saved_blocks = self::get_admin_settings_option( '_uagb_blocks' ); if ( is_array( $blocks ) ) { foreach ( $blocks as $slug => $data ) { $_slug = str_replace( 'uagb/', '', $slug ); if ( isset( $saved_blocks[ $_slug ] ) ) { if ( 'disabled' === $saved_blocks[ $_slug ] ) { $blocks[ $slug ]['is_activate'] = false; } else { $blocks[ $slug ]['is_activate'] = true; } } else { $blocks[ $slug ]['is_activate'] = ( isset( $data['default'] ) ) ? $data['default'] : false; } } } UAGB_Helper::$block_list = $blocks; return apply_filters( 'uagb_enabled_blocks', UAGB_Helper::$block_list ); } /** * Updates an option from the admin settings page. * * @param string $key The option key. * @param mixed $value The value to update. * @param bool $network Whether to allow the network admin setting to be overridden on subsites. * @return mixed * @since 0.0.1 */ public static function update_admin_settings_option( $key, $value, $network = false ) { // Update the site-wide option since we're in the network admin. if ( $network && is_multisite() ) { update_site_option( $key, $value ); } else { update_option( $key, $value ); } } /** * Get Specific Stylesheet * * @since 1.13.4 */ public static function create_specific_stylesheet() { $saved_blocks = self::get_admin_settings_option( '_uagb_blocks' ); $combined = array(); $is_already_post = false; $is_already_timeline = false; $is_already_column = false; $is_already_icon_list = false; $is_already_button = false; $is_already_faq = false; $is_already_tabs = false; $blocks_info = UAGB_Block_Module::get_blocks_info(); foreach ( $blocks_info as $key => $block ) { $block_name = str_replace( 'uagb/', '', $key ); if ( isset( $saved_blocks[ $block_name ] ) && 'disabled' === $saved_blocks[ $block_name ] ) { continue; } switch ( $block_name ) { case 'post-grid': case 'post-carousel': case 'post-masonry': case 'post-title': case 'post-image': case 'post-button': case 'post-excerpt': case 'post-meta': if ( ! $is_already_post ) { $combined[] = 'post'; $is_already_post = true; } break; case 'columns': case 'column': if ( ! $is_already_column ) { $combined[] = 'column'; $combined[] = 'columns'; $is_already_column = true; } break; case 'icon-list': case 'icon-list-child': if ( ! $is_already_icon_list ) { $combined[] = 'icon-list'; $combined[] = 'icon-list-child'; $is_already_icon_list = true; } break; case 'buttons-child': case 'buttons': if ( ! $is_already_button ) { $combined[] = 'buttons'; $combined[] = 'buttons-child'; $is_already_button = true; } break; case 'post-timeline': case 'content-timeline': if ( ! $is_already_timeline ) { $combined[] = 'timeline'; $is_already_timeline = true; } break; case 'restaurant-menu': $combined[] = 'price-list'; break; case 'faq-child': case 'faq': if ( ! $is_already_faq ) { $combined[] = 'faq'; $combined[] = 'faq-child'; $is_already_faq = true; } break; case 'tabs-child': case 'tabs': if ( ! $is_already_tabs ) { $combined[] = 'tabs'; $combined[] = 'tabs-child'; $is_already_tabs = true; } break; default: $combined[] = $block_name; break; } } // Load common CSS for all the blocks. $combined[] = 'extensions'; $wp_upload_dir = UAGB_Helper::get_uag_upload_dir_path(); $combined_path = $wp_upload_dir . 'custom-style-blocks.css'; $style = ''; $wp_filesystem = uagb_filesystem(); foreach ( $combined as $key => $c_block ) { if ( false !== strpos( $c_block, '-pro' ) ) { $style_file = SPECTRA_PRO_DIR . 'assets/css/blocks/' . $c_block . '.css'; } else { $style_file = UAGB_DIR . 'assets/css/blocks/' . $c_block . '.css'; } if ( file_exists( $style_file ) ) { $style .= $wp_filesystem->get_contents( $style_file ); } } $wp_filesystem->put_contents( $combined_path, $style, FS_CHMOD_FILE ); } /** * Get Rollback versions. * * @since 1.23.0 * @return array * @access public */ public function get_rollback_versions() { $rollback_versions = get_transient( 'uag_rollback_versions_' . UAGB_VER ); if ( empty( $rollback_versions ) ) { $max_versions = 10; require_once ABSPATH . 'wp-admin/includes/plugin-install.php'; $plugin_information = plugins_api( 'plugin_information', array( 'slug' => 'ultimate-addons-for-gutenberg', ) ); if ( empty( $plugin_information->versions ) || ! is_array( $plugin_information->versions ) ) { return array(); } krsort( $plugin_information->versions ); $rollback_versions = array(); foreach ( $plugin_information->versions as $version => $download_link ) { $lowercase_version = strtolower( $version ); $is_valid_rollback_version = ! preg_match( '/(trunk|beta|rc|dev)/i', $lowercase_version ); if ( ! $is_valid_rollback_version ) { continue; } if ( version_compare( $version, UAGB_VER, '>=' ) ) { continue; } $rollback_versions[] = $version; } usort( $rollback_versions, array( $this, 'sort_rollback_versions' ) ); $rollback_versions = array_slice( $rollback_versions, 0, $max_versions, true ); set_transient( 'uag_rollback_versions_' . UAGB_VER, $rollback_versions, WEEK_IN_SECONDS ); } return $rollback_versions; } /** * Sort Rollback versions. * * @param string $prev Previous Version. * @param string $next Next Version. * * @since 1.23.0 * @return array * @access public */ public function sort_rollback_versions( $prev, $next ) { if ( version_compare( $prev, $next, '==' ) ) { return 0; } if ( version_compare( $prev, $next, '>' ) ) { return -1; } return 1; } /** * Checks if assets should be excluded for a given Custom Post Type (CPT). * * This static method determines if assets should be excluded based on the given CPT and * any additional exclusions provided via a filter. * * @since 2.16.0 * @return bool True if assets should be excluded for the given CPT, false otherwise. */ public static function should_exclude_assets_for_cpt() { // Define the default CPTs to always exclude. $default_excluded_cpts = array( 'sureforms_form' ); // Get the filtered CPT(s) that should not load assets. $filtered_excluded_cpts = apply_filters( 'exclude_uagb_assets_for_cpts', array() ); // If the filtered value is not an array, set it to an empty array. if ( ! is_array( $filtered_excluded_cpts ) ) { $filtered_excluded_cpts = array(); } // Merge default and filtered excluded CPTs. $excluded_cpts = array_merge( $default_excluded_cpts, $filtered_excluded_cpts ); // Pass the excluded CPTs to the 'ast_block_templates_exclude_post_types' filter. add_filter( 'ast_block_templates_exclude_post_types', function() use ( $excluded_cpts ) { return $excluded_cpts; } ); // Pass the excluded CPTs to the 'zipwp_images_excluded_post_types' filter. add_filter( 'zipwp_images_excluded_post_types', function() use ( $excluded_cpts ) { return $excluded_cpts; } ); // Initialize post type variable. $post_type = ''; // Check if we're in the admin/editor context. if ( is_admin() ) { // Get the current screen and retrieve the post type. $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null; $post_type = ( $screen instanceof WP_Screen ) ? $screen->post_type : ''; // Admin: use WP_Screen. } else { // On frontend: get the post type from the queried object. $queried_object = get_queried_object(); $post_type = ( $queried_object instanceof WP_Post ) ? get_post_type( $queried_object ) : ''; // Frontend: use WP_Post. } // Return true if the post type matches any in the excluded CPTs list. return in_array( $post_type, $excluded_cpts ); } /** * Get Global Content Width * * @since 2.0.0 * @return int * @access public */ public static function get_global_content_width() { $content_width = self::get_admin_settings_option( 'uag_content_width', '' ); $uag_content_width_set_by = 'Spectra'; $get_uag_content_width_set_by = self::get_admin_settings_option( 'uag_content_width_set_by', '' ); if ( '' === $content_width ) { $content_width_third_party = apply_filters( 'spectra_global_content_width', 'default' ); $astra_content_width = function_exists( 'astra_get_option' ) ? astra_get_option( 'site-content-width' ) : false; if ( self::is_block_theme() ) { $settings = wp_get_global_settings(); $content_width = intval( $settings['layout']['wideSize'] ); $uag_content_width_set_by = __( "Full Site Editor's Global Styles", 'ultimate-addons-for-gutenberg' ); } elseif ( 'default' !== $content_width_third_party ) { $content_width = intval( $content_width_third_party ); $uag_content_width_set_by = __( 'Filter added through any 3rd Party Theme/Plugin.', 'ultimate-addons-for-gutenberg' ); } elseif ( $astra_content_width ) { $content_width = intval( $astra_content_width ); $ast_theme_name = function_exists( 'astra_get_theme_name' ) ? astra_get_theme_name() : 'Astra'; $uag_content_width_set_by = $ast_theme_name . ' ' . __( 'Theme', 'ultimate-addons-for-gutenberg' ); } } // Update admin settings option uag_content_width_set_by if $get_uag_content_width_set_by and $uag_content_width_set_by are not same. if ( $get_uag_content_width_set_by !== $uag_content_width_set_by ) { self::update_admin_settings_option( 'uag_content_width_set_by', $uag_content_width_set_by ); } return '' === $content_width ? 1140 : $content_width; } /** * Function to check if the current theme is a block theme. * * @since 2.7.11 * @return boolean */ public static function is_block_theme() { return ( function_exists( 'wp_is_block_theme' ) && wp_is_block_theme() ) ? true : false; } /** * Get Spectra Pro URL with required params * * @param string $path Path for the Website URL. * @param string $source UMM source. * @param string $medium UTM medium. * @param string $campaign UTM campaign. * @since 2.7.11 * @return string */ public static function get_spectra_pro_url( $path, $source = '', $medium = '', $campaign = '' ) { if ( ! defined( 'UAGB_URI' ) ) { define( 'UAGB_URI', trailingslashit( 'https://wpspectra.com/' ) ); } $url = esc_url( UAGB_URI . $path ); $spectra_pro_url = trailingslashit( $url ); // Modify the utm_source parameter using the UTM ready link function to include tracking information. if ( class_exists( '\BSF_UTM_Analytics\Inc\Utils' ) && is_callable( '\BSF_UTM_Analytics\Inc\Utils::get_utm_ready_link' ) ) { $spectra_pro_url = \BSF_UTM_Analytics\Inc\Utils::get_utm_ready_link( $spectra_pro_url, 'ultimate-addons-for-gutenberg' ); } else { if ( ! empty( $source ) ) { $spectra_pro_url = add_query_arg( 'utm_source', sanitize_text_field( $source ), $spectra_pro_url ); } } // Set up our URL if we have a medium. if ( ! empty( $medium ) ) { $spectra_pro_url = add_query_arg( 'utm_medium', sanitize_text_field( $medium ), $spectra_pro_url ); } // Set up our URL if we have a campaign. if ( ! empty( $campaign ) ) { $spectra_pro_url = add_query_arg( 'utm_campaign', sanitize_text_field( $campaign ), $spectra_pro_url ); } $spectra_pro_url = apply_filters( 'spectra_get_pro_url', $spectra_pro_url, $url ); $spectra_pro_url = remove_query_arg( 'bsf', is_string( $spectra_pro_url ) ? $spectra_pro_url : '' ); $ref = get_option( 'spectra_partner_url_param', '' ); if ( ! empty( $ref ) && is_string( $ref ) ) { $spectra_pro_url = add_query_arg( 'bsf', sanitize_text_field( $ref ), $spectra_pro_url ); } return $spectra_pro_url; } /** * Prepare user country code. * * Returns the user's country code. * Checks the cookie first, then the Cloudflare IP Country header if available, * and finally detects the IP address country if the header is not available. * * @since 2.19.8 * @return string The user's country code. */ public static function prepare_user_country_code() { static $currency_code = 'null'; $default_currency_code = 'US'; // Default currency. $user_id = get_current_user_id(); // If user is logged in and currency is already stored. if ( $user_id ) { $stored_code = get_user_meta( $user_id, 'pse_country_code', true ); if ( is_string( $stored_code ) && ! empty( $stored_code ) ) { $currency_code = sanitize_text_field( $stored_code ); return $currency_code; } } // Prefer Cloudflare IP Country header if available. if ( isset( $_SERVER['HTTP_CF_IPCOUNTRY'] ) ) { $default_currency_code = sanitize_text_field( $_SERVER['HTTP_CF_IPCOUNTRY'] ); if ( $user_id && $default_currency_code ) { update_user_meta( $user_id, 'pse_country_code', $default_currency_code ); $currency_code = $default_currency_code; return $default_currency_code; } } // Detect IP address country if Cloudflare header is not available. $tokens = array( 'c1578516a7378c', // rohitp@bsf.io. 'abeeb8e41600b5', // lawaca8819@cashbn.com. '0f5ba880c5ee80', // tern0@mailshan.com. ); $user_ip = static::get_user_ip(); if ( ! empty( $user_ip ) ) { $token = $tokens[ array_rand( $tokens ) ]; $url = "https://ipinfo.io/{$user_ip}?token={$token}"; $request = wp_remote_get( $url ); if ( ! is_wp_error( $request ) && wp_remote_retrieve_response_code( $request ) === 200 ) { $response = json_decode( wp_remote_retrieve_body( $request ), true ); if ( is_array( $response ) && ! empty( $response['country'] ) ) { $default_currency_code = sanitize_text_field( $response['country'] ); } if ( $user_id ) { update_user_meta( $user_id, 'pse_country_code', $default_currency_code ); } $currency_code = $default_currency_code; return $default_currency_code; } } return $default_currency_code; } /** * Retrieves the user's IP address. * * This function works by following the order of preference: * 1. Cloudflare's `HTTP_CF_CONNECTING_IP`. * 2. `HTTP_X_FORWARDED_FOR` (first IP in case of multiple proxies). * 3. `HTTP_CLIENT_IP`. * 4. `REMOTE_ADDR`. * * @since 2.19.8 * @return string The user's IP address. */ public static function get_user_ip() { if ( ! empty( $_SERVER['HTTP_CF_CONNECTING_IP'] ) ) { return sanitize_text_field( $_SERVER['HTTP_CF_CONNECTING_IP'] ); // Cloudflare real IP. } elseif ( ! empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) { return explode( ',', sanitize_text_field( $_SERVER['HTTP_X_FORWARDED_FOR'] ) )[0]; // First IP in case of multiple proxies. } elseif ( ! empty( $_SERVER['HTTP_CLIENT_IP'] ) ) { return sanitize_text_field( $_SERVER['HTTP_CLIENT_IP'] ); } elseif ( ! empty( $_SERVER['REMOTE_ADDR'] ) ) { return sanitize_text_field( $_SERVER['REMOTE_ADDR'] ); } return ''; } /** * Get the user's country code and return a pricing region * * Returns a pricing region based on the user's country code. * The pricing regions are based on the country codes. * The default pricing region is 'US'. * * @since 2.19.8 * @return string The pricing region. */ public static function get_user_country_code() { $country_code = self::prepare_user_country_code(); $pricing_region = 'US'; // Default fallback. switch ( $country_code ) { case 'IN': $pricing_region = 'IN'; break; // Add more cases as needed. default: $pricing_region = 'US'; break; } return $pricing_region; } /** * Sanitize inline css. * * @param string $css User-provided CSS input. * * @since 2.19.15 * @return string Sanitized CSS. */ public static function sanitize_inline_css( $css ) { if ( empty( $css ) || ! is_string( $css ) ) { return ''; } // 1. Strip all HTML/Script tags. $css = wp_strip_all_tags( $css ); $css = is_string( $css ) ? $css : ''; // 2. Additional XSS prevention. $css = str_replace( array( '\\', '<', '&' ), '', $css ); // 3. Context-aware XSS prevention that preserves valid CSS. $css = self::sanitize_css_with_context( $css ); // Final safety check to ensure we always return a string. return is_string( $css ) ? $css : ''; } /** * Context-aware CSS sanitization that preserves quoted content. * * @param string $css CSS content to sanitize. * @return string Sanitized CSS. * @since 2.19.15 */ private static function sanitize_css_with_context( $css ) { if ( empty( $css ) || ! is_string( $css ) ) { return ''; } // Extract and protect quoted strings (including URLs in quotes). $protected_strings = array(); $placeholder_prefix = '___PROTECTED_STRING_'; $counter = 0; // Match quoted strings (single and double quotes). $result = preg_replace_callback( '/(["\'])((?:\\\\.|(?!\1)[^\\\\])*)(\1)/', function( $matches ) use ( &$protected_strings, $placeholder_prefix, &$counter ) { $placeholder = $placeholder_prefix . $counter . '___'; $protected_strings[ $placeholder ] = $matches[0]; $counter++; return $placeholder; }, $css ); $css = is_string( $result ) ? $result : $css; // Apply XSS patterns only to unprotected (non-quoted) content. $xss_patterns = array( // Dangerous CSS functions and protocols. '/javascript\s*:/i', '/vbscript\s*:/i', '/data\s*:\s*[^;]*script/i', // CSS expressions (IE specific). '/expression\s*\(/i', // Event handlers (shouldn't be in CSS but could be injected). '/on\w+\s*=/i', // Script execution attempts. '/alert\s*\(/i', '/eval\s*\(/i', // @import with potentially dangerous URLs (but preserve normal @import). '/@import\s+[^;]*javascript\s*:/i', '/@import\s+[^;]*vbscript\s*:/i', '/@import\s+[^;]*data\s*:\s*[^;]*script/i', ); foreach ( $xss_patterns as $pattern ) { $result = preg_replace( $pattern, '', $css ); $css = is_string( $result ) ? $result : $css; } // Restore protected quoted strings. foreach ( $protected_strings as $placeholder => $original ) { $result = str_replace( $placeholder, $original, $css ); $css = is_string( $result ) ? $result : $css; } return $css; } } /** * Prepare if class 'UAGB_Admin_Helper' exist. * Kicking this off by calling 'get_instance()' method */ UAGB_Admin_Helper::get_instance(); }