PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-a.1
Jetpack – WP Security, Backup, Speed, & Growth v16.3-a.1
16.3-a.3 16.3-a.1 16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 13.9.2 14.0.1 All 504 releases
← All changes | modules/sitemaps/sitemap-builder.php +373 -91 13.2.416.3-a.1 View file →
@@ -6,8 +6,12 @@
6 6 * @since 4.8.0
7 7 * @author Automattic
8 8 */
9 9
10 +if ( ! defined( 'ABSPATH' ) ) {
11 + exit( 0 );
12 +}
13 +
10 14 /* Include sitemap subclasses, if not already, and include proper buffer based on phpxml's availability. */
11 15 require_once __DIR__ . '/sitemap-constants.php';
12 16 require_once __DIR__ . '/sitemap-buffer.php';
13 17
@@ -85,8 +89,24 @@
85 89 */
86 90 class Jetpack_Sitemap_Builder { // phpcs:ignore Generic.Files.OneObjectStructurePerFile.MultipleFound,Generic.Classes.OpeningBraceSameLine.ContentAfterBrace
87 91
88 92 /**
93 + * Returned by the master sitemap builders when the entries do not fit in one
94 + * buffer, so the flat listing has to give way to the nested indexes.
95 + *
96 + * @since 16.2
97 + */
98 + const MASTER_OVERFLOW = 'overflow';
99 +
100 + /**
101 + * Returned by the master sitemap builders when a sitemap file they would link
102 + * is missing, so any master built now would reach fewer URLs than the last one.
103 + *
104 + * @since 16.2
105 + */
106 + const MASTER_INCOMPLETE = 'incomplete';
107 +
108 + /**
89 109 * Librarian object for storing and retrieving sitemap data.
90 110 *
91 111 * @access private
92 112 * @since 4.8.0
@@ -159,9 +179,9 @@
159 179 $this->logger->report( '-- Updating...' );
160 180 if ( ! class_exists( 'DOMDocument' ) ) {
161 181 $this->logger->report(
162 182 __(
163 - 'Jetpack can not load necessary XML manipulation libraries. Please ask your hosting provider to refer to our server requirements at https://jetpack.com/support/server-requirements/ .',
183 + 'Jetpack cannot load necessary XML manipulation libraries. Please ask your hosting provider to refer to our server requirements at https://jetpack.com/support/server-requirements/ .',
164 184 'jetpack'
165 185 ),
166 186 true
167 187 );
@@ -167,8 +187,23 @@
167 187 );
168 188 }
169 189 }
170 190
191 + /**
192 + * Filters whether to suspend cache addition for the entire sitemap generation.
193 + *
194 + * @since 15.0
195 + *
196 + * @param bool|null $suspend_addition Whether to suspend cache addition. Defaults to null.
197 + * @return bool|null Whether to suspend cache addition.
198 + */
199 + $suspend_addition = apply_filters( 'jetpack_sitemap_suspend_cache_addition', null );
200 +
201 + // Cache the previous state in case something else changed it.
202 + $prev_suspend_addition = wp_suspend_cache_addition();
203 +
204 + wp_suspend_cache_addition( $suspend_addition );
205 +
171 206 for ( $i = 1; $i <= JP_SITEMAP_UPDATE_SIZE; $i++ ) {
172 207 if ( true === $this->build_next_sitemap_file() ) {
173 208 break; // All finished!
174 209 }
@@ -173,8 +208,11 @@
173 208 break; // All finished!
174 209 }
175 210 }
176 211
212 + // Restore previous state.
213 + wp_suspend_cache_addition( $prev_suspend_addition );
214 +
177 215 if ( $this->logger ) {
178 216 $this->logger->report( '-- ...done for now.' );
179 217 $this->logger->time();
180 218 }
@@ -374,10 +412,12 @@
374 412 */
375 413 private function build_next_sitemap_index_of_type( $index_type, $next_type, $state ) {
376 414 $sitemap_type = jp_sitemap_child_type_of( $index_type );
377 415
416 + $sitemap_type_exists = isset( $state['max'][ $sitemap_type ] ) && is_array( $state['max'][ $sitemap_type ] );
417 +
378 418 // If only 0 or 1 sitemaps were built, advance to the next type and return.
379 - if ( 1 >= $state['max'][ $sitemap_type ]['number'] ) {
419 + if ( $sitemap_type_exists && 1 >= $state['max'][ $sitemap_type ]['number'] ) {
380 420 Jetpack_Sitemap_State::check_in(
381 421 array(
382 422 'sitemap-type' => $next_type,
383 423 'last-added' => 0,
@@ -469,96 +509,304 @@
469 509
470 510 /**
471 511 * Builds the master sitemap index.
472 512 *
513 + * A sitemap index file may not list other sitemap index files, so with the
514 + * `jetpack_sitemap_flat_master_index` filter on, the master lists every
515 + * individual sitemap file directly whenever they all fit in one buffer. They
516 + * no longer fit somewhere north of a million URLs, and only then does it fall
517 + * back to linking the per-type `*-sitemap-index-N.xml` files. With the filter
518 + * off, which is still the default, it always links them.
519 + *
520 + * Either way the buffer is only stored once every sitemap this generation
521 + * cycle recorded is accounted for. If one is missing, the master the previous
522 + * cycle stored is left in place: it reaches more URLs than anything that
523 + * could be built right now.
524 + *
525 + * @link https://www.sitemaps.org/protocol.html#index
526 + *
473 527 * @param array $max Array of sitemap types with max index and datetime.
474 528 *
475 529 * @since 4.8.0
476 530 */
477 531 private function build_master_sitemap( $max ) {
478 - $page = array();
479 - $image = array();
480 - $video = array();
481 532 if ( $this->logger ) {
482 533 $this->logger->report( '-- Building Master Sitemap.' );
483 534 }
484 535
485 - $buffer = new Jetpack_Sitemap_Buffer_Master(
536 + $sitemap_types = array(
537 + JP_PAGE_SITEMAP_TYPE,
538 + JP_IMAGE_SITEMAP_TYPE,
539 + JP_VIDEO_SITEMAP_TYPE,
540 + );
541 +
542 + $buffer = null;
543 +
544 + /**
545 + * Whether the master sitemap lists each individual sitemap file directly.
546 + *
547 + * A sitemap index file may not list other sitemap index files, and Google
548 + * Search Console reports the nested layout as "Nested indexing". Listing
549 + * the files directly is what fixes that.
550 + *
551 + * Off by default so the flat layout can be rolled out a site at a time.
552 + * The default is expected to flip once it has been verified in production,
553 + * at which point this filter goes away.
554 + *
555 + * @module sitemaps
556 + *
557 + * @since 16.2
558 + *
559 + * @param bool $flat_master_index Whether to list sitemap files directly. Default false.
560 + */
561 + if ( apply_filters( 'jetpack_sitemap_flat_master_index', false ) ) {
562 + $buffer = $this->build_flat_master_buffer( $sitemap_types, $max );
563 + }
564 +
565 + if ( null === $buffer || self::MASTER_OVERFLOW === $buffer ) {
566 + /*
567 + * Either the flat layout is off, or the files did not fit in one
568 + * buffer. Nesting is invalid and Google flags it, but a complete
569 + * invalid tree beats a valid one that drops URLs, and a sitemap
570 + * index cannot be paginated to make the flat layout scale further.
571 + */
572 + $buffer = $this->build_nested_master_buffer( $sitemap_types, $max );
573 + }
574 +
575 + if ( ! is_object( $buffer ) ) {
576 + return;
577 + }
578 +
579 + $this->librarian->store_sitemap_data(
580 + 0,
581 + JP_MASTER_SITEMAP_TYPE,
582 + $buffer->contents(),
583 + ''
584 + );
585 + }
586 +
587 + /**
588 + * Create the buffer a master sitemap is assembled in.
589 + *
590 + * Extracted as a protected seam so a test can hand back a buffer small
591 + * enough to overflow without generating a million URLs.
592 + *
593 + * @access protected
594 + * @since 16.2
595 + *
596 + * @return Jetpack_Sitemap_Buffer|Jetpack_Sitemap_Buffer_XMLWriter|false The buffer, or false if one cannot be created.
597 + */
598 + protected function create_master_buffer() {
599 + return Jetpack_Sitemap_Buffer_Factory::create(
600 + 'master',
486 601 JP_SITEMAP_MAX_ITEMS,
487 602 JP_SITEMAP_MAX_BYTES
488 603 );
604 + }
489 605
490 - if ( 0 < $max[ JP_PAGE_SITEMAP_TYPE ]['number'] ) {
491 - if ( 1 === $max[ JP_PAGE_SITEMAP_TYPE ]['number'] ) {
492 - $page['filename'] = jp_sitemap_filename( JP_PAGE_SITEMAP_TYPE, 1 );
493 - $page['last_modified'] = jp_sitemap_datetime( $max[ JP_PAGE_SITEMAP_TYPE ]['lastmod'] );
494 - } else {
495 - $page['filename'] = jp_sitemap_filename(
496 - JP_PAGE_SITEMAP_INDEX_TYPE,
497 - $max[ JP_PAGE_SITEMAP_INDEX_TYPE ]['number']
498 - );
499 - $page['last_modified'] = jp_sitemap_datetime( $max[ JP_PAGE_SITEMAP_INDEX_TYPE ]['lastmod'] );
606 + /**
607 + * Build a master sitemap buffer listing every individual sitemap file.
608 + *
609 + * The buffer's item and byte limits can both stop this short, and how soon
610 + * the byte limit bites depends on how long this site's URLs are, so the
611 + * partial buffer is discarded and MASTER_OVERFLOW returned rather than
612 + * storing a master that omits sitemaps. Every type is still checked over
613 + * once the buffer overflows, so a missing file is reported as
614 + * MASTER_INCOMPLETE instead of sending the caller off to build a nested
615 + * master out of the same broken state.
616 + *
617 + * @access private
618 + * @since 16.2
619 + *
620 + * @param array $sitemap_types The sitemap types to list, in order.
621 + * @param array $max Array of sitemap types with max index and datetime.
622 + *
623 + * @return Jetpack_Sitemap_Buffer|Jetpack_Sitemap_Buffer_XMLWriter|string The buffer, or MASTER_OVERFLOW / MASTER_INCOMPLETE.
624 + */
625 + private function build_flat_master_buffer( $sitemap_types, $max ) {
626 + $buffer = $this->create_master_buffer();
627 +
628 + if ( ! $buffer ) {
629 + return self::MASTER_INCOMPLETE;
630 + }
631 +
632 + $overflowed = false;
633 +
634 + foreach ( $sitemap_types as $sitemap_type ) {
635 + $expected = $this->sitemap_count_of( $max, $sitemap_type );
636 +
637 + if ( $expected < 1 ) {
638 + continue;
500 639 }
501 640
502 - $buffer->append(
503 - array(
504 - 'sitemap' => array(
505 - 'loc' => $this->finder->construct_sitemap_url( $page['filename'] ),
506 - 'lastmod' => $page['last_modified'],
507 - ),
508 - )
509 - );
641 + $timestamps = $this->stored_sitemap_timestamps( $sitemap_type, $expected );
642 +
643 + if ( false === $timestamps ) {
644 + return self::MASTER_INCOMPLETE;
645 + }
646 +
647 + if ( $overflowed ) {
648 + continue;
649 + }
650 +
651 + for ( $number = 1; $number <= $expected; $number++ ) {
652 + $filename = jp_sitemap_filename( $sitemap_type, $number );
653 +
654 + if ( ! $this->append_sitemap_to_master( $buffer, $filename, $timestamps[ $filename ] ) ) {
655 + if ( $this->logger ) {
656 + $this->logger->report( '-- Master Sitemap is full; falling back to nested indexes.' );
657 + }
658 +
659 + $overflowed = true;
660 + break;
661 + }
662 + }
510 663 }
511 664
512 - if ( 0 < $max[ JP_IMAGE_SITEMAP_TYPE ]['number'] ) {
513 - if ( 1 === $max[ JP_IMAGE_SITEMAP_TYPE ]['number'] ) {
514 - $image['filename'] = jp_sitemap_filename( JP_IMAGE_SITEMAP_TYPE, 1 );
515 - $image['last_modified'] = jp_sitemap_datetime( $max[ JP_IMAGE_SITEMAP_TYPE ]['lastmod'] );
516 - } else {
517 - $image['filename'] = jp_sitemap_filename(
518 - JP_IMAGE_SITEMAP_INDEX_TYPE,
519 - $max[ JP_IMAGE_SITEMAP_INDEX_TYPE ]['number']
520 - );
521 - $image['last_modified'] = jp_sitemap_datetime( $max[ JP_IMAGE_SITEMAP_INDEX_TYPE ]['lastmod'] );
665 + return $overflowed ? self::MASTER_OVERFLOW : $buffer;
666 + }
667 +
668 + /**
669 + * Build a master sitemap buffer linking one file per sitemap type: the single
670 + * sitemap when there is only one, otherwise that type's newest index file.
671 + *
672 + * Only used when the individual files do not all fit. The whole index chain
673 + * has to be intact, because the newest index reaches the older ones only by
674 + * linking back through them, so every index of a type is checked and not just
675 + * the one the master names.
676 + *
677 + * @access private
678 + * @since 16.2
679 + *
680 + * @param array $sitemap_types The sitemap types to list, in order.
681 + * @param array $max Array of sitemap types with max index and datetime.
682 + *
683 + * @return Jetpack_Sitemap_Buffer|Jetpack_Sitemap_Buffer_XMLWriter|string The buffer, or MASTER_INCOMPLETE.
684 + */
685 + private function build_nested_master_buffer( $sitemap_types, $max ) {
686 + $buffer = $this->create_master_buffer();
687 +
688 + if ( ! $buffer ) {
689 + return self::MASTER_INCOMPLETE;
690 + }
691 +
692 + foreach ( $sitemap_types as $sitemap_type ) {
693 + $expected = $this->sitemap_count_of( $max, $sitemap_type );
694 +
695 + if ( $expected < 1 ) {
696 + continue;
522 697 }
523 698
524 - $buffer->append(
525 - array(
526 - 'sitemap' => array(
527 - 'loc' => $this->finder->construct_sitemap_url( $image['filename'] ),
528 - 'lastmod' => $image['last_modified'],
529 - ),
530 - )
531 - );
699 + // The files reached through the index still have to be there.
700 + $timestamps = $this->stored_sitemap_timestamps( $sitemap_type, $expected );
701 +
702 + if ( false === $timestamps ) {
703 + return self::MASTER_INCOMPLETE;
704 + }
705 +
706 + $linked_type = $sitemap_type;
707 + $linked_count = $expected;
708 +
709 + if ( 1 !== $expected ) {
710 + // Only a type with a single sitemap has no index to link.
711 + $linked_type = jp_sitemap_index_type_of( $sitemap_type );
712 + $linked_count = $this->sitemap_count_of( $max, $linked_type );
713 + $timestamps = $this->stored_sitemap_timestamps( $linked_type, $linked_count );
714 +
715 + if ( $linked_count < 1 || false === $timestamps ) {
716 + if ( $this->logger ) {
717 + $this->logger->report( "-- No usable index for $sitemap_type; keeping the previous Master Sitemap." );
718 + }
719 +
720 + return self::MASTER_INCOMPLETE;
721 + }
722 + }
723 +
724 + $linked_name = jp_sitemap_filename( $linked_type, $linked_count );
725 +
726 + if ( ! $this->append_sitemap_to_master( $buffer, $linked_name, $timestamps[ $linked_name ] ) ) {
727 + if ( $this->logger ) {
728 + $this->logger->report( "-- No room for $linked_name; keeping the previous Master Sitemap." );
729 + }
730 +
731 + return self::MASTER_INCOMPLETE;
732 + }
532 733 }
533 734
534 - if ( 0 < $max[ JP_VIDEO_SITEMAP_TYPE ]['number'] ) {
535 - if ( 1 === $max[ JP_VIDEO_SITEMAP_TYPE ]['number'] ) {
536 - $video['filename'] = jp_sitemap_filename( JP_VIDEO_SITEMAP_TYPE, 1 );
537 - $video['last_modified'] = jp_sitemap_datetime( $max[ JP_VIDEO_SITEMAP_TYPE ]['lastmod'] );
538 - } else {
539 - $video['filename'] = jp_sitemap_filename(
540 - JP_VIDEO_SITEMAP_INDEX_TYPE,
541 - $max[ JP_VIDEO_SITEMAP_INDEX_TYPE ]['number']
542 - );
543 - $video['last_modified'] = jp_sitemap_datetime( $max[ JP_VIDEO_SITEMAP_INDEX_TYPE ]['lastmod'] );
735 + return $buffer;
736 + }
737 +
738 + /**
739 + * The number of sitemap files of a type the current generation cycle recorded.
740 + *
741 + * @access private
742 + * @since 16.2
743 + *
744 + * @param array $max Array of sitemap types with max index and datetime.
745 + * @param string $type A sitemap or sitemap index type.
746 + *
747 + * @return int The count, or 0 if the type produced nothing.
748 + */
749 + private function sitemap_count_of( $max, $type ) {
750 + return isset( $max[ $type ]['number'] ) ? (int) $max[ $type ]['number'] : 0;
751 + }
752 +
753 + /**
754 + * Look up the timestamps of files 1..$count of a sitemap type, by filename.
755 + *
756 + * Filenames rather than row order, because a row rewritten by an interrupted
757 + * cleanup no longer sorts where its number says it should.
758 + *
759 + * @access private
760 + * @since 16.2
761 + *
762 + * @param string $type A sitemap or sitemap index type.
763 + * @param int $count How many files of that type to expect.
764 + *
765 + * @return array|false Map of filename to timestamp, or false if any is missing.
766 + */
767 + private function stored_sitemap_timestamps( $type, $count ) {
768 + $names = array();
769 +
770 + for ( $number = 1; $number <= $count; $number++ ) {
771 + $names[] = jp_sitemap_filename( $type, $number );
772 + }
773 +
774 + $timestamps = $this->librarian->query_sitemap_timestamps( $type, $names );
775 +
776 + foreach ( $names as $name ) {
777 + if ( ! isset( $timestamps[ $name ] ) ) {
778 + if ( $this->logger ) {
779 + $this->logger->report( "-- $name is missing; keeping the previous Master Sitemap." );
780 + }
781 +
782 + return false;
544 783 }
784 + }
545 785
546 - $buffer->append(
547 - array(
548 - 'sitemap' => array(
549 - 'loc' => $this->finder->construct_sitemap_url( $video['filename'] ),
550 - 'lastmod' => $video['last_modified'],
551 - ),
552 - )
553 - );
554 - }
786 + return $timestamps;
787 + }
555 788
556 - $this->librarian->store_sitemap_data(
557 - 0,
558 - JP_MASTER_SITEMAP_TYPE,
559 - $buffer->contents(),
560 - ''
789 + /**
790 + * Append one <sitemap> entry to a master sitemap buffer.
791 + *
792 + * @access private
793 + * @since 16.2
794 + *
795 + * @param Jetpack_Sitemap_Buffer|Jetpack_Sitemap_Buffer_XMLWriter $buffer The master sitemap buffer.
796 + * @param string $filename The sitemap filename to link.
797 + * @param string $lastmod Its timestamp, in 'YYYY-MM-DD hh:mm:ss' format.
798 + *
799 + * @return bool Whether the entry fit.
800 + */
801 + private function append_sitemap_to_master( $buffer, $filename, $lastmod ) {
802 + return true === $buffer->append(
803 + array(
804 + 'sitemap' => array(
805 + 'loc' => $this->finder->construct_sitemap_url( $filename ),
806 + 'lastmod' => jp_sitemap_datetime( $lastmod ),
807 + ),
808 + )
561 809 );
562 810 }
563 811
564 812 /**
@@ -586,13 +834,18 @@
586 834 $debug_name = jp_sitemap_filename( JP_PAGE_SITEMAP_TYPE, $number );
587 835 $this->logger->report( "-- Building $debug_name" );
588 836 }
589 837
590 - $buffer = new Jetpack_Sitemap_Buffer_Page(
838 + $buffer = Jetpack_Sitemap_Buffer_Factory::create(
839 + 'page',
591 840 JP_SITEMAP_MAX_ITEMS,
592 841 JP_SITEMAP_MAX_BYTES
593 842 );
594 843
844 + if ( ! $buffer ) {
845 + return false;
846 + }
847 +
595 848 // 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.
596 849 if ( 1 === $number && 'page' !== get_option( 'show_on_front' ) ) {
597 850 $item_array = array(
598 851 'url' => array(
@@ -639,9 +892,9 @@
639 892 }
640 893 }
641 894
642 895 // Handle other page sitemap URLs.
643 - if ( false === $any_posts_left || $last_post_id < 0 ) {
896 + if ( ! $any_posts_left || $last_post_id < 0 ) {
644 897 // Negative IDs are used to track URL indexes.
645 898 $last_post_id = min( 0, $last_post_id );
646 899 $any_posts_left = true; // Reinitialize.
647 900
@@ -685,8 +938,11 @@
685 938 $item = array( 'xml' => compact( 'url' ) );
686 939
687 940 if ( true === $buffer->append( $item['xml'] ) ) {
688 941 $last_post_id = -$index;
942 + if ( isset( $url['lastmod'] ) ) {
943 + $buffer->view_time( jp_sitemap_datetime( $url['lastmod'] ) );
944 + }
689 945 } else {
690 946 break;
691 947 }
692 948 }
@@ -708,13 +964,15 @@
708 964 *
709 965 * @param DOMDocument $doc Data tree for sitemap.
710 966 * @param string $last_modified Date of last modification.
711 967 */
712 - $tree = apply_filters( // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
713 - 'jetpack_print_sitemap',
714 - $buffer->get_document(),
715 - $buffer->last_modified()
716 - );
968 + if ( has_filter( 'jetpack_print_sitemap' ) ) {
969 + apply_filters(
970 + 'jetpack_print_sitemap',
971 + $buffer->get_document(),
972 + $buffer->last_modified()
973 + );
974 + }
717 975
718 976 // Store the buffer as the content of a sitemap row.
719 977 $this->librarian->store_sitemap_data(
720 978 $number,
@@ -759,13 +1017,18 @@
759 1017 $debug_name = jp_sitemap_filename( JP_IMAGE_SITEMAP_TYPE, $number );
760 1018 $this->logger->report( "-- Building $debug_name" );
761 1019 }
762 1020
763 - $buffer = new Jetpack_Sitemap_Buffer_Image(
1021 + $buffer = Jetpack_Sitemap_Buffer_Factory::create(
1022 + 'image',
764 1023 JP_SITEMAP_MAX_ITEMS,
765 1024 JP_SITEMAP_MAX_BYTES
766 1025 );
767 1026
1027 + if ( ! $buffer ) {
1028 + return false;
1029 + }
1030 +
768 1031 // Add as many items to the buffer as possible.
769 1032 while ( false === $buffer->is_full() ) {
770 1033 $posts = $this->librarian->query_images_after_id(
771 1034 $last_post_id,
@@ -838,13 +1101,18 @@
838 1101 $debug_name = jp_sitemap_filename( JP_VIDEO_SITEMAP_TYPE, $number );
839 1102 $this->logger->report( "-- Building $debug_name" );
840 1103 }
841 1104
842 - $buffer = new Jetpack_Sitemap_Buffer_Video(
1105 + $buffer = Jetpack_Sitemap_Buffer_Factory::create(
1106 + 'video',
843 1107 JP_SITEMAP_MAX_ITEMS,
844 1108 JP_SITEMAP_MAX_BYTES
845 1109 );
846 1110
1111 + if ( ! $buffer ) {
1112 + return false;
1113 + }
1114 +
847 1115 // Add as many items to the buffer as possible.
848 1116 while ( false === $buffer->is_full() ) {
849 1117 $posts = $this->librarian->query_videos_after_id(
850 1118 $last_post_id,
@@ -925,13 +1193,17 @@
925 1193 $index_debug_name = jp_sitemap_filename( $index_type, $number );
926 1194 $this->logger->report( "-- Building $index_debug_name" );
927 1195 }
928 1196
929 - $buffer = new Jetpack_Sitemap_Buffer_Master(
1197 + $buffer = Jetpack_Sitemap_Buffer_Factory::create(
1198 + 'master',
930 1199 JP_SITEMAP_MAX_ITEMS,
931 1200 JP_SITEMAP_MAX_BYTES,
932 1201 $datetime
933 1202 );
1203 + if ( ! $buffer ) {
1204 + return false;
1205 + }
934 1206
935 1207 // Add pointer to the previous sitemap index (unless we're at the first one).
936 1208 if ( 1 !== $number ) {
937 1209 $i = $number - 1;
@@ -1055,8 +1327,18 @@
1055 1327 *
1056 1328 * @return string The news sitemap xml.
1057 1329 */
1058 1330 public function news_sitemap_xml() {
1331 + $buffer = Jetpack_Sitemap_Buffer_Factory::create(
1332 + 'news',
1333 + JP_SITEMAP_MAX_ITEMS,
1334 + JP_SITEMAP_MAX_BYTES
1335 + );
1336 +
1337 + if ( ! $buffer ) {
1338 + return '';
1339 + }
1340 +
1059 1341 $the_stored_news_sitemap = get_transient( 'jetpack_news_sitemap_xml' );
1060 1342
1061 1343 if ( false === $the_stored_news_sitemap ) {
1062 1344
@@ -1077,20 +1359,18 @@
1077 1359 'jetpack_sitemap_news_sitemap_count',
1078 1360 JP_NEWS_SITEMAP_MAX_ITEMS
1079 1361 );
1080 1362
1081 - $buffer = new Jetpack_Sitemap_Buffer_News(
1082 - min( $item_limit, JP_NEWS_SITEMAP_MAX_ITEMS ),
1083 - JP_SITEMAP_MAX_BYTES
1084 - );
1363 + $posts = $this->librarian->query_most_recent_posts( $item_limit );
1364 + if ( empty( $posts ) ) {
1365 + $buffer->append( array( 'url' => array( 'loc' => home_url( '/' ) ) ) );
1366 + } else {
1367 + foreach ( $posts as $post ) {
1368 + $current_item = $this->post_to_news_sitemap_item( $post );
1085 1369
1086 - $posts = $this->librarian->query_most_recent_posts( JP_NEWS_SITEMAP_MAX_ITEMS );
1087 -
1088 - foreach ( $posts as $post ) {
1089 - $current_item = $this->post_to_news_sitemap_item( $post );
1090 -
1091 - if ( false === $buffer->append( $current_item['xml'] ) ) {
1092 - break;
1370 + if ( $current_item['xml'] !== null && false === $buffer->append( $current_item['xml'] ) ) {
1371 + break;
1372 + }
1093 1373 }
1094 1374 }
1095 1375
1096 1376 if ( $this->logger ) {
@@ -1115,9 +1395,9 @@
1115 1395 * @link https://www.sitemaps.org/protocol.html#urldef
1116 1396 * @access private
1117 1397 * @since 4.8.0
1118 1398 *
1119 - * @param WP_Post $post The post to be processed.
1399 + * @param object $post The post to be processed. Similar to WP_Post, but without post_content and post_content_filtered.
1120 1400 *
1121 1401 * @return array
1122 1402 * @type array $xml An XML fragment representing the post URL.
1123 1403 * @type string $last_modified Date post was last modified.
@@ -1132,8 +1412,9 @@
1132 1412 * @since 3.9.0
1133 1413 *
1134 1414 * @param bool $skip Current boolean. False by default, so no post is skipped.
1135 1415 * @param object $post Current post in the form of a $wpdb result object. Not WP_Post.
1416 + * Doesn't have all the properties of a WP_Post.
1136 1417 */
1137 1418 if ( true === apply_filters( 'jetpack_sitemap_skip_post', false, $post ) ) {
1138 1419 return array(
1139 1420 'xml' => null,
@@ -1386,9 +1667,9 @@
1386 1667 *
1387 1668 * @access private
1388 1669 * @since 4.8.0
1389 1670 *
1390 - * @param WP_Post $post The post to be processed.
1671 + * @param object $post The post to be processed. Similar to WP_Post, but without post_content and post_content_filtered.
1391 1672 *
1392 1673 * @return string An XML fragment representing the post URL.
1393 1674 */
1394 1675 private function post_to_news_sitemap_item( $post ) {
@@ -1402,10 +1683,11 @@
1402 1683 * @module sitemaps
1403 1684 *
1404 1685 * @since 3.9.0
1405 1686 *
1406 - * @param bool $skip Current boolean. False by default, so no post is skipped.
1407 - * @param WP_POST $post Current post object.
1687 + * @param bool $skip Current boolean. False by default, so no post is skipped.
1688 + * @param object $post Current post in the form of a $wpdb result object. Not WP_Post.
1689 + * Doesn't have all the properties of a WP_Post.
1408 1690 */
1409 1691 if ( apply_filters( 'jetpack_sitemap_news_skip_post', false, $post ) ) {
1410 1692 return array(
1411 1693 'xml' => null,