PluginProbe
Import WP – CSV & XML Import Export for WordPress / 2.7.9
Import WP – CSV & XML Import Export for WordPress v2.7.9
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 / Rest / RestManager.php

RestManager.php in Import WP – CSV & XML Import Export for WordPress 2.7.9, at class/Common/Rest/RestManager.php

1,466 lines 51.8 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\Rest;
4
5 use ImportWP\Common\Exporter\ExporterManager;
6 use ImportWP\Common\Filesystem\Filesystem;
7 use ImportWP\Common\Http\Http;
8 use ImportWP\Common\Importer\ImporterManager;
9 use ImportWP\Common\Importer\Preview\CSVPreview;
10 use ImportWP\Common\Importer\Preview\XMLPreview;
11 use ImportWP\Common\Importer\State\ImporterState;
12 use ImportWP\Common\Importer\Template\Template;
13 use ImportWP\Common\Importer\Template\TemplateManager;
14 use ImportWP\Common\Migration\Migrations;
15 use ImportWP\Common\Model\ExporterModel;
16 use ImportWP\Common\Model\ImporterModel;
17 use ImportWP\Common\Properties\Properties;
18 use ImportWP\Common\Util\Logger;
19 use ImportWP\Common\Util\Util;
20 use ImportWP\Container;
21 use ImportWP\EventHandler;
22
23 class RestManager extends \WP_REST_Controller
24 {
25 /**
26 * @var Properties
27 */
28 protected $properties;
29
30 /**
31 * @var Http
32 */
33 protected $http;
34
35 /**
36 * @var Filesystem
37 */
38 protected $filesystem;
39
40 /**
41 * @var ImporterManager
42 */
43 protected $importer_manager;
44
45 /**
46 * @var ExporterManager
47 */
48 protected $exporter_manager;
49
50 /**
51 * @var TemplateManager $template_manager
52 */
53 protected $template_manager;
54
55 /**
56 * @var EventHandler
57 */
58 protected $event_handler;
59
60 public function __construct(ImporterManager $importer_manager, ExporterManager $exporter_manager, Properties $properties, Http $http, Filesystem $filesystem, TemplateManager $template_manager, $event_handler)
61 {
62 $this->importer_manager = $importer_manager;
63 $this->exporter_manager = $exporter_manager;
64 $this->properties = $properties;
65 $this->http = $http;
66 $this->filesystem = $filesystem;
67 $this->template_manager = $template_manager;
68 $this->event_handler = $event_handler;
69 }
70
71 public function register()
72 {
73 add_action('rest_api_init', array($this, 'register_routes'));
74 }
75
76 public function register_routes()
77 {
78 $namespace = $this->properties->rest_namespace . '/' . $this->properties->rest_version;
79
80 register_rest_route($namespace, '/system/check', array(
81 array(
82 'methods' => \WP_REST_Server::READABLE,
83 'callback' => array($this, 'check_rest_status'),
84 'permission_callback' => array($this, 'get_permission')
85 )
86 ));
87 register_rest_route($namespace, '/system/migrate', array(
88 array(
89 'methods' => \WP_REST_Server::READABLE,
90 'callback' => array($this, 'migrate'),
91 'permission_callback' => array($this, 'get_permission')
92 )
93 ));
94
95 register_rest_route($namespace, '/importer', array(
96 array(
97 'methods' => \WP_REST_Server::CREATABLE,
98 'callback' => array($this, 'save_importer'),
99 'permission_callback' => array($this, 'get_permission')
100 )
101 ));
102
103 register_rest_route($namespace, '/importers', array(
104 array(
105 'methods' => \WP_REST_Server::READABLE,
106 'callback' => array($this, 'get_importers'),
107 'permission_callback' => array($this, 'get_permission')
108 ),
109 ));
110
111 register_rest_route($namespace, '/status', array(
112 array(
113 'methods' => \WP_REST_Server::READABLE,
114 'callback' => array($this, 'get_status'),
115 'permission_callback' => array($this, 'get_permission')
116 ),
117 ));
118
119 register_rest_route($namespace, '/importer/(?P<id>\d+)', array(
120 array(
121 'methods' => \WP_REST_Server::READABLE,
122 'callback' => array($this, 'get_importer'),
123 'permission_callback' => array($this, 'get_permission')
124 ),
125 array(
126 'methods' => \WP_REST_Server::EDITABLE,
127 'callback' => array($this, 'save_importer'),
128 'permission_callback' => array($this, 'get_permission')
129 ),
130 array(
131 'methods' => \WP_REST_Server::DELETABLE,
132 'callback' => array($this, 'delete_importer'),
133 'permission_callback' => array($this, 'get_permission')
134 ),
135 ));
136
137 register_rest_route($namespace, '/importer/(?P<id>\d+)/upload', array(
138 array(
139 'methods' => \WP_REST_Server::EDITABLE,
140 'callback' => array($this, 'attach_file'),
141 'permission_callback' => array($this, 'get_permission')
142 )
143 ));
144
145 register_rest_route($namespace, '/importer/(?P<id>\d+)/file-process', array(
146 array(
147 'methods' => \WP_REST_Server::CREATABLE,
148 'callback' => array($this, 'get_file_process'),
149 'permission_callback' => array($this, 'get_permission')
150 )
151 ));
152
153 register_rest_route($namespace, '/importer/(?P<id>\d+)/file-preview', array(
154 array(
155 'methods' => \WP_REST_Server::CREATABLE,
156 'callback' => array($this, 'get_file_preview'),
157 'permission_callback' => array($this, 'get_permission')
158 )
159 ));
160
161 register_rest_route($namespace, '/importer/(?P<id>\d+)/preview', array(
162 array(
163 'methods' => \WP_REST_Server::CREATABLE,
164 'callback' => array($this, 'get_preview'),
165 'permission_callback' => array($this, 'get_permission')
166 )
167 ));
168
169 register_rest_route($namespace, '/importer/(?P<id>\d+)/init', array(
170 array(
171 'methods' => \WP_REST_Server::READABLE,
172 'callback' => array($this, 'init_import'),
173 'permission_callback' => array($this, 'get_permission')
174 )
175 ));
176
177 register_rest_route($namespace, '/importer/(?P<id>\d+)/run', array(
178 array(
179 'methods' => \WP_REST_Server::CREATABLE,
180 'callback' => array($this, 'run_import'),
181 'permission_callback' => array($this, 'get_permission')
182 )
183 ));
184
185 register_rest_route($namespace, '/importer/(?P<id>\d+)/pause', array(
186 array(
187 'methods' => \WP_REST_Server::CREATABLE,
188 'callback' => array($this, 'pause_import'),
189 'permission_callback' => array($this, 'get_permission')
190 )
191 ));
192
193 register_rest_route($namespace, '/importer/(?P<id>\d+)/stop', array(
194 array(
195 'methods' => \WP_REST_Server::CREATABLE,
196 'callback' => array($this, 'stop_import'),
197 'permission_callback' => array($this, 'get_permission')
198 )
199 ));
200
201 register_rest_route($namespace, '/importer/(?P<id>\d+)/field', array(
202 array(
203 'methods' => \WP_REST_Server::CREATABLE,
204 'callback' => array($this, 'get_template_field_options'),
205 'permission_callback' => array($this, 'get_permission')
206 )
207 ));
208
209 register_rest_route($namespace, '/importer/(?P<id>\d+)/logs/(?P<session>\S+)', array(
210 array(
211 'methods' => \WP_REST_Server::READABLE,
212 'callback' => array($this, 'get_log'),
213 'permission_callback' => array($this, 'get_permission')
214 )
215 ));
216
217 register_rest_route($namespace, '/importer/(?P<id>\d+)/logs', array(
218 array(
219 'methods' => \WP_REST_Server::READABLE,
220 'callback' => array($this, 'get_logs'),
221 'permission_callback' => array($this, 'get_permission')
222 )
223 ));
224
225 register_rest_route($namespace, '/settings', array(
226 array(
227 'methods' => \WP_REST_Server::CREATABLE,
228 'callback' => array($this, 'save_settings'),
229 'permission_callback' => array($this, 'get_permission')
230 ),
231 array(
232 'methods' => \WP_REST_Server::READABLE,
233 'callback' => array($this, 'get_settings'),
234 'permission_callback' => array($this, 'get_permission')
235 ),
236 ));
237
238 register_rest_route($namespace, '/import-export', array(
239 array(
240 'methods' => \WP_REST_Server::CREATABLE,
241 'callback' => array($this, 'import_exporters'),
242 'permission_callback' => array($this, 'get_permission')
243 ),
244 array(
245 'methods' => \WP_REST_Server::READABLE,
246 'callback' => array($this, 'export_importers'),
247 'permission_callback' => array($this, 'get_permission')
248 ),
249 ));
250
251 register_rest_route($namespace, '/importer/(?P<id>\d+)/debug_log', array(
252 array(
253 'methods' => \WP_REST_Server::READABLE,
254 'callback' => array($this, 'get_debug_log'),
255 'permission_callback' => array($this, 'get_permission')
256 )
257 ));
258
259 register_rest_route($namespace, '/importer/(?P<id>\d+)/template', array(
260 array(
261 'methods' => \WP_REST_Server::READABLE,
262 'callback' => array($this, 'get_template'),
263 'permission_callback' => array($this, 'get_permission')
264 )
265 ));
266
267 // Exporter
268
269 register_rest_route($namespace, '/exporter', array(
270 array(
271 'methods' => \WP_REST_Server::CREATABLE,
272 'callback' => array($this, 'save_exporter'),
273 'permission_callback' => array($this, 'get_permission')
274 )
275 ));
276
277 register_rest_route($namespace, '/exporters', array(
278 array(
279 'methods' => \WP_REST_Server::READABLE,
280 'callback' => array($this, 'get_exporters'),
281 'permission_callback' => array($this, 'get_permission')
282 ),
283 ));
284
285 register_rest_route($namespace, '/exporter/(?P<id>\d+)', array(
286 array(
287 'methods' => \WP_REST_Server::READABLE,
288 'callback' => array($this, 'get_exporter'),
289 'permission_callback' => array($this, 'get_permission')
290 ),
291 array(
292 'methods' => \WP_REST_Server::EDITABLE,
293 'callback' => array($this, 'save_exporter'),
294 'permission_callback' => array($this, 'get_permission')
295 ),
296 array(
297 'methods' => \WP_REST_Server::DELETABLE,
298 'callback' => array($this, 'delete_exporter'),
299 'permission_callback' => array($this, 'get_permission')
300 ),
301 ));
302
303 register_rest_route($namespace, '/exporter/(?P<id>\d+)/run', array(
304 array(
305 'methods' => \WP_REST_Server::CREATABLE,
306 'callback' => array($this, 'run_exporter'),
307 'permission_callback' => array($this, 'get_permission')
308 ),
309 ));
310
311 register_rest_route($namespace, '/exporter/status', array(
312 array(
313 'methods' => \WP_REST_Server::READABLE,
314 'callback' => array($this, 'get_exporter_status'),
315 'permission_callback' => array($this, 'get_permission')
316 ),
317 ));
318 }
319
320 public function get_permission()
321 {
322
323 if (!current_user_can('manage_options')) {
324 return new \WP_Error('rest_forbidden', esc_html__('You do not have permissions.', $this->properties->plugin_domain), array('status' => 401));
325 }
326
327 return true;
328 }
329
330 public function sanitize($data, $name = null, $path = [])
331 {
332 $path[] = $name;
333
334 if (is_array($data)) {
335 foreach ($data as $k => $v) {
336 $data[$k] = $this->sanitize($v, $k, $path);
337 }
338
339 return $data;
340 }
341
342 $full_path = implode('.', array_filter($path));
343 if (!is_null($full_path)) {
344
345 if (preg_match('/^map\.\S+/', $full_path) !== false) {
346 return $data;
347 }
348 }
349
350 if (!is_null($name)) {
351 switch ($name) {
352 case 'id':
353 return intval($data);
354 }
355 }
356
357 return sanitize_text_field($data);
358 }
359
360 public function check_rest_status(\WP_REST_Request $request)
361 {
362 $result = [];
363 // find required php ext: https://github.com/llaville/php-compat-info
364
365 $tmp_writable = wp_is_writable($this->filesystem->get_temp_directory());
366
367
368 $result = [
369 'tmp_writable' => [
370 'status' => $tmp_writable ? 'yes' : 'no',
371 'message' => $tmp_writable ? '' : 'WordPress is unable to write to directory: ' . $this->filesystem->get_temp_directory() . ', make sure this directory is writable.',
372 ],
373 'rest_enabled' => [
374 'status' => 'yes',
375 'message' => '',
376 ],
377 'ext_simplexml' => [
378 'status' => extension_loaded('SimpleXML') ? 'yes' : 'no',
379 'message' => '',
380 ],
381 'ext_mbstring' => [
382 'status' => extension_loaded('mbstring') ? 'yes' : 'no',
383 'message' => '',
384 ],
385 'ext_xmlreader' => [
386 'status' => extension_loaded('xmlreader') ? 'yes' : 'no',
387 'message' => '',
388 ],
389 'php_version' => [
390 'status' => version_compare(phpversion(), '5.5.0') ? 'yes' : 'no',
391 'message' => '',
392 ],
393 ];
394 return $this->http->end_rest_success($result);
395 }
396
397 public function migrate(\WP_REST_Request $request)
398 {
399 $migration = new Migrations();
400 $result = $migration->migrate();
401
402 return $this->http->end_rest_success("Migration Complete.");
403 }
404
405 public function get_importers(\WP_REST_Request $request)
406 {
407 $importers = $this->importer_manager->get_importers();
408 $result = [];
409
410 foreach ($importers as $importer) {
411 $result[] = $importer->data();
412 }
413
414 return $this->http->end_rest_success($result);
415 }
416
417 public function get_importer(\WP_REST_Request $request)
418 {
419 Logger::setRequestType('get_importer');
420
421 $id = intval($request->get_param('id'));
422 $importer_data = $this->importer_manager->get_importer($id);
423 $response = $importer_data->data('public');
424 return $this->http->end_rest_success($response);
425 }
426
427 public function save_importer(\WP_REST_Request $request)
428 {
429 Logger::setRequestType('save_importer');
430
431 $post_data = $request->get_body_params();
432 $id = isset($post_data['id']) ? intval($post_data['id']) : null;
433
434 $temp_post_data = [
435 'create' => [],
436 'update' => []
437 ];
438
439 if (isset($post_data['permissions'])) {
440 // TODO: update sanitize method to keep track of hierarchy
441 $temp_post_data['create'] = explode("\n", $post_data['permissions']['create']['fields']);
442 $temp_post_data['update'] = explode("\n", $post_data['permissions']['update']['fields']);
443 }
444
445 $post_data = $this->sanitize($post_data);
446
447 if (isset($post_data['permissions'])) {
448 // TODO: Method to bypass sanitization not needed once sanitize method has been upgraded
449 foreach (['create', 'update'] as $permission_method) {
450 $permission_fields = $temp_post_data[$permission_method];
451 if (!empty($permission_fields)) {
452 $post_data['permissions'][$permission_method]['fields'] = array_values(array_filter(array_map([$this, 'sanitize'], $permission_fields, array_fill(0, count($permission_fields), 'permission_' . $permission_method . '_fields'))));
453 } else {
454 $post_data['permissions'][$permission_method]['fields'] = [];
455 }
456 }
457 }
458
459
460
461 $importer = new ImporterModel($id, $this->importer_manager->is_debug());
462
463 if (isset($post_data['datasource'])) {
464 $importer->setDatasource($post_data['datasource']);
465 }
466
467 if (isset($post_data['remote_url'])) {
468 $importer->setDatasourceSetting('remote_url', $post_data['remote_url']);
469 }
470
471 if (isset($post_data['local_url'])) {
472 $importer->setDatasourceSetting('local_url', $post_data['local_url']);
473 }
474
475 if (isset($post_data['existing_id'])) {
476 $importer->setFileId(intval($post_data['existing_id']));
477 }
478
479 if (isset($post_data['template'], $post_data['template_type'])) {
480 $importer->setTemplate($post_data['template'], $post_data['template_type']);
481
482 // Get default template options
483 $template_class = $this->importer_manager->get_template($post_data['template']);
484
485 /**
486 * @var Template $template
487 */
488 $template = $this->template_manager->load_template($template_class);
489 $template_options = $template->get_default_template_options();
490
491 if (isset($post_data['template_options'])) {
492 $template_options = array_merge($template_options, $post_data['template_options']);
493 }
494
495 foreach ($template_options as $key => $val) {
496 $importer->setSetting($key, $val);
497 }
498 }
499
500 if (isset($post_data['name'])) {
501 $importer->setName($post_data['name']);
502 }
503
504 if (isset($post_data['setting_start_row'])) {
505 $importer->setStartRow($post_data['setting_start_row']);
506 }
507
508 if (isset($post_data['setting_max_row'])) {
509 $importer->setMaxRow($post_data['setting_max_row']);
510 }
511
512 // save settings
513 foreach ($post_data as $key => $value) {
514 if (1 === preg_match('/^setting_(?<key>.*)/', $key, $matches)) {
515
516 $value = $this->sanitize_setting($value);
517 $importer->setSetting($matches['key'], $value);
518 }
519 }
520
521 $parser = $importer->getParser();
522
523 if (isset($post_data['file_settings_encoding'])) {
524 $importer->setFileSetting('file_encoding', $post_data['file_settings_encoding']);
525 }
526
527 if ($parser === 'csv') {
528
529 $clear_config = false;
530 if (isset($post_data['file_settings_delimiter'])) {
531
532 $old_delimiter = $importer->getFileSetting('delimiter');
533 if ($old_delimiter !== $post_data['file_settings_delimiter']) {
534 $clear_config = true;
535 }
536 $importer->setFileSetting('delimiter', $post_data['file_settings_delimiter']);
537 }
538 if (isset($post_data['file_settings_enclosure'])) {
539 $old_enclosure = $importer->getFileSetting('enclosure');
540 if ($old_enclosure !== $post_data['file_settings_enclosure']) {
541 $clear_config = true;
542 }
543 $importer->setFileSetting('enclosure', $post_data['file_settings_enclosure']);
544 }
545 if (isset($post_data['file_settings_show_headings'])) {
546 $importer->setFileSetting('show_headings', $post_data['file_settings_show_headings'] === 'true' || $post_data['file_settings_show_headings'] === true ? true : false);
547 }
548
549 if ($clear_config) {
550 $this->importer_manager->clear_config_files($importer->getId(), true);
551 }
552 } elseif ($parser === 'xml') {
553 if (isset($post_data['file_settings_base_path'])) {
554 $importer->setFileSetting('base_path', $post_data['file_settings_base_path']);
555 }
556 }
557
558 if (isset($post_data['file_settings_setup'])) {
559 $importer->setFileSetting('setup', $post_data['file_settings_setup'] === 'true' ? true : false);
560 }
561
562 if (isset($post_data['map']) && is_array($post_data['map'])) {
563 foreach ($post_data['map'] as $key => $value) {
564
565 if (1 === preg_match('/\._index$/', $key)) {
566 $value = intval($value);
567 }
568
569 $importer->setMap($key, $value);
570 }
571 }
572
573 if (isset($post_data['enabled']) && is_array($post_data['enabled'])) {
574 foreach ($post_data['enabled'] as $key => $value) {
575 if ($value === 'true') {
576 $importer->setEnabled($key);
577 } else {
578 $importer->setEnabled($key, false);
579 }
580 }
581 }
582
583 if (isset($post_data['permissions']) && is_array($post_data['permissions'])) {
584
585 // create
586 if (isset($post_data['permissions']['create'])) {
587 $create = $post_data['permissions']['create'];
588 $importer->setPermission('create', [
589 'enabled' => isset($create['enabled']) && $create['enabled'] === 'true' ? true : false,
590 'type' => isset($create['type']) && in_array($create['type'], ['include', 'exclude'], true) ? $create['type'] : null,
591 'fields' => isset($create['fields']) ? $create['fields'] : [],
592 ]);
593 }
594
595 // update
596 if (isset($post_data['permissions']['update'])) {
597 $update = $post_data['permissions']['update'];
598 $importer->setPermission('update', [
599 'enabled' => isset($update['enabled']) && $update['enabled'] === 'true' ? true : false,
600 'type' => isset($update['type']) && in_array($update['type'], ['include', 'exclude'], true) ? $update['type'] : null,
601 'fields' => isset($update['fields']) ? $update['fields'] : [],
602 ]);
603 }
604
605 // remove
606 if (isset($post_data['permissions']['remove'])) {
607 $importer->setPermission('remove', [
608 'enabled' => $post_data['permissions']['remove']['enabled'] === 'true',
609 'trash' => $post_data['permissions']['remove']['trash'] === 'true',
610 ]);
611 }
612 }
613
614 $result = $importer->save();
615 if (is_wp_error($result)) {
616 return $this->http->end_rest_error($result->get_error_message());
617 }
618 return $this->http->end_rest_success($importer->data());
619 }
620
621 private function sanitize_setting($value)
622 {
623 if (is_array($value)) {
624 foreach ($value as &$tmp) {
625 $tmp = $this->sanitize_setting($tmp);
626 }
627 return $value;
628 } else {
629 if (in_array($value, ['true', 'false'], true)) {
630 if ($value === 'true') {
631 $value = true;
632 } else {
633 $value = false;
634 }
635 }
636 }
637
638 return $value;
639 }
640
641 public function get_status(\WP_REST_Request $request)
642 {
643 $this->http->set_stream_headers();
644
645 Logger::disable();
646
647 $importer_ids = $request->get_param('ids');
648
649 $result = [];
650
651 $query_data = array(
652 'post_type' => IWP_POST_TYPE,
653 'posts_per_page' => -1,
654 'fields' => 'ids'
655 );
656 if (!empty($importer_ids)) {
657 $query_data['post__in'] = $importer_ids;
658 }
659
660 $query = new \WP_Query($query_data);
661
662 foreach ($query->posts as $importer_id) {
663
664 $importer_model = $this->importer_manager->get_importer($importer_id);
665
666 $output = ImporterState::get_state($importer_id);
667 $output['version'] = 2;
668 $output['message'] = $this->generate_status_message($output);
669 $output['importer'] = $importer_id;
670
671 $result[] = $this->event_handler->run('iwp/importer/status/output', [$output, $importer_model]);
672 }
673
674 echo json_encode($result) . "\n";
675 die();
676 }
677
678 /**
679 * @param array $data
680 * @return string
681 */
682 protected function generate_status_message($data)
683 {
684 $output = '';
685
686 if (!$data || !isset($data['status']) || is_null($data['status']) || empty($data['status'])) {
687 return '';
688 }
689
690 if (isset($data['status'])) {
691 switch ($data['status']) {
692 case 'cancelled':
693 $output .= 'Import cancelled';
694 break;
695 case 'complete':
696 $output .= 'Import complete';
697 break;
698 case 'error':
699 $output = $data['message'];
700 return $output;
701 break;
702 }
703 }
704
705 if (isset($data['status']) && $data['status'] === 'running' && isset($data['section'])) {
706 switch ($data['section']) {
707 case 'import':
708 case 'delete':
709 $output = $data['section'] === 'import' ? 'Importing: ' : 'Deleting: ';
710 $output .= $data['progress'][$data['section']]['current_row'] . ' / ' . ($data['progress'][$data['section']]['end'] - $data['progress'][$data['section']]['start']);
711
712 break;
713 }
714 }
715
716 if (isset($data['stats'])) {
717 if ($data['stats']['inserts'] > 0) {
718 $output .= sprintf(', Inserts: %d', $data['stats']['inserts']);
719 }
720 if ($data['stats']['updates'] > 0) {
721 $output .= sprintf(', Updates: %d', $data['stats']['updates']);
722 }
723 if ($data['stats']['deletes'] > 0) {
724 $output .= sprintf(', Deletes: %d', $data['stats']['deletes']);
725 }
726 if ($data['stats']['skips'] > 0) {
727 $output .= sprintf(', Skips: %d', $data['stats']['skips']);
728 }
729 if ($data['stats']['errors'] > 0) {
730 $output .= sprintf(', Errors: %d', $data['stats']['errors']);
731 }
732 }
733
734 if (isset($data['timestamp'], $data['updated'])) {
735 /**
736 * @var Util $util
737 */
738 $util = Container::getInstance()->get('util');
739 $output .= ', Run Time: ' . $util->format_time($data['updated'] - $data['timestamp']);
740 } elseif (isset($data['duration'])) {
741 /**
742 * @var Util $util
743 */
744 $util = Container::getInstance()->get('util');
745 $output .= ', Run Time: ' . $util->format_time($data['duration']);
746 }
747
748 return $output;
749 }
750
751 public function delete_importer(\WP_REST_Request $request)
752 {
753 Logger::setRequestType('delete_importer');
754
755 $id = intval($request->get_param('id'));
756 $this->importer_manager->delete_importer($id);
757 return $this->http->end_rest_success(true);
758 }
759
760 public function attach_file(\WP_REST_Request $request)
761 {
762 $id = intval($request->get_param('id'));
763 $post_data = $this->sanitize($request->get_body_params());
764
765 $action = $post_data['action'];
766
767 $importer = $this->importer_manager->get_importer($id);
768
769 // TODO: Remove duplicate code, found in cron manager
770
771 switch ($action) {
772 case 'file_upload':
773 $attachment_id = $this->importer_manager->upload_file($importer, $_FILES['file']);
774 break;
775 case 'file_remote':
776 $raw_source = $post_data['remote_url'];
777 $source = apply_filters('iwp/importer/datasource', $raw_source, $raw_source, $importer);
778 $source = apply_filters('iwp/importer/datasource/remote', $source, $raw_source, $importer);
779
780 $filetype = $importer->getParser();
781 if (is_null($filetype)) {
782 $filetype = $post_data['filetype'];
783 }
784
785 $attachment_id = $this->importer_manager->remote_file($importer, $source, $filetype, null, $importer->getId() . '-');
786 break;
787 case 'file_local':
788 $raw_source = $post_data['local_url'];
789 $source = apply_filters('iwp/importer/datasource', $raw_source, $raw_source, $importer);
790 $source = apply_filters('iwp/importer/datasource/local', $source, $raw_source, $importer);
791 $attachment_id = $this->importer_manager->local_file($importer, $source);
792 break;
793 default:
794 $attachment_id = new \WP_Error('IWP_RM_1', 'Invalid form action');
795 break;
796 }
797
798 if (is_wp_error($attachment_id)) {
799 return $this->http->end_rest_error($attachment_id->get_error_message());
800 }
801
802 return $this->http->end_rest_success($importer->data());
803 }
804
805 public function get_file_process(\WP_REST_Request $request)
806 {
807 try {
808 $id = intval($request->get_param('id'));
809 $post_data = $this->sanitize($request->get_body_params());
810
811 $importer = $this->importer_manager->get_importer($id);
812 if (!$importer) {
813 return $this->http->end_rest_error('Invalid importer');
814 }
815
816 $this->importer_manager->clear_config_files($id, true);
817 $parser = $importer->getParser();
818
819 if ('xml' === $parser) {
820
821 $nodes = $this->importer_manager->process_xml_file($id, true);
822 $importer->setFileSetting('nodes', $nodes);
823 } elseif ('csv' === $parser) {
824
825 $delimiter = isset($post_data['delimiter']) ? $post_data['delimiter'] : null;
826 if (is_null($delimiter)) {
827 $delimiter = ',';
828 }
829
830 $enclosure = isset($post_data['enclosure']) ? $post_data['enclosure'] : null;
831 if (is_null($enclosure)) {
832 $enclosure = '"';
833 }
834
835 $count = $this->importer_manager->process_csv_file($id, $delimiter, $enclosure, true);
836
837 // Need fresh importer, due to process_csv_file importer modifications might get overwritten.
838 $importer = $this->importer_manager->get_importer($id);
839 $importer->setFileSetting('count', $count);
840 } else {
841
842 do_action('iwp/file-process/' . $parser, $importer, $post_data);
843 }
844
845 $importer->setFileSetting('processed', true);
846 $importer->save();
847
848 return $this->http->end_rest_success($importer->data());
849 } catch (\Exception $e) {
850 return $this->http->end_rest_error($e->getMessage());
851 }
852 }
853
854 public function get_file_preview(\WP_REST_Request $request)
855 {
856 try {
857 $id = intval($request->get_param('id'));
858 $post_data = $this->sanitize($request->get_body_params());
859 $importer = $this->importer_manager->get_importer($id);
860
861 $import_file_exists = $this->filesystem->file_exists($importer->getFile());
862 if (is_wp_error($import_file_exists)) {
863 throw new \Exception("Unable to load preview, " . $import_file_exists->get_error_message());
864 }
865
866 $record_index = isset($post_data['record']) ? $post_data['record'] : null;
867 if (is_null($record_index)) {
868 $record_index = 0;
869 }
870
871 // Temp set file_encoding
872 if (isset($post_data['file_encoding'])) {
873 $importer->setFileSetting('file_encoding', $post_data['file_encoding']);
874 }
875
876 if ($importer->getParser() === 'xml') {
877
878 $config = $this->importer_manager->get_config($importer, true);
879 $file = $this->importer_manager->get_xml_file($importer, $config);
880
881 $base_path = $post_data['base_path'];
882 if (!is_null($base_path)) {
883 $file->setRecordPath($base_path);
884 }
885
886 $preview = new XMLPreview($file, $base_path);
887 $result = $preview->data();
888 if (is_wp_error($result)) {
889 return $this->http->end_rest_error($result);
890 }
891
892 return $this->http->end_rest_success($result[0]);
893 } elseif ($importer->getParser() === 'csv') {
894
895 $config = $this->importer_manager->get_config($importer, true);
896 $file = $this->importer_manager->get_csv_file($importer, $config);
897
898 $clear_config = false;
899 if (!is_null($post_data['delimiter']) && $post_data['delimiter'] !== $file->getDelimiter()) {
900 $clear_config = true;
901 } elseif (!is_null($post_data['enclosure']) && $post_data['enclosure'] !== $file->getEnclosure()) {
902 $clear_config = true;
903 }
904
905 if ($clear_config) {
906 $this->importer_manager->clear_config_files($importer->getId(), true);
907 $config = $this->importer_manager->get_config($importer, true);
908 $file = $this->importer_manager->get_csv_file($importer, $config);
909 }
910
911 $delimiter = $post_data['delimiter'];
912 if (!is_null($delimiter)) {
913 $file->setDelimiter($delimiter);
914 }
915
916 $enclosure = $post_data['enclosure'];
917 if (!is_null($enclosure)) {
918 $file->setEnclosure($enclosure);
919 }
920
921 // parse bool param from string
922 $show_headings = $post_data['show_headings'];
923 if (in_array($show_headings, ['true', 'false'])) {
924 $show_headings = $show_headings === 'true' ? true : false;
925 }
926
927 if (is_null($show_headings)) {
928 $show_headings = $importer->getFileSetting('show_headings');
929 }
930
931 $preview = new CSVPreview($file);
932 $result = $preview->data($record_index, $show_headings);
933 if (is_wp_error($result)) {
934 return $this->http->end_rest_error($result);
935 }
936
937 return $this->http->end_rest_success($result);
938 } else {
939
940 $result = apply_filters('iwp/file-preview/' . $importer->getParser(), null, $importer);
941 if (is_wp_error($result)) {
942 return $this->http->end_rest_error($result);
943 }
944
945 return $this->http->end_rest_success($result);
946 }
947 } catch (\Exception $error) {
948 return $this->http->end_rest_error($error->getMessage());
949 }
950 }
951
952 public function get_preview(\WP_REST_Request $request)
953 {
954 // if no fields have been posted send all previews
955 try {
956 $id = intval($request->get_param('id'));
957 $importer = $this->importer_manager->get_importer($id);
958 $import_file_exists = $this->filesystem->file_exists($importer->getFile());
959 if (is_wp_error($import_file_exists)) {
960 throw new \Exception("Unable to load preview, " . $import_file_exists->get_error_message());
961 }
962
963 $parser = $importer->getParser();
964
965 $fields = $this->sanitize($request->get_body_params());
966 if (is_null($fields) || empty($fields)) {
967 $fields = $importer->getMap();
968 }
969
970 if ('xml' === $parser) {
971 $result = $this->importer_manager->preview_xml_file($importer, $fields);
972 } elseif ('csv' === $parser) {
973 $row = 0;
974 if ($importer->getFileSetting('show_headings') === true) {
975 $row = 1;
976 }
977 $result = $this->importer_manager->preview_csv_file($importer, $fields, $row);
978 } else {
979
980 $result = apply_filters('iwp/record-preview/' . $parser, [], $importer, $fields);
981 if (is_wp_error($result)) {
982 return $this->http->end_rest_error($result);
983 }
984
985 return $this->http->end_rest_success($result);
986 }
987
988 return $this->http->end_rest_success($result);
989 } catch (\Exception $error) {
990 return $this->http->end_rest_error($error->getMessage());
991 }
992 }
993
994 /**
995 * Trigger a new import.
996 *
997 * @param \WP_REST_Request $request
998 * @return array
999 */
1000 public function init_import(\WP_REST_Request $request)
1001 {
1002 $id = intval($request->get_param('id'));
1003
1004 Logger::setRequestType('rest::init_import');
1005
1006 Logger::write('init_import -start', $id);
1007
1008 $importer_data = $this->importer_manager->get_importer($id);
1009
1010 // clear existing
1011 ImporterState::clear_options($importer_data->getId());
1012
1013 // This is used for storing version on imported records
1014 $session_id = md5($importer_data->getId() . time());
1015 update_post_meta($importer_data->getId(), '_iwp_session', $session_id);
1016
1017 Logger::clearRequestType();
1018
1019 return $this->http->end_rest_success([
1020 'session' => $session_id
1021 ]);
1022 }
1023
1024 public function run_import(\WP_REST_Request $request)
1025 {
1026 $this->http->set_stream_headers();
1027
1028 Logger::setRequestType('rest::run_import');
1029
1030 $session = $request->get_param('session');
1031 $user = uniqid('iwp', true);
1032 $id = intval($request->get_param('id'));
1033 Logger::write('run_import -session=' . $session, $id);
1034
1035 $importer_data = $this->importer_manager->get_importer($id);
1036 Logger::write('run_import -import=start', $id);
1037
1038 $state = $this->importer_manager->import($importer_data->getId(), $user, $session);
1039 $state['message'] = $this->generate_status_message($state);
1040
1041 Logger::clearRequestType();
1042
1043 return $this->http->end_rest_success($state);
1044 }
1045
1046 public function pause_import(\WP_REST_Request $request)
1047 {
1048 $id = intval($request->get_param('id'));
1049 $paused = $this->sanitize($request->get_param('paused'), 'paused');
1050
1051 $importer_data = $this->importer_manager->get_importer($id);
1052 $user = uniqid('iwp', true);
1053
1054 $state = ImporterState::wait_for_lock($importer_data->getId(), $user, function () use ($importer_data, $paused) {
1055 $state = ImporterState::get_state($importer_data->getId());
1056
1057
1058 if ($paused === 'no') {
1059 $state['status'] = 'running';
1060 } else {
1061 $state['status'] = 'paused';
1062 }
1063
1064 ImporterState::set_state($importer_data->getId(), $state);
1065 return $state;
1066 });
1067
1068 return $this->http->end_rest_success($state);
1069 }
1070
1071 public function stop_import(\WP_REST_Request $request)
1072 {
1073 $id = intval($request->get_param('id'));
1074
1075 $importer_data = $this->importer_manager->get_importer($id);
1076
1077 $user = uniqid('iwp');
1078
1079 $state = ImporterState::wait_for_lock($importer_data->getId(), $user, function () use ($importer_data) {
1080 $state = ImporterState::get_state($importer_data->getId());
1081 $state['status'] = 'cancelled';
1082 ImporterState::set_state($importer_data->getId(), $state);
1083 return $state;
1084 });
1085
1086 return $this->http->end_rest_success($state);
1087 }
1088
1089 /**
1090 * Get field options for current template
1091 *
1092 * @param \WP_REST_Request $request
1093 * @return void
1094 */
1095 public function get_template_field_options(\WP_REST_Request $request)
1096 {
1097 $id = intval($request->get_param('id'));
1098 $field = $this->sanitize($request->get_param('field'), 'field');
1099
1100 $importer_data = $this->importer_manager->get_importer($id);
1101 $template = $this->importer_manager->get_importer_template($id);
1102
1103 return $this->http->end_rest_success($template->get_field_options($field, $importer_data));
1104 }
1105
1106 public function get_logs(\WP_REST_Request $request)
1107 {
1108 $id = intval($request->get_param('id'));
1109 $importer_data = $this->importer_manager->get_importer($id);
1110 $logs = $this->importer_manager->get_importer_logs($importer_data);
1111 return $this->http->end_rest_success(array_reverse($logs));
1112 }
1113
1114 public function get_log(\WP_REST_Request $request)
1115 {
1116 $id = intval($request->get_param('id'));
1117 $session = $this->sanitize($request->get_param('session'), 'session');
1118 $page = intval($request->get_param('page'));
1119 if ($page < 1) {
1120 $page = 1;
1121 }
1122 $importer_data = $this->importer_manager->get_importer($id);
1123 $log = $this->importer_manager->get_importer_log($importer_data, $session, $page, 500);
1124 $status = $this->importer_manager->get_importer_status_report($importer_data, $session);
1125 return $this->http->end_rest_success(['logs' => $log, 'status' => $status]);
1126 }
1127
1128 public function get_debug_log(\WP_REST_Request $request)
1129 {
1130 $id = intval($request->get_param('id'));
1131 $page = intval($request->get_param('page'));
1132 if ($page < 1) {
1133 $page = 1;
1134 }
1135 $importer_data = $this->importer_manager->get_importer($id);
1136 $log = $this->importer_manager->get_importer_debug_log($importer_data, $page, 100);
1137
1138 $download = Logger::getLogFile($importer_data->getId(), true);
1139 return $this->http->end_rest_success(['log' => $log, 'download' => $download]);
1140 }
1141
1142 public function save_settings(\WP_REST_Request $request)
1143 {
1144 $post_data = $request->get_body_params();
1145 $post_data = $this->sanitize($post_data);
1146
1147 $defaults = $this->properties->_default_settings();
1148
1149 $output = [];
1150
1151 foreach ($defaults as $setting => $default) {
1152 if (isset($post_data[$setting])) {
1153 if ('bool' === $default['type']) {
1154 // Handle Boolean
1155 if (is_bool($post_data[$setting])) {
1156 $output[$setting] = $post_data[$setting];
1157 } elseif (in_array($post_data[$setting], ['true', 'false'])) {
1158 $output[$setting] = $post_data[$setting] === 'true' ? true : false;
1159 } else {
1160 $output[$setting] = boolval($post_data[$setting]);
1161 }
1162 } elseif ('number' === $default['type']) {
1163 $output[$setting] = intval($post_data[$setting]);
1164 } else {
1165 $output[$setting] = $post_data[$setting];
1166 }
1167 } else {
1168 $output[$setting] = $default['value'];
1169 }
1170 }
1171
1172 update_site_option('iwp_settings', $output);
1173
1174 return $this->http->end_rest_success($this->properties->get_settings());
1175 }
1176
1177 public function get_settings(\WP_REST_Request $request = null)
1178 {
1179 $output = $this->properties->get_settings();
1180 return $this->http->end_rest_success($output);
1181 }
1182
1183 public function get_template(\WP_REST_Request $request = null)
1184 {
1185 Logger::setRequestType('get_template');
1186
1187 $importer_id = $request->get_param('id');
1188 $importer_model = $this->importer_manager->get_importer($importer_id);
1189 $importer_template = $importer_model->getTemplate();
1190
1191 $templates = $this->importer_manager->get_templates();
1192
1193 if (!isset($templates[$importer_template])) {
1194 return $this->http->end_rest_error("Invalid template \"" . $importer_template . "\"");
1195 }
1196
1197 $template_id = $templates[$importer_template];
1198 $template_class = $this->template_manager->load_template($template_id);
1199
1200 $output = [
1201 'id' => $template_id,
1202 'label' => $template_class->get_name(),
1203 'map' => $template_class->get_fields($importer_model),
1204 'settings' => $template_class->register_settings(),
1205 'options' => $template_class->register_options(),
1206 ];
1207 return $this->http->end_rest_success($output);
1208 }
1209
1210 public function export_importers(\WP_REST_Request $request = null)
1211 {
1212 Logger::setRequestType('export_importers');
1213
1214 $importer_ids = $request->get_param('ids');
1215
1216 $sql_ids = array_map(function ($v) {
1217 return "'" . esc_sql($v) . "'";
1218 }, $importer_ids);
1219 $sql_ids = implode(',', $sql_ids);
1220
1221 $output = [];
1222
1223 /**
1224 * @var \WPDB $wpdb
1225 */
1226 global $wpdb;
1227 $results = $wpdb->get_results("SELECT post_title, post_content from {$wpdb->posts} WHERE post_type='" . IWP_POST_TYPE . "' AND ID IN (" . $sql_ids . ")", ARRAY_A);
1228 foreach ($results as $result) {
1229
1230 $data = unserialize($result['post_content']);
1231
1232 unset($data['file']['id']);
1233 unset($data['file']['settings']['count']);
1234 unset($data['file']['settings']['processed']);
1235
1236
1237 $row = [
1238 'name' => $result['post_title'],
1239 'data' => $data
1240 ];
1241
1242 if ($this->importer_manager->is_debug()) {
1243 $row['raw'] = base64_encode($result['post_content']);
1244 }
1245
1246 $output[] = $row;
1247 }
1248
1249 $json = json_encode($output);
1250
1251 header('Content-disposition: attachment; filename="ImportWP-export-' . date('Y-m-d') . '.json"');
1252 header('Content-type: "application/json"; charset="utf8"');
1253 header('Expires: 0');
1254 header('Cache-Control: must-revalidate');
1255 header('Pragma: public');
1256 header('Content-Length: ' . strlen($json));
1257
1258 echo $json;
1259 die();
1260 }
1261
1262 public function import_exporters(\WP_REST_Request $request = null)
1263 {
1264 Logger::setRequestType('import_exporters');
1265
1266 $upload = $this->filesystem->upload_file($_FILES['file']);
1267 if (is_wp_error($upload)) {
1268 return $this->http->end_rest_error($upload->get_error_message());
1269 }
1270
1271 $json_contents = file_get_contents($upload['dest']);
1272 @unlink($upload['dest']);
1273
1274 $contents = json_decode($json_contents, true);
1275 $counter = 0;
1276 $errors = [];
1277 if (!empty($contents)) {
1278 foreach ($contents as $importer) {
1279
1280 $result = wp_insert_post([
1281 'post_type' => IWP_POST_TYPE,
1282 'post_title' => $importer['name'],
1283 'post_status' => 'publish',
1284 'post_author' => get_current_user_id(),
1285 'post_content' => serialize($importer['data'])
1286 ], true);
1287
1288 if (is_wp_error($result)) {
1289 $errors[] = $result->get_error_message();
1290 continue;
1291 }
1292
1293 $counter++;
1294 }
1295 }
1296
1297 if (empty($counter)) {
1298 return $this->http->end_rest_error('No Importers found.');
1299 }
1300
1301 return $this->http->end_rest_success("Import Complete, {$counter} Importer" . ($counter > 1 ? 's' : '') . '.');
1302 }
1303
1304 public function save_exporter(\WP_REST_Request $request = null)
1305 {
1306 Logger::setRequestType('save_exporter');
1307
1308 $post_data = $request->get_body_params();
1309 $id = isset($post_data['id']) ? intval($post_data['id']) : null;
1310
1311
1312 $exporter = new ExporterModel($id, $this->importer_manager->is_debug());
1313
1314 if (isset($post_data['name'])) {
1315 $exporter->setName($post_data['name']);
1316 }
1317
1318 if (isset($post_data['type'])) {
1319 $exporter->setType($post_data['type']);
1320 }
1321
1322 if (isset($post_data['file_type'])) {
1323 $exporter->setFileType($post_data['file_type']);
1324 }
1325
1326 if (isset($post_data['fields']) || $id > 0) {
1327 $exporter->setFields(isset($post_data['fields']) ? $post_data['fields'] : []);
1328 }
1329
1330 if (isset($post_data['filters']) || $id > 0) {
1331 $exporter->setFilters(isset($post_data['filters']) ? $post_data['filters'] : []);
1332 }
1333
1334 if (isset($post_data['unique_identifier'])) {
1335 $exporter->setUniqueIdentifier($post_data['unique_identifier']);
1336 }
1337
1338 if (isset($post_data['export_method'])) {
1339 $exporter->setExportMethod($post_data['export_method']);
1340 }
1341
1342 if (isset($post_data['cron'])) {
1343
1344 // TODO: Save cron settings
1345
1346 $cron = $this->sanitize_setting($post_data['cron']);
1347 $exporter->setCron($cron);
1348 }
1349
1350 $result = $exporter->save();
1351 if (is_wp_error($result)) {
1352 return $this->http->end_rest_error($result->get_error_message());
1353 }
1354 return $this->http->end_rest_success($exporter->data());
1355 }
1356
1357 public function get_exporters(\WP_REST_Request $request = null)
1358 {
1359 Logger::setRequestType('get_exporters');
1360
1361 $exporters = $this->exporter_manager->get_exporters();
1362 $result = [];
1363
1364 foreach ($exporters as $exporter) {
1365 $result[] = $exporter->data();
1366 }
1367
1368 return $this->http->end_rest_success($result);
1369 }
1370
1371 public function get_exporter(\WP_REST_Request $request = null)
1372 {
1373 Logger::setRequestType('get_exporter');
1374
1375 $id = intval($request->get_param('id'));
1376 $exporter_data = $this->exporter_manager->get_exporter($id);
1377 $response = $exporter_data->data('public');
1378 return $this->http->end_rest_success($response);
1379 }
1380
1381 public function delete_exporter(\WP_REST_Request $request = null)
1382 {
1383 Logger::setRequestType('delete_exporter');
1384
1385 $id = intval($request->get_param('id'));
1386 $this->exporter_manager->delete_exporter($id);
1387 return $this->http->end_rest_success(true);
1388 }
1389
1390 public function run_exporter(\WP_REST_Request $request)
1391 {
1392 Logger::setRequestType('run_exporter');
1393
1394 $this->http->set_stream_headers();
1395
1396 $id = intval($request->get_param('id'));
1397 $session = $request->get_param('session');
1398
1399 $exporter_data = $this->exporter_manager->get_exporter($id);
1400
1401 $status = $this->exporter_manager->export($exporter_data, $session);
1402 echo json_encode($status);
1403 // Logger::write(__CLASS__ . '::run_import -import=end -status=' . $status->get_status(), $id);
1404
1405 // if (!empty($this->output_cache)) {
1406 // echo $this->output_cache;
1407 // $this->output_cache = '';
1408 // }
1409
1410 // $output = $status->output();
1411 // $output['message'] = $this->generate_exporter_status_message($status);
1412 // echo json_encode($output) . "\n";
1413 die();
1414 }
1415
1416 public function get_exporter_status(\WP_REST_Request $request)
1417 {
1418 Logger::setRequestType('get_exporter_status');
1419
1420 $exporter_ids = $request->get_param('ids');
1421 $result = array();
1422 $query_data = array(
1423 'post_type' => EWP_POST_TYPE,
1424 'posts_per_page' => -1,
1425 'fields' => 'ids'
1426 );
1427
1428 if (!empty($exporter_ids)) {
1429 $query_data['post__in'] = $exporter_ids;
1430 }
1431
1432 $query = new \WP_Query($query_data);
1433
1434 if (!empty($query->posts)) {
1435 foreach ($query->posts as $post_id) {
1436
1437 $exporter = $this->exporter_manager->get_exporter($post_id);
1438 $status = $exporter->get_status();
1439 $status['id'] = $post_id;
1440 $status['version'] = 2;
1441
1442 $output = '';
1443
1444 switch ($status['status']) {
1445 case 'cancelled':
1446 $output .= 'Export cancelled';
1447 break;
1448 case 'complete':
1449 $output .= 'Export complete';
1450 break;
1451 case 'running':
1452 $output .= 'Exporting: ' . $status['count'] . '/' . $status['total'];
1453 break;
1454 }
1455
1456 $status['message'] = $output;
1457
1458 $result[] = $this->event_handler->run('iwp/exporter/status/output', [$status, $exporter]);
1459 }
1460 }
1461
1462 echo json_encode($result) . "\n";
1463 die();
1464 }
1465 }
1466