PluginProbe
Polylang / 2.0.3
Polylang v2.0.3
3.8.11 3.8.10 3.8.9 3.8.8 3.8.7 3.8.6 3.8.5 3.8.4 3.8.3 2.7 2.7.0.1 2.7.1 2.7.2 2.7.3 2.7.4 2.8 2.8.1 2.8.2 2.8.3 2.8.4 2.9 2.9.1 2.9.2 3.0 3.0.1 All 235 releases
polylang / frontend / frontend-filters-links.php

frontend-filters-links.php in Polylang 2.0.3, at frontend/frontend-filters-links.php

408 lines 13.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * manages links filters on frontend
5 *
6 * @since 1.8
7 */
8 class PLL_Frontend_Filters_Links extends PLL_Filters_Links {
9 public $curlang;
10 public $cache; // our internal non persistent cache object
11
12 /**
13 * constructor
14 * adds filters once the language is defined
15 * low priority on links filters to come after any other modification
16 *
17 * @since 1.8
18 *
19 * @param object $polylang
20 */
21 public function __construct( &$polylang ) {
22 parent::__construct( $polylang );
23
24 $this->curlang = &$polylang->curlang;
25 $this->cache = new PLL_Cache();
26
27 // rewrites author and date links to filter them by language
28 foreach ( array( 'feed_link', 'author_link', 'search_link', 'year_link', 'month_link', 'day_link' ) as $filter ) {
29 add_filter( $filter, array( $this, 'archive_link' ), 20 );
30 }
31
32 // rewrites post types archives links to filter them by language
33 add_filter( 'post_type_archive_link', array( $this, 'post_type_archive_link' ), 20, 2 );
34
35 // meta in the html head section
36 add_action( 'wp_head', array( $this, 'wp_head' ) );
37
38 // modifies the home url
39 if ( ! defined( 'PLL_FILTER_HOME_URL' ) || PLL_FILTER_HOME_URL ) {
40 add_filter( 'home_url', array( $this, 'home_url' ), 10, 2 );
41 }
42
43 if ( $this->options['force_lang'] > 1 ) {
44 // rewrites next and previous post links when not automatically done by WordPress
45 add_filter( 'get_pagenum_link', array( $this, 'archive_link' ), 20 );
46
47 // rewrites ajax url
48 add_filter( 'admin_url', array( $this, 'admin_url' ), 10, 2 );
49 }
50
51 // redirects to canonical url before WordPress redirect_canonical
52 // but after Nextgen Gallery which hacks $_SERVER['REQUEST_URI'] !!! and restores it in 'template_redirect' with priority 1
53 add_action( 'template_redirect', array( $this, 'check_canonical_url' ), 4 );
54 }
55
56 /**
57 * modifies the author and date links to add the language parameter ( as well as feed link )
58 *
59 * @since 0.4
60 *
61 * @param string $link
62 * @return string modified link
63 */
64 public function archive_link( $link ) {
65 return $this->links_model->add_language_to_link( $link, $this->curlang );
66 }
67
68 /**
69 * modifies the post type archive links to add the language parameter
70 * only if the post type is translated
71 *
72 * @since 1.7.6
73 *
74 * @param string $link
75 * @param string $post_type
76 * @return string modified link
77 */
78 public function post_type_archive_link( $link, $post_type ) {
79 return $this->model->is_translated_post_type( $post_type ) && 'post' !== $post_type ? $this->links_model->add_language_to_link( $link, $this->curlang ) : $link;
80 }
81
82 /**
83 * modifies post & page links
84 * caches the result
85 *
86 * @since 0.7
87 *
88 * @param string $link post link
89 * @param object $post post object
90 * @return string modified post link
91 */
92 public function post_link( $link, $post ) {
93 $cache_key = 'post:' . $post->ID;
94 if ( false === $_link = $this->cache->get( $cache_key ) ) {
95 $_link = parent::post_link( $link, $post );
96 $this->cache->set( $cache_key, $_link );
97 }
98 return $_link;
99 }
100
101 /**
102 * modifies page links
103 * caches the result
104 *
105 * @since 1.7
106 *
107 * @param string $link post link
108 * @param int $post_id post ID
109 * @return string modified post link
110 */
111 public function _get_page_link( $link, $post_id ) {
112 $cache_key = 'post:' . $post_id;
113 if ( false === $_link = $this->cache->get( $cache_key ) ) {
114 $_link = parent::_get_page_link( $link, $post_id );
115 $this->cache->set( $cache_key, $_link );
116 }
117 return $_link;
118 }
119
120 /**
121 * modifies attachment links
122 * caches the result
123 *
124 * @since 1.6.2
125 *
126 * @param string $link attachment link
127 * @param int $post_id attachment link
128 * @return string modified attachment link
129 */
130 public function attachment_link( $link, $post_id ) {
131 $cache_key = 'post:' . $post_id;
132 if ( false === $_link = $this->cache->get( $cache_key ) ) {
133 $_link = parent::attachment_link( $link, $post_id );
134 $this->cache->set( $cache_key, $_link );
135 }
136 return $_link;
137 }
138
139 /**
140 * modifies custom posts links
141 * caches the result
142 *
143 * @since 1.6
144 *
145 * @param string $link post link
146 * @param object $post post object
147 * @return string modified post link
148 */
149 public function post_type_link( $link, $post ) {
150 $cache_key = 'post:' . $post->ID;
151 if ( false === $_link = $this->cache->get( $cache_key ) ) {
152 $_link = parent::post_type_link( $link, $post );
153 $this->cache->set( $cache_key, $_link );
154 }
155 return $_link;
156 }
157
158 /**
159 * modifies filtered taxonomies ( post format like ) and translated taxonomies links
160 * caches the result
161 *
162 * @since 0.7
163 *
164 * @param string $link
165 * @param object $term term object
166 * @param string $tax taxonomy name
167 * @return string modified link
168 */
169 public function term_link( $link, $term, $tax ) {
170 $cache_key = 'term:' . $term->term_id;
171 if ( false === $_link = $this->cache->get( $cache_key ) ) {
172 if ( in_array( $tax, $this->model->get_filtered_taxonomies() ) ) {
173 $_link = $this->links_model->add_language_to_link( $link, $this->curlang );
174
175 /** This filter is documented in include/filters-links.php */
176 $_link = apply_filters( 'pll_term_link', $_link, $this->curlang, $term );
177 }
178
179 else {
180 $_link = parent::term_link( $link, $term, $tax );
181 }
182 $this->cache->set( $cache_key, $_link );
183 }
184 return $_link;
185 }
186
187 /**
188 * outputs references to translated pages ( if exists ) in the html head section
189 *
190 * @since 0.1
191 */
192 public function wp_head() {
193 // Google recommends to include self link https://support.google.com/webmasters/answer/189077?hl=en
194 foreach ( $this->model->get_languages_list() as $language ) {
195 if ( $url = $this->links->get_translation_url( $language ) ) {
196 $urls[ $language->get_locale( 'display' ) ] = $url;
197 }
198 }
199
200 // ouptputs the section only if there are translations ( $urls always contains self link )
201 // don't output anything on paged archives: see https://wordpress.org/support/topic/hreflang-on-page2
202 if ( ! empty( $urls ) && count( $urls ) > 1 && ! is_paged() ) {
203 // prepare the list of languages to remove the country code
204 foreach ( array_keys( $urls ) as $locale ) {
205 $split = explode( '-', $locale );
206 $languages[ $locale ] = reset( $split );
207 }
208
209 $count = array_count_values( $languages );
210
211 foreach ( $urls as $locale => $url ) {
212 $lang = $count[ $languages[ $locale ] ] > 1 ? $locale : $languages[ $locale ]; // output the country code only when necessary
213 printf( '<link rel="alternate" href="%s" hreflang="%s" />'."\n", esc_url( $url ), esc_attr( $lang ) );
214 }
215
216 // adds the site root url when the default language code is not hidden
217 // see https://wordpress.org/support/topic/implementation-of-hreflangx-default
218 if ( is_front_page() && ! $this->options['hide_default'] && $this->options['force_lang'] < 3 ) {
219 printf( '<link rel="alternate" href="%s" hreflang="x-default" />'."\n", esc_url( home_url( '/' ) ) );
220 }
221 }
222 }
223
224 /**
225 * filters the home url to get the right language
226 *
227 * @since 0.4
228 *
229 * @param string $url
230 * @param string $path
231 * @return string
232 */
233 public function home_url( $url, $path ) {
234 if ( ! ( did_action( 'template_redirect' ) || did_action( 'login_init' ) ) || rtrim( $url,'/' ) != $this->links_model->home ) {
235 return $url;
236 }
237
238 static $white_list, $black_list; // avoid evaluating this at each function call
239
240 // we *want* to filter the home url in these cases
241 if ( empty( $white_list ) ) {
242 // on Windows get_theme_root() mixes / and \
243 // we want only \ for the comparison with debug_backtrace
244 $theme_root = get_theme_root();
245 $theme_root = ( false === strpos( $theme_root, '\\' ) ) ? $theme_root : str_replace( '/', '\\', $theme_root );
246
247 /**
248 * Filter the white list of the Polylang 'home_url' filter
249 * The $args contains an array of arrays each of them having
250 * a 'file' key and/or a 'function' key to decide which functions in
251 * which files using home_url() calls must be filtered
252 *
253 * @since 1.1.2
254 *
255 * @param array $args
256 */
257 $white_list = apply_filters( 'pll_home_url_white_list', array(
258 array( 'file' => $theme_root ),
259 array( 'function' => 'wp_nav_menu' ),
260 array( 'function' => 'login_footer' ),
261 array( 'function' => 'get_custom_logo' ),
262 ) );
263 }
264
265 // we don't want to filter the home url in these cases
266 if ( empty( $black_list ) ) {
267
268 /**
269 * Filter the black list of the Polylang 'home_url' filter
270 * The $args contains an array of arrays each of them having
271 * a 'file' key and/or a 'function' key to decide which functions in
272 * which files using home_url() calls must be filtered
273 *
274 * @since 1.1.2
275 *
276 * @param array $args
277 */
278 $black_list = apply_filters( 'pll_home_url_black_list', array(
279 array( 'file' => 'searchform.php' ), // since WP 3.6 searchform.php is passed through get_search_form
280 array( 'function' => 'get_search_form' ),
281 ) );
282 }
283
284 $traces = version_compare( PHP_VERSION, '5.2.5', '>=' ) ? debug_backtrace( false ) : debug_backtrace();
285 unset( $traces[0], $traces[1] ); // We don't need the last 2 calls: this function + call_user_func_array (or apply_filters on PHP7+)
286
287 foreach ( $traces as $trace ) {
288 // black list first
289 foreach ( $black_list as $v ) {
290 if ( ( isset( $trace['file'], $v['file'] ) && false !== strpos( $trace['file'], $v['file'] ) ) || ( isset( $trace['function'], $v['function'] ) && $trace['function'] == $v['function'] ) ) {
291 return $url;
292 }
293 }
294
295 foreach ( $white_list as $v ) {
296 if ( ( isset( $trace['function'], $v['function'] ) && $trace['function'] == $v['function'] ) ||
297 ( isset( $trace['file'], $v['file'] ) && false !== strpos( $trace['file'], $v['file'] ) && in_array( $trace['function'], array( 'home_url', 'get_home_url', 'bloginfo', 'get_bloginfo' ) ) ) ) {
298 $ok = true;
299 }
300 }
301 }
302
303 return empty( $ok ) ? $url : ( empty( $path ) ? rtrim( $this->links->get_home_url( $this->curlang ), '/' ) : $this->links->get_home_url( $this->curlang ) );
304 }
305
306 /**
307 * rewrites ajax url when using domains or subdomains
308 *
309 * @since 1.5
310 *
311 * @param string $url admin url with path evaluated by WordPress
312 * @param string $path admin path
313 * @return string
314 */
315 public function admin_url( $url, $path ) {
316 return 'admin-ajax.php' === $path ? $this->links_model->switch_language_in_link( $url, $this->curlang ) : $url;
317 }
318
319 /**
320 * if the language code is not in agreement with the language of the content
321 * redirects incoming links to the proper URL to avoid duplicate content
322 *
323 * @since 0.9.6
324 *
325 * @param string $requested_url optional
326 * @param bool $do_redirect optional, whether to perform the redirection or not
327 * @return string if redirect is not performed
328 */
329 public function check_canonical_url( $requested_url = '', $do_redirect = true ) {
330 global $wp_query, $post, $is_IIS;
331
332 // don't redirect in same cases as WP
333 if ( is_trackback() || is_search() || is_admin() || is_preview() || is_robots() || ( $is_IIS && ! iis7_supports_permalinks() ) ) {
334 return;
335 }
336
337 // don't redirect mysite.com/?attachment_id= to mysite.com/en/?attachment_id=
338 if ( 1 == $this->options['force_lang'] && is_attachment() && isset( $_GET['attachment_id'] ) ) {
339 return;
340 }
341
342 // if the default language code is not hidden and the static front page url contains the page name
343 // the customizer lands here and the code below would redirect to the list of posts
344 if ( isset( $_POST['wp_customize'], $_POST['customized'] ) ) {
345 return;
346 }
347
348 if ( empty( $requested_url ) ) {
349 $requested_url = ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
350 }
351
352 if ( is_single() || is_page() ) {
353 if ( isset( $post->ID ) && $this->model->is_translated_post_type( $post->post_type ) ) {
354 $language = $this->model->post->get_language( (int) $post->ID );
355 }
356 }
357
358 elseif ( is_category() || is_tag() || is_tax() ) {
359 $obj = $wp_query->get_queried_object();
360 if ( $this->model->is_translated_taxonomy( $obj->taxonomy ) ) {
361 $language = $this->model->term->get_language( (int) $obj->term_id );
362 }
363 }
364
365 elseif ( $wp_query->is_posts_page ) {
366 $obj = $wp_query->get_queried_object();
367 $language = $this->model->post->get_language( (int) $obj->ID );
368 }
369
370 elseif ( is_404() && ! empty( $wp_query->query['page_id'] ) && $id = get_query_var( 'page_id' ) ) {
371 // special case for page shortlinks when using subdomains or multiple domains
372 // needed because redirect_canonical doesn't accept to change the domain name
373 $language = $this->model->post->get_language( (int) $id );
374 }
375
376 if ( empty( $language ) ) {
377 $language = $this->curlang;
378 $redirect_url = $requested_url;
379 } else {
380 // first get the canonical url evaluated by WP
381 $redirect_url = ( ! $redirect_url = redirect_canonical( $requested_url, false ) ) ? $requested_url : $redirect_url;
382
383 // then get the right language code in url
384 $redirect_url = $this->options['force_lang'] ?
385 $this->links_model->switch_language_in_link( $redirect_url, $language ) :
386 $this->links_model->remove_language_from_link( $redirect_url ); // works only for default permalinks
387 }
388
389 /**
390 * Filters the canonical url detected by Polylang
391 *
392 * @since 1.6
393 *
394 * @param bool|string $redirect_url false or the url to redirect to
395 * @param object $language the language detected
396 */
397 $redirect_url = apply_filters( 'pll_check_canonical_url', $redirect_url, $language );
398
399 // the language is not correctly set so let's redirect to the correct url for this object
400 if ( $do_redirect && $redirect_url && $requested_url != $redirect_url ) {
401 wp_redirect( $redirect_url, 301 );
402 exit;
403 }
404
405 return $redirect_url;
406 }
407 }
408