PluginProbe
Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! / 3.2.0
Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! v3.2.0
3.8.0 3.7.5 3.7.4 3.7.3 3.7.2 1-final 3.7.1 3.7.0 3.6.8 3.6.7 3.6.6 3.6.5 3.6.4 3.6.3 3.6.2 3.6.1 3.0.3 3.0.4 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.1 All 112 releases
← All changes | includes/Core/Importer/WPImport.php +175 -301 3.7.0 → 3.2.0 View file →
@@ -2,9 +2,8 @@
2 2
3 3 namespace Templately\Core\Importer;
4 4
5 5 use Templately\Core\Importer\Parsers\WXR_Parser;
6 -use Templately\Core\Importer\Runners\Loop;
7 6 use Templately\Core\Importer\Utils\Utils;
8 7 use Templately\Utils\Helper;
9 8 use WP_Error;
10 9 use WP_Importer;
@@ -48,11 +47,8 @@
48 47 }
49 48 }
50 49
51 50 class WPImport extends WP_Importer {
52 - use LogHelper;
53 - use Loop;
54 -
55 51 const DEFAULT_BUMP_REQUEST_TIMEOUT = 60;
56 52 const DEFAULT_ALLOW_CREATE_USERS = true;
57 53 const DEFAULT_IMPORT_ATTACHMENT_SIZE_LIMIT = 0; // 0 = unlimited.
58 54
@@ -87,18 +83,8 @@
87 83 * WXR attachment ID
88 84 */
89 85 private $id;
90 86
91 - /**
92 - * @var mixed
93 - */
94 - private $json;
95 -
96 - /**
97 - * @var string
98 - */
99 - private $session_id;
100 -
101 87 // Information to import from WXR file.
102 88 private $version;
103 89 private $authors = [];
104 90 public $posts = [];
@@ -119,10 +105,8 @@
119 105 private $menu_item_orphans = [];
120 106 private $mapped_terms_slug = [];
121 107
122 108 private $fetch_attachments = false;
123 - private $attachment_timeout = 300;
124 - private $attachment_retry_count = 3;
125 109 private $featured_images = [];
126 110
127 111 /**
128 112 * @var array[] [meta_key => meta_value] Meta value that should be set for every imported post.
@@ -135,25 +119,8 @@
135 119 private $terms_meta = [];
136 120
137 121 public static $_replace_image_ids = [];
138 122
139 -
140 - public $backup_attributes = [
141 - 'output',
142 - 'url_remap',
143 - '_replace_image_ids',
144 - 'menu_item_orphans',
145 - 'processed_menu_items',
146 - 'post_orphans',
147 - 'processed_posts',
148 - 'featured_images',
149 - 'mapped_terms_slug',
150 - 'processed_terms',
151 - 'processed_authors',
152 - 'author_mapping',
153 - ];
154 -
155 -
156 123 /**
157 124 * Parses filename from a Content-Disposition header value.
158 125 *
159 126 * As per RFC6266:
@@ -329,10 +296,9 @@
329 296
330 297 wp_defer_term_counting( true );
331 298 wp_defer_comment_counting( true );
332 299
333 - do_action( 'import_start' );
334 - do_action( 'templately_import_start', $this );
300 + do_action( 'import_start', $this );
335 301
336 302 return true;
337 303 }
338 304
@@ -386,8 +352,30 @@
386 352 }
387 353 }
388 354 }
389 355
356 + private function retrieve_attributes($attributes){
357 + $params = $this->origin->get_request_params();
358 + $attr_values = $params["imported_data"][$this->import_data_key] ?? [];
359 + foreach ($attributes as $attribute) {
360 + if(isset($attr_values[$attribute])){
361 + $this->$attribute = $attr_values[$attribute];
362 + }
363 + }
364 + }
365 +
366 + private function backup_attributes($attributes){
367 + $params = $this->origin->get_request_params();
368 + $attr_values = $params["imported_data"][$this->import_data_key] ?? [];
369 +
370 + foreach ($attributes as $attribute) {
371 + if(isset($this->$attribute)){
372 + $attr_values[$attribute] = $this->$attribute;
373 + }
374 + }
375 + $this->origin->update_progress( null, [$this->import_data_key => $attr_values]);
376 + }
377 +
390 378 /**
391 379 * Map old author logins to local user IDs based on decisions made
392 380 * in import options form. Can map to an existing user, create a new user
393 381 * or falls back to the current user in case of error with either of the previous
@@ -396,11 +384,17 @@
396 384 if ( ! isset( $this->args['imported_authors'] ) ) {
397 385 return;
398 386 }
399 387
388 + $attributes = [
389 + 'processed_authors',
390 + 'author_mapping',
391 + 'output',
392 + ];
400 393
401 - $processed_templates = $this->get_loop_result([], $this->import_data_key);
394 + $processed_templates = $this->origin->get_progress([], $this->import_data_key);
402 395 if (!empty($processed_templates)) {
396 + $this->retrieve_attributes($attributes);
403 397 return;
404 398 }
405 399
406 400 $create_users = apply_filters( 'import_allow_create_users', self::DEFAULT_ALLOW_CREATE_USERS );
@@ -459,9 +453,10 @@
459 453 $this->author_mapping[ $santized_old_login ] = (int) get_current_user_id();
460 454 }
461 455 }
462 456
463 - $this->set_loop_result( true, $this->import_data_key );
457 + $this->backup_attributes($attributes);
458 + $this->origin->update_progress( true, null, $this->import_data_key );
464 459 }
465 460
466 461 /**
467 462 * Create new terms based on import information
@@ -470,16 +465,23 @@
470 465 *
471 466 * @return array|array[] the ids of succeed/failed imported terms.
472 467 */
473 468 private function process_terms(): array {
474 - $result = [
469 + $params = $this->origin->get_request_params();
470 + $result = $params["imported_data"]["wp_import_terms_" . $this->import_data_key] ?? [
475 471 'succeed' => [],
476 472 'failed' => [],
477 473 ];
478 474
479 - $processed_templates = $this->get_loop_result([], "wp_import_terms_" . $this->import_data_key);
475 + $attributes = [
476 + 'terms',
477 + 'output',
478 + 'mapped_terms_slug',
479 + 'processed_terms',
480 + ];
481 + $processed_templates = $this->origin->get_progress([], $this->import_data_key);
480 482 if (!empty($processed_templates)) {
481 - $result = $this->get_loop_result([], "wp_import_terms_" . $this->import_data_key);
483 + $this->retrieve_attributes($attributes);
482 484 return $result;
483 485 }
484 486
485 487 $this->terms = apply_filters( 'wp_import_terms', $this->terms );
@@ -562,10 +564,11 @@
562 564 }
563 565
564 566 unset( $this->terms );
565 567
568 + $this->backup_attributes($attributes);
566 569 // Add the template to the processed templates and update the session data
567 - $this->set_loop_result( $result, "wp_import_terms_" . $this->import_data_key);
570 + $this->origin->update_progress( true, [ "wp_import_terms_" . $this->import_data_key => $result ]);
568 571 return $result;
569 572 }
570 573
571 574 /**
@@ -635,44 +638,53 @@
635 638 *
636 639 * @return array the ids of succeed/failed imported posts.
637 640 */
638 641 private function process_posts(): array {
639 - $backup_key = "wp_import_post_" . $this->import_data_key;
642 + $backup_key = "wp_import_" . $this->import_data_key;
643 + $params = $this->origin->get_request_params();
644 + $result = $params["imported_data"][$backup_key] ?? [
645 + 'succeed' => [],
646 + 'failed' => [],
647 + ];
640 648
649 + $attributes = [
650 + 'output',
651 + 'menu_item_orphans',
652 + 'processed_menu_items',
653 + 'post_orphans',
654 + 'processed_posts',
655 + 'featured_images',
656 + ];
657 + $this->retrieve_attributes($attributes);
658 +
641 659 $this->posts = apply_filters( 'wp_import_posts', $this->posts );
642 660
643 - $results = $this->loop( $this->posts, function($key, $post, $result ) {
661 + $processed_templates = $this->origin->get_progress([], $this->import_data_key);
644 662
645 - $result = !empty($result) ? $result : [
646 - 'succeed' => [],
647 - 'failed' => [],
648 - ];
663 + foreach ( $this->posts as $key => $post ) {
664 + if (in_array($key, $processed_templates)) {
665 + continue;
666 + }
649 667
650 - $original_post_id = $post['post_id'];
651 - $post = apply_filters( 'wp_import_post_data_raw', $post, $this );
668 + // Add the template to the processed templates and update the session data
669 + $processed_templates[] = $key;
670 + $this->origin->update_progress( $processed_templates, null, $this->import_data_key);
652 671
653 - if(empty($post)){
654 - return $result;
655 - }
672 + $post = apply_filters( 'wp_import_post_data_raw', $post );
656 673
657 - if(!is_array($post) && is_numeric($post)){
658 - $result['succeed'][ $original_post_id ] = $post;
659 - return $result;
660 - }
661 -
662 674 if ( ! post_type_exists( $post['post_type'] ) ) {
663 675 /* translators: 1: Post title, 2: Post type. */
664 676 $this->output['errors'][] = sprintf( esc_html__( 'Failed to import %1$s: Invalid post type %2$s', 'elementor' ), $post['post_title'], $post['post_type'] );
665 677 do_action( 'wp_import_post_exists', $post );
666 - return $result;
678 + continue;
667 679 }
668 680
669 681 if ( isset( $this->processed_posts[ $post['post_id'] ] ) && ! empty( $post['post_id'] ) ) {
670 - return $result;
682 + continue;
671 683 }
672 684
673 685 if ( 'auto-draft' === $post['status'] ) {
674 - return $result;
686 + continue;
675 687 }
676 688
677 689 if(!empty($post['post_content']) && !empty($post['post_id'])){
678 690 $post['post_content'] = Utils::import_and_replace_attachments($post['post_content'], $post['post_id']);
@@ -679,15 +691,15 @@
679 691 }
680 692
681 693 if ( 'nav_menu_item' === $post['post_type'] ) {
682 694 $result['succeed'] += $this->process_menu_item( $post );
683 - return $result;
695 + continue;
684 696 }
685 697
686 698 if ( 'wp_navigation' === $post['post_type'] ) {
687 699 $processed = $this->process_navigation( $post );
688 700 if ( ! $processed ) {
689 - return $result;
701 + continue;
690 702 }
691 703 }
692 704
693 705 $post_type_object = get_post_type_object( $post['post_type'] );
@@ -727,12 +739,9 @@
727 739 'post_type' => $post['post_type'],
728 740 'post_password' => $post['post_password'],
729 741 ];
730 742
731 - if(isset($post['original_attachment_url'])){
732 - $postdata['original_attachment_url'] = $post['original_attachment_url'];
733 - }
734 -
743 + $original_post_id = $post['post_id'];
735 744 $postdata = apply_filters( 'wp_import_post_data_processed', $postdata, $post );
736 745
737 746 $postdata = wp_slash( $postdata );
738 747
@@ -737,9 +746,9 @@
737 746 $postdata = wp_slash( $postdata );
738 747
739 748 if ( 'attachment' === $postdata['post_type'] ) {
740 749 $remote_url = ! empty( $post['attachment_url'] ) ? $post['attachment_url'] : $post['guid'];
741 - $attachment_sizes = [];
750 +
742 751 // try to use _wp_attached file for upload folder placement to ensure the same location as the export site
743 752 // e.g. location is 2003/05/image.jpg but the attachment post_date is 2010/09, see media_handle_upload()
744 753 $postdata['upload_date'] = $post['post_date'];
745 754 if ( isset( $post['postmeta'] ) ) {
@@ -747,19 +756,14 @@
747 756 if ( '_wp_attached_file' === $meta['key'] ) {
748 757 if ( preg_match( '%^[0-9]{4}/[0-9]{2}%', $meta['value'], $matches ) ) {
749 758 $postdata['upload_date'] = $matches[0];
750 759 }
751 - // break;
760 + break;
752 761 }
753 - else if ( '_wp_attachment_metadata' === $meta['key'] ) {
754 - $attachment_metadata = maybe_unserialize( $meta['value'] );
755 - $attachment_sizes = $attachment_metadata['sizes'] ?? [];
756 - // break;
757 - }
758 762 }
759 763 }
760 764
761 - $post_id = $this->process_attachment( $postdata, $remote_url, $attachment_sizes, $original_post_id );
765 + $post_id = $this->process_attachment( $postdata, $remote_url );
762 766 $comment_post_id = $post_id;
763 767 } else {
764 768 $post_id = wp_insert_post( $postdata, true );
765 769
@@ -780,13 +784,22 @@
780 784 $result['failed'][] = $original_post_id;
781 785
782 786 $this->output['errors'][] = $error;
783 787
784 - if ( 'attachment' === $postdata['post_type'] ) {
785 - do_action( 'templately_import.process_post', $post, $result, $this );
788 + // Add the template to the processed templates and update the session data
789 + $this->backup_attributes($attributes);
790 + $this->origin->update_progress( null, [ $backup_key => $result ], $this->import_data_key);
791 +
792 + // If it's not the last item, send the SSE message and exit
793 + if( end($this->posts) !== $post) {
794 + $this->origin->sse_message( [
795 + 'type' => 'continue',
796 + 'action' => 'continue',
797 + 'results' => __METHOD__ . '::' . __LINE__,
798 + ] );
799 + exit;
786 800 }
787 -
788 - return $result;
801 + continue;
789 802 }
790 803
791 804 $result['succeed'][ $original_post_id ] = $post_id;
792 805
@@ -941,14 +954,26 @@
941 954 }
942 955
943 956 do_action( 'templately_import.process_post', $post, $result, $this );
944 957
945 - return $result;
946 - }, $backup_key); //, true
958 + // Add the template to the processed templates and update the session data
959 + $this->backup_attributes($attributes);
960 + $this->origin->update_progress( null, [ $backup_key => $result ], $this->import_data_key);
947 961
962 + // If it's not the last item, send the SSE message and exit
963 + if( end($this->posts) !== $post) {
964 + $this->origin->sse_message( [
965 + 'type' => 'continue',
966 + 'action' => 'continue',
967 + 'results' => __METHOD__ . '::' . __LINE__,
968 + ] );
969 + exit;
970 + }
971 + }
972 +
948 973 unset( $this->posts );
949 974
950 - return $results;
975 + return $result;
951 976 }
952 977
953 978 /**
954 979 * Attempt to create a new menu item from import data
@@ -1129,9 +1154,9 @@
1129 1154 * @param string $url URL to fetch attachment from
1130 1155 *
1131 1156 * @return int|WP_Error Post ID on success, WP_Error otherwise
1132 1157 */
1133 - public function process_attachment( $post, $url, $sizes = [], $original_post_id = null ) {
1158 + public function process_attachment( $post, $url, $sizes = [] ) {
1134 1159 if ( ! $this->fetch_attachments ) {
1135 1160 return new WP_Error( 'attachment_processing_error', esc_html__( 'Fetching attachments is not enabled', 'elementor' ) );
1136 1161 }
1137 1162 if ( ! function_exists( 'wp_crop_image' ) ) {
@@ -1142,40 +1167,10 @@
1142 1167 $url = rtrim( $this->base_url, '/' ) . $url;
1143 1168 }
1144 1169
1145 1170 if($saved_image = $this->get_saved_image($url)){
1146 - // $this->url_remap[ $url ] = wp_get_attachment_url( $saved_image );
1147 - // $this->url_remap[ $this->remove_extension($url) ] = $this->remove_extension(wp_get_attachment_url( $saved_image ));
1148 - $upload_url = set_url_scheme( wp_get_attachment_url( $saved_image ) );
1149 - $this->set_url_map($url, $upload_url, true);
1150 -
1151 - // AI image replacement: the imported content still carries the ORIGINAL
1152 - // demo URL, so it must be remapped onto the replacement image here too.
1153 - // The fresh-upload branch below does this, but when the replacement
1154 - // image was already imported (a re-import, or the same stock photo
1155 - // reused) we land here instead — without the map the demo URL survives
1156 - // finalize and the pack's original images are silently kept.
1157 - if(!empty($post['original_attachment_url'])){
1158 - $this->set_url_map($post['original_attachment_url'], $upload_url, true);
1159 -
1160 - $original_hash = sha1( $post['original_attachment_url'] );
1161 -
1162 - // A stale hash from a previous import can still point the demo URL at
1163 - // the old, unreplaced attachment — re-point it at the replacement.
1164 - if( (int) $this->get_saved_image($post['original_attachment_url']) !== (int) $saved_image ){
1165 - $hash_meta_id = add_post_meta( $saved_image, '_elementor_source_image_hash', $original_hash );
1166 - add_post_meta( $saved_image, '_templately_image_hash_meta_id', $hash_meta_id );
1167 - }
1168 -
1169 - self::$_replace_image_ids[ $original_hash ] = (int) $saved_image;
1170 - }
1171 -
1172 - $full_size_path = get_attached_file($saved_image);
1173 - $metadata = wp_get_attachment_metadata($saved_image);
1174 - $updated_metadata = $this->import_sizes($sizes, $metadata, $full_size_path, $saved_image);
1175 - if ($updated_metadata !== false) {
1176 - wp_update_attachment_metadata( $saved_image, $updated_metadata );
1177 - }
1171 + $this->url_remap[ $url ] = wp_get_attachment_url( $saved_image );
1172 + $this->url_remap[ $this->remove_extension($url) ] = $this->remove_extension(wp_get_attachment_url( $saved_image ));
1178 1173 return $saved_image;
1179 1174 }
1180 1175
1181 1176 // Check if the URL is from the wp-includes/images directory
@@ -1197,24 +1192,10 @@
1197 1192 // Move the file to the uploads dir.
1198 1193 $file_name = basename( parse_url( $url, PHP_URL_PATH ) );
1199 1194 $file_name = wp_unique_filename( $upload_dir['path'], $file_name );
1200 1195 $dest_file = $upload_dir['path'] . "/$file_name";
1201 - $start = microtime(true);
1196 + $upload = $this->fetch_remote_file( $url, $dest_file, $upload_dir );
1202 1197
1203 - $upload = apply_filters( 'templately_import_copy_attachment', null, $original_post_id, $dest_file, $upload_dir );
1204 - if ( null === $upload ) {
1205 - $upload = $this->fetch_remote_file( $url, $dest_file, $upload_dir );
1206 - }
1207 -
1208 - // Normalize URL scheme to match the current site scheme (fixes HTTP URLs on HTTPS multisites).
1209 - if ( ! is_wp_error( $upload ) && ! empty( $upload['url'] ) ) {
1210 - $upload['url'] = set_url_scheme( $upload['url'] );
1211 - }
1212 -
1213 - $end = microtime(true);
1214 - $duration = $end - $start;
1215 - error_log('Duration: ' . $duration);
1216 -
1217 1198 if ( is_wp_error( $upload ) ) {
1218 1199 return $upload;
1219 1200 }
1220 1201
@@ -1224,19 +1205,14 @@
1224 1205 } else {
1225 1206 return new WP_Error( 'attachment_processing_error', esc_html__( 'Invalid file type', 'elementor' ) );
1226 1207 }
1227 1208
1228 - // $this->url_remap[ $post['guid'] ] = $upload['url']; // r13735, really needed?
1229 - // $this->set_url_map($post['guid'], $upload['url']);
1209 + $this->url_remap[ $post['guid'] ] = $upload['url']; // r13735, really needed?
1230 1210 $post['guid'] = $upload['url'];
1231 1211
1232 1212 // As per wp-admin/includes/upload.php.
1233 1213 $post_id = wp_insert_attachment( $post, $upload['file'] );
1234 1214
1235 - if(is_wp_error($post_id)){
1236 - return $post_id;
1237 - }
1238 -
1239 1215 $this->update_post_meta( $post_id );
1240 1216
1241 1217 // Generate attachment metadata
1242 1218 $metadata = wp_generate_attachment_metadata( $post_id, $upload['file'] );
@@ -1243,46 +1219,21 @@
1243 1219
1244 1220 // error_log('Metadata: ' . print_r($metadata, true));
1245 1221
1246 1222 // For gutenberg pages
1247 - $updated_metadata = $this->import_sizes($sizes, $metadata, $upload['file'], $post_id);
1223 + $metadata = $this->import_sizes($sizes, $metadata, $upload, $upload_dir, $post_id);
1248 1224
1249 1225 // error_log('Metadata: ' . print_r($metadata, true));
1250 - if ($updated_metadata !== false) {
1251 - wp_update_attachment_metadata( $post_id, $updated_metadata );
1252 - } else {
1253 - wp_update_attachment_metadata( $post_id, $metadata );
1254 - }
1226 + wp_update_attachment_metadata( $post_id, $metadata );
1255 1227
1256 1228 // @todo: add missing image sizes
1257 - if(defined('TEMPLATELY_DEV') && TEMPLATELY_DEV){
1258 - update_post_meta( $post_id, '_templately_original_id', $original_post_id );
1259 - update_post_meta( $post_id, '_templately_original_url', $url );
1260 1229
1261 - if(!empty($post['original_attachment_url'])){
1262 - update_post_meta( $post_id, '_templately_demo_url', $post['original_attachment_url'] );
1263 - }
1264 - }
1265 -
1266 1230 update_post_meta( $post_id, '_elementor_source_image_hash', sha1( $url ) );
1267 1231 self::$_replace_image_ids[ sha1( $url ) ] = $post_id;
1268 1232
1269 - // add a second hash for original demo url
1270 - // if user is replacing image.
1271 - // so we can also match original demo url to new image
1272 - if(!empty($post['original_attachment_url'])){
1273 - $hash_meta_id = add_post_meta( $post_id, '_elementor_source_image_hash', sha1( $post['original_attachment_url'] ) );
1274 - add_post_meta( $post_id, '_templately_image_hash_meta_id', $hash_meta_id );
1275 - self::$_replace_image_ids[ sha1( $post['original_attachment_url'] ) ] = $post_id;
1276 - }
1277 -
1278 1233 // Remap resized image URLs, works by stripping the extension and remapping the URL stub.
1279 1234 if ( preg_match( '!^image/!', $info['type'] ) ) {
1280 - // $this->url_remap[ $this->remove_extension($url) ] = $this->remove_extension($upload['url']);
1281 - $this->set_url_map($url, $upload['url'], true);
1282 - if(!empty($post['original_attachment_url'])){
1283 - $this->set_url_map($post['original_attachment_url'], $upload['url'], true);
1284 - }
1235 + $this->url_remap[ $this->remove_extension($url) ] = $this->remove_extension($upload['url']);
1285 1236 }
1286 1237
1287 1238 return $post_id;
1288 1239 }
@@ -1293,17 +1244,8 @@
1293 1244
1294 1245 return $parts['dirname'] . '/' . $name;
1295 1246 }
1296 1247
1297 - public function set_url_map($original_url, $new_url, $remove_extension = false){
1298 - $this->url_remap[ $original_url ] = $new_url;
1299 - if($remove_extension){
1300 - $original_url = $this->remove_extension($original_url);
1301 - $new_url = $this->remove_extension($new_url);
1302 - $this->url_remap[ $original_url ] = $new_url;
1303 - }
1304 - }
1305 -
1306 1248 /**
1307 1249 * Attempt to download a remote file attachment
1308 1250 *
1309 1251 * @param string $url URL of item to fetch
@@ -1326,13 +1268,13 @@
1326 1268 }
1327 1269
1328 1270 // Fetch the remote URL and write it to the placeholder file.
1329 1271 $attempt = 0;
1330 - $retry_count = $this->attachment_retry_count;
1272 + $retry_count = 3;
1331 1273 $remote_response = null;
1332 1274 do {
1333 1275 $remote_response = wp_safe_remote_get( $url, [
1334 - 'timeout' => $this->attachment_timeout,
1276 + 'timeout' => 300,
1335 1277 'stream' => true,
1336 1278 'filename' => $tmp_file_name,
1337 1279 'headers' => [
1338 1280 'Accept-Encoding' => 'identity',
@@ -1440,12 +1382,12 @@
1440 1382 'error' => false,
1441 1383 ];
1442 1384
1443 1385 // Keep track of the old and new urls so we can substitute them later.
1444 - $this->set_url_map($url, $upload['url']);
1386 + $this->url_remap[ $url ] = $upload['url'];
1445 1387 // Keep track of the destination if the remote url is redirected somewhere else.
1446 1388 if ( isset( $headers['x-final-location'] ) && $headers['x-final-location'] !== $url ) {
1447 - $this->set_url_map($headers['x-final-location'], $upload['url']);
1389 + $this->url_remap[ $headers['x-final-location'] ] = $upload['url'];
1448 1390 }
1449 1391
1450 1392 return $upload;
1451 1393 }
@@ -1459,9 +1401,9 @@
1459 1401 * @access private
1460 1402 *
1461 1403 * @param string $url The image URL.
1462 1404 *
1463 - * @return false|int New image ID or false.
1405 + * @return false|array New image ID or false.
1464 1406 */
1465 1407 private function get_saved_image( $url ) {
1466 1408 global $wpdb;
1467 1409
@@ -1470,26 +1412,13 @@
1470 1412 if ( isset( self::$_replace_image_ids[ $hash ] ) ) {
1471 1413 return self::$_replace_image_ids[ $hash ];
1472 1414 }
1473 1415
1474 - // Newest row wins. A URL can be claimed by more than one attachment: an
1475 - // AI/customizer image replacement registers the ORIGINAL demo URL against
1476 - // the REPLACEMENT attachment (see process_attachment), while a previous
1477 - // plain import of the same pack already claimed that URL for the original
1478 - // image — and that older row is never cleaned up (clear_old_el_cache only
1479 - // tracks replacement hashes). Without the ordering the stale original wins
1480 - // and the replacement is silently ignored on every re-import.
1481 - //
1482 - // The static cache above short-circuits this, but the import is split
1483 - // across many requests (each SSE `continue` is a fresh PHP process), so
1484 - // the DB is the real source of truth for anything set in an earlier step.
1485 1416 $post_id = $wpdb->get_var(
1486 1417 $wpdb->prepare(
1487 1418 'SELECT `post_id` FROM `' . $wpdb->postmeta . '`
1488 1419 WHERE `meta_key` = \'_elementor_source_image_hash\'
1489 1420 AND `meta_value` = %s
1490 - ORDER BY `meta_id` DESC
1491 - LIMIT 1
1492 1421 ;',
1493 1422 $hash
1494 1423 )
1495 1424 );
@@ -1495,9 +1424,9 @@
1495 1424 );
1496 1425
1497 1426 if ( $post_id ) {
1498 1427 self::$_replace_image_ids[ $hash ] = $post_id;
1499 - return (int) $post_id;
1428 + return $post_id;
1500 1429 }
1501 1430
1502 1431 return false;
1503 1432 }
@@ -1502,97 +1431,69 @@
1502 1431 return false;
1503 1432 }
1504 1433
1505 1434
1506 - public function import_sizes($sizes, $metadata, $full_size_file, $post_id) {
1507 - $metadata_modified = false;
1435 + public function extract_sizes($sizes) {
1436 + return array_reduce($sizes, function($carry, $url) {
1437 + if (preg_match('/-((\d+)x(\d+))\./', $url, $matches)) {
1438 + $carry[$matches[1]] = $url;
1439 + }
1440 + return $carry;
1441 + }, []);
1442 + }
1508 1443
1509 - if (!empty($sizes)) {
1510 - do_action('templately_import.finalize_gutenberg_attachment', $post_id);
1444 + public function size_exists_in_metadata($size_dimension, $metadata) {
1445 + foreach ($metadata['sizes'] as $size => $size_info) {
1446 + if (strpos($size_info['file'], $size_dimension) !== false) {
1447 + return true;
1448 + }
1449 + }
1450 + return false;
1451 + }
1511 1452
1512 - foreach ($sizes as $size_name => $size) {
1513 - $size_dimension = $size['width'] . 'x' . $size['height'];
1514 - $size_name = !is_string($size_name) ? $size_dimension : $size_name;
1515 - $unique_destination_file = $this->create_unique_destination_file($full_size_file, $size);
1516 - // check non cropped sizes. with dynamic height
1517 - if (!$this->size_exists_in_metadata(basename($unique_destination_file), $metadata)) {
1518 - if ($unique_destination_file) {
1519 - $missing_size = $this->generate_missing_size_from_full($full_size_file, $unique_destination_file, $size);
1520 - if ($missing_size && !is_wp_error($missing_size)) {
1521 - unset($missing_size['path']);
1522 - $metadata['sizes'][$size_name] = $missing_size;
1523 - $metadata_modified = true;
1524 - do_action('templately_import.finalize_gutenberg_attachment', $post_id, $size_dimension);
1525 - }
1526 - }
1527 - }
1528 - }
1529 - }
1453 + public function create_unique_destination_file($upload_file, $size_dimension) {
1454 + $destination_file = preg_replace('/(\.[^.]+)$/', '-' . $size_dimension . '$1', $upload_file);
1455 + $destination_dir = pathinfo($destination_file, PATHINFO_DIRNAME);
1456 + $destination_filename = pathinfo($destination_file, PATHINFO_BASENAME);
1457 + $unique_filename = wp_unique_filename($destination_dir, $destination_filename);
1458 + return $destination_dir . '/' . $unique_filename;
1459 + }
1530 1460
1531 - // Only return metadata if new sizes were actually added
1532 - return $metadata_modified ? $metadata : false;
1533 - }
1461 + public function create_size_array($destination_file, $size_dimension) {
1462 + list($width, $height) = explode('x', $size_dimension);
1463 + return array(
1464 + 'file' => basename($destination_file),
1465 + 'width' => $width,
1466 + 'height' => $height,
1467 + 'mime-type' => wp_check_filetype($destination_file)['type'],
1468 + 'filesize' => filesize($destination_file),
1469 + 'resized' => false,
1470 + );
1471 + }
1534 1472
1535 - public function generate_missing_size_from_full($full_size_file, $destination_file, $size) {
1536 - // Generate the missing size from the full-size image
1537 - $editor = wp_get_image_editor($full_size_file);
1538 - if (is_wp_error($editor)) {
1539 - return $editor;
1540 - }
1473 + public function import_sizes($sizes, $metadata, $upload, $upload_dir, $post_id) {
1474 + if (!empty($sizes)) {
1475 + do_action( 'templately_import.finalize_gutenberg_attachment', $post_id );
1541 1476
1542 - $resized = $editor->resize($size['width'], $size['height'], true);
1543 - if (is_wp_error($resized)) {
1544 - return $resized;
1545 - }
1477 + $size_dimensions = $this->extract_sizes($sizes);
1478 + $size_dimensions = array_filter($size_dimensions);
1546 1479
1547 - $saved = $editor->save($destination_file);
1548 - if (is_wp_error($saved)) {
1549 - return $saved;
1550 - }
1480 + foreach ($size_dimensions as $size_dimension => $__url) {
1481 + if (!$this->size_exists_in_metadata($size_dimension, $metadata)) {
1482 + $unique_destination_file = $this->create_unique_destination_file($upload['file'], $size_dimension);
1483 + $missing_size = $this->fetch_remote_file($__url, $unique_destination_file, $upload_dir);
1551 1484
1552 - return $saved;
1553 - }
1485 + if($missing_size && !is_wp_error($missing_size)) {
1486 + $metadata['sizes'][$size_dimension] = $this->create_size_array($unique_destination_file, $size_dimension);
1487 + do_action( 'templately_import.finalize_gutenberg_attachment', $post_id, $size_dimension );
1488 + }
1489 + }
1490 + }
1554 1491
1555 - public function size_exists_in_metadata($size_file, $metadata) {
1556 - if (isset($metadata['sizes']) && is_array($metadata['sizes'])) {
1557 - foreach ($metadata['sizes'] as $size_info) {
1558 - if ($size_info['file'] == $size_file) {
1559 - return true;
1560 - }
1561 - }
1562 - }
1563 - return false;
1564 - }
1492 + }
1493 + return $metadata;
1494 + }
1565 1495
1566 - public function create_unique_destination_file($full_size_file, $size) {
1567 - $pathinfo = pathinfo($full_size_file);
1568 - $directory = $pathinfo['dirname'];
1569 - $filename = $pathinfo['filename'];
1570 - $extension = $pathinfo['extension'];
1571 -
1572 - $destination_file = $directory . '/' . $filename . '-' . $size['width'] . 'x' . $size['height'] . '.' . $extension;
1573 -
1574 - // Skip if the file already exists
1575 - if (file_exists($destination_file)) {
1576 - // return false;
1577 - }
1578 -
1579 - return $destination_file;
1580 - }
1581 -
1582 - public function create_size_array($destination_file, $size_dimension) {
1583 - list($width, $height) = explode('x', $size_dimension);
1584 - return array(
1585 - 'file' => basename($destination_file),
1586 - 'width' => $width,
1587 - 'height' => $height,
1588 - 'mime-type' => wp_check_filetype($destination_file)['type'],
1589 - 'filesize' => filesize($destination_file),
1590 - 'resized' => false,
1591 - );
1592 - }
1593 -
1594 -
1595 1496 /**
1596 1497 * Attempt to associate posts and menu items with previously missing parents
1597 1498 *
1598 1499 * An imported post's parent may not have been imported when it was first created
@@ -1710,9 +1611,9 @@
1710 1611 */
1711 1612 private function is_valid_meta_key( $key ) {
1712 1613 // Skip attachment metadata since we'll regenerate it from scratch.
1713 1614 // Skip _edit_lock as not relevant for import
1714 - if ( in_array( $key, [ '_wp_attached_file', '_wp_attachment_metadata', '_edit_lock', '_elementor_source_image_hash', 'sm_cloud' ] ) ) {
1615 + if ( in_array( $key, [ '_wp_attached_file', '_wp_attachment_metadata', '_edit_lock' ] ) ) {
1715 1616 return false;
1716 1617 }
1717 1618
1718 1619 return $key;
@@ -1759,9 +1660,9 @@
1759 1660 * @param $post_id
1760 1661 *
1761 1662 * @return void
1762 1663 */
1763 - public function update_post_meta( $post_id ) {
1664 + private function update_post_meta( $post_id ) {
1764 1665 foreach ( $this->posts_meta as $meta_key => $meta_value ) {
1765 1666 update_post_meta( $post_id, $meta_key, $meta_value );
1766 1667 }
1767 1668 }
@@ -1772,23 +1673,8 @@
1772 1673 return $this->output;
1773 1674 }
1774 1675
1775 1676 /**
1776 - * Register Templately with Jetpack Sync's known importers.
1777 - *
1778 - * This filter callback adds Templately to Jetpack's list of known importers,
1779 - * ensuring that 'templately' is sent to WordPress.com instead of the full class name.
1780 - * This provides cleaner analytics data and better tracking.
1781 - *
1782 - * @param array $known_importers Array of known importers with class names as keys and friendly names as values.
1783 - * @return array Modified array of known importers.
1784 - */
1785 - public function register_jetpack_importer( $known_importers ) {
1786 - $known_importers[ __CLASS__ ] = 'templately';
1787 - return $known_importers;
1788 - }
1789 -
1790 - /**
1791 1677 * @param $file
1792 1678 * @param array $args
1793 1679 */
1794 1680 public function __construct( $file, array $args = [] ) {
@@ -1793,10 +1679,9 @@
1793 1679 */
1794 1680 public function __construct( $file, array $args = [] ) {
1795 1681 parent::__construct();
1796 1682
1797 - $this->args = $args;
1798 - $this->session_id = $args['session_id'];
1683 + $this->args = $args;
1799 1684
1800 1685 if ( ! empty( $args['json'] ) ) {
1801 1686 $this->json = $args['json'];
1802 1687 }
@@ -1813,16 +1698,8 @@
1813 1698 if ( ! empty( $this->args['fetch_attachments'] ) ) {
1814 1699 $this->fetch_attachments = true;
1815 1700 }
1816 1701
1817 - if ( isset( $this->args['attachment_timeout'] ) && is_numeric( $this->args['attachment_timeout'] ) ) {
1818 - $this->attachment_timeout = max( 1, (int) $this->args['attachment_timeout'] );
1819 - }
1820 -
1821 - if ( isset( $this->args['attachment_retries'] ) && is_numeric( $this->args['attachment_retries'] ) ) {
1822 - $this->attachment_retry_count = max( 1, (int) $this->args['attachment_retries'] + 1 );
1823 - }
1824 -
1825 1702 if ( isset( $this->args['posts'] ) && is_array( $this->args['posts'] ) ) {
1826 1703 $this->processed_posts = $this->args['posts'];
1827 1704 }
1828 1705
@@ -1840,9 +1717,6 @@
1840 1717
1841 1718 if ( ! empty( $this->args['terms_meta'] ) ) {
1842 1719 $this->terms_meta = $this->args['terms_meta'];
1843 1720 }
1844 -
1845 - // Register with Jetpack Sync for better analytics tracking.
1846 - add_filter( 'jetpack_sync_known_importers', [ $this, 'register_jetpack_importer' ] );
1847 1721 }
1848 1722 }