PluginProbe
Polylang / 3.0.3
Polylang v3.0.3
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
polylang / integrations / wpseo / wpseo.php

wpseo.php in Polylang 3.0.3, at integrations/wpseo/wpseo.php

494 lines 15.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * @package Polylang
4 */
5
6 /**
7 * Manages the compatibility with Yoast SEO
8 * Version tested: 15.9.2
9 *
10 * @since 2.3
11 */
12 class PLL_WPSEO {
13 /**
14 * Translate options and add specific filters and actions
15 *
16 * @since 1.6.4
17 */
18 public function init() {
19 add_action( 'wp_loaded', array( $this, 'wpseo_translate_options' ) );
20
21 if ( PLL() instanceof PLL_Frontend ) {
22 // Filters sitemap queries to remove inactive language or to get
23 // one sitemap per language when using multiple domains or subdomains
24 // because WPSEO does not accept several domains or subdomains in one sitemap
25 add_filter( 'wpseo_posts_join', array( $this, 'wpseo_posts_join' ), 10, 2 );
26 add_filter( 'wpseo_posts_where', array( $this, 'wpseo_posts_where' ), 10, 2 );
27 add_filter( 'wpseo_typecount_join', array( $this, 'wpseo_posts_join' ), 10, 2 );
28 add_filter( 'wpseo_typecount_where', array( $this, 'wpseo_posts_where' ), 10, 2 );
29
30 if ( PLL()->options['force_lang'] > 1 ) {
31 add_filter( 'wpseo_enable_xml_sitemap_transient_caching', '__return_false' ); // Disable cache! otherwise WPSEO keeps only one domain (thanks to Junaid Bhura)
32 add_filter( 'home_url', array( $this, 'wpseo_home_url' ), 10, 2 ); // Fix home_url
33 add_action( 'setup_theme', array( $this, 'maybe_deactivate_sitemap' ) ); // Deactivate sitemaps for inactive languages.
34 } else {
35 // Get all terms in all languages when the language is set from the content or directory name
36 add_filter( 'get_terms_args', array( $this, 'wpseo_remove_terms_filter' ) );
37 add_action( 'pre_get_posts', array( $this, 'before_sitemap' ), 0 ); // Needs to be fired before WPSEO_Sitemaps::redirect()
38 }
39
40 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 }
46 add_filter( 'wpseo_canonical', array( $this, 'wpseo_canonical' ) );
47 add_filter( 'wpseo_frontend_presentation', array( $this, 'frontend_presentation' ) );
48 add_filter( 'wpseo_breadcrumb_indexables', array( $this, 'breadcrumb_indexables' ) );
49 } else {
50 add_filter( 'pll_copy_post_metas', array( $this, 'copy_post_metas' ), 10, 2 );
51 add_filter( 'pll_translate_post_meta', array( $this, 'translate_post_meta' ), 10, 3 );
52 }
53 }
54
55 /**
56 * Registers options for translation.
57 *
58 * @since 2.9
59 */
60 public function wpseo_translate_options() {
61 if ( method_exists( 'WPSEO_Options', 'clear_cache' ) ) {
62 WPSEO_Options::clear_cache();
63 }
64
65 $keys = array(
66 'title-*',
67 'metadesc-*',
68 'bctitle-*',
69 'breadcrumbs-sep',
70 'breadcrumbs-home',
71 'breadcrumbs-prefix',
72 'breadcrumbs-archiveprefix',
73 'breadcrumbs-searchprefix',
74 'breadcrumbs-404crumb',
75 'company_name',
76 'rssbefore',
77 'rssafter',
78 );
79
80 new PLL_Translate_Option( 'wpseo_titles', array_fill_keys( $keys, 1 ), array( 'context' => 'wordpress-seo' ) );
81
82 $keys = array(
83 'og_frontpage_title',
84 'og_frontpage_desc',
85 );
86
87 new PLL_Translate_Option( 'wpseo_social', array_fill_keys( $keys, 1 ), array( 'context' => 'wordpress-seo' ) );
88 }
89
90 /**
91 * Fixes the home url as well as the stylesheet url
92 * Only when using multiple domains or subdomains
93 *
94 * @since 1.6.4
95 *
96 * @param string $url
97 * @param string $path
98 * @return $url
99 */
100 public function wpseo_home_url( $url, $path ) {
101 if ( empty( $path ) ) {
102 $path = ltrim( wp_parse_url( pll_get_requested_url(), PHP_URL_PATH ), '/' );
103 }
104
105 if ( preg_match( '#sitemap(_index)?\.xml|([^\/]+?)-?sitemap([0-9]+)?\.xml|([a-z]+)?-?sitemap\.xsl#', $path ) ) {
106 $url = PLL()->links_model->switch_language_in_link( $url, PLL()->curlang );
107 }
108
109 return $url;
110 }
111
112 /**
113 * Get active languages for the sitemaps
114 *
115 * @since 2.0
116 *
117 * @return array list of active language slugs, empty if all languages are active
118 */
119 protected function wpseo_get_active_languages() {
120 $languages = PLL()->model->get_languages_list();
121 if ( wp_list_filter( $languages, array( 'active' => false ) ) ) {
122 return wp_list_pluck( wp_list_filter( $languages, array( 'active' => false ), 'NOT' ), 'slug' );
123 }
124 return array();
125 }
126
127 /**
128 * Modifies the sql request for posts sitemaps
129 * Only when using multiple domains or subdomains or if some languages are not active
130 *
131 * @since 1.6.4
132 *
133 * @param string $sql JOIN clause
134 * @param string $post_type
135 * @return string
136 */
137 public function wpseo_posts_join( $sql, $post_type ) {
138 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;
139 }
140
141 /**
142 * Modifies the sql request for posts sitemaps
143 * Only when using multiple domains or subdomains or if some languages are not active
144 *
145 * @since 1.6.4
146 *
147 * @param string $sql WHERE clause
148 * @param string $post_type
149 * @return string
150 */
151 public function wpseo_posts_where( $sql, $post_type ) {
152 if ( pll_is_translated_post_type( $post_type ) ) {
153 if ( PLL()->options['force_lang'] > 1 ) {
154 return $sql . PLL()->model->post->where_clause( PLL()->curlang );
155 }
156
157 if ( $languages = $this->wpseo_get_active_languages() ) {
158 return $sql . PLL()->model->post->where_clause( $languages );
159 }
160 }
161 return $sql;
162 }
163
164 /**
165 * Removes the language filter (and remove inactive languages) for the taxonomy sitemaps
166 * Only when the language is set from the content or directory name
167 *
168 * @since 1.0.3
169 *
170 * @param array $args get_terms arguments
171 * @return array modified list of arguments
172 */
173 public function wpseo_remove_terms_filter( $args ) {
174 if ( isset( $GLOBALS['wp_query']->query['sitemap'] ) ) {
175 $args['lang'] = implode( ',', $this->wpseo_get_active_languages() );
176 }
177 return $args;
178 }
179
180 /**
181 * Deactivates the sitemap for inactive languages when using subdomains or multiple domains
182 *
183 * @since 2.6.1
184 */
185 public function maybe_deactivate_sitemap() {
186 global $wpseo_sitemaps;
187
188 if ( isset( $wpseo_sitemaps ) ) {
189 $active_languages = $this->wpseo_get_active_languages();
190 if ( ! empty( $active_languages ) && ! in_array( pll_current_language(), $active_languages ) ) {
191 remove_action( 'pre_get_posts', array( $wpseo_sitemaps, 'redirect' ), 1 );
192 }
193 }
194 }
195
196 /**
197 * Add filters before the sitemap is evaluated and outputed.
198 *
199 * @since 2.6
200 *
201 * @param WP_Query $query Instance of WP_Query being filtered.
202 */
203 public function before_sitemap( $query ) {
204 $type = $query->get( 'sitemap' );
205
206 // Add the post post type archives in all languages to the sitemap
207 // Add the homepages for all languages to the sitemap when the front page displays posts
208 if ( $type && pll_is_translated_post_type( $type ) && ( 'post' !== $type || ! get_option( 'page_on_front' ) ) ) {
209 add_filter( "wpseo_sitemap_{$type}_content", array( $this, 'add_post_type_archive' ) );
210 }
211 }
212
213 /**
214 * Generates a post type archive sitemap url
215 *
216 * @since 2.6.1
217 *
218 * @param string $link The url.
219 * @param string $post_type The post type name.
220 * @return string Formatted sitemap url.
221 */
222 protected function format_sitemap_url( $link, $post_type ) {
223 global $wpseo_sitemaps;
224
225 return $wpseo_sitemaps->renderer->sitemap_url(
226 array(
227 'loc' => $link,
228 'mod' => WPSEO_Sitemaps::get_last_modified_gmt( $post_type ),
229 'pri' => 1,
230 'chf' => 'daily',
231 )
232 );
233 }
234
235 /**
236 * Adds the home and post type archives urls for all (active) languages to the sitemap
237 *
238 * @since 2.6
239 *
240 * @param string $str additional urls to sitemap post
241 * @return string
242 */
243 public function add_post_type_archive( $str ) {
244 $post_type = substr( substr( current_filter(), 14 ), 0, -8 );
245 $post_type_obj = get_post_type_object( $post_type );
246 $languages = wp_list_filter( PLL()->model->get_languages_list(), array( 'active' => false ), 'NOT' );
247
248 if ( 'post' === $post_type ) {
249 if ( ! empty( PLL()->options['hide_default'] ) ) {
250 // The home url is of course already added by WPSEO.
251 $languages = wp_list_filter( $languages, array( 'slug' => pll_default_language() ), 'NOT' );
252 }
253
254 foreach ( $languages as $lang ) {
255 $str .= $this->format_sitemap_url( pll_home_url( $lang->slug ), $post_type );
256 }
257 } elseif ( $post_type_obj->has_archive ) {
258 // Exclude cases where a post type archive is attached to a page (ex: WooCommerce).
259 $slug = ( true === $post_type_obj->has_archive ) ? $post_type_obj->rewrite['slug'] : $post_type_obj->has_archive;
260
261 if ( ! wpcom_vip_get_page_by_path( $slug ) ) {
262 // The post type archive in the current language is already added by WPSEO.
263 $languages = wp_list_filter( $languages, array( 'slug' => pll_current_language() ), 'NOT' );
264
265 foreach ( $languages as $lang ) {
266 PLL()->curlang = $lang; // Switch the language to get the correct archive link.
267 $link = get_post_type_archive_link( $post_type );
268 $str .= $this->format_sitemap_url( $link, $post_type );
269 }
270 }
271 }
272
273 return $str;
274 }
275
276 /**
277 * Filters home url
278 *
279 * @since 1.1.2
280 *
281 * @param array $arr
282 * @return array
283 */
284 public function wpseo_home_url_white_list( $arr ) {
285 return array_merge( $arr, array( array( 'file' => 'wordpress-seo' ) ) );
286 }
287
288 /**
289 * Get alternate language codes for Opengraph
290 *
291 * @since 2.7.3
292 *
293 * @return array
294 */
295 protected function get_ogp_alternate_languages() {
296 $alternates = array();
297
298 foreach ( PLL()->model->get_languages_list() as $language ) {
299 if ( PLL()->curlang->slug !== $language->slug && PLL()->links->get_translation_url( $language ) && isset( $language->facebook ) ) {
300 $alternates[] = $language->facebook;
301 }
302 }
303
304 // There is a risk that 2 languages have the same Facebook locale. So let's make sure to output each locale only once.
305 return array_unique( $alternates );
306 }
307
308 /**
309 * Adds opengraph support for translations
310 *
311 * @since 1.6
312 */
313 public function wpseo_ogp() {
314 global $wpseo_og;
315
316 // WPSEO already deals with the locale
317 if ( did_action( 'pll_init' ) && method_exists( $wpseo_og, 'og_tag' ) ) {
318 foreach ( $this->get_ogp_alternate_languages() as $lang ) {
319 $wpseo_og->og_tag( 'og:locale:alternate', $lang );
320 }
321 }
322 }
323
324 /**
325 * Adds opengraph support for translations
326 *
327 * @since 2.7.3
328 *
329 * @param array $presenters An array of objects implementing Abstract_Indexable_Presenter
330 * @return array
331 */
332 public function wpseo_frontend_presenters( $presenters ) {
333 $_presenters = array();
334
335 foreach ( $presenters as $presenter ) {
336 $_presenters[] = $presenter;
337 if ( $presenter instanceof Yoast\WP\SEO\Presenters\Open_Graph\Locale_Presenter ) {
338 foreach ( $this->get_ogp_alternate_languages() as $lang ) {
339 $_presenters[] = new PLL_WPSEO_OGP( $lang );
340 }
341 }
342 }
343 return $_presenters;
344 }
345
346 /**
347 * Fixes the canonical front page url as unlike WP, WPSEO does not add a trailing slash to the canonical front page url
348 *
349 * @since 1.7.10
350 *
351 * @param string $url
352 * @return $url
353 */
354 public function wpseo_canonical( $url ) {
355 return is_front_page( $url ) && get_option( 'permalink_structure' ) ? trailingslashit( $url ) : $url;
356 }
357
358 /**
359 * Fixes the links and strings stored in the indexable table since Yoast SEO 14.0
360 *
361 * @since 2.8.2
362 *
363 * @param object $presentation The indexable presentation.
364 * @return object
365 */
366 public function frontend_presentation( $presentation ) {
367 switch ( $presentation->model->object_type ) {
368 case 'home-page':
369 $presentation->model->permalink = pll_home_url();
370 $presentation->model->title = WPSEO_Options::get( 'title-home-wpseo' );
371 $presentation->model->description = WPSEO_Options::get( 'metadesc-home-wpseo' );
372 $presentation->model->open_graph_title = WPSEO_Options::get( 'og_frontpage_title' );
373 $presentation->model->open_graph_description = WPSEO_Options::get( 'og_frontpage_desc' );
374 break;
375
376 case 'post-type-archive':
377 if ( pll_is_translated_post_type( $presentation->model->object_sub_type ) ) {
378 $presentation->model->permalink = get_post_type_archive_link( $presentation->model->object_sub_type );
379 $presentation->model->title = WPSEO_Options::get( 'title-ptarchive-' . $presentation->model->object_sub_type );
380 $presentation->model->description = WPSEO_Options::get( 'metadesc-ptarchive-' . $presentation->model->object_sub_type );
381 }
382 break;
383
384 case 'user':
385 $presentation->model->permalink = get_author_posts_url( $presentation->model->object_id );
386 break;
387
388 case 'system-page':
389 switch ( $presentation->model->object_sub_type ) {
390 case '404':
391 $presentation->model->title = WPSEO_Options::get( 'title-404-wpseo' );
392 break;
393 case 'search-result':
394 $presentation->model->title = WPSEO_Options::get( 'title-search-wpseo' );
395 break;
396 }
397 break;
398 }
399
400 return $presentation;
401 }
402
403 /**
404 * Fixes the breadcrumb links and strings stored in the indexable table since Yoast SEO 14.0
405 *
406 * @since 2.8.3
407 *
408 * @param array $indexables An array of Indexable objects.
409 * @return object
410 */
411 public function breadcrumb_indexables( $indexables ) {
412 foreach ( $indexables as &$indexable ) {
413 switch ( $indexable->object_type ) {
414 case 'home-page':
415 $indexable->permalink = pll_home_url();
416 $indexable->breadcrumb_title = pll__( WPSEO_Options::get( 'breadcrumbs-home' ) );
417 break;
418
419 case 'post-type-archive':
420 if ( pll_is_translated_post_type( $indexable->object_sub_type ) ) {
421 $indexable->permalink = get_post_type_archive_link( $indexable->object_sub_type );
422 $breadcrumb_title = WPSEO_Options::get( 'bctitle-ptarchive-' . $indexable->object_sub_type );
423 $breadcrumb_title = $breadcrumb_title ? $breadcrumb_title : $indexable->breadcrumb_title; // The option may be empty.
424 $indexable->breadcrumb_title = pll__( $breadcrumb_title );
425 }
426 break;
427 }
428 }
429
430 return $indexables;
431 }
432
433 /**
434 * Copies or synchronizes the metas.
435 *
436 * @since 2.3.3
437 *
438 * @param string[] $keys List of custom fields names.
439 * @param bool $sync True if it is synchronization, false if it is a copy.
440 * @return array
441 */
442 public function copy_post_metas( $keys, $sync ) {
443 if ( ! $sync ) {
444 // Text requiring translation.
445 $keys[] = '_yoast_wpseo_title';
446 $keys[] = '_yoast_wpseo_metadesc';
447 $keys[] = '_yoast_wpseo_bctitle';
448 $keys[] = '_yoast_wpseo_focuskw';
449 $keys[] = '_yoast_wpseo_opengraph-title';
450 $keys[] = '_yoast_wpseo_opengraph-description';
451 $keys[] = '_yoast_wpseo_twitter-title';
452 $keys[] = '_yoast_wpseo_twitter-description';
453
454 // Copy the image urls.
455 $keys[] = '_yoast_wpseo_opengraph-image';
456 $keys[] = '_yoast_wpseo_twitter-image';
457 }
458
459 $keys[] = '_yoast_wpseo_meta-robots-noindex';
460 $keys[] = '_yoast_wpseo_meta-robots-nofollow';
461 $keys[] = '_yoast_wpseo_meta-robots-adv';
462
463 $taxonomies = get_taxonomies(
464 array(
465 'hierarchical' => true,
466 'public' => true,
467 )
468 );
469
470 foreach ( $taxonomies as $taxonomy ) {
471 $keys[] = '_yoast_wpseo_primary_' . $taxonomy;
472 }
473
474 return $keys;
475 }
476
477 /**
478 * Translate the primary term during the synchronization process
479 *
480 * @since 2.3.3
481 *
482 * @param int $value Meta value.
483 * @param string $key Meta key.
484 * @param string $lang Language of target.
485 * @return int
486 */
487 public function translate_post_meta( $value, $key, $lang ) {
488 if ( false !== strpos( $key, '_yoast_wpseo_primary_' ) ) {
489 $value = pll_get_term( $value, $lang );
490 }
491 return $value;
492 }
493 }
494