PATH:
home
/
sparklp6
/
public_html
/
website_356c8563
/
wp-content
/
plugins
/
translatepress-multilingual
/
includes
<?php if ( ! defined( 'ABSPATH' ) ) { exit; } /** * Class TRP_Language_Switcher_Tab * * Extracts language-switcher settings (with backwards-compat migration), * then renders the single Vue mount point carrying the serialized config. */ class TRP_Language_Switcher_Tab { private array $settings; /** * * @param array $settings */ public function __construct( array $settings ) { $this->settings = $settings; add_filter( 'trp_settings_tabs', [$this, 'add_tab_to_navigation'] ); add_action( 'admin_menu', [$this, 'add_submenu_page'] ); add_action( 'admin_enqueue_scripts', [$this, 'enqueue_assets'] ); add_action( 'wp_ajax_trp_language_switcher_save', [ $this, 'ajax_save_language_switcher' ] ); add_action( 'wp_ajax_trp_disable_legacy_language_switcher', [ $this, 'ajax_disable_legacy_language_switcher' ] ); } /** * Retrieve the nested language_switcher config. * - If option is missing/empty: seed with defaults. * - If option exists but misses keys: complete only the missing keys (deep), then update option. * * @return array */ public function get_initial_config(): array { $saved = get_option( 'trp_language_switcher_settings', null ); $defaults = self::default_switcher_config(); $changed = false; // Option is not set or is invalid - use defaults if ( !is_array( $saved ) || empty( $saved ) ) { update_option( 'trp_language_switcher_settings', $defaults ); return $defaults; } // Recursive merge that only fills missing keys $merge_missing = static function ( array $have, array $defaults ) use ( &$changed, &$merge_missing ): array { foreach ( $defaults as $key => $def_val ) { if ( !array_key_exists( $key, $have ) ) { $have[ $key ] = $def_val; $changed = true; continue; } if ( is_array( $def_val ) && is_array( $have[ $key ] ) ) { $have[ $key ] = $merge_missing( $have[ $key ], $def_val ); } } return $have; }; $completed = $merge_missing( $saved, $defaults ); if ( $changed ) update_option( 'trp_language_switcher_settings', $completed ); return $completed; } /** * Return the complete default Language Switcher config (all scopes). * * @return array */ public static function default_switcher_config(): array { $layout_customizer_default_map = [ 'floater' => [ 'position' => 'bottom-right', 'width' => 'default', 'customWidth' => 216, 'padding' => 'default', 'customPadding' => 0, 'flagIconPosition' => 'before', 'languageNames' => 'full', ], 'shortcode' => [ 'flagIconPosition' => 'before', 'languageNames' => 'full' ], 'menu' => [ 'flagIconPosition' => 'before', 'languageNames' => 'full', 'flagShape' => 'rect' ] ]; $layoutCustomizerDefault = [ 'floater' => [ 'desktop' => $layout_customizer_default_map['floater'], 'mobile' => $layout_customizer_default_map['floater'] ], 'shortcode' => [ 'desktop' => $layout_customizer_default_map['shortcode'], 'mobile' => $layout_customizer_default_map['shortcode'] ], 'menu' => [ 'desktop' => $layout_customizer_default_map['menu'], 'mobile' => $layout_customizer_default_map['menu'] ] ]; return [ 'floater' => [ 'enabled' => true, 'type' => 'dropdown', 'bgColor' => '#ffffff', 'bgHoverColor' => '#0000000d', 'textColor' => '#143852', 'textHoverColor' => '#1d2327', 'borderColor' => '#1438521a', 'borderWidth' => 1, 'borderRadius' => [8, 8, 0, 0], 'size' => 'normal', 'flagShape' => 'rect', 'flagRadius' => 2, 'enableCustomCss' => false, 'customCss' => '', 'oppositeLanguage' => false, 'showPoweredBy' => false, 'layoutCustomizer' => $layoutCustomizerDefault['floater'], 'enableTransitions' => true, ], 'shortcode' => [ 'bgColor' => '#ffffff', 'bgHoverColor' => '#0000000d', 'textColor' => '#143852', 'textHoverColor' => '#1d2327', 'borderColor' => '#1438521a', 'borderWidth' => 1, 'borderRadius' => 5, 'size' => 'normal', 'flagShape' => 'rect', 'flagRadius' => 2, 'enableCustomCss' => false, 'customCss' => '', 'clickLanguage' => false, 'layoutCustomizer' => $layoutCustomizerDefault['shortcode'], 'enableTransitions' => true, 'oppositeLanguage' => false ], 'menu' => [ 'layoutCustomizer' => $layoutCustomizerDefault['menu'], ], ]; } /** * AJAX: Save one language-switcher scope. * Action: trp_language_switcher_save */ public function ajax_save_language_switcher(): void { if ( ! current_user_can( apply_filters( 'trp_settings_capability', 'manage_options' ) ) ) wp_send_json_error( __( 'Permission denied.', 'translatepress-multilingual' ), 403 ); $nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : ''; if ( ! wp_verify_nonce( $nonce, 'trp_language_switcher_save' ) ) wp_send_json_error( __( 'Invalid nonce.', 'translatepress-multilingual' ), 403 ); $scope = sanitize_key( wp_unslash( $_POST['scope'] ?? '' ) ); $config_raw = isset( $_POST['config'] ) ? wp_unslash( $_POST['config'] ) : '{}'; //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized $config_str = is_string( $config_raw ) ? $config_raw : '{}'; $config = json_decode( $config_str, true ); $allowed_scopes = [ 'floater', 'shortcode', 'menu' ]; if ( ! in_array( $scope, $allowed_scopes, true ) || ! is_array( $config ) ) wp_send_json_error( __( 'Settings scope unknown.', 'translatepress-multilingual' ), 400 ); $sanitised = $this->sanitize_scope_config( $scope, $config ); $options = get_option( 'trp_language_switcher_settings', [] ); $options[ $scope ] = $sanitised; update_option( 'trp_language_switcher_settings', $options ); wp_send_json_success( __( 'Settings saved.', 'translatepress-multilingual' ) ); } /** * AJAX: disable the legacy Language Switcher. * * Validates capability and nonce, sets * trp_advanced_settings['load_legacy_language_switcher'] = 'no', * and returns a JSON response. * * Expects POST: 'nonce' for action 'trp_disable_legacy'. * * @return void */ public function ajax_disable_legacy_language_switcher(): void { if ( ! current_user_can( apply_filters( 'trp_settings_capability', 'manage_options' ) ) ) { wp_send_json_error( __( 'Permission denied.', 'translatepress-multilingual' ), 403 ); } $nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : ''; if ( ! wp_verify_nonce( $nonce, 'trp_disable_legacy' ) ) { wp_send_json_error( __( 'Invalid nonce.', 'translatepress-multilingual' ), 403 ); } $adv = get_option( 'trp_advanced_settings', [] ); if ( ! is_array( $adv ) ) { $adv = []; } // Flip legacy OFF $adv['load_legacy_language_switcher'] = 'no'; update_option( 'trp_advanced_settings', $adv ); TRP_Plugin_Notifications::get_instance()->dismiss_notification( 'trp_ls_v2_intro' ); wp_send_json_success( __( 'Legacy disabled.', 'translatepress-multilingual' ) ); } /** * Sanitise a single-scope config using allow-listed rules. * * @param string $scope floater|shortcode|menu * @param array $data Incoming config * @return array */ private function sanitize_scope_config( string $scope, array $data ): array { /* Rule map */ $rules = [ 'floater' => [ 'enabled' => 'bool', 'type' => 'text', 'bgColor' => 'color', 'bgHoverColor' => 'color', 'textColor' => 'color', 'textHoverColor' => 'color', 'borderColor' => 'color', 'borderWidth' => 'int', 'borderRadius' => 'int_array', 'size' => 'text', 'flagShape' => 'text', 'flagRadius' => 'int', 'enableCustomCss' => 'bool', 'customCss' => 'css', 'oppositeLanguage' => 'bool', 'layoutCustomizer' => 'layoutCustomizer', 'showPoweredBy' => 'bool', 'enableTransitions' => 'bool' ], 'shortcode' => [ 'bgColor' => 'color', 'bgHoverColor' => 'color', 'textColor' => 'color', 'textHoverColor' => 'color', 'borderColor' => 'color', 'borderWidth' => 'int', 'borderRadius' => 'int', 'size' => 'text', 'flagShape' => 'text', 'flagRadius' => 'int', 'enableCustomCss' => 'bool', 'customCss' => 'css', 'layoutCustomizer' => 'layoutCustomizer', 'clickLanguage' => 'bool', 'enableTransitions' => 'bool', 'oppositeLanguage' => 'bool' ], 'menu' => [ 'flagShape' => 'text', 'flagIconPosition' => 'text', 'languageNames' => 'text', 'layoutCustomizer' => 'layoutCustomizer', ], ]; /* sanitiser callbacks */ $filters = [ 'bool' => static fn ( $v ) => (bool) $v, 'int' => 'intval', 'text' => 'sanitize_text_field', 'color' => static function ( $v ) { $v = trim( strtolower( $v ) ); if ( $v === 'transparent' ) return $v; // Allow 4, 5, 7 or 9-character hex codes (with #) if ( preg_match( '/^#(?:[0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/i', $v ) ) return $v; return ''; }, 'int_array' => static fn ( $v ) => array_map( 'intval', (array) $v ), 'css' => [ $this, 'sanitize_custom_css' ], 'layoutCustomizer' => [ $this, 'sanitize_layout_customizer' ], ]; /* iterate */ $out = []; foreach ( $rules[ $scope ] as $key => $rule ) { if ( array_key_exists( $key, $data ) ) { $out[ $key ] = $filters[ $rule ]( $data[ $key ] ); } } return $out; } /** * Sanitize a raw CSS string for storage. * * - Rejects any HTML markup. * - Removes dangerous CSS constructs. * * @param string $css Raw user input. * @return string Sanitized CSS (or empty string on fatal error). */ public function sanitize_custom_css( string $css ): string { // Reject any HTML tags if ( preg_match( '#</?\w+#', $css ) ) return ''; // Neutralize CSS expressions & javascript: URLs $css = preg_replace( '/expression\s*\([^)]*\)/i', '', $css ); $css = preg_replace( '/url\s*\(\s*[\'"]?\s*javascript:[^)]*\)/i', 'url("about:blank")', $css ); $css = preg_replace( '/@import\s+[^;]+;/i', '', $css ); return trim( $css ); } /** * Sanitises the layoutCustomizer object for both desktop and mobile. * * @param mixed $value * @return array */ private function sanitize_layout_customizer( array $value ): array { $out = [ 'desktop' => [], 'mobile' => [] ]; $int_fields = [ 'customWidth', 'customPadding' ]; $text_fields = [ 'position', 'width', 'padding', 'flagIconPosition', 'languageNames', 'flagShape' ]; foreach ( [ 'desktop', 'mobile' ] as $device ) { foreach ( array_merge( $int_fields, $text_fields ) as $key ) { if ( ! isset( $value[ $device ][ $key ] ) ) continue; $raw = $value[ $device ][ $key ]; if ( in_array( $key, $int_fields, true ) ) { $out[ $device ][ $key ] = (int) $raw; } else { $out[ $device ][ $key ] = sanitize_text_field( $raw ); } } } return $out; } /** * Adds the Language Switcher tab * * Hooked: trp_settings_tabs * * @param array $tabs * @return array */ public function add_tab_to_navigation( array $tabs ): array { $tab = [ 'name' => __( 'Language Switcher', 'translatepress-multilingual' ), 'url' => admin_url( 'admin.php?page=trp_language_switcher' ), 'page' => 'trp_language_switcher' ]; array_splice( $tabs, 1, 0, [$tab] ); return $tabs; } /** * Adds a hidden submenu page for TranslatePress language switcher tab * * Hooked: admin_menu */ public function add_submenu_page(): void { add_submenu_page( 'TRPHidden', __( 'Language Switcher', 'translatepress-multilingual' ), 'TRPHidden', apply_filters( 'trp_settings_capability', 'manage_options' ), 'trp_language_switcher', [$this, 'language_switcher_page_content'] ); } /** * Echoes the Vue mount point <div>. * * @return void */ public function language_switcher_page_content(): void { require_once TRP_PLUGIN_DIR . 'partials/language-switcher-configurator-page.php'; } /** * Build the data array sent to the Vue app via wp_localize_script(). * * @return array */ private function get_localize_payload(): array { $config = $this->get_initial_config(); $trp = TRP_Translate_Press::get_trp_instance(); $languages_component = $trp->get_component( 'languages' ); $published_codes = $this->settings['publish-languages'] ?? []; $short_language_names = $this->settings['url-slugs'] ?? []; $published = []; $all_languages = $languages_component->get_language_names( $published_codes ); foreach ( $published_codes as $code ) { if ( ! isset( $all_languages[ $code ] ) ) continue; /** Custom language flag support */ $flag_path = apply_filters( 'trp_flags_path', '', $code ); $lang_data = [ 'name' => $all_languages[ $code ], 'shortName' => strtoupper( $short_language_names[ $code ] ), ]; // Only include flagPath if it’s non-empty, works only for custom languages if ( ! empty( $flag_path ) ) $lang_data['flagPath'] = $flag_path; $published[ $code ] = $lang_data; } $default_code = $this->settings['default-language']; $default_name = $all_languages[ $default_code ]; return [ 'lsConfig' => $config, 'languages' => [ 'published' => $published, 'default' => [ 'code' => $default_code, 'name' => $default_name, ], ], 'misc' => [ 'pluginUrl' => TRP_PLUGIN_URL ], 'nonce' => wp_create_nonce( 'trp_language_switcher_save' ), ]; } /** * Enqueue the Vue app script & CSS. * @param ?string $hook */ public function enqueue_assets( $hook = null ): void { if ( !is_string( $hook ) && function_exists( 'get_current_screen' ) ) { $screen = get_current_screen(); $hook = $screen ? $screen->id : ''; } if ( 'admin_page_trp_language_switcher' !== $hook ) return; $script_url = TRP_PLUGIN_URL . 'assets/js/trp-lang-switcher-configurator.js'; $style_url = TRP_PLUGIN_URL . 'assets/css/trp-lang-switcher-configurator.css'; $version = TRP_PLUGIN_VERSION; $script_handle = 'tp-lang-switcher-configurator'; wp_enqueue_style( 'tp-lang-switcher-configurator-style', $style_url, [], $version ); wp_register_script( $script_handle, $script_url, [ 'wp-i18n', 'wp-element'], $version, true ); wp_set_script_translations( $script_handle, 'translatepress-multilingual' ); wp_localize_script( $script_handle, 'tpLangSwitcherData', $this->get_localize_payload() ); wp_enqueue_script( $script_handle ); } public function is_legacy_enabled(): bool { return ( $this->settings['trp_advanced_settings']['load_legacy_language_switcher'] ?? 'no' ) === 'yes'; } }
[-] class-plugin-notices.php
[edit]
[-] class-editor-api-gettext-strings.php
[edit]
[-] class-machine-translator-logger.php
[edit]
[-] functions.php
[edit]
[-] class-check-invalid-text.php
[edit]
[+]
google-translate
[-] .htaccess
[edit]
[-] class-error-manager.php
[edit]
[+]
gutenberg-blocks
[-] external-functions.php
[edit]
[+]
string-translation
[-] class-upgrade.php
[edit]
[-] class-editor-api-regular-strings.php
[edit]
[-] class-translation-manager.php
[edit]
[+]
queries
[-] class-elementor-language-for-blocks.php
[edit]
[-] class-advanced-tab.php
[edit]
[-] class-search.php
[edit]
[-] class-hooks-loader.php
[edit]
[+]
gettext
[-] class-ai-words-notification.php
[edit]
[-] class-reviews.php
[edit]
[-] class-plugin-optin.php
[edit]
[-] class-translation-memory.php
[edit]
[-] class-machine-translation-tab.php
[edit]
[-] class-support-chat.php
[edit]
[-] shortcodes.php
[edit]
[-] class-preferred-user-language.php
[edit]
[+]
onboarding
[-] class-language-switcher.php
[edit]
[-] class-languages.php
[edit]
[+]
upgrade
[+]
advanced-settings
[-] trp-ajax.php
[edit]
[-] class-onboarding.php
[edit]
[-] class-wp-bakery-language-for-blocks.php
[edit]
[-] class-rewrite-rules.php
[edit]
[-] class-language-switcher-tab.php
[edit]
[-] class-language-switcher-v2.php
[edit]
[-] compatibility-functions.php
[edit]
[-] class-woocommerce-emails.php
[edit]
[-] class-translation-render.php
[edit]
[-] class-install-plugins.php
[edit]
[-] class-edd-sl-plugin-updater.php
[edit]
[-] class-machine-translator.php
[edit]
[-] custom-language.php
[edit]
[-] class-settings.php
[edit]
[-] class-uri.php
[edit]
[-] class-url-converter.php
[edit]
[+]
mtapi
[+]
..