assets/js/soo-import.js 0000644 00000007724 14751055733 0011154 0 ustar 00 jQuery( document ).ready( function( $ ) {
"use strict";
var soodi = {
init: function() {
this.$progress = $( '#soo-demo-import-progress' );
this.$log = $( '#soo-demo-import-log' );
this.$importer = $( '#soo-demo-importer' );
this.steps = ['content', 'customizer', 'widgets', 'sliders'];
if ( ! this.$importer.length ) {
return;
}
/**
* The first step: download content file
*/
this.download( soodi.steps.shift() );
},
download: function( type ) {
soodi.log( 'Downloading ' + type + ' file' );
$.get(
ajaxurl,
{
action: 'soodi_download_file',
type: type,
demo: soodi.$importer.find( 'input[name="demo"]' ).val(),
_wpnonce: soodi.$importer.find( 'input[name="_wpnonce"]' ).val()
},
function( response ) {
if ( response.success ) {
soodi.import( type );
} else {
soodi.log( response.data );
if ( soodi.steps.length ) {
soodi.download( soodi.steps.shift() );
} else {
soodi.configTheme();
}
}
}
).fail( function() {
soodi.log( 'Failed' );
} );
},
import: function( type ) {
soodi.log( 'Importing ' + type );
var data = {
action: 'soodi_import',
type: type,
_wpnonce: soodi.$importer.find( 'input[name="_wpnonce"]' ).val()
};
var url = ajaxurl + '?' + $.param( data );
var evtSource = new EventSource( url );
evtSource.addEventListener( 'message', function ( message ) {
var data = JSON.parse( message.data );
switch ( data.action ) {
case 'updateTotal':
console.log( data.delta );
break;
case 'updateDelta':
console.log(data.delta);
break;
case 'complete':
evtSource.close();
soodi.log( type + ' has been imported successfully!' );
if ( soodi.steps.length ) {
soodi.download( soodi.steps.shift() );
} else {
soodi.configTheme();
}
break;
}
} );
evtSource.addEventListener( 'log', function ( message ) {
var data = JSON.parse( message.data );
soodi.log( data.message );
});
},
configTheme: function() {
$.get(
ajaxurl,
{
action: 'soodi_config_theme',
demo: soodi.$importer.find( 'input[name="demo"]' ).val(),
_wpnonce: soodi.$importer.find( 'input[name="_wpnonce"]' ).val()
},
function( response ) {
if ( response.success ) {
soodi.generateImages();
}
soodi.log( response.data );
}
).fail( function() {
soodi.log( 'Failed' );
} );
},
generateImages: function() {
$.get(
ajaxurl,
{
action: 'soodi_get_images',
_wpnonce: soodi.$importer.find( 'input[name="_wpnonce"]' ).val()
},
function( response ) {
if ( ! response.success ) {
soodi.log( response.data );
soodi.log( 'Import completed!' );
soodi.$progress.find( '.spinner' ).hide();
return;
} else {
var ids = response.data;
if ( ! ids.length ) {
soodi.log( 'Import completed!' );
soodi.$progress.find( '.spinner' ).hide();
}
soodi.log( 'Starting generate ' + ids.length + ' images' );
soodi.generateSingleImage( ids );
}
}
);
},
generateSingleImage: function( ids ) {
if ( ! ids.length ) {
soodi.log( 'Import completed!' );
soodi.$progress.find( '.spinner' ).hide();
return;
}
var id = ids.shift();
$.get(
ajaxurl,
{
action: 'soodi_generate_image',
id: id,
_wpnonce: soodi.$importer.find( 'input[name="_wpnonce"]' ).val()
},
function( response ) {
soodi.log( response.data + ' (' + ids.length + ' images left)' );
soodi.generateSingleImage( ids );
}
);
},
log: function( message ) {
soodi.$progress.find( '.text' ).text( message );
soodi.$log.append( '<p>' + message + '</p>' );
}
};
soodi.init();
} );
assets/css/soo-import.css 0000644 00000001452 14751055733 0011474 0 ustar 00 .demos-container {
overflow: hidden;
text-align: center;
}
.demo-selector {
display: inline-block;
width: 20%;
border: 1px solid #dedede;
margin: 0 3% 3% 0;
position: relative;
}
.demo-image {
position: relative;
}
.demo-image img {
width: 100%;
display: block;
}
.demo-selector-tools {
position: relative;
border-top: 1px solid #dedede;
background-color: rgba(255,255,255,.65)
}
.demo-install-actions {
position: relative;
z-index: 2;
}
.demo-selector-tools .button {
float: right;
}
.demo-selector-tools .button {
margin: 10px 15px 10px 0;
}
.demo-title {
font-size: 15px;
font-weight: 600;
text-align: left;
margin: 0;
padding: 15px;
}
#soo-demo-import-progress .spinner {
float: left;
margin-top: 0;
margin-left: 0;
}
README.md 0000644 00000000023 14751055733 0006030 0 ustar 00 soo-demo-importer
includes/class-logger-cli.php 0000644 00000001552 14751055733 0012227 0 ustar 00 <?php
class WP_Importer_Logger_CLI extends WP_Importer_Logger {
public $min_level = 'notice';
/**
* Logs with an arbitrary level.
*
* @param mixed $level
* @param string $message
* @param array $context
* @return null
*/
public function log( $level, $message, array $context = array() ) {
if ( $this->level_to_numeric( $level ) < $this->level_to_numeric( $this->min_level ) ) {
return;
}
printf(
'[%s] %s' . PHP_EOL,
strtoupper( $level ),
$message
);
}
public static function level_to_numeric( $level ) {
$levels = array(
'emergency' => 8,
'alert' => 7,
'critical' => 6,
'error' => 5,
'warning' => 4,
'notice' => 3,
'info' => 2,
'debug' => 1,
);
if ( ! isset( $levels[ $level ] ) ) {
return 0;
}
return $levels[ $level ];
}
}
includes/class-wxr-importer.php 0000644 00000230444 14751055733 0012666 0 ustar 00 <?php
class WXR_Importer extends WP_Importer {
/**
* Maximum supported WXR version
*/
const MAX_WXR_VERSION = 1.2;
/**
* Regular expression for checking if a post references an attachment
*
* Note: This is a quick, weak check just to exclude text-only posts. More
* vigorous checking is done later to verify.
*/
const REGEX_HAS_ATTACHMENT_REFS = '!
(
# Match anything with an image or attachment class
class=[\'"].*?\b(wp-image-\d+|attachment-[\w\-]+)\b
|
# Match anything that looks like an upload URL
src=[\'"][^\'"]*(
[0-9]{4}/[0-9]{2}/[^\'"]+\.(jpg|jpeg|png|gif)
|
content/uploads[^\'"]+
)[\'"]
)!ix';
/**
* Version of WXR we're importing.
*
* Defaults to 1.0 for compatibility. Typically overridden by a
* `<wp:wxr_version>` tag at the start of the file.
*
* @var string
*/
protected $version = '1.0';
// information to import from WXR file
protected $categories = array();
protected $tags = array();
protected $base_url = '';
// TODO: REMOVE THESE
protected $processed_terms = array();
protected $processed_posts = array();
protected $processed_menu_items = array();
protected $menu_item_orphans = array();
protected $missing_menu_items = array();
// NEW STYLE
public $options = array();
protected $mapping = array();
protected $requires_remapping = array();
protected $exists = array();
protected $user_slug_override = array();
protected $url_remap = array();
protected $featured_images = array();
/**
* Logger instance.
*
* @var WPImporterLogger
*/
protected $logger;
/**
* Constructor
*
* @param array $options {
* @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.)
* @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.)
* @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.)
* @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 duplication and reimporting. Default is false.)
* @var bool $fetch_attachments Fetch attachments from the remote server. (True fetches and creates attachment posts, false skips attachments. Default is false.)
* @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.)
* @var int $default_author User ID to use if author is missing or invalid. (Default is null, which leaves posts unassigned.)
* }
*/
public function __construct( $options = array() ) {
// Initialize some important variables
$empty_types = array(
'post' => array(),
'comment' => array(),
'term' => array(),
'user' => array(),
);
$this->mapping = $empty_types;
$this->mapping['user_slug'] = array();
$this->mapping['term_id'] = array();
$this->requires_remapping = $empty_types;
$this->exists = $empty_types;
$this->options = wp_parse_args( $options, array(
'prefill_existing_posts' => true,
'prefill_existing_comments' => true,
'prefill_existing_terms' => true,
'update_attachment_guids' => false,
'fetch_attachments' => false,
'aggressive_url_search' => false,
'default_author' => null,
) );
}
public function set_logger( $logger ) {
$this->logger = $logger;
}
/**
* Get a stream reader for the file.
*
* @param string $file Path to the XML file.
* @return XMLReader|WP_Error Reader instance on success, error otherwise.
*/
protected function get_reader( $file ) {
// Avoid loading external entities for security
$old_value = null;
if ( function_exists( 'libxml_disable_entity_loader' ) ) {
// $old_value = libxml_disable_entity_loader( true );
}
$reader = new XMLReader();
$status = $reader->open( $file );
if ( ! is_null( $old_value ) ) {
// libxml_disable_entity_loader( $old_value );
}
if ( ! $status ) {
return new WP_Error( 'wxr_importer.cannot_parse', __( 'Could not open the file for parsing', 'wordpress-importer' ) );
}
return $reader;
}
/**
* The main controller for the actual import stage.
*
* @param string $file Path to the WXR file for importing
*
* @return WXRImportInfo|WP_Error
*/
public function get_preliminary_information( $file ) {
// Let's run the actual importer now, woot
$reader = $this->get_reader( $file );
if ( is_wp_error( $reader ) ) {
return $reader;
}
// Set the version to compatibility mode first
$this->version = '1.0';
// Start parsing!
$data = new WXRImportInfo();
while ( $reader->read() ) {
// Only deal with element opens
if ( $reader->nodeType !== XMLReader::ELEMENT ) {
continue;
}
switch ( $reader->name ) {
case 'wp:wxr_version':
// Upgrade to the correct version
$this->version = $reader->readString();
if ( version_compare( $this->version, self::MAX_WXR_VERSION, '>' ) ) {
$this->logger->warning( sprintf(
__( 'This WXR file (version %s) is newer than the importer (version %s) and may not be supported. Please consider updating.', 'wordpress-importer' ),
$this->version,
self::MAX_WXR_VERSION
) );
}
// Handled everything in this node, move on to the next
$reader->next();
break;
case 'generator':
$data->generator = $reader->readString();
$reader->next();
break;
case 'title':
$data->title = $reader->readString();
$reader->next();
break;
case 'wp:base_site_url':
$data->siteurl = $reader->readString();
$reader->next();
break;
case 'wp:base_blog_url':
$data->home = $reader->readString();
$reader->next();
break;
case 'wp:author':
$node = $reader->expand();
$parsed = $this->parse_author_node( $node );
if ( is_wp_error( $parsed ) ) {
$this->log_error( $parsed );
// Skip the rest of this post
$reader->next();
break;
}
$data->users[] = $parsed;
// Handled everything in this node, move on to the next
$reader->next();
break;
case 'item':
$node = $reader->expand();
$parsed = $this->parse_post_node( $node );
if ( is_wp_error( $parsed ) ) {
$this->log_error( $parsed );
// Skip the rest of this post
$reader->next();
break;
}
if ( $parsed['data']['post_type'] === 'attachment' ) {
$data->media_count++;
} else {
$data->post_count++;
}
$data->comment_count += count( $parsed['comments'] );
// Handled everything in this node, move on to the next
$reader->next();
break;
case 'wp:category':
case 'wp:tag':
case 'wp:term':
$data->term_count++;
// Handled everything in this node, move on to the next
$reader->next();
break;
}
}
$data->version = $this->version;
return $data;
}
/**
* The main controller for the actual import stage.
*
* @param string $file Path to the WXR file for importing
*
* @return array|WP_Error
*/
public function parse_authors( $file ) {
// Let's run the actual importer now, woot
$reader = $this->get_reader( $file );
if ( is_wp_error( $reader ) ) {
return $reader;
}
// Set the version to compatibility mode first
$this->version = '1.0';
// Start parsing!
$authors = array();
while ( $reader->read() ) {
// Only deal with element opens
if ( $reader->nodeType !== XMLReader::ELEMENT ) {
continue;
}
switch ( $reader->name ) {
case 'wp:wxr_version':
// Upgrade to the correct version
$this->version = $reader->readString();
if ( version_compare( $this->version, self::MAX_WXR_VERSION, '>' ) ) {
$this->logger->warning( sprintf(
__( 'This WXR file (version %s) is newer than the importer (version %s) and may not be supported. Please consider updating.', 'wordpress-importer' ),
$this->version,
self::MAX_WXR_VERSION
) );
}
// Handled everything in this node, move on to the next
$reader->next();
break;
case 'wp:author':
$node = $reader->expand();
$parsed = $this->parse_author_node( $node );
if ( is_wp_error( $parsed ) ) {
$this->log_error( $parsed );
// Skip the rest of this post
$reader->next();
break;
}
$authors[] = $parsed;
// Handled everything in this node, move on to the next
$reader->next();
break;
}
}
return $authors;
}
/**
* The main controller for the actual import stage.
*
* @param string $file Path to the WXR file for importing.
*/
public function import( $file ) {
add_filter( 'import_post_meta_key', array( $this, 'is_valid_meta_key' ) );
add_filter( 'http_request_timeout', array( &$this, 'bump_request_timeout' ) );
/*
* Elementor fix for excessive use of `wp_slash` after our update v3.0.2.
* Method in Elementor: \Elementor\Compatibility::register_actions
* https://wordpress.org/support/topic/version-2-6-0-breaks-every-elementor-theme/
*
* This can be removed after Elementor skips the functionality in above method if our plugin is in use.
*/
if ( method_exists( 'Elementor\Compatibility', 'on_wxr_importer_pre_process_post_meta' ) ) {
remove_action( 'wxr_importer.pre_process.post_meta', array( 'Elementor\Compatibility', 'on_wxr_importer_pre_process_post_meta' ) );
}
$result = $this->import_start( $file );
if ( is_wp_error( $result ) ) {
return $result;
}
// Let's run the actual importer now, woot
$reader = $this->get_reader( $file );
if ( is_wp_error( $reader ) ) {
return $reader;
}
// Set the version to compatibility mode first
$this->version = '1.0';
// Reset other variables
$this->base_url = '';
// Start parsing!
while ( $reader->read() ) {
// Only deal with element opens
if ( $reader->nodeType !== XMLReader::ELEMENT ) {
continue;
}
switch ( $reader->name ) {
case 'wp:wxr_version':
// Upgrade to the correct version
$this->version = $reader->readString();
if ( version_compare( $this->version, self::MAX_WXR_VERSION, '>' ) ) {
$this->logger->warning( sprintf(
__( 'This WXR file (version %s) is newer than the importer (version %s) and may not be supported. Please consider updating.', 'wordpress-importer' ),
$this->version,
self::MAX_WXR_VERSION
) );
}
// Handled everything in this node, move on to the next
$reader->next();
break;
case 'wp:base_site_url':
$this->base_url = $reader->readString();
// Handled everything in this node, move on to the next
$reader->next();
break;
case 'item':
$node = $reader->expand();
$parsed = $this->parse_post_node( $node );
if ( is_wp_error( $parsed ) ) {
$this->log_error( $parsed );
// Skip the rest of this post
$reader->next();
break;
}
$this->process_post( $parsed['data'], $parsed['meta'], $parsed['comments'], $parsed['terms'] );
// Handled everything in this node, move on to the next
$reader->next();
break;
case 'wp:author':
$node = $reader->expand();
$parsed = $this->parse_author_node( $node );
if ( is_wp_error( $parsed ) ) {
$this->log_error( $parsed );
// Skip the rest of this post
$reader->next();
break;
}
$status = $this->process_author( $parsed['data'], $parsed['meta'] );
if ( is_wp_error( $status ) ) {
$this->log_error( $status );
}
// Handled everything in this node, move on to the next
$reader->next();
break;
case 'wp:category':
$node = $reader->expand();
$parsed = $this->parse_term_node( $node, 'category' );
if ( is_wp_error( $parsed ) ) {
$this->log_error( $parsed );
// Skip the rest of this post
$reader->next();
break;
}
$status = $this->process_term( $parsed['data'], $parsed['meta'] );
// Handled everything in this node, move on to the next
$reader->next();
break;
case 'wp:tag':
$node = $reader->expand();
$parsed = $this->parse_term_node( $node, 'tag' );
if ( is_wp_error( $parsed ) ) {
$this->log_error( $parsed );
// Skip the rest of this post
$reader->next();
break;
}
$status = $this->process_term( $parsed['data'], $parsed['meta'] );
// Handled everything in this node, move on to the next
$reader->next();
break;
case 'wp:term':
$node = $reader->expand();
$parsed = $this->parse_term_node( $node );
if ( is_wp_error( $parsed ) ) {
$this->log_error( $parsed );
// Skip the rest of this post
$reader->next();
break;
}
$status = $this->process_term( $parsed['data'], $parsed['meta'] );
// Handled everything in this node, move on to the next
$reader->next();
break;
default:
// Skip this node, probably handled by something already
break;
}
}
// Now that we've done the main processing, do any required
// post-processing and remapping.
$this->post_process();
if ( $this->options['aggressive_url_search'] ) {
$this->replace_attachment_urls_in_content();
}
$this->remap_featured_images();
$this->import_end();
}
/**
* Log an error instance to the logger.
*
* @param WP_Error $error Error instance to log.
*/
protected function log_error( WP_Error $error ) {
$this->logger->warning( $error->get_error_message() );
// Log the data as debug info too
$data = $error->get_error_data();
if ( ! empty( $data ) ) {
$this->logger->debug( var_export( $data, true ) );
}
}
/**
* Parses the WXR file and prepares us for the task of processing parsed data
*
* @param string $file Path to the WXR file for importing
*/
protected function import_start( $file ) {
if ( ! is_file( $file ) ) {
return new WP_Error( 'wxr_importer.file_missing', __( 'The file does not exist, please try again.', 'wordpress-importer' ) );
}
// Suspend bunches of stuff in WP core
wp_defer_term_counting( true );
wp_defer_comment_counting( true );
wp_suspend_cache_invalidation( true );
// Prefill exists calls if told to
if ( $this->options['prefill_existing_posts'] ) {
$this->prefill_existing_posts();
}
if ( $this->options['prefill_existing_comments'] ) {
$this->prefill_existing_comments();
}
if ( $this->options['prefill_existing_terms'] ) {
$this->prefill_existing_terms();
}
/**
* Begin the import.
*
* Fires before the import process has begun. If you need to suspend
* caching or heavy processing on hooks, do so here.
*/
do_action( 'import_start' );
}
/**
* Performs post-import cleanup of files and the cache
*/
protected function import_end() {
// Re-enable stuff in core
wp_suspend_cache_invalidation( false );
wp_cache_flush();
foreach ( get_taxonomies() as $tax ) {
delete_option( "{$tax}_children" );
_get_term_hierarchy( $tax );
}
wp_defer_term_counting( false );
wp_defer_comment_counting( false );
flush_rewrite_rules();
/**
* Complete the import.
*
* Fires after the import process has finished. If you need to update
* your cache or re-enable processing, do so here.
*/
do_action( 'import_end' );
}
/**
* Set the user mapping.
*
* @param array $mapping List of map arrays (containing `old_slug`, `old_id`, `new_id`)
*/
public function set_user_mapping( $mapping ) {
foreach ( $mapping as $map ) {
if ( empty( $map['old_slug'] ) || empty( $map['old_id'] ) || empty( $map['new_id'] ) ) {
$this->logger->warning( __( 'Invalid author mapping', 'wordpress-importer' ) );
$this->logger->debug( var_export( $map, true ) );
continue;
}
$old_slug = $map['old_slug'];
$old_id = $map['old_id'];
$new_id = $map['new_id'];
$this->mapping['user'][ $old_id ] = $new_id;
$this->mapping['user_slug'][ $old_slug ] = $new_id;
}
}
/**
* Set the user slug overrides.
*
* Allows overriding the slug in the import with a custom/renamed version.
*
* @param string[] $overrides Map of old slug to new slug.
*/
public function set_user_slug_overrides( $overrides ) {
foreach ( $overrides as $original => $renamed ) {
$this->user_slug_override[ $original ] = $renamed;
}
}
/**
* Parse a post node into post data.
*
* @param \DOMNode $node Parent node of post data (typically `item`).
* @return array|WP_Error Post data array on success, error otherwise.
*/
protected function parse_post_node( $node ) {
$data = array();
$meta = array();
$comments = array();
$terms = array();
foreach ( $node->childNodes as $child ) {
// We only care about child elements
if ( $child->nodeType !== XML_ELEMENT_NODE ) {
continue;
}
switch ( $child->tagName ) {
case 'wp:post_type':
$data['post_type'] = $child->textContent;
break;
case 'title':
$data['post_title'] = $child->textContent;
break;
case 'guid':
$data['guid'] = $child->textContent;
break;
case 'dc:creator':
$data['post_author'] = $child->textContent;
break;
case 'content:encoded':
$data['post_content'] = $child->textContent;
break;
case 'excerpt:encoded':
$data['post_excerpt'] = $child->textContent;
break;
case 'wp:post_id':
$data['post_id'] = $child->textContent;
break;
case 'wp:post_date':
$data['post_date'] = $child->textContent;
break;
case 'wp:post_date_gmt':
$data['post_date_gmt'] = $child->textContent;
break;
case 'wp:comment_status':
$data['comment_status'] = $child->textContent;
break;
case 'wp:ping_status':
$data['ping_status'] = $child->textContent;
break;
case 'wp:post_name':
$data['post_name'] = $child->textContent;
break;
case 'wp:status':
$data['post_status'] = $child->textContent;
if ( $data['post_status'] === 'auto-draft' ) {
// Bail now
return new WP_Error(
'wxr_importer.post.cannot_import_draft',
__( 'Cannot import auto-draft posts' ),
$data
);
}
break;
case 'wp:post_parent':
$data['post_parent'] = $child->textContent;
break;
case 'wp:menu_order':
$data['menu_order'] = $child->textContent;
break;
case 'wp:post_password':
$data['post_password'] = $child->textContent;
break;
case 'wp:is_sticky':
$data['is_sticky'] = $child->textContent;
break;
case 'wp:attachment_url':
$data['attachment_url'] = $child->textContent;
break;
case 'wp:postmeta':
$meta_item = $this->parse_meta_node( $child );
if ( ! empty( $meta_item ) ) {
$meta[] = $meta_item;
}
break;
case 'wp:comment':
$comment_item = $this->parse_comment_node( $child );
if ( ! empty( $comment_item ) ) {
$comments[] = $comment_item;
}
break;
case 'category':
$term_item = $this->parse_category_node( $child );
if ( ! empty( $term_item ) ) {
$terms[] = $term_item;
}
break;
}
}
return compact( 'data', 'meta', 'comments', 'terms' );
}
/**
* Create new posts based on import information
*
* Posts marked as having a parent which doesn't exist will become top level items.
* Doesn't create a new post if: the post type doesn't exist, the given post ID
* is already noted as imported or a post with the same title and date already exists.
* Note that new/updated terms, comments and meta are imported for the last of the above.
*
* @param array $data Post data.
* @param array $meta Meta data.
* @param array $comments Comments on the post.
* @param array $terms Terms on the post.
*/
protected function process_post( $data, $meta, $comments, $terms ) {
/**
* Pre-process post data.
*
* @param array $data Post data. (Return empty to skip.)
* @param array $meta Meta data.
* @param array $comments Comments on the post.
* @param array $terms Terms on the post.
*/
$data = apply_filters( 'wxr_importer.pre_process.post', $data, $meta, $comments, $terms );
if ( empty( $data ) ) {
return false;
}
$original_id = isset( $data['post_id'] ) ? (int) $data['post_id'] : 0;
$parent_id = isset( $data['post_parent'] ) ? (int) $data['post_parent'] : 0;
// Have we already processed this?
if ( isset( $this->mapping['post'][ $original_id ] ) ) {
return false;
}
$post_type_object = get_post_type_object( $data['post_type'] );
// Is this type even valid?
if ( ! $post_type_object ) {
$this->logger->warning( sprintf(
__( 'Failed to import "%s": Invalid post type %s', 'wordpress-importer' ),
$data['post_title'],
$data['post_type']
) );
return false;
}
$post_exists = $this->post_exists( $data );
if ( $post_exists ) {
$this->logger->info( sprintf(
__( '%s "%s" already exists.', 'wordpress-importer' ),
$post_type_object->labels->singular_name,
$data['post_title']
) );
// Even though this post already exists, new comments might need importing
$this->process_comments( $comments, $original_id, $data, $post_exists );
return false;
}
// Map the parent post, or mark it as one we need to fix
$requires_remapping = false;
if ( $parent_id ) {
if ( isset( $this->mapping['post'][ $parent_id ] ) ) {
$data['post_parent'] = $this->mapping['post'][ $parent_id ];
} else {
$meta[] = array( 'key' => '_wxr_import_parent', 'value' => $parent_id );
$requires_remapping = true;
$data['post_parent'] = 0;
}
}
// Map the author, or mark it as one we need to fix
$author = sanitize_user( $data['post_author'], true );
if ( empty( $author ) ) {
// Missing or invalid author, use default if available.
$data['post_author'] = $this->options['default_author'];
} elseif ( isset( $this->mapping['user_slug'][ $author ] ) ) {
$data['post_author'] = $this->mapping['user_slug'][ $author ];
} else {
$meta[] = array( 'key' => '_wxr_import_user_slug', 'value' => $author );
$requires_remapping = true;
$data['post_author'] = (int) get_current_user_id();
}
// Does the post look like it contains attachment images?
if ( preg_match( self::REGEX_HAS_ATTACHMENT_REFS, $data['post_content'] ) ) {
$meta[] = array( 'key' => '_wxr_import_has_attachment_refs', 'value' => true );
$requires_remapping = true;
}
// Whitelist to just the keys we allow
$postdata = array(
'import_id' => $data['post_id'],
);
$allowed = array(
'post_author' => true,
'post_date' => true,
'post_date_gmt' => true,
'post_content' => true,
'post_excerpt' => true,
'post_title' => true,
'post_status' => true,
'post_name' => true,
'comment_status' => true,
'ping_status' => true,
'guid' => true,
'post_parent' => true,
'menu_order' => true,
'post_type' => true,
'post_password' => true,
);
foreach ( $data as $key => $value ) {
if ( ! isset( $allowed[ $key ] ) ) {
continue;
}
$postdata[ $key ] = $data[ $key ];
}
$postdata = apply_filters( 'wp_import_post_data_processed', wp_slash( $postdata ), $data );
if ( 'attachment' === $postdata['post_type'] ) {
if ( ! $this->options['fetch_attachments'] ) {
$this->logger->notice( sprintf(
__( 'Skipping attachment "%s", fetching attachments disabled' ),
$data['post_title']
) );
return false;
}
$remote_url = ! empty( $data['attachment_url'] ) ? $data['attachment_url'] : $data['guid'];
$post_id = $this->process_attachment( $postdata, $meta, $remote_url );
} else {
$post_id = wp_insert_post( $postdata, true );
do_action( 'wp_import_insert_post', $post_id, $original_id, $postdata, $data );
}
if ( is_wp_error( $post_id ) ) {
$this->logger->error( sprintf(
__( 'Failed to import "%s" (%s)', 'wordpress-importer' ),
$data['post_title'],
$post_type_object->labels->singular_name
) );
$this->logger->debug( $post_id->get_error_message() );
/**
* Post processing failed.
*
* @param WP_Error $post_id Error object.
* @param array $data Raw data imported for the post.
* @param array $meta Raw meta data, already processed by {@see process_post_meta}.
* @param array $comments Raw comment data, already processed by {@see process_comments}.
* @param array $terms Raw term data, already processed.
*/
do_action( 'wxr_importer.process_failed.post', $post_id, $data, $meta, $comments, $terms );
return false;
}
// Ensure stickiness is handled correctly too
if ( $data['is_sticky'] === '1' ) {
stick_post( $post_id );
}
// map pre-import ID to local ID
$this->mapping['post'][ $original_id ] = (int) $post_id;
if ( $requires_remapping ) {
$this->requires_remapping['post'][ $post_id ] = true;
}
$this->mark_post_exists( $data, $post_id );
$this->logger->info( sprintf(
__( 'Imported "%s" (%s)', 'wordpress-importer' ),
$data['post_title'],
$post_type_object->labels->singular_name
) );
$this->logger->debug( sprintf(
__( 'Post %d remapped to %d', 'wordpress-importer' ),
$original_id,
$post_id
) );
// Handle the terms too
$terms = apply_filters( 'wp_import_post_terms', $terms, $post_id, $data );
if ( ! empty( $terms ) ) {
$term_ids = array();
foreach ( $terms as $term ) {
$taxonomy = $term['taxonomy'];
$key = sha1( $taxonomy . ':' . $term['slug'] );
if ( isset( $this->mapping['term'][ $key ] ) ) {
$term_ids[ $taxonomy ][] = (int) $this->mapping['term'][ $key ];
} else {
/**
* Fix for the post format "categories".
* The issue in this importer is, that these post formats are misused as categories in WP export
* (as the export data <category> item in the post export item), but they are not actually
* exported as wp:category items in the XML file, so they need to be inserted on the fly (here).
*
* Maybe something better can be done in the future?
*
* Original issue reported here: https://wordpress.org/support/topic/post-format-videoquotegallery-became-format-standard/#post-8447683
*
*/
if ( 'post_format' === $taxonomy ) {
$term_exists = term_exists( $term['slug'], $taxonomy );
$term_id = is_array( $term_exists ) ? $term_exists['term_id'] : $term_exists;
if ( empty( $term_id ) ) {
$t = wp_insert_term( $term['name'], $taxonomy, array( 'slug' => $term['slug'] ) );
if ( ! is_wp_error( $t ) ) {
$term_id = $t['term_id'];
$this->mapping['term'][ $key ] = $term_id;
} else {
$this->logger->warning( sprintf(
esc_html__( 'Failed to import term: %s - %s', 'wordpress-importer' ),
esc_html( $taxonomy ),
esc_html( $term['name'] )
) );
continue;
}
}
if ( ! empty( $term_id ) ) {
$term_ids[ $taxonomy ][] = intval( $term_id );
}
} // End of fix.
else {
$meta[] = array( 'key' => '_wxr_import_term', 'value' => $term );
$requires_remapping = true;
}
}
}
foreach ( $term_ids as $tax => $ids ) {
$tt_ids = wp_set_post_terms( $post_id, $ids, $tax );
do_action( 'wp_import_set_post_terms', $tt_ids, $ids, $tax, $post_id, $data );
}
}
$this->process_comments( $comments, $post_id, $data );
$this->process_post_meta( $meta, $post_id, $data );
if ( 'nav_menu_item' === $data['post_type'] ) {
$this->process_menu_item_meta( $post_id, $data, $meta );
}
/**
* Post processing completed.
*
* @param int $post_id New post ID.
* @param array $data Raw data imported for the post.
* @param array $meta Raw meta data, already processed by {@see process_post_meta}.
* @param array $comments Raw comment data, already processed by {@see process_comments}.
* @param array $terms Raw term data, already processed.
*/
do_action( 'wxr_importer.processed.post', $post_id, $data, $meta, $comments, $terms );
}
/**
* Attempt to create a new menu item from import data
*
* Fails for draft, orphaned menu items and those without an associated nav_menu
* or an invalid nav_menu term. If the post type or term object which the menu item
* represents doesn't exist then the menu item will not be imported (waits until the
* end of the import to retry again before discarding).
*
* @param int $post_id Menu item post ID.
* @param array $data Menu item details from WXR file.
* @param array $meta Menu item meta details.
*/
protected function process_menu_item_meta( $post_id, $data, $meta ) {
$item_type = get_post_meta( $post_id, '_menu_item_type', true );
$original_object_id = get_post_meta( $post_id, '_menu_item_object_id', true );
$object_id = null;
$this->logger->debug( sprintf( 'Processing menu item %s', $item_type ) );
$requires_remapping = false;
switch ( $item_type ) {
case 'taxonomy':
if ( isset( $this->mapping['term_id'][ $original_object_id ] ) ) {
$object_id = $this->mapping['term_id'][ $original_object_id ];
} else {
add_post_meta( $post_id, '_wxr_import_menu_item', wp_slash( $original_object_id ) );
$requires_remapping = true;
}
break;
case 'post_type':
if ( isset( $this->mapping['post'][ $original_object_id ] ) ) {
$object_id = $this->mapping['post'][ $original_object_id ];
} else {
add_post_meta( $post_id, '_wxr_import_menu_item', wp_slash( $original_object_id ) );
$requires_remapping = true;
}
break;
case 'custom':
// Custom refers to itself, wonderfully easy.
$object_id = $post_id;
break;
default:
// associated object is missing or not imported yet, we'll retry later
$this->missing_menu_items[] = $data;
$this->logger->debug( 'Unknown menu item type' );
break;
}
if ( $requires_remapping ) {
$this->requires_remapping['post'][ $post_id ] = true;
}
if ( empty( $object_id ) ) {
// Nothing needed here.
return;
}
$this->logger->debug( sprintf( 'Menu item %d mapped to %d', $original_object_id, $object_id ) );
update_post_meta( $post_id, '_menu_item_object_id', wp_slash( $object_id ) );
}
/**
* If fetching attachments is enabled then attempt to create a new attachment
*
* @param array $post Attachment post details from WXR.
* @param array $meta Attachment post meta details.
* @param string $remote_url URL to fetch attachment from.
*
* @return int|WP_Error Post ID on success, WP_Error otherwise
*/
protected function process_attachment( $post, $meta, $remote_url ) {
// try to use _wp_attached file for upload folder placement to ensure the same location as the export site
// e.g. location is 2003/05/image.jpg but the attachment post_date is 2010/09, see media_handle_upload()
$post['upload_date'] = $post['post_date'];
foreach ( $meta as $meta_item ) {
if ( $meta_item['key'] !== '_wp_attached_file' ) {
continue;
}
if ( preg_match( '%^[0-9]{4}/[0-9]{2}%', $meta_item['value'], $matches ) ) {
$post['upload_date'] = $matches[0];
}
break;
}
// if the URL is absolute, but does not contain address, then upload it assuming base_site_url
if ( preg_match( '|^/[\w\W]+$|', $remote_url ) ) {
$remote_url = rtrim( $this->base_url, '/' ) . $remote_url;
}
$upload = $this->fetch_remote_file( $remote_url, $post );
if ( is_wp_error( $upload ) ) {
return $upload;
}
$info = wp_check_filetype( $upload['file'] );
if ( ! $info ) {
return new WP_Error( 'attachment_processing_error', __( 'Invalid file type', 'wordpress-importer' ) );
}
$post['post_mime_type'] = $info['type'];
// WP really likes using the GUID for display. Allow updating it.
// See https://core.trac.wordpress.org/ticket/33386
if ( $this->options['update_attachment_guids'] ) {
$post['guid'] = $upload['url'];
}
// as per wp-admin/includes/upload.php
$post_id = wp_insert_attachment( $post, $upload['file'] );
if ( is_wp_error( $post_id ) ) {
return $post_id;
}
$attachment_metadata = wp_generate_attachment_metadata( $post_id, $upload['file'] );
wp_update_attachment_metadata( $post_id, $attachment_metadata );
// Map this image URL later if we need to
$this->url_remap[ $remote_url ] = $upload['url'];
// If we have a HTTPS URL, ensure the HTTP URL gets replaced too
if ( substr( $remote_url, 0, 8 ) === 'https://' ) {
$insecure_url = 'http' . substr( $remote_url, 5 );
$this->url_remap[ $insecure_url ] = $upload['url'];
}
if ( $this->options['aggressive_url_search'] ) {
// remap resized image URLs, works by stripping the extension and remapping the URL stub.
/*if ( preg_match( '!^image/!', $info['type'] ) ) {
$parts = pathinfo( $remote_url );
$name = basename( $parts['basename'], ".{$parts['extension']}" ); // PATHINFO_FILENAME in PHP 5.2
$parts_new = pathinfo( $upload['url'] );
$name_new = basename( $parts_new['basename'], ".{$parts_new['extension']}" );
$this->url_remap[$parts['dirname'] . '/' . $name] = $parts_new['dirname'] . '/' . $name_new;
}*/
}
return $post_id;
}
/**
* Parse a meta node into meta data.
*
* @param \DOMNode $node Parent node of meta data (typically `wp:postmeta` or `wp:commentmeta`).
* @return array|null Meta data array on success, or null on error.
*/
protected function parse_meta_node( $node ) {
foreach ( $node->childNodes as $child ) {
// We only care about child elements
if ( $child->nodeType !== XML_ELEMENT_NODE ) {
continue;
}
switch ( $child->tagName ) {
case 'wp:meta_key':
$key = $child->textContent;
break;
case 'wp:meta_value':
$value = $child->textContent;
break;
}
}
if ( empty( $key ) || ! isset( $value ) ) {
return null;
}
return compact( 'key', 'value' );
}
/**
* Process and import post meta items.
*
* @param array $meta List of meta data arrays
* @param int $post_id Post to associate with
* @param array $post Post data
* @return int|WP_Error Number of meta items imported on success, error otherwise.
*/
protected function process_post_meta( $meta, $post_id, $post ) {
if ( empty( $meta ) ) {
return true;
}
foreach ( $meta as $meta_item ) {
/**
* Pre-process post meta data.
*
* @param array $meta_item Meta data. (Return empty to skip.)
* @param int $post_id Post the meta is attached to.
*/
$meta_item = apply_filters( 'wxr_importer.pre_process.post_meta', $meta_item, $post_id );
if ( empty( $meta_item ) ) {
return false;
}
$key = apply_filters( 'import_post_meta_key', $meta_item['key'], $post_id, $post );
$value = false;
if ( '_edit_last' === $key ) {
$value = intval( $meta_item['value'] );
if ( ! isset( $this->mapping['user'][ $value ] ) ) {
// Skip!
continue;
}
$value = $this->mapping['user'][ $value ];
}
if ( $key ) {
// export gets meta straight from the DB so could have a serialized string
if ( ! $value ) {
$value = maybe_unserialize( $meta_item['value'] );
}
add_post_meta( $post_id, wp_slash( $key ), wp_slash_strings_only( $value ) );
do_action( 'import_post_meta', $post_id, $key, $value );
// if the post has a featured image, take note of this in case of remap
if ( '_thumbnail_id' === $key ) {
$this->featured_images[ $post_id ] = (int) $value;
}
}
}
return true;
}
/**
* Parse a comment node into comment data.
*
* @param \DOMNode $node Parent node of comment data (typically `wp:comment`).
* @return array Comment data array.
*/
protected function parse_comment_node( $node ) {
$data = array(
'commentmeta' => array(),
);
foreach ( $node->childNodes as $child ) {
// We only care about child elements
if ( $child->nodeType !== XML_ELEMENT_NODE ) {
continue;
}
switch ( $child->tagName ) {
case 'wp:comment_id':
$data['comment_id'] = $child->textContent;
break;
case 'wp:comment_author':
$data['comment_author'] = $child->textContent;
break;
case 'wp:comment_author_email':
$data['comment_author_email'] = $child->textContent;
break;
case 'wp:comment_author_IP':
$data['comment_author_IP'] = $child->textContent;
break;
case 'wp:comment_author_url':
$data['comment_author_url'] = $child->textContent;
break;
case 'wp:comment_user_id':
$data['comment_user_id'] = $child->textContent;
break;
case 'wp:comment_date':
$data['comment_date'] = $child->textContent;
break;
case 'wp:comment_date_gmt':
$data['comment_date_gmt'] = $child->textContent;
break;
case 'wp:comment_content':
$data['comment_content'] = $child->textContent;
break;
case 'wp:comment_approved':
$data['comment_approved'] = $child->textContent;
break;
case 'wp:comment_type':
$data['comment_type'] = $child->textContent;
break;
case 'wp:comment_parent':
$data['comment_parent'] = $child->textContent;
break;
case 'wp:commentmeta':
$meta_item = $this->parse_meta_node( $child );
if ( ! empty( $meta_item ) ) {
$data['commentmeta'][] = $meta_item;
}
break;
}
}
return $data;
}
/**
* Process and import comment data.
*
* @param array $comments List of comment data arrays.
* @param int $post_id Post to associate with.
* @param array $post Post data.
* @param boolean $post_exists Boolean if the post already exists.
*
* @return int|WP_Error Number of comments imported on success, error otherwise.
*/
protected function process_comments( $comments, $post_id, $post, $post_exists = false ) {
$comments = apply_filters( 'wp_import_post_comments', $comments, $post_id, $post );
if ( empty( $comments ) ) {
return 0;
}
$num_comments = 0;
// Sort by ID to avoid excessive remapping later
usort( $comments, array( $this, 'sort_comments_by_id' ) );
foreach ( $comments as $key => $comment ) {
/**
* Pre-process comment data
*
* @param array $comment Comment data. (Return empty to skip.)
* @param int $post_id Post the comment is attached to.
*/
$comment = apply_filters( 'wxr_importer.pre_process.comment', $comment, $post_id );
if ( empty( $comment ) ) {
return false;
}
$original_id = isset( $comment['comment_id'] ) ? (int) $comment['comment_id'] : 0;
$parent_id = isset( $comment['comment_parent'] ) ? (int) $comment['comment_parent'] : 0;
$author_id = isset( $comment['comment_user_id'] ) ? (int) $comment['comment_user_id'] : 0;
// if this is a new post we can skip the comment_exists() check
// TODO: Check comment_exists for performance
if ( $post_exists ) {
$existing = $this->comment_exists( $comment );
if ( $existing ) {
$this->mapping['comment'][ $original_id ] = $existing;
continue;
}
}
// Remove meta from the main array
$meta = isset( $comment['commentmeta'] ) ? $comment['commentmeta'] : array();
unset( $comment['commentmeta'] );
// Map the parent comment, or mark it as one we need to fix
$requires_remapping = false;
if ( $parent_id ) {
if ( isset( $this->mapping['comment'][ $parent_id ] ) ) {
$comment['comment_parent'] = $this->mapping['comment'][ $parent_id ];
} else {
// Prepare for remapping later
$meta[] = array( 'key' => '_wxr_import_parent', 'value' => $parent_id );
$requires_remapping = true;
// Wipe the parent for now
$comment['comment_parent'] = 0;
}
}
// Map the author, or mark it as one we need to fix
if ( $author_id ) {
if ( isset( $this->mapping['user'][ $author_id ] ) ) {
$comment['user_id'] = $this->mapping['user'][ $author_id ];
} else {
// Prepare for remapping later
$meta[] = array( 'key' => '_wxr_import_user', 'value' => $author_id );
$requires_remapping = true;
// Wipe the user for now
$comment['user_id'] = 0;
}
}
// Run standard core filters
$comment['comment_post_ID'] = $post_id;
$comment = wp_filter_comment( $comment );
// wp_insert_comment expects slashed data
$comment_id = wp_insert_comment( wp_slash( $comment ) );
$this->mapping['comment'][ $original_id ] = $comment_id;
if ( $requires_remapping ) {
$this->requires_remapping['comment'][ $comment_id ] = true;
}
$this->mark_comment_exists( $comment, $comment_id );
/**
* Comment has been imported.
*
* @param int $comment_id New comment ID
* @param array $comment Comment inserted (`comment_id` item refers to the original ID)
* @param int $post_id Post parent of the comment
* @param array $post Post data
*/
do_action( 'wp_import_insert_comment', $comment_id, $comment, $post_id, $post );
// Process the meta items
foreach ( $meta as $meta_item ) {
$value = maybe_unserialize( $meta_item['value'] );
add_comment_meta( $comment_id, wp_slash( $meta_item['key'] ), wp_slash( $value ) );
}
/**
* Post processing completed.
*
* @param int $post_id New post ID.
* @param array $comment Raw data imported for the comment.
* @param array $meta Raw meta data, already processed by {@see process_post_meta}.
* @param array $post_id Parent post ID.
*/
do_action( 'wxr_importer.processed.comment', $comment_id, $comment, $meta, $post_id );
$num_comments++;
}
return $num_comments;
}
/**
* Parse the category node.
*
* @param \DOMNode $node The category node.
*
* @return array|null
*/
protected function parse_category_node( $node ) {
$data = array(
// Default taxonomy to "category", since this is a `<category>` tag
'taxonomy' => 'category',
);
$meta = array();
if ( $node->hasAttribute( 'domain' ) ) {
$data['taxonomy'] = $node->getAttribute( 'domain' );
}
if ( $node->hasAttribute( 'nicename' ) ) {
$data['slug'] = $node->getAttribute( 'nicename' );
}
$data['name'] = $node->textContent;
if ( empty( $data['slug'] ) ) {
return null;
}
// Just for extra compatibility
if ( $data['taxonomy'] === 'tag' ) {
$data['taxonomy'] = 'post_tag';
}
return $data;
}
/**
* Callback for `usort` to sort comments by ID
*
* @param array $a Comment data for the first comment
* @param array $b Comment data for the second comment
*
* @return int
*/
public static function sort_comments_by_id( $a, $b ) {
if ( empty( $a['comment_id'] ) ) {
return 1;
}
if ( empty( $b['comment_id'] ) ) {
return -1;
}
return $a['comment_id'] - $b['comment_id'];
}
protected function parse_author_node( $node ) {
$data = array();
$meta = array();
foreach ( $node->childNodes as $child ) {
// We only care about child elements
if ( $child->nodeType !== XML_ELEMENT_NODE ) {
continue;
}
switch ( $child->tagName ) {
case 'wp:author_login':
$data['user_login'] = $child->textContent;
break;
case 'wp:author_id':
$data['ID'] = $child->textContent;
break;
case 'wp:author_email':
$data['user_email'] = $child->textContent;
break;
case 'wp:author_display_name':
$data['display_name'] = $child->textContent;
break;
case 'wp:author_first_name':
$data['first_name'] = $child->textContent;
break;
case 'wp:author_last_name':
$data['last_name'] = $child->textContent;
break;
}
}
return compact( 'data', 'meta' );
}
/**
* Process author.
*
* @param array $data The author data from WXR file.
* @param array $meta The author meta data from WXR file.
*/
protected function process_author( $data, $meta ) {
/**
* Pre-process user data.
*
* @param array $data User data. (Return empty to skip.)
* @param array $meta Meta data.
*/
$data = apply_filters( 'wxr_importer.pre_process.user', $data, $meta );
if ( empty( $data ) ) {
return false;
}
// Have we already handled this user?
$original_id = isset( $data['ID'] ) ? $data['ID'] : 0;
$original_slug = $data['user_login'];
if ( isset( $this->mapping['user'][ $original_id ] ) ) {
$existing = $this->mapping['user'][ $original_id ];
// Note the slug mapping if we need to too
if ( ! isset( $this->mapping['user_slug'][ $original_slug ] ) ) {
$this->mapping['user_slug'][ $original_slug ] = $existing;
}
return false;
}
if ( isset( $this->mapping['user_slug'][ $original_slug ] ) ) {
$existing = $this->mapping['user_slug'][ $original_slug ];
// Ensure we note the mapping too
$this->mapping['user'][ $original_id ] = $existing;
return false;
}
// Allow overriding the user's slug
$login = $original_slug;
if ( isset( $this->user_slug_override[ $login ] ) ) {
$login = $this->user_slug_override[ $login ];
}
$userdata = array(
'user_login' => sanitize_user( $login, true ),
'user_pass' => wp_generate_password(),
);
$allowed = array(
'user_email' => true,
'display_name' => true,
'first_name' => true,
'last_name' => true,
);
foreach ( $data as $key => $value ) {
if ( ! isset( $allowed[ $key ] ) ) {
continue;
}
$userdata[ $key ] = $data[ $key ];
}
$user_id = wp_insert_user( wp_slash( $userdata ) );
if ( is_wp_error( $user_id ) ) {
$this->logger->error( sprintf(
__( 'Failed to import user "%s"', 'wordpress-importer' ),
$userdata['user_login']
) );
$this->logger->debug( $user_id->get_error_message() );
/**
* User processing failed.
*
* @param WP_Error $user_id Error object.
* @param array $userdata Raw data imported for the user.
*/
do_action( 'wxr_importer.process_failed.user', $user_id, $userdata );
return false;
}
if ( $original_id ) {
$this->mapping['user'][ $original_id ] = $user_id;
}
$this->mapping['user_slug'][ $original_slug ] = $user_id;
$this->logger->info( sprintf(
__( 'Imported user "%s"', 'wordpress-importer' ),
$userdata['user_login']
) );
$this->logger->debug( sprintf(
__( 'User %d remapped to %d', 'wordpress-importer' ),
$original_id,
$user_id
) );
// TODO: Implement meta handling once WXR includes it
/**
* User processing completed.
*
* @param int $user_id New user ID.
* @param array $userdata Raw data imported for the user.
*/
do_action( 'wxr_importer.processed.user', $user_id, $userdata );
}
/**
* Parse term node.
*
* @param \DOMNode $node The term node from WXR file.
* @param string $type The type of the term node.
*
* @return array|null
*/
protected function parse_term_node( $node, $type = 'term' ) {
$data = array();
$meta = array();
$tag_name = array(
'id' => 'wp:term_id',
'taxonomy' => 'wp:term_taxonomy',
'slug' => 'wp:term_slug',
'parent' => 'wp:term_parent',
'name' => 'wp:term_name',
'description' => 'wp:term_description',
'termmeta' => 'wp:termmeta',
);
$taxonomy = null;
// Special casing!
switch ( $type ) {
case 'category':
$tag_name['slug'] = 'wp:category_nicename';
$tag_name['parent'] = 'wp:category_parent';
$tag_name['name'] = 'wp:cat_name';
$tag_name['description'] = 'wp:category_description';
$tag_name['taxonomy'] = null;
$data['taxonomy'] = 'category';
break;
case 'tag':
$tag_name['slug'] = 'wp:tag_slug';
$tag_name['parent'] = null;
$tag_name['name'] = 'wp:tag_name';
$tag_name['description'] = 'wp:tag_description';
$tag_name['taxonomy'] = null;
$data['taxonomy'] = 'post_tag';
break;
}
foreach ( $node->childNodes as $child ) {
// We only care about child elements
if ( $child->nodeType !== XML_ELEMENT_NODE ) {
continue;
}
$key = array_search( $child->tagName, $tag_name );
if ( $key ) {
$data[ $key ] = $child->textContent;
} else if ( $child->tagName == 'wp:termmeta' ) {
$meta_item = $this->parse_meta_node( $child );
if ( ! empty( $meta_item ) ) {
$meta[] = $meta_item;
}
}
}
if ( empty( $data['taxonomy'] ) ) {
return null;
}
// Compatibility with WXR 1.0
if ( $data['taxonomy'] === 'tag' ) {
$data['taxonomy'] = 'post_tag';
}
return compact( 'data', 'meta' );
}
/**
* Process term.
*
* @param array $data The term data from WXR file.
* @param array $meta The term meta data from WXR file.
*/
protected function process_term( $data, $meta ) {
/**
* Pre-process term data.
*
* @param array $data Term data. (Return empty to skip.)
* @param array $meta Meta data.
*/
$data = apply_filters( 'wxr_importer.pre_process.term', $data, $meta );
if ( empty( $data ) ) {
return false;
}
$original_id = isset( $data['id'] ) ? (int) $data['id'] : 0;
/* FIX for OCDI!
* As of WP 4.5, export.php returns the SLUG for the term's parent,
* rather than an integer ID (this differs from a post_parent)
* wp_insert_term and wp_update_term use the key: 'parent' and an integer value 'id'
*/
$term_slug = isset( $data['slug'] ) ? $data['slug'] : '';
$parent_slug = isset( $data['parent'] ) ? $data['parent'] : '';
$mapping_key = sha1( $data['taxonomy'] . ':' . $data['slug'] );
$existing = $this->term_exists( $data );
if ( $existing ) {
$this->mapping['term'][ $mapping_key ] = $existing;
$this->mapping['term_id'][ $original_id ] = $existing;
$this->mapping['term_slug'][ $term_slug ] = $existing;
return false;
}
// WP really likes to repeat itself in export files
if ( isset( $this->mapping['term'][ $mapping_key ] ) ) {
return false;
}
$termdata = array();
$allowed = array(
'slug' => true,
'description' => true,
'parent' => true, // The parent_id may have already been set, so pass this back to the newly inserted term.
);
// Map the parent comment, or mark it as one we need to fix
$requires_remapping = false;
if ( $parent_slug ) {
if ( isset( $this->mapping['term_slug'][ $parent_slug ] ) ) {
$data['parent'] = $this->mapping['term_slug'][ $parent_slug ];
} else {
// Prepare for remapping later
$meta[] = array( 'key' => '_wxr_import_parent', 'value' => $parent_slug );
$requires_remapping = true;
// Wipe the parent id for now
$data['parent'] = 0;
}
}
foreach ( $data as $key => $value ) {
if ( ! isset( $allowed[ $key ] ) ) {
continue;
}
$termdata[ $key ] = $data[ $key ];
}
$result = wp_insert_term( $data['name'], $data['taxonomy'], $termdata );
if ( is_wp_error( $result ) ) {
$this->logger->warning( sprintf(
__( 'Failed to import %s %s', 'wordpress-importer' ),
$data['taxonomy'],
$data['name']
) );
$this->logger->debug( $result->get_error_message() );
do_action( 'wp_import_insert_term_failed', $result, $data );
/**
* Term processing failed.
*
* @param WP_Error $result Error object.
* @param array $data Raw data imported for the term.
* @param array $meta Meta data supplied for the term.
*/
do_action( 'wxr_importer.process_failed.term', $result, $data, $meta );
return false;
}
$term_id = $result['term_id'];
// Now prepare to map this new term.
$this->mapping['term'][ $mapping_key ] = $term_id;
$this->mapping['term_id'][ $original_id ] = $term_id;
$this->mapping['term_slug'][ $term_slug ] = $term_id;
/*
* Fix for OCDI!
* The parent will be updated later in post_process_terms
* we will need both the term_id AND the term_taxonomy to retrieve existing
* term attributes. Those attributes will be returned with the corrected parent,
* using wp_update_term.
* Pass both the term_id along with the term_taxonomy as key=>value
* in the requires_remapping['term'] array.
*/
if ( $requires_remapping ) {
$this->requires_remapping['term'][ $term_id ] = $data['taxonomy'];
}
$this->logger->info( sprintf(
__( 'Imported "%s" (%s)', 'wordpress-importer' ),
$data['name'],
$data['taxonomy']
) );
$this->logger->debug( sprintf(
__( 'Term %d remapped to %d', 'wordpress-importer' ),
$original_id,
$term_id
) );
// Actuall process of the term meta data.
$this->process_term_meta( $meta, $term_id, $data );
do_action( 'wp_import_insert_term', $term_id, $data );
/**
* Term processing completed.
*
* @param int $term_id New term ID.
* @param array $data Raw data imported for the term.
*/
do_action( 'wxr_importer.processed.term', $term_id, $data );
}
/**
* Process and import term meta items.
*
* @param array $meta List of meta data arrays.
* @param int $term_id Term ID to associate with.
* @param array $term Term data.
*
* @return int|bool Number of meta items imported on success, false otherwise.
*/
protected function process_term_meta( $meta, $term_id, $term ) {
if ( empty( $meta ) ) {
return true;
}
foreach ( $meta as $meta_item ) {
/**
* Pre-process term meta data.
*
* @param array $meta_item Meta data. (Return empty to skip.)
* @param int $term_id Term the meta is attached to.
*/
$meta_item = apply_filters( 'wxr_importer.pre_process.term_meta', $meta_item, $term_id );
if ( empty( $meta_item ) ) {
continue;
}
$key = apply_filters( 'import_term_meta_key', $meta_item['key'], $term_id, $term );
$value = false;
if ( $key ) {
// Export gets meta straight from the DB so could have a serialized string.
if ( ! $value ) {
$value = maybe_unserialize( $meta_item['value'] );
}
$result = add_term_meta( $term_id, $key, $value );
if ( is_wp_error( $result ) ) {
$this->logger->warning( sprintf(
__( 'Failed to add metakey: %s, metavalue: %s to term_id: %d', 'wordpress-importer' ),
$key,
$value,
$term_id
) );
do_action( 'wxr_importer.process_failed.termmeta', $result, $meta_item, $term_id, $term );
}
else {
$this->logger->debug( sprintf(
__( 'Meta for term_id %d : %s => %s ; successfully added!', 'wordpress-importer' ),
$term_id,
$key,
$value
) );
}
do_action( 'import_term_meta', $term_id, $key, $value );
}
}
return true;
}
/**
* Attempt to download a remote file attachment.
*
* @param string $url URL of item to fetch.
* @param array $post Attachment details.
*
* @return array|WP_Error Local file location details on success, WP_Error otherwise
*/
protected function fetch_remote_file( $url, $post ) {
// extract the file name and extension from the url
$file_name = basename( $url );
// get placeholder file in the upload dir with a unique, sanitized filename
$upload = wp_upload_bits( $file_name, 0, '', $post['upload_date'] );
if ( $upload['error'] ) {
return new WP_Error( 'upload_dir_error', $upload['error'] );
}
// fetch the remote url and write it to the placeholder file
$response = wp_remote_get( $url, array(
'stream' => true,
'filename' => $upload['file'],
) );
// request failed
if ( is_wp_error( $response ) ) {
unlink( $upload['file'] );
return $response;
}
$code = (int) wp_remote_retrieve_response_code( $response );
// make sure the fetch was successful
if ( $code !== 200 ) {
unlink( $upload['file'] );
return new WP_Error(
'import_file_error',
sprintf(
__( 'Remote server returned %1$d %2$s for %3$s', 'wordpress-importer' ),
$code,
get_status_header_desc( $code ),
$url
)
);
}
$filesize = filesize( $upload['file'] );
$headers = wp_remote_retrieve_headers( $response );
// OCDI fix!
// Smaller images with server compression do not pass this rule.
// More info here: https://github.com/proteusthemes/WordPress-Importer/pull/2
//
// if ( isset( $headers['content-length'] ) && $filesize !== (int) $headers['content-length'] ) {
// unlink( $upload['file'] );
// return new WP_Error( 'import_file_error', __( 'Remote file is incorrect size', 'wordpress-importer' ) );
// }
if ( 0 === $filesize ) {
unlink( $upload['file'] );
return new WP_Error( 'import_file_error', __( 'Zero size file downloaded', 'wordpress-importer' ) );
}
$max_size = (int) $this->max_attachment_size();
if ( ! empty( $max_size ) && $filesize > $max_size ) {
unlink( $upload['file'] );
$message = sprintf( __( 'Remote file is too large, limit is %s', 'wordpress-importer' ), size_format( $max_size ) );
return new WP_Error( 'import_file_error', $message );
}
return $upload;
}
protected function post_process() {
// Time to tackle any left-over bits
if ( ! empty( $this->requires_remapping['post'] ) ) {
$this->post_process_posts( $this->requires_remapping['post'] );
}
if ( ! empty( $this->requires_remapping['comment'] ) ) {
$this->post_process_comments( $this->requires_remapping['comment'] );
}
if ( ! empty( $this->requires_remapping['term'] ) ) {
$this->post_process_terms( $this->requires_remapping['term'] );
}
}
protected function post_process_posts( $todo ) {
foreach ( $todo as $post_id => $_ ) {
$this->logger->debug( sprintf(
// Note: title intentionally not used to skip extra processing
// for when debug logging is off
__( 'Running post-processing for post %d', 'wordpress-importer' ),
$post_id
) );
$data = array();
$parent_id = get_post_meta( $post_id, '_wxr_import_parent', true );
if ( ! empty( $parent_id ) ) {
// Have we imported the parent now?
if ( isset( $this->mapping['post'][ $parent_id ] ) ) {
$data['post_parent'] = $this->mapping['post'][ $parent_id ];
} else {
$this->logger->warning( sprintf(
__( 'Could not find the post parent for "%s" (post #%d)', 'wordpress-importer' ),
get_the_title( $post_id ),
$post_id
) );
$this->logger->debug( sprintf(
__( 'Post %d was imported with parent %d, but could not be found', 'wordpress-importer' ),
$post_id,
$parent_id
) );
}
}
$author_slug = get_post_meta( $post_id, '_wxr_import_user_slug', true );
if ( ! empty( $author_slug ) ) {
// Have we imported the user now?
if ( isset( $this->mapping['user_slug'][ $author_slug ] ) ) {
$data['post_author'] = $this->mapping['user_slug'][ $author_slug ];
} else {
$this->logger->warning( sprintf(
__( 'Could not find the author for "%s" (post #%d)', 'wordpress-importer' ),
get_the_title( $post_id ),
$post_id
) );
$this->logger->debug( sprintf(
__( 'Post %d was imported with author "%s", but could not be found', 'wordpress-importer' ),
$post_id,
$author_slug
) );
}
}
$has_attachments = get_post_meta( $post_id, '_wxr_import_has_attachment_refs', true );
if ( ! empty( $has_attachments ) ) {
$post = get_post( $post_id );
$content = $post->post_content;
// Replace all the URLs we've got
$new_content = str_replace( array_keys( $this->url_remap ), $this->url_remap, $content );
if ( $new_content !== $content ) {
$data['post_content'] = $new_content;
}
}
if ( get_post_type( $post_id ) === 'nav_menu_item' ) {
$this->post_process_menu_item( $post_id );
}
// Do we have updates to make?
if ( empty( $data ) ) {
$this->logger->debug( sprintf(
__( 'Post %d was marked for post-processing, but none was required.', 'wordpress-importer' ),
$post_id
) );
continue;
}
// Run the update
$data['ID'] = $post_id;
$result = wp_update_post( $data, true );
if ( is_wp_error( $result ) ) {
$this->logger->warning( sprintf(
__( 'Could not update "%s" (post #%d) with mapped data', 'wordpress-importer' ),
get_the_title( $post_id ),
$post_id
) );
$this->logger->debug( $result->get_error_message() );
continue;
}
// Clear out our temporary meta keys
delete_post_meta( $post_id, '_wxr_import_parent' );
delete_post_meta( $post_id, '_wxr_import_user_slug' );
delete_post_meta( $post_id, '_wxr_import_has_attachment_refs' );
}
}
protected function post_process_menu_item( $post_id ) {
$menu_object_id = get_post_meta( $post_id, '_wxr_import_menu_item', true );
if ( empty( $menu_object_id ) ) {
// No processing needed!
return;
}
$menu_item_type = get_post_meta( $post_id, '_menu_item_type', true );
switch ( $menu_item_type ) {
case 'taxonomy':
if ( isset( $this->mapping['term_id'][ $menu_object_id ] ) ) {
$menu_object = $this->mapping['term_id'][ $menu_object_id ];
}
break;
case 'post_type':
if ( isset( $this->mapping['post'][ $menu_object_id ] ) ) {
$menu_object = $this->mapping['post'][ $menu_object_id ];
}
break;
default:
// Cannot handle this.
return;
}
if ( ! empty( $menu_object ) ) {
update_post_meta( $post_id, '_menu_item_object_id', wp_slash( $menu_object ) );
} else {
$this->logger->warning( sprintf(
__( 'Could not find the menu object for "%s" (post #%d)', 'wordpress-importer' ),
get_the_title( $post_id ),
$post_id
) );
$this->logger->debug( sprintf(
__( 'Post %d was imported with object "%d" of type "%s", but could not be found', 'wordpress-importer' ),
$post_id,
$menu_object_id,
$menu_item_type
) );
}
delete_post_meta( $post_id, '_wxr_import_menu_item' );
}
protected function post_process_comments( $todo ) {
foreach ( $todo as $comment_id => $_ ) {
$data = array();
$parent_id = get_comment_meta( $comment_id, '_wxr_import_parent', true );
if ( ! empty( $parent_id ) ) {
// Have we imported the parent now?
if ( isset( $this->mapping['comment'][ $parent_id ] ) ) {
$data['comment_parent'] = $this->mapping['comment'][ $parent_id ];
} else {
$this->logger->warning( sprintf(
__( 'Could not find the comment parent for comment #%d', 'wordpress-importer' ),
$comment_id
) );
$this->logger->debug( sprintf(
__( 'Comment %d was imported with parent %d, but could not be found', 'wordpress-importer' ),
$comment_id,
$parent_id
) );
}
}
$author_id = get_comment_meta( $comment_id, '_wxr_import_user', true );
if ( ! empty( $author_id ) ) {
// Have we imported the user now?
if ( isset( $this->mapping['user'][ $author_id ] ) ) {
$data['user_id'] = $this->mapping['user'][ $author_id ];
} else {
$this->logger->warning( sprintf(
__( 'Could not find the author for comment #%d', 'wordpress-importer' ),
$comment_id
) );
$this->logger->debug( sprintf(
__( 'Comment %d was imported with author %d, but could not be found', 'wordpress-importer' ),
$comment_id,
$author_id
) );
}
}
// Do we have updates to make?
if ( empty( $data ) ) {
continue;
}
// Run the update
$data['comment_ID'] = $comment_id;
$result = wp_update_comment( wp_slash( $data ) );
if ( empty( $result ) ) {
$this->logger->warning( sprintf(
__( 'Could not update comment #%d with mapped data', 'wordpress-importer' ),
$comment_id
) );
continue;
}
// Clear out our temporary meta keys
delete_comment_meta( $comment_id, '_wxr_import_parent' );
delete_comment_meta( $comment_id, '_wxr_import_user' );
}
}
/**
* There is no explicit 'top' or 'root' for a hierarchy of WordPress terms
* Terms without a parent, or parent=0 are either unconnected (orphans)
* or top-level siblings without an explicit root parent
* An unconnected term (orphan) should have a null parent_slug
* Top-level siblings without an explicit root parent, shall be identified
* with the parent_slug: top
* [we'll map parent_slug: top into parent 0]
*
* @param array $terms_to_be_remapped The terms to be remapped.
*/
protected function post_process_terms( $terms_to_be_remapped ) {
$this->mapping['term_slug']['top'] = 0;
// The term_id and term_taxonomy are passed-in with $this->requires_remapping['term'].
foreach ( $terms_to_be_remapped as $termid => $term_taxonomy ) {
// Basic check.
if( empty( $termid ) || ! is_numeric( $termid ) ) {
$this->logger->warning( sprintf(
__( 'Faulty term_id provided in terms-to-be-remapped array %s', 'wordpress-importer' ),
$termid
) );
continue;
}
// This cast to integer may be unnecessary.
$term_id = (int) $termid;
if( empty( $term_taxonomy ) ){
$this->logger->warning( sprintf(
__( 'No taxonomy provided in terms-to-be-remapped array for term #%d', 'wordpress-importer' ),
$term_id
) );
continue;
}
$parent_slug = get_term_meta( $term_id, '_wxr_import_parent', true );
if ( empty( $parent_slug ) ) {
$this->logger->warning( sprintf(
__( 'No parent_slug identified in remapping-array for term: %d', 'wordpress-importer' ),
$term_id
) );
continue;
}
if ( ! isset( $this->mapping['term_slug'][ $parent_slug ] ) || ! is_numeric( $this->mapping['term_slug'][ $parent_slug ] ) ) {
$this->logger->warning( sprintf(
__( 'The term(%d)"s parent_slug (%s) is not found in the remapping-array.', 'wordpress-importer' ),
$term_id,
$parent_slug
) );
continue;
}
$mapped_parent = (int) $this->mapping['term_slug'][ $parent_slug ];
$termattributes = get_term_by( 'id', $term_id, $term_taxonomy, ARRAY_A );
// Note: the default OBJECT return results in a reserved-word clash with 'parent' [$termattributes->parent], so instead return an associative array.
if ( empty( $termattributes ) ) {
$this->logger->warning( sprintf(
__( 'No data returned by get_term_by for term_id #%d', 'wordpress-importer' ),
$term_id
) );
continue;
}
// Check if the correct parent id is already correctly mapped.
if ( isset( $termattributes['parent'] ) && $termattributes['parent'] == $mapped_parent ) {
// Clear out our temporary meta key.
delete_term_meta( $term_id, '_wxr_import_parent' );
continue;
}
// Otherwise set the mapped parent and update the term.
$termattributes['parent'] = $mapped_parent;
$result = wp_update_term( $term_id, $termattributes['taxonomy'], $termattributes );
if ( is_wp_error( $result ) ) {
$this->logger->warning( sprintf(
__( 'Could not update "%s" (term #%d) with mapped data', 'wordpress-importer' ),
$termattributes['name'],
$term_id
) );
$this->logger->debug( $result->get_error_message() );
continue;
}
// Clear out our temporary meta key.
delete_term_meta( $term_id, '_wxr_import_parent' );
$this->logger->debug( sprintf(
__( 'Term %d was successfully updated with parent %d', 'wordpress-importer' ),
$term_id,
$mapped_parent
) );
}
}
/**
* Use stored mapping information to update old attachment URLs
*/
protected function replace_attachment_urls_in_content() {
global $wpdb;
// make sure we do the longest urls first, in case one is a substring of another
uksort( $this->url_remap, array( $this, 'cmpr_strlen' ) );
foreach ( $this->url_remap as $from_url => $to_url ) {
// remap urls in post_content
$query = $wpdb->prepare( "UPDATE {$wpdb->posts} SET post_content = REPLACE(post_content, %s, %s)", $from_url, $to_url );
$wpdb->query( $query );
// remap enclosure urls
$query = $wpdb->prepare( "UPDATE {$wpdb->postmeta} SET meta_value = REPLACE(meta_value, %s, %s) WHERE meta_key='enclosure'", $from_url, $to_url );
$wpdb->query( $query );
}
}
/**
* Update _thumbnail_id meta to new, imported attachment IDs
*/
function remap_featured_images() {
if ( empty( $this->featured_images ) ) {
return;
}
$this->logger->info( esc_html__( 'Starting remapping of featured images', 'wordpress-importer' ) );
// Cycle through posts that have a featured image.
foreach ( $this->featured_images as $post_id => $value ) {
if ( isset( $this->mapping['post'][ $value ] ) ) {
$new_id = $this->mapping['post'][ $value ];
// Only update if there's a difference.
if ( $new_id !== $value ) {
$this->logger->info( sprintf( esc_html__( 'Remapping featured image ID %d to new ID %d for post ID %d', 'wordpress-importer' ), $value, $new_id, $post_id ) );
update_post_meta( $post_id, '_thumbnail_id', $new_id );
}
}
}
}
/**
* Decide if the given meta key maps to information we will want to import
*
* @param string $key The meta key to check
* @return string|bool The key if we do want to import, false if not
*/
public function is_valid_meta_key( $key ) {
// skip attachment metadata since we'll regenerate it from scratch
// skip _edit_lock as not relevant for import
if ( in_array( $key, array( '_wp_attached_file', '_wp_attachment_metadata', '_edit_lock' ) ) ) {
return false;
}
return $key;
}
/**
* Decide what the maximum file size for downloaded attachments is.
* Default is 0 (unlimited), can be filtered via import_attachment_size_limit
*
* @return int Maximum attachment file size to import
*/
protected function max_attachment_size() {
return apply_filters( 'import_attachment_size_limit', 0 );
}
/**
* Added to http_request_timeout filter to force timeout at 60 seconds during import
*
* @param int $val Time in seconds.
* @access protected
* @return int 60
*/
function bump_request_timeout($val) {
return 60;
}
// return the difference in length between two strings
function cmpr_strlen( $a, $b ) {
return strlen( $b ) - strlen( $a );
}
/**
* Prefill existing post data.
*
* This preloads all GUIDs into memory, allowing us to avoid hitting the
* database when we need to check for existence. With larger imports, this
* becomes prohibitively slow to perform SELECT queries on each.
*
* By preloading all this data into memory, it's a constant-time lookup in
* PHP instead. However, this does use a lot more memory, so for sites doing
* small imports onto a large site, it may be a better tradeoff to use
* on-the-fly checking instead.
*/
protected function prefill_existing_posts() {
global $wpdb;
$posts = $wpdb->get_results( "SELECT ID, guid FROM {$wpdb->posts}" );
foreach ( $posts as $item ) {
$this->exists['post'][ $item->guid ] = $item->ID;
}
}
/**
* Does the post exist?
*
* @param array $data Post data to check against.
* @return int|bool Existing post ID if it exists, false otherwise.
*/
protected function post_exists( $data ) {
// Constant-time lookup if we prefilled
$exists_key = $data['guid'];
if ( $this->options['prefill_existing_posts'] ) {
// OCDI: fix for custom post types. The guids in the prefilled section are escaped, so these ones should be as well.
$exists_key = htmlentities( $exists_key );
return isset( $this->exists['post'][ $exists_key ] ) ? $this->exists['post'][ $exists_key ] : false;
}
// No prefilling, but might have already handled it
if ( isset( $this->exists['post'][ $exists_key ] ) ) {
return $this->exists['post'][ $exists_key ];
}
// Still nothing, try post_exists, and cache it
$exists = post_exists( $data['post_title'], $data['post_content'], $data['post_date'] );
$this->exists['post'][ $exists_key ] = $exists;
return $exists;
}
/**
* Mark the post as existing.
*
* @param array $data Post data to mark as existing.
* @param int $post_id Post ID.
*/
protected function mark_post_exists( $data, $post_id ) {
$exists_key = $data['guid'];
$this->exists['post'][ $exists_key ] = $post_id;
}
/**
* Prefill existing comment data.
*
* @see self::prefill_existing_posts() for justification of why this exists.
*/
protected function prefill_existing_comments() {
global $wpdb;
$posts = $wpdb->get_results( "SELECT comment_ID, comment_author, comment_date FROM {$wpdb->comments}" );
foreach ( $posts as $item ) {
$exists_key = sha1( $item->comment_author . ':' . $item->comment_date );
$this->exists['comment'][ $exists_key ] = $item->comment_ID;
}
}
/**
* Does the comment exist?
*
* @param array $data Comment data to check against.
* @return int|bool Existing comment ID if it exists, false otherwise.
*/
protected function comment_exists( $data ) {
$exists_key = sha1( $data['comment_author'] . ':' . $data['comment_date'] );
// Constant-time lookup if we prefilled
if ( $this->options['prefill_existing_comments'] ) {
return isset( $this->exists['comment'][ $exists_key ] ) ? $this->exists['comment'][ $exists_key ] : false;
}
// No prefilling, but might have already handled it
if ( isset( $this->exists['comment'][ $exists_key ] ) ) {
return $this->exists['comment'][ $exists_key ];
}
// Still nothing, try comment_exists, and cache it
$exists = comment_exists( $data['comment_author'], $data['comment_date'] );
$this->exists['comment'][ $exists_key ] = $exists;
return $exists;
}
/**
* Mark the comment as existing.
*
* @param array $data Comment data to mark as existing.
* @param int $comment_id Comment ID.
*/
protected function mark_comment_exists( $data, $comment_id ) {
$exists_key = sha1( $data['comment_author'] . ':' . $data['comment_date'] );
$this->exists['comment'][ $exists_key ] = $comment_id;
}
/**
* Prefill existing term data.
*
* @see self::prefill_existing_posts() for justification of why this exists.
*/
protected function prefill_existing_terms() {
global $wpdb;
$query = "SELECT t.term_id, tt.taxonomy, t.slug FROM {$wpdb->terms} AS t";
$query .= " JOIN {$wpdb->term_taxonomy} AS tt ON t.term_id = tt.term_id";
$terms = $wpdb->get_results( $query );
foreach ( $terms as $item ) {
$exists_key = sha1( $item->taxonomy . ':' . $item->slug );
$this->exists['term'][ $exists_key ] = $item->term_id;
}
}
/**
* Does the term exist?
*
* @param array $data Term data to check against.
* @return int|bool Existing term ID if it exists, false otherwise.
*/
protected function term_exists( $data ) {
$exists_key = sha1( $data['taxonomy'] . ':' . $data['slug'] );
// Constant-time lookup if we prefilled
if ( $this->options['prefill_existing_terms'] ) {
return isset( $this->exists['term'][ $exists_key ] ) ? $this->exists['term'][ $exists_key ] : false;
}
// No prefilling, but might have already handled it
if ( isset( $this->exists['term'][ $exists_key ] ) ) {
return $this->exists['term'][ $exists_key ];
}
// Still nothing, try comment_exists, and cache it
$exists = term_exists( $data['slug'], $data['taxonomy'] );
if ( is_array( $exists ) ) {
$exists = $exists['term_id'];
}
$this->exists['term'][ $exists_key ] = $exists;
return $exists;
}
/**
* Mark the term as existing.
*
* @param array $data Term data to mark as existing.
* @param int $term_id Term ID.
*/
protected function mark_term_exists( $data, $term_id ) {
$exists_key = sha1( $data['taxonomy'] . ':' . $data['slug'] );
$this->exists['term'][ $exists_key ] = $term_id;
}
}
includes/class-logger.php 0000644 00000006660 14751055733 0011467 0 ustar 00 <?php
/**
* Describes a logger instance
*
* Based on PSR-3: http://www.php-fig.org/psr/psr-3/
*
* The message MUST be a string or object implementing __toString().
*
* The message MAY contain placeholders in the form: {foo} where foo
* will be replaced by the context data in key "foo".
*
* The context array can contain arbitrary data, the only assumption that
* can be made by implementors is that if an Exception instance is given
* to produce a stack trace, it MUST be in a key named "exception".
*
* See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md
* for the full interface specification.
*/
class WP_Importer_Logger {
/**
* System is unusable.
*
* @param string $message
* @param array $context
* @return null
*/
public function emergency( $message, array $context = array() ) {
return $this->log( 'emergency', $message, $context );
}
/**
* Action must be taken immediately.
*
* Example: Entire website down, database unavailable, etc. This should
* trigger the SMS alerts and wake you up.
*
* @param string $message
* @param array $context
* @return null
*/
public function alert( $message, array $context = array() ) {
return $this->log( 'alert', $message, $context );
}
/**
* Critical conditions.
*
* Example: Application component unavailable, unexpected exception.
*
* @param string $message
* @param array $context
* @return null
*/
public function critical( $message, array $context = array() ) {
return $this->log( 'critical', $message, $context );
}
/**
* Runtime errors that do not require immediate action but should typically
* be logged and monitored.
*
* @param string $message
* @param array $context
* @return null
*/
public function error( $message, array $context = array()) {
return $this->log( 'error', $message, $context );
}
/**
* Exceptional occurrences that are not errors.
*
* Example: Use of deprecated APIs, poor use of an API, undesirable things
* that are not necessarily wrong.
*
* @param string $message
* @param array $context
* @return null
*/
public function warning( $message, array $context = array() ) {
return $this->log( 'warning', $message, $context );
}
/**
* Normal but significant events.
*
* @param string $message
* @param array $context
* @return null
*/
public function notice( $message, array $context = array() ) {
return $this->log( 'notice', $message, $context );
}
/**
* Interesting events.
*
* Example: User logs in, SQL logs.
*
* @param string $message
* @param array $context
* @return null
*/
public function info( $message, array $context = array() ) {
return $this->log( 'info', $message, $context );
}
/**
* Detailed debug information.
*
* @param string $message
* @param array $context
* @return null
*/
public function debug( $message, array $context = array() ) {
return $this->log( 'debug', $message, $context );
}
/**
* Logs with an arbitrary level.
*
* @param mixed $level
* @param string $message
* @param array $context
* @return null
*/
public function log( $level, $message, array $context = array() ) {
$this->messages[] = array(
'timestamp' => time(),
'level' => $level,
'message' => $message,
'context' => $context,
);
}
}
includes/customizer-importer.php 0000644 00000011506 14751055733 0013143 0 ustar 00 <?php
/**
* Class for the customizer importer used in the One Click Demo Import plugin.
*
* Code is mostly from the Customizer Export/Import plugin.
*
* @see https://wordpress.org/plugins/customizer-export-import/
*/
class Soo_Demo_Customizer_Importer {
public $download_images = false;
private $processed_images = array();
/**
* Imports uploaded mods and calls WordPress core customize_save actions so
* themes that hook into them can act before mods are saved to the database.
*
* @param string $file
*/
public function import( $file ) {
// Setup global vars.
global $wp_customize;
// Setup internal vars.
$template = get_template();
// Get the upload data.
$raw = file_get_contents( $file );
$data = @unserialize( $raw );
// Data checks.
if ( ! is_array( $data ) && ( ! isset( $data['template'] ) || ! isset( $data['mods'] ) ) ) {
return new WP_Error(
'customizer_import_data_error',
esc_html__( 'The customizer import file is not in a correct format. Please make sure to use the correct customizer import file.', 'soodi' )
);
}
if ( $data['template'] !== $template ) {
return new WP_Error(
'customizer_import_wrong_theme',
esc_html__( 'The customizer import file is not suitable for current theme. You can only import customizer settings for the same theme or a child theme.', 'soodi' )
);
}
// Import images.
if ( $this->download_images ) {
$data['mods'] = $this->import_images( $data['mods'] );
}
// Import custom options.
if ( isset( $data['options'] ) ) {
require dirname( __FILE__ ) . '/customizer-option.php';
foreach ( $data['options'] as $option_key => $option_value ) {
$option = new Soo_Demo_Customizer_Option( $wp_customize, $option_key, array(
'default' => '',
'type' => 'option',
'capability' => 'edit_theme_options',
) );
$option->import( $option_value );
}
}
// Loop through the mods.
foreach ( $data['mods'] as $key => $val ) {
// Save the mod.
set_theme_mod( $key, $val );
}
return true;
}
/**
* Imports images for settings saved as mods.
*
* @return array The mods array with any new import data.
*/
private function import_images( $mods ) {
foreach ( $mods as $key => $val ) {
if ( $this->is_image_url( $val ) ) {
if ( array_key_exists( $val, $this->processed_images ) ) {
$data = $this->processed_images[$val];
} else {
$data = $this->sideload_image( $val );
$this->processed_images[$val] = $data;
}
if ( ! is_wp_error( $data ) ) {
$mods[ $key ] = $data->url;
// Handle header image controls.
if ( isset( $mods[ $key . '_data' ] ) ) {
$mods[ $key . '_data' ] = $data;
update_post_meta( $data->attachment_id, '_wp_attachment_is_custom_header', get_stylesheet() );
}
}
}
}
return $mods;
}
/**
* Taken from the core media_sideload_image function and
* modified to return an array of data instead of html.
*
* @param string $file The image file path.
* @return array An array of image data.
*/
private function sideload_image( $file ) {
$data = new stdClass();
if ( ! function_exists( 'media_handle_sideload' ) ) {
require_once( ABSPATH . 'wp-admin/includes/media.php' );
require_once( ABSPATH . 'wp-admin/includes/file.php' );
require_once( ABSPATH . 'wp-admin/includes/image.php' );
}
if ( ! empty( $file ) ) {
// Set variables for storage, fix file filename for query strings.
preg_match( '/[^\?]+\.(jpe?g|jpe|gif|png)\b/i', $file, $matches );
$file_array = array();
$file_array['name'] = basename( $matches[0] );
// Download file to temp location.
$file_array['tmp_name'] = download_url( $file );
// If error storing temporarily, return the error.
if ( is_wp_error( $file_array['tmp_name'] ) ) {
return $file_array['tmp_name'];
}
// Do the validation and storage stuff.
$id = media_handle_sideload( $file_array, 0 );
// If error storing permanently, unlink.
if ( is_wp_error( $id ) ) {
@unlink( $file_array['tmp_name'] );
return $id;
}
// Build the object to return.
$meta = wp_get_attachment_metadata( $id );
$data->attachment_id = $id;
$data->url = wp_get_attachment_url( $id );
$data->thumbnail_url = wp_get_attachment_thumb_url( $id );
$data->height = $meta['height'];
$data->width = $meta['width'];
}
return $data;
}
/**
* Checks to see whether a string is an image url or not.
*
* @return bool Whether the string is an image url or not.
*/
private function is_image_url( $string = '' ) {
if ( is_string( $string ) ) {
if ( preg_match( '/\.(jpg|jpeg|png|gif)/i', $string ) ) {
return true;
}
}
return false;
}
}
includes/widgets-importer.php 0000644 00000020130 14751055733 0012376 0 ustar 00 <?php
/**
* Import widgets from a json file
* Use plugin Widget Importer & Exporter to export
*
* @link https://wordpress.org/plugins/widget-importer-exporter/
*/
/**
* Class to import widgets from exported file
* The exported file must be in json format
*
* @version 1.0.0
*/
class Soo_Demo_Widgets_Importer {
/**
* Main function to import widgets
*
* @param object $data The widgets object
* @return array Imported widgets
*/
public function import( $data ) {
global $wp_registered_sidebars;
// Have valid data?
// If no data or could not decode
if ( empty( $data ) || ! is_object( $data ) ) {
wp_die(
__( 'Import data could not be read. Please try a different file.', 'claudio' ),
'',
array( 'back_link' => true )
);
}
// Get all available widgets site supports
$available_widgets = $this->available_widgets();
// Get all existing widget instances
$widget_instances = array();
foreach ( $available_widgets as $widget_data ) {
$widget_instances[$widget_data['id_base']] = get_option( 'widget_' . $widget_data['id_base'] );
}
// Begin results
$results = array();
// Loop import data's sidebars
foreach ( $data as $sidebar_id => $widgets ) {
// Skip inactive widgets
// (should not be in export file)
if ( 'wp_inactive_widgets' == $sidebar_id ) {
continue;
}
// Check if sidebar is available on this site
// Otherwise add widgets to inactive, and say so
if ( isset( $wp_registered_sidebars[$sidebar_id] ) ) {
$sidebar_available = true;
$use_sidebar_id = $sidebar_id;
$sidebar_message_type = 'success';
$sidebar_message = '';
} else {
$sidebar_available = false;
$use_sidebar_id = 'wp_inactive_widgets'; // add to inactive if sidebar does not exist in theme
$sidebar_message_type = 'error';
$sidebar_message = __( 'Sidebar does not exist in theme (using Inactive)', 'claudio' );
}
// Result for sidebar
$results[$sidebar_id]['name'] = ! empty( $wp_registered_sidebars[$sidebar_id]['name'] ) ? $wp_registered_sidebars[$sidebar_id]['name'] : $sidebar_id; // sidebar name if theme supports it; otherwise ID
$results[$sidebar_id]['message_type'] = $sidebar_message_type;
$results[$sidebar_id]['message'] = $sidebar_message;
$results[$sidebar_id]['widgets'] = array();
// Loop widgets
foreach ( $widgets as $widget_instance_id => $widget ) {
$fail = false;
// Get id_base (remove -# from end) and instance ID number
$id_base = preg_replace( '/-[0-9]+$/', '', $widget_instance_id );
$instance_id_number = str_replace( $id_base . '-', '', $widget_instance_id );
// Does site support this widget?
if ( ! $fail && ! isset( $available_widgets[$id_base] ) ) {
$fail = true;
$widget_message_type = 'error';
$widget_message = __( 'Site does not support widget', 'claudio' ); // explain why widget not imported
}
// Convert multidimensional objects to multidimensional arrays
// Some plugins like Jetpack Widget Visibility store settings as multidimensional arrays
// Without this, they are imported as objects and cause fatal error on Widgets page
// If this creates problems for plugins that do actually intend settings in objects then may need to consider other approach: https://wordpress.org/support/topic/problem-with-array-of-arrays
// It is probably much more likely that arrays are used than objects, however
$widget = json_decode( json_encode( $widget ), true );
// Does widget with identical settings already exist in same sidebar?
if ( ! $fail && isset( $widget_instances[$id_base] ) ) {
// Get existing widgets in this sidebar
$sidebars_widgets = get_option( 'sidebars_widgets' );
$sidebar_widgets = isset( $sidebars_widgets[$use_sidebar_id] ) ? $sidebars_widgets[$use_sidebar_id] : array(); // check Inactive if that's where will go
// Loop widgets with ID base
$single_widget_instances = ! empty( $widget_instances[$id_base] ) ? $widget_instances[$id_base] : array();
foreach ( $single_widget_instances as $check_id => $check_widget ) {
// Is widget in same sidebar and has identical settings?
if ( in_array( "$id_base-$check_id", $sidebar_widgets ) && (array) $widget == $check_widget ) {
$fail = true;
$widget_message_type = 'warning';
$widget_message = __( 'Widget already exists', 'claudio' ); // explain why widget not imported
break;
}
}
}
// No failure
if ( ! $fail ) {
// Add widget instance
$single_widget_instances = get_option( 'widget_' . $id_base ); // all instances for that widget ID base, get fresh every time
$single_widget_instances = ! empty( $single_widget_instances ) ? $single_widget_instances : array( '_multiwidget' => 1 ); // start fresh if have to
$single_widget_instances[] = $widget; // add it
// Get the key it was given
end( $single_widget_instances );
$new_instance_id_number = key( $single_widget_instances );
// If key is 0, make it 1
// When 0, an issue can occur where adding a widget causes data from other widget to load, and the widget doesn't stick (reload wipes it)
if ( '0' === strval( $new_instance_id_number ) ) {
$new_instance_id_number = 1;
$single_widget_instances[$new_instance_id_number] = $single_widget_instances[0];
unset( $single_widget_instances[0] );
}
// Move _multiwidget to end of array for uniformity
if ( isset( $single_widget_instances['_multiwidget'] ) ) {
$multiwidget = $single_widget_instances['_multiwidget'];
unset( $single_widget_instances['_multiwidget'] );
$single_widget_instances['_multiwidget'] = $multiwidget;
}
// Update option with new widget
update_option( 'widget_' . $id_base, $single_widget_instances );
// Assign widget instance to sidebar
$sidebars_widgets = get_option( 'sidebars_widgets' ); // which sidebars have which widgets, get fresh every time
$new_instance_id = $id_base . '-' . $new_instance_id_number; // use ID number from new widget instance
$sidebars_widgets[$use_sidebar_id][] = $new_instance_id; // add new instance to sidebar
update_option( 'sidebars_widgets', $sidebars_widgets ); // save the amended data
// Success message
if ( $sidebar_available ) {
$widget_message_type = 'success';
$widget_message = __( 'Imported', 'claudio' );
} else {
$widget_message_type = 'warning';
$widget_message = __( 'Imported to Inactive', 'claudio' );
}
}
// Result for widget instance
$results[$sidebar_id]['widgets'][$widget_instance_id]['name'] = isset( $available_widgets[$id_base]['name'] ) ? $available_widgets[$id_base]['name'] : $id_base; // widget name or ID if name not available (not supported by site)
$results[$sidebar_id]['widgets'][$widget_instance_id]['title'] = ! empty( $widget['title'] ) ? $widget['title'] : __( 'No Title', 'claudio' ); // show "No Title" if widget instance is untitled
$results[$sidebar_id]['widgets'][$widget_instance_id]['message_type'] = $widget_message_type;
$results[$sidebar_id]['widgets'][$widget_instance_id]['message'] = $widget_message;
}
}
// Return results
return $results;
}
/**
* Get all available widgets
*
* Gather site's widgets into array with ID base, name, etc.
* Used by export and import functions.
*
* @global array $wp_registered_widget_updates
*
* @return array Widget information
*/
public function available_widgets() {
global $wp_registered_widget_controls;
$widget_controls = $wp_registered_widget_controls;
$available_widgets = array();
foreach ( $widget_controls as $widget ) {
if ( ! empty( $widget['id_base'] ) && ! isset( $available_widgets[$widget['id_base']] ) ) { // no dupes
$available_widgets[$widget['id_base']]['id_base'] = $widget['id_base'];
$available_widgets[$widget['id_base']]['name'] = $widget['name'];
}
}
return $available_widgets;
}
}
includes/parsers.php 0000644 00000057126 14751055733 0010567 0 ustar 00 <?php
/**
* WordPress eXtended RSS file parser implementations
*
* @package WordPress
* @subpackage Importer
*/
/**
* WordPress Importer class for managing parsing of WXR files.
*/
class WXR_Parser {
function parse( $file ) {
// Attempt to use proper XML parsers first
if ( extension_loaded( 'simplexml' ) ) {
$parser = new WXR_Parser_SimpleXML;
$result = $parser->parse( $file );
// If SimpleXML succeeds or this is an invalid WXR file then return the results
if ( ! is_wp_error( $result ) || 'SimpleXML_parse_error' != $result->get_error_code() )
return $result;
} else if ( extension_loaded( 'xml' ) ) {
$parser = new WXR_Parser_XML;
$result = $parser->parse( $file );
// If XMLParser succeeds or this is an invalid WXR file then return the results
if ( ! is_wp_error( $result ) || 'XML_parse_error' != $result->get_error_code() )
return $result;
}
// We have a malformed XML file, so display the error and fallthrough to regex
if ( isset($result) && defined('IMPORT_DEBUG') && IMPORT_DEBUG ) {
echo '<pre>';
if ( 'SimpleXML_parse_error' == $result->get_error_code() ) {
foreach ( $result->get_error_data() as $error )
echo $error->line . ':' . $error->column . ' ' . esc_html( $error->message ) . "\n";
} else if ( 'XML_parse_error' == $result->get_error_code() ) {
$error = $result->get_error_data();
echo $error[0] . ':' . $error[1] . ' ' . esc_html( $error[2] );
}
echo '</pre>';
echo '<p><strong>' . __( 'There was an error when reading this WXR file', 'wordpress-importer' ) . '</strong><br />';
echo __( 'Details are shown above. The importer will now try again with a different parser...', 'wordpress-importer' ) . '</p>';
}
// use regular expressions if nothing else available or this is bad XML
$parser = new WXR_Parser_Regex;
return $parser->parse( $file );
}
}
/**
* WXR Parser that makes use of the SimpleXML PHP extension.
*/
class WXR_Parser_SimpleXML {
function parse( $file ) {
$authors = $posts = $categories = $tags = $terms = array();
$internal_errors = libxml_use_internal_errors(true);
$dom = new DOMDocument;
$old_value = null;
if ( function_exists( 'libxml_disable_entity_loader' ) ) {
$old_value = libxml_disable_entity_loader( true );
}
$success = $dom->loadXML( file_get_contents( $file ) );
if ( ! is_null( $old_value ) ) {
libxml_disable_entity_loader( $old_value );
}
if ( ! $success || isset( $dom->doctype ) ) {
return new WP_Error( 'SimpleXML_parse_error', __( 'There was an error when reading this WXR file', 'wordpress-importer' ), libxml_get_errors() );
}
$xml = simplexml_import_dom( $dom );
unset( $dom );
// halt if loading produces an error
if ( ! $xml )
return new WP_Error( 'SimpleXML_parse_error', __( 'There was an error when reading this WXR file', 'wordpress-importer' ), libxml_get_errors() );
$wxr_version = $xml->xpath('/rss/channel/wp:wxr_version');
if ( ! $wxr_version )
return new WP_Error( 'WXR_parse_error', __( 'This does not appear to be a WXR file, missing/invalid WXR version number', 'wordpress-importer' ) );
$wxr_version = (string) trim( $wxr_version[0] );
// confirm that we are dealing with the correct file format
if ( ! preg_match( '/^\d+\.\d+$/', $wxr_version ) )
return new WP_Error( 'WXR_parse_error', __( 'This does not appear to be a WXR file, missing/invalid WXR version number', 'wordpress-importer' ) );
$base_url = $xml->xpath('/rss/channel/wp:base_site_url');
$base_url = (string) trim( $base_url[0] );
$namespaces = $xml->getDocNamespaces();
if ( ! isset( $namespaces['wp'] ) )
$namespaces['wp'] = 'http://wordpress.org/export/1.1/';
if ( ! isset( $namespaces['excerpt'] ) )
$namespaces['excerpt'] = 'http://wordpress.org/export/1.1/excerpt/';
// grab authors
foreach ( $xml->xpath('/rss/channel/wp:author') as $author_arr ) {
$a = $author_arr->children( $namespaces['wp'] );
$login = (string) $a->author_login;
$authors[$login] = array(
'author_id' => (int) $a->author_id,
'author_login' => $login,
'author_email' => (string) $a->author_email,
'author_display_name' => (string) $a->author_display_name,
'author_first_name' => (string) $a->author_first_name,
'author_last_name' => (string) $a->author_last_name
);
}
// grab cats, tags and terms
foreach ( $xml->xpath('/rss/channel/wp:category') as $term_arr ) {
$t = $term_arr->children( $namespaces['wp'] );
$categories[] = array(
'term_id' => (int) $t->term_id,
'category_nicename' => (string) $t->category_nicename,
'category_parent' => (string) $t->category_parent,
'cat_name' => (string) $t->cat_name,
'category_description' => (string) $t->category_description
);
}
foreach ( $xml->xpath('/rss/channel/wp:tag') as $term_arr ) {
$t = $term_arr->children( $namespaces['wp'] );
$tags[] = array(
'term_id' => (int) $t->term_id,
'tag_slug' => (string) $t->tag_slug,
'tag_name' => (string) $t->tag_name,
'tag_description' => (string) $t->tag_description
);
}
foreach ( $xml->xpath('/rss/channel/wp:term') as $term_arr ) {
$t = $term_arr->children( $namespaces['wp'] );
$terms[] = array(
'term_id' => (int) $t->term_id,
'term_taxonomy' => (string) $t->term_taxonomy,
'slug' => (string) $t->term_slug,
'term_parent' => (string) $t->term_parent,
'term_name' => (string) $t->term_name,
'term_description' => (string) $t->term_description
);
}
// grab posts
foreach ( $xml->channel->item as $item ) {
$post = array(
'post_title' => (string) $item->title,
'guid' => (string) $item->guid,
);
$dc = $item->children( 'http://purl.org/dc/elements/1.1/' );
$post['post_author'] = (string) $dc->creator;
$content = $item->children( 'http://purl.org/rss/1.0/modules/content/' );
$excerpt = $item->children( $namespaces['excerpt'] );
$post['post_content'] = (string) $content->encoded;
$post['post_excerpt'] = (string) $excerpt->encoded;
$wp = $item->children( $namespaces['wp'] );
$post['post_id'] = (int) $wp->post_id;
$post['post_date'] = (string) $wp->post_date;
$post['post_date_gmt'] = (string) $wp->post_date_gmt;
$post['comment_status'] = (string) $wp->comment_status;
$post['ping_status'] = (string) $wp->ping_status;
$post['post_name'] = (string) $wp->post_name;
$post['status'] = (string) $wp->status;
$post['post_parent'] = (int) $wp->post_parent;
$post['menu_order'] = (int) $wp->menu_order;
$post['post_type'] = (string) $wp->post_type;
$post['post_password'] = (string) $wp->post_password;
$post['is_sticky'] = (int) $wp->is_sticky;
if ( isset($wp->attachment_url) )
$post['attachment_url'] = (string) $wp->attachment_url;
foreach ( $item->category as $c ) {
$att = $c->attributes();
if ( isset( $att['nicename'] ) )
$post['terms'][] = array(
'name' => (string) $c,
'slug' => (string) $att['nicename'],
'domain' => (string) $att['domain']
);
}
foreach ( $wp->postmeta as $meta ) {
$post['postmeta'][] = array(
'key' => (string) $meta->meta_key,
'value' => (string) $meta->meta_value
);
}
foreach ( $wp->comment as $comment ) {
$meta = array();
if ( isset( $comment->commentmeta ) ) {
foreach ( $comment->commentmeta as $m ) {
$meta[] = array(
'key' => (string) $m->meta_key,
'value' => (string) $m->meta_value
);
}
}
$post['comments'][] = array(
'comment_id' => (int) $comment->comment_id,
'comment_author' => (string) $comment->comment_author,
'comment_author_email' => (string) $comment->comment_author_email,
'comment_author_IP' => (string) $comment->comment_author_IP,
'comment_author_url' => (string) $comment->comment_author_url,
'comment_date' => (string) $comment->comment_date,
'comment_date_gmt' => (string) $comment->comment_date_gmt,
'comment_content' => (string) $comment->comment_content,
'comment_approved' => (string) $comment->comment_approved,
'comment_type' => (string) $comment->comment_type,
'comment_parent' => (string) $comment->comment_parent,
'comment_user_id' => (int) $comment->comment_user_id,
'commentmeta' => $meta,
);
}
$posts[] = $post;
}
return array(
'authors' => $authors,
'posts' => $posts,
'categories' => $categories,
'tags' => $tags,
'terms' => $terms,
'base_url' => $base_url,
'version' => $wxr_version
);
}
}
/**
* WXR Parser that makes use of the XML Parser PHP extension.
*/
class WXR_Parser_XML {
var $wp_tags = array(
'wp:post_id', 'wp:post_date', 'wp:post_date_gmt', 'wp:comment_status', 'wp:ping_status', 'wp:attachment_url',
'wp:status', 'wp:post_name', 'wp:post_parent', 'wp:menu_order', 'wp:post_type', 'wp:post_password',
'wp:is_sticky', 'wp:term_id', 'wp:category_nicename', 'wp:category_parent', 'wp:cat_name', 'wp:category_description',
'wp:tag_slug', 'wp:tag_name', 'wp:tag_description', 'wp:term_taxonomy', 'wp:term_parent',
'wp:term_name', 'wp:term_description', 'wp:author_id', 'wp:author_login', 'wp:author_email', 'wp:author_display_name',
'wp:author_first_name', 'wp:author_last_name',
);
var $wp_sub_tags = array(
'wp:comment_id', 'wp:comment_author', 'wp:comment_author_email', 'wp:comment_author_url',
'wp:comment_author_IP', 'wp:comment_date', 'wp:comment_date_gmt', 'wp:comment_content',
'wp:comment_approved', 'wp:comment_type', 'wp:comment_parent', 'wp:comment_user_id',
);
function parse( $file ) {
$this->wxr_version = $this->in_post = $this->cdata = $this->data = $this->sub_data = $this->in_tag = $this->in_sub_tag = false;
$this->authors = $this->posts = $this->term = $this->category = $this->tag = array();
$xml = xml_parser_create( 'UTF-8' );
xml_parser_set_option( $xml, XML_OPTION_SKIP_WHITE, 1 );
xml_parser_set_option( $xml, XML_OPTION_CASE_FOLDING, 0 );
xml_set_object( $xml, $this );
xml_set_character_data_handler( $xml, 'cdata' );
xml_set_element_handler( $xml, 'tag_open', 'tag_close' );
if ( ! xml_parse( $xml, file_get_contents( $file ), true ) ) {
$current_line = xml_get_current_line_number( $xml );
$current_column = xml_get_current_column_number( $xml );
$error_code = xml_get_error_code( $xml );
$error_string = xml_error_string( $error_code );
return new WP_Error( 'XML_parse_error', 'There was an error when reading this WXR file', array( $current_line, $current_column, $error_string ) );
}
xml_parser_free( $xml );
if ( ! preg_match( '/^\d+\.\d+$/', $this->wxr_version ) )
return new WP_Error( 'WXR_parse_error', __( 'This does not appear to be a WXR file, missing/invalid WXR version number', 'wordpress-importer' ) );
return array(
'authors' => $this->authors,
'posts' => $this->posts,
'categories' => $this->category,
'tags' => $this->tag,
'terms' => $this->term,
'base_url' => $this->base_url,
'version' => $this->wxr_version
);
}
function tag_open( $parse, $tag, $attr ) {
if ( in_array( $tag, $this->wp_tags ) ) {
$this->in_tag = substr( $tag, 3 );
return;
}
if ( in_array( $tag, $this->wp_sub_tags ) ) {
$this->in_sub_tag = substr( $tag, 3 );
return;
}
switch ( $tag ) {
case 'category':
if ( isset($attr['domain'], $attr['nicename']) ) {
$this->sub_data['domain'] = $attr['domain'];
$this->sub_data['slug'] = $attr['nicename'];
}
break;
case 'item': $this->in_post = true;
case 'title': if ( $this->in_post ) $this->in_tag = 'post_title'; break;
case 'guid': $this->in_tag = 'guid'; break;
case 'dc:creator': $this->in_tag = 'post_author'; break;
case 'content:encoded': $this->in_tag = 'post_content'; break;
case 'excerpt:encoded': $this->in_tag = 'post_excerpt'; break;
case 'wp:term_slug': $this->in_tag = 'slug'; break;
case 'wp:meta_key': $this->in_sub_tag = 'key'; break;
case 'wp:meta_value': $this->in_sub_tag = 'value'; break;
}
}
function cdata( $parser, $cdata ) {
if ( ! trim( $cdata ) )
return;
$this->cdata .= trim( $cdata );
}
function tag_close( $parser, $tag ) {
switch ( $tag ) {
case 'wp:comment':
unset( $this->sub_data['key'], $this->sub_data['value'] ); // remove meta sub_data
if ( ! empty( $this->sub_data ) )
$this->data['comments'][] = $this->sub_data;
$this->sub_data = false;
break;
case 'wp:commentmeta':
$this->sub_data['commentmeta'][] = array(
'key' => $this->sub_data['key'],
'value' => $this->sub_data['value']
);
break;
case 'category':
if ( ! empty( $this->sub_data ) ) {
$this->sub_data['name'] = $this->cdata;
$this->data['terms'][] = $this->sub_data;
}
$this->sub_data = false;
break;
case 'wp:postmeta':
if ( ! empty( $this->sub_data ) )
$this->data['postmeta'][] = $this->sub_data;
$this->sub_data = false;
break;
case 'item':
$this->posts[] = $this->data;
$this->data = false;
break;
case 'wp:category':
case 'wp:tag':
case 'wp:term':
$n = substr( $tag, 3 );
array_push( $this->$n, $this->data );
$this->data = false;
break;
case 'wp:author':
if ( ! empty($this->data['author_login']) )
$this->authors[$this->data['author_login']] = $this->data;
$this->data = false;
break;
case 'wp:base_site_url':
$this->base_url = $this->cdata;
break;
case 'wp:wxr_version':
$this->wxr_version = $this->cdata;
break;
default:
if ( $this->in_sub_tag ) {
$this->sub_data[$this->in_sub_tag] = ! empty( $this->cdata ) ? $this->cdata : '';
$this->in_sub_tag = false;
} else if ( $this->in_tag ) {
$this->data[$this->in_tag] = ! empty( $this->cdata ) ? $this->cdata : '';
$this->in_tag = false;
}
}
$this->cdata = false;
}
}
/**
* WXR Parser that uses regular expressions. Fallback for installs without an XML parser.
*/
class WXR_Parser_Regex {
var $authors = array();
var $posts = array();
var $categories = array();
var $tags = array();
var $terms = array();
var $base_url = '';
function WXR_Parser_Regex() {
$this->__construct();
}
function __construct() {
$this->has_gzip = is_callable( 'gzopen' );
}
function parse( $file ) {
$wxr_version = $in_post = false;
$fp = $this->fopen( $file, 'r' );
if ( $fp ) {
while ( ! $this->feof( $fp ) ) {
$importline = rtrim( $this->fgets( $fp ) );
if ( ! $wxr_version && preg_match( '|<wp:wxr_version>(\d+\.\d+)</wp:wxr_version>|', $importline, $version ) )
$wxr_version = $version[1];
if ( false !== strpos( $importline, '<wp:base_site_url>' ) ) {
preg_match( '|<wp:base_site_url>(.*?)</wp:base_site_url>|is', $importline, $url );
$this->base_url = $url[1];
continue;
}
if ( false !== strpos( $importline, '<wp:category>' ) ) {
preg_match( '|<wp:category>(.*?)</wp:category>|is', $importline, $category );
$this->categories[] = $this->process_category( $category[1] );
continue;
}
if ( false !== strpos( $importline, '<wp:tag>' ) ) {
preg_match( '|<wp:tag>(.*?)</wp:tag>|is', $importline, $tag );
$this->tags[] = $this->process_tag( $tag[1] );
continue;
}
if ( false !== strpos( $importline, '<wp:term>' ) ) {
preg_match( '|<wp:term>(.*?)</wp:term>|is', $importline, $term );
$this->terms[] = $this->process_term( $term[1] );
continue;
}
if ( false !== strpos( $importline, '<wp:author>' ) ) {
preg_match( '|<wp:author>(.*?)</wp:author>|is', $importline, $author );
$a = $this->process_author( $author[1] );
$this->authors[$a['author_login']] = $a;
continue;
}
if ( false !== strpos( $importline, '<item>' ) ) {
$post = '';
$in_post = true;
continue;
}
if ( false !== strpos( $importline, '</item>' ) ) {
$in_post = false;
$this->posts[] = $this->process_post( $post );
continue;
}
if ( $in_post ) {
$post .= $importline . "\n";
}
}
$this->fclose($fp);
}
if ( ! $wxr_version )
return new WP_Error( 'WXR_parse_error', __( 'This does not appear to be a WXR file, missing/invalid WXR version number', 'wordpress-importer' ) );
return array(
'authors' => $this->authors,
'posts' => $this->posts,
'categories' => $this->categories,
'tags' => $this->tags,
'terms' => $this->terms,
'base_url' => $this->base_url,
'version' => $wxr_version
);
}
function get_tag( $string, $tag ) {
preg_match( "|<$tag.*?>(.*?)</$tag>|is", $string, $return );
if ( isset( $return[1] ) ) {
if ( substr( $return[1], 0, 9 ) == '<![CDATA[' ) {
if ( strpos( $return[1], ']]]]><![CDATA[>' ) !== false ) {
preg_match_all( '|<!\[CDATA\[(.*?)\]\]>|s', $return[1], $matches );
$return = '';
foreach( $matches[1] as $match )
$return .= $match;
} else {
$return = preg_replace( '|^<!\[CDATA\[(.*)\]\]>$|s', '$1', $return[1] );
}
} else {
$return = $return[1];
}
} else {
$return = '';
}
return $return;
}
function process_category( $c ) {
return array(
'term_id' => $this->get_tag( $c, 'wp:term_id' ),
'cat_name' => $this->get_tag( $c, 'wp:cat_name' ),
'category_nicename' => $this->get_tag( $c, 'wp:category_nicename' ),
'category_parent' => $this->get_tag( $c, 'wp:category_parent' ),
'category_description' => $this->get_tag( $c, 'wp:category_description' ),
);
}
function process_tag( $t ) {
return array(
'term_id' => $this->get_tag( $t, 'wp:term_id' ),
'tag_name' => $this->get_tag( $t, 'wp:tag_name' ),
'tag_slug' => $this->get_tag( $t, 'wp:tag_slug' ),
'tag_description' => $this->get_tag( $t, 'wp:tag_description' ),
);
}
function process_term( $t ) {
return array(
'term_id' => $this->get_tag( $t, 'wp:term_id' ),
'term_taxonomy' => $this->get_tag( $t, 'wp:term_taxonomy' ),
'slug' => $this->get_tag( $t, 'wp:term_slug' ),
'term_parent' => $this->get_tag( $t, 'wp:term_parent' ),
'term_name' => $this->get_tag( $t, 'wp:term_name' ),
'term_description' => $this->get_tag( $t, 'wp:term_description' ),
);
}
function process_author( $a ) {
return array(
'author_id' => $this->get_tag( $a, 'wp:author_id' ),
'author_login' => $this->get_tag( $a, 'wp:author_login' ),
'author_email' => $this->get_tag( $a, 'wp:author_email' ),
'author_display_name' => $this->get_tag( $a, 'wp:author_display_name' ),
'author_first_name' => $this->get_tag( $a, 'wp:author_first_name' ),
'author_last_name' => $this->get_tag( $a, 'wp:author_last_name' ),
);
}
function process_post( $post ) {
$post_id = $this->get_tag( $post, 'wp:post_id' );
$post_title = $this->get_tag( $post, 'title' );
$post_date = $this->get_tag( $post, 'wp:post_date' );
$post_date_gmt = $this->get_tag( $post, 'wp:post_date_gmt' );
$comment_status = $this->get_tag( $post, 'wp:comment_status' );
$ping_status = $this->get_tag( $post, 'wp:ping_status' );
$status = $this->get_tag( $post, 'wp:status' );
$post_name = $this->get_tag( $post, 'wp:post_name' );
$post_parent = $this->get_tag( $post, 'wp:post_parent' );
$menu_order = $this->get_tag( $post, 'wp:menu_order' );
$post_type = $this->get_tag( $post, 'wp:post_type' );
$post_password = $this->get_tag( $post, 'wp:post_password' );
$is_sticky = $this->get_tag( $post, 'wp:is_sticky' );
$guid = $this->get_tag( $post, 'guid' );
$post_author = $this->get_tag( $post, 'dc:creator' );
$post_excerpt = $this->get_tag( $post, 'excerpt:encoded' );
$post_excerpt = preg_replace_callback( '|<(/?[A-Z]+)|', array( &$this, '_normalize_tag' ), $post_excerpt );
$post_excerpt = str_replace( '<br>', '<br />', $post_excerpt );
$post_excerpt = str_replace( '<hr>', '<hr />', $post_excerpt );
$post_content = $this->get_tag( $post, 'content:encoded' );
$post_content = preg_replace_callback( '|<(/?[A-Z]+)|', array( &$this, '_normalize_tag' ), $post_content );
$post_content = str_replace( '<br>', '<br />', $post_content );
$post_content = str_replace( '<hr>', '<hr />', $post_content );
$postdata = compact( 'post_id', 'post_author', 'post_date', 'post_date_gmt', 'post_content', 'post_excerpt',
'post_title', 'status', 'post_name', 'comment_status', 'ping_status', 'guid', 'post_parent',
'menu_order', 'post_type', 'post_password', 'is_sticky'
);
$attachment_url = $this->get_tag( $post, 'wp:attachment_url' );
if ( $attachment_url )
$postdata['attachment_url'] = $attachment_url;
preg_match_all( '|<category domain="([^"]+?)" nicename="([^"]+?)">(.+?)</category>|is', $post, $terms, PREG_SET_ORDER );
foreach ( $terms as $t ) {
$post_terms[] = array(
'slug' => $t[2],
'domain' => $t[1],
'name' => str_replace( array( '<![CDATA[', ']]>' ), '', $t[3] ),
);
}
if ( ! empty( $post_terms ) ) $postdata['terms'] = $post_terms;
preg_match_all( '|<wp:comment>(.+?)</wp:comment>|is', $post, $comments );
$comments = $comments[1];
if ( $comments ) {
foreach ( $comments as $comment ) {
preg_match_all( '|<wp:commentmeta>(.+?)</wp:commentmeta>|is', $comment, $commentmeta );
$commentmeta = $commentmeta[1];
$c_meta = array();
foreach ( $commentmeta as $m ) {
$c_meta[] = array(
'key' => $this->get_tag( $m, 'wp:meta_key' ),
'value' => $this->get_tag( $m, 'wp:meta_value' ),
);
}
$post_comments[] = array(
'comment_id' => $this->get_tag( $comment, 'wp:comment_id' ),
'comment_author' => $this->get_tag( $comment, 'wp:comment_author' ),
'comment_author_email' => $this->get_tag( $comment, 'wp:comment_author_email' ),
'comment_author_IP' => $this->get_tag( $comment, 'wp:comment_author_IP' ),
'comment_author_url' => $this->get_tag( $comment, 'wp:comment_author_url' ),
'comment_date' => $this->get_tag( $comment, 'wp:comment_date' ),
'comment_date_gmt' => $this->get_tag( $comment, 'wp:comment_date_gmt' ),
'comment_content' => $this->get_tag( $comment, 'wp:comment_content' ),
'comment_approved' => $this->get_tag( $comment, 'wp:comment_approved' ),
'comment_type' => $this->get_tag( $comment, 'wp:comment_type' ),
'comment_parent' => $this->get_tag( $comment, 'wp:comment_parent' ),
'comment_user_id' => $this->get_tag( $comment, 'wp:comment_user_id' ),
'commentmeta' => $c_meta,
);
}
}
if ( ! empty( $post_comments ) ) $postdata['comments'] = $post_comments;
preg_match_all( '|<wp:postmeta>(.+?)</wp:postmeta>|is', $post, $postmeta );
$postmeta = $postmeta[1];
if ( $postmeta ) {
foreach ( $postmeta as $p ) {
$post_postmeta[] = array(
'key' => $this->get_tag( $p, 'wp:meta_key' ),
'value' => $this->get_tag( $p, 'wp:meta_value' ),
);
}
}
if ( ! empty( $post_postmeta ) ) $postdata['postmeta'] = $post_postmeta;
return $postdata;
}
function _normalize_tag( $matches ) {
return '<' . strtolower( $matches[1] );
}
function fopen( $filename, $mode = 'r' ) {
if ( $this->has_gzip )
return gzopen( $filename, $mode );
return fopen( $filename, $mode );
}
function feof( $fp ) {
if ( $this->has_gzip )
return gzeof( $fp );
return feof( $fp );
}
function fgets( $fp, $len = 8192 ) {
if ( $this->has_gzip )
return gzgets( $fp, $len );
return fgets( $fp, $len );
}
function fclose( $fp ) {
if ( $this->has_gzip )
return gzclose( $fp );
return fclose( $fp );
}
}
includes/class-content-importer.php 0000644 00000001423 14751055733 0013511 0 ustar 00 <?php
if ( ! class_exists( 'WP_Importer' ) ) {
defined( 'WP_LOAD_IMPORTERS' ) || define( 'WP_LOAD_IMPORTERS', true );
require ABSPATH . '/wp-admin/includes/class-wp-importer.php';
}
// Include required files, if not already present (via separate plugin).
if ( ! class_exists( 'WXR_Importer' ) ) {
require dirname( __FILE__ ) . '/class-wxr-importer.php';
}
class Soo_Demo_Content_Importer extends WXR_Importer {
public function __construct( $options = array() ) {
parent::__construct( $options );
// Set current user to $mapping variable.
// Fixes the [WARNING] Could not find the author for ... log warning messages.
$current_user_obj = wp_get_current_user();
$this->mapping['user_slug'][$current_user_obj->user_login] = $current_user_obj->ID;
}
}
includes/customizer-option.php 0000644 00000001122 14751055733 0012603 0 ustar 00 <?php
/**
* A class that extends WP_Customize_Setting so we can access
* the protected updated method when importing options.
*
* Used in the Customizer importer.
*/
if ( ! class_exists( 'WP_Customize_Setting' ) ) {
require_once ABSPATH . 'wp-includes/class-wp-customize-setting.php';
}
final class Soo_Demo_Customizer_Option extends WP_Customize_Setting {
/**
* Import an option value for this setting.
*
* @since 1.1.1
* @param mixed $value The option value.
* @return void
*/
public function import( $value ) {
$this->update( $value );
}
}
includes/class-logger-serversentevents.php 0000644 00000001474 14751055733 0015110 0 ustar 00 <?php
class WP_Importer_Logger_ServerSentEvents extends WP_Importer_Logger {
/**
* Logs with an arbitrary level.
*
* @param mixed $level
* @param string $message
* @param array $context
* @return null
*/
public function log( $level, $message, array $context = array() ) {
$data = compact( 'level', 'message' );
switch ( $level ) {
case 'emergency':
case 'alert':
case 'critical':
case 'error':
case 'warning':
case 'notice':
case 'info':
echo "event: log\n";
echo 'data: ' . wp_json_encode( $data ) . "\n\n";
flush();
break;
case 'debug':
if ( defined( 'IMPORT_DEBUG' ) && IMPORT_DEBUG ) {
echo "event: log\n";
echo 'data: ' . wp_json_encode( $data ) . "\n\n";
flush();
break;
}
break;
}
}
}
soo-demo-importer.php 0000644 00000047724 14751055733 0010666 0 ustar 00 <?php
/**
* Plugin Name: One Click Import Demo Content
* Plugin URI: http://oceanthemes.net/plugin/soo-demo-importer
* Description: One click import demo content
* Author: OceanThemes
* Author URI: http://oceanthemes.net/
* Version: 1.0
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Main class for importing demo content
*
* @version 1.0.0
*/
class Soo_Demo_Importer {
/**
* Demo data configuration
*
* @var array
*/
public $data;
/**
* Construction function
* Add new submenu under Appearance menu
*/
public function __construct() {
$this->data = apply_filters( 'soo_demo_packages', array() );
add_action( 'admin_menu', array( $this, 'menu' ) );
add_action( 'admin_enqueue_scripts', array( $this, 'scripts' ) );
add_action( 'wp_ajax_soodi_download_file', array( $this, 'ajax_download_file' ) );
add_action( 'wp_ajax_soodi_import', array( $this, 'ajax_import' ) );
add_action( 'wp_ajax_soodi_config_theme', array( $this, 'ajax_config_theme' ) );
add_action( 'wp_ajax_soodi_get_images', array( $this, 'ajax_get_images' ) );
add_action( 'wp_ajax_soodi_generate_image', array( $this, 'ajax_generate_image' ) );
}
/**
* Add new menu under Appearance menu
*/
public function menu() {
add_theme_page(
esc_html__( 'Import Demo Data', 'soodi' ),
esc_html__( 'Import Demo Data', 'soodi' ),
'edit_theme_options',
'import-demo-content',
array( $this, 'page' )
);
}
/**
* Load scripts
*/
public function scripts( $hook ) {
if ( 'appearance_page_import-demo-content' != $hook ) {
return;
}
wp_enqueue_style( 'soo-import', plugins_url( 'assets/css/soo-import.css', __FILE__ ) );
wp_enqueue_script( 'soo-import', plugins_url( 'assets/js/soo-import.js', __FILE__ ), array( 'jquery' ), '1.0.0', true );
}
/**
* Admin page for importing demo content
*/
public function page() {
if ( isset( $_GET['step'] ) ) {
$this->import_demo_page();
} else {
$this->select_demo_page();
}
}
/**
* HTML for select demo page
*/
private function select_demo_page() {
?>
<div class="wrap">
<h2><?php echo esc_html( get_admin_page_title() ); ?></h2>
<div class="updated notice is-dismissible">
<p><?php _e( 'Before start importing, you have to install all required plugins and other plugins that you want to use.', 'soodi' ) ?></p>
<button type="button" class="notice-dismiss"><span class="screen-reader-text"><?php _e( 'Dismiss this notice.', 'soodi' ) ?></span></button>
</div>
<div class="demos-container">
<p class="description"><?php _e( 'It usally take few minutes to finish. Please be patient.', 'soodi' ) ?></p>
<?php foreach( $this->data as $demo => $data ) : ?>
<form action="<?php echo esc_url( add_query_arg( array( 'step' => 2 ) ) ) ?>" method="post" class="demo-selector">
<?php wp_nonce_field( 'soo_import_demo' ); ?>
<input type="hidden" name="demo" value="<?php echo esc_attr( $demo ) ?>">
<div class="demo-image">
<img src="<?php echo esc_url( $data['preview'] ) ?>">
</div>
<div class="demo-selector-tools">
<div class="demo-install-progress"></div>
<div class="demo-install-actions">
<input type="submit" class="button button-primary" value="<?php _e( 'Install', 'soodi' ) ?>">
<h2 class="demo-title"><?php echo $data['name'] ?></h2>
<div class="clear"></div>
</div>
</div>
</form>
<?php endforeach; ?>
</div>
</div>
<?php
}
/**
* HTML for import demo page
*/
private function import_demo_page() {
if ( ! check_admin_referer( 'soo_import_demo' ) ) {
printf(
'<div class="notice warning"><p>%s <a href="%s">%s</a></p></div>',
esc_html__( 'Are you sure want to do this?', 'soodi' ),
remove_query_arg( 'step' ),
esc_html__( 'Please try again', 'soodi' )
);
return;
}
$demo = $_POST['demo'];
if ( ! array_key_exists( $demo, $this->data ) ) {
printf(
'<div class="notice warning"><p>%s <a href="%s">%s</a></p></div>',
esc_html__( 'This import is invalid.', 'soodi' ),
remove_query_arg( 'step' ),
esc_html__( 'Please try again', 'soodi' )
);
return;
}
?>
<div class="wrap">
<h2><?php echo esc_html( get_admin_page_title() ); ?></h2>
<form id="soo-demo-importer" class="hidden" method="post">
<input type="hidden" name="demo" value="<?php echo esc_attr( $_POST['demo'] ) ?>">
<?php wp_nonce_field( 'soo_demo_import' ) ?>
</form>
<div id="soo-demo-import-progress" class="notice warning notice-warning">
<p>
<span class="spinner is-active"></span>
<span class="text"><?php esc_html_e( 'Starting...', 'soodi' ) ?></span>
</p>
</div>
<div id="soo-demo-import-log" class="soo-demo-log hidden"></div>
</div>
<?php
}
/**
* Import content
* Import posts, pages, menus, custom post types
*
* @param string $file The exported file's name
*/
public function import_content( $file ) {
// Disable import of authors.
add_filter( 'wxr_importer.pre_process.user', '__return_false' );
// Disables generation of multiple image sizes (thumbnails) in the content import step.
add_filter( 'intermediate_image_sizes_advanced', '__return_null' );
if ( ! file_exists( $file ) ) {
return false;
}
// Import content.
if ( ! class_exists( 'Soo_Demo_Content_Importer' ) ) {
require_once plugin_dir_path( __FILE__ ) . '/includes/class-content-importer.php';
}
do_action( 'soo_before_import_content', $file );
$importer = new Soo_Demo_Content_Importer( array(
'fetch_attachments' => true
) );
if ( ! class_exists( 'WP_Importer_Logger' ) ) {
require_once plugin_dir_path( __FILE__ ) . '/includes/class-logger.php';
}
if ( ! class_exists( 'WP_Importer_Logger_ServerSentEvents' ) ) {
require_once plugin_dir_path( __FILE__ ) . '/includes/class-logger-serversentevents.php';
}
$logger = new WP_Importer_Logger_ServerSentEvents();
$err = $importer->set_logger( $logger );
return $importer->import( $file );
}
/**
* Import theme options
*
* @param string $file The exported file's name
*/
public function import_customizer( $file ) {
if ( ! file_exists( $file ) ) {
return false;
}
if( ! class_exists( 'Soo_Demo_Customizer_Importer' ) ) {
require_once plugin_dir_path( __FILE__ ) . 'includes/customizer-importer.php';
}
do_action( 'soo_before_import_customizer', $file );
// Disables generation of multiple image sizes (thumbnails) in the content import step.
add_filter( 'intermediate_image_sizes_advanced', '__return_null' );
$import = new Soo_Demo_Customizer_Importer();
$import->download_images = true;
$import->import( $file );
return true;
}
/**
* Import widgets
*
* @param string $file The exported file's name
*/
function import_widgets( $file ) {
if ( ! file_exists( $file ) ) {
return false;
}
if ( ! class_exists( 'Soo_Demo_Widgets_Importer') ) {
require_once plugin_dir_path( __FILE__ ) . 'includes/widgets-importer.php';
}
do_action( 'soo_before_import_customizer', $file );
$data = json_decode( file_get_contents( $file ) );
$importer = new Soo_Demo_Widgets_Importer();
$importer->import( $data );
return true;
}
/**
* Import exported revolution sliders
*/
public function import_sliders() {
if ( ! class_exists( 'RevSlider' ) ) {
return new WP_Error( 'plugin-not-installed', esc_html__( 'Revolution Slider plugin is not installed', 'soodi' ) );
}
$path = $this->get_demo_path();
$sliders_zip = $path . '/sliders.zip';
if ( empty( $sliders_zip ) ) {
return new WP_Error( 'import-sliders', esc_html__( 'There is no slider to import', 'soodi' ) );
}
$unzipfile = unzip_file( $sliders_zip, $path );
if( is_wp_error( $unzipfile ) ) {
define('FS_METHOD', 'direct'); //lets try direct.
WP_Filesystem();
$unzipfile = unzip_file( $sliders_zip, $path );
@unlink( $sliders_zip );
}
$files = scandir( $path . '/sliders/' );
if ( empty( $files ) ) {
return new WP_Error( 'import-sliders', esc_html__( 'There is no slider to import', 'soodi' ) );
}
$slider = new RevSlider();
$this->emit_sse_message( array(
'action' => 'updateTotal',
'type' => 'slider',
'delta' => count( $files ) - 2,
));
foreach( $files as $file ) {
if ( $file == '.' || $file == '..' ) {
continue;
}
$file = $path . '/sliders/' . $file;
$response = $slider->importSliderFromPost( true, true, $file );
$this->emit_sse_message( array(
'action' => 'updateDelta',
'type' => 'slider',
'delta' => $response,
));
unlink( $file );
}
}
/**
* Import menu locations
*
* @param string $file The exported file's name
*/
public function setup_menus( $demo ) {
$demo = $this->data[$demo];
if ( ! isset( $demo['menus'] ) ) {
return;
}
$data = $demo['menus'];
$menus = wp_get_nav_menus();
$locations = array();
foreach( $data as $key => $val ) {
foreach( $menus as $menu ) {
if( $val && $menu->slug == $val ) {
$locations[$key] = absint( $menu->term_id );
}
}
}
set_theme_mod( 'nav_menu_locations', $locations );
}
/**
* Setup pages
*/
public function setup_pages( $demo ) {
$demo = $this->data[$demo];
if ( ! isset( $demo['pages'] ) ) {
return;
}
// Front Page
if ( isset( $demo['pages']['front_page'] ) ) {
$home = get_page_by_title( $demo['pages']['front_page'] );
if ( $home ) {
update_option( 'show_on_front', 'page' );
update_option( 'page_on_front', $home->ID );
}
}
// Blog Page
if ( isset( $demo['pages']['blog'] ) ) {
$blog = get_page_by_title( $demo['pages']['blog'] );
if ( $blog ) {
update_option( 'page_for_posts', $blog->ID );
}
}
// WooCommerce Pages
if ( class_exists( 'WooCommerce' ) ) {
// Shop page
if ( isset( $demo['pages']['shop'] ) && ! get_option( 'woocommerce_shop_page_id' ) ) {
$shop = get_page_by_title( $demo['pages']['shop'] );
if ( $shop ) {
update_option( 'woocommerce_shop_page_id', $shop->ID );
}
}
// Cart page
if ( isset( $demo['pages']['cart'] ) && ! get_option( 'woocommerce_cart_page_id' ) ) {
$cart = get_page_by_title( $demo['pages']['cart'] );
if ( $cart ) {
update_option( 'woocommerce_cart_page_id', $cart->ID );
}
}
// Checkout page
if ( isset( $demo['pages']['checkout'] ) && ! get_option( 'woocommerce_checkout_page_id' ) ) {
$checkout = get_page_by_title( $demo['pages']['checkout'] );
if ( $checkout ) {
update_option( 'woocommerce_checkout_page_id', $checkout->ID );
}
}
// Myaccount page
if ( isset( $demo['pages']['my_account'] ) && ! get_option( 'woocommerce_myaccount_page_id' ) ) {
$account = get_page_by_title( $demo['pages']['my_account'] );
if ( $account ) {
update_option( 'woocommerce_myaccount_page_id', $account->ID );
}
}
}
flush_rewrite_rules();
}
/**
* Update options
*
* @param int $demo
*/
public function update_options( $demo ) {
$demo = $this->data[$demo];
if ( empty( $demo['options'] ) ) {
return;
}
foreach ( $demo['options'] as $option => $value) {
update_option( $option, $value );
}
}
/**
* Generate image sizes
* @param int $id
*/
public function generate_image( $id ) {
$fullsizepath = get_attached_file( $id );
if ( false === $fullsizepath || ! file_exists( $fullsizepath ) ) {
return false;
}
$metadata = wp_generate_attachment_metadata( $id, $fullsizepath );
if ( ! $metadata || is_wp_error( $metadata ) ) {
return false;
}
// If this fails, then it just means that nothing was changed (old value == new value)
wp_update_attachment_metadata( $id, $metadata );
return true;
}
/**
* Ajax function to import demo content
*/
public function ajax_import() {
if ( ! wp_verify_nonce( $_GET['_wpnonce'], 'soo_demo_import' ) ) {
wp_send_json_error( esc_html__( 'Verifing failed', 'soodi' ) );
eixt;
}
// Turn off PHP output compression
$previous = error_reporting( error_reporting() ^ E_WARNING );
ini_set( 'output_buffering', 'off' );
ini_set( 'zlib.output_compression', false );
error_reporting( $previous );
if ( $GLOBALS['is_nginx'] ) {
// Setting this header instructs Nginx to disable fastcgi_buffering
// and disable gzip for this request.
header( 'X-Accel-Buffering: no' );
header( 'Content-Encoding: none' );
}
// Start the event stream.
header( 'Content-Type: text/event-stream' );
set_time_limit( 0 );
// Ensure we're not buffered.
wp_ob_end_flush_all();
flush();
$type = $_GET['type'];
$dir = $this->get_demo_path();
switch ( $type ) {
case 'content':
$file = $dir . '/demo-content.xml';
$result = $this->import_content( $file );
break;
case 'customizer':
$file = $dir . '/customizer.dat';
$result = $this->import_customizer( $file );
break;
case 'widgets':
$file = $dir . '/widgets.wie';
$result = $this->import_widgets( $file );
break;
case 'sliders':
$result = $this->import_sliders();
break;
}
// Let the browser know we're done.
$complete = array(
'action' => 'complete',
'error' => false,
);
if ( is_wp_error( $result ) ) {
$complete['error'] = $result->get_error_message();
}
@unlink( $file );
$this->emit_sse_message( $complete );
exit;
}
/**
* Ajax function to download file
*/
public function ajax_download_file() {
if ( ! wp_verify_nonce( $_GET['_wpnonce'], 'soo_demo_import' ) ) {
wp_send_json_error( esc_html__( 'Verifing failed', 'soodi' ) );
eixt;
}
$demo = $_GET['demo'];
$demo = $this->data[$demo];
$type = $_GET['type'];
if ( ! isset( $demo[$type] ) ) {
wp_send_json_error( esc_html__( 'This demo dose not need', 'soodi' ) . " $type" );
eixt;
}
if ( empty( $demo[$type] ) ) {
wp_send_json_error( esc_html__( 'File does not exists', 'soodi' ) );
eixt;
}
@set_time_limit(0);
$file = $this->download_file( $demo[$type] );
if ( is_wp_error( $file ) ) {
wp_send_json_error( $file->get_error_message() );
exit;
}
wp_send_json_success();
}
/**
* Ajax function to setup front page and blog page
*/
public function ajax_config_theme() {
if ( ! wp_verify_nonce( $_GET['_wpnonce'], 'soo_demo_import' ) ) {
wp_send_json_error( esc_html__( 'Verifing failed', 'soodi' ) );
eixt;
}
$demo = $_GET['demo'];
// Setup pages
$this->setup_pages( $demo );
// Setup menu locations
$this->setup_menus( $demo );
// Update options
$this->update_options( $demo );
wp_send_json_success( esc_html__( 'Finish setting up front page and blog page.', 'soodi' ) );
}
/**
* Ajax function to get all image ids
*/
public function ajax_get_images() {
if ( ! wp_verify_nonce( $_GET['_wpnonce'], 'soo_demo_import' ) ) {
wp_send_json_error( esc_html__( 'Verifing failed', 'soodi' ) );
eixt;
}
global $wpdb;
$images = $wpdb->get_results( "SELECT ID FROM $wpdb->posts WHERE post_type = 'attachment' AND post_mime_type LIKE 'image/%' ORDER BY ID DESC" );
$ids = array();
if ( $images ) {
foreach ( $images as $image ) {
$ids[] = $image->ID;
}
}
wp_send_json_success( $ids );
}
/**
* Ajax function to generate a single image
*/
public function ajax_generate_image() {
if ( ! wp_verify_nonce( $_GET['_wpnonce'], 'soo_demo_import' ) ) {
wp_send_json_error( esc_html__( 'Verifing failed', 'soodi' ) );
eixt;
}
$id = absint( $_REQUEST['id'] );
@set_time_limit( 900 ); // 5 minutes per image should be PLENTY
$result = $this->generate_image( $id );
if ( ! $result ) {
wp_send_json_error( esc_html__( 'Failed to generate image ID:', 'soodi' ) . " $id" );
} else {
wp_send_json_success( sprintf( esc_html__( 'Generated image ID %s successfully', 'soodi' ), $id ) );
}
}
/**
* Download file from URL
*
* @param staring $file_url
* @return string Downloaded file path
*/
protected function download_file( $file_url ) {
$filename = basename( $file_url );
$path = $this->get_demo_path();
$file = $path . '/' . $filename;
wp_mkdir_p( $path );
$ifp = @fopen( $file, 'wb' );
if ( ! $ifp ) {
return new WP_Error( 'import_file_error', sprintf( __( 'Could not write file %s' ), $file ) );
}
@fwrite( $ifp, 0 );
fclose( $ifp );
clearstatcache();
// Set correct file permissions
$stat = @stat( dirname( $file ) );
$perms = $stat['mode'] & 0007777;
$perms = $perms & 0000666;
@chmod( $file, $perms );
clearstatcache();
$response = wp_remote_get( $file_url, array(
'stream' => true,
'filename' => $file,
'timeout' => 500,
) );
// request failed
if ( is_wp_error( $response ) ) {
@unlink( $file );
return $response;
}
$code = (int) wp_remote_retrieve_response_code( $response );
// make sure the fetch was successful
if ( $code !== 200 ) {
@unlink( $file );
return new WP_Error(
'import_file_error',
sprintf(
esc_html__( 'Remote server returned %1$d %2$s for %3$s', 'soodi' ),
$code,
get_status_header_desc( $code ),
$url
)
);
}
if ( 0 == filesize( $file ) ) {
@unlink( $file );
return new WP_Error( 'import_file_error', esc_html__( 'Zero size file downloaded', 'soodi' ) );
}
return $file;
}
/**
* Emit a Server-Sent Events message.
*
* @param mixed $data Data to be JSON-encoded and sent in the message.
*/
protected function emit_sse_message( $data ) {
echo "event: message\n";
echo 'data: ' . wp_json_encode( $data ) . "\n\n";
// Extra padding.
echo ':' . str_repeat( ' ', 2048 ) . "\n\n";
flush();
}
/**
* Get the path to demo directory
*
* @return string
*/
private function get_demo_path() {
$upload_dir = wp_upload_dir();
$dir = path_join( $upload_dir['basedir'], 'demo_files' );
return $dir;
}
}
if ( version_compare( phpversion(), '5.3.2', '<' ) ) {
add_action( 'admin_notices', function() {
$message = sprintf( __( 'The <strong>Soo Demo Importer</strong> plugin requires <strong>PHP 5.3.2+</strong> to run properly. Please contact your hosting company and ask them to update the PHP version of your site to at least PHP 5.3.2.<br> Your current version of PHP: <strong>%s</strong>', 'soodi' ), phpversion() );
printf( '<div class="notice notice-error"><p>%s</p></div>', wp_kses_post( $message ) );
} );
} else {
add_action( 'init', function() {
new Soo_Demo_Importer();
} );
}
add_filter( 'soo_demo_packages', function() {
return array(
array(
'name' => 'Sober',
'preview' => 'http://localhost/demo/images/a1.jpg',
'content' => 'http://localhost/demo/demo-content.xml',
'customizer' => 'http://localhost/demo/customizer.dat',
'widgets' => 'http://localhost/demo/widgets.wie',
'sliders' => 'http://localhost/demo/sliders.zip',
'pages' => array(
'front_page' => 'Home Page 1',
'blog' => 'Blog',
'shop' => 'Shop',
'cart' => 'Cart',
'checkout' => 'Checkout',
'my_account' => 'My Account',
),
'menus' => array(
'primary' => 'primary-menu',
'secondary' => 'secondary-menu',
'footer' => 'footer-menu',
'topbar' => 'topbar-menu',
),
'options' => array(
'shop_catalog_image_size' => array(
'width' => 300,
'height' => 300,
'crop' => 1,
),
'shop_single_image_size' => array(
'width' => 600,
'height' => 600,
'crop' => 1,
),
'shop_thumbnail_image_size' => array(
'width' => 180,
'height' => 180,
'crop' => 1,
),
),
),
);
} );