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.
314 lines
9.0 KiB
PHP
314 lines
9.0 KiB
PHP
<?php
|
|
/**
|
|
* The HTML sitemap generator for posts.
|
|
*
|
|
* @since 1.0.104
|
|
* @package RankMath
|
|
* @subpackage RankMath\Sitemap
|
|
* @author Rank Math <support@rankmath.com>
|
|
*/
|
|
|
|
namespace RankMath\Sitemap\Html;
|
|
|
|
use RankMath\Helper;
|
|
use RankMath\Traits\Hooker;
|
|
use RankMath\Admin\Database\Database;
|
|
use RankMath\Sitemap\Sitemap as SitemapBase;
|
|
|
|
defined( 'ABSPATH' ) || exit;
|
|
|
|
/**
|
|
* Posts class.
|
|
*/
|
|
class Posts {
|
|
|
|
use Hooker;
|
|
|
|
/**
|
|
* An array of posts that have a parent.
|
|
*
|
|
* @var array
|
|
*/
|
|
private $children = [];
|
|
|
|
/**
|
|
* Get all posts from a given post type.
|
|
*
|
|
* @param string $post_type Post type.
|
|
* @param array $post_parents An array of post parent ids.
|
|
*
|
|
* @return array
|
|
*/
|
|
private function get_posts( $post_type, $post_parents = [] ) {
|
|
global $wpdb;
|
|
$sort_map = [
|
|
'published' => [
|
|
'field' => 'post_date',
|
|
'order' => 'DESC',
|
|
],
|
|
'modified' => [
|
|
'field' => 'post_modified',
|
|
'order' => 'DESC',
|
|
],
|
|
'alphabetical' => [
|
|
'field' => 'post_title',
|
|
'order' => 'ASC',
|
|
],
|
|
'post_id' => [
|
|
'field' => 'ID',
|
|
'order' => 'DESC',
|
|
],
|
|
];
|
|
|
|
$sort_setting = Helper::get_settings( 'sitemap.html_sitemap_sort' );
|
|
$sort = ( isset( $sort_map[ $sort_setting ] ) ) ? $sort_map[ $sort_setting ] : $sort_map['published'];
|
|
|
|
/**
|
|
* Filter: 'rank_math/sitemap/html_sitemap/sort_items' - Allow changing the sort order of the HTML sitemap.
|
|
*
|
|
* @var array $sort {
|
|
* @type string $field The field to sort by.
|
|
* @type string $order The sort order.
|
|
* }
|
|
* @var string $post_type The post type name.
|
|
* @var string $order The item type.
|
|
*/
|
|
$sort = $this->do_filter( 'sitemap/html_sitemap/sort_items', $sort, 'posts', $post_type );
|
|
|
|
$statuses = [ 'publish' ];
|
|
|
|
/**
|
|
* Filter: 'rank_math/sitemap/html_sitemap_post_statuses' - Allow changing the post statuses that should be included in the sitemap.
|
|
*
|
|
* @var array $statuses Post statuses.
|
|
* @var string $post_type Post type name.
|
|
*/
|
|
$statuses = $this->do_filter( 'sitemap/html_sitemap_post_statuses', $statuses, $post_type );
|
|
$get_child = ! empty( $post_parents ) ? " WHERE post_parent !='' " : '';
|
|
$sql = "
|
|
SELECT l.ID, post_title, post_name, post_parent, post_date, post_type, l.post_modified
|
|
FROM (
|
|
SELECT DISTINCT p.ID, p.post_modified FROM {$wpdb->posts} as p
|
|
LEFT JOIN {$wpdb->postmeta} AS pm ON ( p.ID = pm.post_id AND pm.meta_key = 'rank_math_robots' )
|
|
WHERE (
|
|
( pm.meta_key = 'rank_math_robots' AND pm.meta_value NOT LIKE '%noindex%' ) OR
|
|
pm.post_id IS NULL
|
|
)
|
|
AND p.post_type IN ( '" . $post_type . "' ) AND p.post_status IN ( '" . join( "', '", esc_sql( $statuses ) ) . "' )
|
|
ORDER BY p.post_modified DESC
|
|
)
|
|
o JOIN {$wpdb->posts} l ON l.ID = o.ID " . $get_child . " ORDER BY " . $sort['field'] . " " . $sort['order']; // phpcs:ignore
|
|
return $wpdb->get_results( $wpdb->prepare( $sql ) ); // phpcs:ignore
|
|
}
|
|
|
|
/**
|
|
* Generate the HTML sitemap for a given post type.
|
|
*
|
|
* @param string $post_type Post type name.
|
|
* @param bool $show_dates Whether to show dates.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function generate_sitemap( $post_type, $show_dates ) {
|
|
$posts = $this->get_posts( $post_type );
|
|
|
|
if ( empty( $posts ) ) {
|
|
return '';
|
|
}
|
|
|
|
$output[] = '<div class="rank-math-html-sitemap__section rank-math-html-sitemap__section--post-type rank-math-html-sitemap__section--' . $post_type . '">';
|
|
$output[] = '<h2 class="rank-math-html-sitemap__title">' . esc_html( get_post_type_object( $post_type )->labels->name ) . '</h2>';
|
|
$output[] = '<ul class="rank-math-html-sitemap__list">';
|
|
$output[] = $this->generate_posts_list( $posts, $show_dates, $post_type );
|
|
$output[] = '</ul>';
|
|
$output[] = '</div>';
|
|
|
|
$output = implode( '', $output );
|
|
|
|
return $output;
|
|
}
|
|
|
|
/**
|
|
* Generate the post list HTML.
|
|
*
|
|
* @param array $posts Array of posts.
|
|
* @param bool $show_dates Whether to show dates.
|
|
* @param string $post_type Post type name.
|
|
*
|
|
* @return string
|
|
*/
|
|
private function generate_posts_list( $posts, $show_dates, $post_type ) {
|
|
if ( empty( $posts ) ) {
|
|
return '';
|
|
}
|
|
|
|
if ( is_post_type_hierarchical( $post_type ) ) {
|
|
$post_ids = [];
|
|
$post_list = [];
|
|
array_map(
|
|
function ( $post ) use ( &$post_ids, &$post_list ) {
|
|
$post_ids[] = $post->ID;
|
|
$post_list[ $post->ID ] = $post;
|
|
},
|
|
$posts
|
|
);
|
|
|
|
$children = $this->get_posts( $post_type, $post_ids );
|
|
foreach ( $children as $child ) {
|
|
// Confirm if child has a parent available, the parent might not be index-able and re-add the child to $posts!
|
|
$parent = array_filter(
|
|
$post_list,
|
|
function ( $post ) use ( $child ) {
|
|
return $child->post_parent === $post->ID;
|
|
}
|
|
);
|
|
|
|
if ( empty( $parent ) ) {
|
|
$child->child_has_no_parent = true;
|
|
$post_list[ $child->ID ] = $child;
|
|
continue;
|
|
}
|
|
$this->children[ $post_type ][ $child->post_parent ][ $child->ID ] = $child;
|
|
}
|
|
|
|
$post_list = $this->remove_with_parent( $post_list );
|
|
return $this->generate_posts_list_hierarchical( $post_list, $show_dates, $post_type );
|
|
}
|
|
|
|
return $this->generate_posts_list_flat( $posts, $show_dates );
|
|
}
|
|
|
|
/**
|
|
* Get the post list HTML for non-hierarchical post types.
|
|
*
|
|
* @param array $posts The posts to output.
|
|
* @param bool $show_dates Whether to show the post dates.
|
|
*
|
|
* @return string
|
|
*/
|
|
private function generate_posts_list_flat( $posts, $show_dates ) {
|
|
$output = [];
|
|
foreach ( $posts as $post ) {
|
|
if ( ! SitemapBase::is_object_indexable( absint( $post->ID ) ) ) {
|
|
continue;
|
|
}
|
|
|
|
$url = $this->do_filter( 'sitemap/entry', esc_url( $this->get_post_link( $post ) ), 'post', $post );
|
|
if ( empty( $url ) ) {
|
|
continue;
|
|
}
|
|
|
|
$output[] = '<li class="rank-math-html-sitemap__item">'
|
|
. '<a href="' . esc_url( $this->get_post_link( $post ) ) . '" class="rank-math-html-sitemap__link">'
|
|
. esc_html( $this->get_post_title( $post ) )
|
|
. '</a>'
|
|
. ( $show_dates ? ' <span class="rank-math-html-sitemap__date">(' . esc_html( mysql2date( get_option( 'date_format' ), $post->post_date ) ) . ')</span>' : '' )
|
|
. '</li>';
|
|
}
|
|
|
|
return implode( '', $output );
|
|
}
|
|
|
|
/**
|
|
* Get the post list HTML for hierarchical post types. This will output the
|
|
* posts in a nested list.
|
|
*
|
|
* @param array $posts The posts to output.
|
|
* @param bool $show_dates Whether to show the post dates.
|
|
* @param string $post_type Post type name.
|
|
* @param bool $child Whether the passed posts are children.
|
|
*
|
|
* @return string
|
|
*/
|
|
private function generate_posts_list_hierarchical( $posts, $show_dates, $post_type, $child = false ) {
|
|
$output = [];
|
|
$exclude = wp_parse_id_list( Helper::get_settings( 'sitemap.exclude_posts' ) );
|
|
|
|
foreach ( $posts as $post ) {
|
|
$check_parent_index = empty( $post->post_parent ) ? 0 : SitemapBase::is_object_indexable( $post->post_parent );
|
|
$is_indexable = SitemapBase::is_object_indexable( absint( $post->ID ) );
|
|
|
|
if ( ( ! $check_parent_index || $child ) && $is_indexable ) {
|
|
$output[] = '<li class="rank-math-html-sitemap__item">'
|
|
. '<a href="' . esc_url( get_permalink( $post->ID ) ) . '" class="rank-math-html-sitemap__link">'
|
|
. esc_html( $this->get_post_title( $post ) )
|
|
. '</a>'
|
|
. ( $show_dates ? ' <span class="rank-math-html-sitemap__date">(' . esc_html( mysql2date( get_option( 'date_format' ), $post->post_date ) ) . ')</span>' : '' );
|
|
}
|
|
|
|
if ( ! empty( $this->children[ $post_type ][ $post->ID ] ) ) {
|
|
if ( $is_indexable ) {
|
|
$output[] = '<ul class="rank-math-html-sitemap__list">';
|
|
}
|
|
|
|
$output[] = $this->generate_posts_list_hierarchical( $this->children[ $post_type ][ $post->ID ], $show_dates, $post_type, true ); // phpcs:ignore
|
|
|
|
if ( $is_indexable ) {
|
|
$output[] = '</ul>';
|
|
}
|
|
}
|
|
|
|
if ( $is_indexable ) {
|
|
$output[] = '</li>';
|
|
}
|
|
}
|
|
|
|
return implode( '', $output );
|
|
}
|
|
|
|
/**
|
|
* Get the post permalink.
|
|
*
|
|
* @param object $post The post object.
|
|
*
|
|
* @return string
|
|
*/
|
|
private function get_post_link( $post ) {
|
|
return get_permalink( $post->ID );
|
|
}
|
|
|
|
/**
|
|
* Get the post title.
|
|
*
|
|
* @param object $post The post data.
|
|
*
|
|
* @return string
|
|
*/
|
|
private function get_post_title( $post ) {
|
|
if ( Helper::get_settings( 'sitemap.html_sitemap_seo_titles' ) !== 'seo_titles' ) {
|
|
return $post->post_title;
|
|
}
|
|
|
|
// Custom SEO title.
|
|
$meta = get_post_meta( $post->ID, 'rank_math_title', true );
|
|
if ( ! empty( $meta ) ) {
|
|
return Helper::replace_vars( $meta, get_post( $post->ID ) );
|
|
}
|
|
|
|
// Default SEO title from the global settings.
|
|
$template = Helper::get_settings( "titles.pt_{$post->post_type}_title" );
|
|
if ( ! empty( $template ) ) {
|
|
return Helper::replace_vars( $template, get_post( $post->ID ) );
|
|
}
|
|
|
|
// Fallback to post title.
|
|
return $post->post_title;
|
|
}
|
|
|
|
/**
|
|
* Removes posts with a parent to avoid them being rendered twice.
|
|
*
|
|
* @param array $posts Array of post objects.
|
|
*
|
|
* @return array
|
|
*/
|
|
private function remove_with_parent( $posts ) {
|
|
return array_filter(
|
|
$posts,
|
|
function ( $post ) {
|
|
return ! $post->post_parent || isset( $post->child_has_no_parent );
|
|
}
|
|
);
|
|
}
|
|
}
|