PluginProbe
Import WP – CSV & XML Import Export for WordPress / 2.14.24
Import WP – CSV & XML Import Export for WordPress v2.14.24
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
jc-importer / class / Common / Importer / ImporterManager.php

ImporterManager.php in Import WP – CSV & XML Import Export for WordPress 2.14.24, at class/Common/Importer/ImporterManager.php

1,105 lines 37.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace ImportWP\Common\Importer;
4
5 use ImportWP\Common\Filesystem\Filesystem;
6 use ImportWP\Common\Importer\Config\Config;
7 use ImportWP\Common\Importer\File\CSVFile;
8 use ImportWP\Common\Importer\File\XMLFile;
9 use ImportWP\Common\Importer\Mapper\AttachmentMapper;
10 use ImportWP\Common\Importer\Mapper\CommentMapper;
11 use ImportWP\Common\Importer\Mapper\PostMapper;
12 use ImportWP\Common\Importer\Mapper\TermMapper;
13 use ImportWP\Common\Importer\Mapper\UserMapper;
14 use ImportWP\Common\Importer\Parser\CSVParser;
15 use ImportWP\Common\Importer\Parser\XMLParser;
16 use ImportWP\Common\Importer\Permission\Permission;
17 use ImportWP\Common\Importer\State\ImporterState;
18 use ImportWP\Common\Importer\Template\AttachmentTemplate;
19 use ImportWP\Common\Importer\Template\CommentTemplate;
20 use ImportWP\Common\Importer\Template\CustomPostTypeTemplate;
21 use ImportWP\Common\Importer\Template\PageTemplate;
22 use ImportWP\Common\Importer\Template\PostTemplate;
23 use ImportWP\Common\Importer\Template\Template;
24 use ImportWP\Common\Importer\Template\TemplateManager;
25 use ImportWP\Common\Importer\Template\TermTemplate;
26 use ImportWP\Common\Importer\Template\UserTemplate;
27 use ImportWP\Common\Model\ImporterModel;
28 use ImportWP\Common\Properties\Properties;
29 use ImportWP\Common\Runner\ImporterRunnerState;
30 use ImportWP\Common\Util\Logger;
31 use ImportWP\Common\Util\Util;
32 use ImportWP\Container;
33 use ImportWP\EventHandler;
34
35 class ImporterManager
36 {
37
38 /**
39 * @var Filesystem
40 */
41 private $filesystem;
42
43 /**
44 * @var TemplateManager $template_manager
45 */
46 private $template_manager;
47
48 /**
49 * @var EventHandler $event_handler
50 */
51 protected $event_handler;
52
53 public function __construct(Filesystem $filesystem, TemplateManager $template_manager, EventHandler $event_handler)
54 {
55 $this->filesystem = $filesystem;
56 $this->template_manager = $template_manager;
57 $this->event_handler = $event_handler;
58 }
59
60 /**
61 * Get Importers
62 *
63 * @return ImporterModel[]
64 */
65 public function get_importers()
66 {
67
68 $result = array();
69 $query = new \WP_Query(array(
70 'post_type' => IWP_POST_TYPE,
71 'posts_per_page' => -1,
72 ));
73
74 foreach ($query->posts as $post) {
75 $result[] = $this->get_importer($post);
76 }
77 return $result;
78 }
79
80 /**
81 * Get Importer
82 *
83 * @param int $id
84 * @return ImporterModel
85 */
86 public function get_importer($id)
87 {
88 if ($id instanceof ImporterModel) {
89 return $id;
90 }
91
92 if (IWP_POST_TYPE !== get_post_type($id)) {
93 return false;
94 }
95
96 return new ImporterModel($id, $this->is_debug());
97 }
98
99 public function is_debug()
100 {
101
102 if (defined('IWP_DEBUG') && true === IWP_DEBUG) {
103 return true;
104 }
105
106 $uninstall_enabled = get_option('iwp_settings');
107 if (isset($uninstall_enabled['debug']) && true === $uninstall_enabled['debug']) {
108 return true;
109 }
110
111 return false;
112 }
113
114 public function set_current_user($id)
115 {
116 $importer_model = $this->get_importer($id);
117 $user_id = $importer_model->getUserId();
118
119 Logger::write('set_current_user -user=' . $user_id, $importer_model->getId());
120
121 if ($user_id) {
122 return wp_set_current_user($user_id);
123 }
124
125 return false;
126 }
127
128 /**
129 * Delete Importer
130 *
131 * @param int $id
132 * @return void
133 */
134 public function delete_importer($id)
135 {
136 $importer = $this->get_importer($id);
137 $importer->delete();
138 }
139
140 public function get_file($id)
141 {
142 $importer = $this->get_importer($id);
143 $config = $this->get_config($importer);
144 $parser = $importer->getParser();
145
146 if ('xml' === $parser) {
147 return $this->get_xml_file($importer, $config);
148 } elseif ('csv' === $parser) {
149 return $this->get_csv_file($importer, $config);
150 }
151
152 return false;
153 }
154
155 public function get_csv_file($id, $config)
156 {
157 $importer = $this->get_importer($id);
158 $file = new CSVFile($importer->getFile(), $config);
159 $file->setDelimiter($importer->getFileSetting('delimiter'));
160 $file->setEnclosure($importer->getFileSetting('enclosure'));
161 $file->setEscape($importer->getFileSetting('escape', "\\"));
162 return $file;
163 }
164
165 public function get_xml_file($id, $config)
166 {
167 $importer = $this->get_importer($id);
168 $file = new XMLFile($importer->getFile(), $config);
169 $file->setRecordPath($importer->getFileSetting('base_path'));
170 return $file;
171 }
172
173 public function preview_csv_file($id, $fields = [], $row = 0)
174 {
175 $importer = $this->get_importer($id);
176 $config = $this->get_config($importer, true);
177
178 $file = $this->get_csv_file($importer, $config);
179 $parser = new CSVParser($file);
180
181 $record = $parser->getRecord($row);
182
183 return $record->queryGroup(['fields' => $fields]);
184 }
185
186 public function preview_xml_file($id, $fields = [], $row = 0)
187 {
188 $importer = $this->get_importer($id);
189 $config = $this->get_config($importer, true);
190
191 $file = $this->get_xml_file($importer, $config);
192 $parser = new XMLParser($file);
193
194 $record = $parser->getRecord($row);
195 return $record->queryGroup(['fields' => $fields]);
196 }
197
198 public function process_csv_file($id, $delimiter, $enclosure, $tmp = false)
199 {
200 $importer = $this->get_importer($id);
201 $config = $this->get_config($importer->getId(), $tmp);
202
203 $file = $this->get_csv_file($importer, $config);
204 $file->setDelimiter($delimiter);
205 $file->setEnclosure($enclosure);
206 $file->processing(true);
207
208 return $file->getRecordCount();
209 }
210
211 public function process_xml_file($id, $tmp = false)
212 {
213
214 $importer = $this->get_importer($id);
215 $config = $this->get_config($importer->getId(), $tmp);
216
217 $filePath = $importer->getFile();
218 $file = new XMLFile($filePath, $config);
219 $file->processing(true);
220 $nodes = $file->get_node_list();
221 $results = [];
222
223 foreach ($nodes as $node) {
224 $config = $this->get_config($importer->getId(), $tmp);
225 $file = new XMLFile($filePath, $config);
226 $file->setRecordPath($node);
227 // TODO: Seperate record count, to when a node has been selected.
228 $results[$node] = 0; //$file->getRecordCount();
229 }
230
231 return $results;
232 }
233
234 /**
235 * Link import file to importer via post meta
236 *
237 * @param ImporterModel $id
238 * @param string $file_path
239 * @return integer Id of inserted file
240 */
241 public function link_importer_file($id, $file_path)
242 {
243 $importer = $this->get_importer($id);
244 $index = get_post_meta($importer->getId(), '_importer_files', true);
245 if (!$index) {
246 $index = 1;
247 }
248
249 $index++;
250
251 // Store uploads-relative paths so site moves / open_basedir changes do not break lookups.
252 $file_path = wp_normalize_path($file_path);
253 $relative = Filesystem::to_uploads_relative_path($file_path);
254 if ($relative !== '' && $relative !== $file_path) {
255 $file_path = $relative;
256 }
257
258 update_post_meta($importer->getId(), '_importer_files', $index);
259 update_post_meta($importer->getId(), '_importer_file_' . $index, $file_path);
260 return $index;
261 }
262
263 public function get_importer_file_prefix($importer)
264 {
265 $importer = $this->get_importer($importer);
266
267 $file_index = get_post_meta($importer->getId(), '_importer_files', true);
268 if (!$file_index) {
269 $file_index = 1;
270 }
271 $file_index++;
272
273 return $importer->getId() . '-' . intval($file_index) . '-' . wp_generate_password(12, false, false) . '-';
274 }
275
276 public function set_custom_upload_path($dir)
277 {
278 remove_filter('upload_dir', [$this, 'set_custom_upload_path']);
279
280 $path = $this->filesystem->get_temp_directory();
281 $url = $this->filesystem->get_temp_directory(true);
282
283 add_filter('upload_dir', [$this, 'set_custom_upload_path']);
284
285 $path .= '/uploads';
286 $url .= '/uploads';
287
288 if (!is_dir($path)) {
289 wp_mkdir_p($path);
290 }
291
292 if (!file_exists($path . '/.htaccess')) {
293 file_put_contents($path . '/.htaccess', "# Apache 2.4+
294 <IfModule mod_authz_core.c>
295 Require all denied
296 </IfModule>
297
298 # Apache 2.2 and older (or when mod_authz_core isn't available)
299 <IfModule !mod_authz_core.c>
300 Deny from all
301 </IfModule>");
302 }
303
304 if (!file_exists($path . '/index.html')) {
305 touch($path . '/index.html');
306 }
307
308 return array(
309 'path' => $path,
310 'url' => $url,
311 'subdir' => '/importwp/uploads',
312 ) + $dir;
313 }
314
315 public function upload_file($id, $file)
316 {
317 $importer = $this->get_importer($id);
318 Logger::setId($importer->getId());
319
320 $allowed_file_types = $this->event_handler->run('importer.allowed_file_types', [$importer->getAllowedFileTypes()]);
321
322 $prefix = $this->get_importer_file_prefix($importer);
323
324 $prefix_upload = function ($file) use ($prefix) {
325 $file['name'] = $prefix . $file['name'];
326 return $file;
327 };
328
329 add_filter('wp_handle_upload_prefilter', $prefix_upload);
330
331 $result = $this->filesystem->upload_file($file, $allowed_file_types);
332
333 remove_filter('wp_handle_upload_prefilter', $prefix_upload);
334
335 if (is_wp_error($result)) {
336 return $result;
337 }
338
339 $attachment_id = $this->insert_file_attachment($importer, $result['dest'], $result['type']);
340 if (is_wp_error($attachment_id)) {
341 return $attachment_id;
342 }
343
344 return $attachment_id;
345 }
346
347 public function remote_file($id, $source, $filetype = null)
348 {
349 $importer = $this->get_importer($id);
350 Logger::setId($importer->getId());
351
352 $allowed_file_types = $this->event_handler->run('importer.allowed_file_types', [$importer->getAllowedFileTypes()]);
353 $prefix = $this->get_importer_file_prefix($importer);
354
355 try {
356
357 if (preg_match('/^s?ftp?:\/\//', $source) === 1) {
358
359 if (preg_match('/^(?<protocol>s?ftp):\/\/(?:(?<user>[^\:@]+)(?:\:(?<pass>[^@]+))?@)?(?<host>[^\:\/]+)(?:\:(?<port>[0-9]+))?(?:\/(?<path>.*))$/', $source, $matches) !== 1) {
360
361 return new \WP_Error("IM_RM_FTP_PARSE", __("Unable to parse FTP connection string", 'jc-importer'));
362 }
363
364 $protocol = isset($matches['protocol']) ? $matches['protocol'] : 'ftp';
365 $user = isset($matches['user']) ? urldecode($matches['user']) : '';
366 $pass = isset($matches['pass']) ? urldecode($matches['pass']) : '';
367 $host = isset($matches['host']) ? $matches['host'] : false;
368 $port = isset($matches['port']) && !empty($matches['port']) ? $matches['port'] : intval(21);
369 $path = isset($matches['path']) ? $matches['path'] : false;
370
371 if (!$host) {
372 return new \WP_Error("IM_RM_FTP_HOST", __("Unable to parse ftp host from connection string", 'jc-importer'));
373 }
374
375 if (!$path) {
376 return new \WP_Error("IM_RM_FTP_HOST", __("Unable to parse ftp host from connection string", 'jc-importer'));
377 }
378
379 /**
380 * @var \ImportWP\Common\Ftp\Ftp $ftp
381 */
382 $ftp = Container::getInstance()->get('ftp');
383
384 $path = apply_filters('iwp/importer/remote_file', $path, $importer);
385 $path = apply_filters(sprintf('iwp/importer=%d/remote_file', $importer->getId()), $path, $importer);
386
387 $filter_connection_args = [
388 'user' => $user,
389 'pass' => $pass,
390 'host' => $host,
391 'port' => $port,
392 'path' => $path,
393 ];
394 $path = apply_filters(sprintf('iwp/importer/remote_file/source=%s', 'ftp'), $path, $importer, $filter_connection_args);
395 $path = apply_filters(sprintf('iwp/importer=%d/remote_file/source=ftp', $importer->getId(), 'ftp'), $path, $importer, $filter_connection_args);
396
397 if ($protocol == 'sftp') {
398
399 // require sftp package
400 require_once __DIR__ . '/../../../libs/autoload.php';
401
402 $sftp = new \phpseclib3\Net\SFTP($host, $port);
403 if (!$sftp->login($user, $pass)) {
404 return new \WP_Error('IWP_FTP_0', __("Unable to login to ftp server", 'jc-importer'));
405 }
406
407 $wp_upload_dir = wp_upload_dir();
408
409 $dest = wp_unique_filename($wp_upload_dir['path'], basename($path));
410 $wp_dest = $wp_upload_dir['path'] . '/' . $dest;
411
412 if (!$sftp->get($path, $wp_dest)) {
413 return new \WP_Error('IWP_FTP_2', sprintf(__('Unable to download: %s file via sftp.', 'jc-importer'), $path));
414 }
415
416 $result = array(
417 'dest' => $wp_dest,
418 'type' => $this->filesystem->get_filetype($wp_dest),
419 'mime' => $this->filesystem->get_file_mime($wp_dest)
420 );
421 } else {
422 $result = $ftp->download_file($path, $host, $user, $pass, false, $port);
423 }
424 } else {
425
426 $result = $this->filesystem->download_file($source, $filetype, $allowed_file_types, null, $prefix);
427 }
428 } catch (\Exception $e) {
429 return new \WP_Error($e->getCode(), $e->getMessage());
430 }
431
432 if (is_wp_error($result)) {
433 return $result;
434 }
435
436 $attachment_id = $this->insert_file_attachment($importer, $result['dest'], $result['type']);
437 if (is_wp_error($attachment_id)) {
438 return $attachment_id;
439 }
440
441 return $attachment_id;
442 }
443
444 public function local_file($id, $source, $filetype = null)
445 {
446 $importer = $this->get_importer($id);
447 Logger::setId($importer->getId());
448
449 $allowed_bases = apply_filters('iwp/importer/local_file/allowed_directories', [
450 realpath(WP_CONTENT_DIR) . DIRECTORY_SEPARATOR
451 ]);
452
453 $source = realpath($source);
454
455 $is_allowed = false;
456 foreach ($allowed_bases as $allowed_base) {
457 if ($allowed_base && strpos($source, $allowed_base) === 0) {
458 $is_allowed = true;
459 break;
460 }
461 }
462
463 if (!$source || !$is_allowed) {
464 return new \WP_Error('IWP_LOCAL_FILE_1', __('Access to this file path is not allowed.', 'jc-importer'));
465 }
466
467 $allowed_file_types = $this->event_handler->run('importer.allowed_file_types', [$importer->getAllowedFileTypes()]);
468 $prefix = $this->get_importer_file_prefix($importer);
469 $result = $this->filesystem->copy_file($source, $allowed_file_types, null, $prefix, $filetype);
470
471 if (is_wp_error($result)) {
472 return $result;
473 }
474
475 $attachment_id = $this->insert_file_attachment($importer, $result['dest'], $result['type']);
476 if (is_wp_error($attachment_id)) {
477 return $attachment_id;
478 }
479
480 return $attachment_id;
481 }
482
483 /**
484 * Add uploaded file to importer
485 *
486 * Insert file record in database, set as current import file.
487 *
488 * @param integer|ImporterModel $id Importer to attach file
489 * @param string $file_path File location on server
490 * @param string $file_type Type of file
491 *
492 * @return \WP_Error|int Id of file
493 */
494 private function insert_file_attachment($id, $file_path, $file_type)
495 {
496 $importer_model = $this->get_importer($id);
497 Logger::setId($importer_model->getId());
498
499 // Allow the modification of file path
500 $file_path = wp_normalize_path($file_path);
501 $file_path = apply_filters('iwp/importer/file_uploaded/file_path', $file_path, $importer_model);
502
503 $file_id = $this->link_importer_file($id, $file_path);
504 if (!$file_id) {
505 return new \WP_Error('IWP_IM_01', __('Unable to link importer file', 'jc-importer'));
506 }
507
508 if (is_null($importer_model->getParser())) {
509 $importer_model->setParser($file_type);
510 }
511
512 $importer_model->setFileId($file_id);
513 $importer_model->save();
514
515 do_action('iwp/importer/file_uploaded', $file_path, $importer_model);
516
517 return $file_id;
518 }
519
520 /**
521 * Clear config files
522 *
523 * @param int $id
524 * @return void
525 */
526 public function clear_config_files($id, $tmp = false, $all = true)
527 {
528 $config_path = $this->get_config_path($id, $tmp);
529 if (file_exists($config_path)) {
530
531 unlink($config_path);
532 if (true === $all) {
533 foreach (glob($config_path . '*') as $file) {
534
535 // don't remove status file
536 if (basename($file) === basename($config_path) . '.status') {
537 continue;
538 }
539
540 unlink($file);
541 }
542 }
543 }
544 }
545
546 public function get_config($importer, $tmp = false)
547 {
548 $config_path = $this->get_config_path($importer, $tmp);
549 $config = new Config($config_path);
550
551
552 $importer = $this->get_importer($importer);
553 $file_encoding = $importer->getFileSetting('file_encoding');
554
555 // file encoding
556 $config->set('file_encoding', apply_filters('iwp/importer/file_encoding', $file_encoding, $importer));
557
558 return $config;
559 }
560
561 public function get_session_path($id, $session, $max_depth = 4)
562 {
563 $base = $this->filesystem->get_temp_directory() . DIRECTORY_SEPARATOR;
564
565 $base .= str_pad($id, 2, STR_PAD_LEFT) . DIRECTORY_SEPARATOR;
566 if (!file_exists($base)) {
567 if (!is_writable(dirname($base)) || !mkdir($base)) {
568 throw new \Exception(sprintf(__("Unable to create directory: %s", 'jc-importer'), $base));
569 }
570 }
571
572 for ($i = 0; $i < ceil(strlen($session) / 2); $i++) {
573 $base .= substr($session, $i * 2, 2) . DIRECTORY_SEPARATOR;
574 if (!file_exists($base)) {
575
576 if (!is_writable(dirname($base)) || !mkdir($base)) {
577 throw new \Exception(sprintf(__("Unable to create directory: %s", 'jc-importer'), $base));
578 }
579 }
580
581 if ($i >= $max_depth - 2) {
582 break;
583 }
584 }
585 return $base;
586 }
587
588 public function get_config_path($id, $tmp = false, $session_id = null)
589 {
590 $importer = $this->get_importer($id);
591
592 $key = 'config-%d.json';
593 if ($tmp) {
594 $key = 'temp-config-%d.json';
595 }
596
597 $base = !is_null($session_id) ? $this->get_session_path($id, $session_id) : $this->filesystem->get_temp_directory() . DIRECTORY_SEPARATOR;
598
599 return $base . sprintf($key, $importer->getId());
600 }
601
602 public function import($id, $user, $session = null)
603 {
604 Logger::timer();
605
606 $importer_data = $this->get_importer($id);
607 $importer_id = $importer_data->getId();
608
609 // store current importer
610 iwp()->importer = $importer_data;
611
612 $config_data = get_option('iwp_importer_config_' . $importer_id, []);
613
614 $this->event_handler->run('importer_manager.import', [$importer_data]);
615
616 $state = new ImporterState($importer_id, $user);
617
618 try {
619
620 Logger::debug('IM -init_state');
621
622 // 1. Set State Session, and load its state
623 $state->init($session);
624
625 // if this is a new session, clear config files
626 if ($state->has_status('init')) {
627 // rest importer log.
628 Logger::clear($importer_id);
629 Logger::debug('IM -clear_config_files');
630 $this->clear_config_files($importer_id, false, true);
631 $config_data['features'] = [
632 'session_table' => true
633 ];
634 }
635
636 Logger::debug('IM -get_config');
637 $config = $this->get_config($importer_data);
638
639 // template
640 Logger::debug('IM -get_importer_template');
641 $template = $this->get_importer_template($importer_data);
642
643 Logger::debug('IM -register_hooks');
644 $template->register_hooks($importer_data);
645
646 // permission
647 Logger::debug('IM -permissions');
648 $permission = new Permission($importer_data);
649
650 // mapper
651 Logger::debug('IM -get_importer_mapper');
652 $mapper = $this->get_importer_mapper($importer_data, $template, $permission);
653
654 // if this is a new session, build config
655 if ($state->has_status('init')) {
656
657 Logger::debug('IM -generate_config');
658
659 $config_data['data'] = $template->config_field_map($importer_data->getMap());
660 $config->set('data', $config_data['data']);
661
662 $config_data['id'] = $state->get_session();
663
664 // This is used for storing version on imported records
665 update_post_meta($importer_id, '_iwp_session', $config_data['id']);
666
667 // Increase Version
668 $version = get_post_meta($importer_id, '_iwp_version', true);
669 if ($version !== false) {
670 $version++;
671 } else {
672 $version = 0;
673 }
674 update_post_meta($importer_id, '_iwp_version', $version);
675 $config_data['version'] = $version;
676
677 /**
678 * Fetch new file if setting is checked
679 * @since 2.7.15
680 */
681 $run_fetch_file = $importer_data->getSetting('run_fetch') || false;
682 $run_fetch_file = apply_filters('iwp/importer/run_fetch_file', $run_fetch_file);
683 if ($run_fetch_file) {
684
685 add_filter('upload_dir', [$this, 'set_custom_upload_path']);
686
687 $datasource = $importer_data->getDatasource();
688 switch ($datasource) {
689 case 'remote':
690 $raw_source = $importer_data->getDatasourceSetting('remote_url');
691 $source = apply_filters('iwp/importer/datasource', $raw_source, $raw_source, $importer_data);
692 $source = apply_filters('iwp/importer/datasource/remote', $source, $raw_source, $importer_data);
693 $attachment_id = $this->remote_file($importer_data, $source, $importer_data->getParser());
694 break;
695 case 'local':
696 $raw_source = $importer_data->getDatasourceSetting('local_url');
697 $source = apply_filters('iwp/importer/datasource', $raw_source, $raw_source, $importer_data);
698 $source = apply_filters('iwp/importer/datasource/local', $source, $raw_source, $importer_data);
699 $attachment_id = $this->local_file($importer_data, $source, $importer_data->getParser());
700 break;
701 default:
702 // TODO: record error
703 $attachment_id = new \WP_Error('IWP_CRON_1', sprintf(__('Unable to get new file using datasource: %s', 'jc-importer'), $datasource));
704 break;
705 }
706
707 remove_filter('upload_dir', [$this, 'set_custom_upload_path']);
708
709 if (is_wp_error($attachment_id)) {
710 throw new \Exception(sprintf(__('Importer Datasource: %s', 'jc-importer'), $attachment_id->get_error_message()));
711 }
712 }
713 }
714
715 $start = 0;
716
717 // get parser
718 if ($importer_data->getParser() === 'csv') {
719 Logger::debug('IM -get_csv_file');
720 $file = $this->get_csv_file($importer_data, $config);
721 Logger::debug('IM -load_parser');
722 $parser = new CSVParser($file);
723 if (true === $importer_data->getFileSetting('show_headings')) {
724 $start = 1;
725 }
726 } elseif ($importer_data->getParser() === 'xml') {
727 Logger::debug('IM -get_xml_file');
728 $file = $this->get_xml_file($importer_data, $config);
729 Logger::debug('IM -load_parser');
730 $parser = new XMLParser($file);
731 } else {
732 $parser = apply_filters('iwp/importer/init_parser', false, $importer_data, $config);
733 }
734
735 // if this is a new session, set start / end rows to state
736 if ($state->has_status('init')) {
737
738 Logger::debug('IM -get_record_count');
739 $end = $parser->file()->getRecordCount();
740
741 // Capture cancelled status from file processor
742 $raw_state = ImporterState::get_state($importer_data->getId());
743 if ($raw_state['status'] === 'cancelled') {
744 return $raw_state;
745 }
746
747 $config_data['start'] = $this->get_start($importer_data, $start);
748 $config_data['end'] = $this->get_end($importer_data, $config_data['start'], $end);
749
750 update_option('iwp_importer_config_' . $importer_id, $config_data);
751
752 Logger::debug('IM -update_state');
753 $state->update(function ($state) use ($config_data) {
754
755 Logger::debug('IM -update_state=running');
756
757 $state['id'] = $config_data['id'];
758 $state['status'] = 'running';
759 $state['progress']['import']['start'] = $config_data['start'];
760 $state['progress']['import']['end'] = $config_data['end'];
761 return $state;
762 });
763
764 Logger::debug('IM -write_status_session_to_file');
765 Util::write_status_session_to_file($id, $state);
766
767 do_action('iwp/importer/init', $importer_data);
768 }
769
770
771 add_filter('iwp/importer/mapper/hash_check_enabled', function ($enabled) use ($importer_data) {
772 return $importer_data->getSetting('hash_check');
773 });
774
775 Logger::debug('IM -import');
776 $importer = new \ImportWP\Common\Importer\Importer($config);
777 $importer->parser($parser);
778 $importer->mapper($mapper);
779 $importer->from($config_data['start']);
780 $importer->to($config_data['end']);
781 $importer->filter($importer_data->getFilters());
782 $importer->import($importer_id, $user, $state);
783 Logger::debug('IM -import_complete');
784 } catch (\Exception $e) {
785
786 // TODO: Missing template errors are currently not being logged to history, possibly others?
787 Logger::error('import -error=' . $e->getMessage(), $importer_id);
788 $state->error($e);
789 Util::write_status_session_to_file($id, $state);
790 return $state->get_raw();
791 }
792
793 /**
794 * @var Properties $properties
795 */
796 $properties = Container::getInstance()->get('properties');
797
798 // rotate files to not fill up server
799 $importer_data->limit_importer_files($properties->file_rotation);
800 $this->prune_importer_logs($importer_data, $properties->log_rotation);
801
802 $template->unregister_hooks();
803
804 $this->event_handler->run('importer_manager.import_shutdown', [$importer_data]);
805
806 return $state->update(function ($data) {
807 $data['duration'] = floatval($data['duration']) + Logger::timer();
808 return $data;
809 })->get_raw();
810 }
811
812 public function pause_import($importer_id, $paused)
813 {
814 // TODO: set flag for paused.
815 $state = ImporterState::get_state($importer_id);
816 if ($paused === 'no') {
817 ImporterState::clear_flag($importer_id);
818 $state['status'] = 'running';
819 } else {
820 ImporterState::set_paused($importer_id);
821 $state['status'] = 'paused';
822 }
823
824 // good chance this will be overwritten
825 ImporterState::set_state($importer_id, $state);
826
827 return $state;
828 }
829
830 public function stop_import($importer_id)
831 {
832 ImporterState::set_cancelled($importer_id);
833
834 // good chance this will be overwritten
835 $state = ImporterState::get_state($importer_id);
836 $state['status'] = 'cancelled';
837 ImporterState::set_state($importer_id, $state);
838
839 return $state;
840 }
841
842 public function get_start($importer_data, $start)
843 {
844 $tmp_start = $importer_data->getStartRow();
845 if (!is_null($tmp_start) && "" !== $tmp_start) {
846 $tmp_start = intval($tmp_start);
847
848 if ($tmp_start > $start) {
849 $start = $tmp_start;
850 }
851 }
852
853 return $start;
854 }
855
856 public function get_end($importer_data, $start, $end)
857 {
858 $tmp_max_row = $importer_data->getMaxRow();
859 if (!is_null($tmp_max_row) && $tmp_max_row !== '') {
860 $tmp_end = $start + intval($tmp_max_row);
861 if ($tmp_end < $end) {
862 $end = $tmp_end;
863 }
864 }
865
866 return $end;
867 }
868
869 public function get_importer_template($id)
870 {
871 $importer_model = $this->get_importer($id);
872 $templates = $this->get_templates();
873 $template_name = $importer_model->getTemplate();
874
875 if (!isset($templates[$template_name])) {
876 $exception_msg = sprintf(__("Unable to locate importer template: %s", 'jc-importer'), $template_name);
877 Logger::error('import -get_importer_template=' . $exception_msg, $importer_model->getId());
878 throw new \Exception($exception_msg);
879 }
880
881 return $this->template_manager->load_template($templates[$template_name]);
882 }
883
884 /**
885 * Get importer mapper
886 *
887 * @param int $id
888 * @param Template $template
889 * @param Permission $permission
890 * @return MapperInterface
891 */
892 public function get_importer_mapper($id, $template, $permission = null)
893 {
894 $importer = $this->get_importer($id);
895 $mapper_name = $template->get_mapper();
896
897 $mappers = $this->get_mappers();
898 return isset($mappers[$mapper_name]) ? new $mappers[$mapper_name]($importer, $template, $permission) : false;
899 }
900
901 public function get_mappers()
902 {
903 $mappers = $this->event_handler->run('mappers.register', [[]]); // apply_filters('iwp/mappers/register', []);
904 $mappers = array_merge($mappers, [
905 'post' => PostMapper::class,
906 'user' => UserMapper::class,
907 'term' => TermMapper::class,
908 'attachment' => AttachmentMapper::class,
909 'comment' => CommentMapper::class,
910 ]);
911 return $mappers;
912 }
913
914 public function get_mapper($key)
915 {
916 $mappers = $this->get_mappers();
917 if (!isset($mappers[$key])) {
918 return new \WP_Error('IWP_IM_1', sprintf(__('Unable to locate mapper: %s', 'jc-importer'), $key));
919 }
920
921 return $mappers[$key];
922 }
923
924 public function get_templates()
925 {
926 $templates = $this->event_handler->run('templates.register', [[]]);
927 $templates = array_merge($templates, [
928 'post' => PostTemplate::class,
929 'page' => PageTemplate::class,
930 'user' => UserTemplate::class,
931 'term' => TermTemplate::class,
932 'attachment' => AttachmentTemplate::class,
933 'comment' => CommentTemplate::class,
934 'custom-post-type' => CustomPostTypeTemplate::class,
935 ]);
936 return $templates;
937 }
938
939 public function get_template($key)
940 {
941 $templates = $this->get_templates();
942 if (!isset($templates[$key])) {
943 return new \WP_Error('IWP_IM_1', sprintf(__('Unable to locate template: %s', 'jc-importer'), $key));
944 }
945
946 return $templates[$key];
947 }
948
949 public function get_importer_debug_log(ImporterModel $importer_data, $page = 0, $per_page = -1)
950 {
951 $file_path = Logger::getLogFile($importer_data->getId());
952
953 $line_counter = 0;
954 $lines = [];
955 $start = $end = -1;
956
957 if ($per_page > 0) {
958 $start = ($page - 1) * $per_page;
959 $end = $start + $per_page;
960 }
961
962 if (file_exists($file_path)) {
963 $fh = fopen($file_path, 'r');
964 if ($fh !== false) {
965 while (($data = fgetcsv($fh)) !== false) {
966 if ($line_counter === $end) {
967 return $lines;
968 } elseif ($per_page === -1 || $line_counter >= $start) {
969 $lines[] = $data;
970 }
971
972 $line_counter++;
973 }
974 fclose($fh);
975 }
976 }
977 return $lines;
978 }
979
980 public function prune_importer_logs($importer_model, $limit)
981 {
982 $limit = intval($limit);
983 if ($limit <= -1) {
984 return;
985 }
986
987 $file_path = Util::get_importer_status_file_path($importer_model->getId());
988 $tmp_file_path = Util::get_importer_status_file_path($importer_model->getId()) . '.tmp';
989 $lines = $this->get_importer_logs($importer_model);
990
991 if (count($lines) > $limit) {
992
993 require_once(ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php');
994 require_once(ABSPATH . 'wp-admin/includes/class-wp-filesystem-direct.php');
995 $fileSystemDirect = new \WP_Filesystem_Direct(false);
996
997 $fh = fopen($tmp_file_path, 'w');
998 for ($i = 0; $i < count($lines); $i++) {
999
1000 if ($i < count($lines) - $limit) {
1001 $log_file_path = Util::get_importer_log_file_path($importer_model->getId(), $lines[$i]['id']);
1002 if ($fileSystemDirect->exists($log_file_path)) {
1003
1004 $fileSystemDirect->delete($log_file_path);
1005
1006 $tmp = $log_file_path;
1007 for ($j = 0; $j < 3; $j++) {
1008 $tmp = dirname($tmp);
1009 $sub_files = $fileSystemDirect->dirlist($log_file_path);
1010 if (empty($sub_files)) {
1011 $fileSystemDirect->rmdir($tmp);
1012 }
1013 }
1014 }
1015 } else {
1016 fputs($fh, json_encode($lines[$i]) . "\n");
1017 }
1018 }
1019 fclose($fh);
1020
1021 if ($fileSystemDirect->move($tmp_file_path, $file_path, true)) {
1022 $fileSystemDirect->delete($tmp_file_path);
1023 }
1024 }
1025 }
1026
1027 public function get_importer_logs(ImporterModel $importer_data, $page = 0, $per_page = -1)
1028 {
1029 $file_path = Util::get_importer_status_file_path($importer_data->getId());
1030
1031 $line_counter = 0;
1032 $lines = [];
1033
1034 $start = $end = -1;
1035
1036 if ($per_page > 0) {
1037 $start = ($page - 1) * $per_page;
1038 $end = $start + $per_page;
1039 }
1040
1041 if (file_exists($file_path)) {
1042 $fh = fopen($file_path, 'r');
1043 if ($fh !== false) {
1044 while (($data = fgets($fh)) !== false) {
1045 if ($data[strlen($data) - 1] === "\n") {
1046 $data = substr($data, 0, strlen($data) - 1);
1047 }
1048
1049 if ($line_counter === $end) {
1050 return $lines;
1051 } elseif ($per_page === -1 || $line_counter >= $start) {
1052 $lines[] = json_decode($data, true);
1053 }
1054
1055 $line_counter++;
1056 }
1057 fclose($fh);
1058 }
1059 }
1060 return $lines;
1061 }
1062
1063 public function get_importer_log(ImporterModel $importer_data, $session_id, $page = 0, $per_page = -1)
1064 {
1065 $file_path = Util::get_importer_log_file_path($importer_data->getId(), $session_id);
1066
1067 $line_counter = 0;
1068 $lines = [];
1069 $start = $end = -1;
1070
1071 if ($per_page > 0) {
1072 $start = ($page - 1) * $per_page;
1073 $end = $start + $per_page;
1074 }
1075
1076 if (file_exists($file_path)) {
1077 $fh = fopen($file_path, 'r');
1078 if ($fh !== false) {
1079 while (($data = fgetcsv($fh)) !== false) {
1080 if ($line_counter === $end) {
1081 return $lines;
1082 } elseif ($per_page === -1 || $line_counter >= $start) {
1083 $lines[] = $data;
1084 }
1085
1086 $line_counter++;
1087 }
1088 fclose($fh);
1089 }
1090 }
1091 return $lines;
1092 }
1093
1094 public function get_importer_status_report(ImporterModel $immpoter_data, $session)
1095 {
1096 $logs = $this->get_importer_logs($immpoter_data);
1097 foreach ($logs as $log) {
1098 if ($log && isset($log['id']) && $log['id'] === $session) {
1099 return $log;
1100 }
1101 }
1102 return false;
1103 }
1104 }
1105