1<?php
2/**
3 * WordPress Export Administration API
4 *
5 * @package WordPress
6 * @subpackage Administration
7 */
8
9/**
10 * Version number for the export format.
11 *
12 * Bump this when something changes that might affect compatibility.
13 *
14 * @since 2.5.0
15 */
16define( 'WXR_VERSION', '1.2' );
17
18/**
19 * Generates the WXR export file for download.
20 *
21 * Default behavior is to export all content, however, note that post content will only
22 * be exported for post types with the `can_export` argument enabled. Any posts with the
23 * 'auto-draft' status will be skipped.
24 *
25 * @since 2.1.0
26 * @since 5.7.0 Added the `post_modified` and `post_modified_gmt` fields to the export file.
27 *
28 * @global wpdb $wpdb WordPress database abstraction object.
29 * @global WP_Post $post Global post object.
30 *
31 * @param array $args {
32 * Optional. Arguments for generating the WXR export file for download. Default empty array.
33 *
34 * @type string $content Type of content to export. If set, only the post content of this post type
35 * will be exported. Accepts 'all', 'post', 'page', 'attachment', or a defined
36 * custom post. If an invalid custom post type is supplied, every post type for
37 * which `can_export` is enabled will be exported instead. If a valid custom post
38 * type is supplied but `can_export` is disabled, then 'posts' will be exported
39 * instead. When 'all' is supplied, only post types with `can_export` enabled will
40 * be exported. Default 'all'.
41 * @type string $author Author to export content for. Only used when `$content` is 'post', 'page', or
42 * 'attachment'. Accepts false (all) or a specific author ID. Default false (all).
43 * @type string $category Category (slug) to export content for. Used only when `$content` is 'post'. If
44 * set, only post content assigned to `$category` will be exported. Accepts false
45 * or a specific category slug. Default is false (all categories).
46 * @type string $start_date Start date to export content from. Expected date format is 'Y-m-d'. Used only
47 * when `$content` is 'post', 'page' or 'attachment'. Default false (since the
48 * beginning of time).
49 * @type string $end_date End date to export content to. Expected date format is 'Y-m-d'. Used only when
50 * `$content` is 'post', 'page' or 'attachment'. Default false (latest publish date).
51 * @type string $status Post status to export posts for. Used only when `$content` is 'post' or 'page'.
52 * Accepts false (all statuses except 'auto-draft'), or a specific status, i.e.
53 * 'publish', 'pending', 'draft', 'auto-draft', 'future', 'private', 'inherit', or
54 * 'trash'. Default false (all statuses except 'auto-draft').
55 * }
56 */
57function export_wp( $args = array() ) {
58 global $wpdb, $post;
59
60 $defaults = array(
61 'content' => 'all',
62 'author' => false,
63 'category' => false,
64 'start_date' => false,
65 'end_date' => false,
66 'status' => false,
67 );
68 $args = wp_parse_args( $args, $defaults );
69
70 /**
71 * Fires at the beginning of an export, before any headers are sent.
72 *
73 * @since 2.3.0
74 *
75 * @param array $args An array of export arguments.
76 */
77 do_action( 'export_wp', $args );
78
79 $sitename = sanitize_key( get_bloginfo( 'name' ) );
80 if ( ! empty( $sitename ) ) {
81 $sitename .= '.';
82 }
83 $date = gmdate( 'Y-m-d' );
84 $wp_filename = $sitename . 'WordPress.' . $date . '.xml';
85 /**
86 * Filters the export filename.
87 *
88 * @since 4.4.0
89 *
90 * @param string $wp_filename The name of the file for download.
91 * @param string $sitename The site name.
92 * @param string $date Today's date, formatted.
93 */
94 $filename = apply_filters( 'export_wp_filename', $wp_filename, $sitename, $date );
95
96 header( 'Content-Description: File Transfer' );
97 header( 'Content-Disposition: attachment; filename=' . $filename );
98 header( 'Content-Type: text/xml; charset=' . get_option( 'blog_charset' ), true );
99
100 if ( 'all' !== $args['content'] && post_type_exists( $args['content'] ) ) {
101 $ptype = get_post_type_object( $args['content'] );
102 if ( ! $ptype->can_export ) {
103 $args['content'] = 'post';
104 }
105
106 $where = $wpdb->prepare( "{$wpdb->posts}.post_type = %s", $args['content'] );
107 } else {
108 $post_types = get_post_types( array( 'can_export' => true ) );
109 $esses = array_fill( 0, count( $post_types ), '%s' );
110
111 // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
112 $where = $wpdb->prepare( "{$wpdb->posts}.post_type IN (" . implode( ',', $esses ) . ')', $post_types );
113 }
114
115 if ( $args['status'] && ( 'post' === $args['content'] || 'page' === $args['content'] ) ) {
116 $where .= $wpdb->prepare( " AND {$wpdb->posts}.post_status = %s", $args['status'] );
117 } else {
118 $where .= " AND {$wpdb->posts}.post_status != 'auto-draft'";
119 }
120
121 $join = '';
122 if ( $args['category'] && 'post' === $args['content'] ) {
123 $term = term_exists( $args['category'], 'category' );
124 if ( $term ) {
125 $join = "INNER JOIN {$wpdb->term_relationships} ON ({$wpdb->posts}.ID = {$wpdb->term_relationships}.object_id)";
126 $where .= $wpdb->prepare( " AND {$wpdb->term_relationships}.term_taxonomy_id = %d", $term['term_taxonomy_id'] );
127 }
128 }
129
130 if ( in_array( $args['content'], array( 'post', 'page', 'attachment' ), true ) ) {
131 if ( $args['author'] ) {
132 $where .= $wpdb->prepare( " AND {$wpdb->posts}.post_author = %d", $args['author'] );
133 }
134
135 if ( $args['start_date'] ) {
136 $where .= $wpdb->prepare( " AND {$wpdb->posts}.post_date >= %s", gmdate( 'Y-m-d', strtotime( $args['start_date'] ) ) );
137 }
138
139 if ( $args['end_date'] ) {
140 $where .= $wpdb->prepare( " AND {$wpdb->posts}.post_date < %s", gmdate( 'Y-m-d', strtotime( '+1 month', strtotime( $args['end_date'] ) ) ) );
141 }
142 }
143
144 // Grab a snapshot of post IDs, just in case it changes during the export.
145 $post_ids = $wpdb->get_col( "SELECT ID FROM {$wpdb->posts} $join WHERE $where" );
146
147 // Get IDs for the attachments of each post, unless all content is already being exported.
148 if ( ! in_array( $args['content'], array( 'all', 'attachment' ), true ) ) {
149 // Array to hold all additional IDs (attachments and thumbnails).
150 $additional_ids = array();
151
152 // Create a copy of the post IDs array to avoid modifying the original array.
153 $processing_ids = $post_ids;
154
155 while ( $next_posts = array_splice( $processing_ids, 0, 20 ) ) {
156 $posts_in = array_map( 'absint', $next_posts );
157 $placeholders = array_fill( 0, count( $posts_in ), '%d' );
158
159 // Create a string for the placeholders.
160 $in_placeholder = implode( ',', $placeholders );
161
162 // Prepare the SQL statement for attachment ids.
163 $attachment_ids = $wpdb->get_col(
164 $wpdb->prepare(
165 "
166 SELECT ID
167 FROM $wpdb->posts
168 WHERE post_parent IN ($in_placeholder) AND post_type = 'attachment'
169 ",
170 $posts_in
171 )
172 );
173
174 $thumbnails_ids = $wpdb->get_col(
175 $wpdb->prepare(
176 "
177 SELECT meta_value
178 FROM $wpdb->postmeta
179 WHERE $wpdb->postmeta.post_id IN ($in_placeholder)
180 AND $wpdb->postmeta.meta_key = '_thumbnail_id'
181 ",
182 $posts_in
183 )
184 );
185
186 $additional_ids = array_merge( $additional_ids, $attachment_ids, $thumbnails_ids );
187 }
188
189 // Merge the additional IDs back with the original post IDs after processing all posts
190 $post_ids = array_unique( array_merge( $post_ids, $additional_ids ) );
191 }
192
193 /*
194 * Get the requested terms ready, empty unless posts filtered by category
195 * or all content.
196 */
197 $cats = array();
198 $tags = array();
199 $terms = array();
200 if ( isset( $term ) && $term ) {
201 $cat = get_term( $term['term_id'], 'category' );
202 $cats = array( $cat->term_id => $cat );
203 unset( $term, $cat );
204 } elseif ( 'all' === $args['content'] ) {
205 $categories = (array) get_categories( array( 'get' => 'all' ) );
206 $tags = (array) get_tags( array( 'get' => 'all' ) );
207
208 $custom_taxonomies = get_taxonomies( array( '_builtin' => false ) );
209 $custom_terms = (array) get_terms(
210 array(
211 'taxonomy' => $custom_taxonomies,
212 'get' => 'all',
213 )
214 );
215
216 // Put categories in order with no child going before its parent.
217 while ( $cat = array_shift( $categories ) ) {
218 if ( ! $cat->parent || isset( $cats[ $cat->parent ] ) ) {
219 $cats[ $cat->term_id ] = $cat;
220 } else {
221 $categories[] = $cat;
222 }
223 }
224
225 // Put terms in order with no child going before its parent.
226 while ( $t = array_shift( $custom_terms ) ) {
227 if ( ! $t->parent || isset( $terms[ $t->parent ] ) ) {
228 $terms[ $t->term_id ] = $t;
229 } else {
230 $custom_terms[] = $t;
231 }
232 }
233
234 unset( $categories, $custom_taxonomies, $custom_terms );
235 }
236
237 /**
238 * Wraps given string in XML CDATA tag.
239 *
240 * @since 2.1.0
241 *
242 * @param string|null $str String to wrap in XML CDATA tag. May be null.
243 * @return string
244 */
245 function wxr_cdata( $str ) {
246 $str = (string) $str;
247
248 if ( ! wp_is_valid_utf8( $str ) ) {
249 $str = utf8_encode( $str );
250 }
251 // $str = ent2ncr(esc_html($str));
252 $str = '<![CDATA[' . str_replace( ']]>', ']]]]><![CDATA[>', $str ) . ']]>';
253
254 return $str;
255 }
256
257 /**
258 * Returns the URL of the site.
259 *
260 * @since 2.5.0
261 *
262 * @return string Site URL.
263 */
264 function wxr_site_url() {
265 if ( is_multisite() ) {
266 // Multisite: the base URL.
267 return network_home_url();
268 } else {
269 // WordPress (single site): the site URL.
270 return get_bloginfo_rss( 'url' );
271 }
272 }
273
274 /**
275 * Outputs a cat_name XML tag from a given category object.
276 *
277 * @since 2.1.0
278 *
279 * @param WP_Term $category Category Object.
280 */
281 function wxr_cat_name( $category ) {
282 if ( empty( $category->name ) ) {
283 return;
284 }
285
286 echo '<wp:cat_name>' . wxr_cdata( $category->name ) . "</wp:cat_name>\n";
287 }
288
289 /**
290 * Outputs a category_description XML tag from a given category object.
291 *
292 * @since 2.1.0
293 *
294 * @param WP_Term $category Category Object.
295 */
296 function wxr_category_description( $category ) {
297 if ( empty( $category->description ) ) {
298 return;
299 }
300
301 echo '<wp:category_description>' . wxr_cdata( $category->description ) . "</wp:category_description>\n";
302 }
303
304 /**
305 * Outputs a tag_name XML tag from a given tag object.
306 *
307 * @since 2.3.0
308 *
309 * @param WP_Term $tag Tag Object.
310 */
311 function wxr_tag_name( $tag ) {
312 if ( empty( $tag->name ) ) {
313 return;
314 }
315
316 echo '<wp:tag_name>' . wxr_cdata( $tag->name ) . "</wp:tag_name>\n";
317 }
318
319 /**
320 * Outputs a tag_description XML tag from a given tag object.
321 *
322 * @since 2.3.0
323 *
324 * @param WP_Term $tag Tag Object.
325 */
326 function wxr_tag_description( $tag ) {
327 if ( empty( $tag->description ) ) {
328 return;
329 }
330
331 echo '<wp:tag_description>' . wxr_cdata( $tag->description ) . "</wp:tag_description>\n";
332 }
333
334 /**
335 * Outputs a term_name XML tag from a given term object.
336 *
337 * @since 2.9.0
338 *
339 * @param WP_Term $term Term Object.
340 */
341 function wxr_term_name( $term ) {
342 if ( empty( $term->name ) ) {
343 return;
344 }
345
346 echo '<wp:term_name>' . wxr_cdata( $term->name ) . "</wp:term_name>\n";
347 }
348
349 /**
350 * Outputs a term_description XML tag from a given term object.
351 *
352 * @since 2.9.0
353 *
354 * @param WP_Term $term Term Object.
355 */
356 function wxr_term_description( $term ) {
357 if ( empty( $term->description ) ) {
358 return;
359 }
360
361 echo "\t\t<wp:term_description>" . wxr_cdata( $term->description ) . "</wp:term_description>\n";
362 }
363
364 /**
365 * Outputs term meta XML tags for a given term object.
366 *
367 * @since 4.6.0
368 *
369 * @global wpdb $wpdb WordPress database abstraction object.
370 *
371 * @param WP_Term $term Term object.
372 */
373 function wxr_term_meta( $term ) {
374 global $wpdb;
375
376 $termmeta = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $wpdb->termmeta WHERE term_id = %d", $term->term_id ) );
377
378 foreach ( $termmeta as $meta ) {
379 /**
380 * Filters whether to selectively skip term meta used for WXR exports.
381 *
382 * Returning a truthy value from the filter will skip the current meta
383 * object from being exported.
384 *
385 * @since 4.6.0
386 *
387 * @param bool $skip Whether to skip the current piece of term meta. Default false.
388 * @param string $meta_key Current meta key.
389 * @param object $meta Current meta object.
390 */
391 if ( ! apply_filters( 'wxr_export_skip_termmeta', false, $meta->meta_key, $meta ) ) {
392 printf( "\t\t<wp:termmeta>\n\t\t\t<wp:meta_key>%s</wp:meta_key>\n\t\t\t<wp:meta_value>%s</wp:meta_value>\n\t\t</wp:termmeta>\n", wxr_cdata( $meta->meta_key ), wxr_cdata( $meta->meta_value ) );
393 }
394 }
395 }
396
397 /**
398 * Outputs list of authors with posts.
399 *
400 * @since 3.1.0
401 *
402 * @global wpdb $wpdb WordPress database abstraction object.
403 *
404 * @param int[] $post_ids Optional. Array of post IDs to filter the query by.
405 */
406 function wxr_authors_list( ?array $post_ids = null ) {
407 global $wpdb;
408
409 if ( ! empty( $post_ids ) ) {
410 $post_ids = array_map( 'absint', $post_ids );
411 $post_id_chunks = array_chunk( $post_ids, 20 );
412 } else {
413 $post_id_chunks = array( array() );
414 }
415
416 $authors = array();
417
418 foreach ( $post_id_chunks as $next_posts ) {
419 $and = ! empty( $next_posts ) ? 'AND ID IN (' . implode( ', ', $next_posts ) . ')' : '';
420
421 $results = $wpdb->get_results( "SELECT DISTINCT post_author FROM $wpdb->posts WHERE post_status != 'auto-draft' $and" );
422
423 foreach ( (array) $results as $result ) {
424 $authors[] = get_userdata( $result->post_author );
425 }
426 }
427
428 $authors = array_filter( $authors );
429 $authors = array_unique( $authors, SORT_REGULAR ); // Remove duplicate authors.
430
431 foreach ( $authors as $author ) {
432 echo "\t<wp:author>";
433 echo '<wp:author_id>' . (int) $author->ID . '</wp:author_id>';
434 echo '<wp:author_login>' . wxr_cdata( $author->user_login ) . '</wp:author_login>';
435 echo '<wp:author_email>' . wxr_cdata( $author->user_email ) . '</wp:author_email>';
436 echo '<wp:author_display_name>' . wxr_cdata( $author->display_name ) . '</wp:author_display_name>';
437 echo '<wp:author_first_name>' . wxr_cdata( $author->first_name ) . '</wp:author_first_name>';
438 echo '<wp:author_last_name>' . wxr_cdata( $author->last_name ) . '</wp:author_last_name>';
439 echo "</wp:author>\n";
440 }
441 }
442
443 /**
444 * Outputs all navigation menu terms.
445 *
446 * @since 3.1.0
447 */
448 function wxr_nav_menu_terms() {
449 $nav_menus = wp_get_nav_menus();
450 if ( empty( $nav_menus ) || ! is_array( $nav_menus ) ) {
451 return;
452 }
453
454 foreach ( $nav_menus as $menu ) {
455 echo "\t<wp:term>";
456 echo '<wp:term_id>' . (int) $menu->term_id . '</wp:term_id>';
457 echo '<wp:term_taxonomy>nav_menu</wp:term_taxonomy>';
458 echo '<wp:term_slug>' . wxr_cdata( $menu->slug ) . '</wp:term_slug>';
459 wxr_term_name( $menu );
460 echo "</wp:term>\n";
461 }
462 }
463
464 /**
465 * Outputs list of taxonomy terms, in XML tag format, associated with a post.
466 *
467 * @since 2.3.0
468 */
469 function wxr_post_taxonomy() {
470 $post = get_post();
471
472 $taxonomies = get_object_taxonomies( $post->post_type );
473 if ( empty( $taxonomies ) ) {
474 return;
475 }
476 $terms = wp_get_object_terms( $post->ID, $taxonomies );
477
478 foreach ( (array) $terms as $term ) {
479 echo "\t\t<category domain=\"{$term->taxonomy}\" nicename=\"{$term->slug}\">" . wxr_cdata( $term->name ) . "</category>\n";
480 }
481 }
482
483 /**
484 * Determines whether to selectively skip post meta used for WXR exports.
485 *
486 * @since 3.3.0
487 *
488 * @param bool $return_me Whether to skip the current post meta. Default false.
489 * @param string $meta_key Meta key.
490 * @return bool
491 */
492 function wxr_filter_postmeta( $return_me, $meta_key ) {
493 if ( '_edit_lock' === $meta_key ) {
494 $return_me = true;
495 }
496 return $return_me;
497 }
498 add_filter( 'wxr_export_skip_postmeta', 'wxr_filter_postmeta', 10, 2 );
499
500 echo '<?xml version="1.0" encoding="' . get_bloginfo( 'charset' ) . "\" ?>\n";
501
502 ?>
503<!-- This is a WordPress eXtended RSS file generated by WordPress as an export of your site. -->
504<!-- It contains information about your site's posts, pages, comments, categories, and other content. -->
505<!-- You may use this file to transfer that content from one site to another. -->
506<!-- This file is not intended to serve as a complete backup of your site. -->
507
508<!-- To import this information into a WordPress site follow these steps: -->
509<!-- 1. Log in to that site as an administrator. -->
510<!-- 2. Go to Tools: Import in the WordPress admin panel. -->
511<!-- 3. Install the "WordPress" importer from the list. -->
512<!-- 4. Activate & Run Importer. -->
513<!-- 5. Upload this file using the form provided on that page. -->
514<!-- 6. You will first be asked to map the authors in this export file to users -->
515<!-- on the site. For each author, you may choose to map to an -->
516<!-- existing user on the site or to create a new user. -->
517<!-- 7. WordPress will then import each of the posts, pages, comments, categories, etc. -->
518<!-- contained in this file into your site. -->
519
520 <?php the_generator( 'export' ); ?>
521<rss version="2.0"
522 xmlns:excerpt="http://wordpress.org/export/<?php echo WXR_VERSION; ?>/excerpt/"
523 xmlns:content="http://purl.org/rss/1.0/modules/content/"
524 xmlns:wfw="http://wellformedweb.org/CommentAPI/"
525 xmlns:dc="http://purl.org/dc/elements/1.1/"
526 xmlns:wp="http://wordpress.org/export/<?php echo WXR_VERSION; ?>/"
527>
528
529<channel>
530 <title><?php bloginfo_rss( 'name' ); ?></title>
531 <link><?php bloginfo_rss( 'url' ); ?></link>
532 <description><?php bloginfo_rss( 'description' ); ?></description>
533 <pubDate><?php echo gmdate( 'D, d M Y H:i:s +0000' ); ?></pubDate>
534 <language><?php bloginfo_rss( 'language' ); ?></language>
535 <wp:wxr_version><?php echo WXR_VERSION; ?></wp:wxr_version>
536 <wp:base_site_url><?php echo wxr_site_url(); ?></wp:base_site_url>
537 <wp:base_blog_url><?php bloginfo_rss( 'url' ); ?></wp:base_blog_url>
538
539 <?php wxr_authors_list( $post_ids ); ?>
540
541 <?php foreach ( $cats as $c ) : ?>
542 <wp:category>
543 <wp:term_id><?php echo (int) $c->term_id; ?></wp:term_id>
544 <wp:category_nicename><?php echo wxr_cdata( $c->slug ); ?></wp:category_nicename>
545 <wp:category_parent><?php echo wxr_cdata( $c->parent ? $cats[ $c->parent ]->slug : '' ); ?></wp:category_parent>
546 <?php
547 wxr_cat_name( $c );
548 wxr_category_description( $c );
549 wxr_term_meta( $c );
550 ?>
551 </wp:category>
552 <?php endforeach; ?>
553 <?php foreach ( $tags as $t ) : ?>
554 <wp:tag>
555 <wp:term_id><?php echo (int) $t->term_id; ?></wp:term_id>
556 <wp:tag_slug><?php echo wxr_cdata( $t->slug ); ?></wp:tag_slug>
557 <?php
558 wxr_tag_name( $t );
559 wxr_tag_description( $t );
560 wxr_term_meta( $t );
561 ?>
562 </wp:tag>
563 <?php endforeach; ?>
564 <?php foreach ( $terms as $t ) : ?>
565 <wp:term>
566 <wp:term_id><?php echo (int) $t->term_id; ?></wp:term_id>
567 <wp:term_taxonomy><?php echo wxr_cdata( $t->taxonomy ); ?></wp:term_taxonomy>
568 <wp:term_slug><?php echo wxr_cdata( $t->slug ); ?></wp:term_slug>
569 <wp:term_parent><?php echo wxr_cdata( $t->parent ? $terms[ $t->parent ]->slug : '' ); ?></wp:term_parent>
570 <?php
571 wxr_term_name( $t );
572 wxr_term_description( $t );
573 wxr_term_meta( $t );
574 ?>
575 </wp:term>
576 <?php endforeach; ?>
577 <?php
578 if ( 'all' === $args['content'] ) {
579 wxr_nav_menu_terms();
580 }
581 ?>
582
583 <?php
584 /** This action is documented in wp-includes/feed-rss2.php */
585 do_action( 'rss2_head' );
586 ?>
587
588 <?php
589 if ( $post_ids ) {
590 /**
591 * @global WP_Query $wp_query WordPress Query object.
592 */
593 global $wp_query;
594
595 // Fake being in the loop.
596 $wp_query->in_the_loop = true;
597
598 // Fetch 20 posts at a time rather than loading the entire table into memory.
599 while ( $next_posts = array_splice( $post_ids, 0, 20 ) ) {
600 $where = 'WHERE ID IN (' . implode( ',', $next_posts ) . ')';
601 $posts = $wpdb->get_results( "SELECT * FROM {$wpdb->posts} $where" );
602
603 // Begin Loop.
604 foreach ( $posts as $post ) {
605 setup_postdata( $post );
606
607 /**
608 * Filters the post title used for WXR exports.
609 *
610 * @since 5.7.0
611 *
612 * @param string $post_title Title of the current post.
613 */
614 $title = wxr_cdata( apply_filters( 'the_title_export', $post->post_title ) );
615
616 /**
617 * Filters the post content used for WXR exports.
618 *
619 * @since 2.5.0
620 *
621 * @param string $post_content Content of the current post.
622 */
623 $content = wxr_cdata( apply_filters( 'the_content_export', $post->post_content ) );
624
625 /**
626 * Filters the post excerpt used for WXR exports.
627 *
628 * @since 2.6.0
629 *
630 * @param string $post_excerpt Excerpt for the current post.
631 */
632 $excerpt = wxr_cdata( apply_filters( 'the_excerpt_export', $post->post_excerpt ) );
633
634 $is_sticky = is_sticky( $post->ID ) ? 1 : 0;
635 ?>
636 <item>
637 <title><?php echo $title; ?></title>
638 <link><?php the_permalink_rss(); ?></link>
639 <pubDate><?php echo mysql2date( 'D, d M Y H:i:s +0000', get_post_time( 'Y-m-d H:i:s', true ), false ); ?></pubDate>
640 <dc:creator><?php echo wxr_cdata( get_the_author_meta( 'login' ) ); ?></dc:creator>
641 <guid isPermaLink="false"><?php the_guid(); ?></guid>
642 <description></description>
643 <content:encoded><?php echo $content; ?></content:encoded>
644 <excerpt:encoded><?php echo $excerpt; ?></excerpt:encoded>
645 <wp:post_id><?php echo (int) $post->ID; ?></wp:post_id>
646 <wp:post_date><?php echo wxr_cdata( $post->post_date ); ?></wp:post_date>
647 <wp:post_date_gmt><?php echo wxr_cdata( $post->post_date_gmt ); ?></wp:post_date_gmt>
648 <wp:post_modified><?php echo wxr_cdata( $post->post_modified ); ?></wp:post_modified>
649 <wp:post_modified_gmt><?php echo wxr_cdata( $post->post_modified_gmt ); ?></wp:post_modified_gmt>
650 <wp:comment_status><?php echo wxr_cdata( $post->comment_status ); ?></wp:comment_status>
651 <wp:ping_status><?php echo wxr_cdata( $post->ping_status ); ?></wp:ping_status>
652 <wp:post_name><?php echo wxr_cdata( $post->post_name ); ?></wp:post_name>
653 <wp:status><?php echo wxr_cdata( $post->post_status ); ?></wp:status>
654 <wp:post_parent><?php echo (int) $post->post_parent; ?></wp:post_parent>
655 <wp:menu_order><?php echo (int) $post->menu_order; ?></wp:menu_order>
656 <wp:post_type><?php echo wxr_cdata( $post->post_type ); ?></wp:post_type>
657 <wp:post_password><?php echo wxr_cdata( $post->post_password ); ?></wp:post_password>
658 <wp:is_sticky><?php echo (int) $is_sticky; ?></wp:is_sticky>
659 <?php if ( 'attachment' === $post->post_type ) : ?>
660 <wp:attachment_url><?php echo wxr_cdata( wp_get_attachment_url( $post->ID ) ); ?></wp:attachment_url>
661 <?php endif; ?>
662 <?php wxr_post_taxonomy(); ?>
663 <?php
664 $postmeta = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $wpdb->postmeta WHERE post_id = %d", $post->ID ) );
665 foreach ( $postmeta as $meta ) :
666 /**
667 * Filters whether to selectively skip post meta used for WXR exports.
668 *
669 * Returning a truthy value from the filter will skip the current meta
670 * object from being exported.
671 *
672 * @since 3.3.0
673 *
674 * @param bool $skip Whether to skip the current post meta. Default false.
675 * @param string $meta_key Current meta key.
676 * @param object $meta Current meta object.
677 */
678 if ( apply_filters( 'wxr_export_skip_postmeta', false, $meta->meta_key, $meta ) ) {
679 continue;
680 }
681 ?>
682 <wp:postmeta>
683 <wp:meta_key><?php echo wxr_cdata( $meta->meta_key ); ?></wp:meta_key>
684 <wp:meta_value><?php echo wxr_cdata( $meta->meta_value ); ?></wp:meta_value>
685 </wp:postmeta>
686 <?php
687 endforeach;
688
689 $_comments = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $wpdb->comments WHERE comment_post_ID = %d AND comment_approved <> 'spam'", $post->ID ) );
690 $comments = array_map( 'get_comment', $_comments );
691 foreach ( $comments as $c ) :
692 ?>
693 <wp:comment>
694 <wp:comment_id><?php echo (int) $c->comment_ID; ?></wp:comment_id>
695 <wp:comment_author><?php echo wxr_cdata( $c->comment_author ); ?></wp:comment_author>
696 <wp:comment_author_email><?php echo wxr_cdata( $c->comment_author_email ); ?></wp:comment_author_email>
697 <wp:comment_author_url><?php echo sanitize_url( $c->comment_author_url ); ?></wp:comment_author_url>
698 <wp:comment_author_IP><?php echo wxr_cdata( $c->comment_author_IP ); ?></wp:comment_author_IP>
699 <wp:comment_date><?php echo wxr_cdata( $c->comment_date ); ?></wp:comment_date>
700 <wp:comment_date_gmt><?php echo wxr_cdata( $c->comment_date_gmt ); ?></wp:comment_date_gmt>
701 <wp:comment_content><?php echo wxr_cdata( $c->comment_content ); ?></wp:comment_content>
702 <wp:comment_approved><?php echo wxr_cdata( $c->comment_approved ); ?></wp:comment_approved>
703 <wp:comment_type><?php echo wxr_cdata( $c->comment_type ); ?></wp:comment_type>
704 <wp:comment_parent><?php echo (int) $c->comment_parent; ?></wp:comment_parent>
705 <wp:comment_user_id><?php echo (int) $c->user_id; ?></wp:comment_user_id>
706 <?php
707 $c_meta = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $wpdb->commentmeta WHERE comment_id = %d", $c->comment_ID ) );
708 foreach ( $c_meta as $meta ) :
709 /**
710 * Filters whether to selectively skip comment meta used for WXR exports.
711 *
712 * Returning a truthy value from the filter will skip the current meta
713 * object from being exported.
714 *
715 * @since 4.0.0
716 *
717 * @param bool $skip Whether to skip the current comment meta. Default false.
718 * @param string $meta_key Current meta key.
719 * @param object $meta Current meta object.
720 */
721 if ( apply_filters( 'wxr_export_skip_commentmeta', false, $meta->meta_key, $meta ) ) {
722 continue;
723 }
724 ?>
725 <wp:commentmeta>
726 <wp:meta_key><?php echo wxr_cdata( $meta->meta_key ); ?></wp:meta_key>
727 <wp:meta_value><?php echo wxr_cdata( $meta->meta_value ); ?></wp:meta_value>
728 </wp:commentmeta>
729 <?php endforeach; ?>
730 </wp:comment>
731 <?php endforeach; ?>
732 </item>
733 <?php
734 }
735 }
736 }
737 ?>
738</channel>
739</rss>
740 <?php
741}
742