PluginProbe ʕ •ᴥ•ʔ
Kubio AI Page Builder / trunk
Kubio AI Page Builder vtrunk
2.9.0 2.8.6 2.8.5 2.8.4 2.8.3 2.8.2 2.8.1 trunk 1.0.0 1.0.1 1.1.0 1.2.0 1.2.1 1.2.2 1.2.3 1.3.0 1.3.1 1.3.2 1.4.0 1.4.1 1.4.2 1.4.3 1.5.0 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.7.0 1.7.1 1.7.2 1.7.3 1.8.0 1.8.1 1.8.2 1.9.0 2.0.0 2.1.1 2.1.2 2.1.3 2.2.0 2.2.3 2.2.4 2.2.5 2.3.0 2.3.1 2.3.3 2.3.4 2.4.0 2.4.1 2.4.2 2.4.3 2.4.5 2.5.0 2.5.1 2.5.2 2.5.3 2.6.0 2.6.1 2.6.2 2.6.3 2.6.5 2.6.6 2.6.7 2.7.0 2.7.1 2.7.2 2.7.3 2.8.0
kubio / vendor / proteusthemes / wp-content-importer-v2 / src / Importer.php
kubio / vendor / proteusthemes / wp-content-importer-v2 / src Last commit date
Importer.php 4 years ago WPImporterLogger.php 4 years ago WPImporterLoggerCLI.php 4 years ago WXRImportInfo.php 4 years ago WXRImporter.php 4 years ago
Importer.php
630 lines
1 <?php
2 /**
3 * The main importer class, extending the slightly modified WP importer 2.0 class WXRImporter
4 */
5
6 namespace ProteusThemes\WPContentImporter2;
7
8 use XMLReader;
9
10 class Importer extends WXRImporter {
11
12 /**
13 * Time in milliseconds, marking the beginning of the import.
14 *
15 * @var float
16 */
17 private $start_time;
18
19 /**
20 * Importer constructor.
21 * Look at the parent constructor for the options parameters.
22 *
23 * @param array $options The importer options.
24 * @param object $logger The logger object.
25 */
26 public function __construct( $options = array(), $logger = null ) {
27 parent::__construct( $options );
28
29 $this->set_logger( $logger );
30
31 // Check, if a new AJAX request is required.
32 add_filter( 'wxr_importer.pre_process.post', array( $this, 'new_ajax_request_maybe' ) );
33
34 // WooCommerce product attributes registration.
35 if ( class_exists( 'WooCommerce' ) ) {
36 add_filter( 'wxr_importer.pre_process.term', array( $this, 'woocommerce_product_attributes_registration' ), 10, 1 );
37 }
38 }
39
40 /**
41 * Get the XML reader for the file.
42 *
43 * @param string $file Path to the XML file.
44 *
45 * @return XMLReader|boolean Reader instance on success, false otherwise.
46 */
47 protected function get_reader( $file ) {
48 // Avoid loading external entities for security
49 $old_value = null;
50 if ( function_exists( 'libxml_disable_entity_loader' ) ) {
51 // $old_value = libxml_disable_entity_loader( true );
52 }
53
54 if ( ! class_exists( 'XMLReader' ) ) {
55 $this->logger->critical( __( 'The XMLReader class is missing! Please install the XMLReader PHP extension on your server', 'wordpress-importer' ) );
56
57 return false;
58 }
59
60 $reader = new XMLReader();
61 $status = $reader->open( $file );
62
63 if ( ! is_null( $old_value ) ) {
64 // libxml_disable_entity_loader( $old_value );
65 }
66
67 if ( ! $status ) {
68 $this->logger->error( __( 'Could not open the XML file for parsing!', 'wordpress-importer' ) );
69
70 return false;
71 }
72
73 return $reader;
74 }
75
76 /**
77 * Get the basic import content data.
78 * Which elements are present in this import file (check possible elements in the $data variable)?
79 *
80 * @param $file
81 *
82 * @return array|bool
83 */
84 public function get_basic_import_content_data( $file ) {
85 $data = array(
86 'users' => false,
87 'categories' => false,
88 'tags' => false,
89 'terms' => false,
90 'posts' => false,
91 );
92
93 // Get the XML reader and open the file.
94 $reader = $this->get_reader( $file );
95
96 if ( empty( $reader ) ) {
97 return false;
98 }
99
100 // Start parsing!
101 while ( $reader->read() ) {
102 // Only deal with element opens.
103 if ( $reader->nodeType !== XMLReader::ELEMENT ) {
104 continue;
105 }
106
107 switch ( $reader->name ) {
108 case 'wp:author':
109 // Skip, if the users were already detected.
110 if ( $data['users'] ) {
111 $reader->next();
112 break;
113 }
114
115 $node = $reader->expand();
116 $parsed = $this->parse_author_node( $node );
117
118 // Skip, if there was an error in parsing the author node.
119 if ( is_wp_error( $parsed ) ) {
120 $reader->next();
121 break;
122 }
123
124 $data['users'] = true;
125
126 // Handled everything in this node, move on to the next.
127 $reader->next();
128 break;
129
130 case 'item':
131 // Skip, if the posts were already detected.
132 if ( $data['posts'] ) {
133 $reader->next();
134 break;
135 }
136
137 $node = $reader->expand();
138 $parsed = $this->parse_post_node( $node );
139
140 // Skip, if there was an error in parsing the item node.
141 if ( is_wp_error( $parsed ) ) {
142 $reader->next();
143 break;
144 }
145
146 $data['posts'] = true;
147
148 // Handled everything in this node, move on to the next
149 $reader->next();
150 break;
151
152 case 'wp:category':
153 $data['categories'] = true;
154
155 // Handled everything in this node, move on to the next
156 $reader->next();
157 break;
158 case 'wp:tag':
159 $data['tags'] = true;
160
161 // Handled everything in this node, move on to the next
162 $reader->next();
163 break;
164 case 'wp:term':
165 $data['terms'] = true;
166
167 // Handled everything in this node, move on to the next
168 $reader->next();
169 break;
170 }
171 }
172
173 return $data;
174 }
175
176
177 /**
178 * Get the number of posts (posts, pages, CPT, attachments), that the import file has.
179 *
180 * @param $file
181 *
182 * @return int
183 */
184 public function get_number_of_posts_to_import( $file ) {
185 $reader = $this->get_reader( $file );
186 $counter = 0;
187
188 if ( empty( $reader ) ) {
189 return $counter;
190 }
191
192 // Start parsing!
193 while ( $reader->read() ) {
194 // Only deal with element opens.
195 if ( $reader->nodeType !== XMLReader::ELEMENT ) {
196 continue;
197 }
198
199 if ( 'item' == $reader->name ) {
200 $node = $reader->expand();
201 $parsed = $this->parse_post_node( $node );
202
203 // Skip, if there was an error in parsing the item node.
204 if ( is_wp_error( $parsed ) ) {
205 $reader->next();
206 continue;
207 }
208
209 $counter++;
210 }
211 }
212
213 return $counter;
214 }
215
216 /**
217 * The main controller for the actual import stage.
218 *
219 * @param string $file Path to the WXR file for importing.
220 * @param array $options Import options (which parts to import).
221 *
222 * @return boolean
223 */
224 public function import( $file, $options = array() ) {
225 add_filter( 'import_post_meta_key', array( $this, 'is_valid_meta_key' ) );
226 add_filter( 'http_request_timeout', array( &$this, 'bump_request_timeout' ) );
227
228 // Start the import timer.
229 $this->start_time = microtime( true );
230
231 // Set the existing import data, from previous AJAX call, if any.
232 $this->restore_import_data_transient();
233
234 // Set the import options defaults.
235 if ( empty( $options ) ) {
236 $options = array(
237 'users' => false,
238 'categories' => true,
239 'tags' => true,
240 'terms' => true,
241 'posts' => true,
242 );
243 }
244
245 $result = $this->import_start( $file );
246
247 if ( is_wp_error( $result ) ) {
248 $this->logger->error( __( 'Content import start error: ', 'wordpress-importer' ) . $result->get_error_message() );
249
250 return false;
251 }
252
253 // Get the actual XML reader.
254 $reader = $this->get_reader( $file );
255
256 if ( empty( $reader ) ) {
257 return false;
258 }
259
260 // Set the version to compatibility mode first
261 $this->version = '1.0';
262
263 // Reset other variables
264 $this->base_url = '';
265
266 // Start parsing!
267 while ( $reader->read() ) {
268 // Only deal with element opens.
269 if ( $reader->nodeType !== XMLReader::ELEMENT ) {
270 continue;
271 }
272
273 switch ( $reader->name ) {
274 case 'wp:wxr_version':
275 // Upgrade to the correct version
276 $this->version = $reader->readString();
277
278 if ( version_compare( $this->version, self::MAX_WXR_VERSION, '>' ) ) {
279 $this->logger->warning( sprintf(
280 __( 'This WXR file (version %s) is newer than the importer (version %s) and may not be supported. Please consider updating.', 'wordpress-importer' ),
281 $this->version,
282 self::MAX_WXR_VERSION
283 ) );
284 }
285
286 // Handled everything in this node, move on to the next
287 $reader->next();
288 break;
289
290 case 'wp:base_site_url':
291 $this->base_url = $reader->readString();
292
293 // Handled everything in this node, move on to the next
294 $reader->next();
295 break;
296
297 case 'item':
298 if ( empty( $options['posts'] ) ) {
299 $reader->next();
300 break;
301 }
302
303 $node = $reader->expand();
304 $parsed = $this->parse_post_node( $node );
305
306 if ( is_wp_error( $parsed ) ) {
307 $this->log_error( $parsed );
308
309 // Skip the rest of this post
310 $reader->next();
311 break;
312 }
313
314 $this->process_post( $parsed['data'], $parsed['meta'], $parsed['comments'], $parsed['terms'] );
315
316 // Handled everything in this node, move on to the next
317 $reader->next();
318 break;
319
320 case 'wp:author':
321 if ( empty( $options['users'] ) ) {
322 $reader->next();
323 break;
324 }
325
326 $node = $reader->expand();
327 $parsed = $this->parse_author_node( $node );
328
329 if ( is_wp_error( $parsed ) ) {
330 $this->log_error( $parsed );
331
332 // Skip the rest of this post
333 $reader->next();
334 break;
335 }
336
337 $status = $this->process_author( $parsed['data'], $parsed['meta'] );
338
339 if ( is_wp_error( $status ) ) {
340 $this->log_error( $status );
341 }
342
343 // Handled everything in this node, move on to the next
344 $reader->next();
345 break;
346
347 case 'wp:category':
348 if ( empty( $options['categories'] ) ) {
349 $reader->next();
350 break;
351 }
352
353 $node = $reader->expand();
354 $parsed = $this->parse_term_node( $node, 'category' );
355
356 if ( is_wp_error( $parsed ) ) {
357 $this->log_error( $parsed );
358
359 // Skip the rest of this post
360 $reader->next();
361 break;
362 }
363
364 $status = $this->process_term( $parsed['data'], $parsed['meta'] );
365
366 // Handled everything in this node, move on to the next
367 $reader->next();
368 break;
369
370 case 'wp:tag':
371 if ( empty( $options['tags'] ) ) {
372 $reader->next();
373 break;
374 }
375
376 $node = $reader->expand();
377 $parsed = $this->parse_term_node( $node, 'tag' );
378
379 if ( is_wp_error( $parsed ) ) {
380 $this->log_error( $parsed );
381
382 // Skip the rest of this post
383 $reader->next();
384 break;
385 }
386
387 $status = $this->process_term( $parsed['data'], $parsed['meta'] );
388
389 // Handled everything in this node, move on to the next
390 $reader->next();
391 break;
392
393 case 'wp:term':
394 if ( empty( $options['terms'] ) ) {
395 $reader->next();
396 break;
397 }
398
399 $node = $reader->expand();
400 $parsed = $this->parse_term_node( $node );
401
402 if ( is_wp_error( $parsed ) ) {
403 $this->log_error( $parsed );
404
405 // Skip the rest of this post
406 $reader->next();
407 break;
408 }
409
410 $status = $this->process_term( $parsed['data'], $parsed['meta'] );
411
412 // Handled everything in this node, move on to the next
413 $reader->next();
414 break;
415
416 default:
417 // Skip this node, probably handled by something already
418 break;
419 }
420 }
421
422 // Now that we've done the main processing, do any required
423 // post-processing and remapping.
424 $this->post_process();
425
426 if ( $this->options['aggressive_url_search'] ) {
427 $this->replace_attachment_urls_in_content();
428 }
429
430 $this->remap_featured_images();
431
432 $this->import_end();
433
434 // Set the current importer state, so the data can be used on the next AJAX call.
435 $this->set_current_importer_data();
436
437 return true;
438 }
439
440 /**
441 * Import users only.
442 *
443 * @param string $file Path to the import file.
444 */
445 public function import_users( $file ) {
446 return $this->import( $file, array( 'users' => true ) );
447 }
448
449 /**
450 * Import categories only.
451 *
452 * @param string $file Path to the import file.
453 */
454 public function import_categories( $file ) {
455 return $this->import( $file, array( 'categories' => true ) );
456 }
457
458 /**
459 * Import tags only.
460 *
461 * @param string $file Path to the import file.
462 */
463 public function import_tags( $file ) {
464 return $this->import( $file, array( 'tags' => true ) );
465 }
466
467 /**
468 * Import terms only.
469 *
470 * @param string $file Path to the import file.
471 */
472 public function import_terms( $file ) {
473 return $this->import( $file, array( 'terms' => true ) );
474 }
475
476 /**
477 * Import posts only.
478 *
479 * @param string $file Path to the import file.
480 */
481 public function import_posts( $file ) {
482 return $this->import( $file, array( 'posts' => true ) );
483 }
484
485 /**
486 * Check if we need to create a new AJAX request, so that server does not timeout.
487 * And fix the import warning for missing post author.
488 *
489 * @param array $data current post data.
490 * @return array
491 */
492 public function new_ajax_request_maybe( $data ) {
493 $time = microtime( true ) - $this->start_time;
494
495 // We should make a new ajax call, if the time is right.
496 if ( $time > apply_filters( 'pt-importer/time_for_one_ajax_call', 20 ) ) {
497 $response = apply_filters( 'pt-importer/new_ajax_request_response_data', array(
498 'status' => 'newAJAX',
499 'log' => 'Time for new AJAX request!: ' . $time,
500 'num_of_imported_posts' => count( $this->mapping['post'] ),
501 ) );
502
503 // Add message to log file.
504 $this->logger->info( __( 'New AJAX call!', 'wordpress-importer' ) );
505
506 // Set the current importer state, so it can be continued on the next AJAX call.
507 $this->set_current_importer_data();
508
509 // Send the request for a new AJAX call.
510 wp_send_json( $response );
511 }
512
513 // Set importing author to the current user.
514 // Fixes the [WARNING] Could not find the author for ... log warning messages.
515 $current_user_obj = wp_get_current_user();
516 $data['post_author'] = $current_user_obj->user_login;
517
518 return $data;
519 }
520
521 /**
522 * Save current importer data to the DB, for later use.
523 */
524 public function set_current_importer_data() {
525 $data = apply_filters( 'pt-importer/set_current_importer_data', array(
526 'options' => $this->options,
527 'mapping' => $this->mapping,
528 'requires_remapping' => $this->requires_remapping,
529 'exists' => $this->exists,
530 'user_slug_override' => $this->user_slug_override,
531 'url_remap' => $this->url_remap,
532 'featured_images' => $this->featured_images,
533 ) );
534
535 $this->save_current_import_data_transient( $data );
536 }
537
538 /**
539 * Set the importer data to the transient.
540 *
541 * @param array $data Data to be saved to the transient.
542 */
543 public function save_current_import_data_transient( $data ) {
544 set_transient( 'pt_importer_data', $data, MINUTE_IN_SECONDS );
545 }
546
547 /**
548 * Restore the importer data from the transient.
549 *
550 * @return boolean
551 */
552 public function restore_import_data_transient() {
553 if ( $data = get_transient( 'pt_importer_data' ) ) {
554 $this->options = empty( $data['options'] ) ? array() : $data['options'];
555 $this->mapping = empty( $data['mapping'] ) ? array() : $data['mapping'];
556 $this->requires_remapping = empty( $data['requires_remapping'] ) ? array() : $data['requires_remapping'];
557 $this->exists = empty( $data['exists'] ) ? array() : $data['exists'];
558 $this->user_slug_override = empty( $data['user_slug_override'] ) ? array() : $data['user_slug_override'];
559 $this->url_remap = empty( $data['url_remap'] ) ? array() : $data['url_remap'];
560 $this->featured_images = empty( $data['featured_images'] ) ? array() : $data['featured_images'];
561
562 do_action( 'pt-importer/restore_import_data_transient' );
563
564 return true;
565 }
566
567 return false;
568 }
569
570 /**
571 * Get the importer mapping data.
572 *
573 * @return array An empty array or an array of mapping data.
574 */
575 public function get_mapping() {
576 return $this->mapping;
577 }
578
579 /**
580 * Hook into the pre-process term filter of the content import and register the
581 * custom WooCommerce product attributes, so that the terms can then be imported normally.
582 *
583 * This should probably be removed once the WP importer 2.0 support is added in WooCommerce.
584 *
585 * Fixes: [WARNING] Failed to import pa_size L warnings in content import.
586 * Code from: woocommerce/includes/admin/class-wc-admin-importers.php (ver 2.6.9).
587 *
588 * Github issue: https://github.com/proteusthemes/one-click-demo-import/issues/71
589 *
590 * @param array $date The term data to import.
591 * @return array The unchanged term data.
592 */
593 public function woocommerce_product_attributes_registration( $data ) {
594 global $wpdb;
595
596 if ( strstr( $data['taxonomy'], 'pa_' ) ) {
597 if ( ! taxonomy_exists( $data['taxonomy'] ) ) {
598 $attribute_name = wc_sanitize_taxonomy_name( str_replace( 'pa_', '', $data['taxonomy'] ) );
599
600 // Create the taxonomy
601 if ( ! in_array( $attribute_name, wc_get_attribute_taxonomies() ) ) {
602 $attribute = array(
603 'attribute_label' => $attribute_name,
604 'attribute_name' => $attribute_name,
605 'attribute_type' => 'select',
606 'attribute_orderby' => 'menu_order',
607 'attribute_public' => 0
608 );
609 $wpdb->insert( $wpdb->prefix . 'woocommerce_attribute_taxonomies', $attribute );
610 delete_transient( 'wc_attribute_taxonomies' );
611 }
612
613 // Register the taxonomy now so that the import works!
614 register_taxonomy(
615 $data['taxonomy'],
616 apply_filters( 'woocommerce_taxonomy_objects_' . $data['taxonomy'], array( 'product' ) ),
617 apply_filters( 'woocommerce_taxonomy_args_' . $data['taxonomy'], array(
618 'hierarchical' => true,
619 'show_ui' => false,
620 'query_var' => true,
621 'rewrite' => false,
622 ) )
623 );
624 }
625 }
626
627 return $data;
628 }
629 }
630