Jetpack_Sitemap_Builder

The Jetpack_Sitemap_Builder object handles the construction of all sitemap files (except the XSL files, which are handled by Jetpack_Sitemap_Stylist.) Other than the constructor, there are only two public functions: build_all_sitemaps and news_sitemap_xml.

Defined (1)

The class is defined in the following location(s).

/modules/sitemaps/sitemap-builder.php  
  1. class Jetpack_Sitemap_Builder { 
  2.  
  3. /** 
  4. * Librarian object for storing and retrieving sitemap data. 
  5. * @access private 
  6. * @since 4.8.0 
  7. * @var $librarian Jetpack_Sitemap_Librarian 
  8. */ 
  9. private $librarian; 
  10.  
  11. /** 
  12. * Logger object for reporting debug messages. 
  13. * @access private 
  14. * @since 4.8.0 
  15. * @var $logger Jetpack_Sitemap_Logger 
  16. */ 
  17. private $logger = false; 
  18.  
  19. /** 
  20. * Finder object for dealing with sitemap URIs. 
  21. * @access private 
  22. * @since 4.8.0 
  23. * @var $finder Jetpack_Sitemap_Finder 
  24. */ 
  25. private $finder; 
  26.  
  27. /** 
  28. * Construct a new Jetpack_Sitemap_Builder object. 
  29. * @access public 
  30. * @since 4.8.0 
  31. */ 
  32. public function __construct() { 
  33. $this->librarian = new Jetpack_Sitemap_Librarian(); 
  34. $this->finder = new Jetpack_Sitemap_Finder(); 
  35.  
  36. if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { 
  37. $this->logger = new Jetpack_Sitemap_Logger(); 
  38.  
  39. update_option( 
  40. 'jetpack_sitemap_post_types',  
  41. /** 
  42. * The array of post types to be included in the sitemap. 
  43. * Add your custom post type name to the array to have posts of 
  44. * that type included in the sitemap. The default array includes 
  45. * 'page' and 'post'. 
  46. * The result of this filter is cached in an option, 'jetpack_sitemap_post_types',  
  47. * so this filter only has to be applied once per generation. 
  48. * @since 4.8.0 
  49. */ 
  50. apply_filters( 
  51. 'jetpack_sitemap_post_types',  
  52. array( 'post', 'page' ) 
  53. ); 
  54.  
  55. return; 
  56.  
  57. /** 
  58. * Update the sitemap. 
  59. * All we do here is call build_next_sitemap_file a bunch of times. 
  60. * @since 4.8.0 
  61. */ 
  62. public function update_sitemap() { 
  63. if ( $this->logger ) { 
  64. $this->logger->report( '-- Updating...' ); 
  65.  
  66. for ( $i = 1; $i <= JP_SITEMAP_UPDATE_SIZE; $i++ ) { 
  67. $this->build_next_sitemap_file(); 
  68.  
  69. if ( $this->logger ) { 
  70. $this->logger->report( '-- ...done for now.' ); 
  71. $this->logger->time(); 
  72.  
  73. /** 
  74. * Generate the next sitemap file. 
  75. * Reads the most recent state of the sitemap generation phase,  
  76. * constructs the next file, and updates the state. 
  77. * @since 4.8.0 
  78. */ 
  79. private function build_next_sitemap_file() { 
  80. // Get the most recent state, and lock the state. 
  81. $state = Jetpack_Sitemap_State::check_out(); 
  82.  
  83. // Do nothing if the state was locked. 
  84. if ( false === $state ) { 
  85. return; 
  86.  
  87. // Otherwise, branch on the sitemap-type key of $state. 
  88. switch ( $state['sitemap-type'] ) { 
  89. case JP_PAGE_SITEMAP_TYPE: 
  90. $this->build_next_sitemap_of_type( 
  91. JP_PAGE_SITEMAP_TYPE,  
  92. array( $this, 'build_one_page_sitemap' ),  
  93. $state 
  94. ); 
  95. break; 
  96.  
  97. case JP_PAGE_SITEMAP_INDEX_TYPE: 
  98. $this->build_next_sitemap_index_of_type( 
  99. JP_PAGE_SITEMAP_INDEX_TYPE,  
  100. JP_IMAGE_SITEMAP_TYPE,  
  101. $state 
  102. ); 
  103. break; 
  104.  
  105. case JP_IMAGE_SITEMAP_TYPE: 
  106. $this->build_next_sitemap_of_type( 
  107. JP_IMAGE_SITEMAP_TYPE,  
  108. array( $this, 'build_one_image_sitemap' ),  
  109. $state 
  110. ); 
  111. break; 
  112.  
  113. case JP_IMAGE_SITEMAP_INDEX_TYPE: 
  114. $this->build_next_sitemap_index_of_type( 
  115. JP_IMAGE_SITEMAP_INDEX_TYPE,  
  116. JP_VIDEO_SITEMAP_TYPE,  
  117. $state 
  118. ); 
  119. break; 
  120.  
  121. case JP_VIDEO_SITEMAP_TYPE: 
  122. $this->build_next_sitemap_of_type( 
  123. JP_VIDEO_SITEMAP_TYPE,  
  124. array( $this, 'build_one_video_sitemap' ),  
  125. $state 
  126. ); 
  127. break; 
  128.  
  129. case JP_VIDEO_SITEMAP_INDEX_TYPE: 
  130. $this->build_next_sitemap_index_of_type( 
  131. JP_VIDEO_SITEMAP_INDEX_TYPE,  
  132. JP_MASTER_SITEMAP_TYPE,  
  133. $state 
  134. ); 
  135. break; 
  136.  
  137. case JP_MASTER_SITEMAP_TYPE: 
  138. $this->build_master_sitemap( $state['max'] ); 
  139.  
  140. // Reset the state and quit. 
  141. Jetpack_Sitemap_State::reset( 
  142. JP_PAGE_SITEMAP_TYPE 
  143. ); 
  144.  
  145. if ( $this->logger ) { 
  146. $this->logger->report( '-- Finished.' ); 
  147. $this->logger->time(); 
  148.  
  149. die(); 
  150.  
  151. default: 
  152. // Otherwise, reset the state. 
  153. Jetpack_Sitemap_State::reset( 
  154. JP_PAGE_SITEMAP_TYPE 
  155. ); 
  156. die(); 
  157.  
  158. // Unlock the state. 
  159. Jetpack_Sitemap_State::unlock(); 
  160.  
  161. return; 
  162.  
  163. /** 
  164. * Build the next sitemap of a given type and update the sitemap state. 
  165. * @since 4.8.0 
  166. * @param string $sitemap_type The type of the sitemap being generated. 
  167. * @param callback $build_one A callback which builds a single sitemap file. 
  168. * @param array $state A sitemap state. 
  169. */ 
  170. private function build_next_sitemap_of_type( $sitemap_type, $build_one, $state ) { 
  171. $index_type = jp_sitemap_index_type_of( $sitemap_type ); 
  172.  
  173. // Try to build a sitemap. 
  174. $result = call_user_func_array( 
  175. $build_one,  
  176. array( 
  177. $state['number'] + 1,  
  178. $state['last-added'],  
  179. ); 
  180.  
  181. if ( false === $result ) { 
  182. // If no sitemap was generated, advance to the next type. 
  183. Jetpack_Sitemap_State::check_in( array( 
  184. 'sitemap-type' => $index_type,  
  185. 'last-added' => 0,  
  186. 'number' => 0,  
  187. 'last-modified' => '1970-01-01 00:00:00',  
  188. ) ); 
  189.  
  190. if ( $this->logger ) { 
  191. $this->logger->report( "-- Cleaning Up $sitemap_type" ); 
  192.  
  193. // Clean up old files. 
  194. $this->librarian->delete_numbered_sitemap_rows_after( 
  195. $state['number'], $sitemap_type 
  196. ); 
  197.  
  198. return; 
  199.  
  200. // Otherwise, update the state. 
  201. Jetpack_Sitemap_State::check_in( array( 
  202. 'sitemap-type' => $state['sitemap-type'],  
  203. 'last-added' => $result['last_id'],  
  204. 'number' => $state['number'] + 1,  
  205. 'last-modified' => $result['last_modified'],  
  206. ) ); 
  207.  
  208. if ( true === $result['any_left'] ) { 
  209. // If there's more work to be done with this type, return. 
  210. return; 
  211.  
  212. // Otherwise, advance state to the next sitemap type. 
  213. Jetpack_Sitemap_State::check_in( array( 
  214. 'sitemap-type' => $index_type,  
  215. 'last-added' => 0,  
  216. 'number' => 0,  
  217. 'last-modified' => '1970-01-01 00:00:00',  
  218. ) ); 
  219.  
  220. if ( $this->logger ) { 
  221. $this->logger->report( "-- Cleaning Up $sitemap_type" ); 
  222.  
  223. // Clean up old files. 
  224. $this->librarian->delete_numbered_sitemap_rows_after( 
  225. $state['number'] + 1, $sitemap_type 
  226. ); 
  227.  
  228. return; 
  229.  
  230. /** 
  231. * Build the next sitemap index of a given type and update the state. 
  232. * @since 4.8.0 
  233. * @param string $index_type The type of index being generated. 
  234. * @param string $next_type The next type to generate after this one. 
  235. * @param array $state A sitemap state. 
  236. */ 
  237. private function build_next_sitemap_index_of_type( $index_type, $next_type, $state ) { 
  238. $sitemap_type = jp_sitemap_child_type_of( $index_type ); 
  239.  
  240. // If only 0 or 1 sitemaps were built, advance to the next type and return. 
  241. if ( 1 >= $state['max'][ $sitemap_type ]['number'] ) { 
  242. Jetpack_Sitemap_State::check_in( array( 
  243. 'sitemap-type' => $next_type,  
  244. 'last-added' => 0,  
  245. 'number' => 0,  
  246. 'last-modified' => '1970-01-01 00:00:00',  
  247. ) ); 
  248.  
  249. if ( $this->logger ) { 
  250. $this->logger->report( "-- Cleaning Up $index_type" ); 
  251.  
  252. // There are no indices of this type. 
  253. $this->librarian->delete_numbered_sitemap_rows_after( 
  254. 0, $index_type 
  255. ); 
  256.  
  257. return; 
  258.  
  259. // Otherwise, try to build a sitemap index. 
  260. $result = $this->build_one_sitemap_index( 
  261. $state['number'] + 1,  
  262. $state['last-added'],  
  263. $state['last-modified'],  
  264. $index_type 
  265. ); 
  266.  
  267. // If no index was built, advance to the next type and return. 
  268. if ( false === $result ) { 
  269. Jetpack_Sitemap_State::check_in( array( 
  270. 'sitemap-type' => $next_type,  
  271. 'last-added' => 0,  
  272. 'number' => 0,  
  273. 'last-modified' => '1970-01-01 00:00:00',  
  274. ) ); 
  275.  
  276. if ( $this->logger ) { 
  277. $this->logger->report( "-- Cleaning Up $index_type" ); 
  278.  
  279. // Clean up old files. 
  280. $this->librarian->delete_numbered_sitemap_rows_after( 
  281. $state['number'], $index_type 
  282. ); 
  283.  
  284. return; 
  285.  
  286. // Otherwise, check in the state. 
  287. Jetpack_Sitemap_State::check_in( array( 
  288. 'sitemap-type' => $index_type,  
  289. 'last-added' => $result['last_id'],  
  290. 'number' => $state['number'] + 1,  
  291. 'last-modified' => $result['last_modified'],  
  292. ) ); 
  293.  
  294. // If there are still sitemaps left to index, return. 
  295. if ( true === $result['any_left'] ) { 
  296. return; 
  297.  
  298. // Otherwise, advance to the next type. 
  299. Jetpack_Sitemap_State::check_in( array( 
  300. 'sitemap-type' => $next_type,  
  301. 'last-added' => 0,  
  302. 'number' => 0,  
  303. 'last-modified' => '1970-01-01 00:00:00',  
  304. ) ); 
  305.  
  306. if ( $this->logger ) { 
  307. $this->logger->report( "-- Cleaning Up $index_type" ); 
  308.  
  309. // We're done generating indices of this type. 
  310. $this->librarian->delete_numbered_sitemap_rows_after( 
  311. $state['number'] + 1, $index_type 
  312. ); 
  313.  
  314. return; 
  315.  
  316. /** 
  317. * Builds the master sitemap index. 
  318. * @param array $max Array of sitemap types with max index and datetime. 
  319. * @since 4.8.0 
  320. */ 
  321. private function build_master_sitemap( $max ) { 
  322. $sitemap_index_xsl_url = $this->finder->construct_sitemap_url( 'sitemap-index.xsl' ); 
  323. $jetpack_version = JETPACK__VERSION; 
  324.  
  325. if ( $this->logger ) { 
  326. $this->logger->report( '-- Building Master Sitemap.' ); 
  327.  
  328. $buffer = new Jetpack_Sitemap_Buffer( 
  329. JP_SITEMAP_MAX_ITEMS,  
  330. JP_SITEMAP_MAX_BYTES,  
  331. <<<HEADER 
  332. <?xml version='1.0' encoding='UTF-8'?> 
  333. <!-- generator='jetpack-{$jetpack_version}' --> 
  334. <?xml-stylesheet type='text/xsl' href='{$sitemap_index_xsl_url}'?> 
  335. <sitemapindex xmlns='http://www.sitemaps.org/schemas/sitemap/0.9'> 
  336. HEADER 
  337. ,  
  338. <<<FOOTER 
  339. </sitemapindex>\n 
  340. FOOTER 
  341. ,  
  342. /** epoch */ 
  343. '1970-01-01 00:00:00' 
  344. ); 
  345.  
  346. if ( 0 < $max[ JP_PAGE_SITEMAP_TYPE ]['number'] ) { 
  347. if ( 1 === $max[ JP_PAGE_SITEMAP_TYPE ]['number'] ) { 
  348. $page['filename'] = jp_sitemap_filename( JP_PAGE_SITEMAP_TYPE, 1 ); 
  349. $page['last_modified'] = jp_sitemap_datetime( $max[ JP_PAGE_SITEMAP_TYPE ]['lastmod'] ); 
  350. } else { 
  351. $page['filename'] = jp_sitemap_filename( 
  352. JP_PAGE_SITEMAP_INDEX_TYPE,  
  353. $max[ JP_PAGE_SITEMAP_INDEX_TYPE ]['number'] 
  354. ); 
  355. $page['last_modified'] = jp_sitemap_datetime( $max[ JP_PAGE_SITEMAP_INDEX_TYPE ]['lastmod'] ); 
  356.  
  357. $buffer->try_to_add_item( Jetpack_Sitemap_Buffer::array_to_xml_string( 
  358. array( 
  359. 'sitemap' => array( 
  360. 'loc' => $this->finder->construct_sitemap_url( $page['filename'] ),  
  361. 'lastmod' => $page['last_modified'],  
  362. ),  
  363. ) ); 
  364.  
  365. if ( 0 < $max[ JP_IMAGE_SITEMAP_TYPE ]['number'] ) { 
  366. if ( 1 === $max[ JP_IMAGE_SITEMAP_TYPE ]['number'] ) { 
  367. $image['filename'] = jp_sitemap_filename( JP_IMAGE_SITEMAP_TYPE, 1 ); 
  368. $image['last_modified'] = jp_sitemap_datetime( $max[ JP_IMAGE_SITEMAP_TYPE ]['lastmod'] ); 
  369. } else { 
  370. $image['filename'] = jp_sitemap_filename( 
  371. JP_IMAGE_SITEMAP_INDEX_TYPE,  
  372. $max[ JP_IMAGE_SITEMAP_INDEX_TYPE ]['number'] 
  373. ); 
  374. $image['last_modified'] = jp_sitemap_datetime( $max[ JP_IMAGE_SITEMAP_INDEX_TYPE ]['lastmod'] ); 
  375.  
  376. $buffer->try_to_add_item( Jetpack_Sitemap_Buffer::array_to_xml_string( 
  377. array( 
  378. 'sitemap' => array( 
  379. 'loc' => $this->finder->construct_sitemap_url( $image['filename'] ),  
  380. 'lastmod' => $image['last_modified'],  
  381. ),  
  382. ) ); 
  383.  
  384. if ( 0 < $max[ JP_VIDEO_SITEMAP_TYPE ]['number'] ) { 
  385. if ( 1 === $max[ JP_VIDEO_SITEMAP_TYPE ]['number'] ) { 
  386. $video['filename'] = jp_sitemap_filename( JP_VIDEO_SITEMAP_TYPE, 1 ); 
  387. $video['last_modified'] = $max[ JP_VIDEO_SITEMAP_TYPE ]['lastmod']; 
  388. } else { 
  389. $video['filename'] = jp_sitemap_filename( 
  390. JP_VIDEO_SITEMAP_INDEX_TYPE,  
  391. $max[ JP_VIDEO_SITEMAP_INDEX_TYPE ]['number'] 
  392. ); 
  393. $video['last_modified'] = $max[ JP_VIDEO_SITEMAP_INDEX_TYPE ]['lastmod']; 
  394.  
  395. $buffer->try_to_add_item( Jetpack_Sitemap_Buffer::array_to_xml_string( 
  396. array( 
  397. 'sitemap' => array( 
  398. 'loc' => $this->finder->construct_sitemap_url( $video['filename'] ),  
  399. 'lastmod' => $video['last_modified'],  
  400. ),  
  401. ) ); 
  402.  
  403. $this->librarian->store_sitemap_data( 
  404. 0,  
  405. JP_MASTER_SITEMAP_TYPE,  
  406. $buffer->contents(),  
  407. '' 
  408. ); 
  409.  
  410. return; 
  411.  
  412. /** 
  413. * Build and store a single page sitemap. Returns false if no sitemap is built. 
  414. * Side effect: Create/update a sitemap row. 
  415. * @access private 
  416. * @since 4.8.0 
  417. * @param int $number The number of the current sitemap. 
  418. * @param int $from_id The greatest lower bound of the IDs of the posts to be included. 
  419. * @return bool|array @args { 
  420. * @type int $last_id The ID of the last item to be successfully added to the buffer. 
  421. * @type bool $any_left 'true' if there are items which haven't been saved to a sitemap, 'false' otherwise. 
  422. * @type string $last_modified The most recent timestamp to appear on the sitemap. 
  423. * } 
  424. */ 
  425. public function build_one_page_sitemap( $number, $from_id ) { 
  426. $last_post_id = $from_id; 
  427. $any_posts_left = true; 
  428.  
  429. if ( $this->logger ) { 
  430. $debug_name = jp_sitemap_filename( JP_PAGE_SITEMAP_TYPE, $number ); 
  431. $this->logger->report( "-- Building $debug_name" ); 
  432.  
  433. $sitemap_xsl_url = $this->finder->construct_sitemap_url( 'sitemap.xsl' ); 
  434.  
  435. $jetpack_version = JETPACK__VERSION; 
  436.  
  437. $namespaces = Jetpack_Sitemap_Buffer::array_to_xml_attr_string( 
  438. /** 
  439. * Filter the attribute value pairs used for namespace and namespace URI mappings. 
  440. * @module sitemaps 
  441. * @since 3.9.0 
  442. * @param array $namespaces Associative array with namespaces and namespace URIs. 
  443. */ 
  444. apply_filters( 
  445. 'jetpack_sitemap_ns',  
  446. array( 
  447. 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',  
  448. 'xsi:schemaLocation' => 'http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd',  
  449. 'xmlns' => 'http://www.sitemaps.org/schemas/sitemap/0.9',  
  450. ); 
  451.  
  452. $buffer = new Jetpack_Sitemap_Buffer( 
  453. JP_SITEMAP_MAX_ITEMS,  
  454. JP_SITEMAP_MAX_BYTES,  
  455. <<<HEADER 
  456. <?xml version='1.0' encoding='UTF-8'?> 
  457. <!-- generator='jetpack-{$jetpack_version}' --> 
  458. <?xml-stylesheet type='text/xsl' href='{$sitemap_xsl_url}'?> 
  459. <urlset{$namespaces}>\n 
  460. HEADER 
  461. ,  
  462. <<<FOOTER 
  463. </urlset>\n 
  464. FOOTER 
  465. ,  
  466. /** epoch */ 
  467. '1970-01-01 00:00:00' 
  468. ); 
  469.  
  470. // Add entry for the main page (only if we're at the first one). 
  471. if ( 1 === $number ) { 
  472. $item_array = array( 
  473. 'url' => array( 
  474. 'loc' => home_url(),  
  475. ),  
  476. ); 
  477.  
  478. /** 
  479. * Filter associative array with data to build <url> node 
  480. * and its descendants for site home. 
  481. * @module sitemaps 
  482. * @since 3.9.0 
  483. * @param array $blog_home Data to build parent and children nodes for site home. 
  484. */ 
  485. $item_array = apply_filters( 'jetpack_sitemap_url_home', $item_array ); 
  486.  
  487. $buffer->try_to_add_item( Jetpack_Sitemap_Buffer::array_to_xml_string( $item_array ) ); 
  488.  
  489. // Add as many items to the buffer as possible. 
  490. while ( false === $buffer->is_full() ) { 
  491. $posts = $this->librarian->query_posts_after_id( 
  492. $last_post_id, JP_SITEMAP_BATCH_SIZE 
  493. ); 
  494.  
  495. if ( null == $posts ) { // WPCS: loose comparison ok. 
  496. $any_posts_left = false; 
  497. break; 
  498.  
  499. foreach ( $posts as $post ) { 
  500. $current_item = $this->post_to_sitemap_item( $post ); 
  501.  
  502. if ( true === $buffer->try_to_add_item( $current_item['xml'] ) ) { 
  503. $last_post_id = $post->ID; 
  504. $buffer->view_time( $current_item['last_modified'] ); 
  505. } else { 
  506. break; 
  507.  
  508. // If no items were added, return false. 
  509. if ( true === $buffer->is_empty() ) { 
  510. return false; 
  511.  
  512. /** 
  513. * Filter sitemap before rendering it as XML. 
  514. * @module sitemaps 
  515. * @since 3.9.0 
  516. * @param SimpleXMLElement $tree Data tree for sitemap. 
  517. * @param string $last_modified Date of last modification. 
  518. */ 
  519. $tree = apply_filters( 
  520. 'jetpack_print_sitemap',  
  521. simplexml_load_string( $buffer->contents() ),  
  522. $buffer->last_modified() 
  523. ); 
  524.  
  525. // Store the buffer as the content of a sitemap row. 
  526. $this->librarian->store_sitemap_data( 
  527. $number,  
  528. JP_PAGE_SITEMAP_TYPE,  
  529. $tree->asXML(),  
  530. $buffer->last_modified() 
  531. ); 
  532.  
  533. /** 
  534. * Now report back with the ID of the last post ID to be 
  535. * successfully added and whether there are any posts left. 
  536. */ 
  537. return array( 
  538. 'last_id' => $last_post_id,  
  539. 'any_left' => $any_posts_left,  
  540. 'last_modified' => $buffer->last_modified(),  
  541. ); 
  542.  
  543. /** 
  544. * Build and store a single image sitemap. Returns false if no sitemap is built. 
  545. * Side effect: Create/update an image sitemap row. 
  546. * @access private 
  547. * @since 4.8.0 
  548. * @param int $number The number of the current sitemap. 
  549. * @param int $from_id The greatest lower bound of the IDs of the posts to be included. 
  550. * @return bool|array @args { 
  551. * @type int $last_id The ID of the last item to be successfully added to the buffer. 
  552. * @type bool $any_left 'true' if there are items which haven't been saved to a sitemap, 'false' otherwise. 
  553. * @type string $last_modified The most recent timestamp to appear on the sitemap. 
  554. * } 
  555. */ 
  556. public function build_one_image_sitemap( $number, $from_id ) { 
  557. $last_post_id = $from_id; 
  558. $any_posts_left = true; 
  559.  
  560. if ( $this->logger ) { 
  561. $debug_name = jp_sitemap_filename( JP_IMAGE_SITEMAP_TYPE, $number ); 
  562. $this->logger->report( "-- Building $debug_name" ); 
  563.  
  564. $image_sitemap_xsl_url = $this->finder->construct_sitemap_url( 'image-sitemap.xsl' ); 
  565.  
  566. $jetpack_version = JETPACK__VERSION; 
  567.  
  568. $namespaces = Jetpack_Sitemap_Buffer::array_to_xml_attr_string( 
  569. /** 
  570. * Filter the XML namespaces included in image sitemaps. 
  571. * @module sitemaps 
  572. * @since 4.8.0 
  573. * @param array $namespaces Associative array with namespaces and namespace URIs. 
  574. */ 
  575. apply_filters( 
  576. 'jetpack_sitemap_image_ns',  
  577. array( 
  578. 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',  
  579. 'xsi:schemaLocation' => 'http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd',  
  580. 'xmlns' => 'http://www.sitemaps.org/schemas/sitemap/0.9',  
  581. 'xmlns:image' => 'http://www.google.com/schemas/sitemap-image/1.1',  
  582. ); 
  583.  
  584. $buffer = new Jetpack_Sitemap_Buffer( 
  585. JP_SITEMAP_MAX_ITEMS,  
  586. JP_SITEMAP_MAX_BYTES,  
  587. <<<HEADER 
  588. <?xml version='1.0' encoding='UTF-8'?> 
  589. <!-- generator='jetpack-{$jetpack_version}' --> 
  590. <?xml-stylesheet type='text/xsl' href='{$image_sitemap_xsl_url}'?> 
  591. <urlset{$namespaces}>\n 
  592. HEADER 
  593. ,  
  594. <<<FOOTER 
  595. </urlset>\n 
  596. FOOTER 
  597. ,  
  598. /** epoch */ 
  599. '1970-01-01 00:00:00' 
  600. ); 
  601.  
  602. // Add as many items to the buffer as possible. 
  603. while ( false === $buffer->is_full() ) { 
  604. $posts = $this->librarian->query_images_after_id( 
  605. $last_post_id, JP_SITEMAP_BATCH_SIZE 
  606. ); 
  607.  
  608. if ( null == $posts ) { // WPCS: loose comparison ok. 
  609. $any_posts_left = false; 
  610. break; 
  611.  
  612. foreach ( $posts as $post ) { 
  613. $current_item = $this->image_post_to_sitemap_item( $post ); 
  614.  
  615. if ( true === $buffer->try_to_add_item( $current_item['xml'] ) ) { 
  616. $last_post_id = $post->ID; 
  617. $buffer->view_time( $current_item['last_modified'] ); 
  618. } else { 
  619. break; 
  620.  
  621. // If no items were added, return false. 
  622. if ( true === $buffer->is_empty() ) { 
  623. return false; 
  624.  
  625. // Store the buffer as the content of a jp_sitemap post. 
  626. $this->librarian->store_sitemap_data( 
  627. $number,  
  628. JP_IMAGE_SITEMAP_TYPE,  
  629. $buffer->contents(),  
  630. $buffer->last_modified() 
  631. ); 
  632.  
  633. /** 
  634. * Now report back with the ID of the last post to be 
  635. * successfully added and whether there are any posts left. 
  636. */ 
  637. return array( 
  638. 'last_id' => $last_post_id,  
  639. 'any_left' => $any_posts_left,  
  640. 'last_modified' => $buffer->last_modified(),  
  641. ); 
  642.  
  643. /** 
  644. * Build and store a single video sitemap. Returns false if no sitemap is built. 
  645. * Side effect: Create/update an video sitemap row. 
  646. * @access private 
  647. * @since 4.8.0 
  648. * @param int $number The number of the current sitemap. 
  649. * @param int $from_id The greatest lower bound of the IDs of the posts to be included. 
  650. * @return bool|array @args { 
  651. * @type int $last_id The ID of the last item to be successfully added to the buffer. 
  652. * @type bool $any_left 'true' if there are items which haven't been saved to a sitemap, 'false' otherwise. 
  653. * @type string $last_modified The most recent timestamp to appear on the sitemap. 
  654. * } 
  655. */ 
  656. public function build_one_video_sitemap( $number, $from_id ) { 
  657. $last_post_id = $from_id; 
  658. $any_posts_left = true; 
  659.  
  660. if ( $this->logger ) { 
  661. $debug_name = jp_sitemap_filename( JP_VIDEO_SITEMAP_TYPE, $number ); 
  662. $this->logger->report( "-- Building $debug_name" ); 
  663.  
  664. $video_sitemap_xsl_url = $this->finder->construct_sitemap_url( 'video-sitemap.xsl' ); 
  665.  
  666. $jetpack_version = JETPACK__VERSION; 
  667.  
  668. $namespaces = Jetpack_Sitemap_Buffer::array_to_xml_attr_string( 
  669. /** 
  670. * Filter the XML namespaces included in video sitemaps. 
  671. * @module sitemaps 
  672. * @since 4.8.0 
  673. * @param array $namespaces Associative array with namespaces and namespace URIs. 
  674. */ 
  675. apply_filters( 
  676. 'jetpack_sitemap_video_ns',  
  677. array( 
  678. 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',  
  679. 'xsi:schemaLocation' => 'http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd',  
  680. 'xmlns' => 'http://www.sitemaps.org/schemas/sitemap/0.9',  
  681. 'xmlns:video' => 'http://www.google.com/schemas/sitemap-video/1.1',  
  682. ); 
  683.  
  684. $buffer = new Jetpack_Sitemap_Buffer( 
  685. JP_SITEMAP_MAX_ITEMS,  
  686. JP_SITEMAP_MAX_BYTES,  
  687. <<<HEADER 
  688. <?xml version='1.0' encoding='UTF-8'?> 
  689. <!-- generator='jetpack-{$jetpack_version}' --> 
  690. <?xml-stylesheet type='text/xsl' href='{$video_sitemap_xsl_url}'?> 
  691. <urlset{$namespaces}>\n 
  692. HEADER 
  693. ,  
  694. <<<FOOTER 
  695. </urlset>\n 
  696. FOOTER 
  697. ,  
  698. /** epoch */ 
  699. '1970-01-01 00:00:00' 
  700. ); 
  701.  
  702. // Add as many items to the buffer as possible. 
  703. while ( false === $buffer->is_full() ) { 
  704. $posts = $this->librarian->query_videos_after_id( 
  705. $last_post_id, JP_SITEMAP_BATCH_SIZE 
  706. ); 
  707.  
  708. if ( null == $posts ) { // WPCS: loose comparison ok. 
  709. $any_posts_left = false; 
  710. break; 
  711.  
  712. foreach ( $posts as $post ) { 
  713. $current_item = $this->video_post_to_sitemap_item( $post ); 
  714.  
  715. if ( true === $buffer->try_to_add_item( $current_item['xml'] ) ) { 
  716. $last_post_id = $post->ID; 
  717. $buffer->view_time( $current_item['last_modified'] ); 
  718. } else { 
  719. break; 
  720.  
  721. // If no items were added, return false. 
  722. if ( true === $buffer->is_empty() ) { 
  723. return false; 
  724.  
  725. if ( false === $buffer->is_empty() ) { 
  726. $this->librarian->store_sitemap_data( 
  727. $number,  
  728. JP_VIDEO_SITEMAP_TYPE,  
  729. $buffer->contents(),  
  730. $buffer->last_modified() 
  731. ); 
  732.  
  733. /** 
  734. * Now report back with the ID of the last post to be 
  735. * successfully added and whether there are any posts left. 
  736. */ 
  737. return array( 
  738. 'last_id' => $last_post_id,  
  739. 'any_left' => $any_posts_left,  
  740. 'last_modified' => $buffer->last_modified(),  
  741. ); 
  742.  
  743. /** 
  744. * Build and store a single page sitemap index. Return false if no index is built. 
  745. * Side effect: Create/update a sitemap index row. 
  746. * @access private 
  747. * @since 4.8.0 
  748. * @param int $number The number of the current sitemap index. 
  749. * @param int $from_id The greatest lower bound of the IDs of the sitemaps to be included. 
  750. * @param string $datetime Datetime of previous sitemap in 'YYYY-MM-DD hh:mm:ss' format. 
  751. * @param string $index_type Sitemap index type. 
  752. * @return bool|array @args { 
  753. * @type int $last_id The ID of the last item to be successfully added to the buffer. 
  754. * @type bool $any_left 'true' if there are items which haven't been saved to a sitemap, 'false' otherwise. 
  755. * @type string $last_modified The most recent timestamp to appear on the sitemap. 
  756. * } 
  757. */ 
  758. private function build_one_sitemap_index( $number, $from_id, $datetime, $index_type ) { 
  759. $last_sitemap_id = $from_id; 
  760. $any_sitemaps_left = true; 
  761.  
  762. // Check the datetime format. 
  763. $datetime = jp_sitemap_datetime( $datetime ); 
  764.  
  765. $sitemap_type = jp_sitemap_child_type_of( $index_type ); 
  766.  
  767. if ( $this->logger ) { 
  768. $index_debug_name = jp_sitemap_filename( $index_type, $number ); 
  769. $this->logger->report( "-- Building $index_debug_name" ); 
  770.  
  771. $sitemap_index_xsl_url = $this->finder->construct_sitemap_url( 'sitemap-index.xsl' ); 
  772.  
  773. $jetpack_version = JETPACK__VERSION; 
  774.  
  775. $buffer = new Jetpack_Sitemap_Buffer( 
  776. JP_SITEMAP_MAX_ITEMS,  
  777. JP_SITEMAP_MAX_BYTES,  
  778. <<<HEADER 
  779. <?xml version='1.0' encoding='UTF-8'?> 
  780. <!-- generator='jetpack-{$jetpack_version}' --> 
  781. <?xml-stylesheet type='text/xsl' href='{$sitemap_index_xsl_url}'?> 
  782. <sitemapindex xmlns='http://www.sitemaps.org/schemas/sitemap/0.9'>\n 
  783. HEADER 
  784. ,  
  785. <<<FOOTER 
  786. </sitemapindex>\n 
  787. FOOTER 
  788. ,  
  789. /** initial last_modified value */ 
  790. $datetime 
  791. ); 
  792.  
  793. // Add pointer to the previous sitemap index (unless we're at the first one). 
  794. if ( 1 !== $number ) { 
  795. $i = $number - 1; 
  796. $prev_index_url = $this->finder->construct_sitemap_url( 
  797. jp_sitemap_filename( $index_type, $i ) 
  798. ); 
  799.  
  800. $item_array = array( 
  801. 'sitemap' => array( 
  802. 'loc' => $prev_index_url,  
  803. 'lastmod' => $datetime,  
  804. ),  
  805. ); 
  806.  
  807. $buffer->try_to_add_item( Jetpack_Sitemap_Buffer::array_to_xml_string( $item_array ) ); 
  808.  
  809. // Add as many items to the buffer as possible. 
  810. while ( false === $buffer->is_full() ) { 
  811. // Retrieve a batch of posts (in order). 
  812. $posts = $this->librarian->query_sitemaps_after_id( 
  813. $sitemap_type, $last_sitemap_id, JP_SITEMAP_BATCH_SIZE 
  814. ); 
  815.  
  816. // If there were no posts to get, make a note. 
  817. if ( null == $posts ) { // WPCS: loose comparison ok. 
  818. $any_sitemaps_left = false; 
  819. break; 
  820.  
  821. // Otherwise, loop through each post in the batch. 
  822. foreach ( $posts as $post ) { 
  823. // Generate the sitemap XML for the post. 
  824. $current_item = $this->sitemap_row_to_index_item( (array) $post ); 
  825.  
  826. // Try adding this item to the buffer. 
  827. if ( true === $buffer->try_to_add_item( $current_item['xml'] ) ) { 
  828. $last_sitemap_id = $post['ID']; 
  829. $buffer->view_time( $current_item['last_modified'] ); 
  830. } else { 
  831. // Otherwise stop looping through posts. 
  832. break; 
  833.  
  834. // If no items were added, return false. 
  835. if ( true === $buffer->is_empty() ) { 
  836. return false; 
  837.  
  838. $this->librarian->store_sitemap_data( 
  839. $number,  
  840. $index_type,  
  841. $buffer->contents(),  
  842. $buffer->last_modified() 
  843. ); 
  844.  
  845. /** 
  846. * Now report back with the ID of the last sitemap post ID to 
  847. * be successfully added, whether there are any sitemap posts 
  848. * left, and the most recent modification time seen. 
  849. */ 
  850. return array( 
  851. 'last_id' => $last_sitemap_id,  
  852. 'any_left' => $any_sitemaps_left,  
  853. 'last_modified' => $buffer->last_modified(),  
  854. ); 
  855.  
  856. /** 
  857. * Construct the sitemap index url entry for a sitemap row. 
  858. * @link http://www.sitemaps.org/protocol.html#sitemapIndex_sitemap 
  859. * @access private 
  860. * @since 4.8.0 
  861. * @param array $row The sitemap data to be processed. 
  862. * @return string An XML fragment representing the post URL. 
  863. */ 
  864. private function sitemap_row_to_index_item( $row ) { 
  865. $url = $this->finder->construct_sitemap_url( $row['post_title'] ); 
  866.  
  867. $item_array = array( 
  868. 'sitemap' => array( 
  869. 'loc' => $url,  
  870. 'lastmod' => jp_sitemap_datetime( $row['post_date'] ),  
  871. ),  
  872. ); 
  873.  
  874. return array( 
  875. 'xml' => Jetpack_Sitemap_Buffer::array_to_xml_string( $item_array ),  
  876. 'last_modified' => $row['post_date'],  
  877. ); 
  878.  
  879. /** 
  880. * Build and return the news sitemap xml. Note that the result of this 
  881. * function is cached in the transient 'jetpack_news_sitemap_xml'. 
  882. * @access public 
  883. * @since 4.8.0 
  884. * @return string The news sitemap xml. 
  885. */ 
  886. public function news_sitemap_xml() { 
  887. $the_stored_news_sitemap = get_transient( 'jetpack_news_sitemap_xml' ); 
  888.  
  889. if ( false === $the_stored_news_sitemap ) { 
  890.  
  891. if ( $this->logger ) { 
  892. $this->logger->report( 'Beginning news sitemap generation.' ); 
  893.  
  894. $news_sitemap_xsl_url = $this->finder->construct_sitemap_url( 'news-sitemap.xsl' ); 
  895.  
  896. $jetpack_version = JETPACK__VERSION; 
  897.  
  898. /** 
  899. * Filter limit of entries to include in news sitemap. 
  900. * @module sitemaps 
  901. * @since 3.9.0 
  902. * @param int $count Number of entries to include in news sitemap. 
  903. */ 
  904. $item_limit = apply_filters( 
  905. 'jetpack_sitemap_news_sitemap_count',  
  906. JP_NEWS_SITEMAP_MAX_ITEMS 
  907. ); 
  908.  
  909. $namespaces = Jetpack_Sitemap_Buffer::array_to_xml_attr_string( 
  910. /** 
  911. * Filter the attribute value pairs used for namespace and namespace URI mappings. 
  912. * @module sitemaps 
  913. * @since 4.8.0 
  914. * @param array $namespaces Associative array with namespaces and namespace URIs. 
  915. */ 
  916. apply_filters( 
  917. 'jetpack_sitemap_news_ns',  
  918. array( 
  919. 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',  
  920. 'xsi:schemaLocation' => 'http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd',  
  921. 'xmlns' => 'http://www.sitemaps.org/schemas/sitemap/0.9',  
  922. 'xmlns:news' => 'http://www.google.com/schemas/sitemap-news/0.9',  
  923. ); 
  924.  
  925. $buffer = new Jetpack_Sitemap_Buffer( 
  926. min( $item_limit, JP_NEWS_SITEMAP_MAX_ITEMS ),  
  927. JP_SITEMAP_MAX_BYTES,  
  928. <<<HEADER 
  929. <?xml version='1.0' encoding='UTF-8'?> 
  930. <!-- generator='jetpack-{$jetpack_version}' --> 
  931. <?xml-stylesheet type='text/xsl' href='{$news_sitemap_xsl_url}'?> 
  932. <urlset{$namespaces}>\n 
  933. HEADER 
  934. ,  
  935. <<<FOOTER 
  936. </urlset>\n 
  937. FOOTER 
  938. ,  
  939. /** epoch */ 
  940. '1970-01-01 00:00:00' 
  941. ); 
  942.  
  943. $posts = $this->librarian->query_most_recent_posts( JP_NEWS_SITEMAP_MAX_ITEMS ); 
  944.  
  945. foreach ( $posts as $post ) { 
  946. $current_item = $this->post_to_news_sitemap_item( $post ); 
  947.  
  948. if ( false === $buffer->try_to_add_item( $current_item['xml'] ) ) { 
  949. break; 
  950.  
  951. if ( $this->logger ) { 
  952. $this->logger->time( 'End news sitemap generation.' ); 
  953.  
  954. $the_stored_news_sitemap = $buffer->contents(); 
  955.  
  956. set_transient( 
  957. 'jetpack_news_sitemap_xml',  
  958. $the_stored_news_sitemap,  
  959. JP_NEWS_SITEMAP_INTERVAL 
  960. ); 
  961.  
  962. return $the_stored_news_sitemap; 
  963.  
  964. /** 
  965. * Construct the sitemap url entry for a WP_Post. 
  966. * @link http://www.sitemaps.org/protocol.html#urldef 
  967. * @access private 
  968. * @since 4.8.0 
  969. * @param WP_Post $post The post to be processed. 
  970. * @return string An XML fragment representing the post URL. 
  971. */ 
  972. private function post_to_sitemap_item( $post ) { 
  973.  
  974. /** 
  975. * Filter condition to allow skipping specific posts in sitemap. 
  976. * @module sitemaps 
  977. * @since 3.9.0 
  978. * @param bool $skip Current boolean. False by default, so no post is skipped. 
  979. * @param WP_POST $post Current post object. 
  980. */ 
  981. if ( true === apply_filters( 'jetpack_sitemap_skip_post', false, $post ) ) { 
  982. return array( 
  983. 'xml' => null,  
  984. 'last_modified' => null,  
  985. ); 
  986.  
  987. $url = get_permalink( $post ); 
  988.  
  989. /** 
  990. * Spec requires the URL to be <=2048 bytes. 
  991. * In practice this constraint is unlikely to be violated. 
  992. */ 
  993. if ( 2048 < strlen( $url ) ) { 
  994. $url = home_url() . '/?p=' . $post->ID; 
  995.  
  996. $last_modified = $post->post_modified_gmt; 
  997.  
  998. // Check for more recent comments. 
  999. // Note that 'Y-m-d h:i:s' strings sort lexicographically. 
  1000. if ( 0 < $post->comment_count ) { 
  1001. $last_modified = max( 
  1002. $last_modified,  
  1003. $this->librarian->query_latest_approved_comment_time_on_post( $post->ID ) 
  1004. ); 
  1005.  
  1006. $item_array = array( 
  1007. 'url' => array( 
  1008. 'loc' => $url,  
  1009. 'lastmod' => jp_sitemap_datetime( $last_modified ),  
  1010. ),  
  1011. ); 
  1012.  
  1013. /** 
  1014. * Filter sitemap URL item before rendering it as XML. 
  1015. * @module sitemaps 
  1016. * @since 3.9.0 
  1017. * @param array $tree Associative array representing sitemap URL element. 
  1018. * @param int $post_id ID of the post being processed. 
  1019. */ 
  1020. $item_array = apply_filters( 'jetpack_sitemap_url', $item_array, $post->ID ); 
  1021.  
  1022. return array( 
  1023. 'xml' => Jetpack_Sitemap_Buffer::array_to_xml_string( $item_array ),  
  1024. 'last_modified' => $last_modified,  
  1025. ); 
  1026.  
  1027. /** 
  1028. * Construct the image sitemap url entry for a WP_Post of image type. 
  1029. * @link http://www.sitemaps.org/protocol.html#urldef 
  1030. * @access private 
  1031. * @since 4.8.0 
  1032. * @param WP_Post $post The image post to be processed. 
  1033. * @return string An XML fragment representing the post URL. 
  1034. */ 
  1035. private function image_post_to_sitemap_item( $post ) { 
  1036.  
  1037. /** 
  1038. * Filter condition to allow skipping specific image posts in the sitemap. 
  1039. * @module sitemaps 
  1040. * @since 4.8.0 
  1041. * @param bool $skip Current boolean. False by default, so no post is skipped. 
  1042. * @param WP_POST $post Current post object. 
  1043. */ 
  1044. if ( apply_filters( 'jetpack_sitemap_image_skip_post', false, $post ) ) { 
  1045. return array( 
  1046. 'xml' => null,  
  1047. 'last_modified' => null,  
  1048. ); 
  1049.  
  1050. $url = wp_get_attachment_url( $post->ID ); 
  1051.  
  1052. $parent_url = get_permalink( get_post( $post->post_parent ) ); 
  1053. if ( '' == $parent_url ) { // WPCS: loose comparison ok. 
  1054. $parent_url = get_permalink( $post ); 
  1055.  
  1056. $item_array = array( 
  1057. 'url' => array( 
  1058. 'loc' => $parent_url,  
  1059. 'lastmod' => jp_sitemap_datetime( $post->post_modified_gmt ),  
  1060. 'image:image' => array( 
  1061. 'image:loc' => $url,  
  1062. ),  
  1063. ),  
  1064. ); 
  1065.  
  1066. $title = esc_html( $post->post_title ); 
  1067. if ( '' !== $title ) { 
  1068. $item_array['url']['image:image']['image:title'] = $title; 
  1069.  
  1070. $caption = esc_html( $post->post_excerpt ); 
  1071. if ( '' !== $caption ) { 
  1072. $item_array['url']['image:image']['image:caption'] = $caption; 
  1073.  
  1074. /** 
  1075. * Filter associative array with data to build <url> node 
  1076. * and its descendants for current post in image sitemap. 
  1077. * @module sitemaps 
  1078. * @since 4.8.0 
  1079. * @param array $item_array Data to build parent and children nodes for current post. 
  1080. * @param int $post_id Current image post ID. 
  1081. */ 
  1082. $item_array = apply_filters( 
  1083. 'jetpack_sitemap_image_sitemap_item',  
  1084. $item_array,  
  1085. $post->ID 
  1086. ); 
  1087.  
  1088. return array( 
  1089. 'xml' => Jetpack_Sitemap_Buffer::array_to_xml_string( $item_array ),  
  1090. 'last_modified' => $post->post_modified_gmt,  
  1091. ); 
  1092.  
  1093. /** 
  1094. * Construct the video sitemap url entry for a WP_Post of video type. 
  1095. * @link http://www.sitemaps.org/protocol.html#urldef 
  1096. * @link https://developers.google.com/webmasters/videosearch/sitemaps 
  1097. * @access private 
  1098. * @since 4.8.0 
  1099. * @param WP_Post $post The video post to be processed. 
  1100. * @return string An XML fragment representing the post URL. 
  1101. */ 
  1102. private function video_post_to_sitemap_item( $post ) { 
  1103.  
  1104. /** 
  1105. * Filter condition to allow skipping specific image posts in the sitemap. 
  1106. * @module sitemaps 
  1107. * @since 4.8.0 
  1108. * @param bool $skip Current boolean. False by default, so no post is skipped. 
  1109. * @param WP_POST $post Current post object. 
  1110. */ 
  1111. if ( apply_filters( 'jetpack_sitemap_video_skip_post', false, $post ) ) { 
  1112. return array( 
  1113. 'xml' => null,  
  1114. 'last_modified' => null,  
  1115. ); 
  1116.  
  1117. $parent_url = get_permalink( get_post( $post->post_parent ) ); 
  1118. if ( '' == $parent_url ) { // WPCS: loose comparison ok. 
  1119. $parent_url = get_permalink( $post ); 
  1120.  
  1121. $item_array = array( 
  1122. 'url' => array( 
  1123. 'loc' => $parent_url,  
  1124. 'lastmod' => jp_sitemap_datetime( $post->post_modified_gmt ),  
  1125. 'video:video' => array( 
  1126. 'video:title' => esc_html( $post->post_title ),  
  1127. 'video:thumbnail_loc' => '',  
  1128. 'video:description' => esc_html( $post->post_content ),  
  1129. 'video:content_loc' => wp_get_attachment_url( $post->ID ),  
  1130. ),  
  1131. ),  
  1132. ); 
  1133.  
  1134. // TODO: Integrate with VideoPress here. 
  1135. // cf. video:player_loc tag in video sitemap spec. 
  1136.  
  1137. /** 
  1138. * Filter associative array with data to build <url> node 
  1139. * and its descendants for current post in video sitemap. 
  1140. * @module sitemaps 
  1141. * @since 4.8.0 
  1142. * @param array $item_array Data to build parent and children nodes for current post. 
  1143. * @param int $post_id Current video post ID. 
  1144. */ 
  1145. $item_array = apply_filters( 
  1146. 'jetpack_sitemap_video_sitemap_item',  
  1147. $item_array,  
  1148. $post->ID 
  1149. ); 
  1150.  
  1151. return array( 
  1152. 'xml' => Jetpack_Sitemap_Buffer::array_to_xml_string( $item_array ),  
  1153. 'last_modified' => $post->post_modified_gmt,  
  1154. ); 
  1155.  
  1156. /** 
  1157. * Construct the news sitemap url entry for a WP_Post. 
  1158. * @link http://www.sitemaps.org/protocol.html#urldef 
  1159. * @access private 
  1160. * @since 4.8.0 
  1161. * @param WP_Post $post The post to be processed. 
  1162. * @return string An XML fragment representing the post URL. 
  1163. */ 
  1164. private function post_to_news_sitemap_item( $post ) { 
  1165.  
  1166. /** 
  1167. * Filter condition to allow skipping specific posts in news sitemap. 
  1168. * @module sitemaps 
  1169. * @since 3.9.0 
  1170. * @param bool $skip Current boolean. False by default, so no post is skipped. 
  1171. * @param WP_POST $post Current post object. 
  1172. */ 
  1173. if ( apply_filters( 'jetpack_sitemap_news_skip_post', false, $post ) ) { 
  1174. return array( 
  1175. 'xml' => null,  
  1176. ); 
  1177.  
  1178. $url = get_permalink( $post ); 
  1179.  
  1180. /** 
  1181. * Spec requires the URL to be <=2048 bytes. 
  1182. * In practice this constraint is unlikely to be violated. 
  1183. */ 
  1184. if ( 2048 < strlen( $url ) ) { 
  1185. $url = home_url() . '/?p=' . $post->ID; 
  1186.  
  1187. /** 
  1188. * Trim the locale to an ISO 639 language code as required by Google. 
  1189. * Special cases are zh-cn (Simplified Chinese) and zh-tw (Traditional Chinese). 
  1190. * @link http://www.loc.gov/standards/iso639-2/php/code_list.php 
  1191. */ 
  1192. $language = strtolower( get_locale() ); 
  1193.  
  1194. if ( in_array( $language, array( 'zh_tw', 'zh_cn' ), true ) ) { 
  1195. $language = str_replace( '_', '-', $language ); 
  1196. } else { 
  1197. $language = preg_replace( '/(_.*)$/i', '', $language ); 
  1198.  
  1199. $item_array = array( 
  1200. 'url' => array( 
  1201. 'loc' => $url,  
  1202. 'lastmod' => jp_sitemap_datetime( $post->post_modified_gmt ),  
  1203. 'news:news' => array( 
  1204. 'news:publication' => array( 
  1205. 'news:name' => esc_html( get_bloginfo( 'name' ) ),  
  1206. 'news:language' => $language,  
  1207. ),  
  1208. 'news:title' => esc_html( $post->post_title ),  
  1209. 'news:publication_date' => jp_sitemap_datetime( $post->post_date_gmt ),  
  1210. 'news:genres' => 'Blog',  
  1211. ),  
  1212. ),  
  1213. ); 
  1214.  
  1215. /** 
  1216. * Filter associative array with data to build <url> node 
  1217. * and its descendants for current post in news sitemap. 
  1218. * @module sitemaps 
  1219. * @since 3.9.0 
  1220. * @param array $item_array Data to build parent and children nodes for current post. 
  1221. * @param int $post_id Current post ID. 
  1222. */ 
  1223. $item_array = apply_filters( 
  1224. 'jetpack_sitemap_news_sitemap_item',  
  1225. $item_array,  
  1226. $post->ID 
  1227. ); 
  1228.  
  1229. return array( 
  1230. 'xml' => Jetpack_Sitemap_Buffer::array_to_xml_string( $item_array ),  
  1231. ); 
  1232.