'array', 'current_language_code' => 'string', 'css_classes' => 'string', 'css_classes_link' => 'string', 'backward_compatibility' => 'array', ); private $allowed_language_vars = array( 'code' => 'string', 'url' => 'string', 'flag_url' => 'string', 'flag_title' => 'string', 'native_name' => 'string', 'display_name' => 'string', 'is_current' => 'bool', 'css_classes' => 'string', 'db_id' => 'string', 'menu_item_parent' => 'mixed', 'is_parent' => 'bool', 'backward_compatibility' => 'array', ); /** * WPML_Language_Switcher_Render_Model constructor. * * @param WPML_LS_Settings $settings * @param SitePress $sitepress * @param string $css_prefix */ public function __construct( $settings, $sitepress, $css_prefix ) { $this->settings = $settings; $this->css_prefix = $css_prefix; parent::__construct( $sitepress ); } /** * @param WPML_LS_Slot $slot * @param array $template_data * * @return array */ public function get( $slot, $template_data = array() ) { $vars = array(); $vars['current_language_code'] = $this->sitepress->get_current_language(); $vars['languages'] = $this->get_language_items( $slot, $template_data ); $vars['css_classes'] = $this->get_slot_css_classes( $slot ); $vars['css_classes_link'] = self::LINK_CSS_CLASS; $vars = $this->add_backward_compatibility_to_wrapper( $vars, $slot ); return $this->sanitize_vars( $vars, $this->allowed_vars ); } /** * @param WPML_LS_Slot $slot * @param array $template_data * * @return array */ private function get_language_items( $slot, $template_data ) { $ret = array(); $get_ls_args = array( 'skip_missing' => ! $this->settings->get_setting( 'link_empty' ), ); if ( $slot->is_post_translations() ) { $get_ls_args['skip_missing'] = true; } $languages = $this->sitepress->get_ls_languages( $get_ls_args ); $languages = is_array( $languages ) ? $languages : array(); $languages = $this->order_languages( $languages, $slot->get( 'language_order_by' ), $slot->get( 'language_order_by' ) ); if ( $languages ) { foreach ( $languages as $code => $data ) { $is_current_language = $code === $this->sitepress->get_current_language(); if ( ! $slot->get( 'display_link_for_current_lang' ) && $is_current_language ) { continue; } $ret[ $code ] = array( 'code' => $code, 'url' => $data['url'], ); /* @deprecated Use 'wpml_ls_language_url' instead */ $ret[ $code ]['url'] = apply_filters( 'WPML_filter_link', $ret[ $code ]['url'], $data ); /** * This filter allows to change the URL for each languages links in the switcher * * @param string $ret[ $code ]['url'] The language URL to be filtered * @param array $data The language information */ $ret[ $code ]['url'] = apply_filters( 'wpml_ls_language_url', $ret[ $code ]['url'], $data ); $ret[ $code ]['url'] = $this->sitepress->get_wp_api()->is_admin() ? '#' : $ret[ $code ]['url']; $css_classes = $this->get_language_css_classes( $slot, $code ); if ( $slot->get( 'display_flags' ) ) { $ret[ $code ]['flag_url'] = $this->filter_flag_url( $data['country_flag_url'], $template_data ); $ret[ $code ]['flag_title'] = $data['native_name']; } if ( $slot->get( 'display_names_in_native_lang' ) ) { $ret[ $code ]['native_name'] = $data['native_name']; } if ( $slot->get( 'display_names_in_current_lang' ) ) { $ret[ $code ]['display_name'] = $data['translated_name']; } if ( $is_current_language ) { $ret[ $code ]['is_current'] = true; array_push( $css_classes, $this->css_prefix . 'current-language' ); } if ( $slot->is_menu() ) { $ret[ $code ]['db_id'] = $this->get_menu_item_id( $code, $slot ); $ret[ $code ]['menu_item_parent'] = $slot->get( 'is_hierarchical' ) && ! $is_current_language ? $this->get_menu_item_id( $this->sitepress->get_current_language(), $slot ) : 0; $ret[ $code ]['is_parent'] = $slot->get( 'is_hierarchical' ) && $is_current_language ? true : false; array_unshift( $css_classes, 'menu-item' ); array_push( $css_classes, $this->css_prefix . 'menu-item' ); } $ret[ $code ]['css_classes'] = $css_classes; } $i = 1; foreach ( $ret as &$lang ) { if( $i === 1 ) { array_push( $lang['css_classes'], $this->css_prefix . 'first-item' ); } if( $i === count( $ret ) ) { array_push( $lang['css_classes'], $this->css_prefix . 'last-item' ); } $lang = $this->add_backward_compatibility_to_languages( $lang, $slot ); /** * Filter the css classes for each language item * * @param array $lang['css_classes'] */ $lang['css_classes'] = apply_filters( 'wpml_ls_model_language_css_classes', $lang['css_classes'] ); $lang['css_classes'] = implode( ' ', $lang['css_classes'] ); $lang = $this->sanitize_vars( $lang, $this->allowed_language_vars ); $i++; } } return $ret; } /** * @param array $languages * @param string $order_by * @param string $order_way * * @return array */ private function order_languages( $languages, $order_by, $order_way ) { $ret = $languages; if ( ! empty( $order_by ) && ! empty( $order_way ) ) { $method = 'order_by_' . $order_by; if ( is_callable( array( $this, $method ) ) ) { uasort( $ret, array( $this, 'order_by_' . $order_by ) ); } $ret = strtoupper( $order_way ) === 'ASC' ? array_reverse( $ret ) : $ret; } return $ret; } /** * @param string $a * @param string $b * * @return int */ private function order_by_code( $a, $b ) { return strnatcmp( $a['code'], $b['code'] ); } /** * @param string $a * @param string $b * * @return int */ private function order_by_native_name( $a, $b ) { return strnatcmp( $a['native_name'], $b['native_name'] ); } /** * @param string $a * @param string $b * * @return int */ private function order_by_translated_name( $a, $b ) { return strnatcmp( $a['translated_name'], $b['translated_name'] ); } /** * @param string $a * @param string $b * * @return int */ private function order_by_id( $a, $b ) { return $a['id'] < $b['id']; } /** * @param string $url * @param array $template_data * * @return string */ private function filter_flag_url( $url, $template_data = array() ) { $wp_upload_dir = wp_upload_dir(); $has_custom_flag = strpos( $url, $wp_upload_dir[ 'baseurl' ] . '/flags/' ) === 0 ? true : false; if ( ! $has_custom_flag && ! empty( $template_data['flags_base_uri'] ) ) { $url = trailingslashit( $template_data['flags_base_uri'] ) . basename( $url ); if ( isset( $template_data['flag_extension'] ) ) { $old_ext = pathinfo( $url, PATHINFO_EXTENSION ); $url = preg_replace( '#' . $old_ext . '$#', $template_data['flag_extension'], $url, 1 ); } } return $url; } /** * @param WPML_LS_Slot $slot * * @return string */ public function get_slot_css_classes( $slot ) { $classes = array( $this->get_slot_css_main_class( $slot->group(), $slot->slug() ) ); $classes[] = trim( $this->css_prefix, '-' ); if ( $this->sitepress->is_rtl( $this->sitepress->get_current_language() ) ) { $classes[] = $this->css_prefix . 'rtl'; } $classes = $this->add_user_agent_touch_device_classes( $classes ); /** * Filter the css classes for the language switcher wrapper * The wrapper is not available for menus * * @param array $classes */ $classes = apply_filters( 'wpml_ls_model_css_classes', $classes ); return implode( ' ', $classes ); } /** * @param $group * @param $slug * @return string */ public function get_slot_css_main_class( $group, $slug ) { return $this->css_prefix . $group . '-' . $slug; } /** * @param WPML_LS_Slot $slot * @param string $code * * @return array */ private function get_language_css_classes( $slot, $code ) { return array( $this->css_prefix . 'slot-' . $slot->slug(), $this->css_prefix . 'item', $this->css_prefix . 'item-' . $code, ); } /** * @param array $classes * * @return array */ private function add_user_agent_touch_device_classes($classes ) { if ( is_null( $this->mobile_detect ) ) { require_once WPML_PLUGIN_PATH . '/lib/mobile-detect.php'; $this->mobile_detect = new WPML_Mobile_Detect(); $this->is_touch_screen = $this->mobile_detect->isMobile() || $this->mobile_detect->isTablet(); } if ( $this->is_touch_screen ) { $classes[] = $this->css_prefix . 'touch-device'; } return $classes; } /** * @return bool */ private function needs_backward_compatibility() { return (bool) $this->settings->get_setting( 'migrated' ); } /** * @param string $code * @param WPML_LS_Slot $slot * * @return string */ private function get_menu_item_id( $code, $slot ) { return $this->css_prefix . $slot->slug() . '-' . $code; } /** * @return string */ public function get_css_prefix() { return $this->css_prefix; } /** * @param array $vars * @param array $allowed_vars * * @return array */ private function sanitize_vars( $vars, $allowed_vars ) { $sanitized = array(); foreach ( $allowed_vars as $allowed_var => $type ) { if ( isset( $vars[ $allowed_var ] ) ) { switch ( $type ) { case 'array': $sanitized[ $allowed_var ] = (array) $vars[ $allowed_var ]; break; case 'string': $sanitized[ $allowed_var ] = (string) $vars[ $allowed_var ]; break; case 'bool': $sanitized[ $allowed_var ] = (bool) $vars[ $allowed_var ]; break; case 'mixed': $sanitized[ $allowed_var ] = $vars[ $allowed_var ]; break; } } } return $sanitized; } /** * @param array $lang * @param WPML_LS_Slot $slot * * @return array */ private function add_backward_compatibility_to_languages( $lang, $slot ) { if ( $this->needs_backward_compatibility() ) { $is_current_language = isset( $lang['is_current'] ) && $lang['is_current']; if ( $slot->is_menu() ) { if ( $is_current_language ) { array_unshift( $lang['css_classes'], 'menu-item-language-current' ); } array_unshift( $lang['css_classes'], 'menu-item-language' ); } if ( $slot->is_sidebar() || $slot->is_shortcode_actions() ) { if ( $this->is_legacy_template( $slot->template(), 'list-vertical' ) || $this->is_legacy_template( $slot->template(), 'list-horizontal' ) ){ array_unshift( $lang['css_classes'], 'icl-' . $lang['code'] ); $lang['backward_compatibility']['css_classes_a'] = $is_current_language ? 'lang_sel_sel' : 'lang_sel_other'; } if ( $this->is_legacy_template( $slot->template(), 'dropdown' ) || $this->is_legacy_template( $slot->template(), 'dropdown-click' ) ){ if ( $is_current_language ) { $lang['backward_compatibility']['css_classes_a'] = 'lang_sel_sel icl-' . $lang['code']; } else { array_unshift( $lang['css_classes'], 'icl-' . $lang['code'] ); } } } } return $lang; } /** * @param array $vars * @param WPML_LS_Slot $slot * * @return mixed */ private function add_backward_compatibility_to_wrapper( $vars, $slot ) { if ( $this->needs_backward_compatibility() ) { if ( $slot->is_sidebar() || $slot->is_shortcode_actions() ) { if ( $this->is_legacy_template( $slot->template(), 'list-vertical' ) || $this->is_legacy_template( $slot->template(), 'list-horizontal' ) ){ $vars['backward_compatibility']['css_id'] = 'lang_sel_list'; if ( $this->is_legacy_template( $slot->template(), 'list-horizontal' ) ) { $vars['css_classes'] = 'lang_sel_list_horizontal ' . $vars['css_classes']; } else { $vars['css_classes'] = 'lang_sel_list_vertical ' . $vars['css_classes']; } } if ( $this->is_legacy_template( $slot->template(), 'dropdown' ) ) { $vars['backward_compatibility']['css_id'] = 'lang_sel'; } if ( $this->is_legacy_template( $slot->template(), 'dropdown-click' ) ) { $vars['backward_compatibility']['css_id'] = 'lang_sel_click'; } } if ( $slot->is_post_translations() ) { $vars['css_classes'] = 'icl_post_in_other_langs ' . $vars['css_classes']; } if ( $slot->is_footer() ) { $vars['backward_compatibility']['css_id'] = 'lang_sel_footer'; } $vars['backward_compatibility']['css_classes_flag'] = 'iclflag'; $vars['backward_compatibility']['css_classes_native'] = 'icl_lang_sel_native'; $vars['backward_compatibility']['css_classes_display'] = 'icl_lang_sel_translated'; $vars['backward_compatibility']['css_classes_bracket'] = 'icl_lang_sel_bracket'; } return $vars; } /** * @param $template_slug * @param mixed|string|null $type * * @return bool */ private function is_legacy_template( $template_slug, $type = null ) { $templates = $this->settings->get_core_templates(); $ret = in_array( $template_slug, $templates, true); if ( $ret && array_key_exists( $type, $templates ) ) { $ret = $templates[ $type ] === $template_slug; } return $ret; } }