PluginProbe
aBlocks – Gutenberg Blocks, User Dashboard Builder, Popup Builder, Form Builder & Animation Builder / 2.8.0
aBlocks – Gutenberg Blocks, User Dashboard Builder, Popup Builder, Form Builder & Animation Builder v2.8.0
2.12.0 2.11.1 2.11.0 2.10.0 2.9.0 2.7.4 2.7.5 2.7.6 2.7.7 2.8.0 2.8.1 2.9.1 trunk 1.0 1.0-beta1 1.0-beta2 1.0-beta3 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 1.1.2 1.2.0 1.2.1 All 78 releases
ablocks / includes / import / wp-import.php

wp-import.php in aBlocks – Gutenberg Blocks, User Dashboard Builder, Popup Builder, Form Builder & Animation Builder 2.8.0, at includes/import/wp-import.php

1,415 lines 45.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace ABlocks\import;
4
5 use ABlocks\Admin\Settings\Base;
6 use ABlocks\Helper;
7 use ABlocks\import\XmlParsers\WXR_Parser;
8 use WP_Error;
9
10 if ( ! defined( 'ABSPATH' ) ) {
11 exit;
12 }
13
14 /**
15 * WordPress importer class.
16 */
17 class WP_Import extends \WP_Importer {
18
19 public $id;
20
21 public $version;
22 public $authors = array();
23 public $posts = array();
24 public $terms = array();
25 public $categories = array();
26 public $tags = array();
27 public $base_url = '';
28 public $show_on_front = 'posts';
29 public $page_on_front = 0;
30 public $page_for_posts = 0;
31 public $storeengine_pages = [];
32 public $academy_pages = [];
33 public $ablocks_pages = [];
34 public $ablocks_options = [];
35
36 public $processed_authors = array();
37 public $author_mapping = array();
38 public $processed_terms = array();
39 public $processed_posts = array();
40 public $post_orphans = array();
41 public $processed_menu_items = array();
42 public $menu_item_orphans = array();
43 public $missing_menu_items = array();
44
45 public $fetch_attachments = true;
46 public $url_remap = array();
47 public $featured_images = array();
48
49 /**
50 * The main controller for the actual import stage.
51 *
52 * @param string $file Path to the WXR file for importing.
53 * @param bool $with_attachments with attachments ?.
54 */
55 public function import( string $file, bool $with_attachments = true ) {
56 add_filter( 'import_post_meta_key', array( $this, 'is_valid_meta_key' ) );
57 add_filter( 'http_request_timeout', array( &$this, 'bump_request_timeout' ) );
58
59 $this->fetch_attachments = $with_attachments;
60
61 $result = $this->import_start( $file );
62 if ( is_wp_error( $result ) ) {
63 return $result;
64 }
65
66 wp_suspend_cache_invalidation();
67 $this->process_categories();
68 $this->process_tags();
69 $this->process_terms();
70 $this->process_posts();
71 wp_suspend_cache_invalidation( false );
72
73 // update incorrect/missing information in the DB
74 $this->backfill_parents();
75 $this->backfill_attachment_urls();
76 $this->remap_featured_images();
77 $this->save_global_settings();
78
79 $this->import_end();
80
81 return true;
82 }
83
84 /**
85 * Parses the WXR file and prepares us for the task of processing parsed data
86 *
87 * @param string $file Path to the WXR file for importing.
88 */
89 public function import_start( string $file ) {
90 if ( ! is_file( $file ) ) {
91 return new WP_Error( 'file_not_found', __( 'File not found', 'ablocks' ) );
92 }
93
94 Helper::emit_sse_message( [
95 'action' => 'log',
96 'message' => __( 'Import process started...', 'ablocks' )
97 ] );
98
99 $import_data = $this->parse( $file );
100
101 if ( is_wp_error( $import_data ) ) {
102 return $import_data;
103 }
104
105 $this->version = $import_data['version'];
106 $this->get_authors_from_import( $import_data );
107 $this->posts = $import_data['posts'];
108 $this->terms = $import_data['terms'];
109 $this->categories = $import_data['categories'];
110 $this->tags = $import_data['tags'];
111 $this->base_url = esc_url( $import_data['base_url'] );
112 $this->show_on_front = $import_data['show_on_front'];
113 $this->page_on_front = $import_data['page_on_front'];
114 $this->page_for_posts = $import_data['page_for_posts'];
115 $this->storeengine_pages = $import_data['storeengine_pages'];
116 $this->academy_pages = $import_data['academy_pages'];
117 $this->ablocks_pages = $import_data['ablocks_pages'];
118 $this->ablocks_options = $import_data['ablocks_options'];
119
120 wp_defer_term_counting( true );
121 wp_defer_comment_counting( true );
122
123 do_action( 'ablocks/template_import_start' );
124
125 return true;
126 }
127
128 /**
129 * Performs post-import cleanup of files and the cache
130 */
131 public function import_end() {
132 wp_import_cleanup( $this->id );
133
134 wp_cache_flush();
135 foreach ( get_taxonomies() as $tax ) {
136 delete_option( "{$tax}_children" );
137 _get_term_hierarchy( $tax );
138 }
139
140 wp_defer_term_counting( false );
141 wp_defer_comment_counting( false );
142
143 do_action( 'ablocks/template_import_end' );
144
145 Helper::emit_sse_message( [
146 'action' => 'log',
147 'level' => 'info',
148 'message' => __( 'Import process Finished.', 'ablocks' ),
149 ] );
150 }
151
152 /**
153 * Handles the WXR upload and initial parsing of the file to prepare for
154 * displaying author import options
155 *
156 * @return bool False if error uploading or invalid file, true otherwise
157 */
158 public function handle_upload(): bool {
159 $file = wp_import_handle_upload();
160
161 if ( isset( $file['error'] ) || ! file_exists( $file['file'] ) ) {
162 return false;
163 }
164
165 $this->id = (int) $file['id'];
166 $import_data = $this->parse( $file['file'] );
167 if ( is_wp_error( $import_data ) ) {
168 return false;
169 }
170
171 $this->version = $import_data['version'];
172 $this->get_authors_from_import( $import_data );
173
174 return true;
175 }
176
177 /**
178 * Retrieve authors from parsed WXR data
179 *
180 * Uses the provided author information from WXR 1.1 files
181 * or extracts info from each post for WXR 1.0 files
182 *
183 * @param array $import_data Data returned by a WXR parser.
184 */
185 public function get_authors_from_import( $import_data ) {
186 if ( ! empty( $import_data['authors'] ) ) {
187 $this->authors = $import_data['authors'];
188 // no author information, grab it from the posts
189 } else {
190 foreach ( $import_data['posts'] as $post ) {
191 $login = sanitize_user( $post['post_author'], true );
192 if ( empty( $login ) ) {
193 continue;
194 }
195
196 if ( ! isset( $this->authors[ $login ] ) ) {
197 $this->authors[ $login ] = array(
198 'author_login' => $login,
199 'author_display_name' => $post['post_author'],
200 );
201 }
202 }
203 }
204 }
205
206 /**
207 * Create new categories based on import information
208 *
209 * Doesn't create a new category if its slug already exists
210 */
211 public function process_categories() {
212 $this->categories = apply_filters( 'ablocks/template_import_categories', $this->categories );
213
214 if ( empty( $this->categories ) ) {
215 return;
216 }
217
218 foreach ( $this->categories as $cat ) {
219 // if the category already exists leave it alone
220 $term_id = term_exists( $cat['category_nicename'], 'category' );
221 if ( $term_id ) {
222 if ( is_array( $term_id ) ) {
223 $term_id = $term_id['term_id'];
224 }
225 if ( isset( $cat['term_id'] ) ) {
226 $this->processed_terms[ intval( $cat['term_id'] ) ] = (int) $term_id;
227 }
228 continue;
229 }
230
231 $parent = empty( $cat['category_parent'] ) ? 0 : category_exists( $cat['category_parent'] );
232 $description = isset( $cat['category_description'] ) ? $cat['category_description'] : '';
233
234 $data = array(
235 'category_nicename' => $cat['category_nicename'],
236 'category_parent' => $parent,
237 'cat_name' => wp_slash( $cat['cat_name'] ),
238 'category_description' => wp_slash( $description ),
239 );
240
241 $id = wp_insert_category( $data, true );
242 if ( ! is_wp_error( $id ) && $id > 0 ) {
243 if ( isset( $cat['term_id'] ) ) {
244 $this->processed_terms[ intval( $cat['term_id'] ) ] = $id;
245 }
246 } else {
247 continue;
248 }
249
250 $this->process_termmeta( $cat, $id );
251 }//end foreach
252
253 Helper::emit_sse_message( [
254 'action' => 'log',
255 'level' => 'info',
256 'message' => __( 'Categories imported', 'ablocks' ),
257 ] );
258
259 unset( $this->categories );
260 }
261
262 /**
263 * Create new post tags based on import information
264 *
265 * Doesn't create a tag if its slug already exists
266 */
267 public function process_tags() {
268 $this->tags = apply_filters( 'ablocks/template_import_tags', $this->tags );
269
270 if ( empty( $this->tags ) ) {
271 return;
272 }
273
274 foreach ( $this->tags as $tag ) {
275 // if the tag already exists leave it alone
276 $term_id = term_exists( $tag['tag_slug'], 'post_tag' );
277 if ( $term_id ) {
278 if ( is_array( $term_id ) ) {
279 $term_id = $term_id['term_id'];
280 }
281 if ( isset( $tag['term_id'] ) ) {
282 $this->processed_terms[ intval( $tag['term_id'] ) ] = (int) $term_id;
283 }
284 continue;
285 }
286
287 $description = isset( $tag['tag_description'] ) ? $tag['tag_description'] : '';
288 $args = array(
289 'slug' => $tag['tag_slug'],
290 'description' => wp_slash( $description ),
291 );
292
293 $id = wp_insert_term( wp_slash( $tag['tag_name'] ), 'post_tag', $args );
294 if ( ! is_wp_error( $id ) ) {
295 if ( isset( $tag['term_id'] ) ) {
296 $this->processed_terms[ intval( $tag['term_id'] ) ] = $id['term_id'];
297 }
298 } else {
299 continue;
300 }
301
302 $this->process_termmeta( $tag, $id['term_id'] );
303 }//end foreach
304
305 Helper::emit_sse_message( [
306 'action' => 'log',
307 'level' => 'info',
308 'message' => __( 'Tags imported', 'ablocks' ),
309 ] );
310
311 unset( $this->tags );
312 }
313
314 /**
315 * Create new terms based on import information
316 *
317 * Doesn't create a term its slug already exists
318 */
319 public function process_terms() {
320 $this->terms = apply_filters( 'ablocks/template_import_terms', $this->terms );
321
322 if ( empty( $this->terms ) ) {
323 return;
324 }
325
326 foreach ( $this->terms as $term ) {
327 // if the term already exists in the correct taxonomy leave it alone
328 $term_id = term_exists( $term['slug'], $term['term_taxonomy'] );
329 if ( $term_id ) {
330 if ( is_array( $term_id ) ) {
331 $term_id = $term_id['term_id'];
332 }
333 if ( isset( $term['term_id'] ) ) {
334 $this->processed_terms[ intval( $term['term_id'] ) ] = (int) $term_id;
335 }
336 continue;
337 }
338
339 if ( empty( $term['term_parent'] ) ) {
340 $parent = 0;
341 } else {
342 $parent = term_exists( $term['term_parent'], $term['term_taxonomy'] );
343 if ( is_array( $parent ) ) {
344 $parent = $parent['term_id'];
345 }
346 }
347
348 $description = isset( $term['term_description'] ) ? $term['term_description'] : '';
349 $args = array(
350 'slug' => $term['slug'],
351 'description' => wp_slash( $description ),
352 'parent' => (int) $parent,
353 );
354
355 $id = wp_insert_term( wp_slash( $term['term_name'] ), $term['term_taxonomy'], $args );
356 if ( ! is_wp_error( $id ) ) {
357 if ( isset( $term['term_id'] ) ) {
358 $this->processed_terms[ intval( $term['term_id'] ) ] = $id['term_id'];
359 }
360 } else {
361 continue;
362 }
363
364 $this->process_termmeta( $term, $id['term_id'] );
365 }//end foreach
366
367 Helper::emit_sse_message( [
368 'action' => 'log',
369 'level' => 'info',
370 'message' => __( 'Terms imported', 'ablocks' ),
371 ] );
372
373 unset( $this->terms );
374 }
375
376 /**
377 * Add metadata to imported term.
378 *
379 * @param array $term Term data from WXR import.
380 * @param int $term_id ID of the newly created term.
381 *
382 * @since 0.6.2
383 */
384 protected function process_termmeta( $term, $term_id ) {
385 if ( ! isset( $term['termmeta'] ) ) {
386 $term['termmeta'] = array();
387 }
388
389 /**
390 * Filters the metadata attached to an imported term.
391 *
392 * @param array $termmeta Array of term meta.
393 * @param int $term_id ID of the newly created term.
394 * @param array $term Term data from the WXR import.
395 *
396 * @since 0.6.2
397 */
398 $term['termmeta'] = apply_filters( 'ablocks/template_import_term_meta', $term['termmeta'], $term_id, $term );
399
400 if ( empty( $term['termmeta'] ) ) {
401 return;
402 }
403
404 foreach ( $term['termmeta'] as $meta ) {
405 /**
406 * Filters the meta key for an imported piece of term meta.
407 *
408 * @param string $meta_key Meta key.
409 * @param int $term_id ID of the newly created term.
410 * @param array $term Term data from the WXR import.
411 *
412 * @since 0.6.2
413 */
414 $key = apply_filters( 'import_term_meta_key', $meta['key'], $term_id, $term );
415 if ( ! $key ) {
416 continue;
417 }
418
419 // Export gets meta straight from the DB so could have a serialized string
420 $value = maybe_unserialize( $meta['value'] );
421
422 add_term_meta( $term_id, wp_slash( $key ), wp_slash_strings_only( $value ) );
423
424 /**
425 * Fires after term meta is imported.
426 *
427 * @param int $term_id ID of the newly created term.
428 * @param string $key Meta key.
429 * @param mixed $value Meta value.
430 *
431 * @since 0.6.2
432 */
433 do_action( 'import_term_meta', $term_id, $key, $value );
434 }//end foreach
435 }
436
437 /**
438 * Create new posts based on import information
439 *
440 * Posts marked as having a parent which doesn't exist will become top level items.
441 * Doesn't create a new post if: the post type doesn't exist, the given post ID
442 * is already noted as imported or a post with the same title and date already exists.
443 * Note that new/updated terms, comments and meta are imported for the last of the above.
444 */
445 public function process_posts() {
446 $this->posts = apply_filters( 'ablocks/template_import_posts', $this->posts );
447
448 foreach ( $this->posts as $post ) {
449 $post = apply_filters( 'ablocks/template_import_post_data_raw', $post );
450
451 if ( ! post_type_exists( $post['post_type'] ) ) {
452 do_action( 'ablocks/template_import_post_exists', $post );
453 continue;
454 }
455
456 if ( isset( $this->processed_posts[ $post['post_id'] ] ) && ! empty( $post['post_id'] ) ) {
457 continue;
458 }
459
460 if ( 'auto-draft' === $post['status'] ) {
461 continue;
462 }
463
464 if ( 'nav_menu_item' === $post['post_type'] ) {
465 $this->process_menu_item( $post );
466 continue;
467 }
468
469 $post_exists = post_exists( $post['post_title'], '', '', $post['post_type'] );
470 if ( $post_exists && in_array( $post['post_type'], [ 'wp_template', 'wp_template_part' ], true ) ) {
471 // remove existence template.
472 wp_delete_post( $post_exists, true );
473 $post_exists = false;
474 }
475
476 /**
477 * Filter ID of the existing post corresponding to post currently importing.
478 *
479 * Return 0 to force the post to be imported. Filter the ID to be something else
480 * to override which existing post is mapped to the imported post.
481 *
482 * @param int $post_exists Post ID, or 0 if post did not exist.
483 * @param array $post The post array to be inserted.
484 *
485 * @see post_exists()
486 * @since 0.6.2
487 */
488 $post_exists = apply_filters( 'ablocks/template_import_existing_post', $post_exists, $post );
489
490 if ( ! $this->check_for_known_page( $post ) && $post_exists ) {
491 $comment_post_id = $post_exists;
492 $post_id = $post_exists;
493 $this->processed_posts[ intval( $post['post_id'] ) ] = intval( $post_exists );
494 } else {
495 $post_parent = (int) $post['post_parent'];
496 if ( $post_parent ) {
497 // if we already know the parent, map it to the new local ID
498 if ( isset( $this->processed_posts[ $post_parent ] ) ) {
499 $post_parent = $this->processed_posts[ $post_parent ];
500 // otherwise record the parent for later
501 } else {
502 $this->post_orphans[ intval( $post['post_id'] ) ] = $post_parent;
503 $post_parent = 0;
504 }
505 }
506
507 // map the post author
508 $author = sanitize_user( $post['post_author'], true );
509 if ( isset( $this->author_mapping[ $author ] ) ) {
510 $author = $this->author_mapping[ $author ];
511 } else {
512 $author = (int) get_current_user_id();
513 }
514
515 $postdata = array(
516 'import_id' => $post['post_id'],
517 'post_author' => $author,
518 'post_date' => $post['post_date'],
519 'post_date_gmt' => $post['post_date_gmt'],
520 'post_content' => $post['post_content'],
521 'post_excerpt' => $post['post_excerpt'],
522 'post_title' => $post['post_title'],
523 'post_status' => $post['status'],
524 'post_name' => $post['post_name'],
525 'comment_status' => $post['comment_status'],
526 'ping_status' => $post['ping_status'],
527 'guid' => $post['guid'],
528 'post_parent' => $post_parent,
529 'menu_order' => $post['menu_order'],
530 'post_type' => $post['post_type'],
531 'post_password' => $post['post_password'],
532 );
533
534 // remove existence global styles.
535 if ( 'wp_global_styles' === $post['post_type'] ) {
536 $this->remove_gutenberg_styles();
537 }
538
539 $original_post_id = $post['post_id'];
540 $postdata = apply_filters( 'ablocks/template_import_post_data_processed', $postdata, $post );
541
542 $postdata = wp_slash( $postdata );
543
544 if ( 'attachment' === $postdata['post_type'] && ! $post_exists ) {
545 if ( ! $this->fetch_attachments ) {
546 continue;
547 }
548 $remote_url = ! empty( $post['attachment_url'] ) ? $post['attachment_url'] : $post['guid'];
549
550 // try to use _wp_attached file for upload folder placement to ensure the same location as the export site
551 // e.g. location is 2003/05/image.jpg but the attachment post_date is 2010/09, see media_handle_upload()
552 $postdata['upload_date'] = $post['post_date'];
553 if ( isset( $post['postmeta'] ) ) {
554 foreach ( $post['postmeta'] as $meta ) {
555 if ( '_wp_attached_file' === $meta['key'] ) {
556 if ( preg_match( '%^[0-9]{4}/[0-9]{2}%', $meta['value'], $matches ) ) {
557 $postdata['upload_date'] = $matches[0];
558 }
559 break;
560 }
561 }
562 }
563
564 $comment_post_id = $this->process_attachment( $postdata, $remote_url );
565 $post_id = $comment_post_id;
566 } else {
567 if ( $post_exists ) {
568 $comment_post_id = wp_update_post( array_merge( $postdata, array( 'ID' => $post_exists ) ) );
569 } else {
570 $comment_post_id = wp_insert_post( $postdata, true );
571 }
572 $post_id = $comment_post_id;
573 do_action( 'ablocks/template_import_insert_post', $post_id, $original_post_id, $postdata, $post );
574 }//end if
575
576 if ( is_wp_error( $post_id ) ) {
577 Helper::emit_sse_message( [
578 'action' => 'log',
579 'level' => 'warning',
580 'message' => "Failed to import: {$post['post_type']} - {$post['post_title']}",
581 ] );
582 continue;
583 }
584
585 if ( 1 === (int) $post['is_sticky'] ) {
586 stick_post( $post_id );
587 }
588
589 Helper::emit_sse_message( [
590 'action' => 'log',
591 'level' => 'info',
592 'message' => "{$post['post_type']} - {$post['post_title']} imported.",
593 ] );
594 }//end if
595
596 // update reading settings.
597 if ( 'page' === $this->show_on_front && 'page' === $post['post_type'] ) {
598 update_option( 'show_on_front', 'page' );
599 if ( $this->page_on_front === $post['post_id'] ) {
600 update_option( 'page_on_front', $post_id );
601 Helper::emit_sse_message( [
602 'action' => 'log',
603 'level' => 'info',
604 'message' => __( 'Front page setting\'s updated.', 'ablocks' ),
605 ] );
606 } elseif ( $post['post_id'] === $this->page_for_posts ) {
607 update_option( 'page_for_posts', $post_id );
608 Helper::emit_sse_message( [
609 'action' => 'log',
610 'level' => 'info',
611 'message' => __( 'Posts page setting\'s updated.', 'ablocks' ),
612 ] );
613 }
614 }//end if
615
616 // update storeengine page settings
617 if ( ! empty( $this->storeengine_pages ) && class_exists( 'StoreEngine' ) ) {
618 $se_page = array_search( $post['post_id'], $this->storeengine_pages, true );
619 if ( $se_page ) {
620 \StoreEngine\Admin\Settings\Base::save_settings( [
621 $se_page => $post_id
622 ] );
623 }
624 }
625
626 // update academy page settings
627 if ( ! empty( $this->academy_pages ) && class_exists( 'Academy' ) ) {
628 $academy_page = array_search( $post['post_id'], $this->academy_pages, true );
629 if ( $academy_page ) {
630 \Academy\Admin\Settings\Base::save_settings( [
631 $academy_page => $post_id
632 ] );
633 }
634 }
635
636 // update ablock page settings
637 if ( ! empty( $this->ablocks_pages ) ) {
638 $ablocks_page = array_search( $post['post_id'], $this->ablocks_pages, true );
639 if ( $ablocks_page ) {
640 Base::save_settings( [
641 $ablocks_page => $post_id
642 ] );
643 }
644 }
645
646 // map pre-import ID to local ID
647 $this->processed_posts[ intval( $post['post_id'] ) ] = (int) $post_id;
648
649 if ( ! isset( $post['terms'] ) ) {
650 $post['terms'] = array();
651 }
652
653 $post['terms'] = apply_filters( 'ablocks/template_import_post_terms', $post['terms'], $post_id, $post );
654
655 // add categories, tags and other terms
656 if ( ! empty( $post['terms'] ) ) {
657 $terms_to_set = array();
658 foreach ( $post['terms'] as $term ) {
659 // back compat with WXR 1.0 map 'tag' to 'post_tag'
660 $taxonomy = ( 'tag' === $term['domain'] ) ? 'post_tag' : $term['domain'];
661 if ( 'wp_theme' === $taxonomy ) {
662 $term['name'] = wp_get_theme()->get_stylesheet();
663 $term['slug'] = wp_get_theme()->get_stylesheet();
664 }
665 $term_exists = term_exists( $term['slug'], $taxonomy );
666 $term_id = is_array( $term_exists ) ? $term_exists['term_id'] : $term_exists;
667
668 if ( ! $term_id ) {
669 $t = wp_insert_term( $term['name'], $taxonomy, array( 'slug' => $term['slug'] ) );
670 if ( ! is_wp_error( $t ) ) {
671 $term_id = $t['term_id'];
672 do_action( 'ablocks/template_import_insert_term', $t, $term, $post_id, $post );
673 } else {
674 do_action( 'ablocks/template_import_insert_term_failed', $t, $term, $post_id, $post );
675 continue;
676 }
677 }
678 $terms_to_set[ $taxonomy ][] = intval( $term_id );
679 }//end foreach
680
681 foreach ( $terms_to_set as $tax => $ids ) {
682 $tt_ids = wp_set_post_terms( $post_id, $ids, $tax );
683 do_action( 'ablocks/template_import_set_post_terms', $tt_ids, $ids, $tax, $post_id, $post );
684 }
685 unset( $post['terms'], $terms_to_set );
686 }//end if
687
688 if ( ! isset( $post['comments'] ) ) {
689 $post['comments'] = array();
690 }
691
692 $post['comments'] = apply_filters( 'ablocks/template_import_post_comments', $post['comments'], $post_id, $post );
693
694 // add/update comments
695 if ( ! empty( $post['comments'] ) ) {
696 $num_comments = 0;
697 $inserted_comments = array();
698 foreach ( $post['comments'] as $comment ) {
699 $comment_id = $comment['comment_id'];
700 $newcomments[ $comment_id ]['comment_post_ID'] = $comment_post_id;
701 $newcomments[ $comment_id ]['comment_author'] = $comment['comment_author'];
702 $newcomments[ $comment_id ]['comment_author_email'] = $comment['comment_author_email'];
703 $newcomments[ $comment_id ]['comment_author_IP'] = $comment['comment_author_IP'];
704 $newcomments[ $comment_id ]['comment_author_url'] = $comment['comment_author_url'];
705 $newcomments[ $comment_id ]['comment_date'] = $comment['comment_date'];
706 $newcomments[ $comment_id ]['comment_date_gmt'] = $comment['comment_date_gmt'];
707 $newcomments[ $comment_id ]['comment_content'] = $comment['comment_content'];
708 $newcomments[ $comment_id ]['comment_approved'] = $comment['comment_approved'];
709 $newcomments[ $comment_id ]['comment_type'] = $comment['comment_type'];
710 $newcomments[ $comment_id ]['comment_parent'] = $comment['comment_parent'];
711 $newcomments[ $comment_id ]['commentmeta'] = isset( $comment['commentmeta'] ) ? $comment['commentmeta'] : array();
712 if ( isset( $this->processed_authors[ $comment['comment_user_id'] ] ) ) {
713 $newcomments[ $comment_id ]['user_id'] = $this->processed_authors[ $comment['comment_user_id'] ];
714 }
715 }
716 ksort( $newcomments );
717
718 foreach ( $newcomments as $key => $comment ) {
719 // if this is a new post we can skip the comment_exists() check
720 if ( ! $post_exists || ! comment_exists( $comment['comment_author'], $comment['comment_date'] ) ) {
721 if ( isset( $inserted_comments[ $comment['comment_parent'] ] ) ) {
722 $comment['comment_parent'] = $inserted_comments[ $comment['comment_parent'] ];
723 }
724
725 $comment_data = wp_slash( $comment );
726 unset( $comment_data['commentmeta'] ); // Handled separately, wp_insert_comment() also expects `comment_meta`.
727 $comment_data = wp_filter_comment( $comment_data );
728
729 $inserted_comments[ $key ] = wp_insert_comment( $comment_data );
730
731 do_action( 'ablocks/template_import_insert_comment', $inserted_comments[ $key ], $comment, $comment_post_id, $post );
732
733 foreach ( $comment['commentmeta'] as $meta ) {
734 $value = maybe_unserialize( $meta['value'] );
735
736 add_comment_meta( $inserted_comments[ $key ], wp_slash( $meta['key'] ), wp_slash_strings_only( $value ) );
737 }
738
739 ++ $num_comments;
740 }//end if
741 }//end foreach
742 unset( $newcomments, $inserted_comments, $post['comments'] );
743 }//end if
744
745 if ( ! isset( $post['postmeta'] ) ) {
746 $post['postmeta'] = array();
747 }
748
749 $post['postmeta'] = apply_filters( 'ablocks/template_import_post_meta', $post['postmeta'], $post_id, $post );
750
751 // add/update post meta
752 if ( ! empty( $post['postmeta'] ) ) {
753 foreach ( $post['postmeta'] as $meta ) {
754 $key = apply_filters( 'import_post_meta_key', $meta['key'], $post_id, $post );
755 $value = false;
756
757 if ( '_edit_last' === $key ) {
758 if ( isset( $this->processed_authors[ intval( $meta['value'] ) ] ) ) {
759 $value = $this->processed_authors[ intval( $meta['value'] ) ];
760 } else {
761 $key = false;
762 }
763 }
764
765 if ( $key ) {
766 // export gets meta straight from the DB so could have a serialized string
767 if ( ! $value ) {
768 $value = maybe_unserialize( $meta['value'] );
769 }
770
771 add_post_meta( $post_id, wp_slash( $key ), wp_slash_strings_only( $value ) );
772
773 do_action( 'import_post_meta', $post_id, $key, $value );
774
775 // if the post has a featured image, take note of this in case of remap
776 if ( '_thumbnail_id' === $key ) {
777 $this->featured_images[ $post_id ] = (int) $value;
778 }
779 }
780 }//end foreach
781 }//end if
782 }//end foreach
783
784 unset( $this->posts );
785 }
786
787 private function remove_gutenberg_styles() {
788 $args = array(
789 'post_type' => 'wp_global_styles',
790 'post_status' => 'any',
791 'posts_per_page' => 10,
792 'fields' => 'ids',
793 );
794 $styles = get_posts( $args );
795 foreach ( $styles as $style ) {
796 wp_delete_post( $style );
797 }
798 Helper::emit_sse_message( [
799 'action' => 'log',
800 'level' => 'info',
801 'message' => __( 'Existence gutenberg styles removed.', 'ablocks' ),
802 ] );
803 }
804
805 private function check_for_known_page( array $post ) {
806 if ( ! empty( $this->storeengine_pages ) && class_exists( 'StoreEngine' ) ) {
807 return (bool) array_search( $post['post_id'], $this->storeengine_pages, true );
808 }
809
810 if ( ! empty( $this->academy_pages ) && class_exists( 'Academy' ) ) {
811 return (bool) array_search( $post['post_id'], $this->academy_pages, true );
812 }
813
814 if ( ! empty( $this->ablocks_pages ) ) {
815 return (bool) array_search( $post['post_id'], $this->ablocks_pages, true );
816 }
817
818 return false;
819 }
820
821 /**
822 * Attempt to create a new menu item from import data
823 *
824 * Fails for draft, orphaned menu items and those without an associated nav_menu
825 * or an invalid nav_menu term. If the post type or term object which the menu item
826 * represents doesn't exist then the menu item will not be imported (waits until the
827 * end of the import to retry again before discarding).
828 *
829 * @param array $item Menu item details from WXR file.
830 */
831 public function process_menu_item( $item ) {
832 // skip draft, orphaned menu items
833 if ( 'draft' === $item['status'] ) {
834 return;
835 }
836
837 $menu_slug = false;
838 if ( isset( $item['terms'] ) ) {
839 // loop through terms, assume first nav_menu term is correct menu
840 foreach ( $item['terms'] as $term ) {
841 if ( 'nav_menu' === $term['domain'] ) {
842 $menu_slug = $term['slug'];
843 break;
844 }
845 }
846 }
847
848 // no nav_menu term associated with this menu item
849 if ( ! $menu_slug ) {
850 return;
851 }
852
853 $menu_id = term_exists( $menu_slug, 'nav_menu' );
854 if ( ! $menu_id ) {
855 return;
856 } else {
857 $menu_id = is_array( $menu_id ) ? $menu_id['term_id'] : $menu_id;
858 }
859
860 foreach ( $item['postmeta'] as $meta ) {
861 ${$meta['key']} = $meta['value'];
862 }
863
864 if ( 'taxonomy' === $_menu_item_type && isset( $this->processed_terms[ intval( $_menu_item_object_id ) ] ) ) {
865 $_menu_item_object_id = $this->processed_terms[ intval( $_menu_item_object_id ) ];
866 } elseif ( 'post_type' === $_menu_item_type && isset( $this->processed_posts[ intval( $_menu_item_object_id ) ] ) ) {
867 $_menu_item_object_id = $this->processed_posts[ intval( $_menu_item_object_id ) ];
868 } elseif ( 'custom' !== $_menu_item_type ) {
869 // associated object is missing or not imported yet, we'll retry later
870 $this->missing_menu_items[] = $item;
871
872 return;
873 }
874
875 if ( isset( $this->processed_menu_items[ intval( $_menu_item_menu_item_parent ) ] ) ) {
876 $_menu_item_menu_item_parent = $this->processed_menu_items[ intval( $_menu_item_menu_item_parent ) ];
877 } elseif ( $_menu_item_menu_item_parent ) {
878 $this->menu_item_orphans[ intval( $item['post_id'] ) ] = (int) $_menu_item_menu_item_parent;
879 $_menu_item_menu_item_parent = 0;
880 }
881
882 // wp_update_nav_menu_item expects CSS classes as a space separated string
883 $_menu_item_classes = maybe_unserialize( $_menu_item_classes );
884 if ( is_array( $_menu_item_classes ) ) {
885 $_menu_item_classes = implode( ' ', $_menu_item_classes );
886 }
887
888 $args = array(
889 'menu-item-object-id' => $_menu_item_object_id,
890 'menu-item-object' => $_menu_item_object,
891 'menu-item-parent-id' => $_menu_item_menu_item_parent,
892 'menu-item-position' => intval( $item['menu_order'] ),
893 'menu-item-type' => $_menu_item_type,
894 'menu-item-title' => $item['post_title'],
895 'menu-item-url' => $_menu_item_url,
896 'menu-item-description' => $item['post_content'],
897 'menu-item-attr-title' => $item['post_excerpt'],
898 'menu-item-target' => $_menu_item_target,
899 'menu-item-classes' => $_menu_item_classes,
900 'menu-item-xfn' => $_menu_item_xfn,
901 'menu-item-status' => $item['status'],
902 );
903
904 $id = wp_update_nav_menu_item( $menu_id, 0, $args );
905 if ( $id && ! is_wp_error( $id ) ) {
906 $this->processed_menu_items[ intval( $item['post_id'] ) ] = (int) $id;
907 }
908 }
909
910 /**
911 * If fetching attachments is enabled then attempt to create a new attachment
912 *
913 * @param array $post Attachment post details from WXR.
914 * @param string $url URL to fetch attachment from.
915 *
916 * @return int|WP_Error Post ID on success, WP_Error otherwise
917 */
918 public function process_attachment( $post, $url ) {
919 if ( ! $this->fetch_attachments ) {
920 return;
921 }
922
923 // if the URL is absolute, but does not contain address, then upload it assuming base_site_url
924 if ( preg_match( '|^/[\w\W]+$|', $url ) ) {
925 $url = rtrim( $this->base_url, '/' ) . $url;
926 }
927
928 $upload = $this->fetch_remote_file( $url, $post );
929 if ( is_wp_error( $upload ) ) {
930 return $upload;
931 }
932
933 $info = wp_check_filetype( $upload['file'] );
934 if ( $info ) {
935 $post['post_mime_type'] = $info['type'];
936 } else {
937 return new WP_Error( 'attachment_processing_error', __( 'Invalid file type', 'ablocks' ) );
938 }
939
940 $post['guid'] = $upload['url'];
941
942 // as per wp-admin/includes/upload.php
943 $post_id = wp_insert_attachment( $post, $upload['file'] );
944 wp_update_attachment_metadata( $post_id, wp_generate_attachment_metadata( $post_id, $upload['file'] ) );
945
946 // remap resized image URLs, works by stripping the extension and remapping the URL stub.
947 if ( preg_match( '!^image/!', $info['type'] ) ) {
948 $parts = pathinfo( $url );
949 $name = basename( $parts['basename'], ".{$parts['extension']}" ); // PATHINFO_FILENAME in PHP 5.2
950
951 $parts_new = pathinfo( $upload['url'] );
952 $name_new = basename( $parts_new['basename'], ".{$parts_new['extension']}" );
953
954 $this->url_remap[ $parts['dirname'] . '/' . $name ] = $parts_new['dirname'] . '/' . $name_new;
955 }
956
957 return $post_id;
958 }
959
960 /**
961 * Attempt to download a remote file attachment
962 *
963 * @param string $url URL of item to fetch.
964 * @param array $post Attachment details.
965 *
966 * @return array|WP_Error Local file location details on success, WP_Error otherwise
967 */
968 public function fetch_remote_file( $url, $post ) {
969 // Extract the file name from the URL.
970 $path = wp_parse_url( $url, PHP_URL_PATH );
971 $file_name = '';
972 if ( is_string( $path ) ) {
973 $file_name = basename( $path );
974 }
975
976 if ( ! $file_name ) {
977 $file_name = md5( $url );
978 }
979
980 $tmp_file_name = wp_tempnam( $file_name );
981 if ( ! $tmp_file_name ) {
982 return new WP_Error( 'import_no_file', __( 'Could not create temporary file.', 'ablocks' ) );
983 }
984
985 // Fetch the remote URL and write it to the placeholder file.
986 $remote_response = wp_safe_remote_get(
987 $url,
988 array(
989 'timeout' => 300,
990 'stream' => true,
991 'filename' => $tmp_file_name,
992 'headers' => array(
993 'Accept-Encoding' => 'identity',
994 ),
995 )
996 );
997
998 if ( is_wp_error( $remote_response ) ) {
999 // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged, WordPress.WP.AlternativeFunctions.unlink_unlink
1000 @unlink( $tmp_file_name );
1001
1002 return new WP_Error(
1003 'import_file_error',
1004 sprintf(
1005 /* translators: 1: The WordPress error message. 2: The WordPress error code. */
1006 __( 'Request failed due to an error: %1$s (%2$s)', 'ablocks' ),
1007 esc_html( $remote_response->get_error_message() ),
1008 esc_html( $remote_response->get_error_code() )
1009 )
1010 );
1011 }
1012
1013 $remote_response_code = (int) wp_remote_retrieve_response_code( $remote_response );
1014
1015 // Make sure the fetch was successful.
1016 if ( 200 !== $remote_response_code ) {
1017 if ( file_exists( $tmp_file_name ) ) {
1018 // phpcs:ignore WordPress.WP.AlternativeFunctions.unlink_unlink
1019 unlink( $tmp_file_name );
1020 }
1021
1022 return new WP_Error(
1023 'import_file_error',
1024 sprintf(
1025 /* translators: 1: The HTTP error message. 2: The HTTP error code. */
1026 __( 'Remote server returned the following unexpected result: %1$s (%2$s)', 'ablocks' ),
1027 get_status_header_desc( $remote_response_code ),
1028 esc_html( $remote_response_code )
1029 )
1030 );
1031 }
1032
1033 $headers = wp_remote_retrieve_headers( $remote_response );
1034
1035 // Request failed.
1036 if ( ! $headers ) {
1037 if ( file_exists( $tmp_file_name ) ) {
1038 // phpcs:ignore WordPress.WP.AlternativeFunctions.unlink_unlink
1039 unlink( $tmp_file_name );
1040 }
1041
1042 return new WP_Error( 'import_file_error', __( 'Remote server did not respond', 'ablocks' ) );
1043 }
1044
1045 $filesize = (int) filesize( $tmp_file_name );
1046
1047 if ( 0 === $filesize ) {
1048 if ( file_exists( $tmp_file_name ) ) {
1049 // phpcs:ignore WordPress.WP.AlternativeFunctions.unlink_unlink
1050 unlink( $tmp_file_name );
1051 }
1052
1053 return new WP_Error( 'import_file_error', __( 'Zero size file downloaded', 'ablocks' ) );
1054 }
1055
1056 if ( ! isset( $headers['content-encoding'] ) && isset( $headers['content-length'] ) && $filesize !== (int) $headers['content-length'] ) {
1057 if ( file_exists( $tmp_file_name ) ) {
1058 // phpcs:ignore WordPress.WP.AlternativeFunctions.unlink_unlink
1059 unlink( $tmp_file_name );
1060 }
1061
1062 return new WP_Error( 'import_file_error', __( 'Downloaded file has incorrect size', 'ablocks' ) );
1063 }
1064
1065 $max_size = (int) $this->max_attachment_size();
1066 if ( ! empty( $max_size ) && $filesize > $max_size ) {
1067 if ( file_exists( $tmp_file_name ) ) {
1068 // phpcs:ignore WordPress.WP.AlternativeFunctions.unlink_unlink
1069 unlink( $tmp_file_name );
1070 }
1071
1072 // Translators: %s is the file size limit for the remote file import
1073 return new WP_Error( 'import_file_error', sprintf( __( 'Remote file is too large, limit is %s', 'ablocks' ), size_format( $max_size ) ) );
1074 }
1075
1076 // Override file name with Content-Disposition header value.
1077 if ( ! empty( $headers['content-disposition'] ) ) {
1078 $file_name_from_disposition = self::get_filename_from_disposition( (array) $headers['content-disposition'] );
1079 if ( $file_name_from_disposition ) {
1080 $file_name = $file_name_from_disposition;
1081 }
1082 }
1083
1084 // Set file extension if missing.
1085 $file_ext = pathinfo( $file_name, PATHINFO_EXTENSION );
1086 if ( ! $file_ext && ! empty( $headers['content-type'] ) ) {
1087 $extension = self::get_file_extension_by_mime_type( $headers['content-type'] );
1088 if ( $extension ) {
1089 $file_name = "{$file_name}.{$extension}";
1090 }
1091 }
1092
1093 // Handle the upload like _wp_handle_upload() does.
1094 $wp_filetype = wp_check_filetype_and_ext( $tmp_file_name, $file_name );
1095 $ext = empty( $wp_filetype['ext'] ) ? '' : $wp_filetype['ext'];
1096 $type = empty( $wp_filetype['type'] ) ? '' : $wp_filetype['type'];
1097 $proper_filename = empty( $wp_filetype['proper_filename'] ) ? '' : $wp_filetype['proper_filename'];
1098
1099 // Check to see if wp_check_filetype_and_ext() determined the filename was incorrect.
1100 if ( $proper_filename ) {
1101 $file_name = $proper_filename;
1102 }
1103
1104 if ( ( ! $type || ! $ext ) && ! current_user_can( 'unfiltered_upload' ) ) {
1105 return new WP_Error( 'import_file_error', __( 'Sorry, this file type is not permitted for security reasons.', 'ablocks' ) );
1106 }
1107
1108 $uploads = wp_upload_dir( $post['upload_date'] );
1109 if ( ! ( $uploads && false === $uploads['error'] ) ) {
1110 return new WP_Error( 'upload_dir_error', $uploads['error'] );
1111 }
1112
1113 // Move the file to the uploads dir.
1114 $file_name = wp_unique_filename( $uploads['path'], $file_name );
1115 $new_file = $uploads['path'] . "/$file_name";
1116 $move_new_file = copy( $tmp_file_name, $new_file );
1117
1118 if ( ! $move_new_file ) {
1119 if ( file_exists( $tmp_file_name ) ) {
1120 // phpcs:ignore WordPress.WP.AlternativeFunctions.unlink_unlink
1121 unlink( $tmp_file_name );
1122 }
1123
1124 return new WP_Error( 'import_file_error', __( 'The uploaded file could not be moved', 'ablocks' ) );
1125 }
1126
1127 // Set correct file permissions.
1128 $stat = stat( dirname( $new_file ) );
1129 $perms = $stat['mode'] & 0000666;
1130 // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_chmod
1131 chmod( $new_file, $perms );
1132
1133 $upload = array(
1134 'file' => $new_file,
1135 'url' => $uploads['url'] . "/$file_name",
1136 'type' => $wp_filetype['type'],
1137 'error' => false,
1138 );
1139
1140 // keep track of the old and new urls so we can substitute them later
1141 $this->url_remap[ $url ] = $upload['url'];
1142 $this->url_remap[ $post['guid'] ] = $upload['url']; // r13735, really needed?
1143 // keep track of the destination if the remote url is redirected somewhere else
1144 if ( isset( $headers['x-final-location'] ) && $headers['x-final-location'] !== $url ) {
1145 $this->url_remap[ $headers['x-final-location'] ] = $upload['url'];
1146 }
1147
1148 return $upload;
1149 }
1150
1151 /**
1152 * Attempt to associate posts and menu items with previously missing parents
1153 *
1154 * An imported post's parent may not have been imported when it was first created
1155 * so try again. Similarly, for child menu items and menu items which were missing
1156 * the object (e.g. post) they represent in the menu
1157 */
1158 public function backfill_parents() {
1159 global $wpdb;
1160
1161 // find parents for post orphans
1162 foreach ( $this->post_orphans as $child_id => $parent_id ) {
1163 $local_child_id = false;
1164 $local_parent_id = false;
1165 if ( isset( $this->processed_posts[ $child_id ] ) ) {
1166 $local_child_id = $this->processed_posts[ $child_id ];
1167 }
1168 if ( isset( $this->processed_posts[ $parent_id ] ) ) {
1169 $local_parent_id = $this->processed_posts[ $parent_id ];
1170 }
1171
1172 if ( $local_child_id && $local_parent_id ) {
1173 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
1174 $wpdb->update( $wpdb->posts, array( 'post_parent' => $local_parent_id ), array( 'ID' => $local_child_id ), '%d', '%d' );
1175 clean_post_cache( $local_child_id );
1176 }
1177 }
1178
1179 // all other posts/terms are imported, retry menu items with missing associated object
1180 $missing_menu_items = $this->missing_menu_items;
1181 foreach ( $missing_menu_items as $item ) {
1182 $this->process_menu_item( $item );
1183 }
1184
1185 // find parents for menu item orphans
1186 foreach ( $this->menu_item_orphans as $child_id => $parent_id ) {
1187 $local_child_id = 0;
1188 $local_parent_id = 0;
1189 if ( isset( $this->processed_menu_items[ $child_id ] ) ) {
1190 $local_child_id = $this->processed_menu_items[ $child_id ];
1191 }
1192 if ( isset( $this->processed_menu_items[ $parent_id ] ) ) {
1193 $local_parent_id = $this->processed_menu_items[ $parent_id ];
1194 }
1195
1196 if ( $local_child_id && $local_parent_id ) {
1197 update_post_meta( $local_child_id, '_menu_item_menu_item_parent', (int) $local_parent_id );
1198 }
1199 }
1200 }
1201
1202 /**
1203 * Use stored mapping information to update old attachment URLs
1204 */
1205 public function backfill_attachment_urls() {
1206 global $wpdb;
1207 // make sure we do the longest urls first, in case one is a substring of another
1208 uksort( $this->url_remap, array( &$this, 'cmpr_strlen' ) );
1209
1210 foreach ( $this->url_remap as $from_url => $to_url ) {
1211 // remap urls in post_content
1212 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
1213 $wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->posts} SET post_content = REPLACE(post_content, %s, %s)", $from_url, $to_url ) );
1214 // remap enclosure urls
1215 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
1216 $wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->postmeta} SET meta_value = REPLACE(meta_value, %s, %s) WHERE meta_key='enclosure'", $from_url, $to_url ) );
1217 }
1218
1219 if ( ! empty( $this->url_remap ) ) {
1220 Helper::emit_sse_message( [
1221 'action' => 'log',
1222 'level' => 'info',
1223 'message' => __( 'Old attachment URLs have been updated.', 'ablocks' ),
1224 ] );
1225 }
1226 }
1227
1228 /**
1229 * Update _thumbnail_id meta to new, imported attachment IDs
1230 */
1231 public function remap_featured_images() {
1232 // cycle through posts that have a featured image
1233 foreach ( $this->featured_images as $post_id => $value ) {
1234 if ( isset( $this->processed_posts[ $value ] ) ) {
1235 $new_id = $this->processed_posts[ $value ];
1236 // only update if there's a difference
1237 if ( $new_id !== $value ) {
1238 update_post_meta( $post_id, '_thumbnail_id', $new_id );
1239 }
1240 }
1241 }
1242 Helper::emit_sse_message( [
1243 'action' => 'log',
1244 'level' => 'info',
1245 'message' => __( 'Imported posts `_thumbnail_id` updated.', 'ablocks' ),
1246 ] );
1247 }
1248
1249 public function save_global_settings() {
1250 if ( ! empty( $this->ablocks_options ) ) {
1251 $settings = $this->ablocks_options;
1252 if ( ! empty( $settings['frontend_dashboard_sub_pages'] ) ) {
1253 update_option( 'ablocks_frontend_dashboard_sub_pages', wp_json_encode( $settings['frontend_dashboard_sub_pages'] ) );
1254 unset( $settings['frontend_dashboard_sub_pages'] );
1255 }
1256
1257 Base::save_settings( $settings );
1258 }
1259 }
1260
1261 /**
1262 * Parse a WXR file
1263 *
1264 * @param string $file Path to WXR file for parsing.
1265 *
1266 * @return array|WP_Error Information gathered from the WXR file.
1267 */
1268 public function parse( string $file ) {
1269 $parser = new WXR_Parser();
1270
1271 return $parser->parse( $file );
1272 }
1273
1274 /**
1275 * Decide if the given meta key maps to information we will want to import
1276 *
1277 * @param string $key The meta key to check.
1278 *
1279 * @return string|bool The key if we do want to import, false if not
1280 */
1281 public function is_valid_meta_key( $key ) {
1282 // skip attachment metadata since we'll regenerate it from scratch
1283 // skip _edit_lock as not relevant for import
1284 if ( in_array( $key, array( '_wp_attached_file', '_wp_attachment_metadata', '_edit_lock' ), true ) ) {
1285 return false;
1286 }
1287
1288 return $key;
1289 }
1290
1291 /**
1292 * Decide what the maximum file size for downloaded attachments is.
1293 * Default is 0 (unlimited), can be filtered via import_attachment_size_limit
1294 *
1295 * @return int Maximum attachment file size to import
1296 */
1297 public function max_attachment_size(): int {
1298 return apply_filters( 'import_attachment_size_limit', 0 );
1299 }
1300
1301 /**
1302 * Added to http_request_timeout filter to force timeout at 60 seconds during import
1303 *
1304 * @param mixed $val Value.
1305 *
1306 * @return int 60
1307 */
1308 public function bump_request_timeout( $val ): int {
1309 return 60;
1310 }
1311
1312 public function cmpr_strlen( $a, $b ): int {
1313 // return the difference in length between two strings.
1314 return strlen( $b ) - strlen( $a );
1315 }
1316
1317 /**
1318 * Parses filename from a Content-Disposition header value.
1319 *
1320 * As per RFC6266:
1321 *
1322 * content-disposition = "Content-Disposition" ":"
1323 * disposition-type *( ";" disposition-parm )
1324 *
1325 * disposition-type = "inline" | "attachment" | disp-ext-type
1326 * ; case-insensitive
1327 * disp-ext-type = token
1328 *
1329 * disposition-parm = filename-parm | disp-ext-parm
1330 *
1331 * filename-parm = "filename" "=" value
1332 * | "filename*" "=" ext-value
1333 *
1334 * disp-ext-parm = token "=" value
1335 * | ext-token "=" ext-value
1336 * ext-token = <the characters in token, followed by "*">
1337 *
1338 * @param string[] $disposition_header List of Content-Disposition header values.
1339 *
1340 * @return string|null Filename if available, or null if not found.
1341 * @link http://tools.ietf.org/html/rfc2388
1342 * @link http://tools.ietf.org/html/rfc6266
1343 *
1344 * @since 0.7.0
1345 *
1346 * @see WP_REST_Attachments_Controller::get_filename_from_disposition()
1347 */
1348 protected static function get_filename_from_disposition( $disposition_header ) {
1349 // Get the filename.
1350 $filename = null;
1351
1352 foreach ( $disposition_header as $value ) {
1353 $value = trim( $value );
1354
1355 if ( strpos( $value, ';' ) === false ) {
1356 continue;
1357 }
1358
1359 list( $type, $attr_parts ) = explode( ';', $value, 2 );
1360
1361 $attr_parts = explode( ';', $attr_parts );
1362 $attributes = array();
1363
1364 foreach ( $attr_parts as $part ) {
1365 if ( strpos( $part, '=' ) === false ) {
1366 continue;
1367 }
1368
1369 list( $key, $value ) = explode( '=', $part, 2 );
1370
1371 $attributes[ trim( $key ) ] = trim( $value );
1372 }
1373
1374 if ( empty( $attributes['filename'] ) ) {
1375 continue;
1376 }
1377
1378 $filename = trim( $attributes['filename'] );
1379
1380 // Unquote quoted filename, but after trimming.
1381 if ( substr( $filename, 0, 1 ) === '"' && substr( $filename, - 1, 1 ) === '"' ) {
1382 $filename = substr( $filename, 1, - 1 );
1383 }
1384 }//end foreach
1385
1386 return $filename;
1387 }
1388
1389 /**
1390 * Retrieves file extension by mime type.
1391 *
1392 * @param string $mime_type Mime type to search extension for.
1393 *
1394 * @return string|null File extension if available, or null if not found.
1395 * @since 0.7.0
1396 */
1397 protected static function get_file_extension_by_mime_type( $mime_type ) {
1398 static $map = null;
1399
1400 if ( is_array( $map ) ) {
1401 return isset( $map[ $mime_type ] ) ? $map[ $mime_type ] : null;
1402 }
1403
1404 $mime_types = wp_get_mime_types();
1405 $map = array_flip( $mime_types );
1406
1407 // Some types have multiple extensions, use only the first one.
1408 foreach ( $map as $type => $extensions ) {
1409 $map[ $type ] = strtok( $extensions, '|' );
1410 }
1411
1412 return isset( $map[ $mime_type ] ) ? $map[ $mime_type ] : null;
1413 }
1414 }
1415