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