PluginProbe ʕ •ᴥ•ʔ
Yoast SEO – Advanced SEO with real-time guidance and built-in AI / 24.8.1
Yoast SEO – Advanced SEO with real-time guidance and built-in AI v24.8.1
27.9 27.8 27.7 27.6 27.5 trunk 18.0 18.1 18.2 18.3 18.4 18.4.1 18.5 18.5.1 18.6 18.7 18.8 18.9 19.0 19.1 19.10 19.11 19.12 19.13 19.14 19.2 19.3 19.4 19.5 19.5.1 19.6 19.6.1 19.7 19.7.1 19.7.2 19.8 19.9 20.0 20.1 20.10 20.11 20.12 20.13 20.2 20.2.1 20.3 20.4 20.5 20.6 20.7 20.8 20.9 21.0 21.1 21.2 21.3 21.4 21.5 21.6 21.7 21.8 21.8.1 21.9 21.9.1 22.0 22.1 22.2 22.3 22.4 22.5 22.6 22.7 22.8 22.9 23.0 23.1 23.2 23.3 23.4 23.5 23.6 23.7 23.8 23.9 24.0 24.1 24.2 24.3 24.4 24.5 24.6 24.7 24.8 24.8.1 24.9 25.0 25.1 25.2 25.3 25.3.1 25.4 25.5 25.6 25.7 25.8 25.9 26.0 26.1 26.1.1 26.2 26.3 26.4 26.5 26.6 26.7 26.8 26.9 27.0 27.1 27.1.1 27.2 27.3 27.4
wordpress-seo / inc / sitemaps / class-taxonomy-sitemap-provider.php
wordpress-seo / inc / sitemaps Last commit date
class-author-sitemap-provider.php 3 years ago class-post-type-sitemap-provider.php 2 years ago class-sitemap-cache-data.php 2 years ago class-sitemap-image-parser.php 2 years ago class-sitemaps-admin.php 2 years ago class-sitemaps-cache-validator.php 1 year ago class-sitemaps-cache.php 2 years ago class-sitemaps-renderer.php 1 year ago class-sitemaps-router.php 2 years ago class-sitemaps.php 1 year ago class-taxonomy-sitemap-provider.php 2 years ago interface-sitemap-cache-data.php 2 years ago interface-sitemap-provider.php 5 years ago
class-taxonomy-sitemap-provider.php
352 lines
1 <?php
2 /**
3 * WPSEO plugin file.
4 *
5 * @package WPSEO\XML_Sitemaps
6 */
7
8 /**
9 * Sitemap provider for author archives.
10 */
11 class WPSEO_Taxonomy_Sitemap_Provider implements WPSEO_Sitemap_Provider {
12
13 /**
14 * Holds image parser instance.
15 *
16 * @var WPSEO_Sitemap_Image_Parser
17 */
18 protected static $image_parser;
19
20 /**
21 * Determines whether images should be included in the XML sitemap.
22 *
23 * @var bool
24 */
25 private $include_images;
26
27 /**
28 * Set up object properties for data reuse.
29 */
30 public function __construct() {
31 /**
32 * Filter - Allows excluding images from the XML sitemap.
33 *
34 * @param bool $include True to include, false to exclude.
35 */
36 $this->include_images = apply_filters( 'wpseo_xml_sitemap_include_images', true );
37 }
38
39 /**
40 * Check if provider supports given item type.
41 *
42 * @param string $type Type string to check for.
43 *
44 * @return bool
45 */
46 public function handles_type( $type ) {
47
48 $taxonomy = get_taxonomy( $type );
49
50 if ( $taxonomy === false || ! $this->is_valid_taxonomy( $taxonomy->name ) || ! $taxonomy->public ) {
51 return false;
52 }
53
54 return true;
55 }
56
57 /**
58 * Retrieves the links for the sitemap.
59 *
60 * @param int $max_entries Entries per sitemap.
61 *
62 * @return array
63 */
64 public function get_index_links( $max_entries ) {
65
66 $taxonomies = get_taxonomies( [ 'public' => true ], 'objects' );
67
68 if ( empty( $taxonomies ) ) {
69 return [];
70 }
71
72 $taxonomy_names = array_filter( array_keys( $taxonomies ), [ $this, 'is_valid_taxonomy' ] );
73 $taxonomies = array_intersect_key( $taxonomies, array_flip( $taxonomy_names ) );
74
75 // Retrieve all the taxonomies and their terms so we can do a proper count on them.
76
77 /**
78 * Filter the setting of excluding empty terms from the XML sitemap.
79 *
80 * @param bool $exclude Defaults to true.
81 * @param array $taxonomy_names Array of names for the taxonomies being processed.
82 */
83 $hide_empty = apply_filters( 'wpseo_sitemap_exclude_empty_terms', true, $taxonomy_names );
84
85 $all_taxonomies = [];
86
87 foreach ( $taxonomy_names as $taxonomy_name ) {
88 /**
89 * Filter the setting of excluding empty terms from the XML sitemap for a specific taxonomy.
90 *
91 * @param bool $exclude Defaults to the sitewide setting.
92 * @param string $taxonomy_name The name of the taxonomy being processed.
93 */
94 $hide_empty_tax = apply_filters( 'wpseo_sitemap_exclude_empty_terms_taxonomy', $hide_empty, $taxonomy_name );
95
96 $term_args = [
97 'taxonomy' => $taxonomy_name,
98 'hide_empty' => $hide_empty_tax,
99 'fields' => 'ids',
100 ];
101 $taxonomy_terms = get_terms( $term_args );
102
103 if ( count( $taxonomy_terms ) > 0 ) {
104 $all_taxonomies[ $taxonomy_name ] = $taxonomy_terms;
105 }
106 }
107
108 $index = [];
109
110 foreach ( $taxonomies as $tax_name => $tax ) {
111
112 if ( ! isset( $all_taxonomies[ $tax_name ] ) ) { // No eligible terms found.
113 continue;
114 }
115
116 $total_count = ( isset( $all_taxonomies[ $tax_name ] ) ) ? count( $all_taxonomies[ $tax_name ] ) : 1;
117 $max_pages = 1;
118
119 if ( $total_count > $max_entries ) {
120 $max_pages = (int) ceil( $total_count / $max_entries );
121 }
122
123 $last_modified_gmt = WPSEO_Sitemaps::get_last_modified_gmt( $tax->object_type );
124
125 for ( $page_counter = 0; $page_counter < $max_pages; $page_counter++ ) {
126
127 $current_page = ( $page_counter === 0 ) ? '' : ( $page_counter + 1 );
128
129 if ( ! is_array( $tax->object_type ) || count( $tax->object_type ) === 0 ) {
130 continue;
131 }
132
133 $terms = array_splice( $all_taxonomies[ $tax_name ], 0, $max_entries );
134
135 if ( ! $terms ) {
136 continue;
137 }
138
139 $args = [
140 'post_type' => $tax->object_type,
141 'tax_query' => [
142 [
143 'taxonomy' => $tax_name,
144 'terms' => $terms,
145 ],
146 ],
147 'orderby' => 'modified',
148 'order' => 'DESC',
149 'posts_per_page' => 1,
150 ];
151 $query = new WP_Query( $args );
152
153 if ( $query->have_posts() ) {
154 $date = $query->posts[0]->post_modified_gmt;
155 }
156 else {
157 $date = $last_modified_gmt;
158 }
159
160 $index[] = [
161 'loc' => WPSEO_Sitemaps_Router::get_base_url( $tax_name . '-sitemap' . $current_page . '.xml' ),
162 'lastmod' => $date,
163 ];
164 }
165 }
166
167 return $index;
168 }
169
170 /**
171 * Get set of sitemap link data.
172 *
173 * @param string $type Sitemap type.
174 * @param int $max_entries Entries per sitemap.
175 * @param int $current_page Current page of the sitemap.
176 *
177 * @return array
178 *
179 * @throws OutOfBoundsException When an invalid page is requested.
180 */
181 public function get_sitemap_links( $type, $max_entries, $current_page ) {
182 global $wpdb;
183
184 $links = [];
185 if ( ! $this->handles_type( $type ) ) {
186 return $links;
187 }
188
189 $taxonomy = get_taxonomy( $type );
190
191 $steps = $max_entries;
192 $offset = ( $current_page > 1 ) ? ( ( $current_page - 1 ) * $max_entries ) : 0;
193
194 /** This filter is documented in inc/sitemaps/class-taxonomy-sitemap-provider.php */
195 $hide_empty = apply_filters( 'wpseo_sitemap_exclude_empty_terms', true, [ $taxonomy->name ] );
196 /** This filter is documented in inc/sitemaps/class-taxonomy-sitemap-provider.php */
197 $hide_empty_tax = apply_filters( 'wpseo_sitemap_exclude_empty_terms_taxonomy', $hide_empty, $taxonomy->name );
198 $terms = get_terms(
199 [
200 'taxonomy' => $taxonomy->name,
201 'hide_empty' => $hide_empty_tax,
202 'update_term_meta_cache' => false,
203 'offset' => $offset,
204 'number' => $steps,
205 ]
206 );
207
208 // If there are no terms fetched for this range, we are on an invalid page.
209 if ( empty( $terms ) ) {
210 throw new OutOfBoundsException( 'Invalid sitemap page requested' );
211 }
212
213 $post_statuses = array_map( 'esc_sql', WPSEO_Sitemaps::get_post_statuses() );
214
215 $replacements = array_merge(
216 [
217 'post_modified_gmt',
218 $wpdb->posts,
219 $wpdb->term_relationships,
220 'object_id',
221 'ID',
222 $wpdb->term_taxonomy,
223 'term_taxonomy_id',
224 'term_taxonomy_id',
225 'taxonomy',
226 'term_id',
227 'post_status',
228 ],
229 $post_statuses,
230 [ 'post_password' ]
231 );
232
233 /**
234 * Filter: 'wpseo_exclude_from_sitemap_by_term_ids' - Allow excluding terms by ID.
235 *
236 * @param array $terms_to_exclude The terms to exclude.
237 */
238 $terms_to_exclude = apply_filters( 'wpseo_exclude_from_sitemap_by_term_ids', [] );
239
240 foreach ( $terms as $term ) {
241
242 if ( in_array( $term->term_id, $terms_to_exclude, true ) ) {
243 continue;
244 }
245
246 $url = [];
247
248 $tax_noindex = WPSEO_Taxonomy_Meta::get_term_meta( $term, $term->taxonomy, 'noindex' );
249
250 if ( $tax_noindex === 'noindex' ) {
251 continue;
252 }
253
254 $canonical = WPSEO_Taxonomy_Meta::get_term_meta( $term, $term->taxonomy, 'canonical' );
255 $url['loc'] = get_term_link( $term, $term->taxonomy );
256
257 if ( is_string( $canonical ) && $canonical !== '' && $canonical !== $url['loc'] ) {
258 continue;
259 }
260
261 $current_replacements = $replacements;
262 array_splice( $current_replacements, 9, 0, $term->taxonomy );
263 array_splice( $current_replacements, 11, 0, $term->term_id );
264
265 //phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- We need to use a direct query here.
266 //phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching -- Reason: No relevant caches.
267 $url['mod'] = $wpdb->get_var(
268 //phpcs:disable WordPress.DB.PreparedSQLPlaceholders -- %i placeholder is still not recognized.
269 $wpdb->prepare(
270 '
271 SELECT MAX(p.%i) AS lastmod
272 FROM %i AS p
273 INNER JOIN %i AS term_rel
274 ON term_rel.%i = p.%i
275 INNER JOIN %i AS term_tax
276 ON term_tax.%i = term_rel.%i
277 AND term_tax.%i = %s
278 AND term_tax.%i = %d
279 WHERE p.%i IN (' . implode( ', ', array_fill( 0, count( $post_statuses ), '%s' ) ) . ")
280 AND p.%i = ''
281 ",
282 $current_replacements
283 )
284 );
285
286 if ( $this->include_images ) {
287 $url['images'] = $this->get_image_parser()->get_term_images( $term );
288 }
289
290 // Deprecated, kept for backwards data compat. R.
291 $url['chf'] = 'daily';
292 $url['pri'] = 1;
293
294 /** This filter is documented at inc/sitemaps/class-post-type-sitemap-provider.php */
295 $url = apply_filters( 'wpseo_sitemap_entry', $url, 'term', $term );
296
297 if ( ! empty( $url ) ) {
298 $links[] = $url;
299 }
300 }
301
302 return $links;
303 }
304
305 /**
306 * Check if taxonomy by name is valid to appear in sitemaps.
307 *
308 * @param string $taxonomy_name Taxonomy name to check.
309 *
310 * @return bool
311 */
312 public function is_valid_taxonomy( $taxonomy_name ) {
313
314 if ( WPSEO_Options::get( "noindex-tax-{$taxonomy_name}" ) === true ) {
315 return false;
316 }
317
318 if ( in_array( $taxonomy_name, [ 'link_category', 'nav_menu', 'wp_pattern_category' ], true ) ) {
319 return false;
320 }
321
322 if ( $taxonomy_name === 'post_format' && WPSEO_Options::get( 'disable-post_format', false ) ) {
323 return false;
324 }
325
326 /**
327 * Filter to exclude the taxonomy from the XML sitemap.
328 *
329 * @param bool $exclude Defaults to false.
330 * @param string $taxonomy_name Name of the taxonomy to exclude..
331 */
332 if ( apply_filters( 'wpseo_sitemap_exclude_taxonomy', false, $taxonomy_name ) ) {
333 return false;
334 }
335
336 return true;
337 }
338
339 /**
340 * Get the Image Parser.
341 *
342 * @return WPSEO_Sitemap_Image_Parser
343 */
344 protected function get_image_parser() {
345 if ( ! isset( self::$image_parser ) ) {
346 self::$image_parser = new WPSEO_Sitemap_Image_Parser();
347 }
348
349 return self::$image_parser;
350 }
351 }
352