wp/wp-content/plugins/wordpress-importer/wordpress-importer.php
changeset 14 00ac8f60d73f
parent 13 d255fe9cd479
child 15 3d4e9c994f10
equal deleted inserted replaced
13:d255fe9cd479 14:00ac8f60d73f
     1 <?php
       
     2 /*
       
     3 Plugin Name: WordPress Importer
       
     4 Plugin URI: https://wordpress.org/plugins/wordpress-importer/
       
     5 Description: Import posts, pages, comments, custom fields, categories, tags and more from a WordPress export file.
       
     6 Author: wordpressdotorg
       
     7 Author URI: https://wordpress.org/
       
     8 Version: 0.6.4
       
     9 Text Domain: wordpress-importer
       
    10 License: GPL version 2 or later - http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
       
    11 */
       
    12 
       
    13 if ( ! defined( 'WP_LOAD_IMPORTERS' ) )
       
    14 	return;
       
    15 
       
    16 /** Display verbose errors */
       
    17 define( 'IMPORT_DEBUG', false );
       
    18 
       
    19 // Load Importer API
       
    20 require_once ABSPATH . 'wp-admin/includes/import.php';
       
    21 
       
    22 if ( ! class_exists( 'WP_Importer' ) ) {
       
    23 	$class_wp_importer = ABSPATH . 'wp-admin/includes/class-wp-importer.php';
       
    24 	if ( file_exists( $class_wp_importer ) )
       
    25 		require $class_wp_importer;
       
    26 }
       
    27 
       
    28 // include WXR file parsers
       
    29 require dirname( __FILE__ ) . '/parsers.php';
       
    30 
       
    31 /**
       
    32  * WordPress Importer class for managing the import process of a WXR file
       
    33  *
       
    34  * @package WordPress
       
    35  * @subpackage Importer
       
    36  */
       
    37 if ( class_exists( 'WP_Importer' ) ) {
       
    38 class WP_Import extends WP_Importer {
       
    39 	var $max_wxr_version = 1.2; // max. supported WXR version
       
    40 
       
    41 	var $id; // WXR attachment ID
       
    42 
       
    43 	// information to import from WXR file
       
    44 	var $version;
       
    45 	var $authors = array();
       
    46 	var $posts = array();
       
    47 	var $terms = array();
       
    48 	var $categories = array();
       
    49 	var $tags = array();
       
    50 	var $base_url = '';
       
    51 
       
    52 	// mappings from old information to new
       
    53 	var $processed_authors = array();
       
    54 	var $author_mapping = array();
       
    55 	var $processed_terms = array();
       
    56 	var $processed_posts = array();
       
    57 	var $post_orphans = array();
       
    58 	var $processed_menu_items = array();
       
    59 	var $menu_item_orphans = array();
       
    60 	var $missing_menu_items = array();
       
    61 
       
    62 	var $fetch_attachments = false;
       
    63 	var $url_remap = array();
       
    64 	var $featured_images = array();
       
    65 
       
    66 	/**
       
    67 	 * Registered callback function for the WordPress Importer
       
    68 	 *
       
    69 	 * Manages the three separate stages of the WXR import process
       
    70 	 */
       
    71 	function dispatch() {
       
    72 		$this->header();
       
    73 
       
    74 		$step = empty( $_GET['step'] ) ? 0 : (int) $_GET['step'];
       
    75 		switch ( $step ) {
       
    76 			case 0:
       
    77 				$this->greet();
       
    78 				break;
       
    79 			case 1:
       
    80 				check_admin_referer( 'import-upload' );
       
    81 				if ( $this->handle_upload() )
       
    82 					$this->import_options();
       
    83 				break;
       
    84 			case 2:
       
    85 				check_admin_referer( 'import-wordpress' );
       
    86 				$this->fetch_attachments = ( ! empty( $_POST['fetch_attachments'] ) && $this->allow_fetch_attachments() );
       
    87 				$this->id = (int) $_POST['import_id'];
       
    88 				$file = get_attached_file( $this->id );
       
    89 				set_time_limit(0);
       
    90 				$this->import( $file );
       
    91 				break;
       
    92 		}
       
    93 
       
    94 		$this->footer();
       
    95 	}
       
    96 
       
    97 	/**
       
    98 	 * The main controller for the actual import stage.
       
    99 	 *
       
   100 	 * @param string $file Path to the WXR file for importing
       
   101 	 */
       
   102 	function import( $file ) {
       
   103 		add_filter( 'import_post_meta_key', array( $this, 'is_valid_meta_key' ) );
       
   104 		add_filter( 'http_request_timeout', array( &$this, 'bump_request_timeout' ) );
       
   105 
       
   106 		$this->import_start( $file );
       
   107 
       
   108 		$this->get_author_mapping();
       
   109 
       
   110 		wp_suspend_cache_invalidation( true );
       
   111 		$this->process_categories();
       
   112 		$this->process_tags();
       
   113 		$this->process_terms();
       
   114 		$this->process_posts();
       
   115 		wp_suspend_cache_invalidation( false );
       
   116 
       
   117 		// update incorrect/missing information in the DB
       
   118 		$this->backfill_parents();
       
   119 		$this->backfill_attachment_urls();
       
   120 		$this->remap_featured_images();
       
   121 
       
   122 		$this->import_end();
       
   123 	}
       
   124 
       
   125 	/**
       
   126 	 * Parses the WXR file and prepares us for the task of processing parsed data
       
   127 	 *
       
   128 	 * @param string $file Path to the WXR file for importing
       
   129 	 */
       
   130 	function import_start( $file ) {
       
   131 		if ( ! is_file($file) ) {
       
   132 			echo '<p><strong>' . __( 'Sorry, there has been an error.', 'wordpress-importer' ) . '</strong><br />';
       
   133 			echo __( 'The file does not exist, please try again.', 'wordpress-importer' ) . '</p>';
       
   134 			$this->footer();
       
   135 			die();
       
   136 		}
       
   137 
       
   138 		$import_data = $this->parse( $file );
       
   139 
       
   140 		if ( is_wp_error( $import_data ) ) {
       
   141 			echo '<p><strong>' . __( 'Sorry, there has been an error.', 'wordpress-importer' ) . '</strong><br />';
       
   142 			echo esc_html( $import_data->get_error_message() ) . '</p>';
       
   143 			$this->footer();
       
   144 			die();
       
   145 		}
       
   146 
       
   147 		$this->version = $import_data['version'];
       
   148 		$this->get_authors_from_import( $import_data );
       
   149 		$this->posts = $import_data['posts'];
       
   150 		$this->terms = $import_data['terms'];
       
   151 		$this->categories = $import_data['categories'];
       
   152 		$this->tags = $import_data['tags'];
       
   153 		$this->base_url = esc_url( $import_data['base_url'] );
       
   154 
       
   155 		wp_defer_term_counting( true );
       
   156 		wp_defer_comment_counting( true );
       
   157 
       
   158 		do_action( 'import_start' );
       
   159 	}
       
   160 
       
   161 	/**
       
   162 	 * Performs post-import cleanup of files and the cache
       
   163 	 */
       
   164 	function import_end() {
       
   165 		wp_import_cleanup( $this->id );
       
   166 
       
   167 		wp_cache_flush();
       
   168 		foreach ( get_taxonomies() as $tax ) {
       
   169 			delete_option( "{$tax}_children" );
       
   170 			_get_term_hierarchy( $tax );
       
   171 		}
       
   172 
       
   173 		wp_defer_term_counting( false );
       
   174 		wp_defer_comment_counting( false );
       
   175 
       
   176 		echo '<p>' . __( 'All done.', 'wordpress-importer' ) . ' <a href="' . admin_url() . '">' . __( 'Have fun!', 'wordpress-importer' ) . '</a>' . '</p>';
       
   177 		echo '<p>' . __( 'Remember to update the passwords and roles of imported users.', 'wordpress-importer' ) . '</p>';
       
   178 
       
   179 		do_action( 'import_end' );
       
   180 	}
       
   181 
       
   182 	/**
       
   183 	 * Handles the WXR upload and initial parsing of the file to prepare for
       
   184 	 * displaying author import options
       
   185 	 *
       
   186 	 * @return bool False if error uploading or invalid file, true otherwise
       
   187 	 */
       
   188 	function handle_upload() {
       
   189 		$file = wp_import_handle_upload();
       
   190 
       
   191 		if ( isset( $file['error'] ) ) {
       
   192 			echo '<p><strong>' . __( 'Sorry, there has been an error.', 'wordpress-importer' ) . '</strong><br />';
       
   193 			echo esc_html( $file['error'] ) . '</p>';
       
   194 			return false;
       
   195 		} else if ( ! file_exists( $file['file'] ) ) {
       
   196 			echo '<p><strong>' . __( 'Sorry, there has been an error.', 'wordpress-importer' ) . '</strong><br />';
       
   197 			printf( __( 'The export file could not be found at <code>%s</code>. It is likely that this was caused by a permissions problem.', 'wordpress-importer' ), esc_html( $file['file'] ) );
       
   198 			echo '</p>';
       
   199 			return false;
       
   200 		}
       
   201 
       
   202 		$this->id = (int) $file['id'];
       
   203 		$import_data = $this->parse( $file['file'] );
       
   204 		if ( is_wp_error( $import_data ) ) {
       
   205 			echo '<p><strong>' . __( 'Sorry, there has been an error.', 'wordpress-importer' ) . '</strong><br />';
       
   206 			echo esc_html( $import_data->get_error_message() ) . '</p>';
       
   207 			return false;
       
   208 		}
       
   209 
       
   210 		$this->version = $import_data['version'];
       
   211 		if ( $this->version > $this->max_wxr_version ) {
       
   212 			echo '<div class="error"><p><strong>';
       
   213 			printf( __( 'This WXR file (version %s) may not be supported by this version of the importer. Please consider updating.', 'wordpress-importer' ), esc_html($import_data['version']) );
       
   214 			echo '</strong></p></div>';
       
   215 		}
       
   216 
       
   217 		$this->get_authors_from_import( $import_data );
       
   218 
       
   219 		return true;
       
   220 	}
       
   221 
       
   222 	/**
       
   223 	 * Retrieve authors from parsed WXR data
       
   224 	 *
       
   225 	 * Uses the provided author information from WXR 1.1 files
       
   226 	 * or extracts info from each post for WXR 1.0 files
       
   227 	 *
       
   228 	 * @param array $import_data Data returned by a WXR parser
       
   229 	 */
       
   230 	function get_authors_from_import( $import_data ) {
       
   231 		if ( ! empty( $import_data['authors'] ) ) {
       
   232 			$this->authors = $import_data['authors'];
       
   233 		// no author information, grab it from the posts
       
   234 		} else {
       
   235 			foreach ( $import_data['posts'] as $post ) {
       
   236 				$login = sanitize_user( $post['post_author'], true );
       
   237 				if ( empty( $login ) ) {
       
   238 					printf( __( 'Failed to import author %s. Their posts will be attributed to the current user.', 'wordpress-importer' ), esc_html( $post['post_author'] ) );
       
   239 					echo '<br />';
       
   240 					continue;
       
   241 				}
       
   242 
       
   243 				if ( ! isset($this->authors[$login]) )
       
   244 					$this->authors[$login] = array(
       
   245 						'author_login' => $login,
       
   246 						'author_display_name' => $post['post_author']
       
   247 					);
       
   248 			}
       
   249 		}
       
   250 	}
       
   251 
       
   252 	/**
       
   253 	 * Display pre-import options, author importing/mapping and option to
       
   254 	 * fetch attachments
       
   255 	 */
       
   256 	function import_options() {
       
   257 		$j = 0;
       
   258 ?>
       
   259 <form action="<?php echo admin_url( 'admin.php?import=wordpress&amp;step=2' ); ?>" method="post">
       
   260 	<?php wp_nonce_field( 'import-wordpress' ); ?>
       
   261 	<input type="hidden" name="import_id" value="<?php echo $this->id; ?>" />
       
   262 
       
   263 <?php if ( ! empty( $this->authors ) ) : ?>
       
   264 	<h3><?php _e( 'Assign Authors', 'wordpress-importer' ); ?></h3>
       
   265 	<p><?php _e( 'To make it easier for you to edit and save the imported content, you may want to reassign the author of the imported item to an existing user of this site. For example, you may want to import all the entries as <code>admin</code>s entries.', 'wordpress-importer' ); ?></p>
       
   266 <?php if ( $this->allow_create_users() ) : ?>
       
   267 	<p><?php printf( __( 'If a new user is created by WordPress, a new password will be randomly generated and the new user&#8217;s role will be set as %s. Manually changing the new user&#8217;s details will be necessary.', 'wordpress-importer' ), esc_html( get_option('default_role') ) ); ?></p>
       
   268 <?php endif; ?>
       
   269 	<ol id="authors">
       
   270 <?php foreach ( $this->authors as $author ) : ?>
       
   271 		<li><?php $this->author_select( $j++, $author ); ?></li>
       
   272 <?php endforeach; ?>
       
   273 	</ol>
       
   274 <?php endif; ?>
       
   275 
       
   276 <?php if ( $this->allow_fetch_attachments() ) : ?>
       
   277 	<h3><?php _e( 'Import Attachments', 'wordpress-importer' ); ?></h3>
       
   278 	<p>
       
   279 		<input type="checkbox" value="1" name="fetch_attachments" id="import-attachments" />
       
   280 		<label for="import-attachments"><?php _e( 'Download and import file attachments', 'wordpress-importer' ); ?></label>
       
   281 	</p>
       
   282 <?php endif; ?>
       
   283 
       
   284 	<p class="submit"><input type="submit" class="button" value="<?php esc_attr_e( 'Submit', 'wordpress-importer' ); ?>" /></p>
       
   285 </form>
       
   286 <?php
       
   287 	}
       
   288 
       
   289 	/**
       
   290 	 * Display import options for an individual author. That is, either create
       
   291 	 * a new user based on import info or map to an existing user
       
   292 	 *
       
   293 	 * @param int $n Index for each author in the form
       
   294 	 * @param array $author Author information, e.g. login, display name, email
       
   295 	 */
       
   296 	function author_select( $n, $author ) {
       
   297 		_e( 'Import author:', 'wordpress-importer' );
       
   298 		echo ' <strong>' . esc_html( $author['author_display_name'] );
       
   299 		if ( $this->version != '1.0' ) echo ' (' . esc_html( $author['author_login'] ) . ')';
       
   300 		echo '</strong><br />';
       
   301 
       
   302 		if ( $this->version != '1.0' )
       
   303 			echo '<div style="margin-left:18px">';
       
   304 
       
   305 		$create_users = $this->allow_create_users();
       
   306 		if ( $create_users ) {
       
   307 			if ( $this->version != '1.0' ) {
       
   308 				_e( 'or create new user with login name:', 'wordpress-importer' );
       
   309 				$value = '';
       
   310 			} else {
       
   311 				_e( 'as a new user:', 'wordpress-importer' );
       
   312 				$value = esc_attr( sanitize_user( $author['author_login'], true ) );
       
   313 			}
       
   314 
       
   315 			echo ' <input type="text" name="user_new['.$n.']" value="'. $value .'" /><br />';
       
   316 		}
       
   317 
       
   318 		if ( ! $create_users && $this->version == '1.0' )
       
   319 			_e( 'assign posts to an existing user:', 'wordpress-importer' );
       
   320 		else
       
   321 			_e( 'or assign posts to an existing user:', 'wordpress-importer' );
       
   322 		wp_dropdown_users( array( 'name' => "user_map[$n]", 'multi' => true, 'show_option_all' => __( '- Select -', 'wordpress-importer' ) ) );
       
   323 		echo '<input type="hidden" name="imported_authors['.$n.']" value="' . esc_attr( $author['author_login'] ) . '" />';
       
   324 
       
   325 		if ( $this->version != '1.0' )
       
   326 			echo '</div>';
       
   327 	}
       
   328 
       
   329 	/**
       
   330 	 * Map old author logins to local user IDs based on decisions made
       
   331 	 * in import options form. Can map to an existing user, create a new user
       
   332 	 * or falls back to the current user in case of error with either of the previous
       
   333 	 */
       
   334 	function get_author_mapping() {
       
   335 		if ( ! isset( $_POST['imported_authors'] ) )
       
   336 			return;
       
   337 
       
   338 		$create_users = $this->allow_create_users();
       
   339 
       
   340 		foreach ( (array) $_POST['imported_authors'] as $i => $old_login ) {
       
   341 			// Multisite adds strtolower to sanitize_user. Need to sanitize here to stop breakage in process_posts.
       
   342 			$santized_old_login = sanitize_user( $old_login, true );
       
   343 			$old_id = isset( $this->authors[$old_login]['author_id'] ) ? intval($this->authors[$old_login]['author_id']) : false;
       
   344 
       
   345 			if ( ! empty( $_POST['user_map'][$i] ) ) {
       
   346 				$user = get_userdata( intval($_POST['user_map'][$i]) );
       
   347 				if ( isset( $user->ID ) ) {
       
   348 					if ( $old_id )
       
   349 						$this->processed_authors[$old_id] = $user->ID;
       
   350 					$this->author_mapping[$santized_old_login] = $user->ID;
       
   351 				}
       
   352 			} else if ( $create_users ) {
       
   353 				if ( ! empty($_POST['user_new'][$i]) ) {
       
   354 					$user_id = wp_create_user( $_POST['user_new'][$i], wp_generate_password() );
       
   355 				} else if ( $this->version != '1.0' ) {
       
   356 					$user_data = array(
       
   357 						'user_login' => $old_login,
       
   358 						'user_pass' => wp_generate_password(),
       
   359 						'user_email' => isset( $this->authors[$old_login]['author_email'] ) ? $this->authors[$old_login]['author_email'] : '',
       
   360 						'display_name' => $this->authors[$old_login]['author_display_name'],
       
   361 						'first_name' => isset( $this->authors[$old_login]['author_first_name'] ) ? $this->authors[$old_login]['author_first_name'] : '',
       
   362 						'last_name' => isset( $this->authors[$old_login]['author_last_name'] ) ? $this->authors[$old_login]['author_last_name'] : '',
       
   363 					);
       
   364 					$user_id = wp_insert_user( $user_data );
       
   365 				}
       
   366 
       
   367 				if ( ! is_wp_error( $user_id ) ) {
       
   368 					if ( $old_id )
       
   369 						$this->processed_authors[$old_id] = $user_id;
       
   370 					$this->author_mapping[$santized_old_login] = $user_id;
       
   371 				} else {
       
   372 					printf( __( 'Failed to create new user for %s. Their posts will be attributed to the current user.', 'wordpress-importer' ), esc_html($this->authors[$old_login]['author_display_name']) );
       
   373 					if ( defined('IMPORT_DEBUG') && IMPORT_DEBUG )
       
   374 						echo ' ' . $user_id->get_error_message();
       
   375 					echo '<br />';
       
   376 				}
       
   377 			}
       
   378 
       
   379 			// failsafe: if the user_id was invalid, default to the current user
       
   380 			if ( ! isset( $this->author_mapping[$santized_old_login] ) ) {
       
   381 				if ( $old_id )
       
   382 					$this->processed_authors[$old_id] = (int) get_current_user_id();
       
   383 				$this->author_mapping[$santized_old_login] = (int) get_current_user_id();
       
   384 			}
       
   385 		}
       
   386 	}
       
   387 
       
   388 	/**
       
   389 	 * Create new categories based on import information
       
   390 	 *
       
   391 	 * Doesn't create a new category if its slug already exists
       
   392 	 */
       
   393 	function process_categories() {
       
   394 		$this->categories = apply_filters( 'wp_import_categories', $this->categories );
       
   395 
       
   396 		if ( empty( $this->categories ) )
       
   397 			return;
       
   398 
       
   399 		foreach ( $this->categories as $cat ) {
       
   400 			// if the category already exists leave it alone
       
   401 			$term_id = term_exists( $cat['category_nicename'], 'category' );
       
   402 			if ( $term_id ) {
       
   403 				if ( is_array($term_id) ) $term_id = $term_id['term_id'];
       
   404 				if ( isset($cat['term_id']) )
       
   405 					$this->processed_terms[intval($cat['term_id'])] = (int) $term_id;
       
   406 				continue;
       
   407 			}
       
   408 
       
   409 			$category_parent = empty( $cat['category_parent'] ) ? 0 : category_exists( $cat['category_parent'] );
       
   410 			$category_description = isset( $cat['category_description'] ) ? $cat['category_description'] : '';
       
   411 			$catarr = array(
       
   412 				'category_nicename' => $cat['category_nicename'],
       
   413 				'category_parent' => $category_parent,
       
   414 				'cat_name' => $cat['cat_name'],
       
   415 				'category_description' => $category_description
       
   416 			);
       
   417 			$catarr = wp_slash( $catarr );
       
   418 
       
   419 			$id = wp_insert_category( $catarr );
       
   420 			if ( ! is_wp_error( $id ) ) {
       
   421 				if ( isset($cat['term_id']) )
       
   422 					$this->processed_terms[intval($cat['term_id'])] = $id;
       
   423 			} else {
       
   424 				printf( __( 'Failed to import category %s', 'wordpress-importer' ), esc_html($cat['category_nicename']) );
       
   425 				if ( defined('IMPORT_DEBUG') && IMPORT_DEBUG )
       
   426 					echo ': ' . $id->get_error_message();
       
   427 				echo '<br />';
       
   428 				continue;
       
   429 			}
       
   430 
       
   431 			$this->process_termmeta( $cat, $id['term_id'] );
       
   432 		}
       
   433 
       
   434 		unset( $this->categories );
       
   435 	}
       
   436 
       
   437 	/**
       
   438 	 * Create new post tags based on import information
       
   439 	 *
       
   440 	 * Doesn't create a tag if its slug already exists
       
   441 	 */
       
   442 	function process_tags() {
       
   443 		$this->tags = apply_filters( 'wp_import_tags', $this->tags );
       
   444 
       
   445 		if ( empty( $this->tags ) )
       
   446 			return;
       
   447 
       
   448 		foreach ( $this->tags as $tag ) {
       
   449 			// if the tag already exists leave it alone
       
   450 			$term_id = term_exists( $tag['tag_slug'], 'post_tag' );
       
   451 			if ( $term_id ) {
       
   452 				if ( is_array($term_id) ) $term_id = $term_id['term_id'];
       
   453 				if ( isset($tag['term_id']) )
       
   454 					$this->processed_terms[intval($tag['term_id'])] = (int) $term_id;
       
   455 				continue;
       
   456 			}
       
   457 
       
   458 			$tag = wp_slash( $tag );
       
   459 			$tag_desc = isset( $tag['tag_description'] ) ? $tag['tag_description'] : '';
       
   460 			$tagarr = array( 'slug' => $tag['tag_slug'], 'description' => $tag_desc );
       
   461 
       
   462 			$id = wp_insert_term( $tag['tag_name'], 'post_tag', $tagarr );
       
   463 			if ( ! is_wp_error( $id ) ) {
       
   464 				if ( isset($tag['term_id']) )
       
   465 					$this->processed_terms[intval($tag['term_id'])] = $id['term_id'];
       
   466 			} else {
       
   467 				printf( __( 'Failed to import post tag %s', 'wordpress-importer' ), esc_html($tag['tag_name']) );
       
   468 				if ( defined('IMPORT_DEBUG') && IMPORT_DEBUG )
       
   469 					echo ': ' . $id->get_error_message();
       
   470 				echo '<br />';
       
   471 				continue;
       
   472 			}
       
   473 
       
   474 			$this->process_termmeta( $tag, $id['term_id'] );
       
   475 		}
       
   476 
       
   477 		unset( $this->tags );
       
   478 	}
       
   479 
       
   480 	/**
       
   481 	 * Create new terms based on import information
       
   482 	 *
       
   483 	 * Doesn't create a term its slug already exists
       
   484 	 */
       
   485 	function process_terms() {
       
   486 		$this->terms = apply_filters( 'wp_import_terms', $this->terms );
       
   487 
       
   488 		if ( empty( $this->terms ) )
       
   489 			return;
       
   490 
       
   491 		foreach ( $this->terms as $term ) {
       
   492 			// if the term already exists in the correct taxonomy leave it alone
       
   493 			$term_id = term_exists( $term['slug'], $term['term_taxonomy'] );
       
   494 			if ( $term_id ) {
       
   495 				if ( is_array($term_id) ) $term_id = $term_id['term_id'];
       
   496 				if ( isset($term['term_id']) )
       
   497 					$this->processed_terms[intval($term['term_id'])] = (int) $term_id;
       
   498 				continue;
       
   499 			}
       
   500 
       
   501 			if ( empty( $term['term_parent'] ) ) {
       
   502 				$parent = 0;
       
   503 			} else {
       
   504 				$parent = term_exists( $term['term_parent'], $term['term_taxonomy'] );
       
   505 				if ( is_array( $parent ) ) $parent = $parent['term_id'];
       
   506 			}
       
   507 			$term = wp_slash( $term );
       
   508 			$description = isset( $term['term_description'] ) ? $term['term_description'] : '';
       
   509 			$termarr = array( 'slug' => $term['slug'], 'description' => $description, 'parent' => intval($parent) );
       
   510 
       
   511 			$id = wp_insert_term( $term['term_name'], $term['term_taxonomy'], $termarr );
       
   512 			if ( ! is_wp_error( $id ) ) {
       
   513 				if ( isset($term['term_id']) )
       
   514 					$this->processed_terms[intval($term['term_id'])] = $id['term_id'];
       
   515 			} else {
       
   516 				printf( __( 'Failed to import %s %s', 'wordpress-importer' ), esc_html($term['term_taxonomy']), esc_html($term['term_name']) );
       
   517 				if ( defined('IMPORT_DEBUG') && IMPORT_DEBUG )
       
   518 					echo ': ' . $id->get_error_message();
       
   519 				echo '<br />';
       
   520 				continue;
       
   521 			}
       
   522 
       
   523 			$this->process_termmeta( $term, $id['term_id'] );
       
   524 		}
       
   525 
       
   526 		unset( $this->terms );
       
   527 	}
       
   528 
       
   529 	/**
       
   530 	 * Add metadata to imported term.
       
   531 	 *
       
   532 	 * @since 0.6.2
       
   533 	 *
       
   534 	 * @param array $term    Term data from WXR import.
       
   535 	 * @param int   $term_id ID of the newly created term.
       
   536 	 */
       
   537 	protected function process_termmeta( $term, $term_id ) {
       
   538 		if ( ! isset( $term['termmeta'] ) ) {
       
   539 			$term['termmeta'] = array();
       
   540 		}
       
   541 
       
   542 		/**
       
   543 		 * Filters the metadata attached to an imported term.
       
   544 		 *
       
   545 		 * @since 0.6.2
       
   546 		 *
       
   547 		 * @param array $termmeta Array of term meta.
       
   548 		 * @param int   $term_id  ID of the newly created term.
       
   549 		 * @param array $term     Term data from the WXR import.
       
   550 		 */
       
   551 		$term['termmeta'] = apply_filters( 'wp_import_term_meta', $term['termmeta'], $term_id, $term );
       
   552 
       
   553 		if ( empty( $term['termmeta'] ) ) {
       
   554 			return;
       
   555 		}
       
   556 
       
   557 		foreach ( $term['termmeta'] as $meta ) {
       
   558 			/**
       
   559 			 * Filters the meta key for an imported piece of term meta.
       
   560 			 *
       
   561 			 * @since 0.6.2
       
   562 			 *
       
   563 			 * @param string $meta_key Meta key.
       
   564 			 * @param int    $term_id  ID of the newly created term.
       
   565 			 * @param array  $term     Term data from the WXR import.
       
   566 			 */
       
   567 			$key = apply_filters( 'import_term_meta_key', $meta['key'], $term_id, $term );
       
   568 			if ( ! $key ) {
       
   569 				continue;
       
   570 			}
       
   571 
       
   572 			// Export gets meta straight from the DB so could have a serialized string
       
   573 			$value = maybe_unserialize( $meta['value'] );
       
   574 
       
   575 			add_term_meta( $term_id, $key, $value );
       
   576 
       
   577 			/**
       
   578 			 * Fires after term meta is imported.
       
   579 			 *
       
   580 			 * @since 0.6.2
       
   581 			 *
       
   582 			 * @param int    $term_id ID of the newly created term.
       
   583 			 * @param string $key     Meta key.
       
   584 			 * @param mixed  $value   Meta value.
       
   585 			 */
       
   586 			do_action( 'import_term_meta', $term_id, $key, $value );
       
   587 		}
       
   588 	}
       
   589 
       
   590 	/**
       
   591 	 * Create new posts based on import information
       
   592 	 *
       
   593 	 * Posts marked as having a parent which doesn't exist will become top level items.
       
   594 	 * Doesn't create a new post if: the post type doesn't exist, the given post ID
       
   595 	 * is already noted as imported or a post with the same title and date already exists.
       
   596 	 * Note that new/updated terms, comments and meta are imported for the last of the above.
       
   597 	 */
       
   598 	function process_posts() {
       
   599 		$this->posts = apply_filters( 'wp_import_posts', $this->posts );
       
   600 
       
   601 		foreach ( $this->posts as $post ) {
       
   602 			$post = apply_filters( 'wp_import_post_data_raw', $post );
       
   603 
       
   604 			if ( ! post_type_exists( $post['post_type'] ) ) {
       
   605 				printf( __( 'Failed to import &#8220;%s&#8221;: Invalid post type %s', 'wordpress-importer' ),
       
   606 					esc_html($post['post_title']), esc_html($post['post_type']) );
       
   607 				echo '<br />';
       
   608 				do_action( 'wp_import_post_exists', $post );
       
   609 				continue;
       
   610 			}
       
   611 
       
   612 			if ( isset( $this->processed_posts[$post['post_id']] ) && ! empty( $post['post_id'] ) )
       
   613 				continue;
       
   614 
       
   615 			if ( $post['status'] == 'auto-draft' )
       
   616 				continue;
       
   617 
       
   618 			if ( 'nav_menu_item' == $post['post_type'] ) {
       
   619 				$this->process_menu_item( $post );
       
   620 				continue;
       
   621 			}
       
   622 
       
   623 			$post_type_object = get_post_type_object( $post['post_type'] );
       
   624 
       
   625 			$post_exists = post_exists( $post['post_title'], '', $post['post_date'] );
       
   626 
       
   627 			/**
       
   628 			* Filter ID of the existing post corresponding to post currently importing.
       
   629 			*
       
   630 			* Return 0 to force the post to be imported. Filter the ID to be something else
       
   631 			* to override which existing post is mapped to the imported post.
       
   632 			*
       
   633 			* @see post_exists()
       
   634 			* @since 0.6.2
       
   635 			*
       
   636 			* @param int   $post_exists  Post ID, or 0 if post did not exist.
       
   637 			* @param array $post         The post array to be inserted.
       
   638 			*/
       
   639 			$post_exists = apply_filters( 'wp_import_existing_post', $post_exists, $post );
       
   640 
       
   641 			if ( $post_exists && get_post_type( $post_exists ) == $post['post_type'] ) {
       
   642 				printf( __('%s &#8220;%s&#8221; already exists.', 'wordpress-importer'), $post_type_object->labels->singular_name, esc_html($post['post_title']) );
       
   643 				echo '<br />';
       
   644 				$comment_post_ID = $post_id = $post_exists;
       
   645 				$this->processed_posts[ intval( $post['post_id'] ) ] = intval( $post_exists );
       
   646 			} else {
       
   647 				$post_parent = (int) $post['post_parent'];
       
   648 				if ( $post_parent ) {
       
   649 					// if we already know the parent, map it to the new local ID
       
   650 					if ( isset( $this->processed_posts[$post_parent] ) ) {
       
   651 						$post_parent = $this->processed_posts[$post_parent];
       
   652 					// otherwise record the parent for later
       
   653 					} else {
       
   654 						$this->post_orphans[intval($post['post_id'])] = $post_parent;
       
   655 						$post_parent = 0;
       
   656 					}
       
   657 				}
       
   658 
       
   659 				// map the post author
       
   660 				$author = sanitize_user( $post['post_author'], true );
       
   661 				if ( isset( $this->author_mapping[$author] ) )
       
   662 					$author = $this->author_mapping[$author];
       
   663 				else
       
   664 					$author = (int) get_current_user_id();
       
   665 
       
   666 				$postdata = array(
       
   667 					'import_id' => $post['post_id'], 'post_author' => $author, 'post_date' => $post['post_date'],
       
   668 					'post_date_gmt' => $post['post_date_gmt'], 'post_content' => $post['post_content'],
       
   669 					'post_excerpt' => $post['post_excerpt'], 'post_title' => $post['post_title'],
       
   670 					'post_status' => $post['status'], 'post_name' => $post['post_name'],
       
   671 					'comment_status' => $post['comment_status'], 'ping_status' => $post['ping_status'],
       
   672 					'guid' => $post['guid'], 'post_parent' => $post_parent, 'menu_order' => $post['menu_order'],
       
   673 					'post_type' => $post['post_type'], 'post_password' => $post['post_password']
       
   674 				);
       
   675 
       
   676 				$original_post_ID = $post['post_id'];
       
   677 				$postdata = apply_filters( 'wp_import_post_data_processed', $postdata, $post );
       
   678 
       
   679 				$postdata = wp_slash( $postdata );
       
   680 
       
   681 				if ( 'attachment' == $postdata['post_type'] ) {
       
   682 					$remote_url = ! empty($post['attachment_url']) ? $post['attachment_url'] : $post['guid'];
       
   683 
       
   684 					// try to use _wp_attached file for upload folder placement to ensure the same location as the export site
       
   685 					// e.g. location is 2003/05/image.jpg but the attachment post_date is 2010/09, see media_handle_upload()
       
   686 					$postdata['upload_date'] = $post['post_date'];
       
   687 					if ( isset( $post['postmeta'] ) ) {
       
   688 						foreach( $post['postmeta'] as $meta ) {
       
   689 							if ( $meta['key'] == '_wp_attached_file' ) {
       
   690 								if ( preg_match( '%^[0-9]{4}/[0-9]{2}%', $meta['value'], $matches ) )
       
   691 									$postdata['upload_date'] = $matches[0];
       
   692 								break;
       
   693 							}
       
   694 						}
       
   695 					}
       
   696 
       
   697 					$comment_post_ID = $post_id = $this->process_attachment( $postdata, $remote_url );
       
   698 				} else {
       
   699 					$comment_post_ID = $post_id = wp_insert_post( $postdata, true );
       
   700 					do_action( 'wp_import_insert_post', $post_id, $original_post_ID, $postdata, $post );
       
   701 				}
       
   702 
       
   703 				if ( is_wp_error( $post_id ) ) {
       
   704 					printf( __( 'Failed to import %s &#8220;%s&#8221;', 'wordpress-importer' ),
       
   705 						$post_type_object->labels->singular_name, esc_html($post['post_title']) );
       
   706 					if ( defined('IMPORT_DEBUG') && IMPORT_DEBUG )
       
   707 						echo ': ' . $post_id->get_error_message();
       
   708 					echo '<br />';
       
   709 					continue;
       
   710 				}
       
   711 
       
   712 				if ( $post['is_sticky'] == 1 )
       
   713 					stick_post( $post_id );
       
   714 			}
       
   715 
       
   716 			// map pre-import ID to local ID
       
   717 			$this->processed_posts[intval($post['post_id'])] = (int) $post_id;
       
   718 
       
   719 			if ( ! isset( $post['terms'] ) )
       
   720 				$post['terms'] = array();
       
   721 
       
   722 			$post['terms'] = apply_filters( 'wp_import_post_terms', $post['terms'], $post_id, $post );
       
   723 
       
   724 			// add categories, tags and other terms
       
   725 			if ( ! empty( $post['terms'] ) ) {
       
   726 				$terms_to_set = array();
       
   727 				foreach ( $post['terms'] as $term ) {
       
   728 					// back compat with WXR 1.0 map 'tag' to 'post_tag'
       
   729 					$taxonomy = ( 'tag' == $term['domain'] ) ? 'post_tag' : $term['domain'];
       
   730 					$term_exists = term_exists( $term['slug'], $taxonomy );
       
   731 					$term_id = is_array( $term_exists ) ? $term_exists['term_id'] : $term_exists;
       
   732 					if ( ! $term_id ) {
       
   733 						$t = wp_insert_term( $term['name'], $taxonomy, array( 'slug' => $term['slug'] ) );
       
   734 						if ( ! is_wp_error( $t ) ) {
       
   735 							$term_id = $t['term_id'];
       
   736 							do_action( 'wp_import_insert_term', $t, $term, $post_id, $post );
       
   737 						} else {
       
   738 							printf( __( 'Failed to import %s %s', 'wordpress-importer' ), esc_html($taxonomy), esc_html($term['name']) );
       
   739 							if ( defined('IMPORT_DEBUG') && IMPORT_DEBUG )
       
   740 								echo ': ' . $t->get_error_message();
       
   741 							echo '<br />';
       
   742 							do_action( 'wp_import_insert_term_failed', $t, $term, $post_id, $post );
       
   743 							continue;
       
   744 						}
       
   745 					}
       
   746 					$terms_to_set[$taxonomy][] = intval( $term_id );
       
   747 				}
       
   748 
       
   749 				foreach ( $terms_to_set as $tax => $ids ) {
       
   750 					$tt_ids = wp_set_post_terms( $post_id, $ids, $tax );
       
   751 					do_action( 'wp_import_set_post_terms', $tt_ids, $ids, $tax, $post_id, $post );
       
   752 				}
       
   753 				unset( $post['terms'], $terms_to_set );
       
   754 			}
       
   755 
       
   756 			if ( ! isset( $post['comments'] ) )
       
   757 				$post['comments'] = array();
       
   758 
       
   759 			$post['comments'] = apply_filters( 'wp_import_post_comments', $post['comments'], $post_id, $post );
       
   760 
       
   761 			// add/update comments
       
   762 			if ( ! empty( $post['comments'] ) ) {
       
   763 				$num_comments = 0;
       
   764 				$inserted_comments = array();
       
   765 				foreach ( $post['comments'] as $comment ) {
       
   766 					$comment_id	= $comment['comment_id'];
       
   767 					$newcomments[$comment_id]['comment_post_ID']      = $comment_post_ID;
       
   768 					$newcomments[$comment_id]['comment_author']       = $comment['comment_author'];
       
   769 					$newcomments[$comment_id]['comment_author_email'] = $comment['comment_author_email'];
       
   770 					$newcomments[$comment_id]['comment_author_IP']    = $comment['comment_author_IP'];
       
   771 					$newcomments[$comment_id]['comment_author_url']   = $comment['comment_author_url'];
       
   772 					$newcomments[$comment_id]['comment_date']         = $comment['comment_date'];
       
   773 					$newcomments[$comment_id]['comment_date_gmt']     = $comment['comment_date_gmt'];
       
   774 					$newcomments[$comment_id]['comment_content']      = $comment['comment_content'];
       
   775 					$newcomments[$comment_id]['comment_approved']     = $comment['comment_approved'];
       
   776 					$newcomments[$comment_id]['comment_type']         = $comment['comment_type'];
       
   777 					$newcomments[$comment_id]['comment_parent'] 	  = $comment['comment_parent'];
       
   778 					$newcomments[$comment_id]['commentmeta']          = isset( $comment['commentmeta'] ) ? $comment['commentmeta'] : array();
       
   779 					if ( isset( $this->processed_authors[$comment['comment_user_id']] ) )
       
   780 						$newcomments[$comment_id]['user_id'] = $this->processed_authors[$comment['comment_user_id']];
       
   781 				}
       
   782 				ksort( $newcomments );
       
   783 
       
   784 				foreach ( $newcomments as $key => $comment ) {
       
   785 					// if this is a new post we can skip the comment_exists() check
       
   786 					if ( ! $post_exists || ! comment_exists( $comment['comment_author'], $comment['comment_date'] ) ) {
       
   787 						if ( isset( $inserted_comments[$comment['comment_parent']] ) )
       
   788 							$comment['comment_parent'] = $inserted_comments[$comment['comment_parent']];
       
   789 						$comment = wp_slash( $comment );
       
   790 						$comment = wp_filter_comment( $comment );
       
   791 						$inserted_comments[$key] = wp_insert_comment( $comment );
       
   792 						do_action( 'wp_import_insert_comment', $inserted_comments[$key], $comment, $comment_post_ID, $post );
       
   793 
       
   794 						foreach( $comment['commentmeta'] as $meta ) {
       
   795 							$value = maybe_unserialize( $meta['value'] );
       
   796 							add_comment_meta( $inserted_comments[$key], $meta['key'], $value );
       
   797 						}
       
   798 
       
   799 						$num_comments++;
       
   800 					}
       
   801 				}
       
   802 				unset( $newcomments, $inserted_comments, $post['comments'] );
       
   803 			}
       
   804 
       
   805 			if ( ! isset( $post['postmeta'] ) )
       
   806 				$post['postmeta'] = array();
       
   807 
       
   808 			$post['postmeta'] = apply_filters( 'wp_import_post_meta', $post['postmeta'], $post_id, $post );
       
   809 
       
   810 			// add/update post meta
       
   811 			if ( ! empty( $post['postmeta'] ) ) {
       
   812 				foreach ( $post['postmeta'] as $meta ) {
       
   813 					$key = apply_filters( 'import_post_meta_key', $meta['key'], $post_id, $post );
       
   814 					$value = false;
       
   815 
       
   816 					if ( '_edit_last' == $key ) {
       
   817 						if ( isset( $this->processed_authors[intval($meta['value'])] ) )
       
   818 							$value = $this->processed_authors[intval($meta['value'])];
       
   819 						else
       
   820 							$key = false;
       
   821 					}
       
   822 
       
   823 					if ( $key ) {
       
   824 						// export gets meta straight from the DB so could have a serialized string
       
   825 						if ( ! $value )
       
   826 							$value = maybe_unserialize( $meta['value'] );
       
   827 
       
   828 						add_post_meta( $post_id, $key, $value );
       
   829 						do_action( 'import_post_meta', $post_id, $key, $value );
       
   830 
       
   831 						// if the post has a featured image, take note of this in case of remap
       
   832 						if ( '_thumbnail_id' == $key )
       
   833 							$this->featured_images[$post_id] = (int) $value;
       
   834 					}
       
   835 				}
       
   836 			}
       
   837 		}
       
   838 
       
   839 		unset( $this->posts );
       
   840 	}
       
   841 
       
   842 	/**
       
   843 	 * Attempt to create a new menu item from import data
       
   844 	 *
       
   845 	 * Fails for draft, orphaned menu items and those without an associated nav_menu
       
   846 	 * or an invalid nav_menu term. If the post type or term object which the menu item
       
   847 	 * represents doesn't exist then the menu item will not be imported (waits until the
       
   848 	 * end of the import to retry again before discarding).
       
   849 	 *
       
   850 	 * @param array $item Menu item details from WXR file
       
   851 	 */
       
   852 	function process_menu_item( $item ) {
       
   853 		// skip draft, orphaned menu items
       
   854 		if ( 'draft' == $item['status'] )
       
   855 			return;
       
   856 
       
   857 		$menu_slug = false;
       
   858 		if ( isset($item['terms']) ) {
       
   859 			// loop through terms, assume first nav_menu term is correct menu
       
   860 			foreach ( $item['terms'] as $term ) {
       
   861 				if ( 'nav_menu' == $term['domain'] ) {
       
   862 					$menu_slug = $term['slug'];
       
   863 					break;
       
   864 				}
       
   865 			}
       
   866 		}
       
   867 
       
   868 		// no nav_menu term associated with this menu item
       
   869 		if ( ! $menu_slug ) {
       
   870 			_e( 'Menu item skipped due to missing menu slug', 'wordpress-importer' );
       
   871 			echo '<br />';
       
   872 			return;
       
   873 		}
       
   874 
       
   875 		$menu_id = term_exists( $menu_slug, 'nav_menu' );
       
   876 		if ( ! $menu_id ) {
       
   877 			printf( __( 'Menu item skipped due to invalid menu slug: %s', 'wordpress-importer' ), esc_html( $menu_slug ) );
       
   878 			echo '<br />';
       
   879 			return;
       
   880 		} else {
       
   881 			$menu_id = is_array( $menu_id ) ? $menu_id['term_id'] : $menu_id;
       
   882 		}
       
   883 
       
   884 		foreach ( $item['postmeta'] as $meta )
       
   885 			${$meta['key']} = $meta['value'];
       
   886 
       
   887 		if ( 'taxonomy' == $_menu_item_type && isset( $this->processed_terms[intval($_menu_item_object_id)] ) ) {
       
   888 			$_menu_item_object_id = $this->processed_terms[intval($_menu_item_object_id)];
       
   889 		} else if ( 'post_type' == $_menu_item_type && isset( $this->processed_posts[intval($_menu_item_object_id)] ) ) {
       
   890 			$_menu_item_object_id = $this->processed_posts[intval($_menu_item_object_id)];
       
   891 		} else if ( 'custom' != $_menu_item_type ) {
       
   892 			// associated object is missing or not imported yet, we'll retry later
       
   893 			$this->missing_menu_items[] = $item;
       
   894 			return;
       
   895 		}
       
   896 
       
   897 		if ( isset( $this->processed_menu_items[intval($_menu_item_menu_item_parent)] ) ) {
       
   898 			$_menu_item_menu_item_parent = $this->processed_menu_items[intval($_menu_item_menu_item_parent)];
       
   899 		} else if ( $_menu_item_menu_item_parent ) {
       
   900 			$this->menu_item_orphans[intval($item['post_id'])] = (int) $_menu_item_menu_item_parent;
       
   901 			$_menu_item_menu_item_parent = 0;
       
   902 		}
       
   903 
       
   904 		// wp_update_nav_menu_item expects CSS classes as a space separated string
       
   905 		$_menu_item_classes = maybe_unserialize( $_menu_item_classes );
       
   906 		if ( is_array( $_menu_item_classes ) )
       
   907 			$_menu_item_classes = implode( ' ', $_menu_item_classes );
       
   908 
       
   909 		$args = array(
       
   910 			'menu-item-object-id' => $_menu_item_object_id,
       
   911 			'menu-item-object' => $_menu_item_object,
       
   912 			'menu-item-parent-id' => $_menu_item_menu_item_parent,
       
   913 			'menu-item-position' => intval( $item['menu_order'] ),
       
   914 			'menu-item-type' => $_menu_item_type,
       
   915 			'menu-item-title' => $item['post_title'],
       
   916 			'menu-item-url' => $_menu_item_url,
       
   917 			'menu-item-description' => $item['post_content'],
       
   918 			'menu-item-attr-title' => $item['post_excerpt'],
       
   919 			'menu-item-target' => $_menu_item_target,
       
   920 			'menu-item-classes' => $_menu_item_classes,
       
   921 			'menu-item-xfn' => $_menu_item_xfn,
       
   922 			'menu-item-status' => $item['status']
       
   923 		);
       
   924 
       
   925 		$id = wp_update_nav_menu_item( $menu_id, 0, $args );
       
   926 		if ( $id && ! is_wp_error( $id ) )
       
   927 			$this->processed_menu_items[intval($item['post_id'])] = (int) $id;
       
   928 	}
       
   929 
       
   930 	/**
       
   931 	 * If fetching attachments is enabled then attempt to create a new attachment
       
   932 	 *
       
   933 	 * @param array $post Attachment post details from WXR
       
   934 	 * @param string $url URL to fetch attachment from
       
   935 	 * @return int|WP_Error Post ID on success, WP_Error otherwise
       
   936 	 */
       
   937 	function process_attachment( $post, $url ) {
       
   938 		if ( ! $this->fetch_attachments )
       
   939 			return new WP_Error( 'attachment_processing_error',
       
   940 				__( 'Fetching attachments is not enabled', 'wordpress-importer' ) );
       
   941 
       
   942 		// if the URL is absolute, but does not contain address, then upload it assuming base_site_url
       
   943 		if ( preg_match( '|^/[\w\W]+$|', $url ) )
       
   944 			$url = rtrim( $this->base_url, '/' ) . $url;
       
   945 
       
   946 		$upload = $this->fetch_remote_file( $url, $post );
       
   947 		if ( is_wp_error( $upload ) )
       
   948 			return $upload;
       
   949 
       
   950 		if ( $info = wp_check_filetype( $upload['file'] ) )
       
   951 			$post['post_mime_type'] = $info['type'];
       
   952 		else
       
   953 			return new WP_Error( 'attachment_processing_error', __('Invalid file type', 'wordpress-importer') );
       
   954 
       
   955 		$post['guid'] = $upload['url'];
       
   956 
       
   957 		// as per wp-admin/includes/upload.php
       
   958 		$post_id = wp_insert_attachment( $post, $upload['file'] );
       
   959 		wp_update_attachment_metadata( $post_id, wp_generate_attachment_metadata( $post_id, $upload['file'] ) );
       
   960 
       
   961 		// remap resized image URLs, works by stripping the extension and remapping the URL stub.
       
   962 		if ( preg_match( '!^image/!', $info['type'] ) ) {
       
   963 			$parts = pathinfo( $url );
       
   964 			$name = basename( $parts['basename'], ".{$parts['extension']}" ); // PATHINFO_FILENAME in PHP 5.2
       
   965 
       
   966 			$parts_new = pathinfo( $upload['url'] );
       
   967 			$name_new = basename( $parts_new['basename'], ".{$parts_new['extension']}" );
       
   968 
       
   969 			$this->url_remap[$parts['dirname'] . '/' . $name] = $parts_new['dirname'] . '/' . $name_new;
       
   970 		}
       
   971 
       
   972 		return $post_id;
       
   973 	}
       
   974 
       
   975 	/**
       
   976 	 * Attempt to download a remote file attachment
       
   977 	 *
       
   978 	 * @param string $url URL of item to fetch
       
   979 	 * @param array $post Attachment details
       
   980 	 * @return array|WP_Error Local file location details on success, WP_Error otherwise
       
   981 	 */
       
   982 	function fetch_remote_file( $url, $post ) {
       
   983 		// extract the file name and extension from the url
       
   984 		$file_name = basename( $url );
       
   985 
       
   986 		// get placeholder file in the upload dir with a unique, sanitized filename
       
   987 		$upload = wp_upload_bits( $file_name, 0, '', $post['upload_date'] );
       
   988 		if ( $upload['error'] )
       
   989 			return new WP_Error( 'upload_dir_error', $upload['error'] );
       
   990 
       
   991 		// fetch the remote url and write it to the placeholder file
       
   992 		$remote_response = wp_safe_remote_get( $url, array(
       
   993 			'timeout' => 300,
       
   994             		'stream' => true,
       
   995             		'filename' => $upload['file'],
       
   996         	) );
       
   997 
       
   998 		$headers = wp_remote_retrieve_headers( $remote_response );
       
   999 
       
  1000 		// request failed
       
  1001 		if ( ! $headers ) {
       
  1002 			@unlink( $upload['file'] );
       
  1003 			return new WP_Error( 'import_file_error', __('Remote server did not respond', 'wordpress-importer') );
       
  1004 		}
       
  1005 
       
  1006 		$remote_response_code = wp_remote_retrieve_response_code( $remote_response );
       
  1007 
       
  1008 		// make sure the fetch was successful
       
  1009 		if ( $remote_response_code != '200' ) {
       
  1010 			@unlink( $upload['file'] );
       
  1011 			return new WP_Error( 'import_file_error', sprintf( __('Remote server returned error response %1$d %2$s', 'wordpress-importer'), esc_html($remote_response_code), get_status_header_desc($remote_response_code) ) );
       
  1012 		}
       
  1013 
       
  1014 		$filesize = filesize( $upload['file'] );
       
  1015 
       
  1016 		if ( isset( $headers['content-length'] ) && $filesize != $headers['content-length'] ) {
       
  1017 			@unlink( $upload['file'] );
       
  1018 			return new WP_Error( 'import_file_error', __('Remote file is incorrect size', 'wordpress-importer') );
       
  1019 		}
       
  1020 
       
  1021 		if ( 0 == $filesize ) {
       
  1022 			@unlink( $upload['file'] );
       
  1023 			return new WP_Error( 'import_file_error', __('Zero size file downloaded', 'wordpress-importer') );
       
  1024 		}
       
  1025 
       
  1026 		$max_size = (int) $this->max_attachment_size();
       
  1027 		if ( ! empty( $max_size ) && $filesize > $max_size ) {
       
  1028 			@unlink( $upload['file'] );
       
  1029 			return new WP_Error( 'import_file_error', sprintf(__('Remote file is too large, limit is %s', 'wordpress-importer'), size_format($max_size) ) );
       
  1030 		}
       
  1031 
       
  1032 		// keep track of the old and new urls so we can substitute them later
       
  1033 		$this->url_remap[$url] = $upload['url'];
       
  1034 		$this->url_remap[$post['guid']] = $upload['url']; // r13735, really needed?
       
  1035 		// keep track of the destination if the remote url is redirected somewhere else
       
  1036 		if ( isset($headers['x-final-location']) && $headers['x-final-location'] != $url )
       
  1037 			$this->url_remap[$headers['x-final-location']] = $upload['url'];
       
  1038 
       
  1039 		return $upload;
       
  1040 	}
       
  1041 
       
  1042 	/**
       
  1043 	 * Attempt to associate posts and menu items with previously missing parents
       
  1044 	 *
       
  1045 	 * An imported post's parent may not have been imported when it was first created
       
  1046 	 * so try again. Similarly for child menu items and menu items which were missing
       
  1047 	 * the object (e.g. post) they represent in the menu
       
  1048 	 */
       
  1049 	function backfill_parents() {
       
  1050 		global $wpdb;
       
  1051 
       
  1052 		// find parents for post orphans
       
  1053 		foreach ( $this->post_orphans as $child_id => $parent_id ) {
       
  1054 			$local_child_id = $local_parent_id = false;
       
  1055 			if ( isset( $this->processed_posts[$child_id] ) )
       
  1056 				$local_child_id = $this->processed_posts[$child_id];
       
  1057 			if ( isset( $this->processed_posts[$parent_id] ) )
       
  1058 				$local_parent_id = $this->processed_posts[$parent_id];
       
  1059 
       
  1060 			if ( $local_child_id && $local_parent_id ) {
       
  1061 				$wpdb->update( $wpdb->posts, array( 'post_parent' => $local_parent_id ), array( 'ID' => $local_child_id ), '%d', '%d' );
       
  1062 				clean_post_cache( $local_child_id );
       
  1063 			}
       
  1064 		}
       
  1065 
       
  1066 		// all other posts/terms are imported, retry menu items with missing associated object
       
  1067 		$missing_menu_items = $this->missing_menu_items;
       
  1068 		foreach ( $missing_menu_items as $item )
       
  1069 			$this->process_menu_item( $item );
       
  1070 
       
  1071 		// find parents for menu item orphans
       
  1072 		foreach ( $this->menu_item_orphans as $child_id => $parent_id ) {
       
  1073 			$local_child_id = $local_parent_id = 0;
       
  1074 			if ( isset( $this->processed_menu_items[$child_id] ) )
       
  1075 				$local_child_id = $this->processed_menu_items[$child_id];
       
  1076 			if ( isset( $this->processed_menu_items[$parent_id] ) )
       
  1077 				$local_parent_id = $this->processed_menu_items[$parent_id];
       
  1078 
       
  1079 			if ( $local_child_id && $local_parent_id )
       
  1080 				update_post_meta( $local_child_id, '_menu_item_menu_item_parent', (int) $local_parent_id );
       
  1081 		}
       
  1082 	}
       
  1083 
       
  1084 	/**
       
  1085 	 * Use stored mapping information to update old attachment URLs
       
  1086 	 */
       
  1087 	function backfill_attachment_urls() {
       
  1088 		global $wpdb;
       
  1089 		// make sure we do the longest urls first, in case one is a substring of another
       
  1090 		uksort( $this->url_remap, array(&$this, 'cmpr_strlen') );
       
  1091 
       
  1092 		foreach ( $this->url_remap as $from_url => $to_url ) {
       
  1093 			// remap urls in post_content
       
  1094 			$wpdb->query( $wpdb->prepare("UPDATE {$wpdb->posts} SET post_content = REPLACE(post_content, %s, %s)", $from_url, $to_url) );
       
  1095 			// remap enclosure urls
       
  1096 			$result = $wpdb->query( $wpdb->prepare("UPDATE {$wpdb->postmeta} SET meta_value = REPLACE(meta_value, %s, %s) WHERE meta_key='enclosure'", $from_url, $to_url) );
       
  1097 		}
       
  1098 	}
       
  1099 
       
  1100 	/**
       
  1101 	 * Update _thumbnail_id meta to new, imported attachment IDs
       
  1102 	 */
       
  1103 	function remap_featured_images() {
       
  1104 		// cycle through posts that have a featured image
       
  1105 		foreach ( $this->featured_images as $post_id => $value ) {
       
  1106 			if ( isset( $this->processed_posts[$value] ) ) {
       
  1107 				$new_id = $this->processed_posts[$value];
       
  1108 				// only update if there's a difference
       
  1109 				if ( $new_id != $value )
       
  1110 					update_post_meta( $post_id, '_thumbnail_id', $new_id );
       
  1111 			}
       
  1112 		}
       
  1113 	}
       
  1114 
       
  1115 	/**
       
  1116 	 * Parse a WXR file
       
  1117 	 *
       
  1118 	 * @param string $file Path to WXR file for parsing
       
  1119 	 * @return array Information gathered from the WXR file
       
  1120 	 */
       
  1121 	function parse( $file ) {
       
  1122 		$parser = new WXR_Parser();
       
  1123 		return $parser->parse( $file );
       
  1124 	}
       
  1125 
       
  1126 	// Display import page title
       
  1127 	function header() {
       
  1128 		echo '<div class="wrap">';
       
  1129 		echo '<h2>' . __( 'Import WordPress', 'wordpress-importer' ) . '</h2>';
       
  1130 
       
  1131 		$updates = get_plugin_updates();
       
  1132 		$basename = plugin_basename(__FILE__);
       
  1133 		if ( isset( $updates[$basename] ) ) {
       
  1134 			$update = $updates[$basename];
       
  1135 			echo '<div class="error"><p><strong>';
       
  1136 			printf( __( 'A new version of this importer is available. Please update to version %s to ensure compatibility with newer export files.', 'wordpress-importer' ), $update->update->new_version );
       
  1137 			echo '</strong></p></div>';
       
  1138 		}
       
  1139 	}
       
  1140 
       
  1141 	// Close div.wrap
       
  1142 	function footer() {
       
  1143 		echo '</div>';
       
  1144 	}
       
  1145 
       
  1146 	/**
       
  1147 	 * Display introductory text and file upload form
       
  1148 	 */
       
  1149 	function greet() {
       
  1150 		echo '<div class="narrow">';
       
  1151 		echo '<p>'.__( 'Howdy! Upload your WordPress eXtended RSS (WXR) file and we&#8217;ll import the posts, pages, comments, custom fields, categories, and tags into this site.', 'wordpress-importer' ).'</p>';
       
  1152 		echo '<p>'.__( 'Choose a WXR (.xml) file to upload, then click Upload file and import.', 'wordpress-importer' ).'</p>';
       
  1153 		wp_import_upload_form( 'admin.php?import=wordpress&amp;step=1' );
       
  1154 		echo '</div>';
       
  1155 	}
       
  1156 
       
  1157 	/**
       
  1158 	 * Decide if the given meta key maps to information we will want to import
       
  1159 	 *
       
  1160 	 * @param string $key The meta key to check
       
  1161 	 * @return string|bool The key if we do want to import, false if not
       
  1162 	 */
       
  1163 	function is_valid_meta_key( $key ) {
       
  1164 		// skip attachment metadata since we'll regenerate it from scratch
       
  1165 		// skip _edit_lock as not relevant for import
       
  1166 		if ( in_array( $key, array( '_wp_attached_file', '_wp_attachment_metadata', '_edit_lock' ) ) )
       
  1167 			return false;
       
  1168 		return $key;
       
  1169 	}
       
  1170 
       
  1171 	/**
       
  1172 	 * Decide whether or not the importer is allowed to create users.
       
  1173 	 * Default is true, can be filtered via import_allow_create_users
       
  1174 	 *
       
  1175 	 * @return bool True if creating users is allowed
       
  1176 	 */
       
  1177 	function allow_create_users() {
       
  1178 		return apply_filters( 'import_allow_create_users', true );
       
  1179 	}
       
  1180 
       
  1181 	/**
       
  1182 	 * Decide whether or not the importer should attempt to download attachment files.
       
  1183 	 * Default is true, can be filtered via import_allow_fetch_attachments. The choice
       
  1184 	 * made at the import options screen must also be true, false here hides that checkbox.
       
  1185 	 *
       
  1186 	 * @return bool True if downloading attachments is allowed
       
  1187 	 */
       
  1188 	function allow_fetch_attachments() {
       
  1189 		return apply_filters( 'import_allow_fetch_attachments', true );
       
  1190 	}
       
  1191 
       
  1192 	/**
       
  1193 	 * Decide what the maximum file size for downloaded attachments is.
       
  1194 	 * Default is 0 (unlimited), can be filtered via import_attachment_size_limit
       
  1195 	 *
       
  1196 	 * @return int Maximum attachment file size to import
       
  1197 	 */
       
  1198 	function max_attachment_size() {
       
  1199 		return apply_filters( 'import_attachment_size_limit', 0 );
       
  1200 	}
       
  1201 
       
  1202 	/**
       
  1203 	 * Added to http_request_timeout filter to force timeout at 60 seconds during import
       
  1204 	 * @return int 60
       
  1205 	 */
       
  1206 	function bump_request_timeout( $val ) {
       
  1207 		return 60;
       
  1208 	}
       
  1209 
       
  1210 	// return the difference in length between two strings
       
  1211 	function cmpr_strlen( $a, $b ) {
       
  1212 		return strlen($b) - strlen($a);
       
  1213 	}
       
  1214 }
       
  1215 
       
  1216 } // class_exists( 'WP_Importer' )
       
  1217 
       
  1218 function wordpress_importer_init() {
       
  1219 	load_plugin_textdomain( 'wordpress-importer' );
       
  1220 
       
  1221 	/**
       
  1222 	 * WordPress Importer object for registering the import callback
       
  1223 	 * @global WP_Import $wp_import
       
  1224 	 */
       
  1225 	$GLOBALS['wp_import'] = new WP_Import();
       
  1226 	register_importer( 'wordpress', 'WordPress', __('Import <strong>posts, pages, comments, custom fields, categories, and tags</strong> from a WordPress export file.', 'wordpress-importer'), array( $GLOBALS['wp_import'], 'dispatch' ) );
       
  1227 }
       
  1228 add_action( 'admin_init', 'wordpress_importer_init' );