PluginProbe ʕ •ᴥ•ʔ
Kubio AI Page Builder / 1.6.4
Kubio AI Page Builder v1.6.4
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 / lib / src / DemoSites / WXRImporter.php
kubio / lib / src / DemoSites Last commit date
DemoPartsRepository.php 3 years ago DemoSites.php 3 years ago DemoSitesHelpers.php 4 years ago DemoSitesImportBlockMap.php 4 years ago DemoSitesImporter.php 3 years ago DemoSitesLogger.php 4 years ago DemoSitesRepository.php 3 years ago WXRExporter.php 4 years ago WXRImporter.php 3 years ago WpCliCommand.php 3 years ago
WXRImporter.php
621 lines
1 <?php
2
3 namespace Kubio\DemoSites;
4
5
6 use IlluminateAgnostic\Arr\Support\Arr;
7 use Kubio\Core\Importer;
8 use ProteusThemes\WPContentImporter2\Importer as ProteusImporter;
9
10 class WXRImporter extends ProteusImporter {
11
12 const WXR_IMPORTER_TRANSIENT = 'kubio-wxr-importer-transient';
13 const EMAIL_REGEXP = "/(?:[a-z0-9!#$%&'*+=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+=?^_`{|}~-]+)*|\"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*\")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/";
14 /**
15 * Time in milliseconds, marking the beginning of the import.
16 *
17 * @var float
18 */
19 private $start_time;
20
21 public function __construct( $options = array( 'is_stepped' => true ), $logger = null ) {
22
23 if ( ! defined( 'KUBIO_IS_STARTER_SITES_IMPORT' ) ) {
24 define( 'KUBIO_IS_STARTER_SITES_IMPORT', true );
25 }
26
27 add_filter( 'wxr_importer.pre_process.post', array( $this, 'preProcessPost' ), 10, 4 );
28 add_filter( 'wp_import_post_data_processed', array( $this, 'beforePostImport' ), 10, 1 );
29 add_filter( 'wp_import_post_terms', array( $this, 'importPostTerms' ), 10, 3 );
30 add_action( 'wp_import_insert_post', array( $this, 'afterPostImport' ), 10, 4 );
31
32 add_action( 'wxr_importer.pre_process.term', array( $this, 'beforeTermImport' ), 10, 2 );
33 add_action( 'wxr_importer.processed.term', array( $this, 'afterTermImport' ), 10, 2 );
34
35 DemoSitesImportBlockMap::init();
36
37 parent::__construct( $options, $logger );
38 }
39
40
41 public function updatePermalink() {
42 global $wp_rewrite;
43
44 $new_permalink_structure = '/%postname%/';
45 $current_permalink_structure = $wp_rewrite->permalink_structure;
46 if ( $new_permalink_structure === $current_permalink_structure ) {
47 return;
48 }
49 //Write the rule
50 $wp_rewrite->set_permalink_structure( $new_permalink_structure );
51
52 //Set the option
53 update_option( 'rewrite_rules', false );
54
55 //Flush the rules and tell it to write htaccess
56 $wp_rewrite->flush_rules( true );
57 }
58
59 public function preProcessPost( $data, $meta, $comments, $terms ) {
60 if ( $data['post_type'] === kubio_global_data_post_type() ) {
61
62 $current_global_data = kubio_global_data_post_id( true, true );
63
64 $ids = get_posts(
65 array(
66 'post_type' => kubio_global_data_post_type(),
67 'post_status' => array( 'publish' ),
68 'posts_per_page' => - 1,
69 'fields' => 'ids',
70 'exclude' => kubio_global_data_post_id(),
71 )
72 );
73
74 foreach ( $ids as $id ) {
75 if ( intval( $id ) !== intval( $current_global_data ) ) {
76 wp_delete_post( $id, true );
77 }
78 }
79
80 kubio_replace_global_data_content( $data['post_content'] );
81 return false;
82 }
83
84 if ( $data['post_type'] === 'wp_template' || $data['post_type'] === 'wp_template_part' ) {
85 // Skip imported template from twenty* themes
86 foreach ( $terms as $term ) {
87 $taxonomy = Arr::get( $term, 'taxonomy' );
88 $slug = Arr::get( $term, 'slug' );
89 if ( $taxonomy === 'wp_theme' && is_string( $slug ) && strpos( $slug, 'twenty' ) === 0 ) {
90 return false;
91 }
92 }
93 }
94
95 return $data;
96 }
97
98 public function beforePostImport( $postdata ) {
99
100 remove_filter( 'wp_insert_post_data', 'kubio_on_post_update', 10, 3 );
101 remove_action( 'wp_insert_post', 'kubio_update_meta', 10, 3 );
102 remove_filter( 'theme_mod_nav_menu_locations', 'kubio_nav_menu_locations_from_global_data' );
103
104 $content = $postdata['post_content'];
105 $content = str_replace( 'var(\\u002d\\u002d', 'var(--', $content );
106
107 $blocks = parse_blocks( $content );
108
109 if ( empty( $blocks ) ) {
110 return $postdata;
111 }
112
113 $blocks = kubio_blocks_update_template_parts_theme( $blocks, get_stylesheet() );
114 $blocks = kubio_blocks_update_block_links( $blocks, $this->base_url );
115
116 $has_blocks = false;
117
118 foreach ( $blocks as $block ) {
119 $has_blocks = $has_blocks || Arr::get( $block, 'blockName', null );
120 if ( $has_blocks ) {
121 break;
122 }
123 }
124
125 if ( ! $has_blocks ) {
126 return $postdata;
127 }
128
129 $this->blocksMapping( $postdata['import_id'], $blocks );
130
131 $blocks = Importer::maybeImportBlockAssets( $blocks, array( $this, 'requestNewAjaxCall' ) );
132
133 // WP_REST_Posts_Controller is adding slashes when saving content
134 $postdata['post_content'] = wp_slash( kubio_serialize_blocks( $blocks ) );
135
136 return $postdata;
137 }
138
139 private function blocksMapping( $import_id, $blocks ) {
140 foreach ( $blocks as $block ) {
141
142 if ( ! Arr::get( $block, 'blockName' ) ) {
143 continue;
144 }
145
146 $should_map = apply_filters( 'kubio/demo-import/should-map', false, $block['blockName'], $block );
147
148 if ( $should_map ) {
149 $this->setHasBlockMapping( $import_id, $block['blockName'] );
150 }
151
152 $this->blocksMapping( $import_id, $block['innerBlocks'] );
153 }
154
155 }
156
157 private function setHasBlockMapping( $import_id, $block_name ) {
158 Arr::set( $this->mapping, "blocks.{$import_id}.{$block_name}", true );
159 }
160
161 public function afterPostImport( $post_id, $original_id, $postdata, $data ) {
162
163 if ( Arr::get( $postdata, 'post_type', null ) === kubio_global_data_post_type() ) {
164 $global_data = json_decode( $postdata['post_content'], true );
165 Arr::forget( $global_data, 'menuLocations' );
166 wp_update_post(
167 array(
168 'ID' => $post_id,
169 'post_content' => json_encode( $global_data ),
170 )
171 );
172 }
173
174 $this->updatePermalink();
175 }
176
177 public function beforeTermImport( $data ) {
178
179 if ( $data['taxonomy'] === 'nav_menu' ) {
180 if ( strpos( strtolower( $data['name'] ), '[old]' ) !== false ) {
181 return false;
182
183 }
184 }
185
186 if ( $data['taxonomy'] === 'wp_theme' ) {
187 $theme = get_stylesheet();
188 return array(
189 'taxonomy' => 'wp_theme',
190 'slug' => $theme,
191 'name' => $theme,
192 );
193 }
194
195 return $data;
196
197 }
198
199 public function afterTermImport( $term_id, $data ) {
200 $original_id = isset( $data['id'] ) ? (int) $data['id'] : 0;
201
202 if ( $original_id && $data['taxonomy'] === 'nav_menu' ) {
203 Arr::set( $this->mapping, "menus_map.{$original_id}", (int) $term_id );
204 }
205 }
206
207 public function importPostTerms( $terms, $post_id, $data ) {
208
209 $post_type = Arr::get( $data, 'post_type', null );
210 $theme = get_stylesheet();
211
212 if ( $post_type && in_array( $post_type, array( 'wp_template', 'wp_template_part' ), true ) ) {
213
214 Arr::set( $this->requires_remapping, "post.{$post_id}", true );
215
216 foreach ( $terms as $index => $term ) {
217 if ( $term['taxonomy'] === 'wp_theme' ) {
218 array_splice( $terms, $index, 1 );
219 }
220 }
221
222 $terms[] = array(
223 'taxonomy' => 'wp_theme',
224 'slug' => $theme,
225 'name' => $theme,
226 );
227
228 $key = sha1( $term['taxonomy'] . ':' . $term['taxonomy'] );
229 Arr::forget( $this->mapping, "term.{$key}" );
230 }
231
232 return $terms;
233 }
234
235
236 /**
237 * Restore the importer data from the transient.
238 *
239 * @return boolean
240 */
241 public function restore_import_data_transient() {
242 if ( $data = get_transient( static::WXR_IMPORTER_TRANSIENT ) ) {
243 $this->options = empty( $data['options'] ) ? array() : $data['options'];
244 $this->mapping = empty( $data['mapping'] ) ? array() : $data['mapping'];
245 $this->requires_remapping = empty( $data['requires_remapping'] ) ? array() : $data['requires_remapping'];
246 $this->exists = empty( $data['exists'] ) ? array() : $data['exists'];
247 $this->user_slug_override = empty( $data['user_slug_override'] ) ? array() : $data['user_slug_override'];
248 $this->url_remap = empty( $data['url_remap'] ) ? array() : $data['url_remap'];
249 $this->featured_images = empty( $data['featured_images'] ) ? array() : $data['featured_images'];
250
251 return true;
252 }
253
254 return false;
255 }
256
257 public function delete_import_data_transient() {
258 delete_transient( static::WXR_IMPORTER_TRANSIENT );
259 }
260
261 public function import_content( $import_file ) {
262
263 if ( strpos( ini_get( 'disable_functions' ), 'set_time_limit' ) === false ) {
264 set_time_limit( 300 );
265 }
266
267 // Disable import of authors.
268 add_filter( 'wxr_importer.pre_process.user', '__return_false' );
269
270 // Check, if we need to send another AJAX request and set the importing author to the current user.
271 add_filter( 'wxr_importer.pre_process.post', array( $this, 'new_ajax_request_maybe' ) );
272
273 // Import content.
274 if ( ! empty( $import_file ) ) {
275 ob_start();
276 $this->import( $import_file );
277 $output = ob_get_clean();
278 }
279
280 return $this->logger->error_output;
281 }
282
283 /**
284 * The main controller for the actual import stage.
285 *
286 * @param string $file Path to the WXR file for importing.
287 * @param array $options Import options (which parts to import).
288 *
289 * @return boolean
290 */
291 public function import( $file, $options = array() ) {
292 // Start the import timer.
293 $this->start_time = microtime( true );
294
295 return parent::import( $file, $options );
296 }
297
298 /**
299 * Does the post exist?
300 *
301 * @param array $data Post data to check against.
302 *
303 * @return int|bool Existing post ID if it exists, false otherwise.
304 */
305 protected function post_exists( $data ) {
306 // Constant-time lookup if we prefilled
307 $exists_key = $data['guid'];
308
309 if ( empty( $exists_key ) ) {
310 $exists_key = $data['post_title'] . '___' . $data['post_type'];
311 }
312
313 if ( $this->options['prefill_existing_posts'] ) {
314 // OCDI: fix for custom post types. The guids in the prefilled section are escaped, so these ones should be as well.
315 $exists_key = htmlentities( $exists_key );
316
317 return isset( $this->exists['post'][ $exists_key ] ) ? $this->exists['post'][ $exists_key ] : false;
318 }
319
320 // No prefilling, but might have already handled it
321 if ( isset( $this->exists['post'][ $exists_key ] ) ) {
322 return $this->exists['post'][ $exists_key ];
323 }
324
325 if ( $data['post_type'] === 'attachment' && $data['guid'] ) {
326 if ( $imported_asset = Importer::getImportByGuid( $data['guid'] ) ) {
327 $this->exists['post'][ $exists_key ] = $imported_asset['id'];
328 }
329 }
330
331 // Still nothing, try post_exists, and cache it
332 $exists = post_exists( $data['post_title'], $data['post_content'], $data['post_date'] );
333 $this->exists['post'][ $exists_key ] = $exists;
334
335 return $exists;
336 }
337
338 protected function process_attachment( $post, $meta, $remote_url ) {
339
340 $imported = Importer::importRemoteFile( $remote_url );
341 $post_id = $imported['id'];
342
343 if ( $post_id === 0 ) {
344 $error_message = sprintf( __( 'Unable to import remote file: %s', 'kubio' ), $remote_url );
345 $this->logger->warning( $error_message );
346
347 return new \WP_Error( 'attachment_processing_error', $error_message );
348 }
349
350 return $post_id;
351 }
352
353 /**
354 * Process and import post meta items.
355 *
356 * @param array $meta List of meta data arrays
357 * @param int $post_id Post to associate with
358 * @param array $post Post data
359 *
360 * @return int|WP_Error Number of meta items imported on success, error otherwise.
361 */
362 protected function process_post_meta( $meta, $post_id, $post ) {
363
364 // replace anchors urls
365 if ( Arr::get( $post, 'post_type' ) === 'nav_menu_item' ) {
366 foreach ( $meta as $index => $meta_item ) {
367 if ( $meta_item['key'] === '_menu_item_url' ) {
368 $value = $meta_item['value'];
369
370 $meta[ $index ] = array_merge(
371 $meta_item,
372 array(
373 'value' => str_replace( $this->base_url, site_url(), $value ),
374 )
375 );
376 break;
377 }
378 }
379 } else {
380 foreach ( $meta as $index => $meta_item ) {
381 $meta_item['value'] = maybe_serialize( $this->cleanupEmails( maybe_unserialize( $meta_item['value'] ) ) );
382 $meta[ $index ] = $meta_item;
383 }
384 }
385
386 if ( Arr::get( $post, 'post_type' ) === 'wp_template' || Arr::get( $post, 'post_type' ) === 'wp_template_part' ) {
387 $meta[] = array(
388 'key' => '_kubio_template_source',
389 'value' => 'kubio',
390 );
391 }
392
393 return parent::process_post_meta( $meta, $post_id, $post );
394 }
395
396
397 private function cleanupEmails( $content ) {
398 if ( is_array( $content ) ) {
399 $copy = $content;
400
401 array_walk_recursive(
402 $copy,
403 function ( &$value ) {
404 if ( is_string( $value ) ) {
405 $value = preg_replace( static::EMAIL_REGEXP, 'mail@example.com', $value );
406 }
407 }
408 );
409
410 return $copy;
411 }
412
413 return preg_replace( static::EMAIL_REGEXP, 'mail@example.com', $content );
414
415 }
416
417 protected function post_process() {
418 wp_cache_flush();
419 do_action( 'kubio/demo-site-import/post-process', $this );
420
421 $this->postProcessMenuItems();
422
423 parent::post_process();
424
425 // execute post process blocks after the template parts were set
426 $this->postProccessBlocks();
427
428 }
429
430 // update nav menu items parent meta value
431 protected function postProcessMenuItems() {
432 global $wpdb;
433 $post_mapping = $this->mapping['post'];
434
435 foreach ( $post_mapping as $original => $next_value ) {
436
437 $query = $wpdb->prepare(
438 "UPDATE {$wpdb->postmeta} SET meta_value = %d WHERE meta_key='_menu_item_menu_item_parent' AND meta_value= %d",
439 intval( $next_value ),
440 intval( $original )
441 );
442
443 $wpdb->query( $query );
444 }
445 }
446
447 protected function postProccessBlocks() {
448
449 $blocks_mapping = Arr::get( $this->mapping, 'blocks', array() );
450
451 foreach ( $blocks_mapping as $import_id => $mapped_blocks ) {
452 if ( $mapped_blocks !== null ) {
453
454 $post_id = Arr::get( $this->mapping, "post.{$import_id}", $import_id );
455
456 $post = get_post( $post_id );
457 if ( is_wp_error( $post ) ) {
458 DemoSitesHelpers::sendAjaxError( $post );
459 }
460
461 $blocks = parse_blocks( $post->post_content );
462
463 $blocks = $this->applyBlocksMapping( $blocks, $import_id );
464 $content = kubio_serialize_blocks( $blocks );
465
466 $result = wp_update_post(
467 wp_slash(
468 array(
469 'ID' => $post_id,
470 'post_content' => $content,
471 )
472 ),
473 true,
474 false
475 );
476
477 if ( is_wp_error( $result ) ) {
478 DemoSitesHelpers::sendAjaxError( $result );
479 }
480
481 Arr::set( $this->mapping, "blocks.{$import_id}", null );
482 $this->new_ajax_request_maybe();
483 }
484 }
485
486 }
487
488 private function applyBlocksMapping( $blocks, $import_id ) {
489
490 $mapped_blocks = Arr::get( $this->mapping, "blocks.{$import_id}", array() );
491
492 foreach ( $blocks as $index => $block ) {
493 $block_name = Arr::get( $block, 'blockName' );
494
495 if ( ! $block_name ) {
496 continue;
497 }
498
499 $block_has_mappings = Arr::get( $mapped_blocks, $block_name, false );
500
501 if ( $block_has_mappings ) {
502 $block = apply_filters( 'kubio/demo-import/apply-block-mapping', $block, $this );
503 }
504
505 $block['innerBlocks'] = $this->applyBlocksMapping( $block['innerBlocks'], $import_id );
506 $blocks[ $index ] = $block;
507 }
508
509 return $blocks;
510 }
511
512 /**
513 * Check if we need to create a new AJAX request, so that server does not timeout.
514 * And fix the import warning for missing post author.
515 *
516 * @param array $data current post data.
517 *
518 * @return array
519 */
520 public function new_ajax_request_maybe( $data = null ) {
521 $time = microtime( true ) - $this->start_time;
522
523 // We should make a new ajax call, if the time is right.
524 // On CLI execute this in one step.
525 if ( $this->options['is_stepped'] && $time > 20 ) {
526 $this->requestNewAjaxCall();
527 }
528
529 // Set importing author to the current user.
530 // Fixes the [WARNING] Could not find the author for ... log warning messages.
531 $current_user_obj = wp_get_current_user();
532 $data['post_author'] = $current_user_obj->user_login;
533
534 return $data;
535 }
536
537 public function requestNewAjaxCall() {
538 if ( defined( 'WP_CLI' ) && WP_CLI ) {
539 return;
540 }
541 $time = microtime( true ) - $this->start_time;
542 $response = array(
543 'status' => 'requires-new-ajax-call',
544 'log' => 'Request time almost expired. Start new request!: ' . $time,
545 'num_of_imported_posts' => count( $this->mapping['post'] ),
546 );
547
548 // Add message to log file.
549 $this->logger->info( __( 'New AJAX call!', 'wordpress-importer' ) );
550
551 // Set the current importer state, so it can be continued on the next AJAX call.
552 $this->set_current_importer_data();
553
554 // Send the request for a new AJAX call.
555 wp_send_json( $response );
556 }
557
558 /**
559 * Save current importer data to the DB, for later use.
560 */
561 public function set_current_importer_data() {
562 $data = array(
563 'options' => $this->options,
564 'mapping' => $this->mapping,
565 'requires_remapping' => $this->requires_remapping,
566 'exists' => $this->exists,
567 'user_slug_override' => $this->user_slug_override,
568 'url_remap' => $this->url_remap,
569 'featured_images' => $this->featured_images,
570 );
571
572 $this->save_current_import_data_transient( $data );
573 }
574
575 /**
576 * Set the importer data to the transient.
577 *
578 * @param array $data Data to be saved to the transient.
579 */
580 public function save_current_import_data_transient( $data ) {
581 set_transient( static::WXR_IMPORTER_TRANSIENT, $data, MINUTE_IN_SECONDS );
582 }
583
584 protected function post_process_posts( $todo ) {
585 $this->logger->info( __( 'Posts post processing', 'kubio' ) );
586 foreach ( $todo as $post_id => $_ ) {
587 $terms = get_post_meta( $post_id, '_wxr_import_term', false );
588 foreach ( $terms as $term ) {
589
590 $found_term = term_exists( $term['slug'], $term['taxonomy'] );
591
592 if ( ! $found_term ) {
593 $this->logger->info( sprintf( __( 'Create term `%1$s` in `%2$s`', 'kubio' ), $term['slug'], $term['taxonomy'] ) );
594 $found_term = wp_insert_term(
595 $term['name'],
596 $term['taxonomy'],
597 array(
598 'slug' => $term['slug'],
599 )
600 );
601 }
602
603 if ( is_wp_error( $found_term ) ) {
604 continue;
605 }
606
607 $term_id = (int) $found_term['term_id'];
608 wp_set_post_terms( $post_id, array( $term_id ), $term['taxonomy'], false );
609 }
610
611 delete_post_meta( $post_id, '_wxr_import_term' );
612 }
613
614 parent::post_process_posts( $todo );
615 }
616
617 public function getBaseUrl() {
618 return $this->base_url;
619 }
620 }
621