PluginProbe
Import WP – CSV & XML Import Export for WordPress / 2.14.23
Import WP – CSV & XML Import Export for WordPress v2.14.23
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.23, at class/Common/Importer/ImporterManager.php

1,098 lines 36.7 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 update_post_meta($importer->getId(), '_importer_files', $index);
252 update_post_meta($importer->getId(), '_importer_file_' . $index, $file_path);
253 return $index;
254 }
255
256 public function get_importer_file_prefix($importer)
257 {
258 $importer = $this->get_importer($importer);
259
260 $file_index = get_post_meta($importer->getId(), '_importer_files', true);
261 if (!$file_index) {
262 $file_index = 1;
263 }
264 $file_index++;
265
266 return $importer->getId() . '-' . intval($file_index) . '-' . wp_generate_password(12, false, false) . '-';
267 }
268
269 public function set_custom_upload_path($dir)
270 {
271 remove_filter('upload_dir', [$this, 'set_custom_upload_path']);
272
273 $path = $this->filesystem->get_temp_directory();
274 $url = $this->filesystem->get_temp_directory(true);
275
276 add_filter('upload_dir', [$this, 'set_custom_upload_path']);
277
278 $path .= '/uploads';
279 $url .= '/uploads';
280
281 if (!is_dir($path)) {
282 wp_mkdir_p($path);
283 }
284
285 if (!file_exists($path . '/.htaccess')) {
286 file_put_contents($path . '/.htaccess', "# Apache 2.4+
287 <IfModule mod_authz_core.c>
288 Require all denied
289 </IfModule>
290
291 # Apache 2.2 and older (or when mod_authz_core isn't available)
292 <IfModule !mod_authz_core.c>
293 Deny from all
294 </IfModule>");
295 }
296
297 if (!file_exists($path . '/index.html')) {
298 touch($path . '/index.html');
299 }
300
301 return array(
302 'path' => $path,
303 'url' => $url,
304 'subdir' => '/importwp/uploads',
305 ) + $dir;
306 }
307
308 public function upload_file($id, $file)
309 {
310 $importer = $this->get_importer($id);
311 Logger::setId($importer->getId());
312
313 $allowed_file_types = $this->event_handler->run('importer.allowed_file_types', [$importer->getAllowedFileTypes()]);
314
315 $prefix = $this->get_importer_file_prefix($importer);
316
317 $prefix_upload = function ($file) use ($prefix) {
318 $file['name'] = $prefix . $file['name'];
319 return $file;
320 };
321
322 add_filter('wp_handle_upload_prefilter', $prefix_upload);
323
324 $result = $this->filesystem->upload_file($file, $allowed_file_types);
325
326 remove_filter('wp_handle_upload_prefilter', $prefix_upload);
327
328 if (is_wp_error($result)) {
329 return $result;
330 }
331
332 $attachment_id = $this->insert_file_attachment($importer, $result['dest'], $result['type']);
333 if (is_wp_error($attachment_id)) {
334 return $attachment_id;
335 }
336
337 return $attachment_id;
338 }
339
340 public function remote_file($id, $source, $filetype = null)
341 {
342 $importer = $this->get_importer($id);
343 Logger::setId($importer->getId());
344
345 $allowed_file_types = $this->event_handler->run('importer.allowed_file_types', [$importer->getAllowedFileTypes()]);
346 $prefix = $this->get_importer_file_prefix($importer);
347
348 try {
349
350 if (preg_match('/^s?ftp?:\/\//', $source) === 1) {
351
352 if (preg_match('/^(?<protocol>s?ftp):\/\/(?:(?<user>[^\:@]+)(?:\:(?<pass>[^@]+))?@)?(?<host>[^\:\/]+)(?:\:(?<port>[0-9]+))?(?:\/(?<path>.*))$/', $source, $matches) !== 1) {
353
354 return new \WP_Error("IM_RM_FTP_PARSE", __("Unable to parse FTP connection string", 'jc-importer'));
355 }
356
357 $protocol = isset($matches['protocol']) ? $matches['protocol'] : 'ftp';
358 $user = isset($matches['user']) ? urldecode($matches['user']) : '';
359 $pass = isset($matches['pass']) ? urldecode($matches['pass']) : '';
360 $host = isset($matches['host']) ? $matches['host'] : false;
361 $port = isset($matches['port']) && !empty($matches['port']) ? $matches['port'] : intval(21);
362 $path = isset($matches['path']) ? $matches['path'] : false;
363
364 if (!$host) {
365 return new \WP_Error("IM_RM_FTP_HOST", __("Unable to parse ftp host from connection string", 'jc-importer'));
366 }
367
368 if (!$path) {
369 return new \WP_Error("IM_RM_FTP_HOST", __("Unable to parse ftp host from connection string", 'jc-importer'));
370 }
371
372 /**
373 * @var \ImportWP\Common\Ftp\Ftp $ftp
374 */
375 $ftp = Container::getInstance()->get('ftp');
376
377 $path = apply_filters('iwp/importer/remote_file', $path, $importer);
378 $path = apply_filters(sprintf('iwp/importer=%d/remote_file', $importer->getId()), $path, $importer);
379
380 $filter_connection_args = [
381 'user' => $user,
382 'pass' => $pass,
383 'host' => $host,
384 'port' => $port,
385 'path' => $path,
386 ];
387 $path = apply_filters(sprintf('iwp/importer/remote_file/source=%s', 'ftp'), $path, $importer, $filter_connection_args);
388 $path = apply_filters(sprintf('iwp/importer=%d/remote_file/source=ftp', $importer->getId(), 'ftp'), $path, $importer, $filter_connection_args);
389
390 if ($protocol == 'sftp') {
391
392 // require sftp package
393 require_once __DIR__ . '/../../../libs/autoload.php';
394
395 $sftp = new \phpseclib3\Net\SFTP($host, $port);
396 if (!$sftp->login($user, $pass)) {
397 return new \WP_Error('IWP_FTP_0', __("Unable to login to ftp server", 'jc-importer'));
398 }
399
400 $wp_upload_dir = wp_upload_dir();
401
402 $dest = wp_unique_filename($wp_upload_dir['path'], basename($path));
403 $wp_dest = $wp_upload_dir['path'] . '/' . $dest;
404
405 if (!$sftp->get($path, $wp_dest)) {
406 return new \WP_Error('IWP_FTP_2', sprintf(__('Unable to download: %s file via sftp.', 'jc-importer'), $path));
407 }
408
409 $result = array(
410 'dest' => $wp_dest,
411 'type' => $this->filesystem->get_filetype($wp_dest),
412 'mime' => $this->filesystem->get_file_mime($wp_dest)
413 );
414 } else {
415 $result = $ftp->download_file($path, $host, $user, $pass, false, $port);
416 }
417 } else {
418
419 $result = $this->filesystem->download_file($source, $filetype, $allowed_file_types, null, $prefix);
420 }
421 } catch (\Exception $e) {
422 return new \WP_Error($e->getCode(), $e->getMessage());
423 }
424
425 if (is_wp_error($result)) {
426 return $result;
427 }
428
429 $attachment_id = $this->insert_file_attachment($importer, $result['dest'], $result['type']);
430 if (is_wp_error($attachment_id)) {
431 return $attachment_id;
432 }
433
434 return $attachment_id;
435 }
436
437 public function local_file($id, $source, $filetype = null)
438 {
439 $importer = $this->get_importer($id);
440 Logger::setId($importer->getId());
441
442 $allowed_bases = apply_filters('iwp/importer/local_file/allowed_directories', [
443 realpath(WP_CONTENT_DIR) . DIRECTORY_SEPARATOR
444 ]);
445
446 $source = realpath($source);
447
448 $is_allowed = false;
449 foreach ($allowed_bases as $allowed_base) {
450 if ($allowed_base && strpos($source, $allowed_base) === 0) {
451 $is_allowed = true;
452 break;
453 }
454 }
455
456 if (!$source || !$is_allowed) {
457 return new \WP_Error('IWP_LOCAL_FILE_1', __('Access to this file path is not allowed.', 'jc-importer'));
458 }
459
460 $allowed_file_types = $this->event_handler->run('importer.allowed_file_types', [$importer->getAllowedFileTypes()]);
461 $prefix = $this->get_importer_file_prefix($importer);
462 $result = $this->filesystem->copy_file($source, $allowed_file_types, null, $prefix, $filetype);
463
464 if (is_wp_error($result)) {
465 return $result;
466 }
467
468 $attachment_id = $this->insert_file_attachment($importer, $result['dest'], $result['type']);
469 if (is_wp_error($attachment_id)) {
470 return $attachment_id;
471 }
472
473 return $attachment_id;
474 }
475
476 /**
477 * Add uploaded file to importer
478 *
479 * Insert file record in database, set as current import file.
480 *
481 * @param integer|ImporterModel $id Importer to attach file
482 * @param string $file_path File location on server
483 * @param string $file_type Type of file
484 *
485 * @return \WP_Error|int Id of file
486 */
487 private function insert_file_attachment($id, $file_path, $file_type)
488 {
489 $importer_model = $this->get_importer($id);
490 Logger::setId($importer_model->getId());
491
492 // Allow the modification of file path
493 $file_path = wp_normalize_path($file_path);
494 $file_path = apply_filters('iwp/importer/file_uploaded/file_path', $file_path, $importer_model);
495
496 $file_id = $this->link_importer_file($id, $file_path);
497 if (!$file_id) {
498 return new \WP_Error('IWP_IM_01', __('Unable to link importer file', 'jc-importer'));
499 }
500
501 if (is_null($importer_model->getParser())) {
502 $importer_model->setParser($file_type);
503 }
504
505 $importer_model->setFileId($file_id);
506 $importer_model->save();
507
508 do_action('iwp/importer/file_uploaded', $file_path, $importer_model);
509
510 return $file_id;
511 }
512
513 /**
514 * Clear config files
515 *
516 * @param int $id
517 * @return void
518 */
519 public function clear_config_files($id, $tmp = false, $all = true)
520 {
521 $config_path = $this->get_config_path($id, $tmp);
522 if (file_exists($config_path)) {
523
524 unlink($config_path);
525 if (true === $all) {
526 foreach (glob($config_path . '*') as $file) {
527
528 // don't remove status file
529 if (basename($file) === basename($config_path) . '.status') {
530 continue;
531 }
532
533 unlink($file);
534 }
535 }
536 }
537 }
538
539 public function get_config($importer, $tmp = false)
540 {
541 $config_path = $this->get_config_path($importer, $tmp);
542 $config = new Config($config_path);
543
544
545 $importer = $this->get_importer($importer);
546 $file_encoding = $importer->getFileSetting('file_encoding');
547
548 // file encoding
549 $config->set('file_encoding', apply_filters('iwp/importer/file_encoding', $file_encoding, $importer));
550
551 return $config;
552 }
553
554 public function get_session_path($id, $session, $max_depth = 4)
555 {
556 $base = $this->filesystem->get_temp_directory() . DIRECTORY_SEPARATOR;
557
558 $base .= str_pad($id, 2, STR_PAD_LEFT) . DIRECTORY_SEPARATOR;
559 if (!file_exists($base)) {
560 if (!is_writable(dirname($base)) || !mkdir($base)) {
561 throw new \Exception(sprintf(__("Unable to create directory: %s", 'jc-importer'), $base));
562 }
563 }
564
565 for ($i = 0; $i < ceil(strlen($session) / 2); $i++) {
566 $base .= substr($session, $i * 2, 2) . DIRECTORY_SEPARATOR;
567 if (!file_exists($base)) {
568
569 if (!is_writable(dirname($base)) || !mkdir($base)) {
570 throw new \Exception(sprintf(__("Unable to create directory: %s", 'jc-importer'), $base));
571 }
572 }
573
574 if ($i >= $max_depth - 2) {
575 break;
576 }
577 }
578 return $base;
579 }
580
581 public function get_config_path($id, $tmp = false, $session_id = null)
582 {
583 $importer = $this->get_importer($id);
584
585 $key = 'config-%d.json';
586 if ($tmp) {
587 $key = 'temp-config-%d.json';
588 }
589
590 $base = !is_null($session_id) ? $this->get_session_path($id, $session_id) : $this->filesystem->get_temp_directory() . DIRECTORY_SEPARATOR;
591
592 return $base . sprintf($key, $importer->getId());
593 }
594
595 public function import($id, $user, $session = null)
596 {
597 Logger::timer();
598
599 $importer_data = $this->get_importer($id);
600 $importer_id = $importer_data->getId();
601
602 // store current importer
603 iwp()->importer = $importer_data;
604
605 $config_data = get_option('iwp_importer_config_' . $importer_id, []);
606
607 $this->event_handler->run('importer_manager.import', [$importer_data]);
608
609 $state = new ImporterState($importer_id, $user);
610
611 try {
612
613 Logger::debug('IM -init_state');
614
615 // 1. Set State Session, and load its state
616 $state->init($session);
617
618 // if this is a new session, clear config files
619 if ($state->has_status('init')) {
620 // rest importer log.
621 Logger::clear($importer_id);
622 Logger::debug('IM -clear_config_files');
623 $this->clear_config_files($importer_id, false, true);
624 $config_data['features'] = [
625 'session_table' => true
626 ];
627 }
628
629 Logger::debug('IM -get_config');
630 $config = $this->get_config($importer_data);
631
632 // template
633 Logger::debug('IM -get_importer_template');
634 $template = $this->get_importer_template($importer_data);
635
636 Logger::debug('IM -register_hooks');
637 $template->register_hooks($importer_data);
638
639 // permission
640 Logger::debug('IM -permissions');
641 $permission = new Permission($importer_data);
642
643 // mapper
644 Logger::debug('IM -get_importer_mapper');
645 $mapper = $this->get_importer_mapper($importer_data, $template, $permission);
646
647 // if this is a new session, build config
648 if ($state->has_status('init')) {
649
650 Logger::debug('IM -generate_config');
651
652 $config_data['data'] = $template->config_field_map($importer_data->getMap());
653 $config->set('data', $config_data['data']);
654
655 $config_data['id'] = $state->get_session();
656
657 // This is used for storing version on imported records
658 update_post_meta($importer_id, '_iwp_session', $config_data['id']);
659
660 // Increase Version
661 $version = get_post_meta($importer_id, '_iwp_version', true);
662 if ($version !== false) {
663 $version++;
664 } else {
665 $version = 0;
666 }
667 update_post_meta($importer_id, '_iwp_version', $version);
668 $config_data['version'] = $version;
669
670 /**
671 * Fetch new file if setting is checked
672 * @since 2.7.15
673 */
674 $run_fetch_file = $importer_data->getSetting('run_fetch') || false;
675 $run_fetch_file = apply_filters('iwp/importer/run_fetch_file', $run_fetch_file);
676 if ($run_fetch_file) {
677
678 add_filter('upload_dir', [$this, 'set_custom_upload_path']);
679
680 $datasource = $importer_data->getDatasource();
681 switch ($datasource) {
682 case 'remote':
683 $raw_source = $importer_data->getDatasourceSetting('remote_url');
684 $source = apply_filters('iwp/importer/datasource', $raw_source, $raw_source, $importer_data);
685 $source = apply_filters('iwp/importer/datasource/remote', $source, $raw_source, $importer_data);
686 $attachment_id = $this->remote_file($importer_data, $source, $importer_data->getParser());
687 break;
688 case 'local':
689 $raw_source = $importer_data->getDatasourceSetting('local_url');
690 $source = apply_filters('iwp/importer/datasource', $raw_source, $raw_source, $importer_data);
691 $source = apply_filters('iwp/importer/datasource/local', $source, $raw_source, $importer_data);
692 $attachment_id = $this->local_file($importer_data, $source, $importer_data->getParser());
693 break;
694 default:
695 // TODO: record error
696 $attachment_id = new \WP_Error('IWP_CRON_1', sprintf(__('Unable to get new file using datasource: %s', 'jc-importer'), $datasource));
697 break;
698 }
699
700 remove_filter('upload_dir', [$this, 'set_custom_upload_path']);
701
702 if (is_wp_error($attachment_id)) {
703 throw new \Exception(sprintf(__('Importer Datasource: %s', 'jc-importer'), $attachment_id->get_error_message()));
704 }
705 }
706 }
707
708 $start = 0;
709
710 // get parser
711 if ($importer_data->getParser() === 'csv') {
712 Logger::debug('IM -get_csv_file');
713 $file = $this->get_csv_file($importer_data, $config);
714 Logger::debug('IM -load_parser');
715 $parser = new CSVParser($file);
716 if (true === $importer_data->getFileSetting('show_headings')) {
717 $start = 1;
718 }
719 } elseif ($importer_data->getParser() === 'xml') {
720 Logger::debug('IM -get_xml_file');
721 $file = $this->get_xml_file($importer_data, $config);
722 Logger::debug('IM -load_parser');
723 $parser = new XMLParser($file);
724 } else {
725 $parser = apply_filters('iwp/importer/init_parser', false, $importer_data, $config);
726 }
727
728 // if this is a new session, set start / end rows to state
729 if ($state->has_status('init')) {
730
731 Logger::debug('IM -get_record_count');
732 $end = $parser->file()->getRecordCount();
733
734 // Capture cancelled status from file processor
735 $raw_state = ImporterState::get_state($importer_data->getId());
736 if ($raw_state['status'] === 'cancelled') {
737 return $raw_state;
738 }
739
740 $config_data['start'] = $this->get_start($importer_data, $start);
741 $config_data['end'] = $this->get_end($importer_data, $config_data['start'], $end);
742
743 update_option('iwp_importer_config_' . $importer_id, $config_data);
744
745 Logger::debug('IM -update_state');
746 $state->update(function ($state) use ($config_data) {
747
748 Logger::debug('IM -update_state=running');
749
750 $state['id'] = $config_data['id'];
751 $state['status'] = 'running';
752 $state['progress']['import']['start'] = $config_data['start'];
753 $state['progress']['import']['end'] = $config_data['end'];
754 return $state;
755 });
756
757 Logger::debug('IM -write_status_session_to_file');
758 Util::write_status_session_to_file($id, $state);
759
760 do_action('iwp/importer/init', $importer_data);
761 }
762
763
764 add_filter('iwp/importer/mapper/hash_check_enabled', function ($enabled) use ($importer_data) {
765 return $importer_data->getSetting('hash_check');
766 });
767
768 Logger::debug('IM -import');
769 $importer = new \ImportWP\Common\Importer\Importer($config);
770 $importer->parser($parser);
771 $importer->mapper($mapper);
772 $importer->from($config_data['start']);
773 $importer->to($config_data['end']);
774 $importer->filter($importer_data->getFilters());
775 $importer->import($importer_id, $user, $state);
776 Logger::debug('IM -import_complete');
777 } catch (\Exception $e) {
778
779 // TODO: Missing template errors are currently not being logged to history, possibly others?
780 Logger::error('import -error=' . $e->getMessage(), $importer_id);
781 $state->error($e);
782 Util::write_status_session_to_file($id, $state);
783 return $state->get_raw();
784 }
785
786 /**
787 * @var Properties $properties
788 */
789 $properties = Container::getInstance()->get('properties');
790
791 // rotate files to not fill up server
792 $importer_data->limit_importer_files($properties->file_rotation);
793 $this->prune_importer_logs($importer_data, $properties->log_rotation);
794
795 $template->unregister_hooks();
796
797 $this->event_handler->run('importer_manager.import_shutdown', [$importer_data]);
798
799 return $state->update(function ($data) {
800 $data['duration'] = floatval($data['duration']) + Logger::timer();
801 return $data;
802 })->get_raw();
803 }
804
805 public function pause_import($importer_id, $paused)
806 {
807 // TODO: set flag for paused.
808 $state = ImporterState::get_state($importer_id);
809 if ($paused === 'no') {
810 ImporterState::clear_flag($importer_id);
811 $state['status'] = 'running';
812 } else {
813 ImporterState::set_paused($importer_id);
814 $state['status'] = 'paused';
815 }
816
817 // good chance this will be overwritten
818 ImporterState::set_state($importer_id, $state);
819
820 return $state;
821 }
822
823 public function stop_import($importer_id)
824 {
825 ImporterState::set_cancelled($importer_id);
826
827 // good chance this will be overwritten
828 $state = ImporterState::get_state($importer_id);
829 $state['status'] = 'cancelled';
830 ImporterState::set_state($importer_id, $state);
831
832 return $state;
833 }
834
835 public function get_start($importer_data, $start)
836 {
837 $tmp_start = $importer_data->getStartRow();
838 if (!is_null($tmp_start) && "" !== $tmp_start) {
839 $tmp_start = intval($tmp_start);
840
841 if ($tmp_start > $start) {
842 $start = $tmp_start;
843 }
844 }
845
846 return $start;
847 }
848
849 public function get_end($importer_data, $start, $end)
850 {
851 $tmp_max_row = $importer_data->getMaxRow();
852 if (!is_null($tmp_max_row) && $tmp_max_row !== '') {
853 $tmp_end = $start + intval($tmp_max_row);
854 if ($tmp_end < $end) {
855 $end = $tmp_end;
856 }
857 }
858
859 return $end;
860 }
861
862 public function get_importer_template($id)
863 {
864 $importer_model = $this->get_importer($id);
865 $templates = $this->get_templates();
866 $template_name = $importer_model->getTemplate();
867
868 if (!isset($templates[$template_name])) {
869 $exception_msg = sprintf(__("Unable to locate importer template: %s", 'jc-importer'), $template_name);
870 Logger::error('import -get_importer_template=' . $exception_msg, $importer_model->getId());
871 throw new \Exception($exception_msg);
872 }
873
874 return $this->template_manager->load_template($templates[$template_name]);
875 }
876
877 /**
878 * Get importer mapper
879 *
880 * @param int $id
881 * @param Template $template
882 * @param Permission $permission
883 * @return MapperInterface
884 */
885 public function get_importer_mapper($id, $template, $permission = null)
886 {
887 $importer = $this->get_importer($id);
888 $mapper_name = $template->get_mapper();
889
890 $mappers = $this->get_mappers();
891 return isset($mappers[$mapper_name]) ? new $mappers[$mapper_name]($importer, $template, $permission) : false;
892 }
893
894 public function get_mappers()
895 {
896 $mappers = $this->event_handler->run('mappers.register', [[]]); // apply_filters('iwp/mappers/register', []);
897 $mappers = array_merge($mappers, [
898 'post' => PostMapper::class,
899 'user' => UserMapper::class,
900 'term' => TermMapper::class,
901 'attachment' => AttachmentMapper::class,
902 'comment' => CommentMapper::class,
903 ]);
904 return $mappers;
905 }
906
907 public function get_mapper($key)
908 {
909 $mappers = $this->get_mappers();
910 if (!isset($mappers[$key])) {
911 return new \WP_Error('IWP_IM_1', sprintf(__('Unable to locate mapper: %s', 'jc-importer'), $key));
912 }
913
914 return $mappers[$key];
915 }
916
917 public function get_templates()
918 {
919 $templates = $this->event_handler->run('templates.register', [[]]);
920 $templates = array_merge($templates, [
921 'post' => PostTemplate::class,
922 'page' => PageTemplate::class,
923 'user' => UserTemplate::class,
924 'term' => TermTemplate::class,
925 'attachment' => AttachmentTemplate::class,
926 'comment' => CommentTemplate::class,
927 'custom-post-type' => CustomPostTypeTemplate::class,
928 ]);
929 return $templates;
930 }
931
932 public function get_template($key)
933 {
934 $templates = $this->get_templates();
935 if (!isset($templates[$key])) {
936 return new \WP_Error('IWP_IM_1', sprintf(__('Unable to locate template: %s', 'jc-importer'), $key));
937 }
938
939 return $templates[$key];
940 }
941
942 public function get_importer_debug_log(ImporterModel $importer_data, $page = 0, $per_page = -1)
943 {
944 $file_path = Logger::getLogFile($importer_data->getId());
945
946 $line_counter = 0;
947 $lines = [];
948 $start = $end = -1;
949
950 if ($per_page > 0) {
951 $start = ($page - 1) * $per_page;
952 $end = $start + $per_page;
953 }
954
955 if (file_exists($file_path)) {
956 $fh = fopen($file_path, 'r');
957 if ($fh !== false) {
958 while (($data = fgetcsv($fh)) !== false) {
959 if ($line_counter === $end) {
960 return $lines;
961 } elseif ($per_page === -1 || $line_counter >= $start) {
962 $lines[] = $data;
963 }
964
965 $line_counter++;
966 }
967 fclose($fh);
968 }
969 }
970 return $lines;
971 }
972
973 public function prune_importer_logs($importer_model, $limit)
974 {
975 $limit = intval($limit);
976 if ($limit <= -1) {
977 return;
978 }
979
980 $file_path = Util::get_importer_status_file_path($importer_model->getId());
981 $tmp_file_path = Util::get_importer_status_file_path($importer_model->getId()) . '.tmp';
982 $lines = $this->get_importer_logs($importer_model);
983
984 if (count($lines) > $limit) {
985
986 require_once(ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php');
987 require_once(ABSPATH . 'wp-admin/includes/class-wp-filesystem-direct.php');
988 $fileSystemDirect = new \WP_Filesystem_Direct(false);
989
990 $fh = fopen($tmp_file_path, 'w');
991 for ($i = 0; $i < count($lines); $i++) {
992
993 if ($i < count($lines) - $limit) {
994 $log_file_path = Util::get_importer_log_file_path($importer_model->getId(), $lines[$i]['id']);
995 if ($fileSystemDirect->exists($log_file_path)) {
996
997 $fileSystemDirect->delete($log_file_path);
998
999 $tmp = $log_file_path;
1000 for ($j = 0; $j < 3; $j++) {
1001 $tmp = dirname($tmp);
1002 $sub_files = $fileSystemDirect->dirlist($log_file_path);
1003 if (empty($sub_files)) {
1004 $fileSystemDirect->rmdir($tmp);
1005 }
1006 }
1007 }
1008 } else {
1009 fputs($fh, json_encode($lines[$i]) . "\n");
1010 }
1011 }
1012 fclose($fh);
1013
1014 if ($fileSystemDirect->move($tmp_file_path, $file_path, true)) {
1015 $fileSystemDirect->delete($tmp_file_path);
1016 }
1017 }
1018 }
1019
1020 public function get_importer_logs(ImporterModel $importer_data, $page = 0, $per_page = -1)
1021 {
1022 $file_path = Util::get_importer_status_file_path($importer_data->getId());
1023
1024 $line_counter = 0;
1025 $lines = [];
1026
1027 $start = $end = -1;
1028
1029 if ($per_page > 0) {
1030 $start = ($page - 1) * $per_page;
1031 $end = $start + $per_page;
1032 }
1033
1034 if (file_exists($file_path)) {
1035 $fh = fopen($file_path, 'r');
1036 if ($fh !== false) {
1037 while (($data = fgets($fh)) !== false) {
1038 if ($data[strlen($data) - 1] === "\n") {
1039 $data = substr($data, 0, strlen($data) - 1);
1040 }
1041
1042 if ($line_counter === $end) {
1043 return $lines;
1044 } elseif ($per_page === -1 || $line_counter >= $start) {
1045 $lines[] = json_decode($data, true);
1046 }
1047
1048 $line_counter++;
1049 }
1050 fclose($fh);
1051 }
1052 }
1053 return $lines;
1054 }
1055
1056 public function get_importer_log(ImporterModel $importer_data, $session_id, $page = 0, $per_page = -1)
1057 {
1058 $file_path = Util::get_importer_log_file_path($importer_data->getId(), $session_id);
1059
1060 $line_counter = 0;
1061 $lines = [];
1062 $start = $end = -1;
1063
1064 if ($per_page > 0) {
1065 $start = ($page - 1) * $per_page;
1066 $end = $start + $per_page;
1067 }
1068
1069 if (file_exists($file_path)) {
1070 $fh = fopen($file_path, 'r');
1071 if ($fh !== false) {
1072 while (($data = fgetcsv($fh)) !== false) {
1073 if ($line_counter === $end) {
1074 return $lines;
1075 } elseif ($per_page === -1 || $line_counter >= $start) {
1076 $lines[] = $data;
1077 }
1078
1079 $line_counter++;
1080 }
1081 fclose($fh);
1082 }
1083 }
1084 return $lines;
1085 }
1086
1087 public function get_importer_status_report(ImporterModel $immpoter_data, $session)
1088 {
1089 $logs = $this->get_importer_logs($immpoter_data);
1090 foreach ($logs as $log) {
1091 if ($log && isset($log['id']) && $log['id'] === $session) {
1092 return $log;
1093 }
1094 }
1095 return false;
1096 }
1097 }
1098