You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
317 lines
8.1 KiB
PHTML
317 lines
8.1 KiB
PHTML
8 months ago
|
<?php
|
||
|
/**
|
||
|
* The sitemap provider for taxonomies.
|
||
|
*
|
||
|
* @since 0.9.0
|
||
|
* @package RankMath
|
||
|
* @subpackage RankMath\Sitemap
|
||
|
* @author Rank Math <support@rankmath.com>
|
||
|
*
|
||
|
* @copyright Copyright (C) 2008-2019, Yoast BV
|
||
|
* The following code is a derivative work of the code from the Yoast(https://github.com/Yoast/wordpress-seo/), which is licensed under GPL v3.
|
||
|
*/
|
||
|
|
||
|
namespace RankMath\Sitemap\Providers;
|
||
|
|
||
|
use RankMath\Helper;
|
||
|
use RankMath\Traits\Hooker;
|
||
|
use RankMath\Sitemap\Router;
|
||
|
use RankMath\Sitemap\Sitemap;
|
||
|
use RankMath\Sitemap\Image_Parser;
|
||
|
|
||
|
defined( 'ABSPATH' ) || exit;
|
||
|
|
||
|
/**
|
||
|
* Taxonomy provider
|
||
|
*/
|
||
|
class Taxonomy implements Provider {
|
||
|
|
||
|
use Hooker;
|
||
|
|
||
|
/**
|
||
|
* Holds image parser instance.
|
||
|
*
|
||
|
* @var Image_Parser
|
||
|
*/
|
||
|
protected static $image_parser;
|
||
|
|
||
|
/**
|
||
|
* Check if provider supports given item type.
|
||
|
*
|
||
|
* @param string $type Type string to check for.
|
||
|
* @return boolean
|
||
|
*/
|
||
|
public function handles_type( $type ) {
|
||
|
if ( is_a( $type, 'WP_Taxonomy' ) ) {
|
||
|
$type = $type->name;
|
||
|
}
|
||
|
|
||
|
if (
|
||
|
empty( $type ) ||
|
||
|
false === taxonomy_exists( $type ) ||
|
||
|
false === Helper::is_taxonomy_viewable( $type ) ||
|
||
|
false === Helper::is_taxonomy_indexable( $type ) ||
|
||
|
in_array( $type, [ 'link_category', 'nav_menu', 'post_format' ], true )
|
||
|
) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Filter decision if taxonomy is excluded from the XML sitemap.
|
||
|
*
|
||
|
* @param bool $exclude Default false.
|
||
|
* @param string $type Taxonomy name.
|
||
|
*/
|
||
|
return ! $this->do_filter( 'sitemap/exclude_taxonomy', false, $type );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get set of sitemaps index link data.
|
||
|
*
|
||
|
* @param int $max_entries Entries per sitemap.
|
||
|
* @return array
|
||
|
*/
|
||
|
public function get_index_links( $max_entries ) {
|
||
|
$taxonomies = Helper::get_accessible_taxonomies();
|
||
|
$taxonomies = array_filter( $taxonomies, [ $this, 'handles_type' ] );
|
||
|
if ( empty( $taxonomies ) ) {
|
||
|
return [];
|
||
|
}
|
||
|
|
||
|
// Retrieve all the taxonomies and their terms so we can do a proper count on them.
|
||
|
/**
|
||
|
* Filter the setting of excluding empty terms from the XML sitemap.
|
||
|
*
|
||
|
* @param boolean $exclude Defaults to true.
|
||
|
* @param array $taxonomy_names Array of names for the taxonomies being processed.
|
||
|
*/
|
||
|
$hide_empty = $this->do_filter( 'sitemap/exclude_empty_terms', true, $taxonomies );
|
||
|
|
||
|
$all_taxonomies = [];
|
||
|
foreach ( $taxonomies as $taxonomy_name => $object ) {
|
||
|
$all_taxonomies[ $taxonomy_name ] = get_terms(
|
||
|
$taxonomy_name,
|
||
|
[
|
||
|
'hide_empty' => $hide_empty,
|
||
|
'fields' => 'ids',
|
||
|
'orderby' => 'name',
|
||
|
'meta_query' => [
|
||
|
'relation' => 'OR',
|
||
|
[
|
||
|
'key' => 'rank_math_robots',
|
||
|
'value' => 'noindex',
|
||
|
'compare' => 'NOT LIKE',
|
||
|
],
|
||
|
[
|
||
|
'key' => 'rank_math_robots',
|
||
|
'compare' => 'NOT EXISTS',
|
||
|
],
|
||
|
],
|
||
|
]
|
||
|
);
|
||
|
}
|
||
|
|
||
|
$index = [];
|
||
|
foreach ( $all_taxonomies as $tax_name => $terms ) {
|
||
|
if ( is_wp_error( $terms ) ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
$max_pages = 1;
|
||
|
$total_count = empty( $terms ) ? 1 : count( $terms );
|
||
|
if ( $total_count > $max_entries ) {
|
||
|
$max_pages = (int) ceil( $total_count / $max_entries );
|
||
|
}
|
||
|
|
||
|
$tax = $taxonomies[ $tax_name ];
|
||
|
if ( ! is_array( $tax->object_type ) || count( $tax->object_type ) === 0 ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
$last_modified_gmt = Sitemap::get_last_modified_gmt( $tax->object_type );
|
||
|
for ( $page_counter = 0; $page_counter < $max_pages; $page_counter++ ) {
|
||
|
$current_page = ( $max_pages > 1 ) ? ( $page_counter + 1 ) : '';
|
||
|
$terms_page = array_splice( $terms, 0, $max_entries );
|
||
|
if ( ! $terms_page ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
$query = new \WP_Query(
|
||
|
[
|
||
|
'post_type' => $tax->object_type,
|
||
|
'tax_query' => [
|
||
|
[
|
||
|
'taxonomy' => $tax_name,
|
||
|
'terms' => $terms_page,
|
||
|
],
|
||
|
],
|
||
|
'orderby' => 'modified',
|
||
|
'order' => 'DESC',
|
||
|
'posts_per_page' => 1,
|
||
|
]
|
||
|
);
|
||
|
|
||
|
$item = $this->do_filter(
|
||
|
'sitemap/index/entry',
|
||
|
[
|
||
|
'loc' => Router::get_base_url( $tax_name . '-sitemap' . $current_page . '.xml' ),
|
||
|
'lastmod' => $query->have_posts() ? $query->posts[0]->post_modified_gmt : $last_modified_gmt,
|
||
|
],
|
||
|
'term',
|
||
|
$tax_name,
|
||
|
);
|
||
|
|
||
|
if ( ! $item ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
$index[] = $item;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return $index;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get set of sitemap link data.
|
||
|
*
|
||
|
* @param string $type Sitemap type.
|
||
|
* @param int $max_entries Entries per sitemap.
|
||
|
* @param int $current_page Current page of the sitemap.
|
||
|
* @return array
|
||
|
*/
|
||
|
public function get_sitemap_links( $type, $max_entries, $current_page ) {
|
||
|
$links = [];
|
||
|
$taxonomy = get_taxonomy( $type );
|
||
|
$terms = $this->get_terms( $taxonomy, $max_entries, $current_page );
|
||
|
Sitemap::maybe_redirect( count( $this->get_terms( $taxonomy, 0, $current_page ) ), $max_entries );
|
||
|
|
||
|
foreach ( $terms as $term ) {
|
||
|
$url = [];
|
||
|
if ( ! Sitemap::is_object_indexable( $term, 'term' ) ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
$link = $this->get_term_link( $term );
|
||
|
if ( ! $link ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
$url['loc'] = $link;
|
||
|
$url['mod'] = $this->get_lastmod( $term );
|
||
|
$url['images'] = ! is_null( $this->get_image_parser() ) ? $this->get_image_parser()->get_term_images( $term ) : [];
|
||
|
|
||
|
/** This filter is documented at inc/sitemaps/class-post-type-sitemap-provider.php */
|
||
|
$url = $this->do_filter( 'sitemap/entry', $url, 'term', $term );
|
||
|
|
||
|
if ( ! empty( $url ) ) {
|
||
|
$links[] = $url;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return $links;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the Image Parser.
|
||
|
*
|
||
|
* @return Image_Parser
|
||
|
*/
|
||
|
protected function get_image_parser() {
|
||
|
if ( class_exists( 'RankMath\Sitemap\Image_Parser' ) && ! isset( self::$image_parser ) ) {
|
||
|
self::$image_parser = new Image_Parser();
|
||
|
}
|
||
|
|
||
|
return self::$image_parser;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get terms for taxonomy.
|
||
|
*
|
||
|
* @param object $taxonomy Taxonomy name.
|
||
|
* @param int $max_entries Entries per sitemap.
|
||
|
* @param int $current_page Current page of the sitemap.
|
||
|
* @return false|array
|
||
|
*/
|
||
|
private function get_terms( $taxonomy, $max_entries, $current_page ) {
|
||
|
$offset = $current_page > 1 ? ( ( $current_page - 1 ) * $max_entries ) : 0;
|
||
|
$hide_empty = ! Helper::get_settings( 'sitemap.tax_' . $taxonomy->name . '_include_empty' );
|
||
|
|
||
|
// Getting terms.
|
||
|
$terms = get_terms(
|
||
|
[
|
||
|
'taxonomy' => $taxonomy->name,
|
||
|
'orderby' => 'term_order',
|
||
|
'hide_empty' => $hide_empty,
|
||
|
'offset' => $offset,
|
||
|
'number' => $max_entries,
|
||
|
'exclude' => wp_parse_id_list( Helper::get_settings( 'sitemap.exclude_terms' ) ),
|
||
|
|
||
|
/*
|
||
|
* Limits aren't included in queries when hierarchical is set to true (by default).
|
||
|
*
|
||
|
* @link: https://github.com/WordPress/WordPress/blob/5.3/wp-includes/class-wp-term-query.php#L558-L567
|
||
|
*/
|
||
|
'hierarchical' => false,
|
||
|
'update_term_meta_cache' => false,
|
||
|
]
|
||
|
);
|
||
|
|
||
|
if ( is_wp_error( $terms ) || empty( $terms ) ) {
|
||
|
return [];
|
||
|
}
|
||
|
|
||
|
return $terms;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get term link.
|
||
|
*
|
||
|
* @param WP_Term $term Term object.
|
||
|
* @return string
|
||
|
*/
|
||
|
private function get_term_link( $term ) {
|
||
|
$url = get_term_link( $term, $term->taxonomy );
|
||
|
$canonical = Helper::get_term_meta( 'canonical_url', $term, $term->taxonomy );
|
||
|
if ( $canonical && $canonical !== $url ) {
|
||
|
/*
|
||
|
* Let's assume that if a canonical is set for this term and it's different from
|
||
|
* the URL of this term, that page is either already in the XML sitemap OR is on
|
||
|
* an external site, either way, we shouldn't include it here.
|
||
|
*/
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return $url;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get last modified date of post by term.
|
||
|
*
|
||
|
* @param WP_Term $term Term object.
|
||
|
* @return string
|
||
|
*/
|
||
|
public function get_lastmod( $term ) {
|
||
|
global $wpdb;
|
||
|
|
||
|
return $wpdb->get_var(
|
||
|
$wpdb->prepare(
|
||
|
"
|
||
|
SELECT MAX(p.post_modified_gmt) AS lastmod
|
||
|
FROM $wpdb->posts AS p
|
||
|
INNER JOIN $wpdb->term_relationships AS term_rel
|
||
|
ON term_rel.object_id = p.ID
|
||
|
INNER JOIN $wpdb->term_taxonomy AS term_tax
|
||
|
ON term_tax.term_taxonomy_id = term_rel.term_taxonomy_id
|
||
|
AND term_tax.taxonomy = %s
|
||
|
AND term_tax.term_id = %d
|
||
|
WHERE p.post_status IN ('publish', 'inherit')
|
||
|
AND p.post_password = ''
|
||
|
",
|
||
|
$term->taxonomy,
|
||
|
$term->term_id
|
||
|
)
|
||
|
);
|
||
|
}
|
||
|
}
|