Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like Jetpack_Sitemap_Builder often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Jetpack_Sitemap_Builder, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
77 | class Jetpack_Sitemap_Builder { |
||
78 | |||
79 | /** |
||
80 | * Librarian object for storing and retrieving sitemap data. |
||
81 | * |
||
82 | * @access private |
||
83 | * @since 4.8.0 |
||
84 | * @var $librarian Jetpack_Sitemap_Librarian |
||
85 | */ |
||
86 | private $librarian; |
||
87 | |||
88 | /** |
||
89 | * Logger object for reporting debug messages. |
||
90 | * |
||
91 | * @access private |
||
92 | * @since 4.8.0 |
||
93 | * @var $logger Jetpack_Sitemap_Logger |
||
94 | */ |
||
95 | private $logger = false; |
||
96 | |||
97 | /** |
||
98 | * Finder object for dealing with sitemap URIs. |
||
99 | * |
||
100 | * @access private |
||
101 | * @since 4.8.0 |
||
102 | * @var $finder Jetpack_Sitemap_Finder |
||
103 | */ |
||
104 | private $finder; |
||
105 | |||
106 | /** |
||
107 | * Construct a new Jetpack_Sitemap_Builder object. |
||
108 | * |
||
109 | * @access public |
||
110 | * @since 4.8.0 |
||
111 | */ |
||
112 | public function __construct() { |
||
113 | $this->librarian = new Jetpack_Sitemap_Librarian(); |
||
114 | $this->finder = new Jetpack_Sitemap_Finder(); |
||
115 | |||
116 | if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { |
||
117 | $this->logger = new Jetpack_Sitemap_Logger(); |
||
118 | } |
||
119 | |||
120 | update_option( |
||
121 | 'jetpack_sitemap_post_types', |
||
122 | /** |
||
123 | * The array of post types to be included in the sitemap. |
||
124 | * |
||
125 | * Add your custom post type name to the array to have posts of |
||
126 | * that type included in the sitemap. The default array includes |
||
127 | * 'page' and 'post'. |
||
128 | * |
||
129 | * The result of this filter is cached in an option, 'jetpack_sitemap_post_types', |
||
130 | * so this filter only has to be applied once per generation. |
||
131 | * |
||
132 | * @since 4.8.0 |
||
133 | */ |
||
134 | apply_filters( |
||
135 | 'jetpack_sitemap_post_types', |
||
136 | array( 'post', 'page' ) |
||
137 | ) |
||
138 | ); |
||
139 | } |
||
140 | |||
141 | /** |
||
142 | * Update the sitemap. |
||
143 | * |
||
144 | * All we do here is call build_next_sitemap_file a bunch of times. |
||
145 | * |
||
146 | * @since 4.8.0 |
||
147 | */ |
||
148 | public function update_sitemap() { |
||
149 | if ( $this->logger ) { |
||
150 | $this->logger->report( '-- Updating...' ); |
||
151 | if ( ! class_exists( 'DOMDocument' ) ) { |
||
152 | $this->logger->report( |
||
153 | __( |
||
154 | '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/ .', |
||
155 | 'jetpack' |
||
156 | ), |
||
157 | true |
||
158 | ); |
||
159 | } |
||
160 | } |
||
161 | |||
162 | for ( $i = 1; $i <= JP_SITEMAP_UPDATE_SIZE; $i++ ) { |
||
163 | if ( true === $this->build_next_sitemap_file() ) { |
||
164 | break; // All finished! |
||
165 | } |
||
166 | } |
||
167 | |||
168 | if ( $this->logger ) { |
||
169 | $this->logger->report( '-- ...done for now.' ); |
||
170 | $this->logger->time(); |
||
171 | } |
||
172 | } |
||
173 | |||
174 | /** |
||
175 | * Generate the next sitemap file. |
||
176 | * |
||
177 | * Reads the most recent state of the sitemap generation phase, |
||
178 | * constructs the next file, and updates the state. |
||
179 | * |
||
180 | * @since 4.8.0 |
||
181 | * |
||
182 | * @return bool True when finished. |
||
183 | */ |
||
184 | private function build_next_sitemap_file() { |
||
185 | $finished = false; // Initialize finished flag. |
||
186 | |||
187 | // Get the most recent state, and lock the state. |
||
188 | $state = Jetpack_Sitemap_State::check_out(); |
||
189 | |||
190 | // Do nothing if the state was locked. |
||
191 | if ( false === $state ) { |
||
192 | return false; |
||
193 | } |
||
194 | |||
195 | // Otherwise, branch on the sitemap-type key of $state. |
||
196 | switch ( $state['sitemap-type'] ) { |
||
197 | case JP_PAGE_SITEMAP_TYPE: |
||
198 | $this->build_next_sitemap_of_type( |
||
199 | JP_PAGE_SITEMAP_TYPE, |
||
200 | array( $this, 'build_one_page_sitemap' ), |
||
201 | $state |
||
202 | ); |
||
203 | break; |
||
204 | |||
205 | case JP_PAGE_SITEMAP_INDEX_TYPE: |
||
206 | $this->build_next_sitemap_index_of_type( |
||
207 | JP_PAGE_SITEMAP_INDEX_TYPE, |
||
208 | JP_IMAGE_SITEMAP_TYPE, |
||
209 | $state |
||
210 | ); |
||
211 | break; |
||
212 | |||
213 | case JP_IMAGE_SITEMAP_TYPE: |
||
214 | $this->build_next_sitemap_of_type( |
||
215 | JP_IMAGE_SITEMAP_TYPE, |
||
216 | array( $this, 'build_one_image_sitemap' ), |
||
217 | $state |
||
218 | ); |
||
219 | break; |
||
220 | |||
221 | case JP_IMAGE_SITEMAP_INDEX_TYPE: |
||
222 | $this->build_next_sitemap_index_of_type( |
||
223 | JP_IMAGE_SITEMAP_INDEX_TYPE, |
||
224 | JP_VIDEO_SITEMAP_TYPE, |
||
225 | $state |
||
226 | ); |
||
227 | break; |
||
228 | |||
229 | case JP_VIDEO_SITEMAP_TYPE: |
||
230 | $this->build_next_sitemap_of_type( |
||
231 | JP_VIDEO_SITEMAP_TYPE, |
||
232 | array( $this, 'build_one_video_sitemap' ), |
||
233 | $state |
||
234 | ); |
||
235 | break; |
||
236 | |||
237 | case JP_VIDEO_SITEMAP_INDEX_TYPE: |
||
238 | $this->build_next_sitemap_index_of_type( |
||
239 | JP_VIDEO_SITEMAP_INDEX_TYPE, |
||
240 | JP_MASTER_SITEMAP_TYPE, |
||
241 | $state |
||
242 | ); |
||
243 | break; |
||
244 | |||
245 | case JP_MASTER_SITEMAP_TYPE: |
||
246 | $this->build_master_sitemap( $state['max'] ); |
||
247 | |||
248 | // Reset the state and quit. |
||
249 | Jetpack_Sitemap_State::reset( |
||
250 | JP_PAGE_SITEMAP_TYPE |
||
251 | ); |
||
252 | |||
253 | if ( $this->logger ) { |
||
254 | $this->logger->report( '-- Finished.' ); |
||
255 | $this->logger->time(); |
||
256 | } |
||
257 | $finished = true; |
||
258 | |||
259 | break; |
||
260 | |||
261 | default: |
||
262 | Jetpack_Sitemap_State::reset( |
||
263 | JP_PAGE_SITEMAP_TYPE |
||
264 | ); |
||
265 | $finished = true; |
||
266 | |||
267 | break; |
||
268 | } // End switch. |
||
269 | |||
270 | // Unlock the state. |
||
271 | Jetpack_Sitemap_State::unlock(); |
||
272 | |||
273 | return $finished; |
||
274 | } |
||
275 | |||
276 | /** |
||
277 | * Build the next sitemap of a given type and update the sitemap state. |
||
278 | * |
||
279 | * @since 4.8.0 |
||
280 | * |
||
281 | * @param string $sitemap_type The type of the sitemap being generated. |
||
282 | * @param callback $build_one A callback which builds a single sitemap file. |
||
283 | * @param array $state A sitemap state. |
||
284 | */ |
||
285 | private function build_next_sitemap_of_type( $sitemap_type, $build_one, $state ) { |
||
286 | $index_type = jp_sitemap_index_type_of( $sitemap_type ); |
||
287 | |||
288 | // Try to build a sitemap. |
||
289 | $result = call_user_func_array( |
||
290 | $build_one, |
||
291 | array( |
||
292 | $state['number'] + 1, |
||
293 | $state['last-added'], |
||
294 | ) |
||
295 | ); |
||
296 | |||
297 | if ( false === $result ) { |
||
298 | // If no sitemap was generated, advance to the next type. |
||
299 | Jetpack_Sitemap_State::check_in( |
||
300 | array( |
||
301 | 'sitemap-type' => $index_type, |
||
302 | 'last-added' => 0, |
||
303 | 'number' => 0, |
||
304 | 'last-modified' => '1970-01-01 00:00:00', |
||
305 | ) |
||
306 | ); |
||
307 | |||
308 | if ( $this->logger ) { |
||
309 | $this->logger->report( "-- Cleaning Up $sitemap_type" ); |
||
310 | } |
||
311 | |||
312 | // Clean up old files. |
||
313 | $this->librarian->delete_numbered_sitemap_rows_after( |
||
314 | $state['number'], |
||
315 | $sitemap_type |
||
316 | ); |
||
317 | |||
318 | return; |
||
319 | } |
||
320 | |||
321 | // Otherwise, update the state. |
||
322 | Jetpack_Sitemap_State::check_in( |
||
323 | array( |
||
324 | 'sitemap-type' => $state['sitemap-type'], |
||
325 | 'last-added' => $result['last_id'], |
||
326 | 'number' => $state['number'] + 1, |
||
327 | 'last-modified' => $result['last_modified'], |
||
328 | ) |
||
329 | ); |
||
330 | |||
331 | if ( true === $result['any_left'] ) { |
||
332 | // If there's more work to be done with this type, return. |
||
333 | return; |
||
334 | } |
||
335 | |||
336 | // Otherwise, advance state to the next sitemap type. |
||
337 | Jetpack_Sitemap_State::check_in( |
||
338 | array( |
||
339 | 'sitemap-type' => $index_type, |
||
340 | 'last-added' => 0, |
||
341 | 'number' => 0, |
||
342 | 'last-modified' => '1970-01-01 00:00:00', |
||
343 | ) |
||
344 | ); |
||
345 | |||
346 | if ( $this->logger ) { |
||
347 | $this->logger->report( "-- Cleaning Up $sitemap_type" ); |
||
348 | } |
||
349 | |||
350 | // Clean up old files. |
||
351 | $this->librarian->delete_numbered_sitemap_rows_after( |
||
352 | $state['number'] + 1, |
||
353 | $sitemap_type |
||
354 | ); |
||
355 | } |
||
356 | |||
357 | /** |
||
358 | * Build the next sitemap index of a given type and update the state. |
||
359 | * |
||
360 | * @since 4.8.0 |
||
361 | * |
||
362 | * @param string $index_type The type of index being generated. |
||
363 | * @param string $next_type The next type to generate after this one. |
||
364 | * @param array $state A sitemap state. |
||
365 | */ |
||
366 | private function build_next_sitemap_index_of_type( $index_type, $next_type, $state ) { |
||
367 | $sitemap_type = jp_sitemap_child_type_of( $index_type ); |
||
368 | |||
369 | // If only 0 or 1 sitemaps were built, advance to the next type and return. |
||
370 | if ( 1 >= $state['max'][ $sitemap_type ]['number'] ) { |
||
371 | Jetpack_Sitemap_State::check_in( |
||
372 | array( |
||
373 | 'sitemap-type' => $next_type, |
||
374 | 'last-added' => 0, |
||
375 | 'number' => 0, |
||
376 | 'last-modified' => '1970-01-01 00:00:00', |
||
377 | ) |
||
378 | ); |
||
379 | |||
380 | if ( $this->logger ) { |
||
381 | $this->logger->report( "-- Cleaning Up $index_type" ); |
||
382 | } |
||
383 | |||
384 | // There are no indices of this type. |
||
385 | $this->librarian->delete_numbered_sitemap_rows_after( |
||
386 | 0, |
||
387 | $index_type |
||
388 | ); |
||
389 | |||
390 | return; |
||
391 | } |
||
392 | |||
393 | // Otherwise, try to build a sitemap index. |
||
394 | $result = $this->build_one_sitemap_index( |
||
395 | $state['number'] + 1, |
||
396 | $state['last-added'], |
||
397 | $state['last-modified'], |
||
398 | $index_type |
||
399 | ); |
||
400 | |||
401 | // If no index was built, advance to the next type and return. |
||
402 | if ( false === $result ) { |
||
403 | Jetpack_Sitemap_State::check_in( |
||
404 | array( |
||
405 | 'sitemap-type' => $next_type, |
||
406 | 'last-added' => 0, |
||
407 | 'number' => 0, |
||
408 | 'last-modified' => '1970-01-01 00:00:00', |
||
409 | ) |
||
410 | ); |
||
411 | |||
412 | if ( $this->logger ) { |
||
413 | $this->logger->report( "-- Cleaning Up $index_type" ); |
||
414 | } |
||
415 | |||
416 | // Clean up old files. |
||
417 | $this->librarian->delete_numbered_sitemap_rows_after( |
||
418 | $state['number'], |
||
419 | $index_type |
||
420 | ); |
||
421 | |||
422 | return; |
||
423 | } |
||
424 | |||
425 | // Otherwise, check in the state. |
||
426 | Jetpack_Sitemap_State::check_in( |
||
427 | array( |
||
428 | 'sitemap-type' => $index_type, |
||
429 | 'last-added' => $result['last_id'], |
||
430 | 'number' => $state['number'] + 1, |
||
431 | 'last-modified' => $result['last_modified'], |
||
432 | ) |
||
433 | ); |
||
434 | |||
435 | // If there are still sitemaps left to index, return. |
||
436 | if ( true === $result['any_left'] ) { |
||
437 | return; |
||
438 | } |
||
439 | |||
440 | // Otherwise, advance to the next type. |
||
441 | Jetpack_Sitemap_State::check_in( |
||
442 | array( |
||
443 | 'sitemap-type' => $next_type, |
||
444 | 'last-added' => 0, |
||
445 | 'number' => 0, |
||
446 | 'last-modified' => '1970-01-01 00:00:00', |
||
447 | ) |
||
448 | ); |
||
449 | |||
450 | if ( $this->logger ) { |
||
451 | $this->logger->report( "-- Cleaning Up $index_type" ); |
||
452 | } |
||
453 | |||
454 | // We're done generating indices of this type. |
||
455 | $this->librarian->delete_numbered_sitemap_rows_after( |
||
456 | $state['number'] + 1, |
||
457 | $index_type |
||
458 | ); |
||
459 | } |
||
460 | |||
461 | /** |
||
462 | * Builds the master sitemap index. |
||
463 | * |
||
464 | * @param array $max Array of sitemap types with max index and datetime. |
||
465 | * |
||
466 | * @since 4.8.0 |
||
467 | */ |
||
468 | private function build_master_sitemap( $max ) { |
||
469 | $page = array(); |
||
470 | $image = array(); |
||
471 | $video = array(); |
||
472 | if ( $this->logger ) { |
||
473 | $this->logger->report( '-- Building Master Sitemap.' ); |
||
474 | } |
||
475 | |||
476 | $buffer = new Jetpack_Sitemap_Buffer_Master( |
||
477 | JP_SITEMAP_MAX_ITEMS, |
||
478 | JP_SITEMAP_MAX_BYTES |
||
479 | ); |
||
480 | |||
481 | if ( 0 < $max[ JP_PAGE_SITEMAP_TYPE ]['number'] ) { |
||
482 | if ( 1 === $max[ JP_PAGE_SITEMAP_TYPE ]['number'] ) { |
||
483 | $page['filename'] = jp_sitemap_filename( JP_PAGE_SITEMAP_TYPE, 1 ); |
||
484 | $page['last_modified'] = jp_sitemap_datetime( $max[ JP_PAGE_SITEMAP_TYPE ]['lastmod'] ); |
||
485 | } else { |
||
486 | $page['filename'] = jp_sitemap_filename( |
||
487 | JP_PAGE_SITEMAP_INDEX_TYPE, |
||
488 | $max[ JP_PAGE_SITEMAP_INDEX_TYPE ]['number'] |
||
489 | ); |
||
490 | $page['last_modified'] = jp_sitemap_datetime( $max[ JP_PAGE_SITEMAP_INDEX_TYPE ]['lastmod'] ); |
||
491 | } |
||
492 | |||
493 | $buffer->append( |
||
494 | array( |
||
495 | 'sitemap' => array( |
||
496 | 'loc' => $this->finder->construct_sitemap_url( $page['filename'] ), |
||
497 | 'lastmod' => $page['last_modified'], |
||
498 | ), |
||
499 | ) |
||
500 | ); |
||
501 | } |
||
502 | |||
503 | if ( 0 < $max[ JP_IMAGE_SITEMAP_TYPE ]['number'] ) { |
||
504 | if ( 1 === $max[ JP_IMAGE_SITEMAP_TYPE ]['number'] ) { |
||
505 | $image['filename'] = jp_sitemap_filename( JP_IMAGE_SITEMAP_TYPE, 1 ); |
||
506 | $image['last_modified'] = jp_sitemap_datetime( $max[ JP_IMAGE_SITEMAP_TYPE ]['lastmod'] ); |
||
507 | } else { |
||
508 | $image['filename'] = jp_sitemap_filename( |
||
509 | JP_IMAGE_SITEMAP_INDEX_TYPE, |
||
510 | $max[ JP_IMAGE_SITEMAP_INDEX_TYPE ]['number'] |
||
511 | ); |
||
512 | $image['last_modified'] = jp_sitemap_datetime( $max[ JP_IMAGE_SITEMAP_INDEX_TYPE ]['lastmod'] ); |
||
513 | } |
||
514 | |||
515 | $buffer->append( |
||
516 | array( |
||
517 | 'sitemap' => array( |
||
518 | 'loc' => $this->finder->construct_sitemap_url( $image['filename'] ), |
||
519 | 'lastmod' => $image['last_modified'], |
||
520 | ), |
||
521 | ) |
||
522 | ); |
||
523 | } |
||
524 | |||
525 | if ( 0 < $max[ JP_VIDEO_SITEMAP_TYPE ]['number'] ) { |
||
526 | if ( 1 === $max[ JP_VIDEO_SITEMAP_TYPE ]['number'] ) { |
||
527 | $video['filename'] = jp_sitemap_filename( JP_VIDEO_SITEMAP_TYPE, 1 ); |
||
528 | $video['last_modified'] = jp_sitemap_datetime( $max[ JP_VIDEO_SITEMAP_TYPE ]['lastmod'] ); |
||
529 | } else { |
||
530 | $video['filename'] = jp_sitemap_filename( |
||
531 | JP_VIDEO_SITEMAP_INDEX_TYPE, |
||
532 | $max[ JP_VIDEO_SITEMAP_INDEX_TYPE ]['number'] |
||
533 | ); |
||
534 | $video['last_modified'] = jp_sitemap_datetime( $max[ JP_VIDEO_SITEMAP_INDEX_TYPE ]['lastmod'] ); |
||
535 | } |
||
536 | |||
537 | $buffer->append( |
||
538 | array( |
||
539 | 'sitemap' => array( |
||
540 | 'loc' => $this->finder->construct_sitemap_url( $video['filename'] ), |
||
541 | 'lastmod' => $video['last_modified'], |
||
542 | ), |
||
543 | ) |
||
544 | ); |
||
545 | } |
||
546 | |||
547 | $this->librarian->store_sitemap_data( |
||
548 | 0, |
||
549 | JP_MASTER_SITEMAP_TYPE, |
||
550 | $buffer->contents(), |
||
551 | '' |
||
552 | ); |
||
553 | } |
||
554 | |||
555 | /** |
||
556 | * Build and store a single page sitemap. Returns false if no sitemap is built. |
||
557 | * |
||
558 | * Side effect: Create/update a sitemap row. |
||
559 | * |
||
560 | * @access private |
||
561 | * @since 4.8.0 |
||
562 | * |
||
563 | * @param int $number The number of the current sitemap. |
||
564 | * @param int $from_id The greatest lower bound of the IDs of the posts to be included. |
||
565 | * |
||
566 | * @return bool|array @args { |
||
567 | * @type int $last_id The ID of the last item to be successfully added to the buffer. |
||
568 | * @type bool $any_left 'true' if there are items which haven't been saved to a sitemap, 'false' otherwise. |
||
569 | * @type string $last_modified The most recent timestamp to appear on the sitemap. |
||
570 | * } |
||
571 | */ |
||
572 | public function build_one_page_sitemap( $number, $from_id ) { |
||
573 | $last_post_id = $from_id; |
||
574 | $any_posts_left = true; |
||
575 | |||
576 | if ( $this->logger ) { |
||
577 | $debug_name = jp_sitemap_filename( JP_PAGE_SITEMAP_TYPE, $number ); |
||
578 | $this->logger->report( "-- Building $debug_name" ); |
||
579 | } |
||
580 | |||
581 | $buffer = new Jetpack_Sitemap_Buffer_Page( |
||
582 | JP_SITEMAP_MAX_ITEMS, |
||
583 | JP_SITEMAP_MAX_BYTES |
||
584 | ); |
||
585 | |||
586 | // 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. |
||
587 | if ( 1 === $number && 'page' !== get_option( 'show_on_front' ) ) { |
||
588 | $item_array = array( |
||
589 | 'url' => array( |
||
590 | 'loc' => home_url(), |
||
591 | ), |
||
592 | ); |
||
593 | |||
594 | /** |
||
595 | * Filter associative array with data to build <url> node |
||
596 | * and its descendants for site home. |
||
597 | * |
||
598 | * @module sitemaps |
||
599 | * |
||
600 | * @since 3.9.0 |
||
601 | * |
||
602 | * @param array $blog_home Data to build parent and children nodes for site home. |
||
603 | */ |
||
604 | $item_array = apply_filters( 'jetpack_sitemap_url_home', $item_array ); |
||
605 | |||
606 | $buffer->append( $item_array ); |
||
607 | } |
||
608 | |||
609 | // Add as many items to the buffer as possible. |
||
610 | while ( $last_post_id >= 0 && false === $buffer->is_full() ) { |
||
611 | $posts = $this->librarian->query_posts_after_id( |
||
612 | $last_post_id, |
||
613 | JP_SITEMAP_BATCH_SIZE |
||
614 | ); |
||
615 | |||
616 | if ( null == $posts ) { // WPCS: loose comparison ok. |
||
617 | $any_posts_left = false; |
||
618 | break; |
||
619 | } |
||
620 | |||
621 | foreach ( $posts as $post ) { |
||
622 | $current_item = $this->post_to_sitemap_item( $post ); |
||
623 | |||
624 | if ( true === $buffer->append( $current_item['xml'] ) ) { |
||
625 | $last_post_id = $post->ID; |
||
626 | $buffer->view_time( $current_item['last_modified'] ); |
||
627 | } else { |
||
628 | break; |
||
629 | } |
||
630 | } |
||
631 | } |
||
632 | |||
633 | // Handle other page sitemap URLs. |
||
634 | if ( false === $any_posts_left || $last_post_id < 0 ) { |
||
635 | // Negative IDs are used to track URL indexes. |
||
636 | $last_post_id = min( 0, $last_post_id ); |
||
637 | $any_posts_left = true; // Reinitialize. |
||
638 | |||
639 | /** |
||
640 | * Filter other page sitemap URLs. |
||
641 | * |
||
642 | * @module sitemaps |
||
643 | * |
||
644 | * @since 6.1.0 |
||
645 | * |
||
646 | * @param array $urls An array of other URLs. |
||
647 | */ |
||
648 | $other_urls = apply_filters( 'jetpack_page_sitemap_other_urls', array() ); |
||
649 | |||
650 | if ( $other_urls ) { // Start with index [1]. |
||
651 | $other_urls = array_values( $other_urls ); |
||
652 | array_unshift( $other_urls, $other_urls[0] ); |
||
653 | unset( $other_urls[0] ); |
||
654 | } |
||
655 | |||
656 | // Add as many items to the buffer as possible. |
||
657 | while ( false === $buffer->is_full() ) { |
||
658 | $last_post_id_index = abs( $last_post_id ); |
||
659 | $start_from_post_id_index = $last_post_id_index ? $last_post_id_index + 1 : 0; |
||
660 | $urls = array_slice( |
||
661 | $other_urls, |
||
662 | $start_from_post_id_index, |
||
663 | JP_SITEMAP_BATCH_SIZE, |
||
664 | true |
||
665 | ); |
||
666 | |||
667 | if ( ! $urls ) { |
||
668 | $any_posts_left = false; |
||
669 | break; |
||
670 | } |
||
671 | |||
672 | foreach ( $urls as $index => $url ) { |
||
673 | if ( ! is_array( $url ) ) { |
||
674 | $url = array( 'loc' => $url ); |
||
675 | } |
||
676 | $item = array( 'xml' => compact( 'url' ) ); |
||
677 | |||
678 | if ( true === $buffer->append( $item['xml'] ) ) { |
||
679 | $last_post_id = -$index; |
||
680 | } else { |
||
681 | break; |
||
682 | } |
||
683 | } |
||
684 | } |
||
685 | } |
||
686 | |||
687 | // If no items were added, return false. |
||
688 | if ( true === $buffer->is_empty() ) { |
||
689 | return false; |
||
690 | } |
||
691 | |||
692 | /** |
||
693 | * Filter sitemap before rendering it as XML. |
||
694 | * |
||
695 | * @module sitemaps |
||
696 | * |
||
697 | * @since 3.9.0 |
||
698 | * @since 5.3.0 returns an element of DOMDocument type instead of SimpleXMLElement |
||
699 | * |
||
700 | * @param DOMDocument $doc Data tree for sitemap. |
||
701 | * @param string $last_modified Date of last modification. |
||
702 | */ |
||
703 | $tree = apply_filters( |
||
704 | 'jetpack_print_sitemap', |
||
705 | $buffer->get_document(), |
||
706 | $buffer->last_modified() |
||
707 | ); |
||
708 | |||
709 | // Store the buffer as the content of a sitemap row. |
||
710 | $this->librarian->store_sitemap_data( |
||
711 | $number, |
||
712 | JP_PAGE_SITEMAP_TYPE, |
||
713 | $buffer->contents(), |
||
714 | $buffer->last_modified() |
||
715 | ); |
||
716 | |||
717 | /* |
||
718 | * Now report back with the ID of the last post ID to be |
||
719 | * successfully added and whether there are any posts left. |
||
720 | */ |
||
721 | return array( |
||
722 | 'last_id' => $last_post_id, |
||
723 | 'any_left' => $any_posts_left, |
||
724 | 'last_modified' => $buffer->last_modified(), |
||
725 | ); |
||
726 | } |
||
727 | |||
728 | /** |
||
729 | * Build and store a single image sitemap. Returns false if no sitemap is built. |
||
730 | * |
||
731 | * Side effect: Create/update an image sitemap row. |
||
732 | * |
||
733 | * @access private |
||
734 | * @since 4.8.0 |
||
735 | * |
||
736 | * @param int $number The number of the current sitemap. |
||
737 | * @param int $from_id The greatest lower bound of the IDs of the posts to be included. |
||
738 | * |
||
739 | * @return bool|array @args { |
||
740 | * @type int $last_id The ID of the last item to be successfully added to the buffer. |
||
741 | * @type bool $any_left 'true' if there are items which haven't been saved to a sitemap, 'false' otherwise. |
||
742 | * @type string $last_modified The most recent timestamp to appear on the sitemap. |
||
743 | * } |
||
744 | */ |
||
745 | public function build_one_image_sitemap( $number, $from_id ) { |
||
746 | $last_post_id = $from_id; |
||
747 | $any_posts_left = true; |
||
748 | |||
749 | if ( $this->logger ) { |
||
750 | $debug_name = jp_sitemap_filename( JP_IMAGE_SITEMAP_TYPE, $number ); |
||
751 | $this->logger->report( "-- Building $debug_name" ); |
||
752 | } |
||
753 | |||
754 | $buffer = new Jetpack_Sitemap_Buffer_Image( |
||
755 | JP_SITEMAP_MAX_ITEMS, |
||
756 | JP_SITEMAP_MAX_BYTES |
||
757 | ); |
||
758 | |||
759 | // Add as many items to the buffer as possible. |
||
760 | while ( false === $buffer->is_full() ) { |
||
761 | $posts = $this->librarian->query_images_after_id( |
||
762 | $last_post_id, |
||
763 | JP_SITEMAP_BATCH_SIZE |
||
764 | ); |
||
765 | |||
766 | if ( null == $posts ) { // WPCS: loose comparison ok. |
||
767 | $any_posts_left = false; |
||
768 | break; |
||
769 | } |
||
770 | |||
771 | foreach ( $posts as $post ) { |
||
772 | $current_item = $this->image_post_to_sitemap_item( $post ); |
||
773 | |||
774 | if ( true === $buffer->append( $current_item['xml'] ) ) { |
||
775 | $last_post_id = $post->ID; |
||
776 | $buffer->view_time( $current_item['last_modified'] ); |
||
777 | } else { |
||
778 | break; |
||
779 | } |
||
780 | } |
||
781 | } |
||
782 | |||
783 | // If no items were added, return false. |
||
784 | if ( true === $buffer->is_empty() ) { |
||
785 | return false; |
||
786 | } |
||
787 | |||
788 | // Store the buffer as the content of a jp_sitemap post. |
||
789 | $this->librarian->store_sitemap_data( |
||
790 | $number, |
||
791 | JP_IMAGE_SITEMAP_TYPE, |
||
792 | $buffer->contents(), |
||
793 | $buffer->last_modified() |
||
794 | ); |
||
795 | |||
796 | /* |
||
797 | * Now report back with the ID of the last post to be |
||
798 | * successfully added and whether there are any posts left. |
||
799 | */ |
||
800 | return array( |
||
801 | 'last_id' => $last_post_id, |
||
802 | 'any_left' => $any_posts_left, |
||
803 | 'last_modified' => $buffer->last_modified(), |
||
804 | ); |
||
805 | } |
||
806 | |||
807 | /** |
||
808 | * Build and store a single video sitemap. Returns false if no sitemap is built. |
||
809 | * |
||
810 | * Side effect: Create/update an video sitemap row. |
||
811 | * |
||
812 | * @access private |
||
813 | * @since 4.8.0 |
||
814 | * |
||
815 | * @param int $number The number of the current sitemap. |
||
816 | * @param int $from_id The greatest lower bound of the IDs of the posts to be included. |
||
817 | * |
||
818 | * @return bool|array @args { |
||
819 | * @type int $last_id The ID of the last item to be successfully added to the buffer. |
||
820 | * @type bool $any_left 'true' if there are items which haven't been saved to a sitemap, 'false' otherwise. |
||
821 | * @type string $last_modified The most recent timestamp to appear on the sitemap. |
||
822 | * } |
||
823 | */ |
||
824 | public function build_one_video_sitemap( $number, $from_id ) { |
||
825 | $last_post_id = $from_id; |
||
826 | $any_posts_left = true; |
||
827 | |||
828 | if ( $this->logger ) { |
||
829 | $debug_name = jp_sitemap_filename( JP_VIDEO_SITEMAP_TYPE, $number ); |
||
830 | $this->logger->report( "-- Building $debug_name" ); |
||
831 | } |
||
832 | |||
833 | $buffer = new Jetpack_Sitemap_Buffer_Video( |
||
834 | JP_SITEMAP_MAX_ITEMS, |
||
835 | JP_SITEMAP_MAX_BYTES |
||
836 | ); |
||
837 | |||
838 | // Add as many items to the buffer as possible. |
||
839 | while ( false === $buffer->is_full() ) { |
||
840 | $posts = $this->librarian->query_videos_after_id( |
||
841 | $last_post_id, |
||
842 | JP_SITEMAP_BATCH_SIZE |
||
843 | ); |
||
844 | |||
845 | if ( null == $posts ) { // WPCS: loose comparison ok. |
||
846 | $any_posts_left = false; |
||
847 | break; |
||
848 | } |
||
849 | |||
850 | foreach ( $posts as $post ) { |
||
851 | $current_item = $this->video_post_to_sitemap_item( $post ); |
||
852 | |||
853 | if ( true === $buffer->append( $current_item['xml'] ) ) { |
||
854 | $last_post_id = $post->ID; |
||
855 | $buffer->view_time( $current_item['last_modified'] ); |
||
856 | } else { |
||
857 | break; |
||
858 | } |
||
859 | } |
||
860 | } |
||
861 | |||
862 | // If no items were added, return false. |
||
863 | if ( true === $buffer->is_empty() ) { |
||
864 | return false; |
||
865 | } |
||
866 | |||
867 | if ( false === $buffer->is_empty() ) { |
||
868 | $this->librarian->store_sitemap_data( |
||
869 | $number, |
||
870 | JP_VIDEO_SITEMAP_TYPE, |
||
871 | $buffer->contents(), |
||
872 | $buffer->last_modified() |
||
873 | ); |
||
874 | } |
||
875 | |||
876 | /* |
||
877 | * Now report back with the ID of the last post to be |
||
878 | * successfully added and whether there are any posts left. |
||
879 | */ |
||
880 | return array( |
||
881 | 'last_id' => $last_post_id, |
||
882 | 'any_left' => $any_posts_left, |
||
883 | 'last_modified' => $buffer->last_modified(), |
||
884 | ); |
||
885 | } |
||
886 | |||
887 | /** |
||
888 | * Build and store a single page sitemap index. Return false if no index is built. |
||
889 | * |
||
890 | * Side effect: Create/update a sitemap index row. |
||
891 | * |
||
892 | * @access private |
||
893 | * @since 4.8.0 |
||
894 | * |
||
895 | * @param int $number The number of the current sitemap index. |
||
896 | * @param int $from_id The greatest lower bound of the IDs of the sitemaps to be included. |
||
897 | * @param string $datetime Datetime of previous sitemap in 'YYYY-MM-DD hh:mm:ss' format. |
||
898 | * @param string $index_type Sitemap index type. |
||
899 | * |
||
900 | * @return bool|array @args { |
||
901 | * @type int $last_id The ID of the last item to be successfully added to the buffer. |
||
902 | * @type bool $any_left 'true' if there are items which haven't been saved to a sitemap, 'false' otherwise. |
||
903 | * @type string $last_modified The most recent timestamp to appear on the sitemap. |
||
904 | * } |
||
905 | */ |
||
906 | private function build_one_sitemap_index( $number, $from_id, $datetime, $index_type ) { |
||
907 | $last_sitemap_id = $from_id; |
||
908 | $any_sitemaps_left = true; |
||
909 | |||
910 | // Check the datetime format. |
||
911 | $datetime = jp_sitemap_datetime( $datetime ); |
||
912 | |||
913 | $sitemap_type = jp_sitemap_child_type_of( $index_type ); |
||
914 | |||
915 | if ( $this->logger ) { |
||
916 | $index_debug_name = jp_sitemap_filename( $index_type, $number ); |
||
917 | $this->logger->report( "-- Building $index_debug_name" ); |
||
918 | } |
||
919 | |||
920 | $buffer = new Jetpack_Sitemap_Buffer_Master( |
||
921 | JP_SITEMAP_MAX_ITEMS, |
||
922 | JP_SITEMAP_MAX_BYTES, |
||
923 | $datetime |
||
924 | ); |
||
925 | |||
926 | // Add pointer to the previous sitemap index (unless we're at the first one). |
||
927 | if ( 1 !== $number ) { |
||
928 | $i = $number - 1; |
||
929 | $prev_index_url = $this->finder->construct_sitemap_url( |
||
930 | jp_sitemap_filename( $index_type, $i ) |
||
931 | ); |
||
932 | |||
933 | $item_array = array( |
||
934 | 'sitemap' => array( |
||
935 | 'loc' => $prev_index_url, |
||
936 | 'lastmod' => $datetime, |
||
937 | ), |
||
938 | ); |
||
939 | |||
940 | $buffer->append( $item_array ); |
||
941 | } |
||
942 | |||
943 | // Add as many items to the buffer as possible. |
||
944 | while ( false === $buffer->is_full() ) { |
||
945 | // Retrieve a batch of posts (in order). |
||
946 | $posts = $this->librarian->query_sitemaps_after_id( |
||
947 | $sitemap_type, |
||
948 | $last_sitemap_id, |
||
949 | JP_SITEMAP_BATCH_SIZE |
||
950 | ); |
||
951 | |||
952 | // If there were no posts to get, make a note. |
||
953 | if ( null == $posts ) { // WPCS: loose comparison ok. |
||
954 | $any_sitemaps_left = false; |
||
955 | break; |
||
956 | } |
||
957 | |||
958 | // Otherwise, loop through each post in the batch. |
||
959 | foreach ( $posts as $post ) { |
||
960 | // Generate the sitemap XML for the post. |
||
961 | $current_item = $this->sitemap_row_to_index_item( (array) $post ); |
||
962 | |||
963 | // Try adding this item to the buffer. |
||
964 | if ( true === $buffer->append( $current_item['xml'] ) ) { |
||
965 | $last_sitemap_id = $post['ID']; |
||
966 | $buffer->view_time( $current_item['last_modified'] ); |
||
967 | } else { |
||
968 | // Otherwise stop looping through posts. |
||
969 | break; |
||
970 | } |
||
971 | } |
||
972 | } |
||
973 | |||
974 | // If no items were added, return false. |
||
975 | if ( true === $buffer->is_empty() ) { |
||
976 | return false; |
||
977 | } |
||
978 | |||
979 | $this->librarian->store_sitemap_data( |
||
980 | $number, |
||
981 | $index_type, |
||
982 | $buffer->contents(), |
||
983 | $buffer->last_modified() |
||
984 | ); |
||
985 | |||
986 | /* |
||
987 | * Now report back with the ID of the last sitemap post ID to |
||
988 | * be successfully added, whether there are any sitemap posts |
||
989 | * left, and the most recent modification time seen. |
||
990 | */ |
||
991 | return array( |
||
992 | 'last_id' => $last_sitemap_id, |
||
993 | 'any_left' => $any_sitemaps_left, |
||
994 | 'last_modified' => $buffer->last_modified(), |
||
995 | ); |
||
996 | } |
||
997 | |||
998 | /** |
||
999 | * Construct the sitemap index url entry for a sitemap row. |
||
1000 | * |
||
1001 | * @link http://www.sitemaps.org/protocol.html#sitemapIndex_sitemap |
||
1002 | * |
||
1003 | * @access private |
||
1004 | * @since 4.8.0 |
||
1005 | * |
||
1006 | * @param array $row The sitemap data to be processed. |
||
1007 | * |
||
1008 | * @return string An XML fragment representing the post URL. |
||
1009 | */ |
||
1010 | private function sitemap_row_to_index_item( $row ) { |
||
1011 | $url = $this->finder->construct_sitemap_url( $row['post_title'] ); |
||
1012 | |||
1013 | $item_array = array( |
||
1014 | 'sitemap' => array( |
||
1015 | 'loc' => $url, |
||
1016 | 'lastmod' => jp_sitemap_datetime( $row['post_date'] ), |
||
1017 | ), |
||
1018 | ); |
||
1019 | |||
1020 | return array( |
||
1021 | 'xml' => $item_array, |
||
1022 | 'last_modified' => $row['post_date'], |
||
1023 | ); |
||
1024 | } |
||
1025 | |||
1026 | |||
1027 | /** |
||
1028 | * This is served instead of a 404 when the master sitemap is requested |
||
1029 | * but not yet generated. |
||
1030 | * |
||
1031 | * @access public |
||
1032 | * @since 6.7.0 |
||
1033 | * |
||
1034 | * @return string The empty sitemap xml. |
||
1035 | */ |
||
1036 | public function empty_sitemap_xml() { |
||
1037 | $empty_sitemap = new Jetpack_Sitemap_Buffer_Empty(); |
||
1038 | return $empty_sitemap->contents(); |
||
1039 | } |
||
1040 | |||
1041 | /** |
||
1042 | * Build and return the news sitemap xml. Note that the result of this |
||
1043 | * function is cached in the transient 'jetpack_news_sitemap_xml'. |
||
1044 | * |
||
1045 | * @access public |
||
1046 | * @since 4.8.0 |
||
1047 | * |
||
1048 | * @return string The news sitemap xml. |
||
1049 | */ |
||
1050 | public function news_sitemap_xml() { |
||
1051 | $the_stored_news_sitemap = get_transient( 'jetpack_news_sitemap_xml' ); |
||
1052 | |||
1053 | if ( false === $the_stored_news_sitemap ) { |
||
1054 | |||
1055 | if ( $this->logger ) { |
||
1056 | $this->logger->report( 'Beginning news sitemap generation.' ); |
||
1057 | } |
||
1058 | |||
1059 | /** |
||
1060 | * Filter limit of entries to include in news sitemap. |
||
1061 | * |
||
1062 | * @module sitemaps |
||
1063 | * |
||
1064 | * @since 3.9.0 |
||
1065 | * |
||
1066 | * @param int $count Number of entries to include in news sitemap. |
||
1067 | */ |
||
1068 | $item_limit = apply_filters( |
||
1069 | 'jetpack_sitemap_news_sitemap_count', |
||
1070 | JP_NEWS_SITEMAP_MAX_ITEMS |
||
1071 | ); |
||
1072 | |||
1073 | $buffer = new Jetpack_Sitemap_Buffer_News( |
||
1074 | min( $item_limit, JP_NEWS_SITEMAP_MAX_ITEMS ), |
||
1075 | JP_SITEMAP_MAX_BYTES |
||
1076 | ); |
||
1077 | |||
1078 | $posts = $this->librarian->query_most_recent_posts( JP_NEWS_SITEMAP_MAX_ITEMS ); |
||
1079 | |||
1080 | foreach ( $posts as $post ) { |
||
1081 | $current_item = $this->post_to_news_sitemap_item( $post ); |
||
1082 | |||
1083 | if ( false === $buffer->append( $current_item['xml'] ) ) { |
||
1084 | break; |
||
1085 | } |
||
1086 | } |
||
1087 | |||
1088 | if ( $this->logger ) { |
||
1089 | $this->logger->time( 'End news sitemap generation.' ); |
||
1090 | } |
||
1091 | |||
1092 | $the_stored_news_sitemap = $buffer->contents(); |
||
1093 | |||
1094 | set_transient( |
||
1095 | 'jetpack_news_sitemap_xml', |
||
1096 | $the_stored_news_sitemap, |
||
1097 | JP_NEWS_SITEMAP_INTERVAL |
||
1098 | ); |
||
1099 | } // End if. |
||
1100 | |||
1101 | return $the_stored_news_sitemap; |
||
1102 | } |
||
1103 | |||
1104 | /** |
||
1105 | * Construct the sitemap url entry for a WP_Post. |
||
1106 | * |
||
1107 | * @link http://www.sitemaps.org/protocol.html#urldef |
||
1108 | * @access private |
||
1109 | * @since 4.8.0 |
||
1110 | * |
||
1111 | * @param WP_Post $post The post to be processed. |
||
1112 | * |
||
1113 | * @return array |
||
1114 | * @type array $xml An XML fragment representing the post URL. |
||
1115 | * @type string $last_modified Date post was last modified. |
||
1116 | */ |
||
1117 | private function post_to_sitemap_item( $post ) { |
||
1118 | |||
1119 | /** |
||
1120 | * Filter condition to allow skipping specific posts in sitemap. |
||
1121 | * |
||
1122 | * @module sitemaps |
||
1123 | * |
||
1124 | * @since 3.9.0 |
||
1125 | * |
||
1126 | * @param bool $skip Current boolean. False by default, so no post is skipped. |
||
1127 | * @param object $post Current post in the form of a $wpdb result object. Not WP_Post. |
||
1128 | */ |
||
1129 | if ( true === apply_filters( 'jetpack_sitemap_skip_post', false, $post ) ) { |
||
1130 | return array( |
||
1131 | 'xml' => null, |
||
1132 | 'last_modified' => null, |
||
1133 | ); |
||
1134 | } |
||
1135 | |||
1136 | $url = esc_url( get_permalink( $post ) ); |
||
1137 | |||
1138 | /* |
||
1139 | * Spec requires the URL to be <=2048 bytes. |
||
1140 | * In practice this constraint is unlikely to be violated. |
||
1141 | */ |
||
1142 | if ( 2048 < strlen( $url ) ) { |
||
1143 | $url = home_url() . '/?p=' . $post->ID; |
||
1144 | } |
||
1145 | |||
1146 | $last_modified = $post->post_modified_gmt; |
||
1147 | |||
1148 | // Check for more recent comments. |
||
1149 | // Note that 'Y-m-d h:i:s' strings sort lexicographically. |
||
1150 | if ( 0 < $post->comment_count ) { |
||
1151 | $last_modified = max( |
||
1152 | $last_modified, |
||
1153 | $this->librarian->query_latest_approved_comment_time_on_post( $post->ID ) |
||
1154 | ); |
||
1155 | } |
||
1156 | |||
1157 | $item_array = array( |
||
1158 | 'url' => array( |
||
1159 | 'loc' => $url, |
||
1160 | 'lastmod' => jp_sitemap_datetime( $last_modified ), |
||
1161 | ), |
||
1162 | ); |
||
1163 | |||
1164 | /** |
||
1165 | * Filter sitemap URL item before rendering it as XML. |
||
1166 | * |
||
1167 | * @module sitemaps |
||
1168 | * |
||
1169 | * @since 3.9.0 |
||
1170 | * |
||
1171 | * @param array $tree Associative array representing sitemap URL element. |
||
1172 | * @param int $post_id ID of the post being processed. |
||
1173 | */ |
||
1174 | $item_array = apply_filters( 'jetpack_sitemap_url', $item_array, $post->ID ); |
||
1175 | |||
1176 | return array( |
||
1177 | 'xml' => $item_array, |
||
1178 | 'last_modified' => $last_modified, |
||
1179 | ); |
||
1180 | } |
||
1181 | |||
1182 | /** |
||
1183 | * Construct the image sitemap url entry for a WP_Post of image type. |
||
1184 | * |
||
1185 | * @link http://www.sitemaps.org/protocol.html#urldef |
||
1186 | * |
||
1187 | * @access private |
||
1188 | * @since 4.8.0 |
||
1189 | * |
||
1190 | * @param WP_Post $post The image post to be processed. |
||
1191 | * |
||
1192 | * @return array |
||
1193 | * @type array $xml An XML fragment representing the post URL. |
||
1194 | * @type string $last_modified Date post was last modified. |
||
1195 | */ |
||
1196 | private function image_post_to_sitemap_item( $post ) { |
||
1197 | |||
1198 | /** |
||
1199 | * Filter condition to allow skipping specific image posts in the sitemap. |
||
1200 | * |
||
1201 | * @module sitemaps |
||
1202 | * |
||
1203 | * @since 4.8.0 |
||
1204 | * |
||
1205 | * @param bool $skip Current boolean. False by default, so no post is skipped. |
||
1206 | * @param WP_POST $post Current post object. |
||
1207 | */ |
||
1208 | if ( apply_filters( 'jetpack_sitemap_image_skip_post', false, $post ) ) { |
||
1209 | return array( |
||
1210 | 'xml' => null, |
||
1211 | 'last_modified' => null, |
||
1212 | ); |
||
1213 | } |
||
1214 | |||
1215 | $url = wp_get_attachment_url( $post->ID ); |
||
1216 | |||
1217 | // Do not include the image if the attached parent is not published. |
||
1218 | // Unattached will be published. Otherwise, will inherit parent status. |
||
1219 | if ( 'publish' !== get_post_status( $post ) ) { |
||
1220 | return array( |
||
1221 | 'xml' => null, |
||
1222 | 'last_modified' => null, |
||
1223 | ); |
||
1224 | } |
||
1225 | |||
1226 | $parent_url = get_permalink( get_post( $post->post_parent ) ); |
||
1227 | if ( '' == $parent_url ) { // WPCS: loose comparison ok. |
||
1228 | $parent_url = get_permalink( $post ); |
||
1229 | } |
||
1230 | |||
1231 | $item_array = array( |
||
1232 | 'url' => array( |
||
1233 | 'loc' => $parent_url, |
||
1234 | 'lastmod' => jp_sitemap_datetime( $post->post_modified_gmt ), |
||
1235 | 'image:image' => array( |
||
1236 | 'image:loc' => $url, |
||
1237 | ), |
||
1238 | ), |
||
1239 | ); |
||
1240 | |||
1241 | $item_array['url']['image:image']['image:title'] = $post->post_title; |
||
1242 | $item_array['url']['image:image']['image:caption'] = $post->post_excerpt; |
||
1243 | |||
1244 | /** |
||
1245 | * Filter associative array with data to build <url> node |
||
1246 | * and its descendants for current post in image sitemap. |
||
1247 | * |
||
1248 | * @module sitemaps |
||
1249 | * |
||
1250 | * @since 4.8.0 |
||
1251 | * |
||
1252 | * @param array $item_array Data to build parent and children nodes for current post. |
||
1253 | * @param int $post_id Current image post ID. |
||
1254 | */ |
||
1255 | $item_array = apply_filters( |
||
1256 | 'jetpack_sitemap_image_sitemap_item', |
||
1257 | $item_array, |
||
1258 | $post->ID |
||
1259 | ); |
||
1260 | |||
1261 | return array( |
||
1262 | 'xml' => $item_array, |
||
1263 | 'last_modified' => $post->post_modified_gmt, |
||
1264 | ); |
||
1265 | } |
||
1266 | |||
1267 | /** |
||
1268 | * Construct the video sitemap url entry for a WP_Post of video type. |
||
1269 | * |
||
1270 | * @link http://www.sitemaps.org/protocol.html#urldef |
||
1271 | * @link https://developers.google.com/webmasters/videosearch/sitemaps |
||
1272 | * |
||
1273 | * @access private |
||
1274 | * @since 4.8.0 |
||
1275 | * |
||
1276 | * @param WP_Post $post The video post to be processed. |
||
1277 | * |
||
1278 | * @return array |
||
1279 | * @type array $xml An XML fragment representing the post URL. |
||
1280 | * @type string $last_modified Date post was last modified. |
||
1281 | */ |
||
1282 | private function video_post_to_sitemap_item( $post ) { |
||
1283 | |||
1284 | /** |
||
1285 | * Filter condition to allow skipping specific image posts in the sitemap. |
||
1286 | * |
||
1287 | * @module sitemaps |
||
1288 | * |
||
1289 | * @since 4.8.0 |
||
1290 | * |
||
1291 | * @param bool $skip Current boolean. False by default, so no post is skipped. |
||
1292 | * @param WP_POST $post Current post object. |
||
1293 | */ |
||
1294 | if ( apply_filters( 'jetpack_sitemap_video_skip_post', false, $post ) ) { |
||
1295 | return array( |
||
1296 | 'xml' => null, |
||
1297 | 'last_modified' => null, |
||
1298 | ); |
||
1299 | } |
||
1300 | |||
1301 | // Do not include the video if the attached parent is not published. |
||
1302 | // Unattached will be published. Otherwise, will inherit parent status. |
||
1303 | if ( 'publish' !== get_post_status( $post ) ) { |
||
1304 | return array( |
||
1305 | 'xml' => null, |
||
1306 | 'last_modified' => null, |
||
1307 | ); |
||
1308 | } |
||
1309 | |||
1310 | $parent_url = esc_url( get_permalink( get_post( $post->post_parent ) ) ); |
||
1311 | if ( '' == $parent_url ) { // WPCS: loose comparison ok. |
||
1312 | $parent_url = esc_url( get_permalink( $post ) ); |
||
1313 | } |
||
1314 | |||
1315 | // Prepare the content like get_the_content_feed(). |
||
1316 | $content = $post->post_content; |
||
1317 | /** This filter is already documented in core/wp-includes/post-template.php */ |
||
1318 | $content = apply_filters( 'the_content', $content ); |
||
1319 | |||
1320 | /** This filter is already documented in core/wp-includes/feed.php */ |
||
1321 | $content = apply_filters( 'the_content_feed', $content, 'rss2' ); |
||
1322 | |||
1323 | // Include thumbnails for VideoPress videos, use blank image for others |
||
1324 | if ( 'complete' === get_post_meta( $post->ID, 'videopress_status', true ) && has_post_thumbnail( $post ) ) { |
||
1325 | $video_thumbnail_url = get_the_post_thumbnail_url( $post ); |
||
1326 | } else { |
||
1327 | * Filter the thumbnail image used in the video sitemap for non-VideoPress videos. |
||
|
|||
1328 | * |
||
1329 | * @since 7.2.0 |
||
1330 | * |
||
1331 | * @param string $str Image URL. |
||
1332 | */ |
||
1333 | $video_thumbnail_url = apply_filters( 'jetpack_video_sitemap_default_thumbnail', 'https://s0.wp.com/i/blank.jpg' ); |
||
1334 | } |
||
1335 | |||
1336 | $item_array = array( |
||
1337 | 'url' => array( |
||
1338 | 'loc' => $parent_url, |
||
1339 | 'lastmod' => jp_sitemap_datetime( $post->post_modified_gmt ), |
||
1340 | 'video:video' => array( |
||
1341 | /** This filter is already documented in core/wp-includes/feed.php */ |
||
1342 | 'video:title' => apply_filters( 'the_title_rss', $post->post_title ), |
||
1343 | 'video:thumbnail_loc' => $video_thumbnail_url, |
||
1344 | 'video:description' => $content, |
||
1345 | 'video:content_loc' => esc_url( wp_get_attachment_url( $post->ID ) ), |
||
1346 | ), |
||
1347 | ), |
||
1348 | ); |
||
1349 | |||
1350 | // TODO: Integrate with VideoPress here. |
||
1351 | // cf. video:player_loc tag in video sitemap spec. |
||
1352 | |||
1353 | /** |
||
1354 | * Filter associative array with data to build <url> node |
||
1355 | * and its descendants for current post in video sitemap. |
||
1356 | * |
||
1357 | * @module sitemaps |
||
1358 | * |
||
1359 | * @since 4.8.0 |
||
1360 | * |
||
1361 | * @param array $item_array Data to build parent and children nodes for current post. |
||
1362 | * @param int $post_id Current video post ID. |
||
1363 | */ |
||
1364 | $item_array = apply_filters( |
||
1365 | 'jetpack_sitemap_video_sitemap_item', |
||
1366 | $item_array, |
||
1367 | $post->ID |
||
1368 | ); |
||
1369 | |||
1370 | return array( |
||
1371 | 'xml' => $item_array, |
||
1372 | 'last_modified' => $post->post_modified_gmt, |
||
1373 | ); |
||
1374 | } |
||
1375 | |||
1376 | /** |
||
1377 | * Construct the news sitemap url entry for a WP_Post. |
||
1378 | * |
||
1379 | * @link http://www.sitemaps.org/protocol.html#urldef |
||
1380 | * |
||
1381 | * @access private |
||
1382 | * @since 4.8.0 |
||
1383 | * |
||
1384 | * @param WP_Post $post The post to be processed. |
||
1385 | * |
||
1386 | * @return string An XML fragment representing the post URL. |
||
1387 | */ |
||
1388 | private function post_to_news_sitemap_item( $post ) { |
||
1389 | |||
1390 | /** |
||
1391 | * Filter condition to allow skipping specific posts in news sitemap. |
||
1392 | * |
||
1393 | * @module sitemaps |
||
1394 | * |
||
1395 | * @since 3.9.0 |
||
1396 | * |
||
1397 | * @param bool $skip Current boolean. False by default, so no post is skipped. |
||
1398 | * @param WP_POST $post Current post object. |
||
1399 | */ |
||
1400 | if ( apply_filters( 'jetpack_sitemap_news_skip_post', false, $post ) ) { |
||
1401 | return array( |
||
1402 | 'xml' => null, |
||
1403 | ); |
||
1404 | } |
||
1405 | |||
1406 | $url = get_permalink( $post ); |
||
1407 | |||
1408 | /* |
||
1409 | * Spec requires the URL to be <=2048 bytes. |
||
1410 | * In practice this constraint is unlikely to be violated. |
||
1411 | */ |
||
1412 | if ( 2048 < strlen( $url ) ) { |
||
1413 | $url = home_url() . '/?p=' . $post->ID; |
||
1414 | } |
||
1415 | |||
1416 | /* |
||
1417 | * Trim the locale to an ISO 639 language code as required by Google. |
||
1418 | * Special cases are zh-cn (Simplified Chinese) and zh-tw (Traditional Chinese). |
||
1419 | * @link http://www.loc.gov/standards/iso639-2/php/code_list.php |
||
1420 | */ |
||
1421 | $language = strtolower( get_locale() ); |
||
1422 | |||
1423 | if ( in_array( $language, array( 'zh_tw', 'zh_cn' ), true ) ) { |
||
1424 | $language = str_replace( '_', '-', $language ); |
||
1425 | } else { |
||
1426 | $language = preg_replace( '/(_.*)$/i', '', $language ); |
||
1427 | } |
||
1428 | |||
1429 | $item_array = array( |
||
1430 | 'url' => array( |
||
1431 | 'loc' => $url, |
||
1432 | 'lastmod' => jp_sitemap_datetime( $post->post_modified_gmt ), |
||
1433 | 'news:news' => array( |
||
1434 | 'news:publication' => array( |
||
1435 | 'news:name' => html_entity_decode( get_bloginfo( 'name' ) ), |
||
1436 | 'news:language' => $language, |
||
1437 | ), |
||
1438 | /** This filter is already documented in core/wp-includes/feed.php */ |
||
1439 | 'news:title' => apply_filters( 'the_title_rss', $post->post_title ), |
||
1440 | 'news:publication_date' => jp_sitemap_datetime( $post->post_date_gmt ), |
||
1441 | 'news:genres' => 'Blog', |
||
1442 | ), |
||
1443 | ), |
||
1444 | ); |
||
1445 | |||
1446 | /** |
||
1447 | * Filter associative array with data to build <url> node |
||
1448 | * and its descendants for current post in news sitemap. |
||
1449 | * |
||
1450 | * @module sitemaps |
||
1451 | * |
||
1452 | * @since 3.9.0 |
||
1453 | * |
||
1454 | * @param array $item_array Data to build parent and children nodes for current post. |
||
1455 | * @param int $post_id Current post ID. |
||
1468 |