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 +361 -42 2.7.12 → 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
@@ -315,10 +431,86 @@
315 431 Logger::setId($importer->getId());
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 - $result = $this->filesystem->download_file($source, $filetype, $allowed_file_types, null, $prefix);
320 435
436 + try {
437 +
438 + if (preg_match('/^s?ftp?:\/\//', $source) === 1) {
439 +
440 + if (preg_match('/^(?<protocol>s?ftp):\/\/(?:(?<user>[^\:@]+)(?:\:(?<pass>[^@]+))?@)?(?<host>[^\:\/]+)(?:\:(?<port>[0-9]+))?(?:\/(?<path>.*))$/', $source, $matches) !== 1) {
441 +
442 + return new \WP_Error("IM_RM_FTP_PARSE", __("Unable to parse FTP connection string", 'jc-importer'));
443 + }
444 +
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;
451 +
452 + if (!$host) {
453 + return new \WP_Error("IM_RM_FTP_HOST", __("Unable to parse ftp host from connection string", 'jc-importer'));
454 + }
455 +
456 + if (!$path) {
457 + return new \WP_Error("IM_RM_FTP_HOST", __("Unable to parse ftp host from connection string", 'jc-importer'));
458 + }
459 +
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());
511 + }
512 +
321 513 if (is_wp_error($result)) {
322 514 return $result;
323 515 }
324 516
@@ -329,16 +521,34 @@
329 521
330 522 return $attachment_id;
331 523 }
332 524
333 - public function local_file($id, $source)
525 + public function local_file($id, $source, $filetype = null)
334 526 {
335 527 $importer = $this->get_importer($id);
336 528 Logger::setId($importer->getId());
337 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 +
338 548 $allowed_file_types = $this->event_handler->run('importer.allowed_file_types', [$importer->getAllowedFileTypes()]);
339 549 $prefix = $this->get_importer_file_prefix($importer);
340 - $result = $this->filesystem->copy_file($source, $allowed_file_types, null, $prefix);
550 + $result = $this->filesystem->copy_file($source, $allowed_file_types, null, $prefix, $filetype);
341 551
342 552 if (is_wp_error($result)) {
343 553 return $result;
344 554 }
@@ -372,9 +582,9 @@
372 582 $file_path = apply_filters('iwp/importer/file_uploaded/file_path', $file_path, $importer_model);
373 583
374 584 $file_id = $this->link_importer_file($id, $file_path);
375 585 if (!$file_id) {
376 - 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'));
377 587 }
378 588
379 589 if (is_null($importer_model->getParser())) {
380 590 $importer_model->setParser($file_type);
@@ -435,9 +645,9 @@
435 645
436 646 $base .= str_pad($id, 2, STR_PAD_LEFT) . DIRECTORY_SEPARATOR;
437 647 if (!file_exists($base)) {
438 648 if (!is_writable(dirname($base)) || !mkdir($base)) {
439 - throw new \Exception("Unable to create directory: " . $base);
649 + throw new \Exception(sprintf(__("Unable to create directory: %s", 'jc-importer'), $base));
440 650 }
441 651 }
442 652
443 653 for ($i = 0; $i < ceil(strlen($session) / 2); $i++) {
@@ -444,9 +654,9 @@
444 654 $base .= substr($session, $i * 2, 2) . DIRECTORY_SEPARATOR;
445 655 if (!file_exists($base)) {
446 656
447 657 if (!is_writable(dirname($base)) || !mkdir($base)) {
448 - throw new \Exception("Unable to create directory: " . $base);
658 + throw new \Exception(sprintf(__("Unable to create directory: %s", 'jc-importer'), $base));
449 659 }
450 660 }
451 661
452 662 if ($i >= $max_depth - 2) {
@@ -476,10 +686,13 @@
476 686
477 687 $importer_data = $this->get_importer($id);
478 688 $importer_id = $importer_data->getId();
479 689
480 - $config_data = get_site_option('iwp_importer_config_' . $importer_id, []);
690 + // store current importer
691 + iwp()->importer = $importer_data;
481 692
693 + $config_data = get_option('iwp_importer_config_' . $importer_id, []);
694 +
482 695 $this->event_handler->run('importer_manager.import', [$importer_data]);
483 696
484 697 $state = new ImporterState($importer_id, $user);
485 698
@@ -485,11 +698,16 @@
485 698
486 699 try {
487 700
488 701 Logger::debug('IM -init_state');
702 +
703 + // 1. Set State Session, and load its state
489 704 $state->init($session);
490 705
706 + // if this is a new session, clear config files
491 707 if ($state->has_status('init')) {
708 + // rest importer log.
709 + Logger::clear($importer_id);
492 710 Logger::debug('IM -clear_config_files');
493 711 $this->clear_config_files($importer_id, false, true);
494 712 $config_data['features'] = [
495 713 'session_table' => true
@@ -513,8 +731,9 @@
513 731 // mapper
514 732 Logger::debug('IM -get_importer_mapper');
515 733 $mapper = $this->get_importer_mapper($importer_data, $template, $permission);
516 734
735 + // if this is a new session, build config
517 736 if ($state->has_status('init')) {
518 737
519 738 Logger::debug('IM -generate_config');
520 739
@@ -534,8 +753,45 @@
534 753 $version = 0;
535 754 }
536 755 update_post_meta($importer_id, '_iwp_version', $version);
537 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 + }
538 794 }
539 795
540 796 $start = 0;
541 797
@@ -552,21 +808,43 @@
552 808 Logger::debug('IM -get_xml_file');
553 809 $file = $this->get_xml_file($importer_data, $config);
554 810 Logger::debug('IM -load_parser');
555 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);
556 817 } else {
557 818 $parser = apply_filters('iwp/importer/init_parser', false, $importer_data, $config);
558 819 }
559 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
560 832 if ($state->has_status('init')) {
561 833
562 834 Logger::debug('IM -get_record_count');
563 835 $end = $parser->file()->getRecordCount();
564 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 +
565 843 $config_data['start'] = $this->get_start($importer_data, $start);
566 844 $config_data['end'] = $this->get_end($importer_data, $config_data['start'], $end);
567 845
568 - update_site_option('iwp_importer_config_' . $importer_id, $config_data);
846 + update_option('iwp_importer_config_' . $importer_id, $config_data);
569 847
570 848 Logger::debug('IM -update_state');
571 849 $state->update(function ($state) use ($config_data) {
572 850
@@ -584,8 +862,13 @@
584 862
585 863 do_action('iwp/importer/init', $importer_data);
586 864 }
587 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 +
588 871 Logger::debug('IM -import');
589 872 $importer = new \ImportWP\Common\Importer\Importer($config);
590 873 $importer->parser($parser);
591 874 $importer->mapper($mapper);
@@ -597,9 +880,11 @@
597 880 } catch (\Exception $e) {
598 881
599 882 // TODO: Missing template errors are currently not being logged to history, possibly others?
600 883 Logger::error('import -error=' . $e->getMessage(), $importer_id);
601 - return $state->error($e)->get_raw();
884 + $state->error($e);
885 + Util::write_status_session_to_file($id, $state);
886 + return $state->get_raw();
602 887 }
603 888
604 889 /**
605 890 * @var Properties $properties
@@ -619,8 +904,38 @@
619 904 return $data;
620 905 })->get_raw();
621 906 }
622 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 +
623 938 public function get_start($importer_data, $start)
624 939 {
625 940 $tmp_start = $importer_data->getStartRow();
626 941 if (!is_null($tmp_start) && "" !== $tmp_start) {
@@ -653,9 +968,9 @@
653 968 $templates = $this->get_templates();
654 969 $template_name = $importer_model->getTemplate();
655 970
656 971 if (!isset($templates[$template_name])) {
657 - $exception_msg = "Unable to locate importer template: " . $template_name;
972 + $exception_msg = sprintf(__("Unable to locate importer template: %s", 'jc-importer'), $template_name);
658 973 Logger::error('import -get_importer_template=' . $exception_msg, $importer_model->getId());
659 974 throw new \Exception($exception_msg);
660 975 }
661 976
@@ -684,9 +999,11 @@
684 999 $mappers = $this->event_handler->run('mappers.register', [[]]); // apply_filters('iwp/mappers/register', []);
685 1000 $mappers = array_merge($mappers, [
686 1001 'post' => PostMapper::class,
687 1002 'user' => UserMapper::class,
688 - 'term' => TermMapper::class
1003 + 'term' => TermMapper::class,
1004 + 'attachment' => AttachmentMapper::class,
1005 + 'comment' => CommentMapper::class,
689 1006 ]);
690 1007 return $mappers;
691 1008 }
692 1009
@@ -693,9 +1010,9 @@
693 1010 public function get_mapper($key)
694 1011 {
695 1012 $mappers = $this->get_mappers();
696 1013 if (!isset($mappers[$key])) {
697 - 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));
698 1015 }
699 1016
700 1017 return $mappers[$key];
701 1018 }
@@ -707,8 +1024,10 @@
707 1024 'post' => PostTemplate::class,
708 1025 'page' => PageTemplate::class,
709 1026 'user' => UserTemplate::class,
710 1027 'term' => TermTemplate::class,
1028 + 'attachment' => AttachmentTemplate::class,
1029 + 'comment' => CommentTemplate::class,
711 1030 'custom-post-type' => CustomPostTypeTemplate::class,
712 1031 ]);
713 1032 return $templates;
714 1033 }
@@ -716,9 +1035,9 @@
716 1035 public function get_template($key)
717 1036 {
718 1037 $templates = $this->get_templates();
719 1038 if (!isset($templates[$key])) {
720 - 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));
721 1040 }
722 1041
723 1042 return $templates[$key];
724 1043 }