PluginProbe
Polylang / 3.7.7
Polylang v3.7.7
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 3.0.2 3.0.3 All 233 releases
← All changes | integrations/wpseo/wpseo.php +161 -108 2.93.7.7 View file →
@@ -4,9 +4,9 @@
4 4 */
5 5
6 6 /**
7 7 * Manages the compatibility with Yoast SEO
8 - * Version tested: 11.5
8 + * Version tested: 15.9.2
9 9 *
10 10 * @since 2.3
11 11 */
12 12 class PLL_WPSEO {
@@ -16,8 +16,9 @@
16 16 * @since 1.6.4
17 17 */
18 18 public function init() {
19 19 add_action( 'wp_loaded', array( $this, 'wpseo_translate_options' ) );
20 + add_filter( 'wpseo_dynamic_permalinks_enabled', '__return_true', 999 );
20 21
21 22 if ( PLL() instanceof PLL_Frontend ) {
22 23 // Filters sitemap queries to remove inactive language or to get
23 24 // one sitemap per language when using multiple domains or subdomains
@@ -37,74 +38,77 @@
37 38 add_action( 'pre_get_posts', array( $this, 'before_sitemap' ), 0 ); // Needs to be fired before WPSEO_Sitemaps::redirect()
38 39 }
39 40
40 41 add_filter( 'pll_home_url_white_list', array( $this, 'wpseo_home_url_white_list' ) );
41 - if ( version_compare( WPSEO_VERSION, '14.0', '<' ) ) {
42 - add_action( 'wpseo_opengraph', array( $this, 'wpseo_ogp' ), 2 );
43 - } else {
44 - add_filter( 'wpseo_frontend_presenters', array( $this, 'wpseo_frontend_presenters' ) );
45 - }
42 + add_filter( 'wpseo_frontend_presenters', array( $this, 'wpseo_frontend_presenters' ) );
46 43 add_filter( 'wpseo_canonical', array( $this, 'wpseo_canonical' ) );
47 44 add_filter( 'wpseo_frontend_presentation', array( $this, 'frontend_presentation' ) );
48 45 add_filter( 'wpseo_breadcrumb_indexables', array( $this, 'breadcrumb_indexables' ) );
49 46 } else {
50 - // Primary category
51 - add_filter( 'pll_copy_post_metas', array( $this, 'copy_post_metas' ) );
47 + add_filter( 'pll_copy_post_metas', array( $this, 'copy_post_metas' ), 10, 4 );
52 48 add_filter( 'pll_translate_post_meta', array( $this, 'translate_post_meta' ), 10, 3 );
49 + add_filter( 'pll_post_metas_to_export', array( $this, 'export_post_metas' ) );
50 +
51 + // Yoast SEO adds the columns hooks only for the 'inline-save' action. We need them for 'pll_update_post_rows' too.
52 + if ( wp_doing_ajax() && isset( $_POST['action'] ) && 'pll_update_post_rows' === $_POST['action'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
53 + $GLOBALS['wpseo_meta_columns'] = new WPSEO_Meta_Columns();
54 + }
53 55 }
54 56 }
55 57
56 58 /**
57 - * Registers custom post types and taxonomy titles for translation.
59 + * Registers options for translation.
58 60 *
59 61 * @since 2.9
60 62 */
61 63 public function wpseo_translate_options() {
62 - $keys = array();
64 + if ( method_exists( 'WPSEO_Options', 'clear_cache' ) ) {
65 + WPSEO_Options::clear_cache();
66 + }
63 67
64 - foreach ( get_post_types( array( 'public' => true, '_builtin' => false ) ) as $t ) {
65 - if ( pll_is_translated_post_type( $t ) ) {
66 - $keys[] = 'title-' . $t;
67 - $keys[] = 'metadesc-' . $t;
68 - }
69 - }
68 + $keys = array(
69 + 'title-*',
70 + 'metadesc-*',
71 + 'bctitle-*',
72 + 'breadcrumbs-sep',
73 + 'breadcrumbs-home',
74 + 'breadcrumbs-prefix',
75 + 'breadcrumbs-archiveprefix',
76 + 'breadcrumbs-searchprefix',
77 + 'breadcrumbs-404crumb',
78 + 'company_name',
79 + 'rssbefore',
80 + 'rssafter',
81 + 'social-title-*',
82 + 'social-description-*',
83 + );
70 84
71 - foreach ( get_post_types( array( 'has_archive' => true, '_builtin' => false ) ) as $t ) {
72 - if ( pll_is_translated_post_type( $t ) ) {
73 - $keys[] = 'title-ptarchive-' . $t;
74 - $keys[] = 'metadesc-ptarchive-' . $t;
75 - $keys[] = 'bctitle-ptarchive-' . $t;
76 - }
77 - }
85 + new PLL_Translate_Option( 'wpseo_titles', array_fill_keys( $keys, 1 ), array( 'context' => 'wordpress-seo' ) );
78 86
79 - foreach ( get_taxonomies( array( 'public' => true, '_builtin' => false ) ) as $t ) {
80 - if ( pll_is_translated_taxonomy( $t ) ) {
81 - $keys[] = 'title-tax-' . $t;
82 - $keys[] = 'metadesc-tax-' . $t;
83 - }
84 - }
87 + $keys = array(
88 + 'og_frontpage_title',
89 + 'og_frontpage_desc',
90 + );
85 91
86 - if ( ! empty( $keys ) ) {
87 - new PLL_Translate_Option( 'wpseo_titles', array_fill_keys( $keys, 1 ), array( 'context' => 'wordpress-seo' ) );
88 - }
92 + new PLL_Translate_Option( 'wpseo_social', array_fill_keys( $keys, 1 ), array( 'context' => 'wordpress-seo' ) );
89 93 }
90 94
91 95 /**
92 - * Fixes the home url as well as the stylesheet url
93 - * Only when using multiple domains or subdomains
96 + * Fixes the home url as well as the stylesheet url,
97 + * only when using multiple domains or subdomains.
94 98 *
95 99 * @since 1.6.4
96 100 *
97 - * @param string $url
98 - * @param string $path
101 + * @param string $url The complete URL including scheme and path.
102 + * @param string $path Path relative to the home URL.
99 103 * @return $url
100 104 */
101 105 public function wpseo_home_url( $url, $path ) {
102 106 if ( empty( $path ) ) {
103 - $path = ltrim( wp_parse_url( pll_get_requested_url(), PHP_URL_PATH ), '/' );
107 + $path = ltrim( (string) wp_parse_url( pll_get_requested_url(), PHP_URL_PATH ), '/' );
104 108 }
105 109
106 - if ( 'sitemap_index.xml' === $path || preg_match( '#([^/]+?)-sitemap([0-9]+)?\.xml|([a-z]+)?-?sitemap\.xsl#', $path ) ) {
110 + if ( preg_match( '#sitemap(_index)?\.xml|([^\/]+?)-?sitemap([0-9]+)?\.xml|([a-z]+)?-?sitemap\.xsl#', $path ) ) {
107 111 $url = PLL()->links_model->switch_language_in_link( $url, PLL()->curlang );
108 112 }
109 113
110 114 return $url;
@@ -125,42 +129,45 @@
125 129 return array();
126 130 }
127 131
128 132 /**
129 - * Modifies the sql request for posts sitemaps
130 - * Only when using multiple domains or subdomains or if some languages are not active
133 + * Modifies the sql request for posts sitemaps.
131 134 *
132 135 * @since 1.6.4
133 136 *
134 - * @param string $sql JOIN clause
135 - * @param string $post_type
137 + * @param string $sql JOIN clause.
138 + * @param string $post_type Post type.
136 139 * @return string
137 140 */
138 141 public function wpseo_posts_join( $sql, $post_type ) {
139 - return pll_is_translated_post_type( $post_type ) && ( PLL()->options['force_lang'] > 1 || $this->wpseo_get_active_languages() ) ? $sql . PLL()->model->post->join_clause() : $sql;
142 + return pll_is_translated_post_type( $post_type ) ? $sql . PLL()->model->post->join_clause() : $sql;
140 143 }
141 144
142 145 /**
143 - * Modifies the sql request for posts sitemaps
144 - * Only when using multiple domains or subdomains or if some languages are not active
146 + * Modifies the sql request for posts sitemaps.
145 147 *
146 148 * @since 1.6.4
147 149 *
148 - * @param string $sql WHERE clause
149 - * @param string $post_type
150 + * @param string $sql WHERE clause.
151 + * @param string $post_type Post type.
150 152 * @return string
151 153 */
152 154 public function wpseo_posts_where( $sql, $post_type ) {
153 - if ( pll_is_translated_post_type( $post_type ) ) {
154 - if ( PLL()->options['force_lang'] > 1 ) {
155 - return $sql . PLL()->model->post->where_clause( PLL()->curlang );
156 - }
155 + if ( ! pll_is_translated_post_type( $post_type ) ) {
156 + return $sql;
157 + }
157 158
158 - if ( $languages = $this->wpseo_get_active_languages() ) {
159 - return $sql . PLL()->model->post->where_clause( $languages );
160 - }
159 + if ( PLL()->options['force_lang'] > 1 && PLL()->curlang instanceof PLL_Language ) {
160 + return $sql . PLL()->model->post->where_clause( PLL()->curlang );
161 161 }
162 - return $sql;
162 +
163 + $languages = $this->wpseo_get_active_languages();
164 +
165 + if ( empty( $languages ) ) { // Empty when all languages are active.
166 + $languages = pll_languages_list();
167 + }
168 +
169 + return $sql . PLL()->model->post->where_clause( $languages );
163 170 }
164 171
165 172 /**
166 173 * Removes the language filter (and remove inactive languages) for the taxonomy sitemaps
@@ -194,13 +201,13 @@
194 201 }
195 202 }
196 203
197 204 /**
198 - * Add filters before the sitemap is evaluated and outputed
205 + * Add filters before the sitemap is evaluated and outputted.
199 206 *
200 207 * @since 2.6
201 208 *
202 - * @param object $query Instance of WP_Query being filtered.
209 + * @param WP_Query $query Instance of WP_Query being filtered.
203 210 */
204 211 public function before_sitemap( $query ) {
205 212 $type = $query->get( 'sitemap' );
206 213
@@ -274,13 +281,13 @@
274 281 return $str;
275 282 }
276 283
277 284 /**
278 - * Filters home url
285 + * Filters the home url.
279 286 *
280 287 * @since 1.1.2
281 288 *
282 - * @param array $arr
289 + * @param array $arr The list of files or functions for which `home_url()` must be filtered.
283 290 * @return array
284 291 */
285 292 public function wpseo_home_url_white_list( $arr ) {
286 293 return array_merge( $arr, array( array( 'file' => 'wordpress-seo' ) ) );
@@ -286,19 +293,19 @@
286 293 return array_merge( $arr, array( array( 'file' => 'wordpress-seo' ) ) );
287 294 }
288 295
289 296 /**
290 - * Get alternate language codes for Opengraph
297 + * Get alternate language codes for Opengraph.
291 298 *
292 299 * @since 2.7.3
293 300 *
294 - * @return array
301 + * @return string[]
295 302 */
296 303 protected function get_ogp_alternate_languages() {
297 304 $alternates = array();
298 305
299 306 foreach ( PLL()->model->get_languages_list() as $language ) {
300 - if ( PLL()->curlang->slug !== $language->slug && PLL()->links->get_translation_url( $language ) && isset( $language->facebook ) ) {
307 + if ( isset( PLL()->curlang ) && PLL()->curlang->slug !== $language->slug && PLL()->links->get_translation_url( $language ) && isset( $language->facebook ) ) {
301 308 $alternates[] = $language->facebook;
302 309 }
303 310 }
304 311
@@ -308,24 +315,8 @@
308 315
309 316 /**
310 317 * Adds opengraph support for translations
311 318 *
312 - * @since 1.6
313 - */
314 - public function wpseo_ogp() {
315 - global $wpseo_og;
316 -
317 - // WPSEO already deals with the locale
318 - if ( did_action( 'pll_init' ) && method_exists( $wpseo_og, 'og_tag' ) ) {
319 - foreach ( $this->get_ogp_alternate_languages() as $lang ) {
320 - $wpseo_og->og_tag( 'og:locale:alternate', $lang );
321 - }
322 - }
323 - }
324 -
325 - /**
326 - * Adds opengraph support for translations
327 - *
328 319 * @since 2.7.3
329 320 *
330 321 * @param array $presenters An array of objects implementing Abstract_Indexable_Presenter
331 322 * @return array
@@ -344,13 +335,13 @@
344 335 return $_presenters;
345 336 }
346 337
347 338 /**
348 - * Fixes the canonical front page url as unlike WP, WPSEO does not add a trailing slash to the canonical front page url
339 + * Fixes the canonical front page url as unlike WP, WPSEO does not add a trailing slash to the canonical front page url.
349 340 *
350 341 * @since 1.7.10
351 342 *
352 - * @param string $url
343 + * @param string $url The canonical URL evaluated by Yoast SEO.
353 344 * @return $url
354 345 */
355 346 public function wpseo_canonical( $url ) {
356 347 return is_front_page( $url ) && get_option( 'permalink_structure' ) ? trailingslashit( $url ) : $url;
@@ -366,28 +357,29 @@
366 357 */
367 358 public function frontend_presentation( $presentation ) {
368 359 switch ( $presentation->model->object_type ) {
369 360 case 'home-page':
370 - $presentation->model->permalink = pll_home_url();
371 361 $presentation->model->title = WPSEO_Options::get( 'title-home-wpseo' );
372 362 $presentation->model->description = WPSEO_Options::get( 'metadesc-home-wpseo' );
363 + $presentation->model->open_graph_title = WPSEO_Options::get( 'og_frontpage_title' );
364 + $presentation->model->open_graph_description = WPSEO_Options::get( 'og_frontpage_desc' );
373 365 break;
374 366
375 367 case 'post-type-archive':
376 368 if ( pll_is_translated_post_type( $presentation->model->object_sub_type ) ) {
377 - $presentation->model->permalink = get_post_type_archive_link( $presentation->model->object_sub_type );
378 369 $presentation->model->title = WPSEO_Options::get( 'title-ptarchive-' . $presentation->model->object_sub_type );
379 370 $presentation->model->description = WPSEO_Options::get( 'metadesc-ptarchive-' . $presentation->model->object_sub_type );
380 371 }
381 372 break;
382 373
383 - case 'user':
384 - $presentation->model->permalink = get_author_posts_url( $presentation->model->object_id );
385 - break;
386 -
387 374 case 'system-page':
388 - if ( '404' === $presentation->model->object_sub_type ) {
389 - $presentation->model->title = WPSEO_Options::get( 'title-404-wpseo' );
375 + switch ( $presentation->model->object_sub_type ) {
376 + case '404':
377 + $presentation->model->title = WPSEO_Options::get( 'title-404-wpseo' );
378 + break;
379 + case 'search-result':
380 + $presentation->model->title = WPSEO_Options::get( 'title-search-wpseo' );
381 + break;
390 382 }
391 383 break;
392 384 }
393 385
@@ -394,29 +386,32 @@
394 386 return $presentation;
395 387 }
396 388
397 389 /**
398 - * Fixes the breadcrumb links and strings stored in the indexable table since Yoast SEO 14.0
390 + * Fixes the breadcrumb links and strings stored in the indexable table since Yoast SEO 14.0.
399 391 *
392 + * In version 17.0, the breadcrumb links do not honor the filter `wpseo_dynamic_permalinks_enabled`.
393 + *
400 394 * @since 2.8.3
401 395 *
402 396 * @param array $indexables An array of Indexable objects.
403 - * @return object
397 + * @return array
404 398 */
405 399 public function breadcrumb_indexables( $indexables ) {
406 400 foreach ( $indexables as &$indexable ) {
407 - switch ( $indexable->object_type ) {
408 - case 'home-page':
409 - $indexable->permalink = pll_home_url();
410 - $indexable->breadcrumb_title = pll__( WPSEO_Options::get( 'breadcrumbs-home' ) );
411 - break;
412 -
413 - case 'post-type-archive':
414 - if ( pll_is_translated_post_type( $indexable->object_sub_type ) ) {
415 - $indexable->permalink = get_post_type_archive_link( $indexable->object_sub_type );
416 - $indexable->breadcrumb_title = pll__( WPSEO_Options::get( 'bctitle-ptarchive-' . $indexable->object_sub_type ) );
417 - }
418 - break;
401 + if ( 'home-page' === $indexable->object_type || ( 'post' === $indexable->object_type && 'page' === $indexable->object_sub_type && get_option( 'page_on_front' ) === $indexable->object_id ) ) {
402 + // Handles both when the front page displays the list of posts or a static page.
403 + $indexable->permalink = pll_home_url();
404 + $indexable->breadcrumb_title = pll__( WPSEO_Options::get( 'breadcrumbs-home' ) );
405 + } elseif ( 'post' === $indexable->object_type && 'page' === $indexable->object_sub_type && get_option( 'page_for_posts' ) === $indexable->object_id ) {
406 + $indexable->permalink = get_permalink( $indexable->object_id );
407 + } elseif ( 'post-type-archive' === $indexable->object_type && pll_is_translated_post_type( $indexable->object_sub_type ) ) {
408 + $indexable->permalink = get_post_type_archive_link( $indexable->object_sub_type );
409 + $breadcrumb_title = WPSEO_Options::get( 'bctitle-ptarchive-' . $indexable->object_sub_type );
410 + $breadcrumb_title = $breadcrumb_title ?: $indexable->breadcrumb_title; // The option may be empty.
411 + $indexable->breadcrumb_title = pll__( $breadcrumb_title );
412 + } elseif ( 'term' === $indexable->object_type && pll_is_translated_taxonomy( $indexable->object_sub_type ) ) {
413 + $indexable->permalink = get_term_link( $indexable->object_id );
419 414 }
420 415 }
421 416
422 417 return $indexables;
@@ -422,16 +417,32 @@
422 417 return $indexables;
423 418 }
424 419
425 420 /**
426 - * Synchronize the primary term
421 + * Copies or synchronizes the metas.
427 422 *
428 423 * @since 2.3.3
429 424 *
430 - * @param array $keys List of custom fields names.
425 + * @param string[] $keys List of custom fields names.
426 + * @param bool $sync True if it is synchronization, false if it is a copy.
427 + * @param int $from Id of the post from which we copy information.
428 + * @param int $to Id of the post to which we paste information.
431 429 * @return array
432 430 */
433 - public function copy_post_metas( $keys ) {
431 + public function copy_post_metas( $keys, $sync, $from, $to ) {
432 + if ( ! $sync ) {
433 + // Text requiring translation.
434 + $keys = array_merge( $keys, $this->get_translatable_meta_keys() );
435 +
436 + // Copy the image urls.
437 + $keys[] = '_yoast_wpseo_opengraph-image';
438 + $keys[] = '_yoast_wpseo_twitter-image';
439 +
440 + $keys[] = '_yoast_wpseo_meta-robots-noindex';
441 + $keys[] = '_yoast_wpseo_meta-robots-nofollow';
442 + $keys[] = '_yoast_wpseo_meta-robots-adv';
443 + }
444 +
434 445 $taxonomies = get_taxonomies(
435 446 array(
436 447 'hierarchical' => true,
437 448 'public' => true,
@@ -437,8 +448,12 @@
437 448 'public' => true,
438 449 )
439 450 );
440 451
452 + $sync_taxonomies = PLL()->sync->taxonomies->get_taxonomies_to_copy( $sync, $from, $to );
453 +
454 + $taxonomies = array_intersect( $taxonomies, $sync_taxonomies );
455 +
441 456 foreach ( $taxonomies as $taxonomy ) {
442 457 $keys[] = '_yoast_wpseo_primary_' . $taxonomy;
443 458 }
444 459
@@ -455,10 +470,48 @@
455 470 * @param string $lang Language of target.
456 471 * @return int
457 472 */
458 473 public function translate_post_meta( $value, $key, $lang ) {
459 - if ( false !== strpos( $key, '_yoast_wpseo_primary_' ) ) {
460 - $value = pll_get_term( $value, $lang );
474 + if ( 0 !== strpos( $key, '_yoast_wpseo_primary_' ) ) {
475 + return $value;
461 476 }
462 - return $value;
477 +
478 + $taxonomy = str_replace( '_yoast_wpseo_primary_', '', $key );
479 + if ( ! PLL()->model->is_translated_taxonomy( $taxonomy ) ) {
480 + return $value;
481 + }
482 +
483 + return pll_get_term( $value, $lang );
484 + }
485 +
486 + /**
487 + * Adds the yoast translatable metas to export.
488 + *
489 + * @param array $metas An array of post metas (keyed with meta keys) to export.
490 + * @return array The modified array of post metas to export.
491 + */
492 + public function export_post_metas( $metas ) {
493 + $metas_to_export = array_fill_keys( $this->get_translatable_meta_keys(), 1 );
494 +
495 + return array_merge( $metas, $metas_to_export );
496 + }
497 +
498 + /**
499 + * Returns the meta keys with translatable text.
500 + *
501 + * @since 3.3
502 + *
503 + * @return string[]
504 + */
505 + protected function get_translatable_meta_keys() {
506 + return array(
507 + '_yoast_wpseo_title',
508 + '_yoast_wpseo_metadesc',
509 + '_yoast_wpseo_bctitle',
510 + '_yoast_wpseo_focuskw',
511 + '_yoast_wpseo_opengraph-title',
512 + '_yoast_wpseo_opengraph-description',
513 + '_yoast_wpseo_twitter-title',
514 + '_yoast_wpseo_twitter-description',
515 + );
463 516 }
464 517 }