PluginProbe
Import WP – CSV & XML Import Export for WordPress / 2.15.0
Import WP – CSV & XML Import Export for WordPress v2.15.0
2.15.1 2.15.0 2.14.24 2.14.23 2.7.0 2.7.1 2.7.10 2.7.11 2.7.12 2.7.13 2.7.14 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 2.7.8 2.7.9 2.8.0 2.8.1 2.8.2 2.8.3 2.9.0 2.9.1 All 144 releases
← All changes | class/Common/Importer/ImporterManager.php +352 -64 2.7.14 → 2.15.0 View file →
@@ -4,16 +4,22 @@
4 4
5 5 use ImportWP\Common\Filesystem\Filesystem;
6 6 use ImportWP\Common\Importer\Config\Config;
7 7 use ImportWP\Common\Importer\File\CSVFile;
8 +use ImportWP\Common\Importer\File\JSONFile;
8 9 use ImportWP\Common\Importer\File\XMLFile;
10 +use ImportWP\Common\Importer\Mapper\AttachmentMapper;
11 +use ImportWP\Common\Importer\Mapper\CommentMapper;
9 12 use ImportWP\Common\Importer\Mapper\PostMapper;
10 13 use ImportWP\Common\Importer\Mapper\TermMapper;
11 14 use ImportWP\Common\Importer\Mapper\UserMapper;
12 15 use ImportWP\Common\Importer\Parser\CSVParser;
16 +use ImportWP\Common\Importer\Parser\JSONParser;
13 17 use ImportWP\Common\Importer\Parser\XMLParser;
14 18 use ImportWP\Common\Importer\Permission\Permission;
15 19 use ImportWP\Common\Importer\State\ImporterState;
20 +use ImportWP\Common\Importer\Template\AttachmentTemplate;
21 +use ImportWP\Common\Importer\Template\CommentTemplate;
16 22 use ImportWP\Common\Importer\Template\CustomPostTypeTemplate;
17 23 use ImportWP\Common\Importer\Template\PageTemplate;
18 24 use ImportWP\Common\Importer\Template\PostTemplate;
19 25 use ImportWP\Common\Importer\Template\Template;
@@ -50,11 +56,57 @@
50 56 {
51 57 $this->filesystem = $filesystem;
52 58 $this->template_manager = $template_manager;
53 59 $this->event_handler = $event_handler;
60 +
61 + add_action('admin_init', [$this, 'download_debug_log']);
54 62 }
55 63
56 64 /**
65 + * Stream an importer debug log through an authenticated admin request.
66 + *
67 + * Direct public URLs under uploads/importwp are blocked by .htaccess (CVE-2025-12894).
68 + *
69 + * @return void
70 + */
71 + public function download_debug_log()
72 + {
73 + if (!isset($_GET['page'], $_GET['import'], $_GET['download_debug']) || $_GET['page'] !== 'importwp') {
74 + return;
75 + }
76 +
77 + if (!is_user_logged_in() || !current_user_can('manage_options')) {
78 + wp_die(esc_html__('You do not have permission to download this file.', 'jc-importer'), '', array('response' => 403));
79 + }
80 +
81 + if (!isset($_GET['_wpnonce']) || !wp_verify_nonce(sanitize_key(wp_unslash($_GET['_wpnonce'])), 'iwp_debug_log_download')) {
82 + wp_die(esc_html__('Invalid download request.', 'jc-importer'), '', array('response' => 403));
83 + }
84 +
85 + $importer_id = intval($_GET['import']);
86 + $importer_data = $this->get_importer($importer_id);
87 + if (!$importer_data) {
88 + wp_die(esc_html__('Invalid download request.', 'jc-importer'), '', array('response' => 403));
89 + }
90 +
91 + if (!$this->is_debug()) {
92 + wp_die(esc_html__('Debug mode is not enabled.', 'jc-importer'), '', array('response' => 403));
93 + }
94 +
95 + $file_path = Logger::getLogFile($importer_id);
96 + if (!is_string($file_path) || !file_exists($file_path)) {
97 + wp_die(esc_html__('Debug log file not found.', 'jc-importer'), '', array('response' => 404));
98 + }
99 +
100 + nocache_headers();
101 + header('Content-Type: text/plain; charset=utf-8');
102 + header('Content-Disposition: attachment; filename="' . basename($file_path) . '"');
103 + header('Content-Length: ' . (string) filesize($file_path));
104 + readfile($file_path);
105 + exit;
106 + }
107 +
108 + /**
57 109 * Get Importers
58 110 *
59 111 * @return ImporterModel[]
60 112 */
@@ -142,8 +194,10 @@
142 194 if ('xml' === $parser) {
143 195 return $this->get_xml_file($importer, $config);
144 196 } elseif ('csv' === $parser) {
145 197 return $this->get_csv_file($importer, $config);
198 + } elseif ('json' === $parser) {
199 + return $this->get_json_file($importer, $config);
146 200 }
147 201
148 202 return false;
149 203 }
@@ -153,8 +207,9 @@
153 207 $importer = $this->get_importer($id);
154 208 $file = new CSVFile($importer->getFile(), $config);
155 209 $file->setDelimiter($importer->getFileSetting('delimiter'));
156 210 $file->setEnclosure($importer->getFileSetting('enclosure'));
211 + $file->setEscape($importer->getFileSetting('escape', "\\"));
157 212 return $file;
158 213 }
159 214
160 215 public function get_xml_file($id, $config)
@@ -164,8 +219,16 @@
164 219 $file->setRecordPath($importer->getFileSetting('base_path'));
165 220 return $file;
166 221 }
167 222
223 + public function get_json_file($id, $config)
224 + {
225 + $importer = $this->get_importer($id);
226 + $file = new JSONFile($importer->getFile(), $config);
227 + $file->setRecordPath($importer->getFileSetting('base_path'));
228 + return $file;
229 + }
230 +
168 231 public function preview_csv_file($id, $fields = [], $row = 0)
169 232 {
170 233 $importer = $this->get_importer($id);
171 234 $config = $this->get_config($importer, true);
@@ -189,8 +252,20 @@
189 252 $record = $parser->getRecord($row);
190 253 return $record->queryGroup(['fields' => $fields]);
191 254 }
192 255
256 + public function preview_json_file($id, $fields = [], $row = 0)
257 + {
258 + $importer = $this->get_importer($id);
259 + $config = $this->get_config($importer, true);
260 +
261 + $file = $this->get_json_file($importer, $config);
262 + $parser = new JSONParser($file);
263 +
264 + $record = $parser->getRecord($row);
265 + return $record->queryGroup(['fields' => $fields]);
266 + }
267 +
193 268 public function process_csv_file($id, $delimiter, $enclosure, $tmp = false)
194 269 {
195 270 $importer = $this->get_importer($id);
196 271 $config = $this->get_config($importer->getId(), $tmp);
@@ -199,36 +274,8 @@
199 274 $file->setDelimiter($delimiter);
200 275 $file->setEnclosure($enclosure);
201 276 $file->processing(true);
202 277
203 - if (empty($importer->getMap())) {
204 -
205 - // we are on first file import
206 - $headings = str_getcsv($file->getRecord(0), $file->getDelimiter(), $file->getEnclosure());
207 - $headings = array_map('trim', $headings);
208 -
209 - $template = $this->get_importer_template($id);
210 - $field_map = $template->generate_field_map($headings, $importer);
211 - $field_map = apply_filters('iwp/importer/generate_field_map', $field_map, $headings, $importer);
212 -
213 - $map = $field_map['map'];
214 - foreach ($map as $key => $value) {
215 - $importer->setMap($key, $value);
216 - }
217 -
218 - $enabled = $field_map['enabled'];
219 - foreach ($enabled as $enabled_field) {
220 - $importer->setEnabled($enabled_field);
221 - }
222 -
223 - // Disabled due to testing:
224 - // https://localdev/wp-admin/tools.php?page=importwp&edit=29&step=1
225 - //
226 - // a:9:{s:8:"template";s:19:"woocommerce-product";s:13:"template_type";s:0:"";s:4:"file";a:2:{s:2:"id";i:9;s:8:"settings";a:6:{s:9:"enclosure";s:1:""";s:9:"delimiter";s:1:",";s:13:"show_headings";b:1;s:5:"setup";b:0;s:5:"count";i:2;s:9:"processed";b:1;}}s:10:"datasource";a:2:{s:4:"type";s:5:"local";s:8:"settings";a:2:{s:10:"remote_url";s:0:"";s:9:"local_url";s:48:"/var/www/html/wp-content/uploads/exportwp/30.csv";}}s:6:"parser";s:3:"csv";s:3:"map";a:0:{}s:7:"enabled";a:0:{}s:11:"permissions";a:0:{}s:8:"settings";a:4:{s:9:"post_type";a:2:{i:0;s:7:"product";i:1;s:17:"product_variation";}s:12:"unique_field";a:3:{i:0;s:2:"ID";i:1;s:4:"_sku";i:2;s:9:"post_name";}s:9:"start_row";N;s:7:"max_row";N;}}
227 - //
228 - $importer->save();
229 - }
230 -
231 278 return $file->getRecordCount();
232 279 }
233 280
234 281 public function process_xml_file($id, $tmp = false)
@@ -253,8 +300,19 @@
253 300
254 301 return $results;
255 302 }
256 303
304 + public function process_json_file($id, $tmp = false)
305 + {
306 + $importer = $this->get_importer($id);
307 + $config = $this->get_config($importer->getId(), $tmp);
308 +
309 + $file = new JSONFile($importer->getFile(), $config);
310 + $file->processing(true);
311 +
312 + return $file->get_path_list();
313 + }
314 +
257 315 /**
258 316 * Link import file to importer via post meta
259 317 *
260 318 * @param ImporterModel $id
@@ -270,8 +328,15 @@
270 328 }
271 329
272 330 $index++;
273 331
332 + // Store uploads-relative paths so site moves / open_basedir changes do not break lookups.
333 + $file_path = wp_normalize_path($file_path);
334 + $relative = Filesystem::to_uploads_relative_path($file_path);
335 + if ($relative !== '' && $relative !== $file_path) {
336 + $file_path = $relative;
337 + }
338 +
274 339 update_post_meta($importer->getId(), '_importer_files', $index);
275 340 update_post_meta($importer->getId(), '_importer_file_' . $index, $file_path);
276 341 return $index;
277 342 }
@@ -285,11 +350,50 @@
285 350 $file_index = 1;
286 351 }
287 352 $file_index++;
288 353
289 - return $importer->getId() . '-' . intval($file_index) . '-';
354 + return $importer->getId() . '-' . intval($file_index) . '-' . wp_generate_password(12, false, false) . '-';
290 355 }
291 356
357 + public function set_custom_upload_path($dir)
358 + {
359 + remove_filter('upload_dir', [$this, 'set_custom_upload_path']);
360 +
361 + $path = $this->filesystem->get_temp_directory();
362 + $url = $this->filesystem->get_temp_directory(true);
363 +
364 + add_filter('upload_dir', [$this, 'set_custom_upload_path']);
365 +
366 + $path .= '/uploads';
367 + $url .= '/uploads';
368 +
369 + if (!is_dir($path)) {
370 + wp_mkdir_p($path);
371 + }
372 +
373 + if (!file_exists($path . '/.htaccess')) {
374 + file_put_contents($path . '/.htaccess', "# Apache 2.4+
375 +<IfModule mod_authz_core.c>
376 + Require all denied
377 +</IfModule>
378 +
379 +# Apache 2.2 and older (or when mod_authz_core isn't available)
380 +<IfModule !mod_authz_core.c>
381 + Deny from all
382 +</IfModule>");
383 + }
384 +
385 + if (!file_exists($path . '/index.html')) {
386 + touch($path . '/index.html');
387 + }
388 +
389 + return array(
390 + 'path' => $path,
391 + 'url' => $url,
392 + 'subdir' => '/importwp/uploads',
393 + ) + $dir;
394 + }
395 +
292 396 public function upload_file($id, $file)
293 397 {
294 398 $importer = $this->get_importer($id);
295 399 Logger::setId($importer->getId());
@@ -294,10 +398,22 @@
294 398 $importer = $this->get_importer($id);
295 399 Logger::setId($importer->getId());
296 400
297 401 $allowed_file_types = $this->event_handler->run('importer.allowed_file_types', [$importer->getAllowedFileTypes()]);
402 +
403 + $prefix = $this->get_importer_file_prefix($importer);
404 +
405 + $prefix_upload = function ($file) use ($prefix) {
406 + $file['name'] = $prefix . $file['name'];
407 + return $file;
408 + };
409 +
410 + add_filter('wp_handle_upload_prefilter', $prefix_upload);
411 +
298 412 $result = $this->filesystem->upload_file($file, $allowed_file_types);
299 413
414 + remove_filter('wp_handle_upload_prefilter', $prefix_upload);
415 +
300 416 if (is_wp_error($result)) {
301 417 return $result;
302 418 }
303 419
@@ -316,40 +432,85 @@
316 432
317 433 $allowed_file_types = $this->event_handler->run('importer.allowed_file_types', [$importer->getAllowedFileTypes()]);
318 434 $prefix = $this->get_importer_file_prefix($importer);
319 435
320 - if (preg_match('/^s?ftp?:\/\//', $source) === 1) {
436 + try {
321 437
322 - if (preg_match('/^(?<protocol>s?ftp):\/\/(?:(?<user>[^\:@]+)(?:\:(?<pass>[^@]+))?@)?(?<host>[^\:\/]+)(?:\:(?<port>[0-9]+))?(?:\/(?<path>.*))$/', $source, $matches) !== 1) {
438 + if (preg_match('/^s?ftp?:\/\//', $source) === 1) {
323 439
324 - return new \WP_Error("IM_RM_FTP_PARSE", "Unable to parse FTP connection string");
325 - }
440 + if (preg_match('/^(?<protocol>s?ftp):\/\/(?:(?<user>[^\:@]+)(?:\:(?<pass>[^@]+))?@)?(?<host>[^\:\/]+)(?:\:(?<port>[0-9]+))?(?:\/(?<path>.*))$/', $source, $matches) !== 1) {
326 441
327 - $user = isset($matches['user']) ? urldecode($matches['user']) : '';
328 - $pass = isset($matches['pass']) ? urldecode($matches['pass']) : '';
329 - $host = isset($matches['host']) ? $matches['host'] : false;
330 - $port = isset($matches['port']) && !empty($matches['port']) ? $matches['port'] : intval(21);
331 - $path = isset($matches['path']) ? $matches['path'] : false;
442 + return new \WP_Error("IM_RM_FTP_PARSE", __("Unable to parse FTP connection string", 'jc-importer'));
443 + }
332 444
333 - if (!$host) {
334 - return new \WP_Error("IM_RM_FTP_HOST", "Unable to parse ftp host from connection string");
335 - }
445 + $protocol = isset($matches['protocol']) ? $matches['protocol'] : 'ftp';
446 + $user = isset($matches['user']) ? urldecode($matches['user']) : '';
447 + $pass = isset($matches['pass']) ? urldecode($matches['pass']) : '';
448 + $host = isset($matches['host']) ? $matches['host'] : false;
449 + $port = isset($matches['port']) && !empty($matches['port']) ? $matches['port'] : intval(21);
450 + $path = isset($matches['path']) ? $matches['path'] : false;
336 451
337 - if (!$path) {
338 - return new \WP_Error("IM_RM_FTP_HOST", "Unable to parse ftp host from connection string");
339 - }
452 + if (!$host) {
453 + return new \WP_Error("IM_RM_FTP_HOST", __("Unable to parse ftp host from connection string", 'jc-importer'));
454 + }
340 455
341 - /**
342 - * @var \ImportWP\Common\Ftp\Ftp $ftp
343 - */
344 - $ftp = Container::getInstance()->get('ftp');
345 - $result = $ftp->download_file($path, $host, $user, $pass, false, $port);
346 - } else {
456 + if (!$path) {
457 + return new \WP_Error("IM_RM_FTP_HOST", __("Unable to parse ftp host from connection string", 'jc-importer'));
458 + }
347 459
348 - $result = $this->filesystem->download_file($source, $filetype, $allowed_file_types, null, $prefix);
460 + /**
461 + * @var \ImportWP\Common\Ftp\Ftp $ftp
462 + */
463 + $ftp = Container::getInstance()->get('ftp');
464 +
465 + $path = apply_filters('iwp/importer/remote_file', $path, $importer);
466 + $path = apply_filters(sprintf('iwp/importer=%d/remote_file', $importer->getId()), $path, $importer);
467 +
468 + $filter_connection_args = [
469 + 'user' => $user,
470 + 'pass' => $pass,
471 + 'host' => $host,
472 + 'port' => $port,
473 + 'path' => $path,
474 + ];
475 + $path = apply_filters(sprintf('iwp/importer/remote_file/source=%s', 'ftp'), $path, $importer, $filter_connection_args);
476 + $path = apply_filters(sprintf('iwp/importer=%d/remote_file/source=ftp', $importer->getId(), 'ftp'), $path, $importer, $filter_connection_args);
477 +
478 + if ($protocol == 'sftp') {
479 +
480 + // require sftp package
481 + require_once __DIR__ . '/../../../libs/autoload.php';
482 +
483 + $sftp = new \phpseclib3\Net\SFTP($host, $port);
484 + if (!$sftp->login($user, $pass)) {
485 + return new \WP_Error('IWP_FTP_0', __("Unable to login to ftp server", 'jc-importer'));
486 + }
487 +
488 + $wp_upload_dir = wp_upload_dir();
489 +
490 + $dest = wp_unique_filename($wp_upload_dir['path'], basename($path));
491 + $wp_dest = $wp_upload_dir['path'] . '/' . $dest;
492 +
493 + if (!$sftp->get($path, $wp_dest)) {
494 + return new \WP_Error('IWP_FTP_2', sprintf(__('Unable to download: %s file via sftp.', 'jc-importer'), $path));
495 + }
496 +
497 + $result = array(
498 + 'dest' => $wp_dest,
499 + 'type' => $this->filesystem->get_filetype($wp_dest),
500 + 'mime' => $this->filesystem->get_file_mime($wp_dest)
501 + );
502 + } else {
503 + $result = $ftp->download_file($path, $host, $user, $pass, false, $port);
504 + }
505 + } else {
506 +
507 + $result = $this->filesystem->download_file($source, $filetype, $allowed_file_types, null, $prefix);
508 + }
509 + } catch (\Exception $e) {
510 + return new \WP_Error($e->getCode(), $e->getMessage());
349 511 }
350 512
351 -
352 513 if (is_wp_error($result)) {
353 514 return $result;
354 515 }
355 516
@@ -360,16 +521,34 @@
360 521
361 522 return $attachment_id;
362 523 }
363 524
364 - public function local_file($id, $source)
525 + public function local_file($id, $source, $filetype = null)
365 526 {
366 527 $importer = $this->get_importer($id);
367 528 Logger::setId($importer->getId());
368 529
530 + $allowed_bases = apply_filters('iwp/importer/local_file/allowed_directories', [
531 + realpath(WP_CONTENT_DIR) . DIRECTORY_SEPARATOR
532 + ]);
533 +
534 + $source = realpath($source);
535 +
536 + $is_allowed = false;
537 + foreach ($allowed_bases as $allowed_base) {
538 + if ($allowed_base && strpos($source, $allowed_base) === 0) {
539 + $is_allowed = true;
540 + break;
541 + }
542 + }
543 +
544 + if (!$source || !$is_allowed) {
545 + return new \WP_Error('IWP_LOCAL_FILE_1', __('Access to this file path is not allowed.', 'jc-importer'));
546 + }
547 +
369 548 $allowed_file_types = $this->event_handler->run('importer.allowed_file_types', [$importer->getAllowedFileTypes()]);
370 549 $prefix = $this->get_importer_file_prefix($importer);
371 - $result = $this->filesystem->copy_file($source, $allowed_file_types, null, $prefix);
550 + $result = $this->filesystem->copy_file($source, $allowed_file_types, null, $prefix, $filetype);
372 551
373 552 if (is_wp_error($result)) {
374 553 return $result;
375 554 }
@@ -403,9 +582,9 @@
403 582 $file_path = apply_filters('iwp/importer/file_uploaded/file_path', $file_path, $importer_model);
404 583
405 584 $file_id = $this->link_importer_file($id, $file_path);
406 585 if (!$file_id) {
407 - return new \WP_Error('IWP_IM_01', 'Unable to link importer file');
586 + return new \WP_Error('IWP_IM_01', __('Unable to link importer file', 'jc-importer'));
408 587 }
409 588
410 589 if (is_null($importer_model->getParser())) {
411 590 $importer_model->setParser($file_type);
@@ -466,9 +645,9 @@
466 645
467 646 $base .= str_pad($id, 2, STR_PAD_LEFT) . DIRECTORY_SEPARATOR;
468 647 if (!file_exists($base)) {
469 648 if (!is_writable(dirname($base)) || !mkdir($base)) {
470 - throw new \Exception("Unable to create directory: " . $base);
649 + throw new \Exception(sprintf(__("Unable to create directory: %s", 'jc-importer'), $base));
471 650 }
472 651 }
473 652
474 653 for ($i = 0; $i < ceil(strlen($session) / 2); $i++) {
@@ -475,9 +654,9 @@
475 654 $base .= substr($session, $i * 2, 2) . DIRECTORY_SEPARATOR;
476 655 if (!file_exists($base)) {
477 656
478 657 if (!is_writable(dirname($base)) || !mkdir($base)) {
479 - throw new \Exception("Unable to create directory: " . $base);
658 + throw new \Exception(sprintf(__("Unable to create directory: %s", 'jc-importer'), $base));
480 659 }
481 660 }
482 661
483 662 if ($i >= $max_depth - 2) {
@@ -507,10 +686,13 @@
507 686
508 687 $importer_data = $this->get_importer($id);
509 688 $importer_id = $importer_data->getId();
510 689
511 - $config_data = get_site_option('iwp_importer_config_' . $importer_id, []);
690 + // store current importer
691 + iwp()->importer = $importer_data;
512 692
693 + $config_data = get_option('iwp_importer_config_' . $importer_id, []);
694 +
513 695 $this->event_handler->run('importer_manager.import', [$importer_data]);
514 696
515 697 $state = new ImporterState($importer_id, $user);
516 698
@@ -516,11 +698,16 @@
516 698
517 699 try {
518 700
519 701 Logger::debug('IM -init_state');
702 +
703 + // 1. Set State Session, and load its state
520 704 $state->init($session);
521 705
706 + // if this is a new session, clear config files
522 707 if ($state->has_status('init')) {
708 + // rest importer log.
709 + Logger::clear($importer_id);
523 710 Logger::debug('IM -clear_config_files');
524 711 $this->clear_config_files($importer_id, false, true);
525 712 $config_data['features'] = [
526 713 'session_table' => true
@@ -544,8 +731,9 @@
544 731 // mapper
545 732 Logger::debug('IM -get_importer_mapper');
546 733 $mapper = $this->get_importer_mapper($importer_data, $template, $permission);
547 734
735 + // if this is a new session, build config
548 736 if ($state->has_status('init')) {
549 737
550 738 Logger::debug('IM -generate_config');
551 739
@@ -565,8 +753,45 @@
565 753 $version = 0;
566 754 }
567 755 update_post_meta($importer_id, '_iwp_version', $version);
568 756 $config_data['version'] = $version;
757 +
758 + /**
759 + * Fetch new file if setting is checked
760 + * @since 2.7.15
761 + */
762 + $run_fetch_file = $importer_data->getSetting('run_fetch') || false;
763 + $run_fetch_file = apply_filters('iwp/importer/run_fetch_file', $run_fetch_file);
764 + if ($run_fetch_file) {
765 +
766 + add_filter('upload_dir', [$this, 'set_custom_upload_path']);
767 +
768 + $datasource = $importer_data->getDatasource();
769 + switch ($datasource) {
770 + case 'remote':
771 + $raw_source = $importer_data->getDatasourceSetting('remote_url');
772 + $source = apply_filters('iwp/importer/datasource', $raw_source, $raw_source, $importer_data);
773 + $source = apply_filters('iwp/importer/datasource/remote', $source, $raw_source, $importer_data);
774 + $attachment_id = $this->remote_file($importer_data, $source, $importer_data->getParser());
775 + break;
776 + case 'local':
777 + $raw_source = $importer_data->getDatasourceSetting('local_url');
778 + $source = apply_filters('iwp/importer/datasource', $raw_source, $raw_source, $importer_data);
779 + $source = apply_filters('iwp/importer/datasource/local', $source, $raw_source, $importer_data);
780 + $attachment_id = $this->local_file($importer_data, $source, $importer_data->getParser());
781 + break;
782 + default:
783 + // TODO: record error
784 + $attachment_id = new \WP_Error('IWP_CRON_1', sprintf(__('Unable to get new file using datasource: %s', 'jc-importer'), $datasource));
785 + break;
786 + }
787 +
788 + remove_filter('upload_dir', [$this, 'set_custom_upload_path']);
789 +
790 + if (is_wp_error($attachment_id)) {
791 + throw new \Exception(sprintf(__('Importer Datasource: %s', 'jc-importer'), $attachment_id->get_error_message()));
792 + }
793 + }
569 794 }
570 795
571 796 $start = 0;
572 797
@@ -583,21 +808,43 @@
583 808 Logger::debug('IM -get_xml_file');
584 809 $file = $this->get_xml_file($importer_data, $config);
585 810 Logger::debug('IM -load_parser');
586 811 $parser = new XMLParser($file);
812 + } elseif ($importer_data->getParser() === 'json') {
813 + Logger::debug('IM -get_json_file');
814 + $file = $this->get_json_file($importer_data, $config);
815 + Logger::debug('IM -load_parser');
816 + $parser = new JSONParser($file);
587 817 } else {
588 818 $parser = apply_filters('iwp/importer/init_parser', false, $importer_data, $config);
589 819 }
590 820
821 + if (!$parser || !is_object($parser) || !method_exists($parser, 'file')) {
822 + $parser_type = $importer_data->getParser();
823 + throw new \Exception(
824 + sprintf(
825 + __('Unable to load importer parser for type: %s', 'jc-importer'),
826 + $parser_type ? $parser_type : __('unknown', 'jc-importer')
827 + )
828 + );
829 + }
830 +
831 + // if this is a new session, set start / end rows to state
591 832 if ($state->has_status('init')) {
592 833
593 834 Logger::debug('IM -get_record_count');
594 835 $end = $parser->file()->getRecordCount();
595 836
837 + // Capture cancelled status from file processor
838 + $raw_state = ImporterState::get_state($importer_data->getId());
839 + if ($raw_state['status'] === 'cancelled') {
840 + return $raw_state;
841 + }
842 +
596 843 $config_data['start'] = $this->get_start($importer_data, $start);
597 844 $config_data['end'] = $this->get_end($importer_data, $config_data['start'], $end);
598 845
599 - update_site_option('iwp_importer_config_' . $importer_id, $config_data);
846 + update_option('iwp_importer_config_' . $importer_id, $config_data);
600 847
601 848 Logger::debug('IM -update_state');
602 849 $state->update(function ($state) use ($config_data) {
603 850
@@ -615,8 +862,13 @@
615 862
616 863 do_action('iwp/importer/init', $importer_data);
617 864 }
618 865
866 +
867 + add_filter('iwp/importer/mapper/hash_check_enabled', function ($enabled) use ($importer_data) {
868 + return $importer_data->getSetting('hash_check');
869 + });
870 +
619 871 Logger::debug('IM -import');
620 872 $importer = new \ImportWP\Common\Importer\Importer($config);
621 873 $importer->parser($parser);
622 874 $importer->mapper($mapper);
@@ -628,9 +880,11 @@
628 880 } catch (\Exception $e) {
629 881
630 882 // TODO: Missing template errors are currently not being logged to history, possibly others?
631 883 Logger::error('import -error=' . $e->getMessage(), $importer_id);
632 - return $state->error($e)->get_raw();
884 + $state->error($e);
885 + Util::write_status_session_to_file($id, $state);
886 + return $state->get_raw();
633 887 }
634 888
635 889 /**
636 890 * @var Properties $properties
@@ -650,8 +904,38 @@
650 904 return $data;
651 905 })->get_raw();
652 906 }
653 907
908 + public function pause_import($importer_id, $paused)
909 + {
910 + // TODO: set flag for paused.
911 + $state = ImporterState::get_state($importer_id);
912 + if ($paused === 'no') {
913 + ImporterState::clear_flag($importer_id);
914 + $state['status'] = 'running';
915 + } else {
916 + ImporterState::set_paused($importer_id);
917 + $state['status'] = 'paused';
918 + }
919 +
920 + // good chance this will be overwritten
921 + ImporterState::set_state($importer_id, $state);
922 +
923 + return $state;
924 + }
925 +
926 + public function stop_import($importer_id)
927 + {
928 + ImporterState::set_cancelled($importer_id);
929 +
930 + // good chance this will be overwritten
931 + $state = ImporterState::get_state($importer_id);
932 + $state['status'] = 'cancelled';
933 + ImporterState::set_state($importer_id, $state);
934 +
935 + return $state;
936 + }
937 +
654 938 public function get_start($importer_data, $start)
655 939 {
656 940 $tmp_start = $importer_data->getStartRow();
657 941 if (!is_null($tmp_start) && "" !== $tmp_start) {
@@ -684,9 +968,9 @@
684 968 $templates = $this->get_templates();
685 969 $template_name = $importer_model->getTemplate();
686 970
687 971 if (!isset($templates[$template_name])) {
688 - $exception_msg = "Unable to locate importer template: " . $template_name;
972 + $exception_msg = sprintf(__("Unable to locate importer template: %s", 'jc-importer'), $template_name);
689 973 Logger::error('import -get_importer_template=' . $exception_msg, $importer_model->getId());
690 974 throw new \Exception($exception_msg);
691 975 }
692 976
@@ -715,9 +999,11 @@
715 999 $mappers = $this->event_handler->run('mappers.register', [[]]); // apply_filters('iwp/mappers/register', []);
716 1000 $mappers = array_merge($mappers, [
717 1001 'post' => PostMapper::class,
718 1002 'user' => UserMapper::class,
719 - 'term' => TermMapper::class
1003 + 'term' => TermMapper::class,
1004 + 'attachment' => AttachmentMapper::class,
1005 + 'comment' => CommentMapper::class,
720 1006 ]);
721 1007 return $mappers;
722 1008 }
723 1009
@@ -724,9 +1010,9 @@
724 1010 public function get_mapper($key)
725 1011 {
726 1012 $mappers = $this->get_mappers();
727 1013 if (!isset($mappers[$key])) {
728 - return new \WP_Error('IWP_IM_1', 'Unable to locate mapper: ' . $key);
1014 + return new \WP_Error('IWP_IM_1', sprintf(__('Unable to locate mapper: %s', 'jc-importer'), $key));
729 1015 }
730 1016
731 1017 return $mappers[$key];
732 1018 }
@@ -738,8 +1024,10 @@
738 1024 'post' => PostTemplate::class,
739 1025 'page' => PageTemplate::class,
740 1026 'user' => UserTemplate::class,
741 1027 'term' => TermTemplate::class,
1028 + 'attachment' => AttachmentTemplate::class,
1029 + 'comment' => CommentTemplate::class,
742 1030 'custom-post-type' => CustomPostTypeTemplate::class,
743 1031 ]);
744 1032 return $templates;
745 1033 }
@@ -747,9 +1035,9 @@
747 1035 public function get_template($key)
748 1036 {
749 1037 $templates = $this->get_templates();
750 1038 if (!isset($templates[$key])) {
751 - return new \WP_Error('IWP_IM_1', 'Unable to locate template: ' . $key);
1039 + return new \WP_Error('IWP_IM_1', sprintf(__('Unable to locate template: %s', 'jc-importer'), $key));
752 1040 }
753 1041
754 1042 return $templates[$key];
755 1043 }