balmet.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

WXRImporter.php (85925B)


      1 <?php
      2 namespace ProteusThemes\WPContentImporter2;
      3 
      4 ini_set( 'display_startup_errors', 1 );
      5 ini_set( 'display_errors', 1 );
      6 error_reporting( -1 );
      7 class WXRImporter extends \WP_Importer {
      8 
      9 	/**
     10 	 * Maximum supported WXR version
     11 	 */
     12 	const MAX_WXR_VERSION = 1.2;
     13 
     14 	/**
     15 	 * Regular expression for checking if a post references an attachment
     16 	 *
     17 	 * Note: This is a quick, weak check just to exclude text-only posts. More
     18 	 * vigorous checking is done later to verify.
     19 	 */
     20 	const REGEX_HAS_ATTACHMENT_REFS = '!
     21 		(
     22 			# Match anything with an image or attachment class
     23 			class=[\'"].*?\b(wp-image-\d+|attachment-[\w\-]+)\b
     24 		|
     25 			# Match anything that looks like an upload URL
     26 			src=[\'"][^\'"]*(
     27 				[0-9]{4}/[0-9]{2}/[^\'"]+\.(jpg|jpeg|png|gif)
     28 			|
     29 				content/uploads[^\'"]+
     30 			)[\'"]
     31 		)!ix';
     32 
     33 	/**
     34 	 * Version of WXR we're importing.
     35 	 *
     36 	 * Defaults to 1.0 for compatibility. Typically overridden by a
     37 	 * `<wp:wxr_version>` tag at the start of the file.
     38 	 *
     39 	 * @var string
     40 	 */
     41 	protected $version = '1.0';
     42 
     43 	// information to import from WXR file
     44 	protected $categories  = array();
     45 	protected $tags        = array();
     46 	protected $base_url    = '';
     47 	protected $images_list = array();
     48 	// TODO: REMOVE THESE
     49 	protected $processed_terms      = array();
     50 	protected $processed_posts      = array();
     51 	protected $processed_menu_items = array();
     52 	protected $menu_item_orphans    = array();
     53 	protected $missing_menu_items   = array();
     54 
     55 	// NEW STYLE
     56 	protected $mapping            = array();
     57 	protected $requires_remapping = array();
     58 	protected $exists             = array();
     59 	protected $user_slug_override = array();
     60 
     61 	protected $url_remap       = array();
     62 	protected $featured_images = array();
     63 	// path for image download from local
     64 	protected $path = '';
     65 	// replace link if not image
     66 	protected $url = '';
     67 
     68 	protected $image_url = '';
     69 
     70 	/**
     71 	 * Logger instance.
     72 	 *
     73 	 * @var WP_Importer_Logger
     74 	 */
     75 	protected $logger;
     76 
     77 	/**
     78 	 * Constructor
     79 	 *
     80 	 * @param array $options {
     81 	 * @var   bool $prefill_existing_posts Should we prefill `post_exists` calls? (True prefills and uses more memory, false checks once per imported post and takes longer. Default is true.)
     82 	 * @var   bool $prefill_existing_comments Should we prefill `comment_exists` calls? (True prefills and uses more memory, false checks once per imported comment and takes longer. Default is true.)
     83 	 * @var   bool $prefill_existing_terms Should we prefill `term_exists` calls? (True prefills and uses more memory, false checks once per imported term and takes longer. Default is true.)
     84 	 * @var   bool $update_attachment_guids Should attachment GUIDs be updated to the new URL? (True updates the GUID, which keeps compatibility with v1, false doesn't update, and allows deduplication and reimporting. Default is false.)
     85 	 * @var   bool $fetch_attachments Fetch attachments from the remote server. (True fetches and creates attachment posts, false skips attachments. Default is false.)
     86 	 * @var   bool $aggressive_url_search Should we search/replace for URLs aggressively? (True searches all posts' content for old URLs and replaces, false checks for `<img class="wp-image-*">` only. Default is false.)
     87 	 * @var   int $default_author User ID to use if author is missing or invalid. (Default is null, which leaves posts unassigned.)
     88 	 * }
     89 	 */
     90 	public function __construct( $options = array() ) {
     91 		// Initialize some important variables
     92 		$empty_types = array(
     93 			'post'    => array(),
     94 			'comment' => array(),
     95 			'term'    => array(),
     96 			'user'    => array(),
     97 		);
     98 
     99 		$this->mapping              = $empty_types;
    100 		$this->mapping['user_slug'] = array();
    101 		$this->mapping['term_id']   = array();
    102 		$this->requires_remapping   = $empty_types;
    103 		$this->exists               = $empty_types;
    104 
    105 		$this->options = wp_parse_args(
    106 			$options,
    107 			array(
    108 				'prefill_existing_posts'    => true,
    109 				'prefill_existing_comments' => true,
    110 				'prefill_existing_terms'    => true,
    111 				'update_attachment_guids'   => false,
    112 				'fetch_attachments'         => false,
    113 				'aggressive_url_search'     => false,
    114 				'default_author'            => null,
    115 			)
    116 		);
    117 	}
    118 
    119 	public function set_logger( $logger ) {
    120 		$this->logger = $logger;
    121 	}
    122 
    123 	/**
    124 	 * Get a stream reader for the file.
    125 	 *
    126 	 * @param  string $file Path to the XML file.
    127 	 * @return XMLReader|WP_Error Reader instance on success, error otherwise.
    128 	 */
    129 	protected function get_reader( $file ) {
    130 		// Avoid loading external entities for security
    131 		$old_value = null;
    132 		if ( function_exists( 'libxml_disable_entity_loader' ) ) {
    133 			// $old_value = libxml_disable_entity_loader( true );
    134 		}
    135 
    136 		$reader = new \XMLReader();
    137 		$status = $reader->open( $file );
    138 
    139 		if ( ! is_null( $old_value ) ) {
    140 			// libxml_disable_entity_loader( $old_value );
    141 		}
    142 
    143 		if ( ! $status ) {
    144 			return new \WP_Error( 'wxr_importer.cannot_parse', __( 'Could not open the file for parsing', 'wordpress-importer' ) );
    145 		}
    146 
    147 		return $reader;
    148 	}
    149 
    150 	/**
    151 	 * The main controller for the actual import stage.
    152 	 *
    153 	 * @param string $file Path to the WXR file for importing
    154 	 */
    155 	public function get_preliminary_information( $file ) {
    156 		// Let's run the actual importer now, woot
    157 		$reader = $this->get_reader( $file );
    158 		if ( is_wp_error( $reader ) ) {
    159 			return $reader;
    160 		}
    161 
    162 		// Set the version to compatibility mode first
    163 		$this->version = '1.0';
    164 
    165 		// Start parsing!
    166 		$data = new WXRImportInfo();
    167 		while ( $reader->read() ) {
    168 			// Only deal with element opens
    169 			if ( $reader->nodeType !== \XMLReader::ELEMENT ) {
    170 				continue;
    171 			}
    172 
    173 			switch ( $reader->name ) {
    174 				case 'wp:wxr_version':
    175 					// Upgrade to the correct version
    176 					$this->version = $reader->readString();
    177 
    178 					if ( version_compare( $this->version, self::MAX_WXR_VERSION, '>' ) ) {
    179 						$this->logger->warning(
    180 							sprintf(
    181 								__( 'This WXR file (version %1$s) is newer than the importer (version %2$s) and may not be supported. Please consider updating.', 'wordpress-importer' ),
    182 								$this->version,
    183 								self::MAX_WXR_VERSION
    184 							)
    185 						);
    186 					}
    187 
    188 					// Handled everything in this node, move on to the next
    189 					$reader->next();
    190 					break;
    191 
    192 				case 'generator':
    193 					$data->generator = $reader->readString();
    194 					$reader->next();
    195 					break;
    196 
    197 				case 'title':
    198 					$data->title = $reader->readString();
    199 					$reader->next();
    200 					break;
    201 
    202 				case 'wp:base_site_url':
    203 					$data->siteurl = $reader->readString();
    204 					$reader->next();
    205 					break;
    206 
    207 				case 'wp:base_blog_url':
    208 					$data->home = $reader->readString();
    209 					$reader->next();
    210 					break;
    211 
    212 				case 'wp:author':
    213 					$node = $reader->expand();
    214 
    215 					$parsed = $this->parse_author_node( $node );
    216 					if ( is_wp_error( $parsed ) ) {
    217 						$this->log_error( $parsed );
    218 
    219 						// Skip the rest of this post
    220 						$reader->next();
    221 						break;
    222 					}
    223 
    224 					$data->users[] = $parsed;
    225 
    226 					// Handled everything in this node, move on to the next
    227 					$reader->next();
    228 					break;
    229 
    230 				case 'item':
    231 					$node   = $reader->expand();
    232 					$parsed = $this->parse_post_node( $node );
    233 					if ( is_wp_error( $parsed ) ) {
    234 						$this->log_error( $parsed );
    235 
    236 						// Skip the rest of this post
    237 						$reader->next();
    238 						break;
    239 					}
    240 
    241 					if ( $parsed['data']['post_type'] === 'attachment' ) {
    242 						$data->media_count++;
    243 					} else {
    244 						$data->post_count++;
    245 					}
    246 					$data->comment_count += count( $parsed['comments'] );
    247 
    248 					// Handled everything in this node, move on to the next
    249 					$reader->next();
    250 					break;
    251 
    252 				case 'wp:category':
    253 				case 'wp:tag':
    254 				case 'wp:term':
    255 					$data->term_count++;
    256 
    257 					// Handled everything in this node, move on to the next
    258 					$reader->next();
    259 					break;
    260 			}
    261 		}
    262 
    263 		$data->version = $this->version;
    264 
    265 		return $data;
    266 	}
    267 
    268 	/**
    269 	 * The main controller for the actual import stage.
    270 	 *
    271 	 * @param string $file Path to the WXR file for importing
    272 	 */
    273 	public function parse_authors( $file ) {
    274 		// Let's run the actual importer now, woot
    275 		$reader = $this->get_reader( $file );
    276 		if ( is_wp_error( $reader ) ) {
    277 			return $reader;
    278 		}
    279 
    280 		// Set the version to compatibility mode first
    281 		$this->version = '1.0';
    282 
    283 		// Start parsing!
    284 		$authors = array();
    285 		while ( $reader->read() ) {
    286 			// Only deal with element opens
    287 			if ( $reader->nodeType !== \XMLReader::ELEMENT ) {
    288 				continue;
    289 			}
    290 
    291 			switch ( $reader->name ) {
    292 				case 'wp:wxr_version':
    293 					// Upgrade to the correct version
    294 					$this->version = $reader->readString();
    295 
    296 					if ( version_compare( $this->version, self::MAX_WXR_VERSION, '>' ) ) {
    297 						$this->logger->warning(
    298 							sprintf(
    299 								__( 'This WXR file (version %1$s) is newer than the importer (version %2$s) and may not be supported. Please consider updating.', 'wordpress-importer' ),
    300 								$this->version,
    301 								self::MAX_WXR_VERSION
    302 							)
    303 						);
    304 					}
    305 
    306 					// Handled everything in this node, move on to the next
    307 					$reader->next();
    308 					break;
    309 
    310 				case 'wp:author':
    311 					$node = $reader->expand();
    312 
    313 					$parsed = $this->parse_author_node( $node );
    314 					if ( is_wp_error( $parsed ) ) {
    315 						$this->log_error( $parsed );
    316 
    317 						// Skip the rest of this post
    318 						$reader->next();
    319 						break;
    320 					}
    321 
    322 					$authors[] = $parsed;
    323 
    324 					// Handled everything in this node, move on to the next
    325 					$reader->next();
    326 					break;
    327 			}
    328 		}
    329 
    330 		return $authors;
    331 	}
    332 
    333 	/**
    334 	 * The main controller for the actual import stage.
    335 	 *
    336 	 * @param string $file Path to the WXR file for importing
    337 	 */
    338 	public function import( $file ) {
    339 		add_filter( 'import_post_meta_key', array( $this, 'is_valid_meta_key' ) );
    340 		add_filter( 'http_request_timeout', array( &$this, 'bump_request_timeout' ) );
    341 
    342 		$result = $this->import_start( $file );
    343 		if ( is_wp_error( $result ) ) {
    344 			return $result;
    345 		}
    346 
    347 		// Let's run the actual importer now, woot
    348 		$reader = $this->get_reader( $file );
    349 		if ( is_wp_error( $reader ) ) {
    350 			return $reader;
    351 		}
    352 
    353 		// Set the version to compatibility mode first
    354 		$this->version = '1.0';
    355 
    356 		// Reset other variables
    357 		$this->base_url = '';
    358 
    359 		// Start parsing!
    360 		while ( $reader->read() ) {
    361 			// Only deal with element opens
    362 			if ( $reader->nodeType !== \XMLReader::ELEMENT ) {
    363 				continue;
    364 			}
    365 
    366 			switch ( $reader->name ) {
    367 				case 'wp:wxr_version':
    368 					// Upgrade to the correct version
    369 					$this->version = $reader->readString();
    370 
    371 					if ( version_compare( $this->version, self::MAX_WXR_VERSION, '>' ) ) {
    372 						$this->logger->warning(
    373 							sprintf(
    374 								__( 'This WXR file (version %1$s) is newer than the importer (version %2$s) and may not be supported. Please consider updating.', 'wordpress-importer' ),
    375 								$this->version,
    376 								self::MAX_WXR_VERSION
    377 							)
    378 						);
    379 					}
    380 
    381 					// Handled everything in this node, move on to the next
    382 					$reader->next();
    383 					break;
    384 
    385 				case 'wp:base_site_url':
    386 					$this->base_url = $reader->readString();
    387 
    388 					// Handled everything in this node, move on to the next
    389 					$reader->next();
    390 					break;
    391 
    392 				case 'item':
    393 					$node   = $reader->expand();
    394 					$parsed = $this->parse_post_node( $node );
    395 					if ( is_wp_error( $parsed ) ) {
    396 						$this->log_error( $parsed );
    397 
    398 						// Skip the rest of this post
    399 						$reader->next();
    400 						break;
    401 					}
    402 
    403 					$this->process_post( $parsed['data'], $parsed['meta'], $parsed['comments'], $parsed['terms'] );
    404 
    405 					// Handled everything in this node, move on to the next
    406 					$reader->next();
    407 					break;
    408 
    409 				case 'wp:author':
    410 					$node = $reader->expand();
    411 
    412 					$parsed = $this->parse_author_node( $node );
    413 					if ( is_wp_error( $parsed ) ) {
    414 						$this->log_error( $parsed );
    415 
    416 						// Skip the rest of this post
    417 						$reader->next();
    418 						break;
    419 					}
    420 
    421 					$status = $this->process_author( $parsed['data'], $parsed['meta'] );
    422 					if ( is_wp_error( $status ) ) {
    423 						$this->log_error( $status );
    424 					}
    425 
    426 					// Handled everything in this node, move on to the next
    427 					$reader->next();
    428 					break;
    429 
    430 				case 'wp:category':
    431 					$node = $reader->expand();
    432 
    433 					$parsed = $this->parse_term_node( $node, 'category' );
    434 					if ( is_wp_error( $parsed ) ) {
    435 						$this->log_error( $parsed );
    436 
    437 						// Skip the rest of this post
    438 						$reader->next();
    439 						break;
    440 					}
    441 
    442 					$status = $this->process_term( $parsed['data'], $parsed['meta'] );
    443 
    444 					// Handled everything in this node, move on to the next
    445 					$reader->next();
    446 					break;
    447 
    448 				case 'wp:tag':
    449 					$node = $reader->expand();
    450 
    451 					$parsed = $this->parse_term_node( $node, 'tag' );
    452 					if ( is_wp_error( $parsed ) ) {
    453 						$this->log_error( $parsed );
    454 
    455 						// Skip the rest of this post
    456 						$reader->next();
    457 						break;
    458 					}
    459 
    460 					$status = $this->process_term( $parsed['data'], $parsed['meta'] );
    461 
    462 					// Handled everything in this node, move on to the next
    463 					$reader->next();
    464 					break;
    465 
    466 				case 'wp:term':
    467 					$node = $reader->expand();
    468 
    469 					$parsed = $this->parse_term_node( $node );
    470 					if ( is_wp_error( $parsed ) ) {
    471 						$this->log_error( $parsed );
    472 
    473 						// Skip the rest of this post
    474 						$reader->next();
    475 						break;
    476 					}
    477 
    478 					$status = $this->process_term( $parsed['data'], $parsed['meta'] );
    479 
    480 					// Handled everything in this node, move on to the next
    481 					$reader->next();
    482 					break;
    483 
    484 				default:
    485 					// Skip this node, probably handled by something already
    486 					break;
    487 			}
    488 		}
    489 
    490 		// Now that we've done the main processing, do any required
    491 		// post-processing and remapping.
    492 		$this->post_process();
    493 
    494 		if ( $this->options['aggressive_url_search'] ) {
    495 			$this->replace_attachment_urls_in_content();
    496 		}
    497 
    498 		$this->remap_featured_images();
    499 
    500 		$this->import_end();
    501 	}
    502 
    503 	/**
    504 	 * Log an error instance to the logger.
    505 	 *
    506 	 * @param WP_Error $error Error instance to log.
    507 	 */
    508 	protected function log_error( WP_Error $error ) {
    509 		$this->logger->warning( $error->get_error_message() );
    510 
    511 		// Log the data as debug info too
    512 		$data = $error->get_error_data();
    513 		if ( ! empty( $data ) ) {
    514 			$this->logger->debug( var_export( $data, true ) );
    515 		}
    516 	}
    517 
    518 	/**
    519 	 * Parses the WXR file and prepares us for the task of processing parsed data
    520 	 *
    521 	 * @param string $file Path to the WXR file for importing
    522 	 */
    523 	protected function import_start( $file ) {
    524 		if ( ! is_file( $file ) ) {
    525 			return new \WP_Error( 'wxr_importer.file_missing', __( 'The file does not exist, please try again.', 'wordpress-importer' ) );
    526 		}
    527 
    528 		// Suspend bunches of stuff in WP core
    529 		wp_defer_term_counting( true );
    530 		wp_defer_comment_counting( true );
    531 		wp_suspend_cache_invalidation( true );
    532 
    533 		// Prefill exists calls if told to
    534 		if ( $this->options['prefill_existing_posts'] ) {
    535 			$this->prefill_existing_posts();
    536 		}
    537 		if ( $this->options['prefill_existing_comments'] ) {
    538 			$this->prefill_existing_comments();
    539 		}
    540 		if ( $this->options['prefill_existing_terms'] ) {
    541 			$this->prefill_existing_terms();
    542 		}
    543 
    544 		/**
    545 		 * Begin the import.
    546 		 *
    547 		 * Fires before the import process has begun. If you need to suspend
    548 		 * caching or heavy processing on hooks, do so here.
    549 		 */
    550 		do_action( 'import_start' );
    551 	}
    552 
    553 	/**
    554 	 * Performs post-import cleanup of files and the cache
    555 	 */
    556 	protected function import_end() {
    557 		// Re-enable stuff in core
    558 		wp_suspend_cache_invalidation( false );
    559 		wp_cache_flush();
    560 		foreach ( get_taxonomies() as $tax ) {
    561 			delete_option( "{$tax}_children" );
    562 			_get_term_hierarchy( $tax );
    563 		}
    564 
    565 		wp_defer_term_counting( false );
    566 		wp_defer_comment_counting( false );
    567 
    568 		/**
    569 		 * Complete the import.
    570 		 *
    571 		 * Fires after the import process has finished. If you need to update
    572 		 * your cache or re-enable processing, do so here.
    573 		 */
    574 		do_action( 'import_end' );
    575 	}
    576 
    577 	/**
    578 	 * Set the user mapping.
    579 	 *
    580 	 * @param array $mapping List of map arrays (containing `old_slug`, `old_id`, `new_id`)
    581 	 */
    582 	public function set_user_mapping( $mapping ) {
    583 		foreach ( $mapping as $map ) {
    584 			if ( empty( $map['old_slug'] ) || empty( $map['old_id'] ) || empty( $map['new_id'] ) ) {
    585 				$this->logger->warning( __( 'Invalid author mapping', 'wordpress-importer' ) );
    586 				$this->logger->debug( var_export( $map, true ) );
    587 				continue;
    588 			}
    589 
    590 			$old_slug = $map['old_slug'];
    591 			$old_id   = $map['old_id'];
    592 			$new_id   = $map['new_id'];
    593 
    594 			$this->mapping['user'][ $old_id ]        = $new_id;
    595 			$this->mapping['user_slug'][ $old_slug ] = $new_id;
    596 		}
    597 	}
    598 
    599 	/**
    600 	 * Set the user slug overrides.
    601 	 *
    602 	 * Allows overriding the slug in the import with a custom/renamed version.
    603 	 *
    604 	 * @param string[] $overrides Map of old slug to new slug.
    605 	 */
    606 	public function set_user_slug_overrides( $overrides ) {
    607 		foreach ( $overrides as $original => $renamed ) {
    608 			$this->user_slug_override[ $original ] = $renamed;
    609 		}
    610 	}
    611 
    612 	/**
    613 	 * Parse a post node into post data.
    614 	 *
    615 	 * @param  DOMElement $node Parent node of post data (typically `item`).
    616 	 * @return array|WP_Error Post data array on success, error otherwise.
    617 	 */
    618 	protected function parse_post_node( $node ) {
    619 		$data     = array();
    620 		$meta     = array();
    621 		$comments = array();
    622 		$terms    = array();
    623 
    624 		foreach ( $node->childNodes as $child ) {
    625 			// We only care about child elements
    626 			if ( $child->nodeType !== XML_ELEMENT_NODE ) {
    627 				continue;
    628 			}
    629 
    630 			switch ( $child->tagName ) {
    631 				case 'wp:post_type':
    632 					$data['post_type'] = $child->textContent;
    633 					break;
    634 
    635 				case 'title':
    636 					$data['post_title'] = $child->textContent;
    637 					break;
    638 
    639 				case 'guid':
    640 					$data['guid'] = $child->textContent;
    641 					break;
    642 
    643 				case 'dc:creator':
    644 					$data['post_author'] = $child->textContent;
    645 					break;
    646 
    647 				case 'content:encoded':
    648 					$data['post_content'] = $child->textContent;
    649 					break;
    650 
    651 				case 'excerpt:encoded':
    652 					$data['post_excerpt'] = $child->textContent;
    653 					break;
    654 
    655 				case 'wp:post_id':
    656 					$data['post_id'] = $child->textContent;
    657 					break;
    658 
    659 				case 'wp:post_date':
    660 					$data['post_date'] = $child->textContent;
    661 					break;
    662 
    663 				case 'wp:post_date_gmt':
    664 					$data['post_date_gmt'] = $child->textContent;
    665 					break;
    666 
    667 				case 'wp:comment_status':
    668 					$data['comment_status'] = $child->textContent;
    669 					break;
    670 
    671 				case 'wp:ping_status':
    672 					$data['ping_status'] = $child->textContent;
    673 					break;
    674 
    675 				case 'wp:post_name':
    676 					$data['post_name'] = $child->textContent;
    677 					break;
    678 
    679 				case 'wp:status':
    680 					$data['post_status'] = $child->textContent;
    681 
    682 					if ( $data['post_status'] === 'auto-draft' ) {
    683 						// Bail now
    684 						return new \WP_Error(
    685 							'wxr_importer.post.cannot_import_draft',
    686 							__( 'Cannot import auto-draft posts' ),
    687 							$data
    688 						);
    689 					}
    690 					break;
    691 
    692 				case 'wp:post_parent':
    693 					$data['post_parent'] = $child->textContent;
    694 					break;
    695 
    696 				case 'wp:menu_order':
    697 					$data['menu_order'] = $child->textContent;
    698 					break;
    699 
    700 				case 'wp:post_password':
    701 					$data['post_password'] = $child->textContent;
    702 					break;
    703 
    704 				case 'wp:is_sticky':
    705 					$data['is_sticky'] = $child->textContent;
    706 					break;
    707 
    708 				case 'wp:attachment_url':
    709 					$data['attachment_url'] = $child->textContent;
    710 					break;
    711 
    712 				case 'wp:postmeta':
    713 					$meta_item = $this->parse_meta_node( $child );
    714 					if ( ! empty( $meta_item ) ) {
    715 						$meta[] = $meta_item;
    716 					}
    717 					break;
    718 
    719 				case 'wp:comment':
    720 					$comment_item = $this->parse_comment_node( $child );
    721 					if ( ! empty( $comment_item ) ) {
    722 						$comments[] = $comment_item;
    723 					}
    724 					break;
    725 
    726 				case 'category':
    727 					$term_item = $this->parse_category_node( $child );
    728 					if ( ! empty( $term_item ) ) {
    729 						$terms[] = $term_item;
    730 					}
    731 					break;
    732 			}
    733 		}
    734 
    735 		return compact( 'data', 'meta', 'comments', 'terms' );
    736 	}
    737 
    738 	/**
    739 	 * Create new posts based on import information
    740 	 *
    741 	 * Posts marked as having a parent which doesn't exist will become top level items.
    742 	 * Doesn't create a new post if: the post type doesn't exist, the given post ID
    743 	 * is already noted as imported or a post with the same title and date already exists.
    744 	 * Note that new/updated terms, comments and meta are imported for the last of the above.
    745 	 */
    746 	protected function process_post( $data, $meta, $comments, $terms ) {
    747 		/**
    748 		 * Pre-process post data.
    749 		 *
    750 		 * @param array $data Post data. (Return empty to skip.)
    751 		 * @param array $meta Meta data.
    752 		 * @param array $comments Comments on the post.
    753 		 * @param array $terms Terms on the post.
    754 		 */
    755 		$data = apply_filters( 'wxr_importer.pre_process.post', $data, $meta, $comments, $terms );
    756 		if ( empty( $data ) ) {
    757 			return false;
    758 		}
    759 
    760 		$original_id = isset( $data['post_id'] ) ? (int) $data['post_id'] : 0;
    761 		$parent_id   = isset( $data['post_parent'] ) ? (int) $data['post_parent'] : 0;
    762 		$author_id   = isset( $data['post_author'] ) ? (int) $data['post_author'] : 0;
    763 
    764 		// Have we already processed this?
    765 		if ( isset( $this->mapping['post'][ $original_id ] ) ) {
    766 			return;
    767 		}
    768 
    769 		$post_type_object = get_post_type_object( $data['post_type'] );
    770 
    771 		// Is this type even valid?
    772 		if ( ! $post_type_object ) {
    773 			$this->logger->warning(
    774 				sprintf(
    775 					__( 'Failed to import "%1$s": Invalid post type %2$s', 'wordpress-importer' ),
    776 					$data['post_title'],
    777 					$data['post_type']
    778 				)
    779 			);
    780 			return false;
    781 		}
    782 
    783 		$post_exists = $this->post_exists( $data );
    784 		if ( $post_exists ) {
    785 			$this->logger->info(
    786 				sprintf(
    787 					__( '%1$s "%2$s" already exists.', 'wordpress-importer' ),
    788 					$post_type_object->labels->singular_name,
    789 					$data['post_title']
    790 				)
    791 			);
    792 
    793 			// Even though this post already exists, new comments might need importing
    794 			$this->process_comments( $comments, $original_id, $data, $post_exists );
    795 
    796 			return false;
    797 		}
    798 
    799 		// Map the parent post, or mark it as one we need to fix
    800 		$requires_remapping = false;
    801 		if ( $parent_id ) {
    802 			if ( isset( $this->mapping['post'][ $parent_id ] ) ) {
    803 				$data['post_parent'] = $this->mapping['post'][ $parent_id ];
    804 			} else {
    805 				$meta[]             = array(
    806 					'key'   => '_wxr_import_parent',
    807 					'value' => $parent_id,
    808 				);
    809 				$requires_remapping = true;
    810 
    811 				$data['post_parent'] = 0;
    812 			}
    813 		}
    814 
    815 		// Map the author, or mark it as one we need to fix
    816 		$author = sanitize_user( $data['post_author'], true );
    817 		if ( empty( $author ) ) {
    818 			// Missing or invalid author, use default if available.
    819 			$data['post_author'] = $this->options['default_author'];
    820 		} elseif ( isset( $this->mapping['user_slug'][ $author ] ) ) {
    821 			$data['post_author'] = $this->mapping['user_slug'][ $author ];
    822 		} else {
    823 			$meta[]             = array(
    824 				'key'   => '_wxr_import_user_slug',
    825 				'value' => $author,
    826 			);
    827 			$requires_remapping = true;
    828 
    829 			$data['post_author'] = (int) get_current_user_id();
    830 		}
    831 		$htmlString = $data['post_content'];
    832 
    833 		// Does the post look like it contains attachment images?
    834 		if ( preg_match( self::REGEX_HAS_ATTACHMENT_REFS, $data['post_content'] ) ) {
    835 			$meta[]             = array(
    836 				'key'   => '_wxr_import_has_attachment_refs',
    837 				'value' => true,
    838 			);
    839 			$requires_remapping = true;
    840 		}
    841 
    842 		// Whitelist to just the keys we allow
    843 		$postdata = array(
    844 			'import_id' => $data['post_id'],
    845 		);
    846 		$allowed  = array(
    847 			'post_author'    => true,
    848 			'post_date'      => true,
    849 			'post_date_gmt'  => true,
    850 			'post_content'   => true,
    851 			'post_excerpt'   => true,
    852 			'post_title'     => true,
    853 			'post_status'    => true,
    854 			'post_name'      => true,
    855 			'comment_status' => true,
    856 			'ping_status'    => true,
    857 			'guid'           => true,
    858 			'post_parent'    => true,
    859 			'menu_order'     => true,
    860 			'post_type'      => true,
    861 			'post_password'  => true,
    862 		);
    863 		foreach ( $data as $key => $value ) {
    864 			if ( ! isset( $allowed[ $key ] ) ) {
    865 				continue;
    866 			}
    867 
    868 			$postdata[ $key ] = $data[ $key ];
    869 		}
    870 
    871 		$postdata = apply_filters( 'wp_import_post_data_processed', $postdata, $data );
    872 		$aaa      = false;
    873 		if ( 'attachment' === $postdata['post_type'] ) {
    874 			if ( ! $this->options['fetch_attachments'] ) {
    875 				$this->logger->notice(
    876 					sprintf(
    877 						__( 'Skipping attachment "%s", fetching attachments disabled' ),
    878 						$data['post_title']
    879 					)
    880 				);
    881 				return false;
    882 			}
    883 			$remote_url = ! empty( $data['attachment_url'] ) ? $data['attachment_url'] : $data['guid'];
    884 			$remote_url = $this->checkandreturnnewimageurl( $remote_url );
    885 
    886 			// $this->write_log("\n" . 'wp_import_post_data_processed:-' . $remote_url);
    887 			$post_id = $this->process_attachment( $postdata, $meta, $remote_url );
    888 			// echo   $post_id ;
    889 
    890 		} else {
    891 
    892 			if ( preg_match_all( '@src="([^"]+)"@', $htmlString, $match ) ) {
    893 				$srcs       = array_pop( $match );
    894 				$domainname = apply_filters( 'pt-ocdi/domain_name', 'smartdata' );
    895 				foreach ( $srcs as $src ) {
    896 
    897 					if ( $this->strpos_arr( $src, $domainname ) !== false ) {
    898 						// $this->write_log("\n" . ' remote URL:-' . $src);
    899 						$htmlString               = $this->insert_remote_image( $src, $htmlString );
    900 						$data['post_content']     = $htmlString;
    901 						$postdata['post_content'] = $htmlString;
    902 					}
    903 				}
    904 			}
    905 
    906 			$this->url = apply_filters( 'pt-ocdi/replace_url', $this->url );
    907 			$site_url  = rtrim( site_url() );
    908 			foreach ( $this->url as $url ) {
    909 				$postdata = str_replace( $url, $site_url, $postdata );
    910 			}
    911 			$post_id = wp_insert_post( $postdata, true );
    912 			do_action( 'wp_import_insert_post', $post_id, $original_id, $postdata, $data );
    913 		}
    914 
    915 		if ( is_wp_error( $post_id ) ) {
    916 			$this->logger->error(
    917 				sprintf(
    918 					__( 'Failed to import "%1$s" (%2$s)', 'wordpress-importer' ),
    919 					$data['post_title'],
    920 					$post_type_object->labels->singular_name
    921 				)
    922 			);
    923 			$this->logger->debug( $post_id->get_error_message() );
    924 
    925 			/**
    926 			 * Post processing failed.
    927 			 *
    928 			 * @param WP_Error $post_id Error object.
    929 			 * @param array $data Raw data imported for the post.
    930 			 * @param array $meta Raw meta data, already processed by {@see process_post_meta}.
    931 			 * @param array $comments Raw comment data, already processed by {@see process_comments}.
    932 			 * @param array $terms Raw term data, already processed.
    933 			 */
    934 			do_action( 'wxr_importer.process_failed.post', $post_id, $data, $meta, $comments, $terms );
    935 			return false;
    936 		}
    937 
    938 		// Ensure stickiness is handled correctly too
    939 		if ( $data['is_sticky'] === '1' ) {
    940 			stick_post( $post_id );
    941 		}
    942 
    943 		// map pre-import ID to local ID
    944 		$this->mapping['post'][ $original_id ] = (int) $post_id;
    945 		if ( $requires_remapping ) {
    946 			$this->requires_remapping['post'][ $post_id ] = true;
    947 		}
    948 		$this->mark_post_exists( $data, $post_id );
    949 
    950 		$this->logger->info(
    951 			sprintf(
    952 				__( 'Imported "%1$s" (%2$s)', 'wordpress-importer' ),
    953 				$data['post_title'],
    954 				$post_type_object->labels->singular_name
    955 			)
    956 		);
    957 		$this->logger->debug(
    958 			sprintf(
    959 				__( 'Post %1$d remapped to %2$d', 'wordpress-importer' ),
    960 				$original_id,
    961 				$post_id
    962 			)
    963 		);
    964 
    965 		// Handle the terms too
    966 		$terms = apply_filters( 'wp_import_post_terms', $terms, $post_id, $data );
    967 
    968 		if ( ! empty( $terms ) ) {
    969 			$term_ids = array();
    970 			foreach ( $terms as $term ) {
    971 				$taxonomy = $term['taxonomy'];
    972 				$key      = sha1( $taxonomy . ':' . $term['slug'] );
    973 
    974 				if ( isset( $this->mapping['term'][ $key ] ) ) {
    975 					$term_ids[ $taxonomy ][] = (int) $this->mapping['term'][ $key ];
    976 				} else {
    977 
    978 					/**
    979 					 * Fix for the post format "categories".
    980 					 * The issue in this importer is, that these post formats are misused as categories in WP export
    981 					 * (as the export data <category> item in the post export item), but they are not actually
    982 					 * exported as wp:category items in the XML file, so they need to be inserted on the fly (here).
    983 					 *
    984 					 * Maybe something better can be done in the future?
    985 					 *
    986 					 * Original issue reported here: https://wordpress.org/support/topic/post-format-videoquotegallery-became-format-standard/#post-8447683
    987 					 */
    988 					if ( 'post_format' === $taxonomy ) {
    989 						$term_exists = term_exists( $term['slug'], $taxonomy );
    990 						$term_id     = is_array( $term_exists ) ? $term_exists['term_id'] : $term_exists;
    991 
    992 						if ( empty( $term_id ) ) {
    993 							$t = wp_insert_term( $term['name'], $taxonomy, array( 'slug' => $term['slug'] ) );
    994 							if ( ! is_wp_error( $t ) ) {
    995 								$term_id                       = $t['term_id'];
    996 								$this->mapping['term'][ $key ] = $term_id;
    997 							} else {
    998 								$this->logger->warning(
    999 									sprintf(
   1000 										esc_html__( 'Failed to import term: %1$s - %2$s', 'wordpress-importer' ),
   1001 										esc_html( $taxonomy ),
   1002 										esc_html( $term['name'] )
   1003 									)
   1004 								);
   1005 								continue;
   1006 							}
   1007 						}
   1008 
   1009 						if ( ! empty( $term_id ) ) {
   1010 							$term_ids[ $taxonomy ][] = intval( $term_id );
   1011 						}
   1012 					} // End of fix.
   1013 					else {
   1014 						$meta[]             = array(
   1015 							'key'   => '_wxr_import_term',
   1016 							'value' => $term,
   1017 						);
   1018 						$requires_remapping = true;
   1019 					}
   1020 				}
   1021 			}
   1022 
   1023 			foreach ( $term_ids as $tax => $ids ) {
   1024 				$tt_ids = wp_set_post_terms( $post_id, $ids, $tax );
   1025 				do_action( 'wp_import_set_post_terms', $tt_ids, $ids, $tax, $post_id, $data );
   1026 			}
   1027 		}
   1028 
   1029 		$this->process_comments( $comments, $post_id, $data );
   1030 		$this->process_post_meta( $meta, $post_id, $data );
   1031 
   1032 		if ( 'nav_menu_item' === $data['post_type'] ) {
   1033 			$this->process_menu_item_meta( $post_id, $data, $meta );
   1034 		}
   1035 
   1036 		/**
   1037 		 * Post processing completed.
   1038 		 *
   1039 		 * @param int $post_id New post ID.
   1040 		 * @param array $data Raw data imported for the post.
   1041 		 * @param array $meta Raw meta data, already processed by {@see process_post_meta}.
   1042 		 * @param array $comments Raw comment data, already processed by {@see process_comments}.
   1043 		 * @param array $terms Raw term data, already processed.
   1044 		 */
   1045 		do_action( 'wxr_importer.processed.post', $post_id, $data, $meta, $comments, $terms );
   1046 	}
   1047 
   1048 	public function strpos_arr( $haystack, $needle ) {
   1049 		if ( ! is_array( $needle ) ) {
   1050 			$needle = array( $needle );
   1051 		}
   1052 
   1053 		foreach ( $needle as $what ) {
   1054 			if ( ( $pos = strpos( $haystack, $what ) ) !== false ) {
   1055 				return $pos;
   1056 			}
   1057 		}
   1058 		return false;
   1059 	}
   1060 
   1061 	/**
   1062 	 * Attempt to change remote url
   1063 	 *
   1064 	 * @param array $remote_url
   1065 	 */
   1066 	public function checkandreturnnewimageurl( $remote_url ) {
   1067 		if ( strpos( $remote_url, 'wp-content' ) !== false ) {
   1068 			$urlpath    = substr_replace( $remote_url, '', 0, strpos( $remote_url, 'wp-content' ) + 11 );
   1069 			$this->path = apply_filters( 'pt-ocdi/destination_path', $this->path );
   1070 			// $this->write_log("\n" . 'checkandreturnnewimageurl:-' . $remote_url);
   1071 			if ( $this->path != '' ) {
   1072 
   1073 				if ( file_exists( ABSPATH . $this->path . $urlpath ) ) {
   1074 					$site_url   = rtrim( site_url(), '/' );
   1075 					$remote_url = $site_url . $this->path . $urlpath;
   1076 					// $this->write_log("\n" . 'after_checkandreturnnewimageurl:-' . $remote_url);
   1077 				}
   1078 			}
   1079 		}
   1080 		return $remote_url;
   1081 	}
   1082 
   1083 	public function isHTML( $string ) {
   1084 		return $string != strip_tags( $string ) ? true : false;
   1085 	}
   1086 
   1087 	/**
   1088 	 * Attempt to create a new menu item from import data
   1089 	 *
   1090 	 * Fails for draft, orphaned menu items and those without an associated nav_menu
   1091 	 * or an invalid nav_menu term. If the post type or term object which the menu item
   1092 	 * represents doesn't exist then the menu item will not be imported (waits until the
   1093 	 * end of the import to retry again before discarding).
   1094 	 *
   1095 	 * @param array $item Menu item details from WXR file
   1096 	 */
   1097 	protected function process_menu_item_meta( $post_id, $data, $meta ) {
   1098 
   1099 		$item_type          = get_post_meta( $post_id, '_menu_item_type', true );
   1100 		$original_object_id = get_post_meta( $post_id, '_menu_item_object_id', true );
   1101 		$object_id          = null;
   1102 
   1103 		$this->logger->debug( sprintf( 'Processing menu item %s', $item_type ) );
   1104 
   1105 		$requires_remapping = false;
   1106 		switch ( $item_type ) {
   1107 			case 'taxonomy':
   1108 				if ( isset( $this->mapping['term_id'][ $original_object_id ] ) ) {
   1109 					$object_id = $this->mapping['term_id'][ $original_object_id ];
   1110 				} else {
   1111 					add_post_meta( $post_id, '_wxr_import_menu_item', wp_slash( $original_object_id ) );
   1112 					$requires_remapping = true;
   1113 				}
   1114 				break;
   1115 
   1116 			case 'post_type':
   1117 				if ( isset( $this->mapping['post'][ $original_object_id ] ) ) {
   1118 					$object_id = $this->mapping['post'][ $original_object_id ];
   1119 				} else {
   1120 					add_post_meta( $post_id, '_wxr_import_menu_item', wp_slash( $original_object_id ) );
   1121 					$requires_remapping = true;
   1122 				}
   1123 				break;
   1124 
   1125 			case 'custom':
   1126 				// Custom refers to itself, wonderfully easy.
   1127 				$object_id = $post_id;
   1128 				break;
   1129 
   1130 			default:
   1131 				// associated object is missing or not imported yet, we'll retry later
   1132 				$this->missing_menu_items[] = $data;
   1133 				$this->logger->debug( 'Unknown menu item type' );
   1134 				break;
   1135 		}
   1136 
   1137 		if ( $requires_remapping ) {
   1138 			$this->requires_remapping['post'][ $post_id ] = true;
   1139 		}
   1140 
   1141 		if ( empty( $object_id ) ) {
   1142 			// Nothing needed here.
   1143 			return;
   1144 		}
   1145 
   1146 		$this->logger->debug( sprintf( 'Menu item %d mapped to %d', $original_object_id, $object_id ) );
   1147 		update_post_meta( $post_id, '_menu_item_object_id', wp_slash( $object_id ) );
   1148 	}
   1149 
   1150 	/**
   1151 	 * If fetching attachments is enabled then attempt to create a new attachment
   1152 	 *
   1153 	 * @param  array  $post Attachment post details from WXR
   1154 	 * @param  string $url  URL to fetch attachment from
   1155 	 * @return int|WP_Error Post ID on success, WP_Error otherwise
   1156 	 */
   1157 
   1158 	public function my_custom_upload_dir( $upload ) {
   1159 
   1160 		$urlpath = substr_replace( $this->image_url, '', 0, strpos( $this->image_url, 'uploads' ) + 7 );
   1161 		$subdir  = substr( $urlpath, 0, strrpos( $urlpath, '/' ) );
   1162 
   1163 		$dir     = WP_CONTENT_DIR . '/uploads';
   1164 		$url     = WP_CONTENT_URL . '/uploads';
   1165 		$basedir = $dir;
   1166 		$baseurl = $url;
   1167 		$dir    .= $subdir;
   1168 		$url    .= $subdir;
   1169 		$array   = array(
   1170 			'path'    => $dir,
   1171 			'url'     => $url,
   1172 			'subdir'  => $subdir,
   1173 			'basedir' => $basedir,
   1174 			'baseurl' => $baseurl,
   1175 			'error'   => false,
   1176 		);
   1177 		// error_log(print_r($array, true));
   1178 		return $array;
   1179 	}
   1180 	protected function process_attachment( $post, $meta, $remote_url ) {
   1181 		// try to use _wp_attached file for upload folder placement to ensure the same location as the export site
   1182 		// e.g. location is 2003/05/image.jpg but the attachment post_date is 2010/09, see media_handle_upload()
   1183 		$post['upload_date'] = $post['post_date'];
   1184 		foreach ( $meta as $meta_item ) {
   1185 			if ( $meta_item['key'] !== '_wp_attached_file' ) {
   1186 				continue;
   1187 			}
   1188 
   1189 			if ( preg_match( '%^[0-9]{4}/[0-9]{2}%', $meta_item['value'], $matches ) ) {
   1190 				$post['upload_date'] = $matches[0];
   1191 			}
   1192 			break;
   1193 		}
   1194 
   1195 		// if the URL is absolute, but does not contain address, then upload it assuming base_site_url
   1196 		if ( preg_match( '|^/[\w\W]+$|', $remote_url ) ) {
   1197 			$remote_url = rtrim( $this->base_url, '/' ) . $remote_url;
   1198 		}
   1199 
   1200 		// echo $remote_url;
   1201 		// echo "<br>";
   1202 		$this->image_url = $remote_url;
   1203 		if ( strpos( $remote_url, 'revslider' ) !== false ) {
   1204 			// $this->write_log("\n" . 'revslider=' . $remote_url);
   1205 			add_filter( 'upload_dir', array( $this, 'my_custom_upload_dir' ) );
   1206 		}
   1207 
   1208 		$upload = $this->fetch_remote_file( $remote_url, $post );
   1209 		if ( strpos( $remote_url, 'revslider' ) !== false ) {
   1210 			// $this->write_log("\n" . 'revslider=' . $remote_url);
   1211 			remove_filter( 'upload_dir', array( $this, 'my_custom_upload_dir' ) );
   1212 		}
   1213 
   1214 		if ( is_wp_error( $upload ) ) {
   1215 			return $upload;
   1216 		}
   1217 
   1218 		$info = wp_check_filetype( $upload['file'] );
   1219 		if ( ! $info ) {
   1220 			return new \WP_Error( 'attachment_processing_error', __( 'Invalid file type', 'wordpress-importer' ) );
   1221 		}
   1222 
   1223 		$post['post_mime_type'] = $info['type'];
   1224 
   1225 		// WP really likes using the GUID for display. Allow updating it.
   1226 		// See https://core.trac.wordpress.org/ticket/33386
   1227 		if ( $this->options['update_attachment_guids'] ) {
   1228 			$post['guid'] = $upload['url'];
   1229 		}
   1230 
   1231 		// as per wp-admin/includes/upload.php
   1232 		$post_id = wp_insert_attachment( $post, $upload['file'] );
   1233 		if ( is_wp_error( $post_id ) ) {
   1234 			return $post_id;
   1235 		}
   1236 		// $this->write_log("\n" . 'POSTID=' .$post_id .' ---   url'. $remote_url);
   1237 
   1238 		$attachment_metadata = wp_generate_attachment_metadata( $post_id, $upload['file'] );
   1239 		wp_update_attachment_metadata( $post_id, $attachment_metadata );
   1240 
   1241 		// Map this image URL later if we need to
   1242 		$this->url_remap[ $remote_url ] = $upload['url'];
   1243 
   1244 		// If we have a HTTPS URL, ensure the HTTP URL gets replaced too
   1245 		if ( substr( $remote_url, 0, 8 ) === 'https://' ) {
   1246 			$insecure_url                     = 'http' . substr( $remote_url, 5 );
   1247 			$this->url_remap[ $insecure_url ] = $upload['url'];
   1248 		}
   1249 
   1250 		if ( $this->options['aggressive_url_search'] ) {
   1251 			// remap resized image URLs, works by stripping the extension and remapping the URL stub.
   1252 			/*
   1253 			if ( preg_match( '!^image/!', $info['type'] ) ) {
   1254 			$parts = pathinfo( $remote_url );
   1255 			$name = basename( $parts['basename'], ".{$parts['extension']}" ); // PATHINFO_FILENAME in PHP 5.2
   1256 
   1257 			$parts_new = pathinfo( $upload['url'] );
   1258 			$name_new = basename( $parts_new['basename'], ".{$parts_new['extension']}" );
   1259 
   1260 			$this->url_remap[$parts['dirname'] . '/' . $name] = $parts_new['dirname'] . '/' . $name_new;
   1261 			}*/
   1262 		}
   1263 		update_post_meta( $post_id, '_smartdata_source_image_hash', $this->get_hash_image( $remote_url ) );
   1264 		return $post_id;
   1265 	}
   1266 
   1267 	/**
   1268 	 * Parse a meta node into meta data.
   1269 	 *
   1270 	 * @param  DOMElement $node Parent node of meta data (typically `wp:postmeta` or `wp:commentmeta`).
   1271 	 * @return array|null Meta data array on success, or null on error.
   1272 	 */
   1273 	protected function parse_meta_node( $node ) {
   1274 		foreach ( $node->childNodes as $child ) {
   1275 			// We only care about child elements
   1276 			if ( $child->nodeType !== XML_ELEMENT_NODE ) {
   1277 				continue;
   1278 			}
   1279 
   1280 			switch ( $child->tagName ) {
   1281 				case 'wp:meta_key':
   1282 					$key = $child->textContent;
   1283 					break;
   1284 
   1285 				case 'wp:meta_value':
   1286 					$value = $child->textContent;
   1287 					break;
   1288 			}
   1289 		}
   1290 
   1291 		if ( empty( $key ) || ! isset( $value ) ) {
   1292 			return null;
   1293 		}
   1294 
   1295 		return compact( 'key', 'value' );
   1296 	}
   1297 
   1298 	/**
   1299 	 * Process and import post meta items.
   1300 	 *
   1301 	 * @param  array $meta    List of meta data arrays
   1302 	 * @param  int   $post_id Post to associate with
   1303 	 * @param  array $post    Post data
   1304 	 * @return int|WP_Error Number of meta items imported on success, error otherwise.
   1305 	 */
   1306 	protected function process_post_meta( $meta, $post_id, $post ) {
   1307 		if ( empty( $meta ) ) {
   1308 			return true;
   1309 		}
   1310 
   1311 		// print_r($imageswa);
   1312 		$domainname = apply_filters( 'pt-ocdi/domain_name', 'smartdata' );
   1313 		foreach ( $meta as $meta_item ) {
   1314 			if ( $meta_item['key'] == '_elementor_data' ) {
   1315 				preg_match_all( '/"url"\:\"([^\"]+)\"/', $meta_item['value'], $meta_item_values );
   1316 				$meta_item_html = $meta_item['value'];
   1317 				if ( ! empty( $meta_item_values[1] ) ) {
   1318 					foreach ( $meta_item_values[1] as $meta_item_value ) {
   1319 						if ( $this->strpos_arr( $meta_item_value, $domainname ) !== false ) {
   1320 							$meta_item_html_new = $this->replace_remote_uri( $meta_item_value, $meta_item_html );
   1321 							$meta_item_html_new = $this->replace_remote_image_uri( $meta_item_value, $meta_item_html );
   1322 							if ( $meta_item_html_new ) {
   1323 								$meta_item_html = $meta_item_html_new;
   1324 							}
   1325 						}
   1326 					}
   1327 				}
   1328 				$meta_item['value'] = $meta_item_html;
   1329 			}
   1330 
   1331 			if ( $meta_item['key'] == '_menu_item_url' ) {
   1332 				// print_r( $meta_item['value']);
   1333 				if ( $this->strpos_arr( $meta_item['value'], $domainname ) !== false ) {
   1334 					$meta_item_html     = $this->replace_remote_uri( $meta_item['value'], $meta_item['value'] );
   1335 					$meta_item['value'] = $meta_item_html;
   1336 					// print_r("new:-". $meta_item['value']);
   1337 				}
   1338 				// exit();
   1339 
   1340 			}
   1341 
   1342 			/**
   1343 			 * Pre-process post meta data.
   1344 			 *
   1345 			 * @param array $meta_item Meta data. (Return empty to skip.)
   1346 			 * @param int $post_id Post the meta is attached to.
   1347 			 */
   1348 			$meta_item = apply_filters( 'wxr_importer.pre_process.post_meta', $meta_item, $post_id );
   1349 			if ( empty( $meta_item ) ) {
   1350 				return false;
   1351 			}
   1352 
   1353 			$key   = apply_filters( 'import_post_meta_key', $meta_item['key'], $post_id, $post );
   1354 			$value = false;
   1355 
   1356 			if ( '_edit_last' === $key ) {
   1357 				$value = intval( $meta_item['value'] );
   1358 				if ( ! isset( $this->mapping['user'][ $value ] ) ) {
   1359 					// Skip!
   1360 					continue;
   1361 				}
   1362 
   1363 				$value = $this->mapping['user'][ $value ];
   1364 			}
   1365 
   1366 			if ( $key ) {
   1367 				// export gets meta straight from the DB so could have a serialized string
   1368 				if ( ! $value ) {
   1369 					$value = maybe_unserialize( $meta_item['value'] );
   1370 				}
   1371 
   1372 				add_post_meta( $post_id, wp_slash( $key ), wp_slash_strings_only( $value ) );
   1373 				do_action( 'import_post_meta', $post_id, $key, $value );
   1374 
   1375 				// if the post has a featured image, take note of this in case of remap
   1376 				if ( '_thumbnail_id' === $key ) {
   1377 					$this->featured_images[ $post_id ] = (int) $value;
   1378 				}
   1379 			}
   1380 		}
   1381 
   1382 		return true;
   1383 	}
   1384 
   1385 	private function get_saved_image( $attachment, $metakey ) {
   1386 		global $wpdb;
   1387 
   1388 		$post_id = $wpdb->get_var(
   1389 			$wpdb->prepare(
   1390 				'SELECT `post_id` FROM `' . $wpdb->postmeta . '`
   1391 					WHERE `meta_key` = \'' . $metakey . '\'
   1392 						AND `meta_value` = %s
   1393 				;',
   1394 				$this->get_hash_image( $attachment )
   1395 			)
   1396 		);
   1397 
   1398 		if ( $post_id ) {
   1399 			$new_attachment = wp_get_attachment_url( $post_id );
   1400 			return $new_attachment;
   1401 		}
   1402 
   1403 		return false;
   1404 	}
   1405 
   1406 	protected function write_log( $log ) {
   1407 		if ( true === WP_DEBUG ) {
   1408 			if ( is_array( $log ) || is_object( $log ) ) {
   1409 				error_log( print_r( $log, true ) );
   1410 			} else {
   1411 				error_log( $log );
   1412 			}
   1413 		}
   1414 	}
   1415 
   1416 	protected function replace_remote_image_uri( $remote_url, $html ) {
   1417 		$remote_url_new = stripslashes( $remote_url );
   1418 		$this->url      = apply_filters( 'pt-ocdi/replace_url', $this->url );
   1419 		$site_url       = rtrim( site_url(), '/' );
   1420 		foreach ( $this->url as $url ) {
   1421 			if ( strpos( $remote_url_new, $url ) !== false ) {
   1422 				$urlpath_url = substr_replace( $remote_url_new, '', 0, strpos( $remote_url_new, $url ) + strlen( $url ) + 1 );
   1423 				$html        = str_replace( $remote_url, addslashes( $site_url . '/' . $urlpath_url ), $html );
   1424 			}
   1425 		}
   1426 		return $html;
   1427 	}
   1428 
   1429 	protected function replace_remote_uri( $remote_url, $html ) {
   1430 
   1431 		$remote_url_new = stripslashes( $remote_url );
   1432 		// $this->write_log('replace_remote_uri preocess:-' . remote_url_new);
   1433 		$this->url = apply_filters( 'pt-ocdi/replace_url', $this->url );
   1434 		if ( ! preg_match( '/\.(png|jpeg|jpg|gif|bmp)$/i', $remote_url_new ) && $this->url != '' ) {
   1435 			$site_url = rtrim( site_url(), '/' );
   1436 			foreach ( $this->url as $url ) {
   1437 				if ( strpos( $remote_url_new, $url ) !== false ) {
   1438 					$urlpath_url = substr_replace( $remote_url_new, '', 0, strpos( $remote_url_new, $url ) + strlen( $url ) + 1 );
   1439 					// $this->write_log('replace_remote_uri preocess:-' . $site_url . '/' . $urlpath_url);
   1440 					$html = str_replace( $remote_url, addslashes( $site_url . '/' . $urlpath_url ), $html );
   1441 				}
   1442 			}
   1443 			// $this->write_log("\n" . 'A tag URL:-' . $site_url . '/' . $urlpath_url);
   1444 			return $html;
   1445 		}
   1446 		return false;
   1447 	}
   1448 
   1449 	protected function insert_remote_image( $remote_url, $html, $type = '' ) {
   1450 		$html_with_replace_link = $this->replace_remote_uri( $remote_url, $html );
   1451 		if ( $html_with_replace_link ) {
   1452 			return $html_with_replace_link;
   1453 		}
   1454 		$remote_url_s = stripslashes( $remote_url );
   1455 		// $this->write_log("\n" . 'Main URL:-' . $remote_url_s);
   1456 		if ( strpos( $remote_url_s, 'wp-content' ) !== false ) {
   1457 			$remote_url_s = $this->checkandreturnnewimageurl( $remote_url_s );
   1458 			// $this->write_log('Main URL after preocess:-' . $remote_url_s);
   1459 			$saved_image = $this->get_saved_image( $remote_url_s, '_smartdata_source_image_hash' );
   1460 			// $this->write_log("\n" . 'Save Image:-' . $saved_image);
   1461 			if ( $saved_image ) {
   1462 				$html = str_replace( $remote_url, addslashes( $saved_image ), $html );
   1463 				// $this->write_log("\n" . 'Replace This Image:-' . $saved_image);
   1464 				return $html;
   1465 			}
   1466 			$file = $this->get_image_temp_file( $remote_url_s );
   1467 			if ( $file ) {
   1468 				$overrides = array(
   1469 					'test_form' => false,
   1470 					'test_size' => true,
   1471 				);
   1472 				// Move the temporary file into the uploads directory
   1473 				$results       = $this->move_image_temp_file_to_uploaddir( $file );
   1474 				$attachment_id = $this->move_image_uploaddir_to_attachment( $results );
   1475 				if ( $attachment_id ) {
   1476 					$this->upload_image_hash_to_postmeta( $type, $attachment_id, $remote_url );
   1477 					if ( ! empty( $results['error'] ) ) {
   1478 					} else {
   1479 						$local_url = $results['url']; // URL to the file in the uploads dir
   1480 						$html      = str_replace( $remote_url, addslashes( $local_url ), $html );
   1481 						// $this->write_log("\n" . 'New Image Replace:-' . $local_url);
   1482 					}
   1483 					return $html;
   1484 				}
   1485 			}
   1486 		}
   1487 		return $html;
   1488 	}
   1489 
   1490 	private function upload_image_hash_to_postmeta( $type, $attachment_id, $remote_url ) {
   1491 
   1492 		if ( $type == 'elementor' ) {
   1493 			update_post_meta( $attachment_id, '_elementor_source_image_hash', $this->get_hash_image( $remote_url ) );
   1494 			update_post_meta( $attachment_id, '_smartdata_source_image_hash', $this->get_hash_image( $remote_url ) );
   1495 		} else {
   1496 			update_post_meta( $attachment_id, '_smartdata_source_image_hash', $this->get_hash_image( $remote_url ) );
   1497 		}
   1498 	}
   1499 
   1500 	private function move_image_temp_file_to_uploaddir( $file ) {
   1501 
   1502 		$overrides = array(
   1503 			'test_form' => false,
   1504 			'test_size' => true,
   1505 		);
   1506 		// Move the temporary file into the uploads directory
   1507 		return wp_handle_sideload( $file, $overrides );
   1508 	}
   1509 
   1510 	private function move_image_uploaddir_to_attachment( $results ) {
   1511 
   1512 		$wp_upload_dir = wp_upload_dir();
   1513 		// Prepare an array of post data for the attachment.
   1514 		$attachment_data = array(
   1515 			'guid'           => $wp_upload_dir['url'] . '/' . basename( $results['file'] ),
   1516 			'post_mime_type' => $results['type'],
   1517 			'post_title'     => preg_replace( '/\.[^.]+$/', '', basename( $results['file'] ) ),
   1518 			'post_content'   => '',
   1519 			'post_status'    => 'inherit',
   1520 		);
   1521 		$attachment_id   = wp_insert_attachment( $attachment_data, $results['file'] );
   1522 		if ( ! $attachment_id ) {
   1523 			return;
   1524 		}
   1525 		wp_update_attachment_metadata(
   1526 			$attachment_id,
   1527 			wp_generate_attachment_metadata( $attachment_id, $results['file'] )
   1528 		);
   1529 		return $attachment_id;
   1530 	}
   1531 
   1532 	private function get_image_temp_file( $remote_url ) {
   1533 		$timeout_seconds = 30;
   1534 		$temp_file       = download_url( $remote_url, $timeout_seconds );
   1535 		$ext             = pathinfo( $remote_url, PATHINFO_EXTENSION );
   1536 		if ( ! is_wp_error( $temp_file ) ) {
   1537 			$file = array(
   1538 				'name'     => basename( $remote_url ), // ex: wp-header-logo.png
   1539 				'type'     => 'image/' . $ext,
   1540 				'tmp_name' => $temp_file,
   1541 				'error'    => 0,
   1542 				'size'     => filesize( $temp_file ),
   1543 			);
   1544 			return $file;
   1545 		} else {
   1546 			return false;
   1547 		}
   1548 	}
   1549 
   1550 	private function get_hash_image( $attachment_url ) {
   1551 		return sha1( $attachment_url );
   1552 	}
   1553 	/**
   1554 	 * Parse a comment node into comment data.
   1555 	 *
   1556 	 * @param  DOMElement $node Parent node of comment data (typically `wp:comment`).
   1557 	 * @return array Comment data array.
   1558 	 */
   1559 	protected function parse_comment_node( $node ) {
   1560 		$data = array(
   1561 			'commentmeta' => array(),
   1562 		);
   1563 
   1564 		foreach ( $node->childNodes as $child ) {
   1565 			// We only care about child elements
   1566 			if ( $child->nodeType !== XML_ELEMENT_NODE ) {
   1567 				continue;
   1568 			}
   1569 
   1570 			switch ( $child->tagName ) {
   1571 				case 'wp:comment_id':
   1572 					$data['comment_id'] = $child->textContent;
   1573 					break;
   1574 				case 'wp:comment_author':
   1575 					$data['comment_author'] = $child->textContent;
   1576 					break;
   1577 
   1578 				case 'wp:comment_author_email':
   1579 					$data['comment_author_email'] = $child->textContent;
   1580 					break;
   1581 
   1582 				case 'wp:comment_author_IP':
   1583 					$data['comment_author_IP'] = $child->textContent;
   1584 					break;
   1585 
   1586 				case 'wp:comment_author_url':
   1587 					$data['comment_author_url'] = $child->textContent;
   1588 					break;
   1589 
   1590 				case 'wp:comment_user_id':
   1591 					$data['comment_user_id'] = $child->textContent;
   1592 					break;
   1593 
   1594 				case 'wp:comment_date':
   1595 					$data['comment_date'] = $child->textContent;
   1596 					break;
   1597 
   1598 				case 'wp:comment_date_gmt':
   1599 					$data['comment_date_gmt'] = $child->textContent;
   1600 					break;
   1601 
   1602 				case 'wp:comment_content':
   1603 					$data['comment_content'] = $child->textContent;
   1604 					break;
   1605 
   1606 				case 'wp:comment_approved':
   1607 					$data['comment_approved'] = $child->textContent;
   1608 					break;
   1609 
   1610 				case 'wp:comment_type':
   1611 					$data['comment_type'] = $child->textContent;
   1612 					break;
   1613 
   1614 				case 'wp:comment_parent':
   1615 					$data['comment_parent'] = $child->textContent;
   1616 					break;
   1617 
   1618 				case 'wp:commentmeta':
   1619 					$meta_item = $this->parse_meta_node( $child );
   1620 					if ( ! empty( $meta_item ) ) {
   1621 						$data['commentmeta'][] = $meta_item;
   1622 					}
   1623 					break;
   1624 			}
   1625 		}
   1626 
   1627 		return $data;
   1628 	}
   1629 
   1630 	/**
   1631 	 * Process and import comment data.
   1632 	 *
   1633 	 * @param  array $comments List of comment data arrays.
   1634 	 * @param  int   $post_id  Post to associate with.
   1635 	 * @param  array $post     Post data.
   1636 	 * @return int|WP_Error Number of comments imported on success, error otherwise.
   1637 	 */
   1638 	protected function process_comments( $comments, $post_id, $post, $post_exists = false ) {
   1639 
   1640 		$comments = apply_filters( 'wp_import_post_comments', $comments, $post_id, $post );
   1641 		if ( empty( $comments ) ) {
   1642 			return 0;
   1643 		}
   1644 
   1645 		$num_comments = 0;
   1646 
   1647 		// Sort by ID to avoid excessive remapping later
   1648 		usort( $comments, array( $this, 'sort_comments_by_id' ) );
   1649 
   1650 		foreach ( $comments as $key => $comment ) {
   1651 			/**
   1652 			 * Pre-process comment data
   1653 			 *
   1654 			 * @param array $comment Comment data. (Return empty to skip.)
   1655 			 * @param int $post_id Post the comment is attached to.
   1656 			 */
   1657 			$comment = apply_filters( 'wxr_importer.pre_process.comment', $comment, $post_id );
   1658 			if ( empty( $comment ) ) {
   1659 				return false;
   1660 			}
   1661 
   1662 			$original_id = isset( $comment['comment_id'] ) ? (int) $comment['comment_id'] : 0;
   1663 			$parent_id   = isset( $comment['comment_parent'] ) ? (int) $comment['comment_parent'] : 0;
   1664 			$author_id   = isset( $comment['comment_user_id'] ) ? (int) $comment['comment_user_id'] : 0;
   1665 
   1666 			// if this is a new post we can skip the comment_exists() check
   1667 			// TODO: Check comment_exists for performance
   1668 			if ( $post_exists ) {
   1669 				$existing = $this->comment_exists( $comment );
   1670 				if ( $existing ) {
   1671 					$this->mapping['comment'][ $original_id ] = $existing;
   1672 					continue;
   1673 				}
   1674 			}
   1675 
   1676 			// Remove meta from the main array
   1677 			$meta = isset( $comment['commentmeta'] ) ? $comment['commentmeta'] : array();
   1678 			unset( $comment['commentmeta'] );
   1679 
   1680 			// Map the parent comment, or mark it as one we need to fix
   1681 			$requires_remapping = false;
   1682 			if ( $parent_id ) {
   1683 				if ( isset( $this->mapping['comment'][ $parent_id ] ) ) {
   1684 					$comment['comment_parent'] = $this->mapping['comment'][ $parent_id ];
   1685 				} else {
   1686 					// Prepare for remapping later
   1687 					$meta[]             = array(
   1688 						'key'   => '_wxr_import_parent',
   1689 						'value' => $parent_id,
   1690 					);
   1691 					$requires_remapping = true;
   1692 
   1693 					// Wipe the parent for now
   1694 					$comment['comment_parent'] = 0;
   1695 				}
   1696 			}
   1697 
   1698 			// Map the author, or mark it as one we need to fix
   1699 			if ( $author_id ) {
   1700 				if ( isset( $this->mapping['user'][ $author_id ] ) ) {
   1701 					$comment['user_id'] = $this->mapping['user'][ $author_id ];
   1702 				} else {
   1703 					// Prepare for remapping later
   1704 					$meta[]             = array(
   1705 						'key'   => '_wxr_import_user',
   1706 						'value' => $author_id,
   1707 					);
   1708 					$requires_remapping = true;
   1709 
   1710 					// Wipe the user for now
   1711 					$comment['user_id'] = 0;
   1712 				}
   1713 			}
   1714 
   1715 			// Run standard core filters
   1716 			$comment['comment_post_ID'] = $post_id;
   1717 			$comment                    = wp_filter_comment( $comment );
   1718 
   1719 			// wp_insert_comment expects slashed data
   1720 			$comment_id                               = wp_insert_comment( wp_slash( $comment ) );
   1721 			$this->mapping['comment'][ $original_id ] = $comment_id;
   1722 			if ( $requires_remapping ) {
   1723 				$this->requires_remapping['comment'][ $comment_id ] = true;
   1724 			}
   1725 			$this->mark_comment_exists( $comment, $comment_id );
   1726 
   1727 			/**
   1728 			 * Comment has been imported.
   1729 			 *
   1730 			 * @param int $comment_id New comment ID
   1731 			 * @param array $comment Comment inserted (`comment_id` item refers to the original ID)
   1732 			 * @param int $post_id Post parent of the comment
   1733 			 * @param array $post Post data
   1734 			 */
   1735 			do_action( 'wp_import_insert_comment', $comment_id, $comment, $post_id, $post );
   1736 
   1737 			// Process the meta items
   1738 			foreach ( $meta as $meta_item ) {
   1739 				$value = maybe_unserialize( $meta_item['value'] );
   1740 				add_comment_meta( $comment_id, wp_slash( $meta_item['key'] ), wp_slash( $value ) );
   1741 			}
   1742 
   1743 			/**
   1744 			 * Post processing completed.
   1745 			 *
   1746 			 * @param int $post_id New post ID.
   1747 			 * @param array $comment Raw data imported for the comment.
   1748 			 * @param array $meta Raw meta data, already processed by {@see process_post_meta}.
   1749 			 * @param array $post_id Parent post ID.
   1750 			 */
   1751 			do_action( 'wxr_importer.processed.comment', $comment_id, $comment, $meta, $post_id );
   1752 
   1753 			$num_comments++;
   1754 		}
   1755 
   1756 		return $num_comments;
   1757 	}
   1758 
   1759 	protected function parse_category_node( $node ) {
   1760 		$data = array(
   1761 			// Default taxonomy to "category", since this is a `<category>` tag
   1762 			'taxonomy' => 'category',
   1763 		);
   1764 		$meta = array();
   1765 
   1766 		if ( $node->hasAttribute( 'domain' ) ) {
   1767 			$data['taxonomy'] = $node->getAttribute( 'domain' );
   1768 		}
   1769 		if ( $node->hasAttribute( 'nicename' ) ) {
   1770 			$data['slug'] = $node->getAttribute( 'nicename' );
   1771 		}
   1772 
   1773 		$data['name'] = $node->textContent;
   1774 
   1775 		if ( empty( $data['slug'] ) ) {
   1776 			return null;
   1777 		}
   1778 
   1779 		// Just for extra compatibility
   1780 		if ( $data['taxonomy'] === 'tag' ) {
   1781 			$data['taxonomy'] = 'post_tag';
   1782 		}
   1783 
   1784 		return $data;
   1785 	}
   1786 
   1787 	/**
   1788 	 * Callback for `usort` to sort comments by ID
   1789 	 *
   1790 	 * @param  array $a Comment data for the first comment
   1791 	 * @param  array $b Comment data for the second comment
   1792 	 * @return int
   1793 	 */
   1794 	public static function sort_comments_by_id( $a, $b ) {
   1795 		if ( empty( $a['comment_id'] ) ) {
   1796 			return 1;
   1797 		}
   1798 
   1799 		if ( empty( $b['comment_id'] ) ) {
   1800 			return -1;
   1801 		}
   1802 
   1803 		return $a['comment_id'] - $b['comment_id'];
   1804 	}
   1805 
   1806 	protected function parse_author_node( $node ) {
   1807 		$data = array();
   1808 		$meta = array();
   1809 		foreach ( $node->childNodes as $child ) {
   1810 			// We only care about child elements
   1811 			if ( $child->nodeType !== XML_ELEMENT_NODE ) {
   1812 				continue;
   1813 			}
   1814 
   1815 			switch ( $child->tagName ) {
   1816 				case 'wp:author_login':
   1817 					$data['user_login'] = $child->textContent;
   1818 					break;
   1819 
   1820 				case 'wp:author_id':
   1821 					$data['ID'] = $child->textContent;
   1822 					break;
   1823 
   1824 				case 'wp:author_email':
   1825 					$data['user_email'] = $child->textContent;
   1826 					break;
   1827 
   1828 				case 'wp:author_display_name':
   1829 					$data['display_name'] = $child->textContent;
   1830 					break;
   1831 
   1832 				case 'wp:author_first_name':
   1833 					$data['first_name'] = $child->textContent;
   1834 					break;
   1835 
   1836 				case 'wp:author_last_name':
   1837 					$data['last_name'] = $child->textContent;
   1838 					break;
   1839 			}
   1840 		}
   1841 
   1842 		return compact( 'data', 'meta' );
   1843 	}
   1844 
   1845 	protected function process_author( $data, $meta ) {
   1846 		/**
   1847 		 * Pre-process user data.
   1848 		 *
   1849 		 * @param array $data User data. (Return empty to skip.)
   1850 		 * @param array $meta Meta data.
   1851 		 */
   1852 		$data = apply_filters( 'wxr_importer.pre_process.user', $data, $meta );
   1853 		if ( empty( $data ) ) {
   1854 			return false;
   1855 		}
   1856 
   1857 		// Have we already handled this user?
   1858 		$original_id   = isset( $data['ID'] ) ? $data['ID'] : 0;
   1859 		$original_slug = $data['user_login'];
   1860 
   1861 		if ( isset( $this->mapping['user'][ $original_id ] ) ) {
   1862 			$existing = $this->mapping['user'][ $original_id ];
   1863 
   1864 			// Note the slug mapping if we need to too
   1865 			if ( ! isset( $this->mapping['user_slug'][ $original_slug ] ) ) {
   1866 				$this->mapping['user_slug'][ $original_slug ] = $existing;
   1867 			}
   1868 
   1869 			return false;
   1870 		}
   1871 
   1872 		if ( isset( $this->mapping['user_slug'][ $original_slug ] ) ) {
   1873 			$existing = $this->mapping['user_slug'][ $original_slug ];
   1874 
   1875 			// Ensure we note the mapping too
   1876 			$this->mapping['user'][ $original_id ] = $existing;
   1877 
   1878 			return false;
   1879 		}
   1880 
   1881 		// Allow overriding the user's slug
   1882 		$login = $original_slug;
   1883 		if ( isset( $this->user_slug_override[ $login ] ) ) {
   1884 			$login = $this->user_slug_override[ $login ];
   1885 		}
   1886 
   1887 		$userdata = array(
   1888 			'user_login' => sanitize_user( $login, true ),
   1889 			'user_pass'  => wp_generate_password(),
   1890 		);
   1891 
   1892 		$allowed = array(
   1893 			'user_email'   => true,
   1894 			'display_name' => true,
   1895 			'first_name'   => true,
   1896 			'last_name'    => true,
   1897 		);
   1898 		foreach ( $data as $key => $value ) {
   1899 			if ( ! isset( $allowed[ $key ] ) ) {
   1900 				continue;
   1901 			}
   1902 
   1903 			$userdata[ $key ] = $data[ $key ];
   1904 		}
   1905 
   1906 		$user_id = wp_insert_user( wp_slash( $userdata ) );
   1907 		if ( is_wp_error( $user_id ) ) {
   1908 			$this->logger->error(
   1909 				sprintf(
   1910 					__( 'Failed to import user "%s"', 'wordpress-importer' ),
   1911 					$userdata['user_login']
   1912 				)
   1913 			);
   1914 			$this->logger->debug( $user_id->get_error_message() );
   1915 
   1916 			/**
   1917 			 * User processing failed.
   1918 			 *
   1919 			 * @param WP_Error $user_id Error object.
   1920 			 * @param array $userdata Raw data imported for the user.
   1921 			 */
   1922 			do_action( 'wxr_importer.process_failed.user', $user_id, $userdata );
   1923 			return false;
   1924 		}
   1925 
   1926 		if ( $original_id ) {
   1927 			$this->mapping['user'][ $original_id ] = $user_id;
   1928 		}
   1929 		$this->mapping['user_slug'][ $original_slug ] = $user_id;
   1930 
   1931 		$this->logger->info(
   1932 			sprintf(
   1933 				__( 'Imported user "%s"', 'wordpress-importer' ),
   1934 				$userdata['user_login']
   1935 			)
   1936 		);
   1937 		$this->logger->debug(
   1938 			sprintf(
   1939 				__( 'User %1$d remapped to %2$d', 'wordpress-importer' ),
   1940 				$original_id,
   1941 				$user_id
   1942 			)
   1943 		);
   1944 
   1945 		// TODO: Implement meta handling once WXR includes it
   1946 		/**
   1947 		 * User processing completed.
   1948 		 *
   1949 		 * @param int $user_id New user ID.
   1950 		 * @param array $userdata Raw data imported for the user.
   1951 		 */
   1952 		do_action( 'wxr_importer.processed.user', $user_id, $userdata );
   1953 	}
   1954 
   1955 	protected function parse_term_node( $node, $type = 'term' ) {
   1956 		$data = array();
   1957 		$meta = array();
   1958 
   1959 		$tag_name = array(
   1960 			'id'          => 'wp:term_id',
   1961 			'taxonomy'    => 'wp:term_taxonomy',
   1962 			'slug'        => 'wp:term_slug',
   1963 			'parent'      => 'wp:term_parent',
   1964 			'name'        => 'wp:term_name',
   1965 			'description' => 'wp:term_description',
   1966 		);
   1967 		$taxonomy = null;
   1968 
   1969 		// Special casing!
   1970 		switch ( $type ) {
   1971 			case 'category':
   1972 				$tag_name['slug']        = 'wp:category_nicename';
   1973 				$tag_name['parent']      = 'wp:category_parent';
   1974 				$tag_name['name']        = 'wp:cat_name';
   1975 				$tag_name['description'] = 'wp:category_description';
   1976 				$tag_name['taxonomy']    = null;
   1977 
   1978 				$data['taxonomy'] = 'category';
   1979 				break;
   1980 
   1981 			case 'tag':
   1982 				$tag_name['slug']        = 'wp:tag_slug';
   1983 				$tag_name['parent']      = null;
   1984 				$tag_name['name']        = 'wp:tag_name';
   1985 				$tag_name['description'] = 'wp:tag_description';
   1986 				$tag_name['taxonomy']    = null;
   1987 
   1988 				$data['taxonomy'] = 'post_tag';
   1989 				break;
   1990 		}
   1991 
   1992 		foreach ( $node->childNodes as $child ) {
   1993 			// We only care about child elements
   1994 			if ( $child->nodeType !== XML_ELEMENT_NODE ) {
   1995 				continue;
   1996 			}
   1997 
   1998 			$key = array_search( $child->tagName, $tag_name );
   1999 			if ( $key ) {
   2000 				$data[ $key ] = $child->textContent;
   2001 			} elseif ( $child->tagName == 'wp:termmeta' ) {
   2002 				$meta_item = $this->parse_meta_node( $child );
   2003 				if ( ! empty( $meta_item ) ) {
   2004 					$meta[] = $meta_item;
   2005 				}
   2006 			}
   2007 		}
   2008 
   2009 		if ( empty( $data['taxonomy'] ) ) {
   2010 			return null;
   2011 		}
   2012 
   2013 		// Compatibility with WXR 1.0
   2014 		if ( $data['taxonomy'] === 'tag' ) {
   2015 			$data['taxonomy'] = 'post_tag';
   2016 		}
   2017 
   2018 		return compact( 'data', 'meta' );
   2019 	}
   2020 
   2021 	protected function process_term( $data, $meta ) {
   2022 		/**
   2023 		 * Pre-process term data.
   2024 		 *
   2025 		 * @param array $data Term data. (Return empty to skip.)
   2026 		 * @param array $meta Meta data.
   2027 		 */
   2028 		$data = apply_filters( 'wxr_importer.pre_process.term', $data, $meta );
   2029 		if ( empty( $data ) ) {
   2030 			return false;
   2031 		}
   2032 
   2033 		$original_id = isset( $data['id'] ) ? (int) $data['id'] : 0;
   2034 
   2035 		/*
   2036 		 FIX for OCDI!
   2037 		 * As of WP 4.5, export.php returns the SLUG for the term's parent,
   2038 		 * rather than an integer ID (this differs from a post_parent)
   2039 		 * wp_insert_term and wp_update_term use the key: 'parent' and an integer value 'id'
   2040 		 */
   2041 		$term_slug   = isset( $data['slug'] ) ? $data['slug'] : '';
   2042 		$parent_slug = isset( $data['parent'] ) ? $data['parent'] : '';
   2043 
   2044 		$mapping_key = sha1( $data['taxonomy'] . ':' . $data['slug'] );
   2045 		$existing    = $this->term_exists( $data );
   2046 		if ( $existing ) {
   2047 			$this->mapping['term'][ $mapping_key ]    = $existing;
   2048 			$this->mapping['term_id'][ $original_id ] = $existing;
   2049 			$this->mapping['term_slug'][ $term_slug ] = $existing;
   2050 			return false;
   2051 		}
   2052 
   2053 		// WP really likes to repeat itself in export files
   2054 		if ( isset( $this->mapping['term'][ $mapping_key ] ) ) {
   2055 			return false;
   2056 		}
   2057 
   2058 		$termdata = array();
   2059 		$allowed  = array(
   2060 			'slug'        => true,
   2061 			'description' => true,
   2062 			'parent'      => true, // The parent_id may have already been set, so pass this back to the newly inserted term.
   2063 		);
   2064 
   2065 		// Map the parent comment, or mark it as one we need to fix
   2066 		$requires_remapping = false;
   2067 		if ( $parent_slug ) {
   2068 			if ( isset( $this->mapping['term_slug'][ $parent_slug ] ) ) {
   2069 				$data['parent'] = $this->mapping['term_slug'][ $parent_slug ];
   2070 			} else {
   2071 				// Prepare for remapping later
   2072 				$meta[]             = array(
   2073 					'key'   => '_wxr_import_parent',
   2074 					'value' => $parent_slug,
   2075 				);
   2076 				$requires_remapping = true;
   2077 
   2078 				// Wipe the parent id for now
   2079 				$data['parent'] = 0;
   2080 			}
   2081 		}
   2082 
   2083 		foreach ( $data as $key => $value ) {
   2084 			if ( ! isset( $allowed[ $key ] ) ) {
   2085 				continue;
   2086 			}
   2087 
   2088 			$termdata[ $key ] = $data[ $key ];
   2089 		}
   2090 
   2091 		$result = wp_insert_term( $data['name'], $data['taxonomy'], $termdata );
   2092 		if ( is_wp_error( $result ) ) {
   2093 			$this->logger->warning(
   2094 				sprintf(
   2095 					__( 'Failed to import %1$s %2$s', 'wordpress-importer' ),
   2096 					$data['taxonomy'],
   2097 					$data['name']
   2098 				)
   2099 			);
   2100 			$this->logger->debug( $result->get_error_message() );
   2101 			do_action( 'wp_import_insert_term_failed', $result, $data );
   2102 
   2103 			/**
   2104 			 * Term processing failed.
   2105 			 *
   2106 			 * @param WP_Error $result Error object.
   2107 			 * @param array $data Raw data imported for the term.
   2108 			 * @param array $meta Meta data supplied for the term.
   2109 			 */
   2110 			do_action( 'wxr_importer.process_failed.term', $result, $data, $meta );
   2111 			return false;
   2112 		}
   2113 
   2114 		$term_id = $result['term_id'];
   2115 
   2116 		// Now prepare to map this new term.
   2117 		$this->mapping['term'][ $mapping_key ]    = $term_id;
   2118 		$this->mapping['term_id'][ $original_id ] = $term_id;
   2119 		$this->mapping['term_slug'][ $term_slug ] = $term_id;
   2120 
   2121 		/*
   2122 		 * Fix for OCDI!
   2123 		 * The parent will be updated later in post_process_terms
   2124 		 * we will need both the term_id AND the term_taxonomy to retrieve existing
   2125 		 * term attributes. Those attributes will be returned with the corrected parent,
   2126 		 * using wp_update_term.
   2127 		 * Pass both the term_id along with the term_taxonomy as key=>value
   2128 		 * in the requires_remapping['term'] array.
   2129 		 */
   2130 		if ( $requires_remapping ) {
   2131 			$this->requires_remapping['term'][ $term_id ] = $data['taxonomy'];
   2132 		}
   2133 
   2134 		$this->logger->info(
   2135 			sprintf(
   2136 				__( 'Imported "%1$s" (%2$s)', 'wordpress-importer' ),
   2137 				$data['name'],
   2138 				$data['taxonomy']
   2139 			)
   2140 		);
   2141 		$this->logger->debug(
   2142 			sprintf(
   2143 				__( 'Term %1$d remapped to %2$d', 'wordpress-importer' ),
   2144 				$original_id,
   2145 				$term_id
   2146 			)
   2147 		);
   2148 
   2149 		// Actuall process of the term meta data.
   2150 		$this->process_term_meta( $meta, $term_id, $data );
   2151 
   2152 		do_action( 'wp_import_insert_term', $term_id, $data );
   2153 
   2154 		/**
   2155 		 * Term processing completed.
   2156 		 *
   2157 		 * @param int $term_id New term ID.
   2158 		 * @param array $data Raw data imported for the term.
   2159 		 */
   2160 		do_action( 'wxr_importer.processed.term', $term_id, $data );
   2161 	}
   2162 
   2163 	/**
   2164 	 * Process and import term meta items.
   2165 	 *
   2166 	 * @param  array $meta    List of meta data arrays
   2167 	 * @param  int   $term_id Term ID to associate with
   2168 	 * @param  array $term    Term data
   2169 	 * @return int|bool Number of meta items imported on success, false otherwise.
   2170 	 */
   2171 	protected function process_term_meta( $meta, $term_id, $term ) {
   2172 		if ( empty( $meta ) ) {
   2173 			return true;
   2174 		}
   2175 
   2176 		foreach ( $meta as $meta_item ) {
   2177 			/**
   2178 			 * Pre-process term meta data.
   2179 			 *
   2180 			 * @param array $meta_item Meta data. (Return empty to skip.)
   2181 			 * @param int $term_id Term the meta is attached to.
   2182 			 */
   2183 			$meta_item = apply_filters( 'wxr_importer.pre_process.term_meta', $meta_item, $term_id );
   2184 
   2185 			if ( empty( $meta_item ) ) {
   2186 				continue;
   2187 			}
   2188 
   2189 			$key   = apply_filters( 'import_term_meta_key', $meta_item['key'], $term_id, $term );
   2190 			$value = false;
   2191 
   2192 			if ( $key ) {
   2193 				// Export gets meta straight from the DB so could have a serialized string.
   2194 				if ( ! $value ) {
   2195 					$value = maybe_unserialize( $meta_item['value'] );
   2196 				}
   2197 
   2198 				$result = add_term_meta( $term_id, $key, $value );
   2199 
   2200 				if ( is_wp_error( $result ) ) {
   2201 					$this->logger->warning(
   2202 						sprintf(
   2203 							__( 'Failed to add metakey: %1$s, metavalue: %2$s to term_id: %3$d', 'wordpress-importer' ),
   2204 							$key,
   2205 							$value,
   2206 							$term_id
   2207 						)
   2208 					);
   2209 					do_action( 'wxr_importer.process_failed.termmeta', $result, $meta_item, $term_id, $term );
   2210 				} else {
   2211 					$this->logger->debug(
   2212 						sprintf(
   2213 							__( 'Meta for term_id %1$d : %2$s => %3$s ; successfully added!', 'wordpress-importer' ),
   2214 							$term_id,
   2215 							$key,
   2216 							$value
   2217 						)
   2218 					);
   2219 				}
   2220 
   2221 				do_action( 'import_term_meta', $term_id, $key, $value );
   2222 			}
   2223 		}
   2224 
   2225 		return true;
   2226 	}
   2227 
   2228 	/**
   2229 	 * Attempt to download a remote file attachment
   2230 	 *
   2231 	 * @param  string $url  URL of item to fetch
   2232 	 * @param  array  $post Attachment details
   2233 	 * @return array|WP_Error Local file location details on success, WP_Error otherwise
   2234 	 */
   2235 	protected function fetch_remote_file( $url, $post ) {
   2236 		// extract the file name and extension from the url
   2237 		$file_name = basename( $url );
   2238 
   2239 		// get placeholder file in the upload dir with a unique, sanitized filename
   2240 		$upload = wp_upload_bits( $file_name, 0, '', $post['upload_date'] );
   2241 		// $this->write_log("\n" . 'wp_upload_bits='. $file_name);
   2242 
   2243 		if ( $upload['error'] ) {
   2244 			return new \WP_Error( 'upload_dir_error', $upload['error'] );
   2245 		}
   2246 
   2247 		// fetch the remote url and write it to the placeholder file
   2248 		$response = wp_remote_get(
   2249 			$url,
   2250 			array(
   2251 				'stream'   => true,
   2252 				'filename' => $upload['file'],
   2253 			)
   2254 		);
   2255 
   2256 		// request failed
   2257 		if ( is_wp_error( $response ) ) {
   2258 			unlink( $upload['file'] );
   2259 			return $response;
   2260 		}
   2261 
   2262 		$code = (int) wp_remote_retrieve_response_code( $response );
   2263 
   2264 		// make sure the fetch was successful
   2265 		if ( $code !== 200 ) {
   2266 			unlink( $upload['file'] );
   2267 			return new \WP_Error(
   2268 				'import_file_error',
   2269 				sprintf(
   2270 					__( 'Remote server returned %1$d %2$s for %3$s', 'wordpress-importer' ),
   2271 					$code,
   2272 					get_status_header_desc( $code ),
   2273 					$url
   2274 				)
   2275 			);
   2276 		}
   2277 
   2278 		$filesize = filesize( $upload['file'] );
   2279 		$headers  = wp_remote_retrieve_headers( $response );
   2280 
   2281 		// OCDI fix!
   2282 		// Smaller images with server compression do not pass this rule.
   2283 		// More info here: https://github.com/proteusthemes/WordPress-Importer/pull/2
   2284 		//
   2285 		// if ( isset( $headers['content-length'] ) && $filesize !== (int) $headers['content-length'] ) {
   2286 		// unlink( $upload['file'] );
   2287 		// return new \WP_Error( 'import_file_error', __( 'Remote file is incorrect size', 'wordpress-importer' ) );
   2288 		// }
   2289 
   2290 		if ( 0 === $filesize ) {
   2291 			unlink( $upload['file'] );
   2292 			return new \WP_Error( 'import_file_error', __( 'Zero size file downloaded', 'wordpress-importer' ) );
   2293 		}
   2294 
   2295 		$max_size = (int) $this->max_attachment_size();
   2296 		if ( ! empty( $max_size ) && $filesize > $max_size ) {
   2297 			unlink( $upload['file'] );
   2298 			$message = sprintf( __( 'Remote file is too large, limit is %s', 'wordpress-importer' ), size_format( $max_size ) );
   2299 			return new \WP_Error( 'import_file_error', $message );
   2300 		}
   2301 
   2302 		return $upload;
   2303 	}
   2304 
   2305 	protected function post_process() {
   2306 		// Time to tackle any left-over bits
   2307 		if ( ! empty( $this->requires_remapping['post'] ) ) {
   2308 			$this->post_process_posts( $this->requires_remapping['post'] );
   2309 		}
   2310 		if ( ! empty( $this->requires_remapping['comment'] ) ) {
   2311 			$this->post_process_comments( $this->requires_remapping['comment'] );
   2312 		}
   2313 		if ( ! empty( $this->requires_remapping['term'] ) ) {
   2314 			$this->post_process_terms( $this->requires_remapping['term'] );
   2315 		}
   2316 	}
   2317 
   2318 	protected function post_process_posts( $todo ) {
   2319 		foreach ( $todo as $post_id => $_ ) {
   2320 			$this->logger->debug(
   2321 				sprintf(
   2322 				// Note: title intentionally not used to skip extra processing
   2323 				// for when debug logging is off
   2324 					__( 'Running post-processing for post %d', 'wordpress-importer' ),
   2325 					$post_id
   2326 				)
   2327 			);
   2328 
   2329 			$data = array();
   2330 
   2331 			$parent_id = get_post_meta( $post_id, '_wxr_import_parent', true );
   2332 			if ( ! empty( $parent_id ) ) {
   2333 				// Have we imported the parent now?
   2334 				if ( isset( $this->mapping['post'][ $parent_id ] ) ) {
   2335 					$data['post_parent'] = $this->mapping['post'][ $parent_id ];
   2336 				} else {
   2337 					$this->logger->warning(
   2338 						sprintf(
   2339 							__( 'Could not find the post parent for "%1$s" (post #%2$d)', 'wordpress-importer' ),
   2340 							get_the_title( $post_id ),
   2341 							$post_id
   2342 						)
   2343 					);
   2344 					$this->logger->debug(
   2345 						sprintf(
   2346 							__( 'Post %1$d was imported with parent %2$d, but could not be found', 'wordpress-importer' ),
   2347 							$post_id,
   2348 							$parent_id
   2349 						)
   2350 					);
   2351 				}
   2352 			}
   2353 
   2354 			$author_slug = get_post_meta( $post_id, '_wxr_import_user_slug', true );
   2355 			if ( ! empty( $author_slug ) ) {
   2356 				// Have we imported the user now?
   2357 				if ( isset( $this->mapping['user_slug'][ $author_slug ] ) ) {
   2358 					$data['post_author'] = $this->mapping['user_slug'][ $author_slug ];
   2359 				} else {
   2360 					$this->logger->warning(
   2361 						sprintf(
   2362 							__( 'Could not find the author for "%1$s" (post #%2$d)', 'wordpress-importer' ),
   2363 							get_the_title( $post_id ),
   2364 							$post_id
   2365 						)
   2366 					);
   2367 					$this->logger->debug(
   2368 						sprintf(
   2369 							__( 'Post %1$d was imported with author "%2$s", but could not be found', 'wordpress-importer' ),
   2370 							$post_id,
   2371 							$author_slug
   2372 						)
   2373 					);
   2374 				}
   2375 			}
   2376 
   2377 			$has_attachments = get_post_meta( $post_id, '_wxr_import_has_attachment_refs', true );
   2378 			if ( ! empty( $has_attachments ) ) {
   2379 				$post    = get_post( $post_id );
   2380 				$content = $post->post_content;
   2381 
   2382 				// Replace all the URLs we've got
   2383 				$new_content = str_replace( array_keys( $this->url_remap ), $this->url_remap, $content );
   2384 				if ( $new_content !== $content ) {
   2385 					$data['post_content'] = $new_content;
   2386 				}
   2387 			}
   2388 
   2389 			if ( get_post_type( $post_id ) === 'nav_menu_item' ) {
   2390 				$this->post_process_menu_item( $post_id );
   2391 			}
   2392 
   2393 			// Do we have updates to make?
   2394 			if ( empty( $data ) ) {
   2395 				$this->logger->debug(
   2396 					sprintf(
   2397 						__( 'Post %d was marked for post-processing, but none was required.', 'wordpress-importer' ),
   2398 						$post_id
   2399 					)
   2400 				);
   2401 				continue;
   2402 			}
   2403 
   2404 			// Run the update
   2405 			$data['ID'] = $post_id;
   2406 			$result     = wp_update_post( $data, true );
   2407 			if ( is_wp_error( $result ) ) {
   2408 				$this->logger->warning(
   2409 					sprintf(
   2410 						__( 'Could not update "%1$s" (post #%2$d) with mapped data', 'wordpress-importer' ),
   2411 						get_the_title( $post_id ),
   2412 						$post_id
   2413 					)
   2414 				);
   2415 				$this->logger->debug( $result->get_error_message() );
   2416 				continue;
   2417 			}
   2418 
   2419 			// Clear out our temporary meta keys
   2420 			delete_post_meta( $post_id, '_wxr_import_parent' );
   2421 			delete_post_meta( $post_id, '_wxr_import_user_slug' );
   2422 			delete_post_meta( $post_id, '_wxr_import_has_attachment_refs' );
   2423 		}
   2424 	}
   2425 
   2426 	protected function post_process_menu_item( $post_id ) {
   2427 		$menu_object_id = get_post_meta( $post_id, '_wxr_import_menu_item', true );
   2428 		if ( empty( $menu_object_id ) ) {
   2429 			// No processing needed!
   2430 			return;
   2431 		}
   2432 
   2433 		$menu_item_type = get_post_meta( $post_id, '_menu_item_type', true );
   2434 		switch ( $menu_item_type ) {
   2435 			case 'taxonomy':
   2436 				if ( isset( $this->mapping['term_id'][ $menu_object_id ] ) ) {
   2437 					$menu_object = $this->mapping['term_id'][ $menu_object_id ];
   2438 				}
   2439 				break;
   2440 
   2441 			case 'post_type':
   2442 				if ( isset( $this->mapping['post'][ $menu_object_id ] ) ) {
   2443 					$menu_object = $this->mapping['post'][ $menu_object_id ];
   2444 				}
   2445 				break;
   2446 
   2447 			default:
   2448 				// Cannot handle this.
   2449 				return;
   2450 		}
   2451 
   2452 		if ( ! empty( $menu_object ) ) {
   2453 			update_post_meta( $post_id, '_menu_item_object_id', wp_slash( $menu_object ) );
   2454 		} else {
   2455 			$this->logger->warning(
   2456 				sprintf(
   2457 					__( 'Could not find the menu object for "%1$s" (post #%2$d)', 'wordpress-importer' ),
   2458 					get_the_title( $post_id ),
   2459 					$post_id
   2460 				)
   2461 			);
   2462 			$this->logger->debug(
   2463 				sprintf(
   2464 					__( 'Post %1$d was imported with object "%2$d" of type "%3$s", but could not be found', 'wordpress-importer' ),
   2465 					$post_id,
   2466 					$menu_object_id,
   2467 					$menu_item_type
   2468 				)
   2469 			);
   2470 		}
   2471 
   2472 		delete_post_meta( $post_id, '_wxr_import_menu_item' );
   2473 	}
   2474 
   2475 	protected function post_process_comments( $todo ) {
   2476 		foreach ( $todo as $comment_id => $_ ) {
   2477 			$data = array();
   2478 
   2479 			$parent_id = get_comment_meta( $comment_id, '_wxr_import_parent', true );
   2480 			if ( ! empty( $parent_id ) ) {
   2481 				// Have we imported the parent now?
   2482 				if ( isset( $this->mapping['comment'][ $parent_id ] ) ) {
   2483 					$data['comment_parent'] = $this->mapping['comment'][ $parent_id ];
   2484 				} else {
   2485 					$this->logger->warning(
   2486 						sprintf(
   2487 							__( 'Could not find the comment parent for comment #%d', 'wordpress-importer' ),
   2488 							$comment_id
   2489 						)
   2490 					);
   2491 					$this->logger->debug(
   2492 						sprintf(
   2493 							__( 'Comment %1$d was imported with parent %2$d, but could not be found', 'wordpress-importer' ),
   2494 							$comment_id,
   2495 							$parent_id
   2496 						)
   2497 					);
   2498 				}
   2499 			}
   2500 
   2501 			$author_id = get_comment_meta( $comment_id, '_wxr_import_user', true );
   2502 			if ( ! empty( $author_id ) ) {
   2503 				// Have we imported the user now?
   2504 				if ( isset( $this->mapping['user'][ $author_id ] ) ) {
   2505 					$data['user_id'] = $this->mapping['user'][ $author_id ];
   2506 				} else {
   2507 					$this->logger->warning(
   2508 						sprintf(
   2509 							__( 'Could not find the author for comment #%d', 'wordpress-importer' ),
   2510 							$comment_id
   2511 						)
   2512 					);
   2513 					$this->logger->debug(
   2514 						sprintf(
   2515 							__( 'Comment %1$d was imported with author %2$d, but could not be found', 'wordpress-importer' ),
   2516 							$comment_id,
   2517 							$author_id
   2518 						)
   2519 					);
   2520 				}
   2521 			}
   2522 
   2523 			// Do we have updates to make?
   2524 			if ( empty( $data ) ) {
   2525 				continue;
   2526 			}
   2527 
   2528 			// Run the update
   2529 			$data['comment_ID'] = $comment_ID;
   2530 			$result             = wp_update_comment( wp_slash( $data ) );
   2531 			if ( empty( $result ) ) {
   2532 				$this->logger->warning(
   2533 					sprintf(
   2534 						__( 'Could not update comment #%d with mapped data', 'wordpress-importer' ),
   2535 						$comment_id
   2536 					)
   2537 				);
   2538 				continue;
   2539 			}
   2540 
   2541 			// Clear out our temporary meta keys
   2542 			delete_comment_meta( $comment_id, '_wxr_import_parent' );
   2543 			delete_comment_meta( $comment_id, '_wxr_import_user' );
   2544 		}
   2545 	}
   2546 
   2547 	/**
   2548 	 * There is no explicit 'top' or 'root' for a hierarchy of WordPress terms
   2549 	 * Terms without a parent, or parent=0 are either unconnected (orphans)
   2550 	 * or top-level siblings without an explicit root parent
   2551 	 * An unconnected term (orphan) should have a null parent_slug
   2552 	 * Top-level siblings without an explicit root parent, shall be identified
   2553 	 * with the parent_slug: top
   2554 	 * [we'll map parent_slug: top into parent 0]
   2555 	 */
   2556 	protected function post_process_terms( $terms_to_be_remapped ) {
   2557 		$this->mapping['term_slug']['top'] = 0;
   2558 		// The term_id and term_taxonomy are passed-in with $this->requires_remapping['term'].
   2559 		foreach ( $terms_to_be_remapped as $termid => $term_taxonomy ) {
   2560 			// Basic check.
   2561 			if ( empty( $termid ) || ! is_numeric( $termid ) ) {
   2562 				$this->logger->warning(
   2563 					sprintf(
   2564 						__( 'Faulty term_id provided in terms-to-be-remapped array %s', 'wordpress-importer' ),
   2565 						$termid
   2566 					)
   2567 				);
   2568 				continue;
   2569 			}
   2570 			// This cast to integer may be unnecessary.
   2571 			$term_id = (int) $termid;
   2572 
   2573 			if ( empty( $term_taxonomy ) ) {
   2574 				$this->logger->warning(
   2575 					sprintf(
   2576 						__( 'No taxonomy provided in terms-to-be-remapped array for term #%d', 'wordpress-importer' ),
   2577 						$term_id
   2578 					)
   2579 				);
   2580 				continue;
   2581 			}
   2582 
   2583 			$data        = array();
   2584 			$parent_slug = get_term_meta( $term_id, '_wxr_import_parent', true );
   2585 
   2586 			if ( empty( $parent_slug ) ) {
   2587 				$this->logger->warning(
   2588 					sprintf(
   2589 						__( 'No parent_slug identified in remapping-array for term: %d', 'wordpress-importer' ),
   2590 						$term_id
   2591 					)
   2592 				);
   2593 				continue;
   2594 			}
   2595 
   2596 			if ( ! isset( $this->mapping['term_slug'][ $parent_slug ] ) || ! is_numeric( $this->mapping['term_slug'][ $parent_slug ] ) ) {
   2597 				$this->logger->warning(
   2598 					sprintf(
   2599 						__( 'The term(%1$d)"s parent_slug (%2$s) is not found in the remapping-array.', 'wordpress-importer' ),
   2600 						$term_id,
   2601 						$parent_slug
   2602 					)
   2603 				);
   2604 				continue;
   2605 			}
   2606 
   2607 			$mapped_parent = (int) $this->mapping['term_slug'][ $parent_slug ];
   2608 
   2609 			$termattributes = get_term_by( 'id', $term_id, $term_taxonomy, ARRAY_A );
   2610 			// Note: the default OBJECT return results in a reserved-word clash with 'parent' [$termattributes->parent], so instead return an associative array.
   2611 
   2612 			if ( empty( $termattributes ) ) {
   2613 				$this->logger->warning(
   2614 					sprintf(
   2615 						__( 'No data returned by get_term_by for term_id #%d', 'wordpress-importer' ),
   2616 						$term_id
   2617 					)
   2618 				);
   2619 				continue;
   2620 			}
   2621 			// Check if the correct parent id is already correctly mapped.
   2622 			if ( isset( $termattributes['parent'] ) && $termattributes['parent'] == $mapped_parent ) {
   2623 				// Clear out our temporary meta key.
   2624 				delete_term_meta( $term_id, '_wxr_import_parent' );
   2625 				continue;
   2626 			}
   2627 
   2628 			// Otherwise set the mapped parent and update the term.
   2629 			$termattributes['parent'] = $mapped_parent;
   2630 
   2631 			$result = wp_update_term( $term_id, $termattributes['taxonomy'], $termattributes );
   2632 
   2633 			if ( is_wp_error( $result ) ) {
   2634 				$this->logger->warning(
   2635 					sprintf(
   2636 						__( 'Could not update "%1$s" (term #%2$d) with mapped data', 'wordpress-importer' ),
   2637 						$termattributes['name'],
   2638 						$term_id
   2639 					)
   2640 				);
   2641 				$this->logger->debug( $result->get_error_message() );
   2642 				continue;
   2643 			}
   2644 			// Clear out our temporary meta key.
   2645 			delete_term_meta( $term_id, '_wxr_import_parent' );
   2646 			$this->logger->debug(
   2647 				sprintf(
   2648 					__( 'Term %1$d was successfully updated with parent %2$d', 'wordpress-importer' ),
   2649 					$term_id,
   2650 					$mapped_parent
   2651 				)
   2652 			);
   2653 		}
   2654 	}
   2655 
   2656 	/**
   2657 	 * Use stored mapping information to update old attachment URLs
   2658 	 */
   2659 	protected function replace_attachment_urls_in_content() {
   2660 		global $wpdb;
   2661 		// make sure we do the longest urls first, in case one is a substring of another
   2662 		uksort( $this->url_remap, array( $this, 'cmpr_strlen' ) );
   2663 
   2664 		foreach ( $this->url_remap as $from_url => $to_url ) {
   2665 			// remap urls in post_content
   2666 			$query = $wpdb->prepare( "UPDATE {$wpdb->posts} SET post_content = REPLACE(post_content, %s, %s)", $from_url, $to_url );
   2667 			$wpdb->query( $query );
   2668 
   2669 			// remap enclosure urls
   2670 			$query  = $wpdb->prepare( "UPDATE {$wpdb->postmeta} SET meta_value = REPLACE(meta_value, %s, %s) WHERE meta_key='enclosure'", $from_url, $to_url );
   2671 			$result = $wpdb->query( $query );
   2672 		}
   2673 	}
   2674 
   2675 	/**
   2676 	 * Update _thumbnail_id meta to new, imported attachment IDs
   2677 	 */
   2678 	function remap_featured_images() {
   2679 		if ( empty( $this->featured_images ) ) {
   2680 			return;
   2681 		}
   2682 
   2683 		$this->logger->info( esc_html__( 'Starting remapping of featured images', 'wordpress-importer' ) );
   2684 
   2685 		// Cycle through posts that have a featured image.
   2686 		foreach ( $this->featured_images as $post_id => $value ) {
   2687 			if ( isset( $this->mapping['post'][ $value ] ) ) {
   2688 				$new_id = $this->mapping['post'][ $value ];
   2689 
   2690 				// Only update if there's a difference.
   2691 				if ( $new_id !== $value ) {
   2692 					$this->logger->info( sprintf( esc_html__( 'Remapping featured image ID %1$d to new ID %2$d for post ID %3$d', 'wordpress-importer' ), $value, $new_id, $post_id ) );
   2693 
   2694 					update_post_meta( $post_id, '_thumbnail_id', $new_id );
   2695 				}
   2696 			}
   2697 		}
   2698 	}
   2699 
   2700 	/**
   2701 	 * Decide if the given meta key maps to information we will want to import
   2702 	 *
   2703 	 * @param  string $key The meta key to check
   2704 	 * @return string|bool The key if we do want to import, false if not
   2705 	 */
   2706 	public function is_valid_meta_key( $key ) {
   2707 		// skip attachment metadata since we'll regenerate it from scratch
   2708 		// skip _edit_lock as not relevant for import
   2709 		if ( in_array( $key, array( '_wp_attached_file', '_wp_attachment_metadata', '_edit_lock' ) ) ) {
   2710 			return false;
   2711 		}
   2712 
   2713 		return $key;
   2714 	}
   2715 
   2716 	/**
   2717 	 * Decide what the maximum file size for downloaded attachments is.
   2718 	 * Default is 0 (unlimited), can be filtered via import_attachment_size_limit
   2719 	 *
   2720 	 * @return int Maximum attachment file size to import
   2721 	 */
   2722 	protected function max_attachment_size() {
   2723 		return apply_filters( 'import_attachment_size_limit', 0 );
   2724 	}
   2725 
   2726 	/**
   2727 	 * Added to http_request_timeout filter to force timeout at 60 seconds during import
   2728 	 *
   2729 	 * @access protected
   2730 	 * @return int 60
   2731 	 */
   2732 	function bump_request_timeout( $val ) {
   2733 		return 60;
   2734 	}
   2735 
   2736 	// return the difference in length between two strings
   2737 	function cmpr_strlen( $a, $b ) {
   2738 		return strlen( $b ) - strlen( $a );
   2739 	}
   2740 
   2741 	/**
   2742 	 * Prefill existing post data.
   2743 	 *
   2744 	 * This preloads all GUIDs into memory, allowing us to avoid hitting the
   2745 	 * database when we need to check for existence. With larger imports, this
   2746 	 * becomes prohibitively slow to perform SELECT queries on each.
   2747 	 *
   2748 	 * By preloading all this data into memory, it's a constant-time lookup in
   2749 	 * PHP instead. However, this does use a lot more memory, so for sites doing
   2750 	 * small imports onto a large site, it may be a better tradeoff to use
   2751 	 * on-the-fly checking instead.
   2752 	 */
   2753 	protected function prefill_existing_posts() {
   2754 		global $wpdb;
   2755 		$posts = $wpdb->get_results( "SELECT ID, guid FROM {$wpdb->posts}" );
   2756 
   2757 		foreach ( $posts as $item ) {
   2758 			$this->exists['post'][ $item->guid ] = $item->ID;
   2759 		}
   2760 	}
   2761 
   2762 	/**
   2763 	 * Does the post exist?
   2764 	 *
   2765 	 * @param  array $data Post data to check against.
   2766 	 * @return int|bool Existing post ID if it exists, false otherwise.
   2767 	 */
   2768 	protected function post_exists( $data ) {
   2769 		// Constant-time lookup if we prefilled
   2770 		$exists_key = $data['guid'];
   2771 
   2772 		if ( $this->options['prefill_existing_posts'] ) {
   2773 			// OCDI: fix for custom post types. The guids in the prefilled section are escaped, so these ones should be as well.
   2774 			$exists_key = htmlentities( $exists_key );
   2775 			return isset( $this->exists['post'][ $exists_key ] ) ? $this->exists['post'][ $exists_key ] : false;
   2776 		}
   2777 
   2778 		// No prefilling, but might have already handled it
   2779 		if ( isset( $this->exists['post'][ $exists_key ] ) ) {
   2780 			return $this->exists['post'][ $exists_key ];
   2781 		}
   2782 
   2783 		// Still nothing, try post_exists, and cache it
   2784 		$exists                              = post_exists( $data['post_title'], $data['post_content'], $data['post_date'] );
   2785 		$this->exists['post'][ $exists_key ] = $exists;
   2786 
   2787 		return $exists;
   2788 	}
   2789 
   2790 	/**
   2791 	 * Mark the post as existing.
   2792 	 *
   2793 	 * @param array $data    Post data to mark as existing.
   2794 	 * @param int   $post_id Post ID.
   2795 	 */
   2796 	protected function mark_post_exists( $data, $post_id ) {
   2797 		$exists_key                          = $data['guid'];
   2798 		$this->exists['post'][ $exists_key ] = $post_id;
   2799 	}
   2800 
   2801 	/**
   2802 	 * Prefill existing comment data.
   2803 	 *
   2804 	 * @see self::prefill_existing_posts() for justification of why this exists.
   2805 	 */
   2806 	protected function prefill_existing_comments() {
   2807 		global $wpdb;
   2808 		$posts = $wpdb->get_results( "SELECT comment_ID, comment_author, comment_date FROM {$wpdb->comments}" );
   2809 
   2810 		foreach ( $posts as $item ) {
   2811 			$exists_key                             = sha1( $item->comment_author . ':' . $item->comment_date );
   2812 			$this->exists['comment'][ $exists_key ] = $item->comment_ID;
   2813 		}
   2814 	}
   2815 
   2816 	/**
   2817 	 * Does the comment exist?
   2818 	 *
   2819 	 * @param  array $data Comment data to check against.
   2820 	 * @return int|bool Existing comment ID if it exists, false otherwise.
   2821 	 */
   2822 	protected function comment_exists( $data ) {
   2823 		$exists_key = sha1( $data['comment_author'] . ':' . $data['comment_date'] );
   2824 
   2825 		// Constant-time lookup if we prefilled
   2826 		if ( $this->options['prefill_existing_comments'] ) {
   2827 			return isset( $this->exists['comment'][ $exists_key ] ) ? $this->exists['comment'][ $exists_key ] : false;
   2828 		}
   2829 
   2830 		// No prefilling, but might have already handled it
   2831 		if ( isset( $this->exists['comment'][ $exists_key ] ) ) {
   2832 			return $this->exists['comment'][ $exists_key ];
   2833 		}
   2834 
   2835 		// Still nothing, try comment_exists, and cache it
   2836 		$exists                                 = comment_exists( $data['comment_author'], $data['comment_date'] );
   2837 		$this->exists['comment'][ $exists_key ] = $exists;
   2838 
   2839 		return $exists;
   2840 	}
   2841 
   2842 	/**
   2843 	 * Mark the comment as existing.
   2844 	 *
   2845 	 * @param array $data       Comment data to mark as existing.
   2846 	 * @param int   $comment_id Comment ID.
   2847 	 */
   2848 	protected function mark_comment_exists( $data, $comment_id ) {
   2849 		$exists_key                             = sha1( $data['comment_author'] . ':' . $data['comment_date'] );
   2850 		$this->exists['comment'][ $exists_key ] = $comment_id;
   2851 	}
   2852 
   2853 	/**
   2854 	 * Prefill existing term data.
   2855 	 *
   2856 	 * @see self::prefill_existing_posts() for justification of why this exists.
   2857 	 */
   2858 	protected function prefill_existing_terms() {
   2859 		global $wpdb;
   2860 		$query  = "SELECT t.term_id, tt.taxonomy, t.slug FROM {$wpdb->terms} AS t";
   2861 		$query .= " JOIN {$wpdb->term_taxonomy} AS tt ON t.term_id = tt.term_id";
   2862 		$terms  = $wpdb->get_results( $query );
   2863 
   2864 		foreach ( $terms as $item ) {
   2865 			$exists_key                          = sha1( $item->taxonomy . ':' . $item->slug );
   2866 			$this->exists['term'][ $exists_key ] = $item->term_id;
   2867 		}
   2868 	}
   2869 
   2870 	/**
   2871 	 * Does the term exist?
   2872 	 *
   2873 	 * @param  array $data Term data to check against.
   2874 	 * @return int|bool Existing term ID if it exists, false otherwise.
   2875 	 */
   2876 	protected function term_exists( $data ) {
   2877 		$exists_key = sha1( $data['taxonomy'] . ':' . $data['slug'] );
   2878 
   2879 		// Constant-time lookup if we prefilled
   2880 		if ( $this->options['prefill_existing_terms'] ) {
   2881 			return isset( $this->exists['term'][ $exists_key ] ) ? $this->exists['term'][ $exists_key ] : false;
   2882 		}
   2883 
   2884 		// No prefilling, but might have already handled it
   2885 		if ( isset( $this->exists['term'][ $exists_key ] ) ) {
   2886 			return $this->exists['term'][ $exists_key ];
   2887 		}
   2888 
   2889 		// Still nothing, try comment_exists, and cache it
   2890 		$exists = term_exists( $data['slug'], $data['taxonomy'] );
   2891 		if ( is_array( $exists ) ) {
   2892 			$exists = $exists['term_id'];
   2893 		}
   2894 
   2895 		$this->exists['term'][ $exists_key ] = $exists;
   2896 
   2897 		return $exists;
   2898 	}
   2899 
   2900 	/**
   2901 	 * Mark the term as existing.
   2902 	 *
   2903 	 * @param array $data    Term data to mark as existing.
   2904 	 * @param int   $term_id Term ID.
   2905 	 */
   2906 	protected function mark_term_exists( $data, $term_id ) {
   2907 		$exists_key                          = sha1( $data['taxonomy'] . ':' . $data['slug'] );
   2908 		$this->exists['term'][ $exists_key ] = $term_id;
   2909 	}
   2910 }