*/
namespace RankMath\Sitemap;
use RankMath\Helper;
use RankMath\Traits\Hooker;
use MyThemeShop\Helpers\Str;
defined( 'ABSPATH' ) || exit;
/**
* Sitemap class.
*/
class Sitemap {
use Hooker;
/**
* The Constructor.
*/
public function __construct() {
if ( is_admin() ) {
new Admin;
new Cache_Watcher;
}
new Router;
$this->filter( 'robots_txt', 'add_sitemap_directive', 99 );
add_filter( 'rank_math/admin/notice/new_post_type', array( $this, 'new_post_type_notice' ) );
add_action( 'rank_math/sitemap/hit_index', array( __CLASS__, 'hit_sitemap_index' ) );
add_action( 'rank_math/sitemap/ping_search_engines', array( __CLASS__, 'ping_search_engines' ) );
if ( class_exists( 'SitePress' ) ) {
$this->filter( 'rank_math/sitemap/build_type', 'rank_math_build_sitemap_filter' );
$this->filter( 'rank_math/sitemap/xml_post_url', 'exclude_hidden_language_posts', 10, 2 );
}
}
/**
* Exclude posts under hidden language.
*
* @since 1.0.5
*
* @param string $url Post URL.
* @param object $post Object with some post information.
*
* @return string
*/
public function exclude_hidden_language_posts( $url, $post ) {
global $sitepress;
// Check that at least ID is set in post object.
if ( ! isset( $post->ID ) ) {
return $url;
}
// Get list of hidden languages.
$hidden_languages = $sitepress->get_setting( 'hidden_languages', [] );
// If there are no hidden languages return original URL.
if ( empty( $hidden_languages ) ) {
return $url;
}
// Get language information for post.
$language_info = $sitepress->post_translations()->get_element_lang_code( $post->ID );
// If language code is one of the hidden languages return empty string to skip the post.
if ( in_array( $language_info, $hidden_languages, true ) ) {
return '';
}
return $url;
}
/**
* Prevent get_permalink from translating and remove filter added by WPML to get terms in current language.
*
* @since 1.0.5
*
* @param string $type Sitemap type.
*
* @return string
*/
public function rank_math_build_sitemap_filter( $type ) {
global $sitepress, $sitepress_settings;
// Before to build the sitemap and as we are on front-end just make sure the links won't be translated. The setting should not be updated in DB.
$sitepress_settings['auto_adjust_ids'] = 0;
if ( WPML_LANGUAGE_NEGOTIATION_TYPE_DOMAIN === (int) $sitepress->get_setting( 'language_negotiation_type' ) ) {
remove_filter( 'terms_clauses', array( $sitepress, 'terms_clauses' ) );
}
remove_filter( 'category_link', array( $sitepress, 'category_link_adjust_id' ), 1 );
remove_filter( 'get_terms_args', array( $sitepress, 'get_terms_args_filter' ) );
remove_filter( 'get_term', array( $sitepress, 'get_term_adjust_id' ) );
remove_filter( 'terms_clauses', array( $sitepress, 'terms_clauses' ) );
return $type;
}
/**
* Add sitemap directive in robots.txt
*
* @param string $output Robots.txt output.
* @return string
*/
public function add_sitemap_directive( $output ) {
if ( Str::contains( 'Sitemap:', $output ) || Str::contains( 'sitemap:', $output ) ) {
return $output;
}
return $output . "\n" . 'Sitemap: ' . Router::get_base_url( 'sitemap_index.xml' );
}
/**
* Add New CPT Notice
*
* @param string $notice New CPT Notice.
* @return string
*/
public function new_post_type_notice( $notice ) {
/* translators: post names */
$notice = __( 'We detected new post type(s) (%1$s), and you would want to check the settings of Titles & Meta page and the Sitemap.', 'rank-math' );
return $notice;
}
/**
* Make a request for the sitemap index so as to cache it before the arrival of the search engines.
*/
public static function hit_sitemap_index() {
wp_remote_get( Router::get_base_url( 'sitemap_index.xml' ) );
}
/**
* Notify search engines of the updated sitemap.
*
* @param string|null $url Optional URL to make the ping for.
*/
public static function ping_search_engines( $url = null ) {
if ( ! self::can_ping() ) {
return;
}
if ( empty( $url ) ) {
$url = urlencode( Router::get_base_url( 'sitemap_index.xml' ) );
}
// Ping Google and Bing.
wp_remote_get( 'http://www.google.com/webmasters/tools/ping?sitemap=' . $url, array( 'blocking' => false ) );
if ( Router::get_base_url( 'geo-sitemap.xml' ) !== $url ) {
wp_remote_get( 'http://www.google.com/ping?sitemap=' . $url, array( 'blocking' => false ) );
wp_remote_get( 'http://www.bing.com/ping?sitemap=' . $url, array( 'blocking' => false ) );
}
}
/**
* Check if we can ping search engines.
*
* @return bool
*/
public static function can_ping() {
if ( false === Helper::get_settings( 'sitemap.ping_search_engines' ) ) {
return false;
}
// Don't ping if blog is not public.
if ( '0' === get_option( 'blog_public' ) ) {
return false;
}
return true;
}
/**
* Exclude object frmofrom sitemap.
*
* @param int $object_id Object id.
* @param string $object_type Object type. Accetps: post, term, user.
* @param boolean $include Add or Remove object.
*/
public static function exclude_object( $object_id, $object_type, $include ) {
$field_id = "exclude_{$object_type}s";
$ids = Helper::get_settings( 'sitemap.' . $field_id );
if ( empty( $ids ) ) {
$ids = $object_id;
} else {
$ids = array_filter( wp_parse_id_list( $ids ) );
// Add object.
if ( $include && ! in_array( $object_id, $ids, true ) ) {
$ids[] = $object_id;
}
// Remove object.
if ( ! $include && in_array( $object_id, $ids, true ) ) {
$ids = array_diff( $ids, array( $object_id ) );
}
$ids = implode( ',', $ids );
}
$opt = cmb2_options( 'rank-math-options-sitemap' );
$opt->update( $field_id, $ids, true );
}
/**
* Get the GMT modification date for the last modified post in the post type.
*
* @param string|array $post_types Post type or array of types.
* @param boolean $return_all Flag to return array of values.
* @return string|array|false
*/
public static function get_last_modified_gmt( $post_types, $return_all = false ) {
global $wpdb;
if ( empty( $post_types ) ) {
return false;
}
static $post_type_dates = null;
if ( ! is_array( $post_types ) ) {
$post_types = array( $post_types );
}
foreach ( $post_types as $post_type ) {
if ( ! isset( $post_type_dates[ $post_type ] ) ) { // If we hadn't seen post type before. R.
$post_type_dates = null;
break;
}
}
if ( is_null( $post_type_dates ) ) {
$post_type_dates = [];
$post_type_names = get_post_types( array( 'public' => true ) );
if ( ! empty( $post_type_names ) ) {
$sql = "
SELECT post_type, MAX(post_modified_gmt) AS date
FROM $wpdb->posts
WHERE post_status IN ('publish','inherit')
AND post_type IN ('" . implode( "','", $post_type_names ) . "')
GROUP BY post_type
ORDER BY post_modified_gmt DESC";
foreach ( $wpdb->get_results( $sql ) as $obj ) { // phpcs:ignore
$post_type_dates[ $obj->post_type ] = $obj->date;
}
}
}
$dates = array_intersect_key( $post_type_dates, array_flip( $post_types ) );
if ( count( $dates ) > 0 ) {
return $return_all ? $dates : max( $dates );
}
return false;
}
/**
* If cache is enabled.
*
* @return boolean
*/
public static function is_cache_enabled() {
static $xml_sitemap_caching;
if ( isset( $xml_sitemap_caching ) ) {
return $xml_sitemap_caching;
}
/**
* Filter if XML sitemap transient cache is enabled.
*
* @param boolean $unsigned Enable cache or not, defaults to true
*/
$xml_sitemap_caching = apply_filters( 'rank_math/sitemap/enable_caching', true );
return $xml_sitemap_caching;
}
}