Ticket #22435: export.5.diff
File export.5.diff, 54.2 KB (added by , 10 years ago) |
---|
-
new file src/wp-includes/Oxymel.php
diff --git a/src/wp-includes/Oxymel.php b/src/wp-includes/Oxymel.php new file mode 100644 index 0000000..857329e
- + 1 <?php 2 class Oxymel { 3 private $xml; 4 private $dom; 5 private $current_element; 6 private $last_inserted; 7 private $go_deep_on_next_element = 0; 8 private $go_up_on_next_element = 0; 9 private $nesting_level = 0; 10 private $contains_nesting_level = 0; 11 private $indentation= ' '; 12 13 public function __construct() { 14 $this->xml = ''; 15 $this->init_new_dom(); 16 } 17 18 public function to_string() { 19 return $this->xml .= $this->indent( $this->xml_from_dom(), $this->nesting_level ); 20 } 21 22 public function __call( $name, $args ) { 23 array_unshift( $args, $name ); 24 return call_user_func_array( array( $this, 'tag' ), $args ); 25 } 26 27 public function __get( $name ) { 28 return $this->$name(); 29 } 30 31 public function contains() { 32 $this->contains_nesting_level++; 33 $this->nesting_level++; 34 if ( $this->go_deep_on_next_element ) { 35 throw new OxymelException( 'contains cannot be used consecutively more than once' ); 36 } 37 $this->go_deep_on_next_element++; 38 return $this; 39 } 40 41 public function end() { 42 $this->contains_nesting_level--; 43 $this->nesting_level--; 44 if ( $this->contains_nesting_level < 0 ) { 45 throw new OxymelException( 'end is used without a matching contains' ); 46 } 47 $this->go_up_on_next_element++; 48 return $this; 49 } 50 51 public function tag( $name, $content_or_attributes = null, $attributes = array() ) { 52 list( $content, $attributes ) = $this->get_content_and_attributes_from_tag_args( $content_or_attributes, $attributes ); 53 $is_opening = 0 === strpos( $name, 'open_' ); 54 $is_closing = 0 === strpos( $name, 'close_' ); 55 $name = preg_replace("/^(open|close)_/", '', $name ); 56 57 $element = $this->create_element( $name, $content, $attributes ); 58 59 if ( !$is_opening && !$is_closing ) 60 $this->add_element_to_dom( $element ); 61 elseif ( $is_opening ) 62 $this->add_opening_tag_from_element( $element ); 63 elseif ( $is_closing ) 64 $this->add_closing_tag_from_tag_name( $name ); 65 66 return $this; 67 } 68 69 public function cdata( $text ) { 70 $this->add_element_to_dom( $this->dom->createCDATASection( $text ) ); 71 return $this; 72 } 73 74 public function text( $text ) { 75 $this->add_element_to_dom( $this->dom->createTextNode( $text ) ); 76 return $this; 77 } 78 79 public function comment( $text ) { 80 $this->add_element_to_dom( $this->dom->createComment( $text ) ); 81 return $this; 82 } 83 84 public function xml() { 85 $this->add_element_to_dom( $this->dom->createProcessingInstruction( 'xml', 'version="1.0" encoding="UTF-8"' ) ); 86 return $this; 87 } 88 89 public function oxymel( Oxymel $other ) { 90 foreach( $other->dom->childNodes as $child ) { 91 $child = $this->dom->importNode( $child, true ); 92 $this->add_element_to_dom( $child ); 93 } 94 return $this; 95 } 96 97 public function raw( $raw_xml ) { 98 if ( !$raw_xml ) { 99 return $this; 100 } 101 $fragment = $this->dom->createDocumentFragment(); 102 $fragment->appendXML($raw_xml); 103 $this->add_element_to_dom( $fragment ); 104 return $this; 105 } 106 107 private function add_element_to_dom( $element ) { 108 $this->move_current_element_deep(); 109 $this->move_current_element_up(); 110 $this->last_inserted = $this->current_element->appendChild($element); 111 } 112 113 private function move_current_element_deep() { 114 if ( $this->go_deep_on_next_element ) { 115 if ( !$this->last_inserted ) { 116 throw new OxymelException( 'contains has been used before adding any tags' ); 117 } 118 $this->current_element = $this->last_inserted; 119 $this->go_deep_on_next_element--; 120 } 121 } 122 123 private function move_current_element_up() { 124 if ( $this->go_up_on_next_element ) { 125 while ( $this->go_up_on_next_element ) { 126 $this->current_element = $this->current_element->parentNode; 127 $this->go_up_on_next_element--; 128 } 129 } 130 } 131 132 private function get_content_and_attributes_from_tag_args( $content_or_attributes, array $attributes ) { 133 $content = null; 134 if ( !$attributes ) { 135 if ( is_array( $content_or_attributes ) ) 136 $attributes = $content_or_attributes; 137 else 138 $content = $content_or_attributes; 139 } else { 140 $content = $content_or_attributes; 141 } 142 return array( $content, $attributes ); 143 } 144 145 private function init_new_dom() { 146 unset( $this->dom, $this->current_element ); 147 $this->dom = new DOMDocument(); 148 $this->dom->formatOutput = true; 149 $this->current_element = $this->dom; 150 $this->last_inserted = null; 151 } 152 153 private function xml_from_dom() { 154 if ( 0 !== $this->contains_nesting_level ) { 155 throw new OxymelException( 'contains and end calls do not match' ); 156 } 157 $xml = ''; 158 foreach( $this->dom->childNodes as $child ) { 159 $xml .= $this->dom->saveXML( $child ) . "\n"; 160 } 161 return $xml; 162 } 163 164 private function create_element( $name, $content, $attributes ) { 165 if ( !is_null( $content ) ) 166 $element = $this->dom->createElement( $name, $content ); 167 else 168 $element = $this->dom->createElement( $name ); 169 170 foreach( $attributes as $attribute_name => $attribute_value ) { 171 $element->setAttribute( $attribute_name, $attribute_value ); 172 } 173 174 return $element; 175 } 176 177 private function add_opening_tag_from_element( $element ) { 178 $this->xml .= $this->indent( $this->xml_from_dom(), $this->nesting_level ); 179 $tag = $this->dom->saveXML($element); 180 $this->xml .= $this->indent( str_replace( '/>', '>', $tag ) . "\n", $this->nesting_level ); 181 $this->nesting_level++; 182 $this->init_new_dom(); 183 } 184 185 private function add_closing_tag_from_tag_name( $name ) { 186 $this->xml .= $this->xml_from_dom(); 187 $this->nesting_level--; 188 if ( $this->nesting_level < 0 ) { 189 $this->xml = $this->indent( $this->xml, -$this->nesting_level ); 190 $this->nesting_level = 0; 191 } 192 $this->xml .= $this->indent( "</$name>\n", $this->nesting_level ); 193 $this->init_new_dom(); 194 } 195 196 private function indent( $string, $level ) { 197 if ( !$level ) { 198 return $string; 199 } 200 $lines = explode( "\n", $string ); 201 foreach( $lines as &$line ) { 202 if ( !trim( $line ) ) 203 continue; 204 $line = str_repeat( $this->indentation, $level ) . $line; 205 } 206 return implode( "\n", $lines ); 207 } 208 } 209 210 class OxymelException extends Exception { 211 } -
src/wp-includes/default-constants.php
diff --git a/src/wp-includes/default-constants.php b/src/wp-includes/default-constants.php index 734509a..dc5c117 100644
a b function wp_initial_constants() { 87 87 define( 'DAY_IN_SECONDS', 24 * HOUR_IN_SECONDS ); 88 88 define( 'WEEK_IN_SECONDS', 7 * DAY_IN_SECONDS ); 89 89 define( 'YEAR_IN_SECONDS', 365 * DAY_IN_SECONDS ); 90 91 // Constants for expressing human-readable data sizes 92 // in their respective number of bytes. 93 define( 'KB_IN_BYTES', 1024 ); 94 define( 'MB_IN_BYTES', 1024 * KB_IN_BYTES ); 95 define( 'GB_IN_BYTES', 1024 * MB_IN_BYTES ); 96 define( 'TB_IN_BYTES', 1024 * GB_IN_BYTES ); 97 define( 'PB_IN_BYTES', 1024 * TB_IN_BYTES ); 98 define( 'EB_IN_BYTES', 1024 * PB_IN_BYTES ); 99 define( 'ZB_IN_BYTES', 1024 * EB_IN_BYTES ); 100 define( 'YB_IN_BYTES', 1024 * ZB_IN_BYTES ); 90 101 } 91 102 92 103 /** -
new file src/wp-includes/export/class-wp-export-query.php
diff --git a/src/wp-includes/export/class-wp-export-query.php b/src/wp-includes/export/class-wp-export-query.php new file mode 100644 index 0000000..30de3fe
- + 1 <?php 2 /** 3 * Represents a set of posts and other site data to be exported. 4 * 5 * An immutable object, which gathers all data needed for the export. 6 */ 7 class WP_Export_Query { 8 const QUERY_CHUNK = 100; 9 10 private static $defaults = array( 11 'post_ids' => null, 12 'post_type' => null, 13 'status' => null, 14 'author' => null, 15 'start_date' => null, 16 'end_date' => null, 17 'category' => null, 18 ); 19 20 private $post_ids; 21 private $filters; 22 private $xml_gen; 23 24 private $wheres = array(); 25 private $joins = array(); 26 27 private $author; 28 private $category; 29 30 public $missing_parents = false; 31 32 public function __construct( $filters = array() ) { 33 $this->filters = wp_parse_args( $filters, self::$defaults ); 34 $this->post_ids = $this->calculate_post_ids(); 35 } 36 37 public function post_ids() { 38 return $this->post_ids; 39 } 40 41 public function charset() { 42 return get_bloginfo( 'charset' ); 43 } 44 45 public function site_metadata() { 46 $metadata = array( 47 'name' => $this->bloginfo_rss( 'name' ), 48 'url' => $this->bloginfo_rss( 'url' ), 49 'language' => $this->bloginfo_rss( 'language' ), 50 'description' => $this->bloginfo_rss( 'description' ), 51 'pubDate' => date( 'D, d M Y H:i:s +0000' ), 52 'site_url' => is_multisite()? network_home_url() : $this->bloginfo_rss( 'url' ), 53 'blog_url' => $this->bloginfo_rss( 'url' ), 54 ); 55 return $metadata; 56 } 57 58 public function wp_generator_tag() { 59 return apply_filters( 'the_generator', get_the_generator( 'export' ), 'export' ); 60 } 61 62 public function authors() { 63 global $wpdb; 64 $authors = array(); 65 $author_ids = $wpdb->get_col( "SELECT DISTINCT post_author FROM $wpdb->posts WHERE post_status != 'auto-draft'" ); 66 foreach ( (array) $author_ids as $author_id ) { 67 $authors[] = get_userdata( $author_id ); 68 } 69 $authors = array_filter( $authors ); 70 return $authors; 71 } 72 73 public function categories() { 74 if ( $this->category ) { 75 return array( $this->category ); 76 } 77 if ( $this->filters['post_type'] ) { 78 return array(); 79 } 80 $categories = (array) get_categories( array( 'get' => 'all' ) ); 81 82 $this->check_for_orphaned_terms( $categories ); 83 84 $categories = self::topologically_sort_terms( $categories ); 85 86 return $categories; 87 } 88 89 public function tags() { 90 if ( $this->filters['post_type'] ) { 91 return array(); 92 } 93 $tags = (array) get_tags( array( 'get' => 'all' ) ); 94 95 $this->check_for_orphaned_terms( $tags ); 96 97 return $tags; 98 } 99 100 public function custom_taxonomies_terms() { 101 if ( $this->filters['post_type'] ) { 102 return array(); 103 } 104 $custom_taxonomies = get_taxonomies( array( '_builtin' => false ) ); 105 $custom_terms = (array) get_terms( $custom_taxonomies, array( 'get' => 'all' ) ); 106 $this->check_for_orphaned_terms( $custom_terms ); 107 $custom_terms = self::topologically_sort_terms( $custom_terms ); 108 return $custom_terms; 109 } 110 111 public function nav_menu_terms() { 112 $nav_menus = wp_get_nav_menus(); 113 foreach( $nav_menus as &$term ) { 114 $term->description = ''; 115 } 116 return $nav_menus; 117 } 118 119 public function exportify_post( $post ) { 120 $GLOBALS['wp_query']->in_the_loop = true; 121 $previous_global_post = isset( $GLOBALS['post'] )? $GLOBALS['post'] : null; 122 $GLOBALS['post'] = $post; 123 setup_postdata( $post ); 124 $post->post_content = apply_filters( 'the_content_export', $post->post_content ); 125 $post->post_excerpt = apply_filters( 'the_excerpt_export', $post->post_excerpt ); 126 $post->is_sticky = is_sticky( $post->ID ) ? 1 : 0; 127 $post->terms = self::get_terms_for_post( $post ); 128 $post->meta = self::get_meta_for_post( $post ); 129 $post->comments = self::get_comments_for_post( $post ); 130 $GLOBALS['post'] = $previous_global_post; 131 return $post; 132 } 133 134 public function posts() { 135 $posts_iterator = new WP_Post_IDs_Iterator( $this->post_ids, self::QUERY_CHUNK ); 136 return new WP_Map_Iterator( $posts_iterator, array( $this, 'exportify_post' ) ); 137 } 138 139 private function calculate_post_ids() { 140 global $wpdb; 141 if ( is_array( $this->filters['post_ids'] ) ) { 142 return $this->filters['post_ids']; 143 } 144 $this->post_type_where(); 145 $this->status_where(); 146 $this->author_where(); 147 $this->start_date_where(); 148 $this->end_date_where(); 149 $this->category_where(); 150 151 $where = implode( ' AND ', array_filter( $this->wheres ) ); 152 if ( $where ) $where = "WHERE $where"; 153 $join = implode( ' ', array_filter( $this->joins ) ); 154 155 $post_ids = $wpdb->get_col( "SELECT ID FROM {$wpdb->posts} AS p $join $where" ); 156 $post_ids = array_merge( $post_ids, $this->attachments_for_specific_post_types( $post_ids ) ); 157 return $post_ids; 158 } 159 160 private function post_type_where() { 161 global $wpdb; 162 $post_types_filters = array( 'can_export' => true ); 163 if ( $this->filters['post_type'] ) { 164 $post_types_filters = array_merge( $post_types_filters, array( 'name' => $this->filters['post_type'] ) ); 165 } 166 $post_types = get_post_types( $post_types_filters ); 167 if ( !$post_types ) { 168 $this->wheres[] = 'p.post_type IS NULL'; 169 return; 170 } 171 $this->wheres[] = $wpdb->build_IN_condition( 'p.post_type', $post_types ); 172 } 173 174 private function status_where() { 175 global $wpdb; 176 if ( !$this->filters['status'] ) { 177 $this->wheres[] = "p.post_status != 'auto-draft'"; 178 return; 179 } 180 $this->wheres[] = $wpdb->prepare( 'p.post_status = %s', $this->filters['status'] ); 181 } 182 183 private function author_where() { 184 global $wpdb; 185 $user = $this->find_user_from_any_object( $this->filters['author'] ); 186 if ( !$user || is_wp_error( $user ) ) { 187 return; 188 } 189 $this->author = $user; 190 $this->wheres[] = $wpdb->prepare( 'p.post_author = %d', $user->ID ); 191 } 192 193 private function start_date_where() { 194 global $wpdb; 195 $timestamp = strtotime( $this->filters['start_date'] ); 196 if ( !$timestamp ) { 197 return; 198 } 199 $this->wheres[] = $wpdb->prepare( 'p.post_date >= %s', date( 'Y-m-d 00:00:00', $timestamp ) ); 200 } 201 202 private function end_date_where() { 203 global $wpdb; 204 $timestamp = strtotime( $this->filters['end_date'] ); 205 if ( !$timestamp ) { 206 return; 207 } 208 $this->wheres[] = $wpdb->prepare( 'p.post_date <= %s', date( 'Y-m-d 23:59:59', $timestamp ) ); 209 } 210 211 private function category_where() { 212 global $wpdb; 213 if ( 'post' != $this->filters['post_type'] ) { 214 return; 215 } 216 $category = $this->find_category_from_any_object( $this->filters['category'] ); 217 if ( !$category ) { 218 return; 219 } 220 $this->category = $category; 221 $this->joins[] = "INNER JOIN {$wpdb->term_relationships} AS tr ON (p.ID = tr.object_id)"; 222 $this->wheres[] = $wpdb->prepare( 'tr.term_taxonomy_id = %d', $category->term_taxonomy_id ); 223 } 224 225 private function attachments_for_specific_post_types( $post_ids ) { 226 global $wpdb; 227 if ( !$this->filters['post_type'] ) { 228 return array(); 229 } 230 $attachment_ids = array(); 231 while ( $batch_of_post_ids = array_splice( $post_ids, 0, self::QUERY_CHUNK ) ) { 232 $post_parent_condition = $wpdb->build_IN_condition( 'post_parent', $batch_of_post_ids ); 233 $attachment_ids = array_merge( $attachment_ids, (array)$wpdb->get_col( "SELECT ID FROM {$wpdb->posts} WHERE post_type = 'attachment' AND $post_parent_condition" ) ); 234 } 235 return array_map( 'intval', $attachment_ids ); 236 } 237 238 private function bloginfo_rss( $section ) { 239 return apply_filters( 'bloginfo_rss', get_bloginfo_rss( $section ), $section ); 240 } 241 242 private function find_user_from_any_object( $user ) { 243 if ( is_numeric( $user ) ) { 244 return get_user_by( 'id', $user ); 245 } elseif ( is_string( $user ) ) { 246 return get_user_by( 'login', $user ); 247 } elseif ( isset( $user->ID ) ) { 248 return get_user_by( 'id', $user->ID ); 249 } 250 return false; 251 } 252 253 private function find_category_from_any_object( $category ) { 254 if ( is_numeric( $category ) ) { 255 return get_term( $category, 'category' ); 256 } elseif ( is_string( $category ) ) { 257 $term = term_exists( $category, 'category' ); 258 return isset( $term['term_id'] )? get_term( $term['term_id'], 'category' ) : false; 259 } elseif ( isset( $category->term_id ) ) { 260 return get_term( $category->term_id, 'category' ); 261 } 262 return false; 263 } 264 265 private static function topologically_sort_terms( $terms ) { 266 $sorted = array(); 267 while ( $term = array_shift( $terms ) ) { 268 if ( $term->parent == 0 || isset( $sorted[$term->parent] ) ) 269 $sorted[$term->term_id] = $term; 270 else 271 $terms[] = $term; 272 } 273 return $sorted; 274 } 275 276 private function check_for_orphaned_terms( $terms ) { 277 $term_ids = array(); 278 $have_parent = array(); 279 280 foreach ( $terms as $term ) { 281 $term_ids[ $term->term_id ] = true; 282 if ( $term->parent != 0 ) 283 $have_parent[] = $term; 284 } 285 286 foreach ( $have_parent as $has_parent ) { 287 if ( ! isset( $term_ids[ $has_parent->parent ] ) ) { 288 $this->missing_parents = $has_parent; 289 throw new WP_Export_Term_Exception( __( 'Term is missing a parent.' ) ); 290 } 291 } 292 } 293 294 private static function get_terms_for_post( $post ) { 295 $taxonomies = get_object_taxonomies( $post->post_type ); 296 if ( empty( $taxonomies ) ) 297 return array(); 298 $terms = wp_get_object_terms( $post->ID, $taxonomies ); 299 $terms = $terms? $terms : array(); 300 return $terms; 301 } 302 303 private static function get_meta_for_post( $post ) { 304 global $wpdb; 305 $meta_for_export = array(); 306 $meta_from_db = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $wpdb->postmeta WHERE post_id = %d", $post->ID ) ); 307 foreach ( $meta_from_db as $meta ) { 308 if ( apply_filters( 'wxr_export_skip_postmeta', false, $meta->meta_key, $meta ) ) 309 continue; 310 $meta_for_export[] = $meta; 311 } 312 return $meta_for_export; 313 } 314 315 private static function get_comments_for_post( $post ) { 316 global $wpdb; 317 $comments = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $wpdb->comments WHERE comment_post_ID = %d AND comment_approved <> 'spam'", $post->ID ) ); 318 foreach( $comments as $comment ) { 319 $meta = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $wpdb->commentmeta WHERE comment_id = %d", $comment->comment_ID ) ); 320 $meta = $meta? $meta : array(); 321 $comment->meta = $meta; 322 } 323 return $comments; 324 } 325 } 326 327 class WP_Export_Exception extends RuntimeException { 328 } 329 330 class WP_Export_Term_Exception extends RuntimeException { 331 } -
new file src/wp-includes/export/class-wp-export-wxr-formatter.php
diff --git a/src/wp-includes/export/class-wp-export-wxr-formatter.php b/src/wp-includes/export/class-wp-export-wxr-formatter.php new file mode 100644 index 0000000..073c271
- + 1 <?php 2 /** 3 * Version number for the export format. 4 * 5 * Bump this when something changes that might affect compatibility. 6 * 7 * @since 2.5.0 8 */ 9 define( 'WXR_VERSION', '1.2' ); 10 11 require_once ABSPATH . WPINC . '/Oxymel.php'; 12 13 class WP_Export_Oxymel extends Oxymel { 14 public function optional( $tag_name, $contents ) { 15 if ( $contents ) { 16 $this->$tag_name( $contents ); 17 } 18 return $this; 19 } 20 21 public function optional_cdata( $tag_name, $contents ) { 22 if ( $contents ) { 23 $this->$tag_name->contains->cdata( $contents )->end; 24 } 25 return $this; 26 } 27 } 28 29 /** 30 * Responsible for formatting the data in WP_Export_Query to WXR 31 */ 32 class WP_Export_WXR_Formatter { 33 function __construct( $export ) { 34 $this->export = $export; 35 $this->wxr_version = WXR_VERSION; 36 } 37 38 function before_posts() { 39 $before_posts_xml = ''; 40 $before_posts_xml .= $this->header(); 41 $before_posts_xml .= $this->site_metadata(); 42 $before_posts_xml .= $this->authors(); 43 $before_posts_xml .= $this->categories(); 44 $before_posts_xml .= $this->tags(); 45 $before_posts_xml .= $this->nav_menu_terms(); 46 $before_posts_xml .= $this->custom_taxonomies_terms(); 47 $before_posts_xml .= $this->rss2_head_action(); 48 return $before_posts_xml; 49 } 50 51 function posts() { 52 return new WP_Map_Iterator( $this->export->posts(), array( $this, 'post' ) ); 53 } 54 55 function after_posts() { 56 return $this->footer(); 57 } 58 59 function header() { 60 $oxymel = new Oxymel; 61 $charset = $this->export->charset(); 62 $wp_generator_tag = $this->export->wp_generator_tag(); 63 $comment = <<<COMMENT 64 65 This is a WordPress eXtended RSS file generated by WordPress as an export of your site. 66 It contains information about your site's posts, pages, comments, categories, and other content. 67 You may use this file to transfer that content from one site to another. 68 This file is not intended to serve as a complete backup of your site. 69 70 To import this information into a WordPress site follow these steps: 71 1. Log in to that site as an administrator. 72 2. Go to Tools: Import in the WordPress admin panel. 73 3. Install the "WordPress" importer from the list. 74 4. Activate & Run Importer. 75 5. Upload this file using the form provided on that page. 76 6. You will first be asked to map the authors in this export file to users 77 on the site. For each author, you may choose to map to an 78 existing user on the site or to create a new user. 79 7. WordPress will then import each of the posts, pages, comments, categories, etc. 80 contained in this file into your site. 81 82 COMMENT; 83 return $oxymel 84 ->xml 85 ->comment( $comment ) 86 ->raw( $wp_generator_tag ) 87 ->open_rss( array( 88 'version' => '2.0', 89 'xmlns:excerpt' => "http://wordpress.org/export/{$this->wxr_version}/excerpt/", 90 'xmlns:content' => "http://purl.org/rss/1.0/modules/content/", 91 'xmlns:wfw' => "http://wellformedweb.org/CommentAPI/", 92 'xmlns:dc' => "http://purl.org/dc/elements/1.1/", 93 'xmlns:wp' => "http://wordpress.org/export/{$this->wxr_version}/", 94 ) ) 95 ->open_channel 96 ->to_string(); 97 98 } 99 100 function site_metadata() { 101 $oxymel = new Oxymel; 102 $metadata = $this->export->site_metadata(); 103 return $oxymel 104 ->title( $metadata['name'] ) 105 ->link( $metadata['url'] ) 106 ->description( $metadata['description'] ) 107 ->pubDate( $metadata['pubDate'] ) 108 ->language( $metadata['language'] ) 109 ->tag( 'wp:wxr_version', $this->wxr_version ) 110 ->tag( 'wp:base_site_url', $metadata['site_url'] ) 111 ->tag( 'wp:base_blog_url', $metadata['blog_url'] ) 112 ->to_string(); 113 } 114 115 function authors() { 116 $oxymel = new Oxymel; 117 $authors = $this->export->authors(); 118 foreach ( $authors as $author ) { 119 $oxymel 120 ->tag( 'wp:author' )->contains 121 ->tag( 'wp:author_login', $author->user_login ) 122 ->tag( 'wp:author_email', $author->user_email ) 123 ->tag( 'wp:author_display_name' )->contains->cdata( $author->display_name )->end 124 ->tag( 'wp:author_first_name' )->contains->cdata( $author->user_first_name )->end 125 ->tag( 'wp:author_last_name' )->contains->cdata( $author->user_last_name )->end 126 ->end; 127 } 128 return $oxymel->to_string(); 129 } 130 131 function categories() { 132 $oxymel = new WP_Export_Oxymel; 133 $categories = $this->export->categories(); 134 foreach( $categories as $term_id => $category ) { 135 $category->parent_slug = $category->parent? $categories[$category->parent]->slug : ''; 136 $oxymel->tag( 'wp:category' )->contains 137 ->tag( 'wp:term_id', $category->term_id ) 138 ->tag( 'wp:category_nicename', $category->slug ) 139 ->tag( 'wp:category_parent', $category->parent_slug ) 140 ->optional_cdata( 'wp:cat_name', $category->name ) 141 ->optional_cdata( 'wp:category_description', $category->description ) 142 ->end; 143 } 144 return $oxymel->to_string(); 145 } 146 147 function tags() { 148 $oxymel = new WP_Export_Oxymel; 149 $tags = $this->export->tags(); 150 foreach( $tags as $tag ) { 151 $oxymel->tag( 'wp:tag' )->contains 152 ->tag( 'wp:term_id', $tag->term_id ) 153 ->tag( 'wp:tag_slug', $tag->slug ) 154 ->optional_cdata( 'wp:tag_name', $tag->name ) 155 ->optional_cdata( 'wp:tag_description', $tag->description ) 156 ->end; 157 } 158 return $oxymel->to_string(); 159 } 160 161 function nav_menu_terms() { 162 return $this->terms( $this->export->nav_menu_terms() ); 163 } 164 165 function custom_taxonomies_terms() { 166 return $this->terms( $this->export->custom_taxonomies_terms() ); 167 } 168 169 function rss2_head_action() { 170 ob_start(); 171 do_action( 'rss2_head' ); 172 $action_output = ob_get_clean(); 173 return $action_output; 174 } 175 176 private function terms( $terms ) { 177 $oxymel = new WP_Export_Oxymel; 178 foreach( $terms as $term ) { 179 $term->parent_slug = $term->parent? $terms[$term->parent]->slug : ''; 180 $oxymel->tag( 'wp:term' )->contains 181 ->tag( 'wp:term_id', $term->term_id ) 182 ->tag( 'wp:term_taxonomy', $term->taxonomy ) 183 ->tag( 'wp:term_slug', $term->slug ); 184 if ( 'nav_menu' != $term->taxonomy ) { 185 $oxymel 186 ->tag( 'wp:term_parent', $term->parent_slug ); 187 } 188 $oxymel 189 ->optional_cdata( 'wp:term_name', $term->name ) 190 ->optional_cdata( 'wp:term_description', $term->description ) 191 ->end; 192 } 193 return $oxymel->to_string(); 194 } 195 196 function post( $post ) { 197 $oxymel = new WP_Export_Oxymel; 198 $GLOBALS['wp_query']->in_the_loop = true; 199 $GLOBALS['post'] = $post; 200 setup_postdata( $post ); 201 202 $oxymel->item->contains 203 ->title( apply_filters( 'the_title_rss', $post->post_title ) ) 204 ->link( apply_filters('the_permalink_rss', get_permalink() ) ) 205 ->pubDate( mysql2date( 'D, d M Y H:i:s +0000', get_post_time( 'Y-m-d H:i:s', true ), false ) ) 206 ->tag( 'dc:creator', get_the_author_meta( 'login' ) ) 207 ->guid( get_the_guid(), array( 'isPermaLink' => 'false' ) ) 208 ->description( '' ) 209 ->tag( 'content:encoded' )->contains->cdata( $post->post_content )->end 210 ->tag( 'excerpt:encoded' )->contains->cdata( $post->post_excerpt )->end 211 ->tag( 'wp:post_id', $post->ID ) 212 ->tag( 'wp:post_date', $post->post_date ) 213 ->tag( 'wp:post_date_gmt', $post->post_date_gmt ) 214 ->tag( 'wp:comment_status', $post->comment_status ) 215 ->tag( 'wp:ping_status', $post->ping_status ) 216 ->tag( 'wp:post_name', $post->post_name ) 217 ->tag( 'wp:status', $post->post_status ) 218 ->tag( 'wp:post_parent', $post->post_parent ) 219 ->tag( 'wp:menu_order', $post->menu_order ) 220 ->tag( 'wp:post_type', $post->post_type ) 221 ->tag( 'wp:post_password', $post->post_password ) 222 ->tag( 'wp:is_sticky', $post->is_sticky ) 223 ->optional( 'wp:attachment_url', wp_get_attachment_url( $post->ID ) ); 224 foreach( $post->terms as $term ) { 225 $oxymel 226 ->category( array( 'domain' => $term->taxonomy, 'nicename' => $term->slug ) )->contains->cdata( $term->name )->end; 227 } 228 foreach( $post->meta as $meta ) { 229 $oxymel 230 ->tag( 'wp:postmeta' )->contains 231 ->tag( 'wp:meta_key', $meta->meta_key ) 232 ->tag( 'wp:meta_value' )->contains->cdata( $meta->meta_value )->end 233 ->end; 234 } 235 foreach( $post->comments as $comment ) { 236 $oxymel 237 ->tag( 'wp:comment' )->contains 238 ->tag( 'wp:comment_id', $comment->comment_ID ) 239 ->tag( 'wp:comment_author' )->contains->cdata( $comment->comment_author )->end 240 ->tag( 'wp:comment_author_email', $comment->comment_author_email ) 241 ->tag( 'wp:comment_author_url', $comment->comment_author_url ) 242 ->tag( 'wp:comment_author_IP', $comment->comment_author_IP ) 243 ->tag( 'wp:comment_date', $comment->comment_date ) 244 ->tag( 'wp:comment_date_gmt', $comment->comment_date_gmt ) 245 ->tag( 'wp:comment_content' )->contains->cdata( $comment->comment_content )->end 246 ->tag( 'wp:comment_approved', $comment->comment_approved ) 247 ->tag( 'wp:comment_type', $comment->comment_type ) 248 ->tag( 'wp:comment_parent', $comment->comment_parent ) 249 ->tag( 'wp:comment_user_id', $comment->user_id ) 250 ->oxymel( $this->comment_meta( $comment ) ) 251 ->end; 252 } 253 $oxymel 254 ->end; 255 return $oxymel->to_string(); 256 } 257 258 private function comment_meta( $comment ) { 259 global $wpdb; 260 $metas = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $wpdb->commentmeta WHERE comment_id = %d", $comment->comment_ID ) ); 261 if ( !$metas ) { 262 return new Oxymel; 263 } 264 $oxymel = new WP_Export_Oxymel; 265 foreach( $metas as $meta ) { 266 $oxymel->tag( 'wp:commentmeta' )->contains 267 ->tag( 'wp:meta_key', $meta->meta_key ) 268 ->tag( 'wp:meta_value', $meta->meta_value ) 269 ->end; 270 } 271 return $oxymel; 272 } 273 274 275 function footer() { 276 $oxymel = new Oxymel; 277 return $oxymel->close_channel->close_rss->to_string(); 278 } 279 } -
new file src/wp-includes/export/functions.export.php
diff --git a/src/wp-includes/export/functions.export.php b/src/wp-includes/export/functions.export.php new file mode 100644 index 0000000..dd3b177
- + 1 <?php 2 3 function wp_export( $args = array() ) { 4 $defaults = array( 5 'filters' => array(), 6 'format' => 'WP_Export_WXR_Formatter', 7 'writer' => 'WP_Export_Returner', 8 'writer_args' => null, 9 ); 10 $args = wp_parse_args( $args, $defaults ); 11 $export_query = new WP_Export_Query( $args['filters'] ); 12 $formatter = new $args['format']( $export_query ); 13 $writer = new $args['writer']( $formatter, $args['writer_args'] ); 14 try { 15 return $writer->export(); 16 } catch ( WP_Export_Exception $e ) { 17 return new WP_Error( 'wp-export-error', $e->getMessage() ); 18 } 19 } -
new file src/wp-includes/export/writers.php
diff --git a/src/wp-includes/export/writers.php b/src/wp-includes/export/writers.php new file mode 100644 index 0000000..e0d1594
- + 1 <?php 2 abstract class WP_Export_Base_Writer { 3 protected $formatter; 4 5 function __construct( $formatter ) { 6 $this->formatter = $formatter; 7 } 8 9 public function export() { 10 $this->write( $this->formatter->before_posts() ); 11 foreach( $this->formatter->posts() as $post_in_wxr ) { 12 $this->write( $post_in_wxr ); 13 } 14 $this->write( $this->formatter->after_posts() ); 15 } 16 17 abstract protected function write( $xml ); 18 } 19 20 class WP_Export_XML_Over_HTTP extends WP_Export_Base_Writer { 21 private $file_name; 22 23 function __construct( $formatter, $file_name ) { 24 parent::__construct( $formatter ); 25 $this->file_name = $file_name; 26 } 27 28 public function export() { 29 try { 30 $export = $this->get_export(); 31 $this->send_headers(); 32 echo $export; 33 } catch ( WP_Export_Exception $e ) { 34 $message = apply_filters( 'export_error_message', $e->getMessage() ); 35 wp_die( $message, __( 'Export Error' ), array( 'back_link' => true ) ); 36 } catch ( WP_Export_Term_Exception $e ) { 37 do_action( 'export_term_orphaned', $this->formatter->export->missing_parents ); 38 $message = apply_filters( 'export_term_error_message', $e->getMessage() ); 39 wp_die( $message, __( 'Export Error' ), array( 'back_link' => true ) ); 40 } 41 } 42 43 protected function write( $xml ) { 44 $this->result .= $xml; 45 } 46 47 protected function get_export() { 48 $this->result = ''; 49 parent::export(); 50 return $this->result; 51 } 52 53 protected function send_headers() { 54 header( 'Content-Description: File Transfer' ); 55 header( 'Content-Disposition: attachment; filename=' . $this->file_name ); 56 header( 'Content-Type: text/xml; charset=' . get_option( 'blog_charset' ), true ); 57 } 58 } 59 60 class WP_Export_Returner extends WP_Export_Base_Writer { 61 private $result = ''; 62 63 public function export() { 64 $this->private = ''; 65 try { 66 parent::export(); 67 } catch ( WP_Export_Exception $e ) { 68 $message = apply_filters( 'export_error_message', $e->getMessage() ); 69 return new WP_Error( 'wp-export-error', $message ); 70 71 } catch ( WP_Export_Term_Exception $e ) { 72 do_action( 'export_term_orphaned', $this->formatter->export->missing_parents ); 73 $message = apply_filters( 'export_term_error_message', $e->getMessage() ); 74 return new WP_Error( 'wp-export-error', $message ); 75 } 76 return $this->result; 77 } 78 protected function write( $xml ) { 79 $this->result .= $xml; 80 } 81 } 82 83 class WP_Export_File_Writer extends WP_Export_Base_Writer { 84 private $f; 85 private $file_name; 86 87 public function __construct( $formatter, $file_name ) { 88 parent::__construct( $formatter ); 89 $this->file_name = $file_name; 90 } 91 92 public function export() { 93 $this->f = fopen( $this->file_name, 'w' ); 94 if ( !$this->f ) { 95 throw new WP_Export_Exception( sprintf( __( 'WP Export: error opening %s for writing.' ), $this->file_name ) ); 96 } 97 98 try { 99 parent::export(); 100 } catch ( WP_Export_Exception $e ) { 101 throw $e; 102 } catch ( WP_Export_Term_Exception $e ) { 103 throw $e; 104 } 105 106 fclose( $this->f ); 107 } 108 109 protected function write( $xml ) { 110 $res = fwrite( $this->f, $xml); 111 if ( false === $res ) { 112 throw new WP_Export_Exception( __( 'WP Export: error writing to export file.' ) ); 113 } 114 } 115 } 116 117 class WP_Export_Split_Files_Writer extends WP_Export_Base_Writer { 118 private $result = ''; 119 private $f; 120 private $next_file_number = 0; 121 private $current_file_size = 0; 122 123 function __construct( $formatter, $writer_args = array() ) { 124 parent::__construct( $formatter ); 125 //TODO: check if args are not missing 126 $this->max_file_size = is_null( $writer_args['max_file_size'] ) ? 15 * MB_IN_BYTES : $max_file_size; 127 $this->destination_directory = $writer_args['destination_directory']; 128 $this->filename_template = $writer_args['filename_template']; 129 $this->before_posts_xml = $this->formatter->before_posts(); 130 $this->after_posts_xml = $this->formatter->after_posts(); 131 } 132 133 public function export() { 134 $this->start_new_file(); 135 foreach( $this->formatter->posts() as $post_xml ) { 136 if ( $this->current_file_size + strlen( $post_xml ) > $this->max_file_size ) { 137 $this->start_new_file(); 138 } 139 $this->write( $post_xml ); 140 } 141 $this->close_current_file(); 142 } 143 144 protected function write( $xml ) { 145 $res = fwrite( $this->f, $xml); 146 if ( false === $res ) { 147 throw new WP_Export_Exception( __( 'WP Export: error writing to export file.' ) ); 148 } 149 $this->current_file_size += strlen( $xml ); 150 } 151 152 private function start_new_file() { 153 if ( $this->f ) { 154 $this->close_current_file(); 155 } 156 $file_path = $this->next_file_path(); 157 $this->f = fopen( $file_path, 'w' ); 158 if ( !$this->f ) { 159 throw new WP_Export_Exception( sprintf( __( 'WP Export: error opening %s for writing.' ), $file_path ) ); 160 } 161 $this->current_file_size = 0; 162 $this->write( $this->before_posts_xml ); 163 } 164 165 private function close_current_file() { 166 if ( !$this->f ) { 167 return; 168 } 169 $this->write( $this->after_posts_xml ); 170 fclose( $this->f ); 171 } 172 173 private function next_file_name() { 174 $next_file_name = sprintf( $this->filename_template, $this->next_file_number ); 175 $this->next_file_number++; 176 return $next_file_name; 177 } 178 179 private function next_file_path() { 180 return untrailingslashit( $this->destination_directory ) . DIRECTORY_SEPARATOR . $this->next_file_name(); 181 } 182 183 } -
new file src/wp-includes/iterators.php
diff --git a/src/wp-includes/iterators.php b/src/wp-includes/iterators.php new file mode 100644 index 0000000..6a4613c
- + 1 <?php 2 class WP_Map_Iterator extends IteratorIterator { 3 function __construct( $iterator, $callback ) { 4 $this->callback = $callback; 5 parent::__construct( $iterator ); 6 } 7 8 function current() { 9 $original_current = parent::current(); 10 return call_user_func( $this->callback, $original_current ); 11 } 12 } 13 14 class WP_Post_IDs_Iterator implements Iterator { 15 private $limit = 100; 16 private $post_ids; 17 private $ids_left; 18 private $results = array(); 19 20 public function __construct( $post_ids, $limit = null ) { 21 $this->db = $GLOBALS['wpdb']; 22 $this->post_ids = $post_ids; 23 $this->ids_left = $post_ids; 24 if ( !is_null( $limit ) ) { 25 $this->limit = $limit; 26 } 27 } 28 29 public function current() { 30 return $this->results[$this->index_in_results]; 31 } 32 33 public function key() { 34 return $this->global_index; 35 } 36 37 public function next() { 38 $this->index_in_results++; 39 $this->global_index++; 40 } 41 42 public function rewind() { 43 $this->results = array(); 44 $this->global_index = 0; 45 $this->index_in_results = 0; 46 $this->ids_left = $this->post_ids; 47 } 48 49 public function valid() { 50 if ( isset( $this->results[$this->index_in_results] ) ) { 51 return true; 52 } 53 if ( empty( $this->ids_left ) ) { 54 return false; 55 } 56 $has_posts = $this->load_next_posts_from_db(); 57 if ( !$has_posts ) { 58 return false; 59 } 60 $this->index_in_results = 0; 61 return true; 62 } 63 64 private function load_next_posts_from_db() { 65 $next_batch_post_ids = array_splice( $this->ids_left, 0, $this->limit ); 66 $in_post_ids_sql = $this->db->build_IN_condition( 'ID', $next_batch_post_ids ); 67 $this->results = $this->db->get_results( "SELECT * FROM {$this->db->posts} WHERE $in_post_ids_sql" ); 68 if ( !$this->results ) { 69 if ( $this->db->last_error ) { 70 throw new WP_Iterator_Exception( 'Database error: ' . $this->db->last_error ); 71 } else { 72 return false; 73 } 74 } 75 return true; 76 } 77 } 78 79 class WP_Iterator_Exception extends Exception { 80 } -
src/wp-includes/wp-db.php
diff --git a/src/wp-includes/wp-db.php b/src/wp-includes/wp-db.php index 5393240..492a222 100644
a b class wpdb { 2843 2843 } 2844 2844 return preg_replace( '/[^0-9.].*/', '', $server_info ); 2845 2845 } 2846 2847 /** 2848 * Builds a SQL condition in the form "post_id IN (1, 2, 3, 4)" 2849 * 2850 * @since 3.6.0 2851 * 2852 * @param string $column_name The name of the table column from the IN condition 2853 * @param array $values Array of values in which the column value should be 2854 * @param string $format Optional printf format specifier for the elements of the array. Defaults to %s. 2855 * @return string The IN condition, with escaped values. If there are no values, the return value is an empty string. 2856 */ 2857 function build_IN_condition( $column_name, $values, $format = '%s' ) { 2858 if ( !is_array( $values ) || empty( $values ) ) { 2859 return ''; 2860 } 2861 $formats = implode( ', ', array_fill( 0, count( $values ), $format ) ); 2862 return $this->prepare( "$column_name IN ($formats)", $values ); 2863 } 2846 2864 } -
src/wp-settings.php
diff --git a/src/wp-settings.php b/src/wp-settings.php index 715a2c2..97c5a94 100644
a b require( ABSPATH . WPINC . '/category-template.php' ); 134 134 require( ABSPATH . WPINC . '/comment.php' ); 135 135 require( ABSPATH . WPINC . '/comment-template.php' ); 136 136 require( ABSPATH . WPINC . '/rewrite.php' ); 137 require( ABSPATH . WPINC . '/iterators.php' ); 138 require( ABSPATH . WPINC . '/export/class-wp-export-query.php' ); 139 require( ABSPATH . WPINC . '/export/class-wp-export-wxr-formatter.php' ); 140 require( ABSPATH . WPINC . '/export/writers.php' ); 141 require( ABSPATH . WPINC . '/export/functions.export.php' ); 137 142 require( ABSPATH . WPINC . '/feed.php' ); 138 143 require( ABSPATH . WPINC . '/bookmark.php' ); 139 144 require( ABSPATH . WPINC . '/bookmark-template.php' ); -
new file tests/phpunit/tests/export/class-wp-export-query.php
diff --git a/tests/phpunit/tests/export/class-wp-export-query.php b/tests/phpunit/tests/export/class-wp-export-query.php new file mode 100644 index 0000000..5be2567
- + 1 <?php 2 3 /** 4 * Test WP_Export_Query class 5 * 6 * @group export 7 * @ticket 22435 8 */ 9 class Test_WP_Export_Query extends WP_UnitTestCase { 10 function setUp() { 11 if ( ! class_exists( 'WP_Export_Query' ) ) { 12 $this->markTestSkipped( "WP_Export_Query class doesn't exist" ); 13 } 14 15 parent::setUp(); 16 } 17 18 function test_WP_Export_Query_should_be_initialized_with_an_array() { 19 $export = new WP_Export_Query( array( 'author' => 'all' ) ); 20 $this->assertTrue( (bool) $export ); 21 } 22 23 function test_WP_Export_Query_should_use_post_ids_if_passed() { 24 $export = new WP_Export_Query( array( 'post_ids' => array( 1, 2, 3 ) ) ); 25 $this->assertEquals( array( 1, 2, 3 ), $export->post_ids() ); 26 } 27 28 function test_WP_Export_Query_should_filter_all_posts_if_all_arg_is_true() { 29 $post_id = $this->factory->post->create(); 30 $export = new WP_Export_Query(); 31 $this->assertEquals( array( $post_id ), $export->post_ids() ); 32 } 33 34 function test_WP_Export_Query_should_filter_all_posts_if_no_args_passed() { 35 $post_id = $this->factory->post->create(); 36 $export = new WP_Export_Query(); 37 $this->assertEquals( array( $post_id ), $export->post_ids() ); 38 } 39 40 function test_WP_Export_Query_should_not_export_anything_if_post_type_arg_is_set_to_non_existing_post_type() { 41 $post_id = $this->factory->post->create(); 42 $export = new WP_Export_Query( array( 'post_type' => 'baba' ) ); 43 $this->assertEquals( array(), $export->post_ids() ); 44 } 45 46 function test_WP_Export_Query_should_filter_only_posts_with_a_certain_post_type_if_the_post_type_arg_is_set() { 47 register_post_type( 'baba' ); 48 $post_id = $this->factory->post->create( array( 'post_type' => 'baba' ) ); 49 $_ = $this->factory->post->create( array( 'post_type' => 'dyado' ) ); 50 $export = new WP_Export_Query( array( 'post_type' => 'baba' ) ); 51 $this->assertEquals( array( $post_id ), $export->post_ids() ); 52 _unregister_post_type( 'baba' ); 53 } 54 55 function test_WP_Export_Query_should_not_export_post_types_with_can_export_set_to_false() { 56 register_post_type( 'non-exportable', array( 'can_export' => false ) ); 57 register_post_type( 'exportable', array( 'can_export' => true ) ); 58 $non_exportable_post_id = $this->factory->post->create( array( 'post_type' => 'non-exportable' ) ); 59 $exportable_post_id = $this->factory->post->create( array( 'post_type' => 'exportable' ) ); 60 $export = new WP_Export_Query(); 61 $this->assertEquals( array( $exportable_post_id ), $export->post_ids() ); 62 _unregister_post_type( 'non-exportable' ); 63 _unregister_post_type( 'exportable' ); 64 } 65 66 function test_WP_Export_Query_should_not_export_auto_drafts_by_default() { 67 $post_id = $this->factory->post->create( array( 'post_status' => 'auto-draft' ) ); 68 $export = new WP_Export_Query(); 69 $this->assertEquals( array(), $export->post_ids() ); 70 } 71 72 function test_WP_Export_Query_should_filter_only_posts_with_certain_status_if_status_arg_is_set() { 73 $post_id_baba = $this->factory->post->create( array( 'post_status' => 'baba' ) ); 74 $post_id_dudu = $this->factory->post->create( array( 'post_status' => 'dudu' ) ); 75 $export = new WP_Export_Query( array( 'status' => 'baba' ) ); 76 $this->assertEquals( array( $post_id_baba ), $export->post_ids() ); 77 } 78 79 function test_WP_Export_Query_should_filter_only_posts_with_certain_author_id_if_status_arg_is_a_number() { 80 $user_id = $this->factory->user->create(); 81 $post_by_user = $this->factory->post->create( array( 'post_author' => $user_id ) ); 82 $other_post = $this->factory->post->create( array( 'post_author' => $user_id + 1 ) ); 83 $export = new WP_Export_Query( array( 'author' => $user_id ) ); 84 $this->assertEquals( array( $post_by_user ), $export->post_ids() ); 85 } 86 87 function test_WP_Export_Query_should_filter_only_posts_with_certain_author_name_if_status_arg_is_a_username() { 88 $user = $this->factory->user->create_and_get( array( 'user_login' => 'baba' ) ); 89 $post_by_user = $this->factory->post->create( array( 'post_author' => $user->ID ) ); 90 $other_post = $this->factory->post->create( array( 'post_author' => $user->ID + 1 ) ); 91 $export = new WP_Export_Query( array( 'author' => 'baba' ) ); 92 $this->assertEquals( array( $post_by_user ), $export->post_ids() ); 93 } 94 95 function test_WP_Export_Query_should_filter_only_posts_with_certain_author_object_if_author_is_an_object_with_ID_member_variable() { 96 $user = $this->factory->user->create_and_get(); 97 $post_by_user = $this->factory->post->create( array( 'post_author' => $user->ID ) ); 98 $other_post = $this->factory->post->create( array( 'post_author' => $user->ID + 1 ) ); 99 $export = new WP_Export_Query( array( 'author' => $user ) ); 100 $this->assertEquals( array( $post_by_user ), $export->post_ids() ); 101 } 102 103 function test_WP_Export_Query_should_filter_only_posts_after_certain_start_date_if_start_date_arg_is_passed() { 104 $post_before = $this->factory->post->create( array( 'post_date' => '2012-11-10 23:59:59' ) ); 105 $post_after = $this->factory->post->create( array( 'post_date' => '2012-11-11 00:00:00' ) ); 106 $export = new WP_Export_Query( array( 'start_date' => '2012-11-11' ) ); 107 $this->assertEquals( array( $post_after ), $export->post_ids() ); 108 } 109 110 function test_WP_Export_Query_should_filter_only_posts_after_certain_end_date_if_end_date_arg_is_passed() { 111 $post_before = $this->factory->post->create( array( 'post_date' => '2012-11-10 23:59:59' ) ); 112 $post_after = $this->factory->post->create( array( 'post_date' => '2012-11-11 00:00:00' ) ); 113 $export = new WP_Export_Query( array( 'end_date' => '2012-11-10' ) ); 114 $this->assertEquals( array( $post_before ), $export->post_ids() ); 115 } 116 117 function test_WP_Export_Query_should_filter_only_posts_with_certain_category_if_category_arg_is_passed() { 118 $category_id = $this->factory->category->create( array( 'name' => 'baba' ) ); 119 $post_with_category = $this->factory->post->create( array( 'post_category' => array( $category_id ) ) ); 120 $post_without = $this->factory->post->create(); 121 $export = new WP_Export_Query( array( 'post_type' => 'post', 'category' => 'baba' ) ); 122 $this->assertEquals( array( $post_with_category ), $export->post_ids() ); 123 } 124 125 function test_WP_Export_Query_should_filter_only_posts_with_certain_category_id_if_category_arg_is_passed() { 126 $category_id = $this->factory->category->create( array( 'name' => 'baba' ) ); 127 $post_with_category = $this->factory->post->create( array( 'post_category' => array( $category_id ) ) ); 128 $post_without = $this->factory->post->create(); 129 $export = new WP_Export_Query( array( 'post_type' => 'post', 'category' => $category_id ) ); 130 $this->assertEquals( array( $post_with_category ), $export->post_ids() ); 131 } 132 133 function test_WP_Export_Query_should_filter_posts_by_category_only_for_post_post_type() { 134 $category_id = $this->factory->category->create( array( 'name' => 'baba' ) ); 135 $post_with_category = $this->factory->post->create( array( 'post_category' => array( $category_id ) ) ); 136 $post_without = $this->factory->post->create(); 137 $different_post_type = $this->factory->post->create( array( 'post_type' => 'page' ) ); 138 $export = new WP_Export_Query( array( 'category' => $category_id ) ); 139 $this->assertEqualSets( array( $post_with_category, $post_without, $different_post_type ), $export->post_ids() ); 140 } 141 142 function test_WP_Export_Query_should_include_attachments_of_posts_if_we_are_filtering_only_some_post_types() { 143 register_post_type( 'baba' ); 144 $post_id = $this->factory->post->create( array( 'post_type' => 'baba' ) ); 145 $attachment_post_id = $this->factory->post->create( array( 'post_type' => 'attachment', 'post_parent' => $post_id ) ); 146 $export = new WP_Export_Query( array( 'post_type' => 'baba' ) ); 147 $this->assertEquals( array( $post_id, $attachment_post_id ), $export->post_ids() ); 148 _unregister_post_type( 'baba' ); 149 } 150 151 function test_authors_should_return_list_of_users_for_each_post_author() { 152 $user_id = $this->factory->user->create(); 153 $this->factory->post->create( array( 'post_author' => $user_id ) ); 154 $export = new WP_Export_Query(); 155 $authors = $export->authors(); 156 $this->assertEquals( 1, count( $authors ) ); 157 $this->assertEquals( $user_id, $authors[0]->ID ); 158 } 159 160 function test_authors_should_skip_non_existing_authors() { 161 $this->factory->post->create( array( 'post_author' => 11 ) ); 162 $export = new WP_Export_Query(); 163 $this->assertEquals( array(), $export->authors() ); 164 } 165 166 function test_authors_should_skip_auto_draft_authors() { 167 $user_id = $this->factory->user->create(); 168 $this->factory->post->create( array( 'post_author' => $user_id, 'post_status' => 'auto-draft' ) ); 169 $export = new WP_Export_Query(); 170 $this->assertEquals( array(), $export->authors() ); 171 } 172 173 function test_categories_should_return_only_the_category_we_are_filtering_on() { 174 $category_id = $this->factory->category->create( array( 'name' => 'baba' ) ); 175 $other_category_id = $this->factory->category->create( array( 'name' => 'dyado' ) ); 176 $export = new WP_Export_Query( array( 'post_type' => 'post', 'category' => $category_id ) ); 177 $categories = $export->categories(); 178 $this->assertTrue( is_array( $categories ) ); 179 $this->assertEquals( 1, count( $categories ) ); 180 } 181 182 function test_categories_should_return_no_categories_if_we_are_requesting_only_one_post_type() { 183 $category_id = $this->factory->category->create(); 184 $export = new WP_Export_Query( array( 'post_type' => 'post' ) ); 185 $this->assertEquals( array(), $export->categories() ); 186 } 187 188 function test_categories_should_return_all_categories_if_we_are_requesting_all_post_types() { 189 $category_id = $this->factory->category->create(); 190 $another_category_id = $this->factory->category->create(); 191 $export = new WP_Export_Query(); 192 $this->assertEqualSets( array( 1, $category_id, $another_category_id ), self::get_term_ids( $export->categories() ) ); 193 } 194 195 function test_categories_should_not_return_a_child_before_its_parent_category() { 196 $child_category_id = $this->factory->category->create(); 197 $top_category_id = $this->factory->category->create(); 198 wp_update_term( $child_category_id, 'category', array( 'parent' => $top_category_id ) ); 199 $export = new WP_Export_Query(); 200 $this->assertNoChildBeforeParent( $export->categories() ); 201 } 202 203 function test_tags_should_return_all_tags() { 204 $tag_id = $this->factory->tag->create(); 205 $export = new WP_Export_Query(); 206 $this->assertEquals( array( $tag_id ), self::get_term_ids( $export->tags() ) ); 207 } 208 209 function test_tags_should_return_no_tags_if_we_are_requesting_only_one_post_type() { 210 $category_id = $this->factory->tag->create(); 211 $export = new WP_Export_Query( array( 'post_type' => 'post' ) ); 212 $this->assertEquals( array(), $export->tags() ); 213 } 214 215 function test_custom_taxonomies_terms_should_return_all_terms() { 216 register_taxonomy( 'taxonomy_all', 'post' ); 217 $term_id = $this->factory->term->create( array( 'taxonomy' => 'taxonomy_all' ) ); 218 $export = new WP_Export_Query(); 219 $this->assertEquals( array( $term_id ), self::get_term_ids( $export->custom_taxonomies_terms() ) ); 220 _unregister_taxonomy( 'taxonomy_all' ); 221 } 222 223 function test_custom_taxonomes_terms_should_return_no_terms_if_we_are_requesting_only_one_post_type() { 224 register_taxonomy( 'taxonomy_one_post_type', 'post' ); 225 $term_id = $this->factory->term->create( array( 'taxonomy' => 'taxonomy_one_post_type' ) ); 226 $export = new WP_Export_Query( array( 'post_type' => 'post' ) ); 227 $this->assertEquals( array(), $export->custom_taxonomies_terms() ); 228 _unregister_taxonomy( 'taxonomy_one_post_type' ); 229 } 230 231 function test_custom_taxonomies_terms_should_not_return_a_child_before_its_parent_term() { 232 register_taxonomy( 'heir', 'post', array( 'hierarchical' => true ) ); 233 $child_term_id = $this->factory->term->create( array( 'taxonomy' => 'heir' ) ); 234 $top_term_id = $this->factory->term->create( array( 'taxonomy' => 'heir' ) ); 235 wp_update_term( $child_term_id, 'heir', array( 'parent' => $top_term_id ) ); 236 $export = new WP_Export_Query(); 237 $this->assertNoChildBeforeParent( $export->custom_taxonomies_terms() ); 238 _unregister_taxonomy( 'heir' ); 239 } 240 241 private function assertNoChildBeforeParent( $terms ) { 242 $visited = array(); 243 foreach( $terms as $term ) { 244 $this->assertTrue( isset( $visited[$term->parent] ) || !$term->parent ); 245 $visited[$term->term_id] = true; 246 } 247 } 248 249 private static function get_term_ids( $terms ) { 250 return array_values( array_map( array( __CLASS__, '_get_term_ids_cb' ), $terms ) ); 251 } 252 253 private static function _get_term_ids_cb( $c ) { 254 return intval( $c->term_id ); 255 } 256 257 } 258 -
new file tests/phpunit/tests/export/functions.export.php
diff --git a/tests/phpunit/tests/export/functions.export.php b/tests/phpunit/tests/export/functions.export.php new file mode 100644 index 0000000..e3b77e7
- + 1 <?php 2 3 /** 4 * Test export functions 5 * 6 * @group export 7 * @ticket 22435 8 */ 9 class Test_WP_Export_Functions extends WP_UnitTestCase { 10 function setUp() { 11 if ( ! function_exists( 'wp_export' ) ) { 12 $this->markTestSkipped( "wp_export function doesn't exist" ); 13 } 14 15 parent::setUp(); 16 } 17 18 function test_wp_export_returns_wp_error_if_the_writer_throws_Export_exception() { 19 $this->assertTrue( is_wp_error( wp_export( array( 'writer' => 'Test_WP_Export_Stub_Writer_Throws_Export_Exception' ) ) ) ); 20 } 21 22 function test_wp_export_passes_the_exception_if_the_writer_throws_other_exception() { 23 $this->setExpectedException( 'Exception' ); 24 wp_export( array( 'writer' => 'Test_WP_Export_Stub_Writer_Throws_Other_Exception' ) ); 25 } 26 27 } 28 29 class Test_WP_Export_Stub_Writer_Throws_Export_Exception { 30 function __construct( $formatter ) { 31 } 32 function export() { 33 throw new WP_Export_Exception( 'baba' ); 34 } 35 } 36 37 class Test_WP_Export_Stub_Writer_Throws_Other_Exception { 38 function __construct( $formatter ) { 39 } 40 function export() { 41 throw new Exception( 'baba' ); 42 } 43 } -
new file tests/phpunit/tests/export/writers.php
diff --git a/tests/phpunit/tests/export/writers.php b/tests/phpunit/tests/export/writers.php new file mode 100644 index 0000000..6715d17
- + 1 <?php 2 3 /** 4 * Test WP_Export_*_Writer classes 5 * 6 * @group export 7 * @ticket 22435 8 */ 9 class Test_WP_Export_Writers extends WP_UnitTestCase { 10 function test_export_returner_returns_all_the_return_values() { 11 if ( ! class_exists( 'WP_Export_Returner' ) ) { 12 $this->markTestSkipped( "WP_Export_Returner class doesn't exist" ); 13 } 14 $returner = new WP_Export_Returner( $this->get_x_formatter() ); 15 $this->assertEquals( 'xxx' , $returner->export() ); 16 } 17 18 private function get_x_formatter() { 19 $methods = array( 'before_posts', 'posts', 'after_posts' ); 20 $formatter = $this->getMock( 'WP_Export_WXR_Formatter', $methods, array( null ) ); 21 foreach( $methods as $method ) { 22 $return = 'posts' == $method? array( 'x' ) : 'x'; 23 $formatter->expects( $this->once() )->method( $method )->with()->will( $this->returnValue( $return ) ); 24 } 25 return $formatter; 26 } 27 } 28 -
new file tests/phpunit/tests/iterators.php
diff --git a/tests/phpunit/tests/iterators.php b/tests/phpunit/tests/iterators.php new file mode 100644 index 0000000..bd5f0d7
- + 1 <?php 2 3 /** 4 * @ticket 22435 5 */ 6 class Test_WP_Post_IDs_Iterator extends WP_UnitTestCase { 7 function setUp() { 8 if ( ! class_exists( 'WP_Post_IDs_Iterator' ) ) { 9 $this->markTestSkipped( "WP_Post_IDs_Iterator class doesn't exist" ); 10 } 11 12 parent::setUp(); 13 } 14 15 function test_create() { 16 new WP_Post_IDs_Iterator( array( 1, 2, 3 ) ); 17 } 18 19 function test_no_posts() { 20 $this->assertIteratorReturnsSamePostIDs( array() ); 21 } 22 23 function test_less_ids_than_limit() { 24 $post_id_0 = $this->factory->post->create(); 25 $post_id_1 = $this->factory->post->create(); 26 $this->assertIteratorReturnsSamePostIDs( array( $post_id_0, $post_id_1 ), 10 ); 27 } 28 29 function test_ids_exactly_as_limit() { 30 $post_id_0 = $this->factory->post->create(); 31 $post_id_1 = $this->factory->post->create(); 32 $this->assertIteratorReturnsSamePostIDs( array( $post_id_0, $post_id_1 ), 2 ); 33 } 34 35 function test_more_ids_than_limit() { 36 $post_id_0 = $this->factory->post->create(); 37 $post_id_1 = $this->factory->post->create(); 38 $post_id_2 = $this->factory->post->create(); 39 $this->assertIteratorReturnsSamePostIDs( array( $post_id_0, $post_id_1, $post_id_2 ), 2 ); 40 } 41 42 function test_ids_exactly_twice_more_than_limit() { 43 $post_id_0 = $this->factory->post->create(); 44 $post_id_1 = $this->factory->post->create(); 45 $post_id_2 = $this->factory->post->create(); 46 $post_id_3 = $this->factory->post->create(); 47 $this->assertIteratorReturnsSamePostIDs( array( $post_id_0, $post_id_1, $post_id_2, $post_id_3 ), 2 ); 48 } 49 50 private function assertIteratorReturnsSamePostIDs( $post_ids, $limit = 2 ) { 51 $this->assertEquals( $post_ids, wp_list_pluck( iterator_to_array( new WP_Post_IDs_Iterator( $post_ids, $limit ) ), 'ID' ) ); 52 } 53 }