Version Description
Release Date - 25 March 2016
Download this release
Release Info
Developer | capuderg |
Plugin | One Click Demo Import |
Version | 1.0.0 |
Comparing to | |
See all releases |
Version 1.0.0
- assets/css/main.css +44 -0
- assets/js/main.js +49 -0
- inc/class-ocdi-helpers.php +509 -0
- inc/class-ocdi-importer.php +55 -0
- inc/class-ocdi-logger.php +64 -0
- inc/class-ocdi-widget-importer.php +304 -0
- languages/one-click-demo-import.pot +258 -0
- one-click-demo-import.php +447 -0
- readme.txt +95 -0
- vendor/humanmade/WordPress-Importer/class-logger-cli.php +43 -0
- vendor/humanmade/WordPress-Importer/class-logger.php +136 -0
- vendor/humanmade/WordPress-Importer/class-wxr-importer.php +2067 -0
assets/css/main.css
ADDED
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/* Plugin title */
|
2 |
+
.ocdi__title span {
|
3 |
+
line-height: 30px;
|
4 |
+
}
|
5 |
+
|
6 |
+
/* AJAX loader */
|
7 |
+
.ocdi__ajax-loader {
|
8 |
+
font-width: bold;
|
9 |
+
font-size: 1.5em;
|
10 |
+
}
|
11 |
+
|
12 |
+
.ocdi__ajax-loader .spinner {
|
13 |
+
display: inline-block;
|
14 |
+
float: none;
|
15 |
+
visibility: visible;
|
16 |
+
}
|
17 |
+
|
18 |
+
/* Plugin intro text */
|
19 |
+
.ocdi__intro-text {
|
20 |
+
background-color: #f5fafd;
|
21 |
+
margin:10px 0;
|
22 |
+
padding: 10px;
|
23 |
+
color: #0C518F;
|
24 |
+
border: 3px solid #cae0f3;
|
25 |
+
clear: both;
|
26 |
+
width: 90%;
|
27 |
+
line-height: 18px;
|
28 |
+
}
|
29 |
+
|
30 |
+
.ocdi__intro-text ul {
|
31 |
+
padding-left: 20px;
|
32 |
+
list-style-position: inside;
|
33 |
+
list-style-type: square;
|
34 |
+
}
|
35 |
+
|
36 |
+
/* WP info notice */
|
37 |
+
.notice-info {
|
38 |
+
border-left-color: #58acfa;
|
39 |
+
}
|
40 |
+
|
41 |
+
/* WP warning notice */
|
42 |
+
.notice-warning {
|
43 |
+
border-left-color: #ed9232;
|
44 |
+
}
|
assets/js/main.js
ADDED
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
jQuery( function ( $ ) {
|
2 |
+
'use strict';
|
3 |
+
|
4 |
+
$( '.js-ocdi-import-data' ).on( 'click', function () {
|
5 |
+
|
6 |
+
// Reset response div content.
|
7 |
+
$( '.js-ocdi-ajax-response' ).empty();
|
8 |
+
|
9 |
+
// Prepare data for the AJAX call
|
10 |
+
var data = new FormData();
|
11 |
+
data.append( 'action', 'ocdi_import_demo_data' );
|
12 |
+
data.append( 'security', ocdi.ajax_nonce );
|
13 |
+
data.append( 'selected', $( '#ocdi__demo-import-files' ).val() );
|
14 |
+
if ( $('#ocdi__content-file-upload').length ) {
|
15 |
+
data.append( 'content_file', $('#ocdi__content-file-upload')[0].files[0] );
|
16 |
+
}
|
17 |
+
if ( $('#ocdi__widget-file-upload').length ) {
|
18 |
+
data.append( 'widget_file', $('#ocdi__widget-file-upload')[0].files[0] );
|
19 |
+
}
|
20 |
+
|
21 |
+
// AJAX call.
|
22 |
+
$.ajax({
|
23 |
+
method: 'POST',
|
24 |
+
url: ocdi.ajax_url,
|
25 |
+
data: data,
|
26 |
+
contentType: false,
|
27 |
+
processData: false,
|
28 |
+
beforeSend: function() {
|
29 |
+
$( '.js-ocdi-import-data' ).after( '<p class="js-ocdi-ajax-loader ocdi__ajax-loader"><span class="spinner"></span>' + ocdi.loader_text + '</p>' );
|
30 |
+
},
|
31 |
+
complete: function() {
|
32 |
+
$( '.js-ocdi-ajax-loader' ).hide( 500, function(){ $( '.js-ocdi-ajax-loader' ).remove(); } );
|
33 |
+
}
|
34 |
+
})
|
35 |
+
.done( function( response ) {
|
36 |
+
if ( 'undefined' !== typeof response.message ) {
|
37 |
+
$( '.js-ocdi-ajax-response' ).append( '<p>' + response.message + '</p>' );
|
38 |
+
}
|
39 |
+
else {
|
40 |
+
$( '.js-ocdi-ajax-response' ).append( '<div class="error below-h2"><p>' + response + '</p></div>' );
|
41 |
+
}
|
42 |
+
})
|
43 |
+
.fail( function( error ) {
|
44 |
+
$( '.js-ocdi-ajax-response' ).append( '<div class="error below-h2"> Error: ' + error.statusText + ' (' + error.status + ')' + '</div>' );
|
45 |
+
});
|
46 |
+
|
47 |
+
});
|
48 |
+
|
49 |
+
});
|
inc/class-ocdi-helpers.php
ADDED
@@ -0,0 +1,509 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Static functions used in the OCDI plugin.
|
4 |
+
*
|
5 |
+
* @package ocdi
|
6 |
+
*/
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Class with static helper functions.
|
10 |
+
*/
|
11 |
+
class OCDI_Helpers {
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Filter through the array of import files and get rid of those who do not comply.
|
15 |
+
*
|
16 |
+
* @param array $import_files list of arrays with import file details.
|
17 |
+
* @return array list of filtered arrays.
|
18 |
+
*/
|
19 |
+
public static function validate_import_file_info( $import_files ) {
|
20 |
+
$filtered_import_file_info = array();
|
21 |
+
|
22 |
+
foreach ( $import_files as $import_file ) {
|
23 |
+
if ( self::is_import_file_info_format_correct( $import_file ) ) {
|
24 |
+
$filtered_import_file_info[] = $import_file;
|
25 |
+
}
|
26 |
+
}
|
27 |
+
|
28 |
+
return $filtered_import_file_info;
|
29 |
+
}
|
30 |
+
|
31 |
+
|
32 |
+
/**
|
33 |
+
* Helper function: a simple check for valid import file format.
|
34 |
+
*
|
35 |
+
* @param array $import_file_info array with import file details.
|
36 |
+
* @return boolean
|
37 |
+
*/
|
38 |
+
private static function is_import_file_info_format_correct( $import_file_info ) {
|
39 |
+
if ( empty( $import_file_info['import_file_url'] ) || empty( $import_file_info['import_file_name'] ) ) {
|
40 |
+
return false;
|
41 |
+
}
|
42 |
+
|
43 |
+
return true;
|
44 |
+
}
|
45 |
+
|
46 |
+
|
47 |
+
/**
|
48 |
+
* Download import files. Content .xml and widgets .wie|.json files.
|
49 |
+
*
|
50 |
+
* @param array $import_file_info array with import file details.
|
51 |
+
* @param string $start_date string of date and time.
|
52 |
+
* @return array|WP_Error array of paths to the downloaded files or WP_Error object with error message.
|
53 |
+
*/
|
54 |
+
public static function download_import_files( $import_file_info, $start_date = '' ) {
|
55 |
+
|
56 |
+
$downloaded_files = array();
|
57 |
+
|
58 |
+
// Retrieve demo data content from the URL.
|
59 |
+
$demo_import_content = self::get_content_from_url( $import_file_info['import_file_url'], $import_file_info['import_file_name'] );
|
60 |
+
|
61 |
+
// Return from this function if there was an error.
|
62 |
+
if ( is_wp_error( $demo_import_content ) ) {
|
63 |
+
return $demo_import_content;
|
64 |
+
}
|
65 |
+
|
66 |
+
// Setup filename path to save the data content.
|
67 |
+
$upload_dir = wp_upload_dir();
|
68 |
+
$upload_path = apply_filters( 'pt-ocdi/upload_file_path', trailingslashit( $upload_dir['path'] ) );
|
69 |
+
$demo_import_file_path = $upload_path . apply_filters( 'pt-ocdi/downloaded_import_file_prefix', 'demo-import-file_' ) . $start_date . apply_filters( 'pt-ocdi/downloaded_import_file_suffix_and_file_extension', '.xml' );
|
70 |
+
|
71 |
+
// Write data content to the file and return the file path on successful write.
|
72 |
+
$downloaded_files['content'] = self::write_to_file( $demo_import_content, $demo_import_file_path );
|
73 |
+
|
74 |
+
// Return from this function if there was an error.
|
75 |
+
if ( is_wp_error( $downloaded_files['content'] ) ) {
|
76 |
+
return $downloaded_files['content'];
|
77 |
+
}
|
78 |
+
|
79 |
+
// Get widgets file as well. If defined!
|
80 |
+
if ( ! empty( $import_file_info['import_widget_file_url'] ) ) {
|
81 |
+
|
82 |
+
// Retrieve widget content from the URL.
|
83 |
+
$demo_import_widgets_content = self::get_content_from_url( $import_file_info['import_widget_file_url'], $import_file_info['import_file_name'] );
|
84 |
+
|
85 |
+
// Return from this function if there was an error.
|
86 |
+
if ( is_wp_error( $demo_import_widgets_content ) ) {
|
87 |
+
return $demo_import_widgets_content;
|
88 |
+
}
|
89 |
+
|
90 |
+
// Setup filename path to save the widget content.
|
91 |
+
$import_widgets_file_path = $upload_path . apply_filters( 'pt-ocdi/downloaded_import_file_prefix', 'demo-import-file_' ) . date( 'Y-m-d__H-i-s' ) . apply_filters( 'pt-ocdi/downloaded_widgets_file_suffix_and_file_extension', '.json' );
|
92 |
+
|
93 |
+
// Write widget content to the file and return the file path on successful write.
|
94 |
+
$downloaded_files['widgets'] = self::write_to_file( $demo_import_widgets_content, $import_widgets_file_path );
|
95 |
+
|
96 |
+
// Return from this function if there was an error.
|
97 |
+
if ( is_wp_error( $downloaded_files['widgets'] ) ) {
|
98 |
+
return $downloaded_files['widgets'];
|
99 |
+
}
|
100 |
+
}
|
101 |
+
|
102 |
+
return $downloaded_files;
|
103 |
+
}
|
104 |
+
|
105 |
+
|
106 |
+
/**
|
107 |
+
* Helper function: get content from an url.
|
108 |
+
*
|
109 |
+
* @param string $url URL to the content file.
|
110 |
+
* @param string $file_name optional, name of the file (used in the error reports).
|
111 |
+
* @return string|WP_Error, content from the URL or WP_Error object with error message
|
112 |
+
*/
|
113 |
+
private static function get_content_from_url( $url, $file_name = 'Import file' ) {
|
114 |
+
|
115 |
+
// Test if the URL to the file is defined.
|
116 |
+
if ( empty( $url ) ) {
|
117 |
+
return new WP_Error(
|
118 |
+
'url_not_defined',
|
119 |
+
sprintf(
|
120 |
+
__( 'URL for %s%s%s file is not defined!', 'pt-ocdi' ),
|
121 |
+
'<strong>',
|
122 |
+
$file_name,
|
123 |
+
'</strong>'
|
124 |
+
)
|
125 |
+
);
|
126 |
+
}
|
127 |
+
|
128 |
+
// Get file content from the server.
|
129 |
+
$response = wp_remote_get(
|
130 |
+
$url,
|
131 |
+
array( 'timeout' => apply_filters( 'pt-ocdi/timeout_for_downloading_import_file', 20 ) )
|
132 |
+
);
|
133 |
+
|
134 |
+
if ( is_wp_error( $response ) || 200 !== $response['response']['code'] ) {
|
135 |
+
|
136 |
+
// Collect the right format of error data (array or WP_Error).
|
137 |
+
$response_error = self::get_error_from_response( $response );
|
138 |
+
|
139 |
+
return new WP_Error(
|
140 |
+
'file_fetching_error',
|
141 |
+
sprintf(
|
142 |
+
__( 'An error occurred while fetching %s%s%s file from the server!%sReason: %s - %s.', 'pt-ocdi' ),
|
143 |
+
'<strong>',
|
144 |
+
$file_name,
|
145 |
+
'</strong>',
|
146 |
+
'<br>',
|
147 |
+
$response_error['error_code'],
|
148 |
+
$response_error['error_message']
|
149 |
+
) . '<br>' .
|
150 |
+
apply_filters( 'pt-ocdi/message_after_file_fetching_error', '' )
|
151 |
+
);
|
152 |
+
}
|
153 |
+
|
154 |
+
// Return content retrieved from the URL.
|
155 |
+
return wp_remote_retrieve_body( $response );
|
156 |
+
}
|
157 |
+
|
158 |
+
|
159 |
+
/**
|
160 |
+
* Write content to a file.
|
161 |
+
*
|
162 |
+
* @param string $content content to be saved to the file.
|
163 |
+
* @param string $file_path file path where the content should be saved.
|
164 |
+
* @return string|WP_Error path to the saved file or WP_Error object with error message.
|
165 |
+
*/
|
166 |
+
public static function write_to_file( $content, $file_path ) {
|
167 |
+
|
168 |
+
// Check if the file-system method is 'direct', if not display an error.
|
169 |
+
if ( ! 'direct' === get_filesystem_method() ) {
|
170 |
+
return self::return_direct_filesystem_error();
|
171 |
+
}
|
172 |
+
|
173 |
+
// Verify WP file-system credentials.
|
174 |
+
$verified_credentials = self::check_wp_filesystem_credentials();
|
175 |
+
|
176 |
+
if ( is_wp_error( $verified_credentials ) ) {
|
177 |
+
return $verified_credentials;
|
178 |
+
}
|
179 |
+
|
180 |
+
// By this point, the $wp_filesystem global should be working, so let's use it to create a file.
|
181 |
+
global $wp_filesystem;
|
182 |
+
|
183 |
+
if ( ! $wp_filesystem->put_contents( $file_path, $content ) ) {
|
184 |
+
return new WP_Error(
|
185 |
+
'failed_writing_file_to_server',
|
186 |
+
sprintf(
|
187 |
+
__( 'An error occurred while writing file to your server! Tried to write a file to: %s%s.', 'pt-ocdi' ),
|
188 |
+
'<br>',
|
189 |
+
$file_path
|
190 |
+
)
|
191 |
+
);
|
192 |
+
}
|
193 |
+
|
194 |
+
// Return the file path on successful file write.
|
195 |
+
return $file_path;
|
196 |
+
}
|
197 |
+
|
198 |
+
|
199 |
+
/**
|
200 |
+
* Append content to the file.
|
201 |
+
*
|
202 |
+
* @param string $content content to be saved to the file.
|
203 |
+
* @param string $file_path file path where the content should be saved.
|
204 |
+
* @param string $separator_text separates the existing content of the file with the new content.
|
205 |
+
* @return boolean|WP_Error, path to the saved file or WP_Error object with error message.
|
206 |
+
*/
|
207 |
+
public static function append_to_file( $content, $file_path, $separator_text = '' ) {
|
208 |
+
|
209 |
+
// Check if the file-system method is 'direct', if not display an error.
|
210 |
+
if ( ! 'direct' === get_filesystem_method() ) {
|
211 |
+
return self::return_direct_filesystem_error();
|
212 |
+
}
|
213 |
+
|
214 |
+
// Verify WP file-system credentials.
|
215 |
+
$verified_credentials = self::check_wp_filesystem_credentials();
|
216 |
+
|
217 |
+
if ( is_wp_error( $verified_credentials ) ) {
|
218 |
+
return $verified_credentials;
|
219 |
+
}
|
220 |
+
|
221 |
+
// By this point, the $wp_filesystem global should be working, so let's use it to create a file.
|
222 |
+
global $wp_filesystem;
|
223 |
+
|
224 |
+
$existing_data = $wp_filesystem->get_contents( $file_path );
|
225 |
+
|
226 |
+
// Style separator.
|
227 |
+
$separator = PHP_EOL . '---' . $separator_text . '---' . PHP_EOL;
|
228 |
+
|
229 |
+
if ( ! $wp_filesystem->put_contents( $file_path, $existing_data . $separator . $content . PHP_EOL ) ) {
|
230 |
+
return new WP_Error(
|
231 |
+
'failed_writing_file_to_server',
|
232 |
+
sprintf(
|
233 |
+
__( 'An error occurred while writing file to your server! Tried to write a file to: %s%s.', 'pt-ocdi' ),
|
234 |
+
'<br>',
|
235 |
+
$file_path
|
236 |
+
)
|
237 |
+
);
|
238 |
+
}
|
239 |
+
|
240 |
+
return true;
|
241 |
+
}
|
242 |
+
|
243 |
+
|
244 |
+
/**
|
245 |
+
* Get data from a file
|
246 |
+
*
|
247 |
+
* @param string $file_path file path where the content should be saved.
|
248 |
+
* @return string $data, content of the file or WP_Error object with error message.
|
249 |
+
*/
|
250 |
+
public static function data_from_file( $file_path ) {
|
251 |
+
|
252 |
+
// Check if the file-system method is 'direct', if not display an error.
|
253 |
+
if ( ! 'direct' === get_filesystem_method() ) {
|
254 |
+
return self::return_direct_filesystem_error();
|
255 |
+
}
|
256 |
+
|
257 |
+
// Verify WP file-system credentials.
|
258 |
+
$verified_credentials = self::check_wp_filesystem_credentials();
|
259 |
+
|
260 |
+
if ( is_wp_error( $verified_credentials ) ) {
|
261 |
+
return $verified_credentials;
|
262 |
+
}
|
263 |
+
|
264 |
+
// By this point, the $wp_filesystem global should be working, so let's use it to read a file.
|
265 |
+
global $wp_filesystem;
|
266 |
+
|
267 |
+
$data = $wp_filesystem->get_contents( $file_path );
|
268 |
+
|
269 |
+
if ( ! $data ) {
|
270 |
+
return new WP_Error(
|
271 |
+
'failed_reading_file_from_server',
|
272 |
+
sprintf(
|
273 |
+
__( 'An error occurred while reading a file from your server! Tried reading file from path: %s%s.', 'pt-ocdi' ),
|
274 |
+
'<br>',
|
275 |
+
$file_path
|
276 |
+
)
|
277 |
+
);
|
278 |
+
}
|
279 |
+
|
280 |
+
// Return the file data.
|
281 |
+
return $data;
|
282 |
+
}
|
283 |
+
|
284 |
+
|
285 |
+
/**
|
286 |
+
* Helper function: check for WP file-system credentials needed for reading and writing to a file.
|
287 |
+
*
|
288 |
+
* @return boolean|WP_Error
|
289 |
+
*/
|
290 |
+
private static function check_wp_filesystem_credentials() {
|
291 |
+
|
292 |
+
// Get user credentials for WP file-system API.
|
293 |
+
$demo_import_page_url = wp_nonce_url( 'themes.php?page=pt-one-click-demo-import', 'pt-one-click-demo-import' );
|
294 |
+
|
295 |
+
if ( false === ( $creds = request_filesystem_credentials( $demo_import_page_url, '', false, false, null ) ) ) {
|
296 |
+
return new WP_error(
|
297 |
+
'filesystem_credentials_could_not_be_retrieved',
|
298 |
+
__( 'An error occurred while retrieving reading/writing permissions to your server (could not retrieve WP filesystem credentials)!', 'pt-ocdi' )
|
299 |
+
);
|
300 |
+
}
|
301 |
+
|
302 |
+
// Now we have credentials, try to get the wp_filesystem running.
|
303 |
+
if ( ! WP_Filesystem( $creds ) ) {
|
304 |
+
return new WP_Error(
|
305 |
+
'wrong_login_credentials',
|
306 |
+
__( 'Your WordPress login credentials don\'t allow to use WP_Filesystem!', 'pt-ocdi' )
|
307 |
+
);
|
308 |
+
}
|
309 |
+
|
310 |
+
return true;
|
311 |
+
}
|
312 |
+
|
313 |
+
|
314 |
+
/**
|
315 |
+
* Helper function: return the "no direct access file-system" error.
|
316 |
+
*
|
317 |
+
* @return WP_Error
|
318 |
+
*/
|
319 |
+
private static function return_direct_filesystem_error() {
|
320 |
+
return new WP_Error(
|
321 |
+
'no_direct_file_access',
|
322 |
+
sprintf(
|
323 |
+
__( 'This WordPress page does not have %sdirect%s write file access. This plugin needs it in order to save the demo import xml file to the upload directory of your site. You can change this setting with these instructions: %s.', 'pt-ocdi' ),
|
324 |
+
'<strong>',
|
325 |
+
'</strong>',
|
326 |
+
'<a href="http://gregorcapuder.com/wordpress-how-to-set-direct-filesystem-method/" target="_blank">How to set <strong>direct</strong> filesystem method</a>'
|
327 |
+
)
|
328 |
+
);
|
329 |
+
}
|
330 |
+
|
331 |
+
|
332 |
+
/**
|
333 |
+
* Helper function: get the right format of response errors
|
334 |
+
*
|
335 |
+
* @param array|WP_Error $response array or WP_Error.
|
336 |
+
* @return array, with error code and error message.
|
337 |
+
*/
|
338 |
+
private static function get_error_from_response( $response ) {
|
339 |
+
$response_error = array();
|
340 |
+
|
341 |
+
if ( is_array( $response ) ) {
|
342 |
+
$response_error['error_code'] = $response['response']['code'];
|
343 |
+
$response_error['error_message'] = $response['response']['message'];
|
344 |
+
}
|
345 |
+
else {
|
346 |
+
$response_error['error_code'] = $response->get_error_code();
|
347 |
+
$response_error['error_message'] = $response->get_error_message();
|
348 |
+
}
|
349 |
+
|
350 |
+
return $response_error;
|
351 |
+
}
|
352 |
+
|
353 |
+
|
354 |
+
/**
|
355 |
+
* Get log file path
|
356 |
+
*
|
357 |
+
* @param string $start_date date|time|timestamp to use in the log filename.
|
358 |
+
* @return string, path to the log file
|
359 |
+
*/
|
360 |
+
public static function get_log_path( $start_date = '' ) {
|
361 |
+
|
362 |
+
$upload_dir = wp_upload_dir();
|
363 |
+
$upload_path = apply_filters( 'pt-ocdi/upload_file_path', trailingslashit( $upload_dir['path'] ) );
|
364 |
+
|
365 |
+
return $upload_path . apply_filters( 'pt-ocdi/log_file_prefix', 'log_file_' ) . $start_date . apply_filters( 'pt-ocdi/log_file_suffix_and_file_extension', '.txt' );
|
366 |
+
}
|
367 |
+
|
368 |
+
|
369 |
+
/**
|
370 |
+
* Get log file url
|
371 |
+
*
|
372 |
+
* @param string $log_path log path to use for the log filename.
|
373 |
+
* @return string, url to the log file.
|
374 |
+
*/
|
375 |
+
public static function get_log_url( $log_path ) {
|
376 |
+
|
377 |
+
$upload_dir = wp_upload_dir();
|
378 |
+
$upload_url = apply_filters( 'pt-ocdi/upload_file_url', trailingslashit( $upload_dir['url'] ) );
|
379 |
+
|
380 |
+
return $upload_url . basename( $log_path );
|
381 |
+
}
|
382 |
+
|
383 |
+
|
384 |
+
/**
|
385 |
+
* Check if the AJAX call is valid.
|
386 |
+
*/
|
387 |
+
public static function verify_ajax_call() {
|
388 |
+
|
389 |
+
check_ajax_referer( 'ocdi-ajax-verification', 'security' );
|
390 |
+
|
391 |
+
// Check if user has the WP capability to import data.
|
392 |
+
if ( ! current_user_can( 'import' ) ) {
|
393 |
+
wp_die(
|
394 |
+
sprintf(
|
395 |
+
__( '%sYour user role isn\'t high enough. You don\'t have permission to import demo data.%s', 'pt-ocdi' ),
|
396 |
+
'<div class="notice notice-error"><p>',
|
397 |
+
'</p></div>'
|
398 |
+
)
|
399 |
+
);
|
400 |
+
}
|
401 |
+
}
|
402 |
+
|
403 |
+
|
404 |
+
/**
|
405 |
+
* Process uploaded files and return the paths to these files.
|
406 |
+
*
|
407 |
+
* @param array $uploaded_files $_FILES array form an AJAX request.
|
408 |
+
* @param string $log_file_path path to the log file.
|
409 |
+
* @return array of paths to the content import and widget import files.
|
410 |
+
*/
|
411 |
+
public static function process_uploaded_files( $uploaded_files, $log_file_path ) {
|
412 |
+
|
413 |
+
// Variable holding the paths to the uploaded files.
|
414 |
+
$selected_import_files = array();
|
415 |
+
|
416 |
+
// Upload settings to disable form and type testing for AJAX uploads.
|
417 |
+
$upload_overrides = array(
|
418 |
+
'test_form' => false,
|
419 |
+
'test_type' => false,
|
420 |
+
);
|
421 |
+
|
422 |
+
// Handle demo content and widgets file upload.
|
423 |
+
$content_file_info = wp_handle_upload( $_FILES['content_file'], $upload_overrides );
|
424 |
+
$widget_file_info = wp_handle_upload( $_FILES['widget_file'], $upload_overrides );
|
425 |
+
|
426 |
+
if ( empty( $content_file_info['file'] ) || isset( $content_file_info['error'] ) ) {
|
427 |
+
|
428 |
+
// Write error to log file and send an AJAX response with the error.
|
429 |
+
self::log_error_and_send_ajax_response(
|
430 |
+
__( 'Please upload XML file for content import. If you want to import widgets only, please use Widget Importer & Exporter plugin.', 'pt-ocdi' ),
|
431 |
+
$log_file_path,
|
432 |
+
esc_html__( 'Upload files', 'pt-ocdi' )
|
433 |
+
);
|
434 |
+
}
|
435 |
+
|
436 |
+
// Set uploaded content file.
|
437 |
+
$selected_import_files['content'] = $content_file_info['file'];
|
438 |
+
|
439 |
+
if ( $widget_file_info && ! isset( $widget_file_info['error'] ) ) {
|
440 |
+
|
441 |
+
// Set uploaded widget file.
|
442 |
+
$selected_import_files['widgets'] = $widget_file_info['file'];
|
443 |
+
}
|
444 |
+
else {
|
445 |
+
|
446 |
+
// Add this error to log file.
|
447 |
+
$log_added = self::append_to_file(
|
448 |
+
sprintf(
|
449 |
+
__( 'Widget file was not uploaded. Error: %s', 'pt-ocdi' ),
|
450 |
+
$widget_file_info['error']
|
451 |
+
),
|
452 |
+
$log_file_path,
|
453 |
+
esc_html__( 'Upload files' , 'pt-ocdi' )
|
454 |
+
);
|
455 |
+
}
|
456 |
+
|
457 |
+
// Add this message to log file.
|
458 |
+
$log_added = self::append_to_file(
|
459 |
+
__( 'The import files were successfully uploaded!', 'pt-ocdi' ) . self::import_file_info( $selected_import_files ),
|
460 |
+
$log_file_path,
|
461 |
+
esc_html__( 'Upload files' , 'pt-ocdi' )
|
462 |
+
);
|
463 |
+
|
464 |
+
// Return array with paths of uploaded files.
|
465 |
+
return $selected_import_files;
|
466 |
+
}
|
467 |
+
|
468 |
+
|
469 |
+
/**
|
470 |
+
* Get import file information and max execution time.
|
471 |
+
*
|
472 |
+
* @param array $selected_import_files array of selected import files.
|
473 |
+
*/
|
474 |
+
public static function import_file_info( $selected_import_files ) {
|
475 |
+
return PHP_EOL .
|
476 |
+
sprintf(
|
477 |
+
__( 'MAX EXECUTION TIME = %s', 'pt-ocdi' ),
|
478 |
+
ini_get( 'max_execution_time' )
|
479 |
+
) . PHP_EOL .
|
480 |
+
sprintf(
|
481 |
+
__( 'Files info:%1$sSite URL = %2$s%1$sData file = %3$s%1$sWidget file = %4$s', 'pt-ocdi' ),
|
482 |
+
PHP_EOL,
|
483 |
+
get_site_url(),
|
484 |
+
$selected_import_files['content'],
|
485 |
+
empty( $selected_import_files['widgets'] ) ? esc_html__( 'not defined!', 'pt-ocdi' ) : $selected_import_files['widgets']
|
486 |
+
);
|
487 |
+
}
|
488 |
+
|
489 |
+
|
490 |
+
/**
|
491 |
+
* Write the error to the log file and send the AJAX response.
|
492 |
+
*
|
493 |
+
* @param string $error_text text to display in the log file and in the AJAX response.
|
494 |
+
* @param string $log_file_path path to the log file.
|
495 |
+
* @param string $separator title separating the old and new content.
|
496 |
+
*/
|
497 |
+
public static function log_error_and_send_ajax_response( $error_text, $log_file_path, $separator = '' ) {
|
498 |
+
|
499 |
+
// Add this error to log file.
|
500 |
+
$log_added = self::append_to_file(
|
501 |
+
$error_text,
|
502 |
+
$log_file_path,
|
503 |
+
$separator
|
504 |
+
);
|
505 |
+
|
506 |
+
// Send JSON Error response to the AJAX call.
|
507 |
+
wp_send_json( $error_text );
|
508 |
+
}
|
509 |
+
}
|
inc/class-ocdi-importer.php
ADDED
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Class for declaring the importer used in the One Click Demo Import plugin
|
4 |
+
*
|
5 |
+
* @package ocdi
|
6 |
+
*/
|
7 |
+
|
8 |
+
class OCDI_Importer {
|
9 |
+
|
10 |
+
private $importer;
|
11 |
+
|
12 |
+
public function __construct( $importer_options = array(), $logger = null ) {
|
13 |
+
|
14 |
+
// Include files that are needed for WordPress Importer v2.
|
15 |
+
$this->include_required_files();
|
16 |
+
|
17 |
+
// Set the WordPress Importer v2 as the importer used in this plugin.
|
18 |
+
// More: https://github.com/humanmade/WordPress-Importer.
|
19 |
+
$this->importer = new WXR_Importer( $importer_options );
|
20 |
+
|
21 |
+
// Set logger to the importer.
|
22 |
+
if ( ! empty( $logger ) ) {
|
23 |
+
$this->set_logger( $logger );
|
24 |
+
}
|
25 |
+
}
|
26 |
+
|
27 |
+
/**
|
28 |
+
* Include required files.
|
29 |
+
*/
|
30 |
+
private function include_required_files() {
|
31 |
+
if ( ! class_exists( 'WP_Importer' ) ) {
|
32 |
+
defined( 'WP_LOAD_IMPORTERS' ) || define( 'WP_LOAD_IMPORTERS', true );
|
33 |
+
require ABSPATH . '/wp-admin/includes/class-wp-importer.php';
|
34 |
+
}
|
35 |
+
require PT_OCDI_PATH . 'vendor/humanmade/WordPress-Importer/class-wxr-importer.php';
|
36 |
+
}
|
37 |
+
|
38 |
+
/**
|
39 |
+
* Imports content from a WordPress export file.
|
40 |
+
*
|
41 |
+
* @param string $data_file path to xml file, file with WordPress export data.
|
42 |
+
*/
|
43 |
+
public function import( $data_file ) {
|
44 |
+
$this->importer->import( $data_file );
|
45 |
+
}
|
46 |
+
|
47 |
+
/**
|
48 |
+
* Set the logger used in the import
|
49 |
+
*
|
50 |
+
* @param object $logger logger instance.
|
51 |
+
*/
|
52 |
+
public function set_logger( $logger ) {
|
53 |
+
$this->importer->set_logger( $logger );
|
54 |
+
}
|
55 |
+
}
|
inc/class-ocdi-logger.php
ADDED
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Logger class used in the One Click Demo Import plugin
|
4 |
+
*
|
5 |
+
* @package ocdi
|
6 |
+
*/
|
7 |
+
|
8 |
+
// Include required files.
|
9 |
+
require PT_OCDI_PATH . 'vendor/humanmade/WordPress-Importer/class-logger.php';
|
10 |
+
require PT_OCDI_PATH . 'vendor/humanmade/WordPress-Importer/class-logger-cli.php';
|
11 |
+
|
12 |
+
class OCDI_Logger extends WP_Importer_Logger_CLI {
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Variable for front-end error display.
|
16 |
+
*/
|
17 |
+
public $error_output = '';
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Overwritten log function from WP_Importer_Logger_CLI.
|
21 |
+
*
|
22 |
+
* Logs with an arbitrary level.
|
23 |
+
*
|
24 |
+
* @param mixed $level level of reporting.
|
25 |
+
* @param string $message log message.
|
26 |
+
* @param array $context context to the log message.
|
27 |
+
*/
|
28 |
+
public function log( $level, $message, array $context = array() ) {
|
29 |
+
|
30 |
+
// Save error messages for front-end display.
|
31 |
+
$this->error_output( $level, $message, $context = array() );
|
32 |
+
|
33 |
+
if ( $this->level_to_numeric( $level ) < $this->level_to_numeric( $this->min_level ) ) {
|
34 |
+
return;
|
35 |
+
}
|
36 |
+
|
37 |
+
printf(
|
38 |
+
'[%s] %s' . PHP_EOL,
|
39 |
+
strtoupper( $level ),
|
40 |
+
$message
|
41 |
+
);
|
42 |
+
}
|
43 |
+
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Save messages for error output.
|
47 |
+
* Only the messages greater then Error.
|
48 |
+
*
|
49 |
+
* @param mixed $level level of reporting.
|
50 |
+
* @param string $message log message.
|
51 |
+
* @param array $context context to the log message.
|
52 |
+
*/
|
53 |
+
public function error_output( $level, $message, array $context = array() ) {
|
54 |
+
if ( $this->level_to_numeric( $level ) < $this->level_to_numeric( 'error' ) ) {
|
55 |
+
return;
|
56 |
+
}
|
57 |
+
|
58 |
+
$this->error_output .= sprintf(
|
59 |
+
'[%s] %s<br>',
|
60 |
+
strtoupper( $level ),
|
61 |
+
$message
|
62 |
+
);
|
63 |
+
}
|
64 |
+
}
|
inc/class-ocdi-widget-importer.php
ADDED
@@ -0,0 +1,304 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Class for the widget importer used in the One Click Demo Import plugin.
|
4 |
+
*
|
5 |
+
* Code is mostly from the Widget Importer & Exporter plugin.
|
6 |
+
*
|
7 |
+
* @see https://wordpress.org/plugins/widget-importer-exporter/
|
8 |
+
* @package ocdi
|
9 |
+
*/
|
10 |
+
|
11 |
+
class OCDI_Widget_Importer {
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Imports widgets from a json file.
|
15 |
+
*
|
16 |
+
* @param string $data_file path to json file with WordPress widget export data.
|
17 |
+
*/
|
18 |
+
public function import_widgets( $data_file ) {
|
19 |
+
|
20 |
+
// Get widgets data from file.
|
21 |
+
$data = $this->process_import_file( $data_file );
|
22 |
+
|
23 |
+
// Return from this function if there was an error.
|
24 |
+
if ( is_wp_error( $data ) ) {
|
25 |
+
return $data;
|
26 |
+
}
|
27 |
+
|
28 |
+
// Import the widget data and save the results.
|
29 |
+
return $this->import_data( $data );
|
30 |
+
}
|
31 |
+
|
32 |
+
/**
|
33 |
+
* Process import file - this parses the widget data and returns it.
|
34 |
+
*
|
35 |
+
* @param string $file path to json file.
|
36 |
+
* @return object $data decoded JSON string
|
37 |
+
*/
|
38 |
+
private function process_import_file( $file ) {
|
39 |
+
|
40 |
+
// File exists?
|
41 |
+
if ( ! file_exists( $file ) ) {
|
42 |
+
return new WP_Error(
|
43 |
+
'widget_import_file_not_found',
|
44 |
+
__( 'Widget import file could not be found.', 'pt-ocdi' )
|
45 |
+
);
|
46 |
+
}
|
47 |
+
|
48 |
+
// Get file contents and decode.
|
49 |
+
$data = OCDI_Helpers::data_from_file( $file );
|
50 |
+
|
51 |
+
// Return from this function if there was an error.
|
52 |
+
if ( is_wp_error( $data ) ) {
|
53 |
+
return $data;
|
54 |
+
}
|
55 |
+
|
56 |
+
return json_decode( $data );
|
57 |
+
}
|
58 |
+
|
59 |
+
|
60 |
+
|
61 |
+
/**
|
62 |
+
* Import widget JSON data
|
63 |
+
*
|
64 |
+
* @global array $wp_registered_sidebars
|
65 |
+
* @param object $data JSON widget data.
|
66 |
+
* @return array $results
|
67 |
+
*/
|
68 |
+
private function import_data( $data ) {
|
69 |
+
|
70 |
+
global $wp_registered_sidebars;
|
71 |
+
|
72 |
+
// Have valid data? If no data or could not decode.
|
73 |
+
if ( empty( $data ) || ! is_object( $data ) ) {
|
74 |
+
return new WP_Error(
|
75 |
+
'corrupted_widget_import_data',
|
76 |
+
__( 'Widget import data could not be read. Please try a different file.', 'pt-ocdi' )
|
77 |
+
);
|
78 |
+
}
|
79 |
+
|
80 |
+
// Hook before import.
|
81 |
+
do_action( 'pt-ocdi/before_widgets_import' );
|
82 |
+
$data = apply_filters( 'pt-ocdi/before_widgets_import_data', $data );
|
83 |
+
|
84 |
+
// Get all available widgets site supports.
|
85 |
+
$available_widgets = $this->available_widgets();
|
86 |
+
|
87 |
+
// Get all existing widget instances.
|
88 |
+
$widget_instances = array();
|
89 |
+
|
90 |
+
foreach ( $available_widgets as $widget_data ) {
|
91 |
+
$widget_instances[ $widget_data['id_base'] ] = get_option( 'widget_' . $widget_data['id_base'] );
|
92 |
+
}
|
93 |
+
|
94 |
+
// Begin results.
|
95 |
+
$results = array();
|
96 |
+
|
97 |
+
// Loop import data's sidebars.
|
98 |
+
foreach ( $data as $sidebar_id => $widgets ) {
|
99 |
+
|
100 |
+
// Skip inactive widgets (should not be in export file).
|
101 |
+
if ( 'wp_inactive_widgets' == $sidebar_id ) {
|
102 |
+
continue;
|
103 |
+
}
|
104 |
+
|
105 |
+
// Check if sidebar is available on this site. Otherwise add widgets to inactive, and say so.
|
106 |
+
if ( isset( $wp_registered_sidebars[ $sidebar_id ] ) ) {
|
107 |
+
$sidebar_available = true;
|
108 |
+
$use_sidebar_id = $sidebar_id;
|
109 |
+
$sidebar_message_type = 'success';
|
110 |
+
$sidebar_message = '';
|
111 |
+
}
|
112 |
+
else {
|
113 |
+
$sidebar_available = false;
|
114 |
+
$use_sidebar_id = 'wp_inactive_widgets'; // Add to inactive if sidebar does not exist in theme.
|
115 |
+
$sidebar_message_type = 'error';
|
116 |
+
$sidebar_message = __( 'Sidebar does not exist in theme (moving widget to Inactive)', 'pt-ocdi' );
|
117 |
+
}
|
118 |
+
|
119 |
+
// Result for sidebar.
|
120 |
+
$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.
|
121 |
+
$results[ $sidebar_id ]['message_type'] = $sidebar_message_type;
|
122 |
+
$results[ $sidebar_id ]['message'] = $sidebar_message;
|
123 |
+
$results[ $sidebar_id ]['widgets'] = array();
|
124 |
+
|
125 |
+
// Loop widgets.
|
126 |
+
foreach ( $widgets as $widget_instance_id => $widget ) {
|
127 |
+
|
128 |
+
$fail = false;
|
129 |
+
|
130 |
+
// Get id_base (remove -# from end) and instance ID number.
|
131 |
+
$id_base = preg_replace( '/-[0-9]+$/', '', $widget_instance_id );
|
132 |
+
$instance_id_number = str_replace( $id_base . '-', '', $widget_instance_id );
|
133 |
+
|
134 |
+
// Does site support this widget?
|
135 |
+
if ( ! $fail && ! isset( $available_widgets[ $id_base ] ) ) {
|
136 |
+
$fail = true;
|
137 |
+
$widget_message_type = 'error';
|
138 |
+
$widget_message = __( 'Site does not support widget', 'pt-ocdi' ); // Explain why widget not imported.
|
139 |
+
}
|
140 |
+
|
141 |
+
// Filter to modify settings object before conversion to array and import.
|
142 |
+
// Leave this filter here for backwards compatibility with manipulating objects (before conversion to array below).
|
143 |
+
// Ideally the newer wie_widget_settings_array below will be used instead of this.
|
144 |
+
$widget = apply_filters( 'pt-ocdi/widget_settings', $widget ); // Object.
|
145 |
+
|
146 |
+
// Convert multidimensional objects to multidimensional arrays.
|
147 |
+
// Some plugins like Jetpack Widget Visibility store settings as multidimensional arrays.
|
148 |
+
// Without this, they are imported as objects and cause fatal error on Widgets page.
|
149 |
+
// 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.
|
150 |
+
// It is probably much more likely that arrays are used than objects, however.
|
151 |
+
$widget = json_decode( json_encode( $widget ), true );
|
152 |
+
|
153 |
+
// Filter to modify settings array.
|
154 |
+
// This is preferred over the older wie_widget_settings filter above.
|
155 |
+
// Do before identical check because changes may make it identical to end result (such as URL replacements).
|
156 |
+
$widget = apply_filters( 'pt-ocdi/widget_settings_array', $widget );
|
157 |
+
|
158 |
+
// Does widget with identical settings already exist in same sidebar?
|
159 |
+
if ( ! $fail && isset( $widget_instances[ $id_base ] ) ) {
|
160 |
+
|
161 |
+
// Get existing widgets in this sidebar.
|
162 |
+
$sidebars_widgets = get_option( 'sidebars_widgets' );
|
163 |
+
$sidebar_widgets = isset( $sidebars_widgets[ $use_sidebar_id ] ) ? $sidebars_widgets[ $use_sidebar_id ] : array(); // Check Inactive if that's where will go.
|
164 |
+
|
165 |
+
// Loop widgets with ID base.
|
166 |
+
$single_widget_instances = ! empty( $widget_instances[ $id_base ] ) ? $widget_instances[ $id_base ] : array();
|
167 |
+
foreach ( $single_widget_instances as $check_id => $check_widget ) {
|
168 |
+
|
169 |
+
// Is widget in same sidebar and has identical settings?
|
170 |
+
if ( in_array( "$id_base-$check_id", $sidebar_widgets ) && (array) $widget == $check_widget ) {
|
171 |
+
$fail = true;
|
172 |
+
$widget_message_type = 'warning';
|
173 |
+
$widget_message = __( 'Widget already exists', 'pt-ocdi' ); // Explain why widget not imported.
|
174 |
+
|
175 |
+
break;
|
176 |
+
}
|
177 |
+
}
|
178 |
+
}
|
179 |
+
|
180 |
+
// No failure.
|
181 |
+
if ( ! $fail ) {
|
182 |
+
|
183 |
+
// Add widget instance.
|
184 |
+
$single_widget_instances = get_option( 'widget_' . $id_base ); // All instances for that widget ID base, get fresh every time.
|
185 |
+
$single_widget_instances = ! empty( $single_widget_instances ) ? $single_widget_instances : array( '_multiwidget' => 1 ); // Start fresh if have to.
|
186 |
+
$single_widget_instances[] = $widget; // Add it.
|
187 |
+
|
188 |
+
// Get the key it was given.
|
189 |
+
end( $single_widget_instances );
|
190 |
+
$new_instance_id_number = key( $single_widget_instances );
|
191 |
+
|
192 |
+
// If key is 0, make it 1.
|
193 |
+
// 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).
|
194 |
+
if ( '0' === strval( $new_instance_id_number ) ) {
|
195 |
+
$new_instance_id_number = 1;
|
196 |
+
$single_widget_instances[ $new_instance_id_number ] = $single_widget_instances[0];
|
197 |
+
unset( $single_widget_instances[0] );
|
198 |
+
}
|
199 |
+
|
200 |
+
// Move _multiwidget to end of array for uniformity.
|
201 |
+
if ( isset( $single_widget_instances['_multiwidget'] ) ) {
|
202 |
+
$multiwidget = $single_widget_instances['_multiwidget'];
|
203 |
+
unset( $single_widget_instances['_multiwidget'] );
|
204 |
+
$single_widget_instances['_multiwidget'] = $multiwidget;
|
205 |
+
}
|
206 |
+
|
207 |
+
// Update option with new widget.
|
208 |
+
update_option( 'widget_' . $id_base, $single_widget_instances );
|
209 |
+
|
210 |
+
// Assign widget instance to sidebar.
|
211 |
+
$sidebars_widgets = get_option( 'sidebars_widgets' ); // Which sidebars have which widgets, get fresh every time.
|
212 |
+
$new_instance_id = $id_base . '-' . $new_instance_id_number; // Use ID number from new widget instance.
|
213 |
+
$sidebars_widgets[ $use_sidebar_id ][] = $new_instance_id; // Add new instance to sidebar.
|
214 |
+
update_option( 'sidebars_widgets', $sidebars_widgets ); // Save the amended data.
|
215 |
+
|
216 |
+
// After widget import action.
|
217 |
+
$after_widget_import = array(
|
218 |
+
'sidebar' => $use_sidebar_id,
|
219 |
+
'sidebar_old' => $sidebar_id,
|
220 |
+
'widget' => $widget,
|
221 |
+
'widget_type' => $id_base,
|
222 |
+
'widget_id' => $new_instance_id,
|
223 |
+
'widget_id_old' => $widget_instance_id,
|
224 |
+
'widget_id_num' => $new_instance_id_number,
|
225 |
+
'widget_id_num_old' => $instance_id_number,
|
226 |
+
);
|
227 |
+
do_action( 'pt-ocdi/after_single_widget_import', $after_widget_import );
|
228 |
+
|
229 |
+
// Success message.
|
230 |
+
if ( $sidebar_available ) {
|
231 |
+
$widget_message_type = 'success';
|
232 |
+
$widget_message = __( 'Imported', 'pt-ocdi' );
|
233 |
+
}
|
234 |
+
else {
|
235 |
+
$widget_message_type = 'warning';
|
236 |
+
$widget_message = __( 'Imported to Inactive', 'pt-ocdi' );
|
237 |
+
}
|
238 |
+
}
|
239 |
+
|
240 |
+
// Result for widget instance.
|
241 |
+
$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).
|
242 |
+
$results[ $sidebar_id ]['widgets'][ $widget_instance_id ]['title'] = ! empty( $widget['title'] ) ? $widget['title'] : __( 'No Title', 'pt-ocdi' ); // Show "No Title" if widget instance is untitled.
|
243 |
+
$results[ $sidebar_id ]['widgets'][ $widget_instance_id ]['message_type'] = $widget_message_type;
|
244 |
+
$results[ $sidebar_id ]['widgets'][ $widget_instance_id ]['message'] = $widget_message;
|
245 |
+
|
246 |
+
}
|
247 |
+
}
|
248 |
+
|
249 |
+
// Hook after import.
|
250 |
+
do_action( 'pt-ocdi/after_widgets_import' );
|
251 |
+
|
252 |
+
// Return results.
|
253 |
+
return apply_filters( 'pt-ocdi/widget_import_results', $results );
|
254 |
+
}
|
255 |
+
|
256 |
+
|
257 |
+
/**
|
258 |
+
* Available widgets.
|
259 |
+
*
|
260 |
+
* Gather site's widgets into array with ID base, name, etc.
|
261 |
+
*
|
262 |
+
* @global array $wp_registered_widget_controls
|
263 |
+
* @return array $available_widgets, Widget information
|
264 |
+
*/
|
265 |
+
private function available_widgets() {
|
266 |
+
|
267 |
+
global $wp_registered_widget_controls;
|
268 |
+
|
269 |
+
$widget_controls = $wp_registered_widget_controls;
|
270 |
+
$available_widgets = array();
|
271 |
+
|
272 |
+
foreach ( $widget_controls as $widget ) {
|
273 |
+
if ( ! empty( $widget['id_base'] ) && ! isset( $available_widgets[ $widget['id_base'] ] ) ) {
|
274 |
+
$available_widgets[ $widget['id_base'] ]['id_base'] = $widget['id_base'];
|
275 |
+
$available_widgets[ $widget['id_base'] ]['name'] = $widget['name'];
|
276 |
+
}
|
277 |
+
}
|
278 |
+
|
279 |
+
return apply_filters( 'pt-ocdi/available_widgets', $available_widgets );
|
280 |
+
}
|
281 |
+
|
282 |
+
|
283 |
+
/**
|
284 |
+
* Format results for log file
|
285 |
+
*
|
286 |
+
* @param array $results widget import results.
|
287 |
+
*/
|
288 |
+
public function format_results_for_log( $results ) {
|
289 |
+
|
290 |
+
if ( empty( $results ) ) {
|
291 |
+
esc_html_e( 'No results for widget import!', 'pt-ocdi' );
|
292 |
+
}
|
293 |
+
|
294 |
+
// Loop sidebars.
|
295 |
+
foreach ( $results as $sidebar ) {
|
296 |
+
echo esc_html( $sidebar['name'] ) . ' : ' . esc_html( $sidebar['message'] ) . PHP_EOL . PHP_EOL;
|
297 |
+
// Loop widgets.
|
298 |
+
foreach ( $sidebar['widgets'] as $widget ) {
|
299 |
+
echo esc_html( $widget['name'] ) . ' - ' . esc_html( $widget['title'] ) . ' - ' . esc_html( $widget['message'] ) . PHP_EOL;
|
300 |
+
}
|
301 |
+
echo PHP_EOL;
|
302 |
+
}
|
303 |
+
}
|
304 |
+
}
|
languages/one-click-demo-import.pot
ADDED
@@ -0,0 +1,258 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Copyright (C) 2016 ProteusThemes
|
2 |
+
# This file is distributed under the GPL 2.0.
|
3 |
+
msgid ""
|
4 |
+
msgstr ""
|
5 |
+
"Project-Id-Version: One Click Demo Import 1.0.0\n"
|
6 |
+
"Report-Msgid-Bugs-To: http://support.proteusthemes.com/\n"
|
7 |
+
"POT-Creation-Date: 2016-03-14 13:27:22+00:00\n"
|
8 |
+
"MIME-Version: 1.0\n"
|
9 |
+
"Content-Type: text/plain; charset=utf-8\n"
|
10 |
+
"Content-Transfer-Encoding: 8bit\n"
|
11 |
+
"PO-Revision-Date: 2016-MO-DA HO:MI+ZONE\n"
|
12 |
+
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
13 |
+
"Language-Team: LANGUAGE <LL@li.org>\n"
|
14 |
+
"X-Generator: grunt-wp-i18n 0.5.4\n"
|
15 |
+
"X-Poedit-KeywordsList: "
|
16 |
+
"__;_e;_x:1,2c;_ex:1,2c;_n:1,2;_nx:1,2,4c;_n_noop:1,2;_nx_noop:1,2,3c;esc_"
|
17 |
+
"attr__;esc_html__;esc_attr_e;esc_html_e;esc_attr_x:1,2c;esc_html_x:1,2c;\n"
|
18 |
+
"Language: en\n"
|
19 |
+
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
20 |
+
"X-Poedit-Country: United States\n"
|
21 |
+
"X-Poedit-SourceCharset: UTF-8\n"
|
22 |
+
"X-Poedit-Basepath: ../\n"
|
23 |
+
"X-Poedit-SearchPath-0: .\n"
|
24 |
+
"X-Poedit-Bookmarks: \n"
|
25 |
+
"X-Textdomain-Support: yes\n"
|
26 |
+
|
27 |
+
#: inc/class-ocdi-helpers.php:120
|
28 |
+
msgid "URL for %s%s%s file is not defined!"
|
29 |
+
msgstr ""
|
30 |
+
|
31 |
+
#: inc/class-ocdi-helpers.php:142
|
32 |
+
msgid ""
|
33 |
+
"An error occurred while fetching %s%s%s file from the server!%sReason: %s - "
|
34 |
+
"%s."
|
35 |
+
msgstr ""
|
36 |
+
|
37 |
+
#: inc/class-ocdi-helpers.php:187 inc/class-ocdi-helpers.php:233
|
38 |
+
msgid ""
|
39 |
+
"An error occurred while writing file to your server! Tried to write a file "
|
40 |
+
"to: %s%s."
|
41 |
+
msgstr ""
|
42 |
+
|
43 |
+
#: inc/class-ocdi-helpers.php:273
|
44 |
+
msgid ""
|
45 |
+
"An error occurred while reading a file from your server! Tried reading file "
|
46 |
+
"from path: %s%s."
|
47 |
+
msgstr ""
|
48 |
+
|
49 |
+
#: inc/class-ocdi-helpers.php:298
|
50 |
+
msgid ""
|
51 |
+
"An error occurred while retrieving reading/writing permissions to your "
|
52 |
+
"server (could not retrieve WP filesystem credentials)!"
|
53 |
+
msgstr ""
|
54 |
+
|
55 |
+
#: inc/class-ocdi-helpers.php:306
|
56 |
+
msgid "Your WordPress login credentials don't allow to use WP_Filesystem!"
|
57 |
+
msgstr ""
|
58 |
+
|
59 |
+
#: inc/class-ocdi-helpers.php:323
|
60 |
+
msgid ""
|
61 |
+
"This WordPress page does not have %sdirect%s write file access. This plugin "
|
62 |
+
"needs it in order to save the demo import xml file to the upload directory "
|
63 |
+
"of your site. You can change this setting with these instructions: %s."
|
64 |
+
msgstr ""
|
65 |
+
|
66 |
+
#: inc/class-ocdi-helpers.php:395
|
67 |
+
msgid ""
|
68 |
+
"%sYour user role isn't high enough. You don't have permission to import "
|
69 |
+
"demo data.%s"
|
70 |
+
msgstr ""
|
71 |
+
|
72 |
+
#: inc/class-ocdi-helpers.php:430
|
73 |
+
msgid ""
|
74 |
+
"Please upload XML file for content import. If you want to import widgets "
|
75 |
+
"only, please use Widget Importer & Exporter plugin."
|
76 |
+
msgstr ""
|
77 |
+
|
78 |
+
#: inc/class-ocdi-helpers.php:432 inc/class-ocdi-helpers.php:453
|
79 |
+
#: inc/class-ocdi-helpers.php:461
|
80 |
+
msgid "Upload files"
|
81 |
+
msgstr ""
|
82 |
+
|
83 |
+
#: inc/class-ocdi-helpers.php:449
|
84 |
+
msgid "Widget file was not uploaded. Error: %s"
|
85 |
+
msgstr ""
|
86 |
+
|
87 |
+
#: inc/class-ocdi-helpers.php:459
|
88 |
+
msgid "The import files were successfully uploaded!"
|
89 |
+
msgstr ""
|
90 |
+
|
91 |
+
#: inc/class-ocdi-helpers.php:477
|
92 |
+
msgid "MAX EXECUTION TIME = %s"
|
93 |
+
msgstr ""
|
94 |
+
|
95 |
+
#: inc/class-ocdi-helpers.php:481
|
96 |
+
msgid "Files info:%1$sSite URL = %2$s%1$sData file = %3$s%1$sWidget file = %4$s"
|
97 |
+
msgstr ""
|
98 |
+
|
99 |
+
#: inc/class-ocdi-helpers.php:485
|
100 |
+
msgid "not defined!"
|
101 |
+
msgstr ""
|
102 |
+
|
103 |
+
#: inc/class-ocdi-widget-importer.php:44
|
104 |
+
msgid "Widget import file could not be found."
|
105 |
+
msgstr ""
|
106 |
+
|
107 |
+
#: inc/class-ocdi-widget-importer.php:76
|
108 |
+
msgid "Widget import data could not be read. Please try a different file."
|
109 |
+
msgstr ""
|
110 |
+
|
111 |
+
#: inc/class-ocdi-widget-importer.php:116
|
112 |
+
msgid "Sidebar does not exist in theme (moving widget to Inactive)"
|
113 |
+
msgstr ""
|
114 |
+
|
115 |
+
#: inc/class-ocdi-widget-importer.php:138
|
116 |
+
msgid "Site does not support widget"
|
117 |
+
msgstr ""
|
118 |
+
|
119 |
+
#: inc/class-ocdi-widget-importer.php:173
|
120 |
+
msgid "Widget already exists"
|
121 |
+
msgstr ""
|
122 |
+
|
123 |
+
#: inc/class-ocdi-widget-importer.php:232
|
124 |
+
msgid "Imported"
|
125 |
+
msgstr ""
|
126 |
+
|
127 |
+
#: inc/class-ocdi-widget-importer.php:236
|
128 |
+
msgid "Imported to Inactive"
|
129 |
+
msgstr ""
|
130 |
+
|
131 |
+
#: inc/class-ocdi-widget-importer.php:242
|
132 |
+
msgid "No Title"
|
133 |
+
msgstr ""
|
134 |
+
|
135 |
+
#: inc/class-ocdi-widget-importer.php:291
|
136 |
+
msgid "No results for widget import!"
|
137 |
+
msgstr ""
|
138 |
+
|
139 |
+
#. Plugin Name of the plugin/theme
|
140 |
+
msgid "One Click Demo Import"
|
141 |
+
msgstr ""
|
142 |
+
|
143 |
+
#: one-click-demo-import.php:114
|
144 |
+
msgid ""
|
145 |
+
"%sWarning: your server is using %sPHP safe mode%s. This means that you "
|
146 |
+
"might experience server timeout errors.%s"
|
147 |
+
msgstr ""
|
148 |
+
|
149 |
+
#: one-click-demo-import.php:125
|
150 |
+
msgid ""
|
151 |
+
"Importing demo data (post, pages, images, theme settings, ...) is the "
|
152 |
+
"easiest way to setup your theme. It will allow you to quickly edit "
|
153 |
+
"everything instead of creating content from scratch. When you import the "
|
154 |
+
"data, the following things might happen:"
|
155 |
+
msgstr ""
|
156 |
+
|
157 |
+
#: one-click-demo-import.php:129
|
158 |
+
msgid ""
|
159 |
+
"No existing posts, pages, categories, images, custom post types or any "
|
160 |
+
"other data will be deleted or modified."
|
161 |
+
msgstr ""
|
162 |
+
|
163 |
+
#: one-click-demo-import.php:130
|
164 |
+
msgid "Posts, pages, images, widgets and menus will get imported."
|
165 |
+
msgstr ""
|
166 |
+
|
167 |
+
#: one-click-demo-import.php:131
|
168 |
+
msgid ""
|
169 |
+
"Please click \"Import Demo Data\" button only once and wait, it can take a "
|
170 |
+
"couple of minutes."
|
171 |
+
msgstr ""
|
172 |
+
|
173 |
+
#: one-click-demo-import.php:136
|
174 |
+
msgid "Before you begin, make sure all the required plugins are activated."
|
175 |
+
msgstr ""
|
176 |
+
|
177 |
+
#: one-click-demo-import.php:142
|
178 |
+
msgid ""
|
179 |
+
"There are no predefined import files available in this theme. Please upload "
|
180 |
+
"the import files manually!"
|
181 |
+
msgstr ""
|
182 |
+
|
183 |
+
#: one-click-demo-import.php:146
|
184 |
+
msgid "Choose a XML file for content import:"
|
185 |
+
msgstr ""
|
186 |
+
|
187 |
+
#: one-click-demo-import.php:149
|
188 |
+
msgid "optional"
|
189 |
+
msgstr ""
|
190 |
+
|
191 |
+
#: one-click-demo-import.php:149
|
192 |
+
msgid "Choose a WIE or JSON file for widget import:"
|
193 |
+
msgstr ""
|
194 |
+
|
195 |
+
#: one-click-demo-import.php:165
|
196 |
+
msgid "Import Demo Data"
|
197 |
+
msgstr ""
|
198 |
+
|
199 |
+
#: one-click-demo-import.php:190
|
200 |
+
msgid "Importing now, please wait!"
|
201 |
+
msgstr ""
|
202 |
+
|
203 |
+
#: one-click-demo-import.php:233
|
204 |
+
msgid "Manually uploaded files"
|
205 |
+
msgstr ""
|
206 |
+
|
207 |
+
#: one-click-demo-import.php:250 one-click-demo-import.php:261
|
208 |
+
msgid "Downloaded files"
|
209 |
+
msgstr ""
|
210 |
+
|
211 |
+
#: one-click-demo-import.php:257
|
212 |
+
msgid "The import files for: %s were successfully downloaded!"
|
213 |
+
msgstr ""
|
214 |
+
|
215 |
+
#: one-click-demo-import.php:267
|
216 |
+
msgid "No import files specified!"
|
217 |
+
msgstr ""
|
218 |
+
|
219 |
+
#: one-click-demo-import.php:295
|
220 |
+
msgid ""
|
221 |
+
"%1$s%3$sThat's it, all done!%4$s%2$sThe demo import has finished. Please "
|
222 |
+
"check your page and make sure that everything has imported correctly. If it "
|
223 |
+
"did, you can deactivate the %3$sOne Click Demo Import%4$s plugin, because "
|
224 |
+
"it has done its job.%5$s"
|
225 |
+
msgstr ""
|
226 |
+
|
227 |
+
#: one-click-demo-import.php:306
|
228 |
+
msgid ""
|
229 |
+
"%1$sThe demo import has finished, but there were some import "
|
230 |
+
"errors.%2$sMore details about the errors can be found in this %3$s%5$slog "
|
231 |
+
"file%6$s%4$s%7$s"
|
232 |
+
msgstr ""
|
233 |
+
|
234 |
+
#: one-click-demo-import.php:345
|
235 |
+
msgid "Importing content"
|
236 |
+
msgstr ""
|
237 |
+
|
238 |
+
#: one-click-demo-import.php:381 one-click-demo-import.php:393
|
239 |
+
msgid "Importing widgets"
|
240 |
+
msgstr ""
|
241 |
+
|
242 |
+
#: one-click-demo-import.php:415
|
243 |
+
msgid "After import setup"
|
244 |
+
msgstr ""
|
245 |
+
|
246 |
+
#. Author URI of the plugin/theme
|
247 |
+
msgid "http://www.proteusthemes.com"
|
248 |
+
msgstr ""
|
249 |
+
|
250 |
+
#. Description of the plugin/theme
|
251 |
+
msgid ""
|
252 |
+
"Import your content, widgets and theme settings with one click. Theme "
|
253 |
+
"authors! Enable simple demo import for your theme demo data."
|
254 |
+
msgstr ""
|
255 |
+
|
256 |
+
#. Author of the plugin/theme
|
257 |
+
msgid "ProteusThemes"
|
258 |
+
msgstr ""
|
one-click-demo-import.php
ADDED
@@ -0,0 +1,447 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
Plugin Name: One Click Demo Import
|
5 |
+
Plugin URI: http://www.proteusthemes.com
|
6 |
+
Description: Import your content, widgets and theme settings with one click. Theme authors! Enable simple demo import for your theme demo data.
|
7 |
+
Version: 1.0.0
|
8 |
+
Author: ProteusThemes
|
9 |
+
Author URI: http://www.proteusthemes.com
|
10 |
+
License: GPL3
|
11 |
+
License URI: http://www.gnu.org/licenses/gpl.html
|
12 |
+
Text Domain: pt-ocdi
|
13 |
+
*/
|
14 |
+
|
15 |
+
// Block direct access to the main plugin file.
|
16 |
+
defined( 'ABSPATH' ) or die( 'No script kiddies please!' );
|
17 |
+
|
18 |
+
// Path/URL to root of this plugin, with trailing slash.
|
19 |
+
define( 'PT_OCDI_PATH', plugin_dir_path( __FILE__ ) );
|
20 |
+
define( 'PT_OCDI_URL', plugin_dir_url( __FILE__ ) );
|
21 |
+
|
22 |
+
// Current version of the plugin.
|
23 |
+
define( 'PT_OCDI_VERSION', '0.6.0' );
|
24 |
+
|
25 |
+
// Include files.
|
26 |
+
require PT_OCDI_PATH . 'inc/class-ocdi-helpers.php';
|
27 |
+
require PT_OCDI_PATH . 'inc/class-ocdi-importer.php';
|
28 |
+
require PT_OCDI_PATH . 'inc/class-ocdi-widget-importer.php';
|
29 |
+
require PT_OCDI_PATH . 'inc/class-ocdi-logger.php';
|
30 |
+
|
31 |
+
/**
|
32 |
+
* One Click Demo Import class, so we don't have to worry about namespaces.
|
33 |
+
*/
|
34 |
+
class PT_One_Click_Demo_Import {
|
35 |
+
|
36 |
+
/**
|
37 |
+
* @var $instance the reference to *Singleton* instance of this class
|
38 |
+
*/
|
39 |
+
private static $instance;
|
40 |
+
|
41 |
+
/**
|
42 |
+
* Private variables used throughout the plugin.
|
43 |
+
*/
|
44 |
+
private $importer, $plugin_page, $import_files, $logger, $log_file_path;
|
45 |
+
|
46 |
+
|
47 |
+
/**
|
48 |
+
* Returns the *Singleton* instance of this class.
|
49 |
+
*
|
50 |
+
* @return PT_One_Click_Demo_Import the *Singleton* instance.
|
51 |
+
*/
|
52 |
+
public static function getInstance() {
|
53 |
+
if ( null === static::$instance ) {
|
54 |
+
static::$instance = new static();
|
55 |
+
}
|
56 |
+
|
57 |
+
return static::$instance;
|
58 |
+
}
|
59 |
+
|
60 |
+
|
61 |
+
/**
|
62 |
+
* Class construct function, to initiate the plugin.
|
63 |
+
* Protected constructor to prevent creating a new instance of the
|
64 |
+
* *Singleton* via the `new` operator from outside of this class.
|
65 |
+
*/
|
66 |
+
protected function __construct() {
|
67 |
+
|
68 |
+
// Actions.
|
69 |
+
add_action( 'admin_menu', array( $this, 'create_plugin_page' ) );
|
70 |
+
add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
|
71 |
+
add_action( 'wp_ajax_ocdi_import_demo_data', array( $this, 'import_demo_data_ajax_callback' ) );
|
72 |
+
add_action( 'after_setup_theme', array( $this, 'setup_plugin_with_filter_data' ) );
|
73 |
+
}
|
74 |
+
|
75 |
+
|
76 |
+
/**
|
77 |
+
* Private clone method to prevent cloning of the instance of the *Singleton* instance.
|
78 |
+
*
|
79 |
+
* @return void
|
80 |
+
*/
|
81 |
+
private function __clone() {}
|
82 |
+
|
83 |
+
|
84 |
+
/**
|
85 |
+
* Private unserialize method to prevent unserializing of the *Singleton* instance.
|
86 |
+
*
|
87 |
+
* @return void
|
88 |
+
*/
|
89 |
+
private function __wakeup() {}
|
90 |
+
|
91 |
+
|
92 |
+
/**
|
93 |
+
* Creates the plugin page and a submenu item in WP Appearance menu.
|
94 |
+
*/
|
95 |
+
public function create_plugin_page() {
|
96 |
+
$this->plugin_page = add_theme_page( 'One Click Demo Import', 'Import Demo Data', 'import', 'pt-one-click-demo-import', array( $this, 'display_plugin_page' ) );
|
97 |
+
}
|
98 |
+
|
99 |
+
|
100 |
+
/**
|
101 |
+
* Plugin page display.
|
102 |
+
*/
|
103 |
+
public function display_plugin_page() {
|
104 |
+
?>
|
105 |
+
|
106 |
+
<div class="ocdi wrap">
|
107 |
+
<h2 class="ocdi__title"><span class="dashicons dashicons-download"></span><?php esc_html_e( 'One Click Demo Import', 'pt-ocdi' ); ?></h2>
|
108 |
+
|
109 |
+
<?php
|
110 |
+
|
111 |
+
// Display warrning if PHP safe mode is enabled, since we wont be able to change the max_execution_time.
|
112 |
+
if ( ini_get( 'safe_mode' ) ) {
|
113 |
+
printf(
|
114 |
+
esc_html__( '%sWarning: your server is using %sPHP safe mode%s. This means that you might experience server timeout errors.%s', 'pt-ocdi' ),
|
115 |
+
'<div class="notice notice-warning"><p>',
|
116 |
+
'<strong>',
|
117 |
+
'</strong>',
|
118 |
+
'</p></div>'
|
119 |
+
);
|
120 |
+
}
|
121 |
+
?>
|
122 |
+
|
123 |
+
<div class="ocdi__intro-text">
|
124 |
+
<p>
|
125 |
+
<?php esc_html_e( 'Importing demo data (post, pages, images, theme settings, ...) is the easiest way to setup your theme. It will allow you to quickly edit everything instead of creating content from scratch. When you import the data, the following things might happen:', 'pt-ocdi' ); ?>
|
126 |
+
</p>
|
127 |
+
|
128 |
+
<ul>
|
129 |
+
<li><?php esc_html_e( 'No existing posts, pages, categories, images, custom post types or any other data will be deleted or modified.', 'pt-ocdi' ); ?></li>
|
130 |
+
<li><?php esc_html_e( 'Posts, pages, images, widgets and menus will get imported.', 'pt-ocdi' ); ?></li>
|
131 |
+
<li><?php esc_html_e( 'Please click "Import Demo Data" button only once and wait, it can take a couple of minutes.', 'pt-ocdi' ); ?></li>
|
132 |
+
</ul>
|
133 |
+
</div>
|
134 |
+
|
135 |
+
<div class="ocdi__intro-text">
|
136 |
+
<p><?php esc_html_e( 'Before you begin, make sure all the required plugins are activated.', 'pt-ocdi' ); ?></p>
|
137 |
+
</div>
|
138 |
+
|
139 |
+
<?php if ( empty( $this->import_files ) ) : ?>
|
140 |
+
<div class="notice notice-info below-h2">
|
141 |
+
<p>
|
142 |
+
<?php esc_html_e( 'There are no predefined import files available in this theme. Please upload the import files manually!', 'pt-ocdi' ); ?>
|
143 |
+
</p>
|
144 |
+
</div>
|
145 |
+
<p>
|
146 |
+
<label for="content-file-upload"><?php esc_html_e( 'Choose a XML file for content import:', 'pt-ocdi' ); ?></label>
|
147 |
+
<input id="ocdi__content-file-upload" type="file" name="content-file-upload">
|
148 |
+
<br>
|
149 |
+
<small><?php esc_html_e( 'optional', 'pt-ocdi' ); ?></small> <label for="widget-file-upload"><?php esc_html_e( 'Choose a WIE or JSON file for widget import:', 'pt-ocdi' ); ?></label>
|
150 |
+
<input id="ocdi__widget-file-upload" type="file" name="widget-file-upload">
|
151 |
+
</p>
|
152 |
+
<?php elseif ( 1 < count( $this->import_files ) ) : ?>
|
153 |
+
<p>
|
154 |
+
<select id="ocdi__demo-import-files">
|
155 |
+
<?php foreach ( $this->import_files as $index => $import_file ) : ?>
|
156 |
+
<option value="<?php echo esc_attr( $index ); ?>">
|
157 |
+
<?php echo esc_html( $import_file['import_file_name'] ); ?>
|
158 |
+
</option>
|
159 |
+
<?php endforeach; ?>
|
160 |
+
</select>
|
161 |
+
</p>
|
162 |
+
<?php endif; ?>
|
163 |
+
|
164 |
+
<p>
|
165 |
+
<button class="ocdi__button button-primary js-ocdi-import-data"><?php esc_html_e( 'Import Demo Data', 'pt-ocdi' ); ?></button>
|
166 |
+
</p>
|
167 |
+
|
168 |
+
<div class="ocdi__response js-ocdi-ajax-response"></div>
|
169 |
+
</div>
|
170 |
+
|
171 |
+
<?php
|
172 |
+
}
|
173 |
+
|
174 |
+
|
175 |
+
/**
|
176 |
+
* Enqueue admin scripts (JS and CSS)
|
177 |
+
*
|
178 |
+
* @param string $hook holds info on which admin page you are currently loading.
|
179 |
+
*/
|
180 |
+
public function admin_enqueue_scripts( $hook ) {
|
181 |
+
|
182 |
+
// Enqueue the scripts only on the plugin page.
|
183 |
+
if ( $this->plugin_page === $hook ) {
|
184 |
+
wp_enqueue_script( 'ocdi-main-js', PT_OCDI_URL . 'assets/js/main.js' , array( 'jquery', 'jquery-form' ), PT_OCDI_VERSION );
|
185 |
+
|
186 |
+
wp_localize_script( 'ocdi-main-js', 'ocdi',
|
187 |
+
array(
|
188 |
+
'ajax_url' => admin_url( 'admin-ajax.php' ),
|
189 |
+
'ajax_nonce' => wp_create_nonce( 'ocdi-ajax-verification' ),
|
190 |
+
'loader_text' => esc_html__( 'Importing now, please wait!', 'pt-ocdi' ),
|
191 |
+
)
|
192 |
+
);
|
193 |
+
|
194 |
+
wp_enqueue_style( 'ocdi-main-css', PT_OCDI_URL . 'assets/css/main.css', array() , PT_OCDI_VERSION );
|
195 |
+
}
|
196 |
+
}
|
197 |
+
|
198 |
+
|
199 |
+
/**
|
200 |
+
* Main AJAX callback function for:
|
201 |
+
* 1. prepare import files (uploaded or predefined via filters)
|
202 |
+
* 2. import content
|
203 |
+
* 3. import widgets (optional)
|
204 |
+
* 4. after import setup (optional)
|
205 |
+
*/
|
206 |
+
public function import_demo_data_ajax_callback() {
|
207 |
+
|
208 |
+
// Verify if the AJAX call is valid (checks nonce and current_user_can).
|
209 |
+
OCDI_Helpers::verify_ajax_call();
|
210 |
+
|
211 |
+
// Error messages displayed on front page.
|
212 |
+
$frontend_error_messages = '';
|
213 |
+
|
214 |
+
// Create a date and time string to use for demo and log file names.
|
215 |
+
$demo_import_start_time = date( apply_filters( 'pt-ocdi/date_format_for_file_names', 'Y-m-d__H-i-s' ) );
|
216 |
+
|
217 |
+
// Define log file path.
|
218 |
+
$this->log_file_path = OCDI_Helpers::get_log_path( $demo_import_start_time );
|
219 |
+
|
220 |
+
// Get selected file index or set it to 0.
|
221 |
+
$selected_index = empty( $_POST['selected'] ) ? 0 : absint( $_POST['selected'] );
|
222 |
+
|
223 |
+
/**
|
224 |
+
* 1. Prepare import files.
|
225 |
+
* Manually uploaded import files or predefined import files via filter: pt-ocdi/import_files
|
226 |
+
*/
|
227 |
+
if ( ! empty( $_FILES ) ) { // Using manual file uploads?
|
228 |
+
|
229 |
+
// Get paths for the uploaded files.
|
230 |
+
$selected_import_files = OCDI_Helpers::process_uploaded_files( $_FILES, $this->log_file_path );
|
231 |
+
|
232 |
+
// Set the name of the import files, because we used the uploaded files.
|
233 |
+
$this->import_files[ $selected_index ]['import_file_name'] = esc_html__( 'Manually uploaded files', 'pt-ocdi' );
|
234 |
+
}
|
235 |
+
elseif ( ! empty( $this->import_files[ $selected_index ] ) ) { // Use predefined import files from wp filter: pt-ocdi/import_files.
|
236 |
+
|
237 |
+
// Download the import files (content and widgets files) and save it to variable for later use.
|
238 |
+
$selected_import_files = OCDI_Helpers::download_import_files(
|
239 |
+
$this->import_files[ $selected_index ],
|
240 |
+
$demo_import_start_time
|
241 |
+
);
|
242 |
+
|
243 |
+
// Check Errors.
|
244 |
+
if ( is_wp_error( $selected_import_files ) ) {
|
245 |
+
|
246 |
+
// Write error to log file and send an AJAX response with the error.
|
247 |
+
OCDI_Helpers::log_error_and_send_ajax_response(
|
248 |
+
$selected_import_files->get_error_message(),
|
249 |
+
$this->log_file_path,
|
250 |
+
esc_html__( 'Downloaded files', 'pt-ocdi' )
|
251 |
+
);
|
252 |
+
}
|
253 |
+
|
254 |
+
// Add this message to log file.
|
255 |
+
$log_added = OCDI_Helpers::append_to_file(
|
256 |
+
sprintf(
|
257 |
+
__( 'The import files for: %s were successfully downloaded!', 'pt-ocdi' ),
|
258 |
+
$this->import_files[ $selected_index ]['import_file_name']
|
259 |
+
) . OCDI_Helpers::import_file_info( $selected_import_files ),
|
260 |
+
$this->log_file_path,
|
261 |
+
esc_html__( 'Downloaded files' , 'pt-ocdi' )
|
262 |
+
);
|
263 |
+
}
|
264 |
+
else {
|
265 |
+
|
266 |
+
// Send JSON Error response to the AJAX call.
|
267 |
+
wp_send_json( esc_html__( 'No import files specified!', 'pt-ocdi' ) );
|
268 |
+
}
|
269 |
+
|
270 |
+
/**
|
271 |
+
* 2. Import content.
|
272 |
+
* Returns any errors greater then the "error" logger level, that will be displayed on front page.
|
273 |
+
*/
|
274 |
+
$frontend_error_messages .= $this->import_content( $selected_import_files['content'] );
|
275 |
+
|
276 |
+
/**
|
277 |
+
* 3. Import widgets.
|
278 |
+
*/
|
279 |
+
if ( ! empty( $selected_import_files['widgets'] ) ) {
|
280 |
+
$this->import_widgets( $selected_import_files['widgets'] );
|
281 |
+
}
|
282 |
+
|
283 |
+
/**
|
284 |
+
* 4. After import setup.
|
285 |
+
*/
|
286 |
+
if ( false !== has_action( 'pt-ocdi/after_import' ) ) {
|
287 |
+
|
288 |
+
// Run the after_import action to setup other settings.
|
289 |
+
$this->after_import_setup( $this->import_files[ $selected_index ] );
|
290 |
+
}
|
291 |
+
|
292 |
+
// Display final messages (success or error messages).
|
293 |
+
if ( empty( $frontend_error_messages ) ) {
|
294 |
+
$response['message'] = sprintf(
|
295 |
+
__( '%1$s%3$sThat\'s it, all done!%4$s%2$sThe demo import has finished. Please check your page and make sure that everything has imported correctly. If it did, you can deactivate the %3$sOne Click Demo Import%4$s plugin, because it has done its job.%5$s', 'pt-ocdi' ),
|
296 |
+
'<div class="notice notice-success"><p>',
|
297 |
+
'<br>',
|
298 |
+
'<strong>',
|
299 |
+
'</strong>',
|
300 |
+
'</p></div>'
|
301 |
+
);
|
302 |
+
}
|
303 |
+
else {
|
304 |
+
$response['message'] = $frontend_error_messages . '<br>';
|
305 |
+
$response['message'] .= sprintf(
|
306 |
+
__( '%1$sThe demo import has finished, but there were some import errors.%2$sMore details about the errors can be found in this %3$s%5$slog file%6$s%4$s%7$s', 'pt-ocdi' ),
|
307 |
+
'<div class="notice notice-error"><p>',
|
308 |
+
'<br>',
|
309 |
+
'<strong>',
|
310 |
+
'</strong>',
|
311 |
+
'<a href="' . OCDI_Helpers::get_log_url( $this->log_file_path ) .'" target="_blank">',
|
312 |
+
'</a>',
|
313 |
+
'</p></div>'
|
314 |
+
);
|
315 |
+
}
|
316 |
+
|
317 |
+
wp_send_json( $response );
|
318 |
+
}
|
319 |
+
|
320 |
+
|
321 |
+
/**
|
322 |
+
* Import content from an WP XML file.
|
323 |
+
*
|
324 |
+
* @param string $import_file_path path to the import file.
|
325 |
+
*/
|
326 |
+
private function import_content( $import_file_path ) {
|
327 |
+
|
328 |
+
// This should be replaced with multiple AJAX calls (import in smaller chunks)
|
329 |
+
// so that it would not come to the Internal Error, because of the PHP script timeout.
|
330 |
+
// Also this function has no effect when PHP is running in safe mode
|
331 |
+
// http://php.net/manual/en/function.set-time-limit.php.
|
332 |
+
// Increase PHP max execution time.
|
333 |
+
set_time_limit( apply_filters( 'pt-ocdi/set_time_limit_for_demo_data_import', 300 ) );
|
334 |
+
|
335 |
+
// Import content.
|
336 |
+
if ( ! empty( $import_file_path ) ) {
|
337 |
+
ob_start();
|
338 |
+
$this->importer->import( $import_file_path );
|
339 |
+
$message = ob_get_clean();
|
340 |
+
|
341 |
+
// Add this message to log file.
|
342 |
+
$log_added = OCDI_Helpers::append_to_file(
|
343 |
+
$message . PHP_EOL . 'MAX EXECUTION TIME = ' . ini_get( 'max_execution_time' ),
|
344 |
+
$this->log_file_path,
|
345 |
+
esc_html__( 'Importing content' , 'pt-ocdi' )
|
346 |
+
);
|
347 |
+
}
|
348 |
+
|
349 |
+
// Return any error messages for the front page output (errors, critical, alert and emergency level messages only).
|
350 |
+
return $this->logger->error_output;
|
351 |
+
}
|
352 |
+
|
353 |
+
|
354 |
+
/**
|
355 |
+
* Import widgets from WIE or JSON file.
|
356 |
+
*
|
357 |
+
* @param string $widget_import_file_path path to the widget import file.
|
358 |
+
*/
|
359 |
+
private function import_widgets( $widget_import_file_path ) {
|
360 |
+
|
361 |
+
// Widget import results.
|
362 |
+
$results = array();
|
363 |
+
|
364 |
+
// Create an instance of the Widget Importer.
|
365 |
+
$widget_importer = new OCDI_Widget_Importer();
|
366 |
+
|
367 |
+
// Import widgets.
|
368 |
+
if ( ! empty( $widget_import_file_path ) ) {
|
369 |
+
|
370 |
+
// Import widgets and return result.
|
371 |
+
$results = $widget_importer->import_widgets( $widget_import_file_path );
|
372 |
+
}
|
373 |
+
|
374 |
+
// Check for errors.
|
375 |
+
if ( is_wp_error( $results ) ) {
|
376 |
+
|
377 |
+
// Write error to log file and send an AJAX response with the error.
|
378 |
+
OCDI_Helpers::log_error_and_send_ajax_response(
|
379 |
+
$widget_output->get_error_message(),
|
380 |
+
$this->log_file_path,
|
381 |
+
esc_html__( 'Importing widgets', 'pt-ocdi' )
|
382 |
+
);
|
383 |
+
}
|
384 |
+
|
385 |
+
ob_start();
|
386 |
+
$widget_importer->format_results_for_log( $results );
|
387 |
+
$message = ob_get_clean();
|
388 |
+
|
389 |
+
// Add this message to log file.
|
390 |
+
$log_added = OCDI_Helpers::append_to_file(
|
391 |
+
$message,
|
392 |
+
$this->log_file_path,
|
393 |
+
esc_html__( 'Importing widgets' , 'pt-ocdi' )
|
394 |
+
);
|
395 |
+
}
|
396 |
+
|
397 |
+
|
398 |
+
/**
|
399 |
+
* Setup other things after the whole import process is finished.
|
400 |
+
*
|
401 |
+
* @param array $selected_import with information about the selected import.
|
402 |
+
*/
|
403 |
+
private function after_import_setup( $selected_import ) {
|
404 |
+
|
405 |
+
// Enable users to add custom code to the end of the import process.
|
406 |
+
// Append any output to the log file.
|
407 |
+
ob_start();
|
408 |
+
do_action( 'pt-ocdi/after_import', $selected_import );
|
409 |
+
$message = ob_get_clean();
|
410 |
+
|
411 |
+
// Add this message to log file.
|
412 |
+
$log_added = OCDI_Helpers::append_to_file(
|
413 |
+
$message,
|
414 |
+
$this->log_file_path,
|
415 |
+
esc_html__( 'After import setup' , 'pt-ocdi' )
|
416 |
+
);
|
417 |
+
}
|
418 |
+
|
419 |
+
|
420 |
+
/**
|
421 |
+
* Get data from filters, after the theme has loaded and instantiate the importer.
|
422 |
+
*/
|
423 |
+
public function setup_plugin_with_filter_data() {
|
424 |
+
|
425 |
+
// Get info of import data files and filter it.
|
426 |
+
$this->import_files = OCDI_Helpers::validate_import_file_info( apply_filters( 'pt-ocdi/import_files', array() ) );
|
427 |
+
|
428 |
+
// Importer options array.
|
429 |
+
$importer_options = apply_filters( 'pt-ocdi/importer_options', array(
|
430 |
+
'fetch_attachments' => true,
|
431 |
+
) );
|
432 |
+
|
433 |
+
// Logger options for the logger used in the importer.
|
434 |
+
$logger_options = apply_filters( 'pt-ocdi/logger_options', array(
|
435 |
+
'logger_min_level' => 'warning',
|
436 |
+
) );
|
437 |
+
|
438 |
+
// Configure logger instance and set it to the importer.
|
439 |
+
$this->logger = new OCDI_Logger();
|
440 |
+
$this->logger->min_level = $logger_options['logger_min_level'];
|
441 |
+
|
442 |
+
// Create importer instance with proper parameters.
|
443 |
+
$this->importer = new OCDI_Importer( $importer_options, $this->logger );
|
444 |
+
}
|
445 |
+
}
|
446 |
+
|
447 |
+
$PT_One_Click_Demo_Import = PT_One_Click_Demo_Import::getInstance();
|
readme.txt
ADDED
@@ -0,0 +1,95 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
=== One Click Demo Import ===
|
2 |
+
Contributors: capuderg, cyman
|
3 |
+
Tags: import, content, demo, data, widgets, settings
|
4 |
+
Requires at least: 4.0.0
|
5 |
+
Tested up to: 4.4.2
|
6 |
+
Stable tag: 1.0.0
|
7 |
+
License: GPLv3 or later
|
8 |
+
|
9 |
+
Import your demo content, widgets and theme settings with one click. Theme authors! Enable simple demo import for your theme demo data.
|
10 |
+
|
11 |
+
== Description ==
|
12 |
+
|
13 |
+
This plugin will create a submenu page under Appearance with the title **Import demo data**.
|
14 |
+
|
15 |
+
If the theme you are using does not have any predefined import files, then you will be presented with two file upload inputs. First one is required and you will have to upload a demo content XML file, for the actual demo import. The second one is optional and will ask you for a WIE or JSON file for widgets import.
|
16 |
+
|
17 |
+
This plugin is using the improved WP import that you can find here: https://github.com/humanmade/WordPress-Importer.
|
18 |
+
|
19 |
+
The best feature of this plugin is, that theme authors can define import files in their themes and so all you (the user of the theme) have to do is click on the "Import Demo Data" button.
|
20 |
+
|
21 |
+
**How do theme author define these files?** The answer is in the FAQ section.
|
22 |
+
|
23 |
+
All progress of this plugin's work is logged in a log file in the default WP upload directory, together with the demo content and widgets import files used in the importing process.
|
24 |
+
|
25 |
+
NOTE: This plugin is still a work in progress!
|
26 |
+
|
27 |
+
NOTE: There is no setting to "connect" authors from the demo import file to the existing users in your WP site (like there is in the original WP Importer plugin).
|
28 |
+
|
29 |
+
== Installation ==
|
30 |
+
|
31 |
+
Upload the One Click Demo Import plugin to your WordPress site, Activate it, and that's it.
|
32 |
+
|
33 |
+
Once the plugin is activated you will find the actual import setting page under *Appearance -> Import Demo Data*.
|
34 |
+
|
35 |
+
== Frequently Asked Questions ==
|
36 |
+
|
37 |
+
= I have activated the plugin. Where is the "Import Demo Data" page? =
|
38 |
+
|
39 |
+
You will find the import page in *wp-admin -> Appearance -> Import Demo Data*.
|
40 |
+
|
41 |
+
= Where are the demo import files and the log files saved? =
|
42 |
+
|
43 |
+
The files used in the demo import will be saved to the default WordPress uploads directory. An example of that directory would be: `../wp-content/uploads/2016/03/`.
|
44 |
+
|
45 |
+
= How to predefine demo imports? =
|
46 |
+
|
47 |
+
This question is for theme authors. To predefine demo imports, you just have to add the following code structure, with your own values to your theme (using the `pt-ocdi/import_files` filter):
|
48 |
+
|
49 |
+
`
|
50 |
+
function ocdi_import_files() {
|
51 |
+
return array(
|
52 |
+
array(
|
53 |
+
'import_file_name' => 'Demo Import 1',
|
54 |
+
'import_file_url' => 'http://www.your_domain.com/ocdi/demo-content.xml',
|
55 |
+
'import_widget_file_url' => 'http://www.your_domain.com/ocdi/widgets.json'
|
56 |
+
),
|
57 |
+
array(
|
58 |
+
'import_file_name' => 'Demo Import 2',
|
59 |
+
'import_file_url' => 'http://www.your_domain.com/ocdi/demo-content2.xml',
|
60 |
+
'import_widget_file_url' => 'http://www.your_domain.com/ocdi/widgets2.json'
|
61 |
+
),
|
62 |
+
);
|
63 |
+
}
|
64 |
+
add_filter( 'pt-ocdi/import_files', 'ocdi_import_files' );
|
65 |
+
`
|
66 |
+
|
67 |
+
= How to handle different "after import setups" depending on which predefined import was selected? =
|
68 |
+
|
69 |
+
This question might be asked by a theme author wanting to implement different after import setups for multiple predefined demo imports. Lets say we have predefined two demo imports with the following names: 'Demo Import 1' and 'Demo Import 2', the code for after import setup would be (using the `pt-ocdi/after_import` filter):
|
70 |
+
|
71 |
+
`
|
72 |
+
function ocdi_after_import( $selected_import ) {
|
73 |
+
echo "This will be displayed on all after imports!";
|
74 |
+
|
75 |
+
if ( 'Demo Import 1' === $selected_import['import_file_name'] ) {
|
76 |
+
echo "This will be displayed only on after import if user selects Demo Import 1";
|
77 |
+
|
78 |
+
// Set logo in customizer
|
79 |
+
set_theme_mod( 'logo_img', get_template_directory_uri() . '/assets/images/logo1.png' );
|
80 |
+
}
|
81 |
+
elseif ( 'Demo Import 2' === $selected_import['import_file_name'] ) {
|
82 |
+
echo "This will be displayed only on after import if user selects Demo Import 2";
|
83 |
+
|
84 |
+
// Set logo in customizer
|
85 |
+
set_theme_mod( 'logo_img', get_template_directory_uri() . '/assets/images/logo2.png' );
|
86 |
+
}
|
87 |
+
}
|
88 |
+
add_action( 'pt-ocdi/after_import', 'ocdi_after_import' );
|
89 |
+
`
|
90 |
+
|
91 |
+
== Changelog ==
|
92 |
+
|
93 |
+
= 1.0.0 =
|
94 |
+
|
95 |
+
*Release Date - 25 March 2016*
|
vendor/humanmade/WordPress-Importer/class-logger-cli.php
ADDED
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class WP_Importer_Logger_CLI extends WP_Importer_Logger {
|
4 |
+
public $min_level = 'notice';
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Logs with an arbitrary level.
|
8 |
+
*
|
9 |
+
* @param mixed $level
|
10 |
+
* @param string $message
|
11 |
+
* @param array $context
|
12 |
+
* @return null
|
13 |
+
*/
|
14 |
+
public function log( $level, $message, array $context = array() ) {
|
15 |
+
if ( $this->level_to_numeric( $level ) < $this->level_to_numeric( $this->min_level ) ) {
|
16 |
+
return;
|
17 |
+
}
|
18 |
+
|
19 |
+
printf(
|
20 |
+
'[%s] %s' . PHP_EOL,
|
21 |
+
strtoupper( $level ),
|
22 |
+
$message
|
23 |
+
);
|
24 |
+
}
|
25 |
+
|
26 |
+
public static function level_to_numeric( $level ) {
|
27 |
+
$levels = array(
|
28 |
+
'emergency' => 8,
|
29 |
+
'alert' => 7,
|
30 |
+
'critical' => 6,
|
31 |
+
'error' => 5,
|
32 |
+
'warning' => 4,
|
33 |
+
'notice' => 3,
|
34 |
+
'info' => 2,
|
35 |
+
'debug' => 1,
|
36 |
+
);
|
37 |
+
if ( ! isset( $levels[ $level ] ) ) {
|
38 |
+
return 0;
|
39 |
+
}
|
40 |
+
|
41 |
+
return $levels[ $level ];
|
42 |
+
}
|
43 |
+
}
|
vendor/humanmade/WordPress-Importer/class-logger.php
ADDED
@@ -0,0 +1,136 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Describes a logger instance
|
5 |
+
*
|
6 |
+
* Based on PSR-3: http://www.php-fig.org/psr/psr-3/
|
7 |
+
*
|
8 |
+
* The message MUST be a string or object implementing __toString().
|
9 |
+
*
|
10 |
+
* The message MAY contain placeholders in the form: {foo} where foo
|
11 |
+
* will be replaced by the context data in key "foo".
|
12 |
+
*
|
13 |
+
* The context array can contain arbitrary data, the only assumption that
|
14 |
+
* can be made by implementors is that if an Exception instance is given
|
15 |
+
* to produce a stack trace, it MUST be in a key named "exception".
|
16 |
+
*
|
17 |
+
* See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md
|
18 |
+
* for the full interface specification.
|
19 |
+
*/
|
20 |
+
class WP_Importer_Logger {
|
21 |
+
/**
|
22 |
+
* System is unusable.
|
23 |
+
*
|
24 |
+
* @param string $message
|
25 |
+
* @param array $context
|
26 |
+
* @return null
|
27 |
+
*/
|
28 |
+
public function emergency( $message, array $context = array() ) {
|
29 |
+
return $this->log( 'emergency', $message, $context );
|
30 |
+
}
|
31 |
+
|
32 |
+
/**
|
33 |
+
* Action must be taken immediately.
|
34 |
+
*
|
35 |
+
* Example: Entire website down, database unavailable, etc. This should
|
36 |
+
* trigger the SMS alerts and wake you up.
|
37 |
+
*
|
38 |
+
* @param string $message
|
39 |
+
* @param array $context
|
40 |
+
* @return null
|
41 |
+
*/
|
42 |
+
public function alert( $message, array $context = array() ) {
|
43 |
+
return $this->log( 'alert', $message, $context );
|
44 |
+
}
|
45 |
+
|
46 |
+
/**
|
47 |
+
* Critical conditions.
|
48 |
+
*
|
49 |
+
* Example: Application component unavailable, unexpected exception.
|
50 |
+
*
|
51 |
+
* @param string $message
|
52 |
+
* @param array $context
|
53 |
+
* @return null
|
54 |
+
*/
|
55 |
+
public function critical( $message, array $context = array() ) {
|
56 |
+
return $this->log( 'critical', $message, $context );
|
57 |
+
}
|
58 |
+
|
59 |
+
/**
|
60 |
+
* Runtime errors that do not require immediate action but should typically
|
61 |
+
* be logged and monitored.
|
62 |
+
*
|
63 |
+
* @param string $message
|
64 |
+
* @param array $context
|
65 |
+
* @return null
|
66 |
+
*/
|
67 |
+
public function error( $message, array $context = array()) {
|
68 |
+
return $this->log( 'error', $message, $context );
|
69 |
+
}
|
70 |
+
|
71 |
+
/**
|
72 |
+
* Exceptional occurrences that are not errors.
|
73 |
+
*
|
74 |
+
* Example: Use of deprecated APIs, poor use of an API, undesirable things
|
75 |
+
* that are not necessarily wrong.
|
76 |
+
*
|
77 |
+
* @param string $message
|
78 |
+
* @param array $context
|
79 |
+
* @return null
|
80 |
+
*/
|
81 |
+
public function warning( $message, array $context = array() ) {
|
82 |
+
return $this->log( 'warning', $message, $context );
|
83 |
+
}
|
84 |
+
|
85 |
+
/**
|
86 |
+
* Normal but significant events.
|
87 |
+
*
|
88 |
+
* @param string $message
|
89 |
+
* @param array $context
|
90 |
+
* @return null
|
91 |
+
*/
|
92 |
+
public function notice( $message, array $context = array() ) {
|
93 |
+
return $this->log( 'notice', $message, $context );
|
94 |
+
}
|
95 |
+
|
96 |
+
/**
|
97 |
+
* Interesting events.
|
98 |
+
*
|
99 |
+
* Example: User logs in, SQL logs.
|
100 |
+
*
|
101 |
+
* @param string $message
|
102 |
+
* @param array $context
|
103 |
+
* @return null
|
104 |
+
*/
|
105 |
+
public function info( $message, array $context = array() ) {
|
106 |
+
return $this->log( 'info', $message, $context );
|
107 |
+
}
|
108 |
+
|
109 |
+
/**
|
110 |
+
* Detailed debug information.
|
111 |
+
*
|
112 |
+
* @param string $message
|
113 |
+
* @param array $context
|
114 |
+
* @return null
|
115 |
+
*/
|
116 |
+
public function debug( $message, array $context = array() ) {
|
117 |
+
return $this->log( 'debug', $message, $context );
|
118 |
+
}
|
119 |
+
|
120 |
+
/**
|
121 |
+
* Logs with an arbitrary level.
|
122 |
+
*
|
123 |
+
* @param mixed $level
|
124 |
+
* @param string $message
|
125 |
+
* @param array $context
|
126 |
+
* @return null
|
127 |
+
*/
|
128 |
+
public function log( $level, $message, array $context = array() ) {
|
129 |
+
$this->messages[] = array(
|
130 |
+
'timestamp' => time(),
|
131 |
+
'level' => $level,
|
132 |
+
'message' => $message,
|
133 |
+
'context' => $context,
|
134 |
+
);
|
135 |
+
}
|
136 |
+
}
|
vendor/humanmade/WordPress-Importer/class-wxr-importer.php
ADDED
@@ -0,0 +1,2067 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class WXR_Importer extends WP_Importer {
|
4 |
+
/**
|
5 |
+
* Maximum supported WXR version
|
6 |
+
*/
|
7 |
+
const MAX_WXR_VERSION = 1.2;
|
8 |
+
|
9 |
+
/**
|
10 |
+
* Regular expression for checking if a post references an attachment
|
11 |
+
*
|
12 |
+
* Note: This is a quick, weak check just to exclude text-only posts. More
|
13 |
+
* vigorous checking is done later to verify.
|
14 |
+
*/
|
15 |
+
const REGEX_HAS_ATTACHMENT_REFS = '!
|
16 |
+
(
|
17 |
+
# Match anything with an image or attachment class
|
18 |
+
class=[\'"].*?\b(wp-image-\d+|attachment-[\w\-]+)\b
|
19 |
+
|
|
20 |
+
# Match anything that looks like an upload URL
|
21 |
+
src=[\'"][^\'"]*(
|
22 |
+
[0-9]{4}/[0-9]{2}/[^\'"]+\.(jpg|jpeg|png|gif)
|
23 |
+
|
|
24 |
+
content/uploads[^\'"]+
|
25 |
+
)[\'"]
|
26 |
+
)!ix';
|
27 |
+
|
28 |
+
/**
|
29 |
+
* Version of WXR we're importing.
|
30 |
+
*
|
31 |
+
* Defaults to 1.0 for compatibility. Typically overridden by a
|
32 |
+
* `<wp:wxr_version>` tag at the start of the file.
|
33 |
+
*
|
34 |
+
* @var string
|
35 |
+
*/
|
36 |
+
protected $version = '1.0';
|
37 |
+
|
38 |
+
// information to import from WXR file
|
39 |
+
protected $categories = array();
|
40 |
+
protected $tags = array();
|
41 |
+
protected $base_url = '';
|
42 |
+
|
43 |
+
// TODO: REMOVE THESE
|
44 |
+
protected $processed_terms = array();
|
45 |
+
protected $processed_posts = array();
|
46 |
+
protected $processed_menu_items = array();
|
47 |
+
protected $menu_item_orphans = array();
|
48 |
+
protected $missing_menu_items = array();
|
49 |
+
|
50 |
+
// NEW STYLE
|
51 |
+
protected $mapping = array();
|
52 |
+
protected $requires_remapping = array();
|
53 |
+
protected $exists = array();
|
54 |
+
protected $user_slug_override = array();
|
55 |
+
|
56 |
+
protected $url_remap = array();
|
57 |
+
protected $featured_images = array();
|
58 |
+
|
59 |
+
/**
|
60 |
+
* Logger instance.
|
61 |
+
*
|
62 |
+
* @var WP_Importer_Logger
|
63 |
+
*/
|
64 |
+
protected $logger;
|
65 |
+
|
66 |
+
/**
|
67 |
+
* Constructor
|
68 |
+
*
|
69 |
+
* @param array $options {
|
70 |
+
* @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.)
|
71 |
+
* @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.)
|
72 |
+
* @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.)
|
73 |
+
* @var bool $update_attachment_guids Should attachment GUIDs be updated to the new URL? (True updates the GUID, which keeps compatibility with v1, false doesn't update, and allows deduplication and reimporting. Default is false.)
|
74 |
+
* @var bool $fetch_attachments Fetch attachments from the remote server. (True fetches and creates attachment posts, false skips attachments. Default is false.)
|
75 |
+
* @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.)
|
76 |
+
* }
|
77 |
+
*/
|
78 |
+
public function __construct( $options = array() ) {
|
79 |
+
// Initialize some important variables
|
80 |
+
$empty_types = array(
|
81 |
+
'post' => array(),
|
82 |
+
'comment' => array(),
|
83 |
+
'term' => array(),
|
84 |
+
'user' => array(),
|
85 |
+
);
|
86 |
+
|
87 |
+
$this->mapping = $empty_types;
|
88 |
+
$this->mapping['user_slug'] = array();
|
89 |
+
$this->mapping['term_id'] = array();
|
90 |
+
$this->requires_remapping = $empty_types;
|
91 |
+
$this->exists = $empty_types;
|
92 |
+
|
93 |
+
$this->options = wp_parse_args( $options, array(
|
94 |
+
'prefill_existing_posts' => true,
|
95 |
+
'prefill_existing_comments' => true,
|
96 |
+
'prefill_existing_terms' => true,
|
97 |
+
'update_attachment_guids' => false,
|
98 |
+
'fetch_attachments' => false,
|
99 |
+
'aggressive_url_search' => false,
|
100 |
+
) );
|
101 |
+
}
|
102 |
+
|
103 |
+
public function set_logger( $logger ) {
|
104 |
+
$this->logger = $logger;
|
105 |
+
}
|
106 |
+
|
107 |
+
/**
|
108 |
+
* Get a stream reader for the file.
|
109 |
+
*
|
110 |
+
* @param string $file Path to the XML file.
|
111 |
+
* @return XMLReader|WP_Error Reader instance on success, error otherwise.
|
112 |
+
*/
|
113 |
+
protected function get_reader( $file ) {
|
114 |
+
// Avoid loading external entities for security
|
115 |
+
$old_value = null;
|
116 |
+
if ( function_exists( 'libxml_disable_entity_loader' ) ) {
|
117 |
+
// $old_value = libxml_disable_entity_loader( true );
|
118 |
+
}
|
119 |
+
|
120 |
+
$reader = new XMLReader();
|
121 |
+
$status = $reader->open( $file );
|
122 |
+
|
123 |
+
if ( ! is_null( $old_value ) ) {
|
124 |
+
// libxml_disable_entity_loader( $old_value );
|
125 |
+
}
|
126 |
+
|
127 |
+
if ( ! $status ) {
|
128 |
+
return new WP_Error( 'wxr_importer.cannot_parse', __( 'Could not open the file for parsing', 'wordpress-importer' ) );
|
129 |
+
}
|
130 |
+
|
131 |
+
return $reader;
|
132 |
+
}
|
133 |
+
|
134 |
+
/**
|
135 |
+
* The main controller for the actual import stage.
|
136 |
+
*
|
137 |
+
* @param string $file Path to the WXR file for importing
|
138 |
+
*/
|
139 |
+
public function parse_authors( $file ) {
|
140 |
+
// Let's run the actual importer now, woot
|
141 |
+
$reader = $this->get_reader( $file );
|
142 |
+
if ( is_wp_error( $reader ) ) {
|
143 |
+
return $reader;
|
144 |
+
}
|
145 |
+
|
146 |
+
// Set the version to compatibility mode first
|
147 |
+
$this->version = '1.0';
|
148 |
+
|
149 |
+
// Start parsing!
|
150 |
+
$authors = array();
|
151 |
+
while ( $reader->read() ) {
|
152 |
+
// Only deal with element opens
|
153 |
+
if ( $reader->nodeType !== XMLReader::ELEMENT ) {
|
154 |
+
continue;
|
155 |
+
}
|
156 |
+
|
157 |
+
switch ( $reader->name ) {
|
158 |
+
case 'wp:wxr_version':
|
159 |
+
// Upgrade to the correct version
|
160 |
+
$this->version = $reader->readString();
|
161 |
+
|
162 |
+
if ( version_compare( $this->version, self::MAX_WXR_VERSION, '>' ) ) {
|
163 |
+
$this->logger->warning( sprintf(
|
164 |
+
__( 'This WXR file (version %s) is newer than the importer (version %s) and may not be supported. Please consider updating.', 'wordpress-importer' ),
|
165 |
+
$this->version,
|
166 |
+
self::MAX_WXR_VERSION
|
167 |
+
) );
|
168 |
+
}
|
169 |
+
|
170 |
+
// Handled everything in this node, move on to the next
|
171 |
+
$reader->next();
|
172 |
+
break;
|
173 |
+
|
174 |
+
case 'wp:wp_author':
|
175 |
+
$node = $reader->expand();
|
176 |
+
|
177 |
+
$parsed = $this->parse_author_node( $node );
|
178 |
+
if ( is_wp_error( $parsed ) ) {
|
179 |
+
$this->log_error( $parsed );
|
180 |
+
|
181 |
+
// Skip the rest of this post
|
182 |
+
$reader->next();
|
183 |
+
break;
|
184 |
+
}
|
185 |
+
|
186 |
+
$authors[] = $parsed;
|
187 |
+
|
188 |
+
// Handled everything in this node, move on to the next
|
189 |
+
$reader->next();
|
190 |
+
break;
|
191 |
+
}
|
192 |
+
}
|
193 |
+
|
194 |
+
return $authors;
|
195 |
+
}
|
196 |
+
|
197 |
+
/**
|
198 |
+
* The main controller for the actual import stage.
|
199 |
+
*
|
200 |
+
* @param string $file Path to the WXR file for importing
|
201 |
+
*/
|
202 |
+
public function import( $file ) {
|
203 |
+
add_filter( 'import_post_meta_key', array( $this, 'is_valid_meta_key' ) );
|
204 |
+
add_filter( 'http_request_timeout', array( &$this, 'bump_request_timeout' ) );
|
205 |
+
|
206 |
+
$result = $this->import_start( $file );
|
207 |
+
if ( is_wp_error( $result ) ) {
|
208 |
+
return $result;
|
209 |
+
}
|
210 |
+
|
211 |
+
// Let's run the actual importer now, woot
|
212 |
+
$reader = $this->get_reader( $file );
|
213 |
+
if ( is_wp_error( $reader ) ) {
|
214 |
+
return $reader;
|
215 |
+
}
|
216 |
+
|
217 |
+
// Set the version to compatibility mode first
|
218 |
+
$this->version = '1.0';
|
219 |
+
|
220 |
+
// Reset other variables
|
221 |
+
$this->base_url = '';
|
222 |
+
|
223 |
+
// Start parsing!
|
224 |
+
while ( $reader->read() ) {
|
225 |
+
// Only deal with element opens
|
226 |
+
if ( $reader->nodeType !== XMLReader::ELEMENT ) {
|
227 |
+
continue;
|
228 |
+
}
|
229 |
+
|
230 |
+
switch ( $reader->name ) {
|
231 |
+
case 'wp:wxr_version':
|
232 |
+
// Upgrade to the correct version
|
233 |
+
$this->version = $reader->readString();
|
234 |
+
|
235 |
+
if ( version_compare( $this->version, self::MAX_WXR_VERSION, '>' ) ) {
|
236 |
+
$this->logger->warning( sprintf(
|
237 |
+
__( 'This WXR file (version %s) is newer than the importer (version %s) and may not be supported. Please consider updating.', 'wordpress-importer' ),
|
238 |
+
$this->version,
|
239 |
+
self::MAX_WXR_VERSION
|
240 |
+
) );
|
241 |
+
}
|
242 |
+
|
243 |
+
// Handled everything in this node, move on to the next
|
244 |
+
$reader->next();
|
245 |
+
break;
|
246 |
+
|
247 |
+
case 'wp:base_site_url':
|
248 |
+
$this->base_url = $reader->readString();
|
249 |
+
|
250 |
+
// Handled everything in this node, move on to the next
|
251 |
+
$reader->next();
|
252 |
+
break;
|
253 |
+
|
254 |
+
case 'item':
|
255 |
+
$node = $reader->expand();
|
256 |
+
$parsed = $this->parse_post_node( $node );
|
257 |
+
if ( is_wp_error( $parsed ) ) {
|
258 |
+
$this->log_error( $parsed );
|
259 |
+
|
260 |
+
// Skip the rest of this post
|
261 |
+
$reader->next();
|
262 |
+
break;
|
263 |
+
}
|
264 |
+
|
265 |
+
$this->process_post( $parsed['data'], $parsed['meta'], $parsed['comments'], $parsed['terms'] );
|
266 |
+
|
267 |
+
// Handled everything in this node, move on to the next
|
268 |
+
$reader->next();
|
269 |
+
break;
|
270 |
+
|
271 |
+
case 'wp:wp_author':
|
272 |
+
$node = $reader->expand();
|
273 |
+
|
274 |
+
$parsed = $this->parse_author_node( $node );
|
275 |
+
if ( is_wp_error( $parsed ) ) {
|
276 |
+
$this->log_error( $parsed );
|
277 |
+
|
278 |
+
// Skip the rest of this post
|
279 |
+
$reader->next();
|
280 |
+
break;
|
281 |
+
}
|
282 |
+
|
283 |
+
$status = $this->process_author( $parsed['data'], $parsed['meta'] );
|
284 |
+
if ( is_wp_error( $status ) ) {
|
285 |
+
$this->log_error( $status );
|
286 |
+
}
|
287 |
+
|
288 |
+
// Handled everything in this node, move on to the next
|
289 |
+
$reader->next();
|
290 |
+
break;
|
291 |
+
|
292 |
+
case 'wp:category':
|
293 |
+
$node = $reader->expand();
|
294 |
+
|
295 |
+
$parsed = $this->parse_term_node( $node, 'category' );
|
296 |
+
if ( is_wp_error( $parsed ) ) {
|
297 |
+
$this->log_error( $parsed );
|
298 |
+
|
299 |
+
// Skip the rest of this post
|
300 |
+
$reader->next();
|
301 |
+
break;
|
302 |
+
}
|
303 |
+
|
304 |
+
$status = $this->process_term( $parsed['data'], $parsed['meta'] );
|
305 |
+
|
306 |
+
// Handled everything in this node, move on to the next
|
307 |
+
$reader->next();
|
308 |
+
break;
|
309 |
+
|
310 |
+
case 'wp:tag':
|
311 |
+
$node = $reader->expand();
|
312 |
+
|
313 |
+
$parsed = $this->parse_term_node( $node, 'tag' );
|
314 |
+
if ( is_wp_error( $parsed ) ) {
|
315 |
+
$this->log_error( $parsed );
|
316 |
+
|
317 |
+
// Skip the rest of this post
|
318 |
+
$reader->next();
|
319 |
+
break;
|
320 |
+
}
|
321 |
+
|
322 |
+
$status = $this->process_term( $parsed['data'], $parsed['meta'] );
|
323 |
+
|
324 |
+
// Handled everything in this node, move on to the next
|
325 |
+
$reader->next();
|
326 |
+
break;
|
327 |
+
|
328 |
+
case 'wp:term':
|
329 |
+
$node = $reader->expand();
|
330 |
+
|
331 |
+
$parsed = $this->parse_term_node( $node );
|
332 |
+
if ( is_wp_error( $parsed ) ) {
|
333 |
+
$this->log_error( $parsed );
|
334 |
+
|
335 |
+
// Skip the rest of this post
|
336 |
+
$reader->next();
|
337 |
+
break;
|
338 |
+
}
|
339 |
+
|
340 |
+
$status = $this->process_term( $parsed['data'], $parsed['meta'] );
|
341 |
+
|
342 |
+
// Handled everything in this node, move on to the next
|
343 |
+
$reader->next();
|
344 |
+
break;
|
345 |
+
|
346 |
+
default:
|
347 |
+
// Skip this node, probably handled by something already
|
348 |
+
break;
|
349 |
+
}
|
350 |
+
}
|
351 |
+
|
352 |
+
// Now that we've done the main processing, do any required
|
353 |
+
// post-processing and remapping.
|
354 |
+
$this->post_process();
|
355 |
+
|
356 |
+
if ( $this->options['aggressive_url_search'] ) {
|
357 |
+
$this->replace_attachment_urls_in_content();
|
358 |
+
}
|
359 |
+
// $this->remap_featured_images();
|
360 |
+
|
361 |
+
$this->import_end();
|
362 |
+
}
|
363 |
+
|
364 |
+
/**
|
365 |
+
* Log an error instance to the logger.
|
366 |
+
*
|
367 |
+
* @param WP_Error $error Error instance to log.
|
368 |
+
*/
|
369 |
+
protected function log_error( WP_Error $error ) {
|
370 |
+
$this->logger->warning( $error->get_error_message() );
|
371 |
+
|
372 |
+
// Log the data as debug info too
|
373 |
+
$data = $error->get_error_data();
|
374 |
+
if ( ! empty( $data ) ) {
|
375 |
+
$this->logger->debug( var_export( $data, true ) );
|
376 |
+
}
|
377 |
+
}
|
378 |
+
|
379 |
+
/**
|
380 |
+
* Parses the WXR file and prepares us for the task of processing parsed data
|
381 |
+
*
|
382 |
+
* @param string $file Path to the WXR file for importing
|
383 |
+
*/
|
384 |
+
protected function import_start( $file ) {
|
385 |
+
if ( ! is_file( $file ) ) {
|
386 |
+
return new WP_Error( 'wxr_importer.file_missing', __( 'The file does not exist, please try again.', 'wordpress-importer' ) );
|
387 |
+
}
|
388 |
+
|
389 |
+
// Suspend bunches of stuff in WP core
|
390 |
+
wp_defer_term_counting( true );
|
391 |
+
wp_defer_comment_counting( true );
|
392 |
+
wp_suspend_cache_invalidation( true );
|
393 |
+
|
394 |
+
// Prefill exists calls if told to
|
395 |
+
if ( $this->options['prefill_existing_posts'] ) {
|
396 |
+
$this->prefill_existing_posts();
|
397 |
+
}
|
398 |
+
if ( $this->options['prefill_existing_comments'] ) {
|
399 |
+
$this->prefill_existing_comments();
|
400 |
+
}
|
401 |
+
if ( $this->options['prefill_existing_terms'] ) {
|
402 |
+
$this->prefill_existing_terms();
|
403 |
+
}
|
404 |
+
|
405 |
+
/**
|
406 |
+
* Begin the import.
|
407 |
+
*
|
408 |
+
* Fires before the import process has begun. If you need to suspend
|
409 |
+
* caching or heavy processing on hooks, do so here.
|
410 |
+
*/
|
411 |
+
do_action( 'import_start' );
|
412 |
+
}
|
413 |
+
|
414 |
+
/**
|
415 |
+
* Performs post-import cleanup of files and the cache
|
416 |
+
*/
|
417 |
+
protected function import_end() {
|
418 |
+
// Re-enable stuff in core
|
419 |
+
wp_suspend_cache_invalidation( false );
|
420 |
+
wp_cache_flush();
|
421 |
+
foreach ( get_taxonomies() as $tax ) {
|
422 |
+
delete_option( "{$tax}_children" );
|
423 |
+
_get_term_hierarchy( $tax );
|
424 |
+
}
|
425 |
+
|
426 |
+
wp_defer_term_counting( false );
|
427 |
+
wp_defer_comment_counting( false );
|
428 |
+
|
429 |
+
/**
|
430 |
+
* Complete the import.
|
431 |
+
*
|
432 |
+
* Fires after the import process has finished. If you need to update
|
433 |
+
* your cache or re-enable processing, do so here.
|
434 |
+
*/
|
435 |
+
do_action( 'import_end' );
|
436 |
+
}
|
437 |
+
|
438 |
+
/**
|
439 |
+
* Set the user mapping.
|
440 |
+
*
|
441 |
+
* @param array $mapping List of map arrays (containing `old_slug`, `old_id`, `new_id`)
|
442 |
+
*/
|
443 |
+
public function set_user_mapping( $mapping ) {
|
444 |
+
foreach ( $mapping as $map ) {
|
445 |
+
if ( empty( $map['old_slug'] ) || empty( $map['old_id'] ) || empty( $map['new_id'] ) ) {
|
446 |
+
$this->logger->warning( __( 'Invalid author mapping', 'wordpress-importer') );
|
447 |
+
$this->logger->debug( var_export( $map, true ) );
|
448 |
+
continue;
|
449 |
+
}
|
450 |
+
|
451 |
+
$old_slug = $map['old_slug'];
|
452 |
+
$old_id = $map['old_id'];
|
453 |
+
$new_id = $map['new_id'];
|
454 |
+
|
455 |
+
$this->mapping['user'][ $old_id ] = $new_id;
|
456 |
+
$this->mapping['user_slug'][ $old_slug ] = $new_id;
|
457 |
+
}
|
458 |
+
}
|
459 |
+
|
460 |
+
/**
|
461 |
+
* Set the user slug overrides.
|
462 |
+
*
|
463 |
+
* Allows overriding the slug in the import with a custom/renamed version.
|
464 |
+
*
|
465 |
+
* @param string[] $overrides Map of old slug to new slug.
|
466 |
+
*/
|
467 |
+
public function set_user_slug_overrides( $overrides ) {
|
468 |
+
foreach ( $overrides as $original => $renamed ) {
|
469 |
+
$this->user_slug_override[ $original ] = $renamed;
|
470 |
+
}
|
471 |
+
}
|
472 |
+
|
473 |
+
/**
|
474 |
+
* Parse a post node into post data.
|
475 |
+
*
|
476 |
+
* @param DOMElement $node Parent node of post data (typically `item`).
|
477 |
+
* @return array|WP_Error Post data array on success, error otherwise.
|
478 |
+
*/
|
479 |
+
protected function parse_post_node( $node ) {
|
480 |
+
$data = array();
|
481 |
+
$meta = array();
|
482 |
+
$comments = array();
|
483 |
+
$terms = array();
|
484 |
+
|
485 |
+
foreach ( $node->childNodes as $child ) {
|
486 |
+
// We only care about child elements
|
487 |
+
if ( $child->nodeType !== XML_ELEMENT_NODE ) {
|
488 |
+
continue;
|
489 |
+
}
|
490 |
+
|
491 |
+
switch ( $child->tagName ) {
|
492 |
+
case 'wp:post_type':
|
493 |
+
$data['post_type'] = $child->textContent;
|
494 |
+
break;
|
495 |
+
|
496 |
+
case 'title':
|
497 |
+
$data['post_title'] = $child->textContent;
|
498 |
+
break;
|
499 |
+
|
500 |
+
case 'guid':
|
501 |
+
$data['guid'] = $child->textContent;
|
502 |
+
break;
|
503 |
+
|
504 |
+
case 'dc:creator':
|
505 |
+
$data['post_author'] = $child->textContent;
|
506 |
+
break;
|
507 |
+
|
508 |
+
case 'content:encoded':
|
509 |
+
$data['post_content'] = $child->textContent;
|
510 |
+
break;
|
511 |
+
|
512 |
+
case 'excerpt:encoded':
|
513 |
+
$data['post_excerpt'] = $child->textContent;
|
514 |
+
break;
|
515 |
+
|
516 |
+
case 'wp:post_id':
|
517 |
+
$data['post_id'] = $child->textContent;
|
518 |
+
break;
|
519 |
+
|
520 |
+
case 'wp:post_date':
|
521 |
+
$data['post_date'] = $child->textContent;
|
522 |
+
break;
|
523 |
+
|
524 |
+
case 'wp:post_date_gmt':
|
525 |
+
$data['post_date_gmt'] = $child->textContent;
|
526 |
+
break;
|
527 |
+
|
528 |
+
case 'wp:comment_status':
|
529 |
+
$data['comment_status'] = $child->textContent;
|
530 |
+
break;
|
531 |
+
|
532 |
+
case 'wp:ping_status':
|
533 |
+
$data['ping_status'] = $child->textContent;
|
534 |
+
break;
|
535 |
+
|
536 |
+
case 'wp:post_name':
|
537 |
+
$data['post_name'] = $child->textContent;
|
538 |
+
break;
|
539 |
+
|
540 |
+
case 'wp:status':
|
541 |
+
$data['post_status'] = $child->textContent;
|
542 |
+
|
543 |
+
if ( $data['post_status'] === 'auto-draft' ) {
|
544 |
+
// Bail now
|
545 |
+
return new WP_Error(
|
546 |
+
'wxr_importer.post.cannot_import_draft',
|
547 |
+
__( 'Cannot import auto-draft posts' ),
|
548 |
+
$data
|
549 |
+
);
|
550 |
+
}
|
551 |
+
break;
|
552 |
+
|
553 |
+
case 'wp:post_parent':
|
554 |
+
$data['post_parent'] = $child->textContent;
|
555 |
+
break;
|
556 |
+
|
557 |
+
case 'wp:menu_order':
|
558 |
+
$data['menu_order'] = $child->textContent;
|
559 |
+
break;
|
560 |
+
|
561 |
+
case 'wp:post_password':
|
562 |
+
$data['post_password'] = $child->textContent;
|
563 |
+
break;
|
564 |
+
|
565 |
+
case 'wp:is_sticky':
|
566 |
+
$data['is_sticky'] = $child->textContent;
|
567 |
+
break;
|
568 |
+
|
569 |
+
case 'wp:attachment_url':
|
570 |
+
$data['attachment_url'] = $child->textContent;
|
571 |
+
break;
|
572 |
+
|
573 |
+
case 'wp:postmeta':
|
574 |
+
$meta_item = $this->parse_meta_node( $child );
|
575 |
+
if ( ! empty( $meta_item ) ) {
|
576 |
+
$meta[] = $meta_item;
|
577 |
+
}
|
578 |
+
break;
|
579 |
+
|
580 |
+
case 'wp:comment':
|
581 |
+
$comment_item = $this->parse_comment_node( $child );
|
582 |
+
if ( ! empty( $comment_item ) ) {
|
583 |
+
$comments[] = $comment_item;
|
584 |
+
}
|
585 |
+
break;
|
586 |
+
|
587 |
+
case 'category':
|
588 |
+
$term_item = $this->parse_category_node( $child );
|
589 |
+
if ( ! empty( $term_item ) ) {
|
590 |
+
$terms[] = $term_item;
|
591 |
+
}
|
592 |
+
break;
|
593 |
+
}
|
594 |
+
}
|
595 |
+
|
596 |
+
return compact( 'data', 'meta', 'comments', 'terms' );
|
597 |
+
}
|
598 |
+
|
599 |
+
/**
|
600 |
+
* Create new posts based on import information
|
601 |
+
*
|
602 |
+
* Posts marked as having a parent which doesn't exist will become top level items.
|
603 |
+
* Doesn't create a new post if: the post type doesn't exist, the given post ID
|
604 |
+
* is already noted as imported or a post with the same title and date already exists.
|
605 |
+
* Note that new/updated terms, comments and meta are imported for the last of the above.
|
606 |
+
*/
|
607 |
+
protected function process_post( $data, $meta, $comments, $terms ) {
|
608 |
+
/**
|
609 |
+
* Pre-process post data.
|
610 |
+
*
|
611 |
+
* @param array $data Post data. (Return empty to skip.)
|
612 |
+
* @param array $meta Meta data.
|
613 |
+
* @param array $comments Comments on the post.
|
614 |
+
* @param array $terms Terms on the post.
|
615 |
+
*/
|
616 |
+
$data = apply_filters( 'wxr_importer.pre_process.post', $data, $meta, $comments, $terms );
|
617 |
+
if ( empty( $data ) ) {
|
618 |
+
return false;
|
619 |
+
}
|
620 |
+
|
621 |
+
$original_id = isset( $data['post_id'] ) ? (int) $data['post_id'] : 0;
|
622 |
+
$parent_id = isset( $data['post_parent'] ) ? (int) $data['post_parent'] : 0;
|
623 |
+
$author_id = isset( $data['post_author'] ) ? (int) $data['post_author'] : 0;
|
624 |
+
|
625 |
+
// Have we already processed this?
|
626 |
+
if ( isset( $this->mapping['post'][ $original_id ] ) ) {
|
627 |
+
return;
|
628 |
+
}
|
629 |
+
|
630 |
+
$post_type_object = get_post_type_object( $data['post_type'] );
|
631 |
+
|
632 |
+
// Is this type even valid?
|
633 |
+
if ( ! $post_type_object ) {
|
634 |
+
$this->logger->warning( sprintf(
|
635 |
+
__( 'Failed to import "%s": Invalid post type %s', 'wordpress-importer' ),
|
636 |
+
$data['post_title'],
|
637 |
+
$data['post_type']
|
638 |
+
) );
|
639 |
+
return false;
|
640 |
+
}
|
641 |
+
|
642 |
+
$post_exists = $this->post_exists( $data );
|
643 |
+
if ( $post_exists ) {
|
644 |
+
$this->logger->info( sprintf(
|
645 |
+
__('%s "%s" already exists.', 'wordpress-importer'),
|
646 |
+
$post_type_object->labels->singular_name,
|
647 |
+
$data['post_title']
|
648 |
+
) );
|
649 |
+
|
650 |
+
return false;
|
651 |
+
}
|
652 |
+
|
653 |
+
// Map the parent post, or mark it as one we need to fix
|
654 |
+
$requires_remapping = false;
|
655 |
+
if ( $parent_id ) {
|
656 |
+
if ( isset( $this->mapping['post'][ $parent_id ] ) ) {
|
657 |
+
$data['post_parent'] = $this->mapping['post'][ $parent_id ];
|
658 |
+
} else {
|
659 |
+
$meta[] = array( 'key' => '_wxr_import_parent', 'value' => $parent_id );
|
660 |
+
$requires_remapping = true;
|
661 |
+
|
662 |
+
$data['post_parent'] = 0;
|
663 |
+
}
|
664 |
+
}
|
665 |
+
|
666 |
+
// Map the author, or mark it as one we need to fix
|
667 |
+
$author = sanitize_user( $data['post_author'], true );
|
668 |
+
if ( isset( $this->mapping['user_slug'][ $author ] ) ) {
|
669 |
+
$data['post_author'] = $this->mapping['user_slug'][ $author ];
|
670 |
+
}
|
671 |
+
else {
|
672 |
+
$meta[] = array( 'key' => '_wxr_import_user_slug', 'value' => $author );
|
673 |
+
$requires_remapping = true;
|
674 |
+
|
675 |
+
$data['post_author'] = (int) get_current_user_id();
|
676 |
+
}
|
677 |
+
|
678 |
+
// Does the post look like it contains attachment images?
|
679 |
+
if ( preg_match( self::REGEX_HAS_ATTACHMENT_REFS, $data['post_content'] ) ) {
|
680 |
+
$meta[] = array( 'key' => '_wxr_import_has_attachment_refs', 'value' => true );
|
681 |
+
$requires_remapping = true;
|
682 |
+
}
|
683 |
+
|
684 |
+
// Whitelist to just the keys we allow
|
685 |
+
$postdata = array(
|
686 |
+
'import_id' => $data['post_id'],
|
687 |
+
);
|
688 |
+
$allowed = array(
|
689 |
+
'post_author' => true,
|
690 |
+
'post_date' => true,
|
691 |
+
'post_date_gmt' => true,
|
692 |
+
'post_content' => true,
|
693 |
+
'post_excerpt' => true,
|
694 |
+
'post_title' => true,
|
695 |
+
'post_status' => true,
|
696 |
+
'post_name' => true,
|
697 |
+
'comment_status' => true,
|
698 |
+
'ping_status' => true,
|
699 |
+
'guid' => true,
|
700 |
+
'post_parent' => true,
|
701 |
+
'menu_order' => true,
|
702 |
+
'post_type' => true,
|
703 |
+
'post_password' => true,
|
704 |
+
);
|
705 |
+
foreach ( $data as $key => $value ) {
|
706 |
+
if ( ! isset( $allowed[ $key ] ) ) {
|
707 |
+
continue;
|
708 |
+
}
|
709 |
+
|
710 |
+
$postdata[ $key ] = $data[ $key ];
|
711 |
+
}
|
712 |
+
|
713 |
+
$postdata = apply_filters( 'wp_import_post_data_processed', $postdata, $data );
|
714 |
+
|
715 |
+
if ( 'attachment' === $postdata['post_type'] ) {
|
716 |
+
if ( ! $this->options['fetch_attachments'] ) {
|
717 |
+
$this->logger->notice( sprintf(
|
718 |
+
__( 'Skipping attachment "%s", fetching attachments disabled' ),
|
719 |
+
$data['post_title']
|
720 |
+
) );
|
721 |
+
return false;
|
722 |
+
}
|
723 |
+
$remote_url = ! empty($data['attachment_url']) ? $data['attachment_url'] : $data['guid'];
|
724 |
+
$post_id = $this->process_attachment( $postdata, $meta, $remote_url );
|
725 |
+
} else {
|
726 |
+
$post_id = wp_insert_post( $postdata, true );
|
727 |
+
do_action( 'wp_import_insert_post', $post_id, $original_id, $postdata, $data );
|
728 |
+
}
|
729 |
+
|
730 |
+
if ( is_wp_error( $post_id ) ) {
|
731 |
+
$this->logger->error( sprintf(
|
732 |
+
__( 'Failed to import "%s" (%s)', 'wordpress-importer' ),
|
733 |
+
$data['post_title'],
|
734 |
+
$post_type_object->labels->singular_name
|
735 |
+
) );
|
736 |
+
$this->logger->debug( $post_id->get_error_message() );
|
737 |
+
return false;
|
738 |
+
}
|
739 |
+
|
740 |
+
// Ensure stickiness is handled correctly too
|
741 |
+
if ( $data['is_sticky'] == 1 ) {
|
742 |
+
stick_post( $post_id );
|
743 |
+
}
|
744 |
+
|
745 |
+
// map pre-import ID to local ID
|
746 |
+
$this->mapping['post'][ $original_id ] = (int) $post_id;
|
747 |
+
if ( $requires_remapping ) {
|
748 |
+
$this->requires_remapping['post'][ $post_id ] = true;
|
749 |
+
}
|
750 |
+
$this->mark_post_exists( $data, $post_id );
|
751 |
+
|
752 |
+
$this->logger->info( sprintf(
|
753 |
+
__( 'Imported "%s" (%s)', 'wordpress-importer' ),
|
754 |
+
$data['post_title'],
|
755 |
+
$post_type_object->labels->singular_name
|
756 |
+
) );
|
757 |
+
$this->logger->debug( sprintf(
|
758 |
+
__( 'Post %d remapped to %d', 'wordpress-importer' ),
|
759 |
+
$original_id,
|
760 |
+
$post_id
|
761 |
+
) );
|
762 |
+
|
763 |
+
// Handle the terms too
|
764 |
+
$terms = apply_filters( 'wp_import_post_terms', $terms, $post_id, $data );
|
765 |
+
|
766 |
+
if ( ! empty( $terms ) ) {
|
767 |
+
$term_ids = array();
|
768 |
+
foreach ( $terms as $term ) {
|
769 |
+
$taxonomy = $term['taxonomy'];
|
770 |
+
$key = sha1( $taxonomy . ':' . $term['slug'] );
|
771 |
+
|
772 |
+
if ( isset( $this->mapping['term'][ $key ] ) ) {
|
773 |
+
$term_ids[ $taxonomy ][] = $this->mapping['term'][ $key ];
|
774 |
+
}
|
775 |
+
else {
|
776 |
+
$meta[] = array( 'key' => '_wxr_import_term', 'value' => $term );
|
777 |
+
$requires_remapping = true;
|
778 |
+
}
|
779 |
+
}
|
780 |
+
|
781 |
+
foreach ( $term_ids as $tax => $ids ) {
|
782 |
+
$tt_ids = wp_set_post_terms( $post_id, $ids, $tax );
|
783 |
+
do_action( 'wp_import_set_post_terms', $tt_ids, $ids, $tax, $post_id, $data );
|
784 |
+
}
|
785 |
+
}
|
786 |
+
|
787 |
+
$this->process_comments( $comments, $post_id, $data );
|
788 |
+
$this->process_post_meta( $meta, $post_id, $data );
|
789 |
+
|
790 |
+
if ( 'nav_menu_item' === $data['post_type'] ) {
|
791 |
+
$this->process_menu_item_meta( $post_id, $data, $meta );
|
792 |
+
}
|
793 |
+
|
794 |
+
/**
|
795 |
+
* Post processing completed.
|
796 |
+
*
|
797 |
+
* @param int $post_id New post ID.
|
798 |
+
* @param array $data Raw data imported for the post.
|
799 |
+
* @param array $meta Raw meta data, already processed by {@see process_post_meta}.
|
800 |
+
* @param array $comments Raw comment data, already processed by {@see process_comments}.
|
801 |
+
* @param array $terms Raw term data, already processed.
|
802 |
+
*/
|
803 |
+
do_action( 'wxr_importer.processed.post', $post_id, $data, $meta, $comments, $terms );
|
804 |
+
}
|
805 |
+
|
806 |
+
/**
|
807 |
+
* Attempt to create a new menu item from import data
|
808 |
+
*
|
809 |
+
* Fails for draft, orphaned menu items and those without an associated nav_menu
|
810 |
+
* or an invalid nav_menu term. If the post type or term object which the menu item
|
811 |
+
* represents doesn't exist then the menu item will not be imported (waits until the
|
812 |
+
* end of the import to retry again before discarding).
|
813 |
+
*
|
814 |
+
* @param array $item Menu item details from WXR file
|
815 |
+
*/
|
816 |
+
protected function process_menu_item_meta( $post_id, $data, $meta ) {
|
817 |
+
|
818 |
+
$item_type = get_post_meta( $post_id, '_menu_item_type', true );
|
819 |
+
$original_object_id = get_post_meta( $post_id, '_menu_item_object_id', true );
|
820 |
+
$object_id = null;
|
821 |
+
|
822 |
+
$this->logger->debug( sprintf( 'Processing menu item %s', $item_type ) );
|
823 |
+
|
824 |
+
$requires_remapping = false;
|
825 |
+
switch ( $item_type ) {
|
826 |
+
case 'taxonomy':
|
827 |
+
if ( isset( $this->mapping['term_id'][ $original_object_id ] ) ) {
|
828 |
+
$object_id = $this->mapping['term_id'][ $original_object_id ];
|
829 |
+
} else {
|
830 |
+
add_post_meta( $post_id, '_wxr_import_menu_item', wp_slash( $original_object_id ) );
|
831 |
+
$requires_remapping = true;
|
832 |
+
}
|
833 |
+
break;
|
834 |
+
|
835 |
+
case 'post_type':
|
836 |
+
if ( isset( $this->mapping['post'][ $original_object_id ] ) ) {
|
837 |
+
$object_id = $this->mapping['post'][ $original_object_id ];
|
838 |
+
} else {
|
839 |
+
add_post_meta( $post_id, '_wxr_import_menu_item', wp_slash( $original_object_id ) );
|
840 |
+
$requires_remapping = true;
|
841 |
+
}
|
842 |
+
break;
|
843 |
+
|
844 |
+
case 'custom':
|
845 |
+
// Custom refers to itself, wonderfully easy.
|
846 |
+
$object_id = $post_id;
|
847 |
+
break;
|
848 |
+
|
849 |
+
default:
|
850 |
+
// associated object is missing or not imported yet, we'll retry later
|
851 |
+
$this->missing_menu_items[] = $item;
|
852 |
+
$this->logger->debug('Unknown menu item type');
|
853 |
+
break;
|
854 |
+
}
|
855 |
+
|
856 |
+
if ( $requires_remapping ) {
|
857 |
+
$this->requires_remapping['post'][ $post_id ] = true;
|
858 |
+
}
|
859 |
+
|
860 |
+
if ( empty( $object_id ) ) {
|
861 |
+
// Nothing needed here.
|
862 |
+
return;
|
863 |
+
}
|
864 |
+
|
865 |
+
$this->logger->debug( sprintf( 'Menu item %d mapped to %d', $original_object_id, $object_id ) );
|
866 |
+
update_post_meta( $post_id, '_menu_item_object_id', wp_slash( $object_id ) );
|
867 |
+
}
|
868 |
+
|
869 |
+
/**
|
870 |
+
* If fetching attachments is enabled then attempt to create a new attachment
|
871 |
+
*
|
872 |
+
* @param array $post Attachment post details from WXR
|
873 |
+
* @param string $url URL to fetch attachment from
|
874 |
+
* @return int|WP_Error Post ID on success, WP_Error otherwise
|
875 |
+
*/
|
876 |
+
protected function process_attachment( $post, $meta, $remote_url ) {
|
877 |
+
// try to use _wp_attached file for upload folder placement to ensure the same location as the export site
|
878 |
+
// e.g. location is 2003/05/image.jpg but the attachment post_date is 2010/09, see media_handle_upload()
|
879 |
+
$post['upload_date'] = $post['post_date'];
|
880 |
+
foreach ( $meta as $meta_item ) {
|
881 |
+
if ( $meta_item['key'] !== '_wp_attached_file' ) {
|
882 |
+
continue;
|
883 |
+
}
|
884 |
+
|
885 |
+
if ( preg_match( '%^[0-9]{4}/[0-9]{2}%', $meta_item['value'], $matches ) ) {
|
886 |
+
$post['upload_date'] = $matches[0];
|
887 |
+
}
|
888 |
+
break;
|
889 |
+
}
|
890 |
+
|
891 |
+
// if the URL is absolute, but does not contain address, then upload it assuming base_site_url
|
892 |
+
if ( preg_match( '|^/[\w\W]+$|', $remote_url ) ) {
|
893 |
+
$remote_url = rtrim( $this->base_url, '/' ) . $remote_url;
|
894 |
+
}
|
895 |
+
|
896 |
+
$upload = $this->fetch_remote_file( $remote_url, $post );
|
897 |
+
if ( is_wp_error( $upload ) ) {
|
898 |
+
return $upload;
|
899 |
+
}
|
900 |
+
|
901 |
+
$info = wp_check_filetype( $upload['file'] );
|
902 |
+
if ( ! $info ) {
|
903 |
+
return new WP_Error( 'attachment_processing_error', __( 'Invalid file type', 'wordpress-importer' ) );
|
904 |
+
}
|
905 |
+
|
906 |
+
$post['post_mime_type'] = $info['type'];
|
907 |
+
|
908 |
+
// WP really likes using the GUID for display. Allow updating it.
|
909 |
+
// See https://core.trac.wordpress.org/ticket/33386
|
910 |
+
if ( $this->options['update_attachment_guids'] ) {
|
911 |
+
$post['guid'] = $upload['url'];
|
912 |
+
}
|
913 |
+
|
914 |
+
// as per wp-admin/includes/upload.php
|
915 |
+
$post_id = wp_insert_attachment( $post, $upload['file'] );
|
916 |
+
if ( is_wp_error( $post_id ) ) {
|
917 |
+
return $post_id;
|
918 |
+
}
|
919 |
+
|
920 |
+
$attachment_metadata = wp_generate_attachment_metadata( $post_id, $upload['file'] );
|
921 |
+
wp_update_attachment_metadata( $post_id, $attachment_metadata );
|
922 |
+
|
923 |
+
// Map this image URL later if we need to
|
924 |
+
$this->url_remap[ $remote_url ] = $upload['url'];
|
925 |
+
|
926 |
+
// If we have a HTTPS URL, ensure the HTTP URL gets replaced too
|
927 |
+
if ( substr( $remote_url, 0, 8 ) === 'https://') {
|
928 |
+
$insecure_url = 'http' . substr( $remote_url, 5 );
|
929 |
+
$this->url_remap[ $insecure_url ] = $upload['url'];
|
930 |
+
}
|
931 |
+
|
932 |
+
if ( $this->options['aggressive_url_search'] ) {
|
933 |
+
// remap resized image URLs, works by stripping the extension and remapping the URL stub.
|
934 |
+
/*if ( preg_match( '!^image/!', $info['type'] ) ) {
|
935 |
+
$parts = pathinfo( $url );
|
936 |
+
$name = basename( $parts['basename'], ".{$parts['extension']}" ); // PATHINFO_FILENAME in PHP 5.2
|
937 |
+
|
938 |
+
$parts_new = pathinfo( $upload['url'] );
|
939 |
+
$name_new = basename( $parts_new['basename'], ".{$parts_new['extension']}" );
|
940 |
+
|
941 |
+
$this->url_remap[$parts['dirname'] . '/' . $name] = $parts_new['dirname'] . '/' . $name_new;
|
942 |
+
}*/
|
943 |
+
}
|
944 |
+
|
945 |
+
return $post_id;
|
946 |
+
}
|
947 |
+
|
948 |
+
/**
|
949 |
+
* Parse a meta node into meta data.
|
950 |
+
*
|
951 |
+
* @param DOMElement $node Parent node of meta data (typically `wp:postmeta` or `wp:commentmeta`).
|
952 |
+
* @return array|null Meta data array on success, or null on error.
|
953 |
+
*/
|
954 |
+
protected function parse_meta_node( $node ) {
|
955 |
+
foreach ( $node->childNodes as $child ) {
|
956 |
+
// We only care about child elements
|
957 |
+
if ( $child->nodeType !== XML_ELEMENT_NODE ) {
|
958 |
+
continue;
|
959 |
+
}
|
960 |
+
|
961 |
+
switch ( $child->tagName ) {
|
962 |
+
case 'wp:meta_key':
|
963 |
+
$key = $child->textContent;
|
964 |
+
break;
|
965 |
+
|
966 |
+
case 'wp:meta_value':
|
967 |
+
$value = $child->textContent;
|
968 |
+
break;
|
969 |
+
}
|
970 |
+
}
|
971 |
+
|
972 |
+
if ( empty( $key ) || empty( $value ) ) {
|
973 |
+
return null;
|
974 |
+
}
|
975 |
+
|
976 |
+
return compact( 'key', 'value' );
|
977 |
+
}
|
978 |
+
|
979 |
+
/**
|
980 |
+
* Process and import post meta items.
|
981 |
+
*
|
982 |
+
* @param array $meta List of meta data arrays
|
983 |
+
* @param int $post_id Post to associate with
|
984 |
+
* @param array $post Post data
|
985 |
+
* @return int|WP_Error Number of meta items imported on success, error otherwise.
|
986 |
+
*/
|
987 |
+
protected function process_post_meta( $meta, $post_id, $post ) {
|
988 |
+
if ( empty( $meta ) ) {
|
989 |
+
return true;
|
990 |
+
}
|
991 |
+
|
992 |
+
foreach ( $meta as $meta_item ) {
|
993 |
+
/**
|
994 |
+
* Pre-process post meta data.
|
995 |
+
*
|
996 |
+
* @param array $meta_item Meta data. (Return empty to skip.)
|
997 |
+
* @param int $post_id Post the meta is attached to.
|
998 |
+
*/
|
999 |
+
$meta_item = apply_filters( 'wxr_importer.pre_process.post_meta', $meta_item, $post_id );
|
1000 |
+
if ( empty( $meta_item ) ) {
|
1001 |
+
return false;
|
1002 |
+
}
|
1003 |
+
|
1004 |
+
$key = apply_filters( 'import_post_meta_key', $meta_item['key'], $post_id, $post );
|
1005 |
+
$value = false;
|
1006 |
+
|
1007 |
+
if ( '_edit_last' == $key ) {
|
1008 |
+
$value = intval( $meta_item['value'] );
|
1009 |
+
if ( ! isset( $this->mapping['user'][ $value ] ) ) {
|
1010 |
+
// Skip!
|
1011 |
+
continue;
|
1012 |
+
}
|
1013 |
+
|
1014 |
+
$value = $this->mapping['user'][ $value ];
|
1015 |
+
}
|
1016 |
+
|
1017 |
+
if ( $key ) {
|
1018 |
+
// export gets meta straight from the DB so could have a serialized string
|
1019 |
+
if ( ! $value )
|
1020 |
+
$value = maybe_unserialize( $meta_item['value'] );
|
1021 |
+
|
1022 |
+
add_post_meta( $post_id, $key, $value );
|
1023 |
+
do_action( 'import_post_meta', $post_id, $key, $value );
|
1024 |
+
|
1025 |
+
// if the post has a featured image, take note of this in case of remap
|
1026 |
+
if ( '_thumbnail_id' == $key )
|
1027 |
+
$this->featured_images[$post_id] = (int) $value;
|
1028 |
+
}
|
1029 |
+
}
|
1030 |
+
|
1031 |
+
return true;
|
1032 |
+
}
|
1033 |
+
|
1034 |
+
/**
|
1035 |
+
* Parse a comment node into comment data.
|
1036 |
+
*
|
1037 |
+
* @param DOMElement $node Parent node of comment data (typically `wp:comment`).
|
1038 |
+
* @return array Comment data array.
|
1039 |
+
*/
|
1040 |
+
protected function parse_comment_node( $node ) {
|
1041 |
+
$data = array(
|
1042 |
+
'commentmeta' => array(),
|
1043 |
+
);
|
1044 |
+
|
1045 |
+
foreach ( $node->childNodes as $child ) {
|
1046 |
+
// We only care about child elements
|
1047 |
+
if ( $child->nodeType !== XML_ELEMENT_NODE ) {
|
1048 |
+
continue;
|
1049 |
+
}
|
1050 |
+
|
1051 |
+
switch ( $child->tagName ) {
|
1052 |
+
case 'wp:comment_id':
|
1053 |
+
$data['comment_id'] = $child->textContent;
|
1054 |
+
break;
|
1055 |
+
case 'wp:comment_author':
|
1056 |
+
$data['comment_author'] = $child->textContent;
|
1057 |
+
break;
|
1058 |
+
|
1059 |
+
case 'wp:comment_author_email':
|
1060 |
+
$data['comment_author_email'] = $child->textContent;
|
1061 |
+
break;
|
1062 |
+
|
1063 |
+
case 'wp:comment_author_IP':
|
1064 |
+
$data['comment_author_IP'] = $child->textContent;
|
1065 |
+
break;
|
1066 |
+
|
1067 |
+
case 'wp:comment_author_url':
|
1068 |
+
$data['comment_author_url'] = $child->textContent;
|
1069 |
+
break;
|
1070 |
+
|
1071 |
+
case 'wp:comment_user_id':
|
1072 |
+
$data['comment_user_id'] = $child->textContent;
|
1073 |
+
break;
|
1074 |
+
|
1075 |
+
case 'wp:comment_date':
|
1076 |
+
$data['comment_date'] = $child->textContent;
|
1077 |
+
break;
|
1078 |
+
|
1079 |
+
case 'wp:comment_date_gmt':
|
1080 |
+
$data['comment_date_gmt'] = $child->textContent;
|
1081 |
+
break;
|
1082 |
+
|
1083 |
+
case 'wp:comment_content':
|
1084 |
+
$data['comment_content'] = $child->textContent;
|
1085 |
+
break;
|
1086 |
+
|
1087 |
+
case 'wp:comment_approved':
|
1088 |
+
$data['comment_approved'] = $child->textContent;
|
1089 |
+
break;
|
1090 |
+
|
1091 |
+
case 'wp:comment_type':
|
1092 |
+
$data['comment_type'] = $child->textContent;
|
1093 |
+
break;
|
1094 |
+
|
1095 |
+
case 'wp:comment_parent':
|
1096 |
+
$data['comment_parent'] = $child->textContent;
|
1097 |
+
break;
|
1098 |
+
|
1099 |
+
case 'wp:commentmeta':
|
1100 |
+
$meta_item = $this->parse_meta_node( $child );
|
1101 |
+
if ( ! empty( $meta_item ) ) {
|
1102 |
+
$data['commentmeta'][] = $meta_item;
|
1103 |
+
}
|
1104 |
+
break;
|
1105 |
+
}
|
1106 |
+
}
|
1107 |
+
|
1108 |
+
return $data;
|
1109 |
+
}
|
1110 |
+
|
1111 |
+
/**
|
1112 |
+
* Process and import comment data.
|
1113 |
+
*
|
1114 |
+
* @param array $comments List of comment data arrays.
|
1115 |
+
* @param int $post_id Post to associate with.
|
1116 |
+
* @param array $post Post data.
|
1117 |
+
* @return int|WP_Error Number of comments imported on success, error otherwise.
|
1118 |
+
*/
|
1119 |
+
protected function process_comments( $comments, $post_id, $post ) {
|
1120 |
+
|
1121 |
+
// TODO: this should use the real value for delta updating
|
1122 |
+
$post_exists = false;
|
1123 |
+
|
1124 |
+
$comments = apply_filters( 'wp_import_post_comments', $comments, $post_id, $post );
|
1125 |
+
if ( empty( $comments ) ) {
|
1126 |
+
return 0;
|
1127 |
+
}
|
1128 |
+
|
1129 |
+
$num_comments = 0;
|
1130 |
+
|
1131 |
+
// Sort by ID to avoid excessive remapping later
|
1132 |
+
usort( $comments, array( $this, 'sort_comments_by_id' ) );
|
1133 |
+
|
1134 |
+
foreach ( $comments as $key => $comment ) {
|
1135 |
+
/**
|
1136 |
+
* Pre-process comment data
|
1137 |
+
*
|
1138 |
+
* @param array $comment Comment data. (Return empty to skip.)
|
1139 |
+
* @param int $post_id Post the comment is attached to.
|
1140 |
+
*/
|
1141 |
+
$comment = apply_filters( 'wxr_importer.pre_process.comment', $comment, $post_id );
|
1142 |
+
if ( empty( $comment ) ) {
|
1143 |
+
return false;
|
1144 |
+
}
|
1145 |
+
|
1146 |
+
$original_id = isset( $comment['comment_id'] ) ? (int) $comment['comment_id'] : 0;
|
1147 |
+
$parent_id = isset( $comment['comment_parent'] ) ? (int) $comment['comment_parent'] : 0;
|
1148 |
+
$author_id = isset( $comment['comment_user_id'] ) ? (int) $comment['comment_user_id'] : 0;
|
1149 |
+
|
1150 |
+
// if this is a new post we can skip the comment_exists() check
|
1151 |
+
// TODO: Check comment_exists for performance
|
1152 |
+
if ( $post_exists && $existing = $this->comment_exists( $comment ) ) {
|
1153 |
+
$this->mapping['comment'][ $original_id ] = $exists;
|
1154 |
+
continue;
|
1155 |
+
}
|
1156 |
+
|
1157 |
+
// Remove meta from the main array
|
1158 |
+
$meta = isset( $comment['commentmeta'] ) ? $comment['commentmeta'] : array();
|
1159 |
+
unset( $comment['commentmeta'] );
|
1160 |
+
|
1161 |
+
// Map the parent comment, or mark it as one we need to fix
|
1162 |
+
$requires_remapping = false;
|
1163 |
+
if ( $parent_id ) {
|
1164 |
+
if ( isset( $this->mapping['comment'][ $parent_id ] ) ) {
|
1165 |
+
$comment['comment_parent'] = $this->mapping['comment'][ $parent_id ];
|
1166 |
+
} else {
|
1167 |
+
// Prepare for remapping later
|
1168 |
+
$meta[] = array( 'key' => '_wxr_import_parent', 'value' => $parent_id );
|
1169 |
+
$requires_remapping = true;
|
1170 |
+
|
1171 |
+
// Wipe the parent for now
|
1172 |
+
$comment['comment_parent'] = 0;
|
1173 |
+
}
|
1174 |
+
}
|
1175 |
+
|
1176 |
+
// Map the author, or mark it as one we need to fix
|
1177 |
+
if ( $author_id ) {
|
1178 |
+
if ( isset( $this->mapping['user'][ $author_id ] ) ) {
|
1179 |
+
$comment['user_id'] = $this->mapping['user'][ $author_id ];
|
1180 |
+
} else {
|
1181 |
+
// Prepare for remapping later
|
1182 |
+
$meta[] = array( 'key' => '_wxr_import_user', 'value' => $author_id );
|
1183 |
+
$requires_remapping = true;
|
1184 |
+
|
1185 |
+
// Wipe the user for now
|
1186 |
+
$comment['user_id'] = 0;
|
1187 |
+
}
|
1188 |
+
}
|
1189 |
+
|
1190 |
+
// Run standard core filters
|
1191 |
+
$comment['comment_post_ID'] = $post_id;
|
1192 |
+
$comment = wp_filter_comment( $comment );
|
1193 |
+
|
1194 |
+
// wp_insert_comment expects slashed data
|
1195 |
+
$comment_id = wp_insert_comment( wp_slash( $comment ) );
|
1196 |
+
$this->mapping['comment'][ $original_id ] = $comment_id;
|
1197 |
+
if ( $requires_remapping ) {
|
1198 |
+
$this->requires_remapping['comment'][ $comment_id ] = true;
|
1199 |
+
}
|
1200 |
+
$this->mark_comment_exists( $comment, $comment_id );
|
1201 |
+
|
1202 |
+
/**
|
1203 |
+
* Comment has been imported.
|
1204 |
+
*
|
1205 |
+
* @param int $comment_id New comment ID
|
1206 |
+
* @param array $comment Comment inserted (`comment_id` item refers to the original ID)
|
1207 |
+
* @param int $post_id Post parent of the comment
|
1208 |
+
* @param array $post Post data
|
1209 |
+
*/
|
1210 |
+
do_action( 'wp_import_insert_comment', $comment_id, $comment, $post_id, $post );
|
1211 |
+
|
1212 |
+
// Process the meta items
|
1213 |
+
foreach ( $meta as $meta_item ) {
|
1214 |
+
$value = maybe_unserialize( $meta_item['value'] );
|
1215 |
+
add_comment_meta( $comment_id, wp_slash( $meta_item['key'] ), wp_slash( $value ) );
|
1216 |
+
}
|
1217 |
+
|
1218 |
+
$num_comments++;
|
1219 |
+
}
|
1220 |
+
|
1221 |
+
return $num_comments;
|
1222 |
+
}
|
1223 |
+
|
1224 |
+
protected function parse_category_node( $node ) {
|
1225 |
+
$data = array(
|
1226 |
+
// Default taxonomy to "category", since this is a `<category>` tag
|
1227 |
+
'taxonomy' => 'category',
|
1228 |
+
);
|
1229 |
+
$meta = array();
|
1230 |
+
|
1231 |
+
if ( $node->hasAttribute( 'domain' ) ) {
|
1232 |
+
$data['taxonomy'] = $node->getAttribute( 'domain' );
|
1233 |
+
}
|
1234 |
+
if ( $node->hasAttribute( 'nicename' ) ) {
|
1235 |
+
$data['slug'] = $node->getAttribute( 'nicename' );
|
1236 |
+
}
|
1237 |
+
|
1238 |
+
$data['name'] = $node->textContent;
|
1239 |
+
|
1240 |
+
if ( empty( $data['slug'] ) ) {
|
1241 |
+
return null;
|
1242 |
+
}
|
1243 |
+
|
1244 |
+
// Just for extra compatibility
|
1245 |
+
if ( $data['taxonomy'] === 'tag' ) {
|
1246 |
+
$data['taxonomy'] = 'post_tag';
|
1247 |
+
}
|
1248 |
+
|
1249 |
+
return $data;
|
1250 |
+
}
|
1251 |
+
|
1252 |
+
/**
|
1253 |
+
* Callback for `usort` to sort comments by ID
|
1254 |
+
*
|
1255 |
+
* @param array $a Comment data for the first comment
|
1256 |
+
* @param array $b Comment data for the second comment
|
1257 |
+
* @return int
|
1258 |
+
*/
|
1259 |
+
public static function sort_comments_by_id( $a, $b ) {
|
1260 |
+
if ( empty( $a['comment_id'] ) ) {
|
1261 |
+
return 1;
|
1262 |
+
}
|
1263 |
+
|
1264 |
+
if ( empty( $b['comment_id'] ) ) {
|
1265 |
+
return -1;
|
1266 |
+
}
|
1267 |
+
|
1268 |
+
return $a['comment_id'] - $b['comment_id'];
|
1269 |
+
}
|
1270 |
+
|
1271 |
+
protected function parse_author_node( $node ) {
|
1272 |
+
$data = array();
|
1273 |
+
$meta = array();
|
1274 |
+
foreach ( $node->childNodes as $child ) {
|
1275 |
+
// We only care about child elements
|
1276 |
+
if ( $child->nodeType !== XML_ELEMENT_NODE ) {
|
1277 |
+
continue;
|
1278 |
+
}
|
1279 |
+
|
1280 |
+
switch ( $child->tagName ) {
|
1281 |
+
case 'wp:author_login':
|
1282 |
+
$data['user_login'] = $child->textContent;
|
1283 |
+
break;
|
1284 |
+
|
1285 |
+
case 'wp:author_id':
|
1286 |
+
$data['ID'] = $child->textContent;
|
1287 |
+
break;
|
1288 |
+
|
1289 |
+
case 'wp:author_email':
|
1290 |
+
$data['user_email'] = $child->textContent;
|
1291 |
+
break;
|
1292 |
+
|
1293 |
+
case 'wp:author_display_name':
|
1294 |
+
$data['display_name'] = $child->textContent;
|
1295 |
+
break;
|
1296 |
+
|
1297 |
+
case 'wp:author_first_name':
|
1298 |
+
$data['first_name'] = $child->textContent;
|
1299 |
+
break;
|
1300 |
+
|
1301 |
+
case 'wp:author_last_name':
|
1302 |
+
$data['last_name'] = $child->textContent;
|
1303 |
+
break;
|
1304 |
+
}
|
1305 |
+
}
|
1306 |
+
|
1307 |
+
return compact( 'data', 'meta' );
|
1308 |
+
}
|
1309 |
+
|
1310 |
+
protected function process_author( $data, $meta ) {
|
1311 |
+
/**
|
1312 |
+
* Pre-process user data.
|
1313 |
+
*
|
1314 |
+
* @param array $data User data. (Return empty to skip.)
|
1315 |
+
* @param array $meta Meta data.
|
1316 |
+
*/
|
1317 |
+
$data = apply_filters( 'wxr_importer.pre_process.user', $data, $meta );
|
1318 |
+
if ( empty( $data ) ) {
|
1319 |
+
return false;
|
1320 |
+
}
|
1321 |
+
|
1322 |
+
// Have we already handled this user?
|
1323 |
+
$original_id = isset( $data['ID'] ) ? $data['ID'] : 0;
|
1324 |
+
$original_slug = $data['user_login'];
|
1325 |
+
|
1326 |
+
if ( isset( $this->mapping['user'][ $original_id ] ) ) {
|
1327 |
+
$existing = $this->mapping['user'][ $original_id ];
|
1328 |
+
|
1329 |
+
// Note the slug mapping if we need to too
|
1330 |
+
if ( ! isset( $this->mapping['user_slug'][ $original_slug ] ) ) {
|
1331 |
+
$this->mapping['user_slug'][ $original_slug ] = $existing;
|
1332 |
+
}
|
1333 |
+
|
1334 |
+
return false;
|
1335 |
+
}
|
1336 |
+
|
1337 |
+
if ( isset( $this->mapping['user_slug'][ $original_slug ] ) ) {
|
1338 |
+
$existing = $this->mapping['user_slug'][ $original_slug ];
|
1339 |
+
|
1340 |
+
// Ensure we note the mapping too
|
1341 |
+
$this->mapping['user'][ $original_id ] = $existing;
|
1342 |
+
|
1343 |
+
return false;
|
1344 |
+
}
|
1345 |
+
|
1346 |
+
// Allow overriding the user's slug
|
1347 |
+
$login = $original_slug;
|
1348 |
+
if ( isset( $this->user_slug_override[ $login ] ) ) {
|
1349 |
+
$login = $this->user_slug_override[ $login ];
|
1350 |
+
}
|
1351 |
+
|
1352 |
+
$userdata = array(
|
1353 |
+
'user_login' => sanitize_user( $login, true ),
|
1354 |
+
'user_pass' => wp_generate_password(),
|
1355 |
+
);
|
1356 |
+
|
1357 |
+
$allowed = array(
|
1358 |
+
'user_email' => true,
|
1359 |
+
'display_name' => true,
|
1360 |
+
'first_name' => true,
|
1361 |
+
'last_name' => true,
|
1362 |
+
);
|
1363 |
+
foreach ( $data as $key => $value ) {
|
1364 |
+
if ( ! isset( $allowed[ $key ] ) ) {
|
1365 |
+
continue;
|
1366 |
+
}
|
1367 |
+
|
1368 |
+
$userdata[ $key ] = $data[ $key ];
|
1369 |
+
}
|
1370 |
+
|
1371 |
+
$user_id = wp_insert_user( wp_slash( $userdata ) );
|
1372 |
+
if ( is_wp_error( $user_id ) ) {
|
1373 |
+
$this->logger->error( sprintf(
|
1374 |
+
__( 'Failed to import user "%s"', 'wordpress-importer' ),
|
1375 |
+
$userdata['user_login']
|
1376 |
+
) );
|
1377 |
+
$this->logger->debug( $user_id->get_error_message() );
|
1378 |
+
return false;
|
1379 |
+
}
|
1380 |
+
|
1381 |
+
if ( $original_id ) {
|
1382 |
+
$this->mapping['user'][ $original_id ] = $user_id;
|
1383 |
+
}
|
1384 |
+
$this->mapping['user_slug'][ $original_slug ] = $user_id;
|
1385 |
+
|
1386 |
+
$this->logger->info( sprintf(
|
1387 |
+
__( 'Imported user "%s"', 'wordpress-importer' ),
|
1388 |
+
$userdata['user_login']
|
1389 |
+
) );
|
1390 |
+
$this->logger->debug( sprintf(
|
1391 |
+
__( 'User %d remapped to %d', 'wordpress-importer' ),
|
1392 |
+
$original_id,
|
1393 |
+
$user_id
|
1394 |
+
) );
|
1395 |
+
|
1396 |
+
// TODO: Implement meta handling once WXR includes it
|
1397 |
+
}
|
1398 |
+
|
1399 |
+
protected function parse_term_node( $node, $type = 'term' ) {
|
1400 |
+
$data = array();
|
1401 |
+
$meta = array();
|
1402 |
+
|
1403 |
+
$tag_name = array(
|
1404 |
+
'id' => 'wp:term_id',
|
1405 |
+
'taxonomy' => 'wp:term_taxonomy',
|
1406 |
+
'slug' => 'wp:term_slug',
|
1407 |
+
'parent' => 'wp:term_parent',
|
1408 |
+
'name' => 'wp:term_name',
|
1409 |
+
'description' => 'wp:term_description',
|
1410 |
+
);
|
1411 |
+
$taxonomy = null;
|
1412 |
+
|
1413 |
+
// Special casing!
|
1414 |
+
switch ( $type ) {
|
1415 |
+
case 'category':
|
1416 |
+
$tag_name['slug'] = 'wp:category_nicename';
|
1417 |
+
$tag_name['parent'] = 'wp:category_parent';
|
1418 |
+
$tag_name['name'] = 'wp:cat_name';
|
1419 |
+
$tag_name['description'] = 'wp:category_description';
|
1420 |
+
$tag_name['taxonomy'] = null;
|
1421 |
+
|
1422 |
+
$data['taxonomy'] = 'category';
|
1423 |
+
break;
|
1424 |
+
|
1425 |
+
case 'tag':
|
1426 |
+
$tag_name['slug'] = 'wp:tag_slug';
|
1427 |
+
$tag_name['parent'] = null;
|
1428 |
+
$tag_name['name'] = 'wp:tag_name';
|
1429 |
+
$tag_name['description'] = 'wp:tag_description';
|
1430 |
+
$tag_name['taxonomy'] = null;
|
1431 |
+
|
1432 |
+
$data['taxonomy'] = 'post_tag';
|
1433 |
+
break;
|
1434 |
+
}
|
1435 |
+
|
1436 |
+
foreach ( $node->childNodes as $child ) {
|
1437 |
+
// We only care about child elements
|
1438 |
+
if ( $child->nodeType !== XML_ELEMENT_NODE ) {
|
1439 |
+
continue;
|
1440 |
+
}
|
1441 |
+
|
1442 |
+
$key = array_search( $child->tagName, $tag_name );
|
1443 |
+
if ( $key ) {
|
1444 |
+
$data[ $key ] = $child->textContent;
|
1445 |
+
}
|
1446 |
+
}
|
1447 |
+
|
1448 |
+
if ( empty( $data['taxonomy'] ) ) {
|
1449 |
+
return null;
|
1450 |
+
}
|
1451 |
+
|
1452 |
+
// Compatibility with WXR 1.0
|
1453 |
+
if ( $data['taxonomy'] === 'tag' ) {
|
1454 |
+
$data['taxonomy'] = 'post_tag';
|
1455 |
+
}
|
1456 |
+
|
1457 |
+
return compact( 'data', 'meta' );
|
1458 |
+
}
|
1459 |
+
|
1460 |
+
protected function process_term( $data, $meta ) {
|
1461 |
+
/**
|
1462 |
+
* Pre-process term data.
|
1463 |
+
*
|
1464 |
+
* @param array $data Term data. (Return empty to skip.)
|
1465 |
+
* @param array $meta Meta data.
|
1466 |
+
*/
|
1467 |
+
$data = apply_filters( 'wxr_importer.pre_process.term', $data, $meta );
|
1468 |
+
if ( empty( $data ) ) {
|
1469 |
+
return false;
|
1470 |
+
}
|
1471 |
+
|
1472 |
+
$original_id = isset( $data['id'] ) ? (int) $data['id'] : 0;
|
1473 |
+
$parent_id = isset( $data['parent'] ) ? (int) $data['parent'] : 0;
|
1474 |
+
|
1475 |
+
$mapping_key = sha1( $data['taxonomy'] . ':' . $data['slug'] );
|
1476 |
+
if ( $existing = $this->term_exists( $data ) ) {
|
1477 |
+
$this->mapping['term'][ $mapping_key ] = $existing;
|
1478 |
+
$this->mapping['term_id'][ $original_id ] = $existing;
|
1479 |
+
return false;
|
1480 |
+
}
|
1481 |
+
|
1482 |
+
// WP really likes to repeat itself in export files
|
1483 |
+
if ( isset( $this->mapping['term'][ $mapping_key ] ) ) {
|
1484 |
+
return false;
|
1485 |
+
}
|
1486 |
+
|
1487 |
+
$termdata = array();
|
1488 |
+
$allowed = array(
|
1489 |
+
'slug' => true,
|
1490 |
+
'description' => true,
|
1491 |
+
);
|
1492 |
+
|
1493 |
+
// Map the parent comment, or mark it as one we need to fix
|
1494 |
+
// TODO: add parent mapping and remapping
|
1495 |
+
/*$requires_remapping = false;
|
1496 |
+
if ( $parent_id ) {
|
1497 |
+
if ( isset( $this->mapping['term'][ $parent_id ] ) ) {
|
1498 |
+
$data['parent'] = $this->mapping['term'][ $parent_id ];
|
1499 |
+
} else {
|
1500 |
+
// Prepare for remapping later
|
1501 |
+
$meta[] = array( 'key' => '_wxr_import_parent', 'value' => $parent_id );
|
1502 |
+
$requires_remapping = true;
|
1503 |
+
|
1504 |
+
// Wipe the parent for now
|
1505 |
+
$data['parent'] = 0;
|
1506 |
+
}
|
1507 |
+
}*/
|
1508 |
+
|
1509 |
+
foreach ( $data as $key => $value ) {
|
1510 |
+
if ( ! isset( $allowed[ $key ] ) ) {
|
1511 |
+
continue;
|
1512 |
+
}
|
1513 |
+
|
1514 |
+
$termdata[ $key ] = $data[ $key ];
|
1515 |
+
}
|
1516 |
+
|
1517 |
+
$result = wp_insert_term( $data['name'], $data['taxonomy'], $termdata );
|
1518 |
+
if ( is_wp_error( $result ) ) {
|
1519 |
+
$this->logger->warning( sprintf(
|
1520 |
+
__( 'Failed to import %s %s', 'wordpress-importer' ),
|
1521 |
+
$data['taxonomy'],
|
1522 |
+
$data['name']
|
1523 |
+
) );
|
1524 |
+
$this->logger->debug( $result->get_error_message() );
|
1525 |
+
do_action( 'wp_import_insert_term_failed', $result, $data );
|
1526 |
+
return false;
|
1527 |
+
}
|
1528 |
+
|
1529 |
+
$term_id = $result['term_id'];
|
1530 |
+
|
1531 |
+
$this->mapping['term'][ $mapping_key ] = $term_id;
|
1532 |
+
$this->mapping['term_id'][ $original_id ] = $term_id;
|
1533 |
+
|
1534 |
+
$this->logger->info( sprintf(
|
1535 |
+
__( 'Imported "%s" (%s)', 'wordpress-importer' ),
|
1536 |
+
$data['name'],
|
1537 |
+
$data['taxonomy']
|
1538 |
+
) );
|
1539 |
+
$this->logger->debug( sprintf(
|
1540 |
+
__( 'Term %d remapped to %d', 'wordpress-importer' ),
|
1541 |
+
$original_id,
|
1542 |
+
$term_id
|
1543 |
+
) );
|
1544 |
+
|
1545 |
+
do_action( 'wp_import_insert_term', $term_id, $data );
|
1546 |
+
}
|
1547 |
+
|
1548 |
+
/**
|
1549 |
+
* Attempt to download a remote file attachment
|
1550 |
+
*
|
1551 |
+
* @param string $url URL of item to fetch
|
1552 |
+
* @param array $post Attachment details
|
1553 |
+
* @return array|WP_Error Local file location details on success, WP_Error otherwise
|
1554 |
+
*/
|
1555 |
+
protected function fetch_remote_file( $url, $post ) {
|
1556 |
+
// extract the file name and extension from the url
|
1557 |
+
$file_name = basename( $url );
|
1558 |
+
|
1559 |
+
// get placeholder file in the upload dir with a unique, sanitized filename
|
1560 |
+
$upload = wp_upload_bits( $file_name, 0, '', $post['upload_date'] );
|
1561 |
+
if ( $upload['error'] )
|
1562 |
+
return new WP_Error( 'upload_dir_error', $upload['error'] );
|
1563 |
+
|
1564 |
+
// fetch the remote url and write it to the placeholder file
|
1565 |
+
$response = wp_remote_get( $url, array(
|
1566 |
+
'stream' => true,
|
1567 |
+
'filename' => $upload['file']
|
1568 |
+
) );
|
1569 |
+
|
1570 |
+
// request failed
|
1571 |
+
if ( is_wp_error( $response ) ) {
|
1572 |
+
@unlink( $upload['file'] );
|
1573 |
+
return $response;
|
1574 |
+
}
|
1575 |
+
|
1576 |
+
$code = (int) wp_remote_retrieve_response_code( $response );
|
1577 |
+
|
1578 |
+
// make sure the fetch was successful
|
1579 |
+
if ( $code !== 200 ) {
|
1580 |
+
@unlink( $upload['file'] );
|
1581 |
+
return new WP_Error(
|
1582 |
+
'import_file_error',
|
1583 |
+
sprintf(
|
1584 |
+
__('Remote server returned %1$d %2$s for %3$s', 'wordpress-importer'),
|
1585 |
+
$code,
|
1586 |
+
get_status_header_desc( $code ),
|
1587 |
+
$url
|
1588 |
+
)
|
1589 |
+
);
|
1590 |
+
}
|
1591 |
+
|
1592 |
+
$filesize = filesize( $upload['file'] );
|
1593 |
+
$headers = wp_remote_retrieve_headers( $response );
|
1594 |
+
|
1595 |
+
if ( isset( $headers['content-length'] ) && $filesize != $headers['content-length'] ) {
|
1596 |
+
@unlink( $upload['file'] );
|
1597 |
+
return new WP_Error( 'import_file_error', __('Remote file is incorrect size', 'wordpress-importer') );
|
1598 |
+
}
|
1599 |
+
|
1600 |
+
if ( 0 == $filesize ) {
|
1601 |
+
@unlink( $upload['file'] );
|
1602 |
+
return new WP_Error( 'import_file_error', __('Zero size file downloaded', 'wordpress-importer') );
|
1603 |
+
}
|
1604 |
+
|
1605 |
+
$max_size = (int) $this->max_attachment_size();
|
1606 |
+
if ( ! empty( $max_size ) && $filesize > $max_size ) {
|
1607 |
+
@unlink( $upload['file'] );
|
1608 |
+
return new WP_Error( 'import_file_error', sprintf(__('Remote file is too large, limit is %s', 'wordpress-importer'), size_format($max_size) ) );
|
1609 |
+
}
|
1610 |
+
|
1611 |
+
return $upload;
|
1612 |
+
}
|
1613 |
+
|
1614 |
+
protected function post_process() {
|
1615 |
+
// Time to tackle any left-over bits
|
1616 |
+
if ( ! empty( $this->requires_remapping['post'] ) ) {
|
1617 |
+
$this->post_process_posts( $this->requires_remapping['post'] );
|
1618 |
+
}
|
1619 |
+
if ( ! empty( $this->requires_remapping['comment'] ) ) {
|
1620 |
+
$this->post_process_comments( $this->requires_remapping['comment'] );
|
1621 |
+
}
|
1622 |
+
}
|
1623 |
+
|
1624 |
+
protected function post_process_posts( $todo ) {
|
1625 |
+
foreach ( $todo as $post_id => $_ ) {
|
1626 |
+
$this->logger->debug( sprintf(
|
1627 |
+
// Note: title intentionally not used to skip extra processing
|
1628 |
+
// for when debug logging is off
|
1629 |
+
__( 'Running post-processing for post %d', 'wordpress-importer' ),
|
1630 |
+
$post_id
|
1631 |
+
) );
|
1632 |
+
|
1633 |
+
$data = array();
|
1634 |
+
|
1635 |
+
$parent_id = get_post_meta( $post_id, '_wxr_import_parent', true );
|
1636 |
+
if ( ! empty( $parent_id ) ) {
|
1637 |
+
// Have we imported the parent now?
|
1638 |
+
if ( isset( $this->mapping['post'][ $parent_id ] ) ) {
|
1639 |
+
$data['post_parent'] = $this->mapping['post'][ $parent_id ];
|
1640 |
+
}
|
1641 |
+
else {
|
1642 |
+
$this->logger->warning( sprintf(
|
1643 |
+
__( 'Could not find the post parent for "%s" (post #%d)', 'wordpress-importer' ),
|
1644 |
+
get_the_title( $post_id ),
|
1645 |
+
$post_id
|
1646 |
+
) );
|
1647 |
+
$this->logger->debug( sprintf(
|
1648 |
+
__( 'Post %d was imported with parent %d, but could not be found', 'wordpress-importer' ),
|
1649 |
+
$post_id,
|
1650 |
+
$parent_id
|
1651 |
+
) );
|
1652 |
+
}
|
1653 |
+
}
|
1654 |
+
|
1655 |
+
$author_slug = get_post_meta( $post_id, '_wxr_import_user_slug', true );
|
1656 |
+
if ( ! empty( $author_slug ) ) {
|
1657 |
+
// Have we imported the user now?
|
1658 |
+
if ( isset( $this->mapping['user_slug'][ $author_slug ] ) ) {
|
1659 |
+
$data['post_author'] = $this->mapping['user_slug'][ $author_slug ];
|
1660 |
+
}
|
1661 |
+
else {
|
1662 |
+
$this->logger->warning( sprintf(
|
1663 |
+
__( 'Could not find the author for "%s" (post #%d)', 'wordpress-importer' ),
|
1664 |
+
get_the_title( $post_id ),
|
1665 |
+
$post_id
|
1666 |
+
) );
|
1667 |
+
$this->logger->debug( sprintf(
|
1668 |
+
__( 'Post %d was imported with author "%s", but could not be found', 'wordpress-importer' ),
|
1669 |
+
$post_id,
|
1670 |
+
$author_slug
|
1671 |
+
) );
|
1672 |
+
}
|
1673 |
+
}
|
1674 |
+
|
1675 |
+
$has_attachments = get_post_meta( $post_id, '_wxr_import_has_attachment_refs', true );
|
1676 |
+
if ( ! empty( $has_attachments ) ) {
|
1677 |
+
$post = get_post( $post_id );
|
1678 |
+
$content = $post->post_content;
|
1679 |
+
|
1680 |
+
// Replace all the URLs we've got
|
1681 |
+
$new_content = str_replace( array_keys( $this->url_remap ), $this->url_remap, $content );
|
1682 |
+
if ( $new_content !== $content ) {
|
1683 |
+
$data['post_content'] = $new_content;
|
1684 |
+
}
|
1685 |
+
}
|
1686 |
+
|
1687 |
+
if ( get_post_type( $post_id ) === 'nav_menu_item' ) {
|
1688 |
+
$this->post_process_menu_item( $post_id );
|
1689 |
+
}
|
1690 |
+
|
1691 |
+
// Do we have updates to make?
|
1692 |
+
if ( empty( $data ) ) {
|
1693 |
+
$this->logger->debug( sprintf(
|
1694 |
+
__( 'Post %d was marked for post-processing, but none was required.', 'wordpress-importer' ),
|
1695 |
+
$post_id
|
1696 |
+
) );
|
1697 |
+
continue;
|
1698 |
+
}
|
1699 |
+
|
1700 |
+
// Run the update
|
1701 |
+
$data['ID'] = $post_id;
|
1702 |
+
$result = wp_update_post( $data, true );
|
1703 |
+
if ( is_wp_error( $result ) ) {
|
1704 |
+
$this->logger->warning( sprintf(
|
1705 |
+
__( 'Could not update "%s" (post #%d) with mapped data', 'wordpress-importer' ),
|
1706 |
+
get_the_title( $post_id ),
|
1707 |
+
$post_id
|
1708 |
+
) );
|
1709 |
+
$this->logger->debug( $result->get_error_message() );
|
1710 |
+
continue;
|
1711 |
+
}
|
1712 |
+
|
1713 |
+
// Clear out our temporary meta keys
|
1714 |
+
delete_post_meta( $post_id, '_wxr_import_parent' );
|
1715 |
+
delete_post_meta( $post_id, '_wxr_import_user_slug' );
|
1716 |
+
delete_post_meta( $post_id, '_wxr_import_has_attachment_refs' );
|
1717 |
+
}
|
1718 |
+
}
|
1719 |
+
|
1720 |
+
protected function post_process_menu_item( $post_id ) {
|
1721 |
+
$menu_object_id = get_post_meta( $post_id, '_wxr_import_menu_item', true );
|
1722 |
+
if ( empty( $menu_object_id ) ) {
|
1723 |
+
// No processing needed!
|
1724 |
+
return;
|
1725 |
+
}
|
1726 |
+
|
1727 |
+
$menu_item_type = get_post_meta( $post_id, '_menu_item_type', true );
|
1728 |
+
switch ( $menu_item_type ) {
|
1729 |
+
case 'taxonomy':
|
1730 |
+
if ( isset( $this->mapping['term_id'][ $menu_object_id ] ) ) {
|
1731 |
+
$menu_object = $this->mapping['term_id'][ $menu_object_id ];
|
1732 |
+
}
|
1733 |
+
break;
|
1734 |
+
|
1735 |
+
case 'post_type':
|
1736 |
+
if ( isset( $this->mapping['post'][ $menu_object_id ] ) ) {
|
1737 |
+
$menu_object = $this->mapping['post'][ $menu_object_id ];
|
1738 |
+
}
|
1739 |
+
break;
|
1740 |
+
|
1741 |
+
default:
|
1742 |
+
// Cannot handle this.
|
1743 |
+
return;
|
1744 |
+
}
|
1745 |
+
|
1746 |
+
if ( ! empty( $menu_object ) ) {
|
1747 |
+
update_post_meta( $post_id, '_menu_item_object_id', wp_slash( $menu_object ) );
|
1748 |
+
} else {
|
1749 |
+
$this->logger->warning( sprintf(
|
1750 |
+
__( 'Could not find the menu object for "%s" (post #%d)', 'wordpress-importer' ),
|
1751 |
+
get_the_title( $post_id ),
|
1752 |
+
$post_id
|
1753 |
+
) );
|
1754 |
+
$this->logger->debug( sprintf(
|
1755 |
+
__( 'Post %d was imported with object "%d" of type "%s", but could not be found', 'wordpress-importer' ),
|
1756 |
+
$post_id,
|
1757 |
+
$menu_object_id,
|
1758 |
+
$menu_item_type
|
1759 |
+
) );
|
1760 |
+
}
|
1761 |
+
|
1762 |
+
delete_post_meta( $post_id, '_wxr_import_menu_item' );
|
1763 |
+
}
|
1764 |
+
|
1765 |
+
|
1766 |
+
protected function post_process_comments( $todo ) {
|
1767 |
+
foreach ( $todo as $comment_id => $_ ) {
|
1768 |
+
$data = array();
|
1769 |
+
|
1770 |
+
$parent_id = get_comment_meta( $comment_id, '_wxr_import_parent', true );
|
1771 |
+
if ( ! empty( $parent_id ) ) {
|
1772 |
+
// Have we imported the parent now?
|
1773 |
+
if ( isset( $this->mapping['comment'][ $parent_id ] ) ) {
|
1774 |
+
$data['comment_parent'] = $this->mapping['comment'][ $parent_id ];
|
1775 |
+
}
|
1776 |
+
else {
|
1777 |
+
$this->logger->warning( sprintf(
|
1778 |
+
__( 'Could not find the comment parent for comment #%d', 'wordpress-importer' ),
|
1779 |
+
$comment_id
|
1780 |
+
) );
|
1781 |
+
$this->logger->debug( sprintf(
|
1782 |
+
__( 'Comment %d was imported with parent %d, but could not be found', 'wordpress-importer' ),
|
1783 |
+
$comment_id,
|
1784 |
+
$parent_id
|
1785 |
+
) );
|
1786 |
+
}
|
1787 |
+
}
|
1788 |
+
|
1789 |
+
$author_id = get_comment_meta( $comment_id, '_wxr_import_user', true );
|
1790 |
+
if ( ! empty( $author_id ) ) {
|
1791 |
+
// Have we imported the user now?
|
1792 |
+
if ( isset( $this->mapping['user'][ $author_id ] ) ) {
|
1793 |
+
$data['user_id'] = $this->mapping['user'][ $author_id ];
|
1794 |
+
}
|
1795 |
+
else {
|
1796 |
+
$this->logger->warning( sprintf(
|
1797 |
+
__( 'Could not find the author for comment #%d', 'wordpress-importer' ),
|
1798 |
+
$comment_id
|
1799 |
+
) );
|
1800 |
+
$this->logger->debug( sprintf(
|
1801 |
+
__( 'Comment %d was imported with author %d, but could not be found', 'wordpress-importer' ),
|
1802 |
+
$comment_id,
|
1803 |
+
$author_id
|
1804 |
+
) );
|
1805 |
+
}
|
1806 |
+
}
|
1807 |
+
|
1808 |
+
// Do we have updates to make?
|
1809 |
+
if ( empty( $data ) ) {
|
1810 |
+
continue;
|
1811 |
+
}
|
1812 |
+
|
1813 |
+
// Run the update
|
1814 |
+
$data['comment_ID'] = $comment_ID;
|
1815 |
+
$result = wp_update_comment( wp_slash( $data ) );
|
1816 |
+
if ( empty( $result ) ) {
|
1817 |
+
$this->logger->warning( sprintf(
|
1818 |
+
__( 'Could not update comment #%d with mapped data', 'wordpress-importer' ),
|
1819 |
+
$comment_id
|
1820 |
+
) );
|
1821 |
+
continue;
|
1822 |
+
}
|
1823 |
+
|
1824 |
+
// Clear out our temporary meta keys
|
1825 |
+
delete_comment_meta( $comment_id, '_wxr_import_parent' );
|
1826 |
+
delete_comment_meta( $comment_id, '_wxr_import_user' );
|
1827 |
+
}
|
1828 |
+
}
|
1829 |
+
|
1830 |
+
/**
|
1831 |
+
* Use stored mapping information to update old attachment URLs
|
1832 |
+
*/
|
1833 |
+
protected function replace_attachment_urls_in_content() {
|
1834 |
+
global $wpdb;
|
1835 |
+
// make sure we do the longest urls first, in case one is a substring of another
|
1836 |
+
uksort( $this->url_remap, array(&$this, 'cmpr_strlen') );
|
1837 |
+
|
1838 |
+
foreach ( $this->url_remap as $from_url => $to_url ) {
|
1839 |
+
// remap urls in post_content
|
1840 |
+
$wpdb->query( $wpdb->prepare("UPDATE {$wpdb->posts} SET post_content = REPLACE(post_content, %s, %s)", $from_url, $to_url) );
|
1841 |
+
// remap enclosure urls
|
1842 |
+
$result = $wpdb->query( $wpdb->prepare("UPDATE {$wpdb->postmeta} SET meta_value = REPLACE(meta_value, %s, %s) WHERE meta_key='enclosure'", $from_url, $to_url) );
|
1843 |
+
}
|
1844 |
+
}
|
1845 |
+
|
1846 |
+
/**
|
1847 |
+
* Update _thumbnail_id meta to new, imported attachment IDs
|
1848 |
+
*/
|
1849 |
+
function remap_featured_images() {
|
1850 |
+
// cycle through posts that have a featured image
|
1851 |
+
foreach ( $this->featured_images as $post_id => $value ) {
|
1852 |
+
if ( isset( $this->processed_posts[$value] ) ) {
|
1853 |
+
$new_id = $this->processed_posts[$value];
|
1854 |
+
// only update if there's a difference
|
1855 |
+
if ( $new_id != $value )
|
1856 |
+
update_post_meta( $post_id, '_thumbnail_id', $new_id );
|
1857 |
+
}
|
1858 |
+
}
|
1859 |
+
}
|
1860 |
+
|
1861 |
+
/**
|
1862 |
+
* Decide if the given meta key maps to information we will want to import
|
1863 |
+
*
|
1864 |
+
* @param string $key The meta key to check
|
1865 |
+
* @return string|bool The key if we do want to import, false if not
|
1866 |
+
*/
|
1867 |
+
public function is_valid_meta_key( $key ) {
|
1868 |
+
// skip attachment metadata since we'll regenerate it from scratch
|
1869 |
+
// skip _edit_lock as not relevant for import
|
1870 |
+
if ( in_array( $key, array( '_wp_attached_file', '_wp_attachment_metadata', '_edit_lock' ) ) )
|
1871 |
+
return false;
|
1872 |
+
return $key;
|
1873 |
+
}
|
1874 |
+
|
1875 |
+
/**
|
1876 |
+
* Decide what the maximum file size for downloaded attachments is.
|
1877 |
+
* Default is 0 (unlimited), can be filtered via import_attachment_size_limit
|
1878 |
+
*
|
1879 |
+
* @return int Maximum attachment file size to import
|
1880 |
+
*/
|
1881 |
+
protected function max_attachment_size() {
|
1882 |
+
return apply_filters( 'import_attachment_size_limit', 0 );
|
1883 |
+
}
|
1884 |
+
|
1885 |
+
/**
|
1886 |
+
* Added to http_request_timeout filter to force timeout at 60 seconds during import
|
1887 |
+
*
|
1888 |
+
* @access protected
|
1889 |
+
* @return int 60
|
1890 |
+
*/
|
1891 |
+
function bump_request_timeout($val) {
|
1892 |
+
return 60;
|
1893 |
+
}
|
1894 |
+
|
1895 |
+
// return the difference in length between two strings
|
1896 |
+
function cmpr_strlen( $a, $b ) {
|
1897 |
+
return strlen($b) - strlen($a);
|
1898 |
+
}
|
1899 |
+
|
1900 |
+
/**
|
1901 |
+
* Prefill existing post data.
|
1902 |
+
*
|
1903 |
+
* This preloads all GUIDs into memory, allowing us to avoid hitting the
|
1904 |
+
* database when we need to check for existence. With larger imports, this
|
1905 |
+
* becomes prohibitively slow to perform SELECT queries on each.
|
1906 |
+
*
|
1907 |
+
* By preloading all this data into memory, it's a constant-time lookup in
|
1908 |
+
* PHP instead. However, this does use a lot more memory, so for sites doing
|
1909 |
+
* small imports onto a large site, it may be a better tradeoff to use
|
1910 |
+
* on-the-fly checking instead.
|
1911 |
+
*/
|
1912 |
+
protected function prefill_existing_posts() {
|
1913 |
+
global $wpdb;
|
1914 |
+
$posts = $wpdb->get_results( "SELECT ID, guid FROM {$wpdb->posts}" );
|
1915 |
+
|
1916 |
+
foreach ( $posts as $item ) {
|
1917 |
+
$this->exists['post'][ $item->guid ] = $item->ID;
|
1918 |
+
}
|
1919 |
+
}
|
1920 |
+
|
1921 |
+
/**
|
1922 |
+
* Does the post exist?
|
1923 |
+
*
|
1924 |
+
* @param array $data Post data to check against.
|
1925 |
+
* @return int|bool Existing post ID if it exists, false otherwise.
|
1926 |
+
*/
|
1927 |
+
protected function post_exists( $data ) {
|
1928 |
+
// Constant-time lookup if we prefilled
|
1929 |
+
$exists_key = $data['guid'];
|
1930 |
+
|
1931 |
+
if ( $this->options['prefill_existing_posts'] ) {
|
1932 |
+
return isset( $this->exists['post'][ $exists_key ] ) ? $this->exists['post'][ $exists_key ] : false;
|
1933 |
+
}
|
1934 |
+
|
1935 |
+
// No prefilling, but might have already handled it
|
1936 |
+
if ( isset( $this->exists['post'][ $exists_key ] ) ) {
|
1937 |
+
return $this->exists['post'][ $exists_key ];
|
1938 |
+
}
|
1939 |
+
|
1940 |
+
// Still nothing, try post_exists, and cache it
|
1941 |
+
$exists = post_exists( $data['post_title'], $data['post_content'], $data['post_date'] );
|
1942 |
+
$this->exists['post'][ $exists_key ] = $exists;
|
1943 |
+
|
1944 |
+
return $exists;
|
1945 |
+
}
|
1946 |
+
|
1947 |
+
/**
|
1948 |
+
* Mark the post as existing.
|
1949 |
+
*
|
1950 |
+
* @param array $data Post data to mark as existing.
|
1951 |
+
* @param int $post_id Post ID.
|
1952 |
+
*/
|
1953 |
+
protected function mark_post_exists( $data, $post_id ) {
|
1954 |
+
$exists_key = $data['guid'];
|
1955 |
+
$this->exists['post'][ $exists_key ] = $post_id;
|
1956 |
+
}
|
1957 |
+
|
1958 |
+
/**
|
1959 |
+
* Prefill existing comment data.
|
1960 |
+
*
|
1961 |
+
* @see self::prefill_existing_posts() for justification of why this exists.
|
1962 |
+
*/
|
1963 |
+
protected function prefill_existing_comments() {
|
1964 |
+
global $wpdb;
|
1965 |
+
$posts = $wpdb->get_results( "SELECT comment_ID, comment_author, comment_date FROM {$wpdb->comments}" );
|
1966 |
+
|
1967 |
+
foreach ( $posts as $item ) {
|
1968 |
+
$exists_key = sha1( $item->comment_author . ':' . $item->comment_date );
|
1969 |
+
$this->exists['post'][ $exists_key ] = $item->comment_ID;
|
1970 |
+
}
|
1971 |
+
}
|
1972 |
+
|
1973 |
+
/**
|
1974 |
+
* Does the comment exist?
|
1975 |
+
*
|
1976 |
+
* @param array $data Comment data to check against.
|
1977 |
+
* @return int|bool Existing comment ID if it exists, false otherwise.
|
1978 |
+
*/
|
1979 |
+
protected function comment_exists( $data ) {
|
1980 |
+
$exists_key = sha1( $data['comment_author'] . ':' . $data['comment_date'] );
|
1981 |
+
|
1982 |
+
// Constant-time lookup if we prefilled
|
1983 |
+
if ( $this->options['prefill_existing_comments'] ) {
|
1984 |
+
return isset( $this->exists['comment'][ $exists_key ] ) ? $this->exists['comment'][ $exists_key ] : false;
|
1985 |
+
}
|
1986 |
+
|
1987 |
+
// No prefilling, but might have already handled it
|
1988 |
+
if ( isset( $this->exists['comment'][ $exists_key ] ) ) {
|
1989 |
+
return $this->exists['comment'][ $exists_key ];
|
1990 |
+
}
|
1991 |
+
|
1992 |
+
// Still nothing, try comment_exists, and cache it
|
1993 |
+
$exists = comment_exists( $data['comment_author'], $data['comment_date'] );
|
1994 |
+
$this->exists['comment'][ $exists_key ] = $exists;
|
1995 |
+
|
1996 |
+
return $exists;
|
1997 |
+
}
|
1998 |
+
|
1999 |
+
/**
|
2000 |
+
* Mark the comment as existing.
|
2001 |
+
*
|
2002 |
+
* @param array $data Comment data to mark as existing.
|
2003 |
+
* @param int $comment_id Comment ID.
|
2004 |
+
*/
|
2005 |
+
protected function mark_comment_exists( $data, $comment_id ) {
|
2006 |
+
$exists_key = sha1( $data['comment_author'] . ':' . $data['comment_date'] );
|
2007 |
+
$this->exists['comment'][ $exists_key ] = $comment_id;
|
2008 |
+
}
|
2009 |
+
|
2010 |
+
/**
|
2011 |
+
* Prefill existing term data.
|
2012 |
+
*
|
2013 |
+
* @see self::prefill_existing_posts() for justification of why this exists.
|
2014 |
+
*/
|
2015 |
+
protected function prefill_existing_terms() {
|
2016 |
+
global $wpdb;
|
2017 |
+
$query = "SELECT t.term_id, tt.taxonomy, t.slug FROM {$wpdb->terms} AS t";
|
2018 |
+
$query .= " JOIN {$wpdb->term_taxonomy} AS tt ON t.term_id = tt.term_id";
|
2019 |
+
$terms = $wpdb->get_results( $query );
|
2020 |
+
|
2021 |
+
foreach ( $terms as $item ) {
|
2022 |
+
$exists_key = sha1( $item->taxonomy . ':' . $item->slug );
|
2023 |
+
$this->exists['term'][ $exists_key ] = $item->term_id;
|
2024 |
+
}
|
2025 |
+
}
|
2026 |
+
|
2027 |
+
/**
|
2028 |
+
* Does the term exist?
|
2029 |
+
*
|
2030 |
+
* @param array $data Term data to check against.
|
2031 |
+
* @return int|bool Existing term ID if it exists, false otherwise.
|
2032 |
+
*/
|
2033 |
+
protected function term_exists( $data ) {
|
2034 |
+
$exists_key = sha1( $data['taxonomy'] . ':' . $data['slug'] );
|
2035 |
+
|
2036 |
+
// Constant-time lookup if we prefilled
|
2037 |
+
if ( $this->options['prefill_existing_terms'] ) {
|
2038 |
+
return isset( $this->exists['term'][ $exists_key ] ) ? $this->exists['term'][ $exists_key ] : false;
|
2039 |
+
}
|
2040 |
+
|
2041 |
+
// No prefilling, but might have already handled it
|
2042 |
+
if ( isset( $this->exists['term'][ $exists_key ] ) ) {
|
2043 |
+
return $this->exists['term'][ $exists_key ];
|
2044 |
+
}
|
2045 |
+
|
2046 |
+
// Still nothing, try comment_exists, and cache it
|
2047 |
+
$exists = term_exists( $data['slug'], $data['taxonomy'] );
|
2048 |
+
if ( is_array( $exists ) ) {
|
2049 |
+
$exists = $exists['term_id'];
|
2050 |
+
}
|
2051 |
+
|
2052 |
+
$this->exists['term'][ $exists_key ] = $exists;
|
2053 |
+
|
2054 |
+
return $exists;
|
2055 |
+
}
|
2056 |
+
|
2057 |
+
/**
|
2058 |
+
* Mark the term as existing.
|
2059 |
+
*
|
2060 |
+
* @param array $data Term data to mark as existing.
|
2061 |
+
* @param int $term_id Term ID.
|
2062 |
+
*/
|
2063 |
+
protected function mark_term_exists( $data, $term_id ) {
|
2064 |
+
$exists_key = sha1( $data['taxonomy'] . ':' . $data['slug'] );
|
2065 |
+
$this->exists['term'][ $exists_key ] = $term_id;
|
2066 |
+
}
|
2067 |
+
}
|