private function build_master_sitemap( $max ) {
$this->logger->report( '-- Building Master Sitemap.' );
$buffer = Jetpack_Sitemap_Buffer_Factory::create(
if ( isset( $max[ JP_PAGE_SITEMAP_TYPE ] ) && 0 < $max[ JP_PAGE_SITEMAP_TYPE ]['number'] ) {
if ( 1 === $max[ JP_PAGE_SITEMAP_TYPE ]['number'] ) {
$page['filename'] = jp_sitemap_filename( JP_PAGE_SITEMAP_TYPE, 1 );
$page['last_modified'] = jp_sitemap_datetime( $max[ JP_PAGE_SITEMAP_TYPE ]['lastmod'] );
$page['filename'] = jp_sitemap_filename(
JP_PAGE_SITEMAP_INDEX_TYPE,
$max[ JP_PAGE_SITEMAP_INDEX_TYPE ]['number']
$page['last_modified'] = jp_sitemap_datetime( $max[ JP_PAGE_SITEMAP_INDEX_TYPE ]['lastmod'] );
'loc' => $this->finder->construct_sitemap_url( $page['filename'] ),
'lastmod' => $page['last_modified'],
if ( isset( $max[ JP_IMAGE_SITEMAP_TYPE ] ) && 0 < $max[ JP_IMAGE_SITEMAP_TYPE ]['number'] ) {
if ( 1 === $max[ JP_IMAGE_SITEMAP_TYPE ]['number'] ) {
$image['filename'] = jp_sitemap_filename( JP_IMAGE_SITEMAP_TYPE, 1 );
$image['last_modified'] = jp_sitemap_datetime( $max[ JP_IMAGE_SITEMAP_TYPE ]['lastmod'] );
$image['filename'] = jp_sitemap_filename(
JP_IMAGE_SITEMAP_INDEX_TYPE,
$max[ JP_IMAGE_SITEMAP_INDEX_TYPE ]['number']
$image['last_modified'] = jp_sitemap_datetime( $max[ JP_IMAGE_SITEMAP_INDEX_TYPE ]['lastmod'] );
'loc' => $this->finder->construct_sitemap_url( $image['filename'] ),
'lastmod' => $image['last_modified'],
if ( isset( $max[ JP_VIDEO_SITEMAP_TYPE ] ) && 0 < $max[ JP_VIDEO_SITEMAP_TYPE ]['number'] ) {
if ( 1 === $max[ JP_VIDEO_SITEMAP_TYPE ]['number'] ) {
$video['filename'] = jp_sitemap_filename( JP_VIDEO_SITEMAP_TYPE, 1 );
$video['last_modified'] = jp_sitemap_datetime( $max[ JP_VIDEO_SITEMAP_TYPE ]['lastmod'] );
$video['filename'] = jp_sitemap_filename(
JP_VIDEO_SITEMAP_INDEX_TYPE,
$max[ JP_VIDEO_SITEMAP_INDEX_TYPE ]['number']
$video['last_modified'] = jp_sitemap_datetime( $max[ JP_VIDEO_SITEMAP_INDEX_TYPE ]['lastmod'] );
'loc' => $this->finder->construct_sitemap_url( $video['filename'] ),
'lastmod' => $video['last_modified'],
$this->librarian->store_sitemap_data(
* Build and store a single page sitemap. Returns false if no sitemap is built.
* Side effect: Create/update a sitemap row.
* @param int $number The number of the current sitemap.
* @param int $from_id The greatest lower bound of the IDs of the posts to be included.
* @return bool|array @args {
* @type int $last_id The ID of the last item to be successfully added to the buffer.
* @type bool $any_left 'true' if there are items which haven't been saved to a sitemap, 'false' otherwise.
* @type string $last_modified The most recent timestamp to appear on the sitemap.
public function build_one_page_sitemap( $number, $from_id ) {
$last_post_id = $from_id;
$debug_name = jp_sitemap_filename( JP_PAGE_SITEMAP_TYPE, $number );
$this->logger->report( "-- Building $debug_name" );
$buffer = Jetpack_Sitemap_Buffer_Factory::create(
// Add entry for the main page (only if we're at the first one) and it isn't already going to be included as a page.
if ( 1 === $number && 'page' !== get_option( 'show_on_front' ) ) {
'loc' => home_url( '/' ),
* Filter associative array with data to build <url> node
* and its descendants for site home.
* @param array $blog_home Data to build parent and children nodes for site home.
$item_array = apply_filters( 'jetpack_sitemap_url_home', $item_array );
$buffer->append( $item_array );
// Add as many items to the buffer as possible.
while ( $last_post_id >= 0 && false === $buffer->is_full() ) {
$posts = $this->librarian->query_posts_after_id(
if ( null == $posts ) { // phpcs:ignore Universal.Operators.StrictComparisons.LooseEqual -- WPCS: loose comparison ok.
foreach ( $posts as $post ) {
$current_item = $this->post_to_sitemap_item( $post );
if ( true === $buffer->append( $current_item['xml'] ) ) {
$last_post_id = $post->ID;
$buffer->view_time( $current_item['last_modified'] );
// Handle other page sitemap URLs.
if ( false === $any_posts_left || $last_post_id < 0 ) {
// Negative IDs are used to track URL indexes.
$last_post_id = min( 0, $last_post_id );
$any_posts_left = true; // Reinitialize.
* Filter other page sitemap URLs.
* @param array $urls An array of other URLs.
$other_urls = apply_filters( 'jetpack_page_sitemap_other_urls', array() );
if ( $other_urls ) { // Start with index [1].
$other_urls = array_values( $other_urls );
array_unshift( $other_urls, $other_urls[0] );
// Add as many items to the buffer as possible.
while ( false === $buffer->is_full() ) {
$last_post_id_index = abs( $last_post_id );
$start_from_post_id_index = $last_post_id_index ? $last_post_id_index + 1 : 0;
$start_from_post_id_index,
foreach ( $urls as $index => $url ) {
if ( ! is_array( $url ) ) {
$url = array( 'loc' => $url );
$item = array( 'xml' => compact( 'url' ) );
if ( true === $buffer->append( $item['xml'] ) ) {
if ( isset( $url['lastmod'] ) ) {
$buffer->view_time( jp_sitemap_datetime( $url['lastmod'] ) );
// If no items were added, return false.
if ( true === $buffer->is_empty() ) {
* Filter sitemap before rendering it as XML.
* @since 5.3.0 returns an element of DOMDocument type instead of SimpleXMLElement
* @param DOMDocument $doc Data tree for sitemap.
* @param string $last_modified Date of last modification.
if ( has_filter( 'jetpack_print_sitemap' ) ) {
// Store the buffer as the content of a sitemap row.
$this->librarian->store_sitemap_data(
* Now report back with the ID of the last post ID to be
* successfully added and whether there are any posts left.
'last_id' => $last_post_id,
'any_left' => $any_posts_left,
'last_modified' => $buffer->last_modified(),
* Build and store a single image sitemap. Returns false if no sitemap is built.
* Side effect: Create/update an image sitemap row.
* @param int $number The number of the current sitemap.
* @param int $from_id The greatest lower bound of the IDs of the posts to be included.
* @return bool|array @args {
* @type int $last_id The ID of the last item to be successfully added to the buffer.
* @type bool $any_left 'true' if there are items which haven't been saved to a sitemap, 'false' otherwise.
* @type string $last_modified The most recent timestamp to appear on the sitemap.
public function build_one_image_sitemap( $number, $from_id ) {
$last_post_id = $from_id;
$debug_name = jp_sitemap_filename( JP_IMAGE_SITEMAP_TYPE, $number );
$this->logger->report( "-- Building $debug_name" );
$buffer = Jetpack_Sitemap_Buffer_Factory::create(
// Add as many items to the buffer as possible.
while ( false === $buffer->is_full() ) {
$posts = $this->librarian->query_images_after_id(
if ( null == $posts ) { // phpcs:ignore Universal.Operators.StrictComparisons.LooseEqual -- WPCS: loose comparison ok.
foreach ( $posts as $post ) {
$current_item = $this->image_post_to_sitemap_item( $post );
if ( true === $buffer->append( $current_item['xml'] ) ) {
$last_post_id = $post->ID;
$buffer->view_time( $current_item['last_modified'] );
// If no items were added, return false.
if ( true === $buffer->is_empty() ) {
// Store the buffer as the content of a jp_sitemap post.
$this->librarian->store_sitemap_data(
* Now report back with the ID of the last post to be
* successfully added and whether there are any posts left.
'last_id' => $last_post_id,
'any_left' => $any_posts_left,
'last_modified' => $buffer->last_modified(),
* Build and store a single video sitemap. Returns false if no sitemap is built.
* Side effect: Create/update an video sitemap row.
* @param int $number The number of the current sitemap.
* @param int $from_id The greatest lower bound of the IDs of the posts to be included.
* @return bool|array @args {
* @type int $last_id The ID of the last item to be successfully added to the buffer.
* @type bool $any_left 'true' if there are items which haven't been saved to a sitemap, 'false' otherwise.
* @type string $last_modified The most recent timestamp to appear on the sitemap.
public function build_one_video_sitemap( $number, $from_id ) {
$last_post_id = $from_id;
$debug_name = jp_sitemap_filename( JP_VIDEO_SITEMAP_TYPE, $number );
$this->logger->report( "-- Building $debug_name" );
$buffer = Jetpack_Sitemap_Buffer_Factory::create(
// Add as many items to the buffer as possible.
while ( false === $buffer->is_full() ) {
$posts = $this->librarian->query_videos_after_id(
if ( null == $posts ) { // phpcs:ignore Universal.Operators.StrictComparisons.LooseEqual -- WPCS: loose comparison ok.
foreach ( $posts as $post ) {
$current_item = $this->video_post_to_sitemap_item( $post );
if ( true === $buffer->append( $current_item['xml'] ) ) {
$last_post_id = $post->ID;
$buffer->view_time( $current_item['last_modified'] );
// If no items were added, return false.
if ( true === $buffer->is_empty() ) {
if ( false === $buffer->is_empty() ) {
$this->librarian->store_sitemap_data(
* Now report back with the ID of the last post to be
* successfully added and whether there are any posts left.
'last_id' => $last_post_id,
'any_left' => $any_posts_left,
'last_modified' => $buffer->last_modified(),
* Build and store a single page sitemap index. Return false if no index is built.
* Side effect: Create/update a sitemap index row.
* @param int $number The number of the current sitemap index.
* @param int $from_id The greatest lower bound of the IDs of the sitemaps to be included.
* @param string $datetime Datetime of previous sitemap in 'YYYY-MM-DD hh:mm:ss' format.
* @param string $index_type Sitemap index type.
* @return bool|array @args {
* @type int $last_id The ID of the last item to be successfully added to the buffer.
* @type bool $any_left 'true' if there are items which haven't been saved to a sitemap, 'false' otherwise.
* @type string $last_modified The most recent timestamp to appear on the sitemap.
private function build_one_sitemap_index( $number, $from_id, $datetime, $index_type ) {
$last_sitemap_id = $from_id;
$any_sitemaps_left = true;
// Check the datetime format.
$datetime = jp_sitemap_datetime( $datetime );
$sitemap_type = jp_sitemap_child_type_of( $index_type );
$index_debug_name = jp_sitemap_filename( $index_type, $number );
$this->logger->report( "-- Building $index_debug_name" );
$buffer = Jetpack_Sitemap_Buffer_Factory::create(
// Add pointer to the previous sitemap index (unless we're at the first one).
$prev_index_url = $this->finder->construct_sitemap_url(
jp_sitemap_filename( $index_type, $i )
'loc' => $prev_index_url,