PluginProbe
Polylang / trunk
Polylang vtrunk
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 / src / modules / sitemaps / multilingual-sitemaps-provider.php

multilingual-sitemaps-provider.php in Polylang trunk, at src/modules/sitemaps/multilingual-sitemaps-provider.php

240 lines 6.3 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 * Decorator to add multilingual capability to sitemaps providers
8 *
9 * @since 2.8
10 */
11 class PLL_Multilingual_Sitemaps_Provider extends WP_Sitemaps_Provider {
12 /**
13 * Separator between name and language slug.
14 */
15 public const SEPARATOR = '---pll-sep---';
16
17 /**
18 * Pattern to match a name with language.
19 * `.*` in `(?<SUBTYPE>.*)` because users don't have sub-types. See `get_sitemap_data()`.
20 */
21 private const PATTERN = '#^(?<SUBTYPE>.*)' . self::SEPARATOR . '(?<LANG>.+)$#';
22
23 /**
24 * The decorated sitemaps provider.
25 *
26 * @since 2.8
27 *
28 * @var WP_Sitemaps_Provider
29 */
30 protected $provider;
31
32 /**
33 * The PLL_Links_Model instance.
34 *
35 * @since 2.8
36 *
37 * @var PLL_Links_Model
38 */
39 protected $links_model;
40
41 /**
42 * The PLL_Model instance.
43 *
44 * @since 2.8
45 *
46 * @var PLL_Model
47 */
48 protected $model;
49
50 /**
51 * Language used to filter queries for the sitemap index.
52 *
53 * @since 2.8
54 *
55 * @var string
56 */
57 private static $filter_lang = '';
58
59 /**
60 * Constructor.
61 *
62 * @since 2.8
63 *
64 * @param WP_Sitemaps_Provider $provider An instance of a WP_Sitemaps_Provider child class.
65 * @param PLL_Links_Model $links_model The PLL_Links_Model instance.
66 */
67 public function __construct( $provider, &$links_model ) {
68 $this->name = $provider->name;
69 $this->object_type = $provider->object_type;
70
71 $this->provider = $provider;
72 $this->links_model = &$links_model;
73 $this->model = &$links_model->model;
74 }
75
76 /**
77 * Gets a URL list for a sitemap.
78 *
79 * @since 2.8
80 *
81 * @param int $page_num Page of results.
82 * @param string $object_subtype Optional. Object subtype name. Default empty.
83 * @return array Array of URLs for a sitemap.
84 */
85 public function get_url_list( $page_num, $object_subtype = '' ) {
86 return $this->provider->get_url_list( $page_num, $object_subtype );
87 }
88
89 /**
90 * Gets the max number of pages available for the object type.
91 *
92 * @since 2.8
93 *
94 * @param string $object_subtype Optional. Object subtype. Default empty.
95 * @return int Total number of pages.
96 */
97 public function get_max_num_pages( $object_subtype = '' ) {
98 return $this->provider->get_max_num_pages( $object_subtype );
99 }
100
101 /**
102 * Filters the query arguments to add the language.
103 *
104 * @since 2.8
105 *
106 * @param array $args Sitemap provider WP_Query or WP_Term_Query arguments.
107 * @return array
108 */
109 public static function query_args( $args ) {
110 if ( ! empty( self::$filter_lang ) ) {
111 $args['lang'] = self::$filter_lang;
112 }
113 return $args;
114 }
115
116 /**
117 * Returns data for a given sitemap sub-type.
118 * Suffixes the given sub-type with a language slug, so `get_sitemap_url()` can receive it.
119 *
120 * @since 2.8
121 *
122 * @param string $object_subtype_name Object sub-type name if any.
123 * @param string $lang Optional language slug.
124 * @return array
125 */
126 protected function get_sitemap_data( $object_subtype_name, $lang = '' ) {
127 $object_subtype_name = (string) $object_subtype_name;
128
129 if ( empty( $lang ) ) {
130 return array(
131 'name' => $object_subtype_name,
132 'pages' => $this->get_max_num_pages( $object_subtype_name ),
133 );
134 }
135
136 // Allow `page---pll-sep---fr` (page is a "posts sub-type") and `---pll-sep---fr` (the "users" type doesn't have sub-types).
137 self::$filter_lang = $lang;
138
139 $return = array(
140 'name' => sprintf( "{$object_subtype_name}%s{$lang}", self::SEPARATOR ),
141 'pages' => $this->get_max_num_pages( $object_subtype_name ),
142 );
143
144 self::$filter_lang = '';
145 return $return;
146 }
147
148 /**
149 * Returns data about each sitemap type.
150 *
151 * @since 2.8
152 *
153 * @return array[] Array of sitemap types including object subtype name and number of pages.
154 */
155 public function get_sitemap_type_data() {
156 $sitemap_data = array();
157
158 add_filter( 'wp_sitemaps_posts_query_args', array( self::class, 'query_args' ) );
159 add_filter( 'wp_sitemaps_taxonomies_query_args', array( self::class, 'query_args' ) );
160
161 $object_subtypes = $this->get_object_subtypes();
162 $language_slugs = $this->model->languages->get_list( array( 'fields' => 'slug' ) );
163
164 if ( empty( $object_subtypes ) ) {
165 // No sub-types. Ex: users.
166 foreach ( $language_slugs as $language_slug ) {
167 $sitemap_data[] = $this->get_sitemap_data( '', $language_slug );
168 }
169 }
170
171 switch ( $this->provider->name ) {
172 case 'posts':
173 $func = array( $this->model, 'is_translated_post_type' );
174 break;
175 case 'taxonomies':
176 $func = array( $this->model, 'is_translated_taxonomy' );
177 break;
178 default:
179 return $sitemap_data;
180 }
181
182 foreach ( array_keys( $object_subtypes ) as $object_subtype_name ) {
183 if ( ! call_user_func( $func, $object_subtype_name ) ) {
184 // Not a translated sub-type.
185 $sitemap_data[] = $this->get_sitemap_data( $object_subtype_name );
186 continue;
187 }
188
189 foreach ( $language_slugs as $language_slug ) {
190 $sitemap_data[] = $this->get_sitemap_data( $object_subtype_name, $language_slug );
191 }
192 }
193
194 return $sitemap_data;
195 }
196
197 /**
198 * Returns the URL of a sitemap entry.
199 *
200 * @since 2.8
201 *
202 * @param string $name The name of the sitemap.
203 * @param int $page The page of the sitemap.
204 * @return string The composed URL for a sitemap entry.
205 */
206 public function get_sitemap_url( $name, $page ) {
207 // Check if a language was added in `$name`.
208 if ( preg_match( self::PATTERN, $name, $matches ) ) {
209 $lang = $this->model->get_language( $matches['LANG'] );
210
211 if ( ! empty( $lang ) ) {
212 $url = $this->provider->get_sitemap_url( $matches['SUBTYPE'], $page );
213 return $this->links_model->add_language_to_link( $url, $lang );
214 }
215 // Should not happen but we don't want our separator to stay in the final URL.
216 $name = $matches['SUBTYPE'];
217 }
218
219 // If no language is present in `$name`, we may attempt to get the current sitemap URL (e.g. in `redirect_canonical()` ).
220 if ( get_query_var( 'lang' ) ) {
221 $lang = $this->model->get_language( get_query_var( 'lang' ) );
222 $url = $this->provider->get_sitemap_url( $name, $page );
223 return $this->links_model->add_language_to_link( $url, $lang );
224 }
225
226 return $this->provider->get_sitemap_url( $name, $page );
227 }
228
229 /**
230 * Returns the list of supported object subtypes exposed by the provider.
231 *
232 * @since 2.8
233 *
234 * @return array List of object subtypes objects keyed by their name.
235 */
236 public function get_object_subtypes() {
237 return $this->provider->get_object_subtypes();
238 }
239 }
240