frontend-filters.php 0000666 00000032601 15213274706 0010557 0 ustar 00 options['media_support'] ) { foreach ( array( 'audio', 'image', 'video' ) as $media ) { add_filter( "widget_media_{$media}_instance", array( $this, 'widget_media_instance' ), 1 ); // Since WP 4.8 } } // Strings translation ( must be applied before WordPress applies its default formatting filters ) foreach ( array( 'widget_text', 'widget_title', 'option_blogname', 'option_blogdescription', 'option_date_format', 'option_time_format' ) as $filter ) { add_filter( $filter, 'pll__', 1 ); } // Translates biography add_filter( 'get_user_metadata', array( $this, 'get_user_metadata' ), 10, 4 ); // Set posts and terms language when created from frontend ( ex with P2 theme ) add_action( 'save_post', array( $this, 'save_post' ), 200, 2 ); add_action( 'create_term', array( $this, 'save_term' ), 10, 3 ); add_action( 'edit_term', array( $this, 'save_term' ), 10, 3 ); if ( $this->options['media_support'] ) { add_action( 'add_attachment', array( $this, 'set_default_language' ) ); } // Support theme customizer // FIXME of course does not work if 'transport' is set to 'postMessage' if ( isset( $_POST['wp_customize'], $_POST['customized'] ) ) { add_filter( 'pre_option_blogname', 'pll__', 20 ); add_filter( 'pre_option_blogdescription', 'pll__', 20 ); } } /** * Returns the locale based on current language * * @since 0.1 * * @param string $locale * @return string */ public function get_locale( $locale ) { return $this->curlang->locale; } /** * Filters sticky posts by current language * * @since 0.8 * * @param array $posts list of sticky posts ids * @return array modified list of sticky posts ids */ public function option_sticky_posts( $posts ) { global $wpdb; if ( $this->curlang && ! empty( $posts ) ) { $_posts = wp_cache_get( 'sticky_posts', 'options' ); // This option is usually cached in 'all_options' by WP if ( empty( $_posts ) || ! is_array( $_posts[ $this->curlang->term_taxonomy_id ] ) ) { $posts = array_map( 'intval', $posts ); $posts = implode( ',', $posts ); $languages = $this->model->get_languages_list( array( 'fields' => 'term_taxonomy_id' ) ); $_posts = array_fill_keys( $languages, array() ); // Init with empty arrays $languages = implode( ',', $languages ); $relations = $wpdb->get_results( "SELECT object_id, term_taxonomy_id FROM {$wpdb->term_relationships} WHERE object_id IN ({$posts}) AND term_taxonomy_id IN ({$languages})" ); foreach ( $relations as $relation ) { $_posts[ $relation->term_taxonomy_id ][] = $relation->object_id; } wp_cache_add( 'sticky_posts', $_posts, 'options' ); } $posts = $_posts[ $this->curlang->term_taxonomy_id ]; } return $posts; } /** * Adds language dependent cache domain when querying terms * useful as the 'lang' parameter is not included in cache key by WordPress * * @since 1.3 * * @param array $args * @return array */ public function get_terms_args( $args ) { $lang = isset( $args['lang'] ) ? $args['lang'] : $this->curlang->slug; $key = '_' . ( is_array( $lang ) ? implode( ',', $lang ) : $lang ); $args['cache_domain'] = empty( $args['cache_domain'] ) ? 'pll' . $key : $args['cache_domain'] . $key; return $args; } /** * Filters categories and post tags by language when needed * * @since 0.2 * * @param array $clauses sql clauses * @param array $taxonomies * @param array $args get_terms arguments * @return array modified sql clauses */ public function terms_clauses( $clauses, $taxonomies, $args ) { // Does nothing except on taxonomies which are filterable // Since WP 4.7, make sure not to filter wp_get_object_terms() if ( ! $this->model->is_translated_taxonomy( $taxonomies ) || ! empty( $args['object_ids'] ) ) { return $clauses; } // Adds our clauses to filter by language return $this->model->terms_clauses( $clauses, isset( $args['lang'] ) ? $args['lang'] : $this->curlang ); } /** * Modifies the sql request for wp_get_archives to filter by the current language * * @since 1.9 * * @param string $sql JOIN clause * @param array $r wp_get_archives arguments * @return string modified JOIN clause */ public function getarchives_join( $sql, $r ) { return ! empty( $r['post_type'] ) && $this->model->is_translated_post_type( $r['post_type'] ) ? $sql . $this->model->post->join_clause() : $sql; } /** * Modifies the sql request for wp_get_archives to filter by the current language * * @since 1.9 * * @param string $sql WHERE clause * @param array $r wp_get_archives arguments * @return string modified WHERE clause */ public function getarchives_where( $sql, $r ) { return ! empty( $r['post_type'] ) && $this->model->is_translated_post_type( $r['post_type'] ) ? $sql . $this->model->post->where_clause( $this->curlang ) : $sql; } /** * Modifies the sql request for get_adjacent_post to filter by the current language * * @since 0.1 * * @param string $sql The JOIN clause in the SQL. * @param bool $in_same_term Whether post should be in a same taxonomy term. * @param array $excluded_terms Array of excluded term IDs. * @param string $taxonomy Taxonomy. Used to identify the term used when `$in_same_term` is true. * @param WP_Post $post WP_Post object. * @return string modified JOIN clause */ public function posts_join( $sql, $in_same_term, $excluded_terms, $taxonomy = '', $post = null ) { return $this->model->is_translated_post_type( $post->post_type ) ? $sql . $this->model->post->join_clause( 'p' ) : $sql; } /** * Modifies the sql request for wp_get_archives and get_adjacent_post to filter by the current language * * @since 0.1 * * @param string $sql The WHERE clause in the SQL. * @param bool $in_same_term Whether post should be in a same taxonomy term. * @param array $excluded_terms Array of excluded term IDs. * @param string $taxonomy Taxonomy. Used to identify the term used when `$in_same_term` is true. * @param WP_Post $post WP_Post object. * @return string modified WHERE clause */ public function posts_where( $sql, $in_same_term, $excluded_terms, $taxonomy = '', $post = null ) { return $this->model->is_translated_post_type( $post->post_type ) ? $sql . $this->model->post->where_clause( $this->curlang ) : $sql; } /** * Filters the widgets according to the current language * Don't display if a language filter is set and this is not the current one * * @since 0.3 * * @param array $instance widget settings * @param object $widget WP_Widget object * @return bool|array false if we hide the widget, unmodified $instance otherwise */ public function widget_display_callback( $instance, $widget ) { return ! empty( $instance['pll_lang'] ) && $instance['pll_lang'] != $this->curlang->slug ? false : $instance; } /** * Remove widgets from sidebars if they are not visible in the current language * Needed to allow is_active_sidebar() to return false if all widgets are not for the current language. See #54 * * @since 2.1 * * @param array $sidebars_widgets An associative array of sidebars and their widgets * @return array */ public function sidebars_widgets( $sidebars_widgets ) { global $wp_registered_widgets; foreach ( $sidebars_widgets as $sidebar => $widgets ) { if ( 'wp_inactive_widgets' == $sidebar || empty( $widgets ) ) { continue; } foreach ( $widgets as $key => $widget ) { // Nothing can be done if the widget is created using pre WP2.8 API :( // There is no object, so we can't access it to get the widget options if ( ! isset( $wp_registered_widgets[ $widget ]['callback'] ) || ! is_array( $wp_registered_widgets[ $widget ]['callback'] ) || ! isset( $wp_registered_widgets[ $widget ]['callback'][0] ) || ! is_object( $wp_registered_widgets[ $widget ]['callback'][0] ) || ! method_exists( $wp_registered_widgets[ $widget ]['callback'][0], 'get_settings' ) ) { continue; } $widget_settings = $wp_registered_widgets[ $widget ]['callback'][0]->get_settings(); $number = $wp_registered_widgets[ $widget ]['params'][0]['number']; // Remove the widget if not visible in the current language if ( ! empty( $widget_settings[ $number ]['pll_lang'] ) && $widget_settings[ $number ]['pll_lang'] !== $this->curlang->slug ) { unset( $sidebars_widgets[ $sidebar ][ $key ] ); } } } return $sidebars_widgets; } /** * Translates media in media widgets * * @since 2.1.5 * * @param array $instance Widget instance data * @return array */ public function widget_media_instance( $instance ) { if ( empty( $instance['pll_lang'] ) && $instance['attachment_id'] && $tr_id = pll_get_post( $instance['attachment_id'] ) ) { $instance['attachment_id'] = $tr_id; $attachment = get_post( $tr_id ); if ( $instance['caption'] && ! empty( $attachment->post_excerpt ) ) { $instance['caption'] = $attachment->post_excerpt; } if ( $instance['alt'] && $alt_text = get_post_meta( $tr_id, '_wp_attachment_image_alt', true ) ) { $instance['alt'] = $alt_text; } if ( $instance['image_title'] && ! empty( $attachment->post_title ) ) { $instance['image_title'] = $attachment->post_title; } } return $instance; } /** * Translates biography * Makes sure that the correct locale is used for ajax calls when the user is logged in * * @since 0.9 * * @param null $return * @param int $id User id * @param string $meta_key * @param bool $single Whether to return only the first value of the specified $meta_key * @return null|string */ public function get_user_metadata( $return, $id, $meta_key, $single ) { switch ( $meta_key ) { case 'description': if ( $this->curlang->slug !== $this->options['default_lang'] ) { $return = get_user_meta( $id, 'description_' . $this->curlang->slug, $single ); } break; case 'locale': if ( Polylang::is_ajax_on_front() ) { $return = get_locale(); } break; } return $return; } /** * Allows to set a language by default for posts if it has no language yet * * @since 1.5.4 * * @param int $post_id */ public function set_default_language( $post_id ) { if ( ! $this->model->post->get_language( $post_id ) ) { if ( isset( $_REQUEST['lang'] ) ) { $this->model->post->set_language( $post_id, $_REQUEST['lang'] ); } elseif ( ( $parent_id = wp_get_post_parent_id( $post_id ) ) && $parent_lang = $this->model->post->get_language( $parent_id ) ) { $this->model->post->set_language( $post_id, $parent_lang ); } else { $this->model->post->set_language( $post_id, $this->curlang ); } } } /** * Called when a post ( or page ) is saved, published or updated * Does nothing except on post types which are filterable * Sets the language but does not allow to modify it * * @since 1.1 * * @param int $post_id * @param object $post */ public function save_post( $post_id, $post ) { if ( $this->model->is_translated_post_type( $post->post_type ) ) { $this->set_default_language( $post_id ); } } /** * Called when a category or post tag is created or edited * Does nothing except on taxonomies which are filterable * Sets the language but does not allow to modify it * * @since 1.1 * * @param int $term_id * @param int $tt_id Term taxonomy id * @param string $taxonomy */ public function save_term( $term_id, $tt_id, $taxonomy ) { if ( $this->model->is_translated_taxonomy( $taxonomy ) && ! $this->model->term->get_language( $term_id ) ) { if ( isset( $_REQUEST['lang'] ) ) { $this->model->term->set_language( $term_id, $_REQUEST['lang'] ); } elseif ( ( $term = get_term( $term_id, $taxonomy ) ) && ! empty( $term->parent ) && $parent_lang = $this->model->term->get_language( $term->parent ) ) { $this->model->term->set_language( $term_id, $parent_lang ); } else { $this->model->term->set_language( $term_id, $this->curlang ); } } } } choose-lang-domain.php 0000666 00000001375 15213274706 0010742 0 ustar 00 model->get_language( $this->links_model->get_language_from_url() ); } /** * Adds query vars to query for home pages in all languages * * @since 1.5 */ public function home_requested() { $this->set_curlang_in_query( $GLOBALS['wp_query'] ); /** This action is documented in include/choose-lang.php */ do_action( 'pll_home_requested' ); } } choose-lang-content.php 0000666 00000010616 15213274706 0011143 0 ustar 00 options['media_support'] ) ) { return $this->get_preferred_language(); } if ( $var = get_query_var( 'lang' ) ) { $lang = explode( ',',$var ); $lang = $this->model->get_language( reset( $lang ) ); // choose the first queried language } elseif ( ( is_single() || is_page() || ( is_attachment() && $this->options['media_support'] ) ) && ( ( $var = get_queried_object_id() ) || ( $var = get_query_var( 'p' ) ) || ( $var = get_query_var( 'page_id' ) ) || ( $var = get_query_var( 'attachment_id' ) ) ) ) { $lang = $this->model->post->get_language( $var ); } else { foreach ( $this->model->get_translated_taxonomies() as $taxonomy ) { if ( $var = get_query_var( get_taxonomy( $taxonomy )->query_var ) ) { $lang = $this->model->term->get_language( $var, $taxonomy ); } } } /** * Filter the language before it is set from the content * * @since 0.9 * * @param bool|object $lang language object or false if none was found */ return apply_filters( 'pll_get_current_language', isset( $lang ) ? $lang : false ); } /** * sets the language for home page * add the lang query var when querying archives with no language code * * @since 1.2 * * @param object $query instance of WP_Query */ public function parse_main_query( $query ) { if ( $query !== $GLOBALS['wp_the_query'] ) { return; } $qv = $query->query_vars; // homepage is requested, let's set the language // take care to avoid posts page for which is_home = 1 if ( empty( $query->query ) && ( is_home() || is_page() ) ) { $this->home_language(); $this->home_requested(); } parent::parse_main_query( $query ); $is_archive = ( count( $query->query ) == 1 && ! empty( $qv['paged'] ) ) || $query->is_date || $query->is_author || ( ! empty( $qv['post_type'] ) && $query->is_post_type_archive && $this->model->is_translated_post_type( $qv['post_type'] ) ); // sets the language in case we hide the default language // use $query->query['s'] as is_search is not set when search is empty // http://wordpress.org/support/topic/search-for-empty-string-in-default-language if ( $this->options['hide_default'] && ! isset( $qv['lang'] ) && ( $is_archive || isset( $query->query['s'] ) || ( count( $query->query ) == 1 && ! empty( $qv['feed'] ) ) ) ) { $this->set_language( $this->model->get_language( $this->options['default_lang'] ) ); $this->set_curlang_in_query( $query ); } } /** * sets the language from content * * @since 1.2 */ public function wp() { // nothing to do if the language has already been set ( although normally the filter has been removed ) if ( ! $this->curlang && $curlang = $this->get_language_from_content() ) { parent::set_language( $curlang ); } } /** * if no language found by get_language_from_content, return the preferred one * * @since 0.9 * * @param object|bool $lang Language found in get_language_from_content * @return object Language */ public function pll_get_current_language( $lang ) { return ! $lang ? $this->get_preferred_language() : $lang; } } frontend-filters-links.php 0000666 00000032255 15213274706 0011702 0 ustar 00 curlang = &$polylang->curlang; $this->cache = new PLL_Cache(); // Rewrites author and date links to filter them by language foreach ( array( 'feed_link', 'author_link', 'search_link', 'year_link', 'month_link', 'day_link' ) as $filter ) { add_filter( $filter, array( $this, 'archive_link' ), 20 ); } // Meta in the html head section add_action( 'wp_head', array( $this, 'wp_head' ) ); // Modifies the home url if ( ! defined( 'PLL_FILTER_HOME_URL' ) || PLL_FILTER_HOME_URL ) { add_filter( 'home_url', array( $this, 'home_url' ), 10, 2 ); } if ( $this->options['force_lang'] > 1 ) { // Rewrites next and previous post links when not automatically done by WordPress add_filter( 'get_pagenum_link', array( $this, 'archive_link' ), 20 ); // Rewrites ajax url add_filter( 'admin_url', array( $this, 'admin_url' ), 10, 2 ); } // Redirects to canonical url before WordPress redirect_canonical // but after Nextgen Gallery which hacks $_SERVER['REQUEST_URI'] !!! and restores it in 'template_redirect' with priority 1 add_action( 'template_redirect', array( $this, 'check_canonical_url' ), 4 ); } /** * Modifies the author and date links to add the language parameter ( as well as feed link ) * * @since 0.4 * * @param string $link * @return string modified link */ public function archive_link( $link ) { return $this->links_model->switch_language_in_link( $link, $this->curlang ); } /** * Modifies page links * and caches the result * * @since 1.7 * * @param string $link post link * @param int $post_id post ID * @return string modified post link */ public function _get_page_link( $link, $post_id ) { $sample = false !== strpos( $link, '%pagename%' ); // To avoid a conflict with plugin Custom Permalinks $cache_key = "post:{$post_id}:{$sample}"; if ( false === $_link = $this->cache->get( $cache_key ) ) { $_link = parent::_get_page_link( $link, $post_id ); $this->cache->set( $cache_key, $_link ); } return $_link; } /** * Modifies attachment links * and caches the result * * @since 1.6.2 * * @param string $link attachment link * @param int $post_id attachment link * @return string modified attachment link */ public function attachment_link( $link, $post_id ) { $cache_key = 'post:' . $post_id; if ( false === $_link = $this->cache->get( $cache_key ) ) { $_link = parent::attachment_link( $link, $post_id ); $this->cache->set( $cache_key, $_link ); } return $_link; } /** * Modifies custom posts links * and caches the result * * @since 1.6 * * @param string $link post link * @param object $post post object * @return string modified post link */ public function post_type_link( $link, $post ) { $sample = false !== strpos( $link, '%postname%' ); // To avoid a conflict with plugin Custom Permalinks $cache_key = "post:{$post->ID}:{$sample}"; if ( false === $_link = $this->cache->get( $cache_key ) ) { $_link = parent::post_type_link( $link, $post ); $this->cache->set( $cache_key, $_link ); } return $_link; } /** * Modifies filtered taxonomies ( post format like ) and translated taxonomies links * and caches the result * * @since 0.7 * * @param string $link * @param object $term term object * @param string $tax taxonomy name * @return string modified link */ public function term_link( $link, $term, $tax ) { $cache_key = 'term:' . $term->term_id; if ( false === $_link = $this->cache->get( $cache_key ) ) { if ( in_array( $tax, $this->model->get_filtered_taxonomies() ) ) { $_link = $this->links_model->switch_language_in_link( $link, $this->curlang ); /** This filter is documented in include/filters-links.php */ $_link = apply_filters( 'pll_term_link', $_link, $this->curlang, $term ); } else { $_link = parent::term_link( $link, $term, $tax ); } $this->cache->set( $cache_key, $_link ); } return $_link; } /** * Outputs references to translated pages ( if exists ) in the html head section * * @since 0.1 */ public function wp_head() { // Don't output anything on paged archives: see https://wordpress.org/support/topic/hreflang-on-page2 // Don't output anything on paged pages and paged posts if ( is_paged() || ( is_singular() && ( $page = get_query_var( 'page' ) ) && $page > 1 ) ) { return; } // Google recommends to include self link https://support.google.com/webmasters/answer/189077?hl=en foreach ( $this->model->get_languages_list() as $language ) { if ( $url = $this->links->get_translation_url( $language ) ) { $urls[ $language->get_locale( 'display' ) ] = $url; } } // Ouptputs the section only if there are translations ( $urls always contains self link ) if ( ! empty( $urls ) && count( $urls ) > 1 ) { // Prepare the list of languages to remove the country code foreach ( array_keys( $urls ) as $locale ) { $split = explode( '-', $locale ); $languages[ $locale ] = reset( $split ); } $count = array_count_values( $languages ); foreach ( $urls as $locale => $url ) { $lang = $count[ $languages[ $locale ] ] > 1 ? $locale : $languages[ $locale ]; // Output the country code only when necessary $hreflangs[ $lang ] = $url; } // Adds the site root url when the default language code is not hidden // See https://wordpress.org/support/topic/implementation-of-hreflangx-default if ( is_front_page() && ! $this->options['hide_default'] && $this->options['force_lang'] < 3 ) { $hreflangs['x-default'] = home_url( '/' ); } /** * Filters the list of rel hreflang attributes * * @since 2.1 * * @param array $hreflangs Array of urls with language codes as keys */ $hreflangs = apply_filters( 'pll_rel_hreflang_attributes', $hreflangs ); foreach ( $hreflangs as $lang => $url ) { printf( '' . "\n", esc_url( $url ), esc_attr( $lang ) ); } } } /** * Filters the home url to get the right language * * @since 0.4 * * @param string $url * @param string $path * @return string */ public function home_url( $url, $path ) { if ( ! ( did_action( 'template_redirect' ) || did_action( 'login_init' ) ) || rtrim( $url,'/' ) != $this->links_model->home ) { return $url; } static $white_list, $black_list; // Avoid evaluating this at each function call // We *want* to filter the home url in these cases if ( empty( $white_list ) ) { // On Windows get_theme_root() mixes / and \ // We want only \ for the comparison with debug_backtrace $theme_root = get_theme_root(); $theme_root = ( false === strpos( $theme_root, '\\' ) ) ? $theme_root : str_replace( '/', '\\', $theme_root ); /** * Filter the white list of the Polylang 'home_url' filter * The $args contains an array of arrays each of them having * a 'file' key and/or a 'function' key to decide which functions in * which files using home_url() calls must be filtered * * @since 1.1.2 * * @param array $args */ $white_list = apply_filters( 'pll_home_url_white_list', array( array( 'file' => $theme_root ), array( 'function' => 'wp_nav_menu' ), array( 'function' => 'login_footer' ), array( 'function' => 'get_custom_logo' ), ) ); } // We don't want to filter the home url in these cases if ( empty( $black_list ) ) { /** * Filter the black list of the Polylang 'home_url' filter * The $args contains an array of arrays each of them having * a 'file' key and/or a 'function' key to decide which functions in * which files using home_url() calls must be filtered * * @since 1.1.2 * * @param array $args */ $black_list = apply_filters( 'pll_home_url_black_list', array( array( 'file' => 'searchform.php' ), // Since WP 3.6 searchform.php is passed through get_search_form array( 'function' => 'get_search_form' ), ) ); } $traces = version_compare( PHP_VERSION, '5.2.5', '>=' ) ? debug_backtrace( false ) : debug_backtrace(); unset( $traces[0], $traces[1] ); // We don't need the last 2 calls: this function + call_user_func_array (or apply_filters on PHP7+) foreach ( $traces as $trace ) { // Black list first foreach ( $black_list as $v ) { if ( ( isset( $trace['file'], $v['file'] ) && false !== strpos( $trace['file'], $v['file'] ) ) || ( isset( $trace['function'], $v['function'] ) && $trace['function'] == $v['function'] ) ) { return $url; } } foreach ( $white_list as $v ) { if ( ( isset( $trace['function'], $v['function'] ) && $trace['function'] == $v['function'] ) || ( isset( $trace['file'], $v['file'] ) && false !== strpos( $trace['file'], $v['file'] ) && in_array( $trace['function'], array( 'home_url', 'get_home_url', 'bloginfo', 'get_bloginfo' ) ) ) ) { $ok = true; } } } return empty( $ok ) ? $url : ( empty( $path ) ? rtrim( $this->links->get_home_url( $this->curlang ), '/' ) : $this->links->get_home_url( $this->curlang ) ); } /** * Rewrites ajax url when using domains or subdomains * * @since 1.5 * * @param string $url admin url with path evaluated by WordPress * @param string $path admin path * @return string */ public function admin_url( $url, $path ) { return 'admin-ajax.php' === $path ? $this->links_model->switch_language_in_link( $url, $this->curlang ) : $url; } /** * If the language code is not in agreement with the language of the content * redirects incoming links to the proper URL to avoid duplicate content * * @since 0.9.6 * * @param string $requested_url optional * @param bool $do_redirect optional, whether to perform the redirection or not * @return string if redirect is not performed */ public function check_canonical_url( $requested_url = '', $do_redirect = true ) { global $wp_query, $post, $is_IIS; // Don't redirect in same cases as WP if ( is_trackback() || is_search() || is_admin() || is_preview() || is_robots() || ( $is_IIS && ! iis7_supports_permalinks() ) ) { return; } // Don't redirect mysite.com/?attachment_id= to mysite.com/en/?attachment_id= if ( 1 == $this->options['force_lang'] && is_attachment() && isset( $_GET['attachment_id'] ) ) { return; } // If the default language code is not hidden and the static front page url contains the page name // the customizer lands here and the code below would redirect to the list of posts if ( is_customize_preview() ) { return; } if ( empty( $requested_url ) ) { $requested_url = ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; } if ( is_single() || is_page() ) { if ( isset( $post->ID ) && $this->model->is_translated_post_type( $post->post_type ) ) { $language = $this->model->post->get_language( (int) $post->ID ); } } elseif ( is_category() || is_tag() || is_tax() ) { $obj = $wp_query->get_queried_object(); if ( $this->model->is_translated_taxonomy( $obj->taxonomy ) ) { $language = $this->model->term->get_language( (int) $obj->term_id ); } } elseif ( $wp_query->is_posts_page ) { $obj = $wp_query->get_queried_object(); $language = $this->model->post->get_language( (int) $obj->ID ); } elseif ( is_404() && ! empty( $wp_query->query['page_id'] ) && $id = get_query_var( 'page_id' ) ) { // Special case for page shortlinks when using subdomains or multiple domains // Needed because redirect_canonical doesn't accept to change the domain name $language = $this->model->post->get_language( (int) $id ); } if ( empty( $language ) ) { $language = $this->curlang; $redirect_url = $requested_url; } else { // First get the canonical url evaluated by WP // Workaround a WP bug wich removes the port for some urls and get it back at second call to redirect_canonical $_redirect_url = ( ! $_redirect_url = redirect_canonical( $requested_url, false ) ) ? $requested_url : $_redirect_url; $redirect_url = ( ! $redirect_url = redirect_canonical( $_redirect_url, false ) ) ? $_redirect_url : $redirect_url; // Then get the right language code in url $redirect_url = $this->options['force_lang'] ? $this->links_model->switch_language_in_link( $redirect_url, $language ) : $this->links_model->remove_language_from_link( $redirect_url ); // Works only for default permalinks } /** * Filters the canonical url detected by Polylang * * @since 1.6 * * @param bool|string $redirect_url false or the url to redirect to * @param object $language the language detected */ $redirect_url = apply_filters( 'pll_check_canonical_url', $redirect_url, $language ); // The language is not correctly set so let's redirect to the correct url for this object if ( $do_redirect && $redirect_url && $requested_url != $redirect_url ) { wp_redirect( $redirect_url, 301 ); exit; } return $redirect_url; } } frontend-static-pages.php 0000666 00000021576 15213274706 0011504 0 ustar 00 links_model = &$polylang->links_model; $this->links = &$polylang->links; add_action( 'pll_language_defined', array( $this, 'pll_language_defined' ) ); add_action( 'pll_home_requested', array( $this, 'pll_home_requested' ) ); // Manages the redirection of the homepage add_filter( 'redirect_canonical', array( $this, 'redirect_canonical' ), 10, 2 ); add_filter( 'pll_pre_translation_url', array( $this, 'pll_pre_translation_url' ), 10, 3 ); add_filter( 'pll_check_canonical_url', array( $this, 'pll_check_canonical_url' ) ); add_filter( 'pll_set_language_from_query', array( $this, 'page_on_front_query' ), 10, 2 ); add_filter( 'pll_set_language_from_query', array( $this, 'page_for_posts_query' ), 10, 2 ); } /** * Init the filters * * @since 1.8 */ public function pll_language_defined() { // Translates our page on front and page for posts properties $this->init(); // Translates page for posts and page on front add_filter( 'option_page_on_front', array( $this, 'translate_page_on_front' ) ); add_filter( 'option_page_for_posts', array( $this, 'translate_page_for_posts' ) ); // Support theme customizer if ( isset( $_POST['wp_customize'], $_POST['customized'] ) ) { add_filter( 'pre_option_page_on_front', 'pll_get_post', 20 ); add_filter( 'pre_option_page_for_post', 'pll_get_post', 20 ); } } /** * Translates the page_id query var when the site root page is requested * * @since 1.8 */ public function pll_home_requested() { set_query_var( 'page_id', $this->curlang->page_on_front ); } /** * Translates page on front * * @since 1.8 * * @param int $v page on front page id * @return int */ public function translate_page_on_front( $v ) { // returns the current page if there is no translation to avoid ugly notices return isset( $this->curlang->page_on_front ) ? $this->curlang->page_on_front : $v; } /** * Manages canonical redirection of the homepage when using page on front * * @since 0.1 * * @param string $redirect_url * @param string $requested_url * @return bool|string modified url, false if redirection is canceled */ public function redirect_canonical( $redirect_url, $requested_url ) { global $wp_query; if ( is_page() && ! is_feed() && isset( $wp_query->queried_object ) && $wp_query->queried_object->ID == $this->curlang->page_on_front ) { $url = is_paged() ? $this->links_model->add_paged_to_link( $this->links->get_home_url(), $wp_query->query_vars['page'] ) : $this->links->get_home_url(); // Don't forget additional query vars $query = parse_url( $redirect_url, PHP_URL_QUERY ); if ( ! empty( $query ) ) { parse_str( $query, $query_vars ); $query_vars = rawurlencode_deep( $query_vars ); // WP encodes query vars values $url = add_query_arg( $query_vars, $url ); } return $url; } return $redirect_url; } /** * Translates the url of the page on front and page for posts * * @since 1.8 * * @param string $url not used * @param object $language language in which we want the translation * @param int $queried_object_id id of the queried object * @return string */ public function pll_pre_translation_url( $url, $language, $queried_object_id ) { if ( ! empty( $queried_object_id ) ) { // Page for posts if ( $GLOBALS['wp_query']->is_posts_page && ( $id = $this->model->post->get( $queried_object_id, $language ) ) ) { $url = get_permalink( $id ); } // Page on front elseif ( is_front_page() && $language->page_on_front && ( $language->page_on_front == $this->model->post->get( $queried_object_id, $language ) ) ) { $url = $language->home_url; } } return $url; } /** * Handles canonical redirection if we are on a static front page * * @since 1.8 * * @param string $redirect_url * @return bool|string */ public function pll_check_canonical_url( $redirect_url ) { if ( ! empty( $this->curlang->page_on_front ) && is_page( $this->curlang->page_on_front ) ) { // Redirect www.mysite.fr to mysite.fr if ( 3 === $this->options['force_lang'] ) { foreach ( $this->options['domains'] as $lang => $domain ) { $host = parse_url( $domain, PHP_URL_HOST ); if ( 'www.' . $_SERVER['HTTP_HOST'] === $host || 'www.' . $host === $_SERVER['HTTP_HOST'] ) { $language = $this->model->get_language( $lang ); return $language->home_url; } } } // Prevents canonical redirection made by WP from secondary language to main language if ( $this->options['redirect_lang'] ) { return false; } } return $redirect_url; } /** * Setups query vars when requesting a static front page * * @since 1.8 * * @param bool|object $lang * @param object $query * @return bool|object */ public function page_on_front_query( $lang, $query ) { if ( ! empty( $lang ) || ! $this->page_on_front ) { return $lang; } // The home page is requested if ( did_action( 'home_requested' ) ) { $query->set( 'page_id', $lang->page_on_front ); } // Redirect the language page to the homepage when using a static front page elseif ( ( $this->options['redirect_lang'] || $this->options['hide_default'] ) && ( count( $query->query ) == 1 || ( ( is_preview() || is_paged() || ! empty( $query->query['page'] ) ) && count( $query->query ) == 2 ) || ( ( is_preview() && ( is_paged() || ! empty( $query->query['page'] ) ) ) && count( $query->query ) == 3 ) ) && is_tax( 'language' ) ) { $lang = $this->model->get_language( get_query_var( 'lang' ) ); $query->set( 'page_id', $lang->page_on_front ); $query->is_singular = $query->is_page = true; $query->is_archive = $query->is_tax = false; unset( $query->query_vars['lang'], $query->queried_object ); // Reset queried object } // Fix paged static front page in plain permalinks when Settings > Reading doesn't match the default language elseif ( ! $this->links_model->using_permalinks && count( $query->query ) === 1 && ! empty( $query->query['page'] ) ) { $lang = $this->model->get_language( $this->options['default_lang'] ); $query->set( 'page_id', $lang->page_on_front ); $query->is_singular = $query->is_page = true; $query->is_archive = $query->is_tax = false; unset( $query->query_vars['lang'], $query->queried_object ); // Reset queried object } // Set the language when requesting a static front page else { $page_id = $this->get_page_id( $query ); $languages = $this->model->get_languages_list(); $pages = wp_list_pluck( $languages, 'page_on_front' ); if ( ! empty( $page_id ) && false !== $n = array_search( $page_id, $pages ) ) { $lang = $languages[ $n ]; } } // Fix for page_on_front if ( ( $this->options['force_lang'] < 2 || ! $this->options['redirect_lang'] ) && $this->links_model->using_permalinks && ! empty( $lang ) && isset( $query->query['paged'] ) ) { $query->set( 'page', $query->query['paged'] ); unset( $query->query['paged'] ); } elseif ( ! $this->links_model->using_permalinks && ! empty( $query->query['page'] ) ) { $query->is_paged = true; } return $lang; } /** * Setups query vars when requesting a posts page * * @since 1.8 * * @param bool|object $lang * @param object $query * @return bool|object */ public function page_for_posts_query( $lang, $query ) { if ( empty( $lang ) && $this->page_for_posts ) { $page_id = $this->get_page_id( $query ); if ( ! empty( $page_id ) && in_array( $page_id, $pages = $this->model->get_languages_list( array( 'fields' => 'page_for_posts' ) ) ) ) { // Fill the cache with all pages for posts to avoid one query per page later // The posts_per_page limit is a trick to avoid splitting the query get_posts( array( 'posts_per_page' => 999, 'post_type' => 'page', 'post__in' => $pages, 'lang' => '' ) ); $lang = $this->model->post->get_language( $page_id ); $query->is_singular = $query->is_page = false; $query->is_home = $query->is_posts_page = true; } } return $lang; } /** * Get queried page_id ( if exists ) * If permalinks are used, WordPress does set and use $query->queried_object_id and sets $query->query_vars['page_id'] to 0 * and does set and use $query->query_vars['page_id'] if permalinks are not used :( * * @since 1.5 * * @param object $query instance of WP_Query * @return int page_id */ protected function get_page_id( $query ) { if ( ! empty( $query->query_vars['pagename'] ) && isset( $query->queried_object_id ) ) { return $query->queried_object_id; } if ( isset( $query->query_vars['page_id'] ) ) { return $query->query_vars['page_id']; } return 0; // No page queried } } frontend.php 0000666 00000014267 15213274706 0007121 0 ustar 00 inherited, reference to Polylang options array * model => inherited, reference to PLL_Model object * links_model => inherited, reference to PLL_Links_Model object * links => reference to PLL_Links object * static_pages => reference to PLL_Frontend_Static_Pages object * filters_links => inherited, reference to PLL_Frontend_Filters_Links object * choose_lang => reference to PLL_Choose_lang object * curlang => current language * filters => reference to PLL_Filters object * filters_search => reference to PLL_Frontend_Filters_Search object * nav_menu => reference to PLL_Frontend_Nav_Menu object * auto_translate => optional, reference to PLL_Auto_Translate object * * @since 1.2 */ class PLL_Frontend extends PLL_Base { public $curlang; public $links, $choose_lang, $filters, $filters_search, $nav_menu, $auto_translate; /** * constructor * * @since 1.2 * * @param object $links_model */ public function __construct( &$links_model ) { parent::__construct( $links_model ); add_action( 'pll_language_defined', array( $this, 'pll_language_defined' ), 1 ); // avoids the language being the queried object when querying multiple taxonomies add_action( 'parse_tax_query', array( $this, 'parse_tax_query' ), 1 ); // filters posts by language add_action( 'parse_query', array( $this, 'parse_query' ), 6 ); // not before 'check_canonical_url' if ( ! defined( 'PLL_AUTO_TRANSLATE' ) || PLL_AUTO_TRANSLATE ) { add_action( 'template_redirect', array( $this, 'auto_translate' ), 7 ); } } /** * setups the language chooser based on options * * @since 1.2 */ public function init() { $this->links = new PLL_Frontend_Links( $this ); // Static front page and page for posts if ( 'page' === get_option( 'show_on_front' ) ) { $this->static_pages = new PLL_Frontend_Static_Pages( $this ); } // setup the language chooser $c = array( 'Content', 'Url', 'Url', 'Domain' ); $class = 'PLL_Choose_Lang_' . $c[ $this->options['force_lang'] ]; $this->choose_lang = new $class( $this ); $this->choose_lang->init(); // need to load nav menu class early to correctly define the locations in the customizer when the language is set from the content $this->nav_menu = new PLL_Frontend_Nav_Menu( $this ); } /** * setups filters and nav menus once the language has been defined * * @since 1.2 */ public function pll_language_defined() { // filters $this->filters_links = new PLL_Frontend_Filters_Links( $this ); $this->filters = new PLL_Frontend_Filters( $this ); $this->filters_search = new PLL_Frontend_Filters_Search( $this ); } /** * when querying multiple taxonomies, makes sure that the language is not the queried object * * @since 1.8 * * @param object $query WP_Query object */ public function parse_tax_query( $query ) { $pll_query = new PLL_Query( $query, $this->model ); $queried_taxonomies = $pll_query->get_queried_taxonomies(); if ( ! empty( $queried_taxonomies ) && 'language' == reset( $queried_taxonomies ) ) { $query->tax_query->queried_terms['language'] = array_shift( $query->tax_query->queried_terms ); } } /** * modifies some query vars to "hide" that the language is a taxonomy and avoid conflicts * * @since 1.2 * * @param object $query WP_Query object */ public function parse_query( $query ) { $qv = $query->query_vars; $pll_query = new PLL_Query( $query, $this->model ); $taxonomies = $pll_query->get_queried_taxonomies(); // Allow filtering recent posts and secondary queries by the current language if ( ! empty( $this->curlang ) ) { $pll_query->filter_query( $this->curlang ); } // modifies query vars when the language is queried if ( ! empty( $qv['lang'] ) || ( ! empty( $taxonomies ) && array( 'language' ) == array_values( $taxonomies ) ) ) { // do we query a custom taxonomy? $taxonomies = array_diff( $taxonomies , array( 'language', 'category', 'post_tag' ) ); // remove pages query when the language is set unless we do a search // take care not to break the single page, attachment and taxonomies queries! if ( empty( $qv['post_type'] ) && ! $query->is_search && ! $query->is_page && ! $query->is_attachment && empty( $taxonomies ) ) { $query->set( 'post_type', 'post' ); } // unset the is_archive flag for language pages to prevent loading the archive template // keep archive flag for comment feed otherwise the language filter does not work if ( empty( $taxonomies ) && ! $query->is_comment_feed && ! $query->is_post_type_archive && ! $query->is_date && ! $query->is_author && ! $query->is_category && ! $query->is_tag ) { $query->is_archive = false; } // unset the is_tax flag except if another custom tax is queried if ( empty( $taxonomies ) && ($query->is_category || $query->is_tag || $query->is_author || $query->is_post_type_archive || $query->is_date || $query->is_search || $query->is_feed ) ) { $query->is_tax = false; unset( $query->queried_object ); // FIXME useless? } } } /** * auto translate posts and terms ids * * @since 1.2 */ public function auto_translate() { $this->auto_translate = new PLL_Frontend_Auto_Translate( $this ); } /** * resets some variables when switching blog * overrides parent method * * @since 1.5.1 * * @param int $new_blog * @param int $old_blog */ public function switch_blog( $new_blog, $old_blog ) { // need to check that some languages are defined when user is logged in, has several blogs, some without any languages if ( parent::switch_blog( $new_blog, $old_blog ) && did_action( 'pll_language_defined' ) && $this->model->get_languages_list() ) { static $restore_curlang; if ( empty( $restore_curlang ) ) { $restore_curlang = $this->curlang->slug; // to always remember the current language through blogs } $lang = $this->model->get_language( $restore_curlang ); $this->curlang = $lang ? $lang : $this->model->get_language( $this->options['default_lang'] ); if ( isset( $this->static_pages ) ) { $this->static_pages->init(); } $this->load_strings_translations(); } } } frontend-auto-translate.php 0000666 00000016532 15213274706 0012057 0 ustar 00 model = &$polylang->model; $this->curlang = &$polylang->curlang; add_action( 'pre_get_posts', array( $this, 'pre_get_posts' ) ); // after main Polylang filter add_filter( 'get_terms_args', array( $this, 'get_terms_args' ), 10, 2 ); } /** * helper function to get the translated post in the current language * * since 1.8 * * @param int $post_id * @return int */ protected function get_post( $post_id ) { return $this->model->post->get( $post_id, $this->curlang ); } /** * helper function to get the translated term in the current language * * since 1.8 * * @param int $term_id * @return int */ protected function get_term( $term_id ) { return $this->model->term->get( $term_id, $this->curlang ); } /** * filters posts query to automatically translate included ids * * @since 1.1 * * @param object $query WP_Query object */ public function pre_get_posts( $query ) { global $wpdb; $qv = &$query->query_vars; if ( $query->is_main_query() || isset( $qv['lang'] ) || ( ! empty( $qv['post_type'] ) && ! $this->model->is_translated_post_type( $qv['post_type'] ) ) ) { return; } // /!\ always keep untranslated as is // term ids separated by a comma $arr = array(); if ( ! empty( $qv['cat'] ) ) { foreach ( explode( ',', $qv['cat'] ) as $cat ) { $tr = $this->get_term( abs( $cat ) ); $arr[] = $cat < 0 ? -$tr : $tr; } $qv['cat'] = implode( ',', $arr ); } // category_name $arr = array(); if ( ! empty( $qv['category_name'] ) ) { foreach ( explode( ',', $qv['category_name'] ) as $slug ) { $arr[] = ( ( $cat = wpcom_vip_get_category_by_slug( $slug ) ) && ( $tr_id = $this->get_term( $cat->term_id ) ) && ! is_wp_error( $tr = get_category( $tr_id ) ) ) ? $tr->slug : $slug; } $qv['category_name'] = implode( ',', $arr ); } // array of term ids foreach ( array( 'category__and', 'category__in', 'category__not_in', 'tag__and', 'tag__in', 'tag__not_in' ) as $key ) { $arr = array(); if ( ! empty( $qv[ $key ] ) ) { foreach ( $qv[ $key ] as $cat ) { $arr[] = ( $tr = $this->get_term( $cat ) ) ? $tr : $cat; } $qv[ $key ] = $arr; } } // tag $arr = array(); if ( ! empty( $qv['tag'] ) ) { $sep = strpos( $qv['tag'], ',' ) !== false ? ',' : '+'; // two possible separators for tag slugs foreach ( explode( $sep, $qv['tag'] ) as $slug ) { $arr[] = ( ( $tag = wpcom_vip_get_term_by( 'slug', $slug, 'post_tag' ) ) && ( $tr_id = $this->get_term( $tag->term_id ) ) && ! is_wp_error( $tr = get_tag( $tr_id ) ) ) ? $tr->slug : $slug; } $qv['tag'] = implode( $sep, $arr ); } // tag_id can only take one id if ( ! empty( $qv['tag_id'] ) && $tr_id = $this->get_term( $qv['tag_id'] ) ) { $qv['tag_id'] = $tr_id; } // array of tag slugs foreach ( array( 'tag_slug__and', 'tag_slug__in' ) as $key ) { $arr = array(); if ( ! empty( $qv[ $key ] ) ) { foreach ( $qv[ $key ] as $slug ) { $arr[] = ( ( $tag = wpcom_vip_get_term_by( 'slug', $slug, 'post_tag' ) ) && ( $tr_id = $this->get_term( $tag->term_id ) ) && ! is_wp_error( $tr = get_tag( $tr_id ) ) ) ? $tr->slug : $slug; } $qv[ $key ] = $arr; } } // custom taxonomies // according to codex, this type of query is deprecated as of WP 3.1 but it does not appear in WP 3.5 source code foreach ( array_intersect( $this->model->get_translated_taxonomies(), get_taxonomies( array( '_builtin' => false ) ) ) as $taxonomy ) { $tax = get_taxonomy( $taxonomy ); $arr = array(); if ( ! empty( $qv[ $tax->query_var ] ) ) { $sep = strpos( $qv[ $tax->query_var ], ',' ) !== false ? ',' : '+'; // two possible separators foreach ( explode( $sep, $qv[ $tax->query_var ] ) as $slug ) { $arr[] = ( ( $tag = wpcom_vip_get_term_by( 'slug', $slug, $taxonomy ) ) && ( $tr_id = $this->get_term( $tag->term_id ) ) && ! is_wp_error( $tr = get_term( $tr_id, $taxonomy ) ) ) ? $tr->slug : $slug; } $qv[ $tax->query_var ] = implode( $sep, $arr ); } } // tax_query since WP 3.1 if ( ! empty( $qv['tax_query'] ) && is_array( $qv['tax_query'] ) ) { $qv['tax_query'] = $this->translate_tax_query_recursive( $qv['tax_query'] ); } // p, page_id, post_parent can only take one id foreach ( array( 'p', 'page_id', 'post_parent' ) as $key ) { if ( ! empty( $qv[ $key ] ) && $tr_id = $this->get_post( $qv[ $key ] ) ) { $qv[ $key ] = $tr_id; } } // name, pagename can only take one slug foreach ( array( 'name', 'pagename' ) as $key ) { if ( ! empty( $qv[ $key ] ) ) { // no function to get post by name except get_posts itself $post_type = empty( $qv['post_type'] ) ? 'post' : $qv['post_type']; $id = $wpdb->get_var( $wpdb->prepare( "SELECT ID from $wpdb->posts WHERE post_type=%s AND post_name=%s", $post_type, $qv[ $key ] ) ); $qv[ $key ] = ( $id && ( $tr_id = $this->get_post( $id ) ) && $tr = get_post( $tr_id ) ) ? $tr->post_name : $qv[ $key ]; } } // array of post ids // post_parent__in & post_parent__not_in since WP 3.6 foreach ( array( 'post__in', 'post__not_in', 'post_parent__in', 'post_parent__not_in' ) as $key ) { $arr = array(); if ( ! empty( $qv[ $key ] ) ) { // post__in used by the 2 functions below // useless to filter them as output is already in the right language and would result in performance loss foreach ( debug_backtrace() as $trace ) { if ( in_array( $trace['function'], array( 'wp_nav_menu', 'gallery_shortcode' ) ) ) { return; } } foreach ( $qv[ $key ] as $p ) { $arr[] = ( $tr = $this->get_post( $p ) ) ? $tr : $p; } $qv[ $key ] = $arr; } } } /** * filters terms query to automatically translate included ids * * @since 1.1.1 * * @param array $args * @param array $taxonomies * @return array modified $args */ public function get_terms_args( $args, $taxonomies ) { if ( ! empty( $args['include'] ) && $this->model->is_translated_taxonomy( $taxonomies ) ) { foreach ( wp_parse_id_list( $args['include'] ) as $id ) { $arr[] = ( $tr = $this->get_term( $id ) ) ? $tr : $id; } $args['include'] = $arr; } return $args; } /** * translates tax queries * compatible with nested tax queries introduced in WP 4.1 * * @since 1.7 * * @param array $tax_queries * @return array translated tax queries */ protected function translate_tax_query_recursive( $tax_queries ) { foreach ( $tax_queries as $key => $q ) { if ( isset( $q['taxonomy'], $q['terms'] ) && $this->model->is_translated_taxonomy( $q['taxonomy'] ) ) { $arr = array(); $field = isset( $q['field'] ) && in_array( $q['field'], array( 'slug', 'name' ) ) ? $q['field'] : 'term_id'; foreach ( (array) $q['terms'] as $t ) { $arr[] = ( ( $tag = wpcom_vip_get_term_by( $field, $t, $q['taxonomy'] ) ) && ( $tr_id = $this->get_term( $tag->term_id ) ) && ! is_wp_error( $tr = get_term( $tr_id, $q['taxonomy'] ) ) ) ? $tr->$field : $t; } $tax_queries[ $key ]['terms'] = $arr; } // nested queries elseif ( is_array( $q ) ) { $tax_queries[ $key ] = $this->translate_tax_query_recursive( $q ); } } return $tax_queries; } } frontend-filters-search.php 0000666 00000010274 15213274706 0012024 0 ustar 00 links_model = &$polylang->links_model; $this->curlang = &$polylang->curlang; // adds the language information in the search form // low priority in case the search form is created using the same filter as described in http://codex.wordpress.org/Function_Reference/get_search_form add_filter( 'get_search_form', array( $this, 'get_search_form' ), 99 ); // adds the language information in admin bar search form add_action( 'add_admin_bar_menus', array( $this, 'add_admin_bar_menus' ) ); // adds javascript at the end of the document // was used for WP < 3.6. kept just in case if ( defined( 'PLL_SEARCH_FORM_JS' ) && PLL_SEARCH_FORM_JS ) { add_action( 'wp_footer', array( $this, 'wp_print_footer_scripts' ) ); } } /** * adds the language information in the search form * does not work if searchform.php ( prior to WP 3.6 ) is used or if the search form is hardcoded in another template file * * @since 0.1 * * @param string $form search form * @return string modified search form */ public function get_search_form( $form ) { if ( $form ) { if ( $this->links_model->using_permalinks ) { // take care to modify only the url in the