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