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 / Rest / RestManager.php

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

1,985 lines 73.4 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\Exporter\State\ExporterState;
7 use ImportWP\Common\Filesystem\Filesystem;
8 use ImportWP\Common\Filesystem\ZipArchive;
9 use ImportWP\Common\Http\Http;
10 use ImportWP\Common\Importer\ImporterManager;
11 use ImportWP\Common\Importer\Preview\CSVPreview;
12 use ImportWP\Common\Importer\Preview\XMLPreview;
13 use ImportWP\Common\Importer\State\ImporterState;
14 use ImportWP\Common\Importer\Template\Template;
15 use ImportWP\Common\Importer\Template\TemplateManager;
16 use ImportWP\Common\Migration\Migrations;
17 use ImportWP\Common\Model\ExporterModel;
18 use ImportWP\Common\Model\ImporterModel;
19 use ImportWP\Common\Properties\Properties;
20 use ImportWP\Common\Util\Logger;
21 use ImportWP\Common\Util\Util;
22 use ImportWP\Container;
23 use ImportWP\EventHandler;
24
25 class RestManager extends \WP_REST_Controller
26 {
27 /**
28 * @var Properties
29 */
30 protected $properties;
31
32 /**
33 * @var Http
34 */
35 protected $http;
36
37 /**
38 * @var Filesystem
39 */
40 protected $filesystem;
41
42 /**
43 * @var ImporterManager
44 */
45 protected $importer_manager;
46
47 /**
48 * @var ExporterManager
49 */
50 protected $exporter_manager;
51
52 /**
53 * @var TemplateManager $template_manager
54 */
55 protected $template_manager;
56
57 /**
58 * @var EventHandler
59 */
60 protected $event_handler;
61
62 public function __construct(ImporterManager $importer_manager, ExporterManager $exporter_manager, Properties $properties, Http $http, Filesystem $filesystem, TemplateManager $template_manager, $event_handler)
63 {
64 $this->importer_manager = $importer_manager;
65 $this->exporter_manager = $exporter_manager;
66 $this->properties = $properties;
67 $this->http = $http;
68 $this->filesystem = $filesystem;
69 $this->template_manager = $template_manager;
70 $this->event_handler = $event_handler;
71 }
72
73 public function register()
74 {
75 add_action('rest_api_init', array($this, 'register_routes'));
76 }
77
78 public function register_routes()
79 {
80 $namespace = $this->properties->rest_namespace . '/' . $this->properties->rest_version;
81
82 register_rest_route($namespace, '/system/check', array(
83 array(
84 'methods' => \WP_REST_Server::READABLE,
85 'callback' => array($this, 'check_rest_status'),
86 'permission_callback' => array($this, 'get_permission')
87 )
88 ));
89 register_rest_route($namespace, '/system/migrate', array(
90 array(
91 'methods' => \WP_REST_Server::READABLE,
92 'callback' => array($this, 'migrate'),
93 'permission_callback' => array($this, 'get_permission')
94 )
95 ));
96
97 register_rest_route($namespace, '/importer', array(
98 array(
99 'methods' => \WP_REST_Server::CREATABLE,
100 'callback' => array($this, 'save_importer'),
101 'permission_callback' => array($this, 'get_permission')
102 )
103 ));
104
105 register_rest_route($namespace, '/importers', array(
106 array(
107 'methods' => \WP_REST_Server::READABLE,
108 'callback' => array($this, 'get_importers'),
109 'permission_callback' => array($this, 'get_permission')
110 ),
111 ));
112
113 register_rest_route($namespace, '/status', array(
114 array(
115 'methods' => \WP_REST_Server::READABLE,
116 'callback' => array($this, 'get_status'),
117 'permission_callback' => array($this, 'get_permission')
118 ),
119 ));
120
121 register_rest_route($namespace, '/importer/(?P<id>\d+)', array(
122 array(
123 'methods' => \WP_REST_Server::READABLE,
124 'callback' => array($this, 'get_importer'),
125 'permission_callback' => array($this, 'get_permission')
126 ),
127 array(
128 'methods' => \WP_REST_Server::EDITABLE,
129 'callback' => array($this, 'save_importer'),
130 'permission_callback' => array($this, 'get_permission')
131 ),
132 array(
133 'methods' => \WP_REST_Server::DELETABLE,
134 'callback' => array($this, 'delete_importer'),
135 'permission_callback' => array($this, 'get_permission')
136 ),
137 ));
138
139 register_rest_route($namespace, '/importer/(?P<id>\d+)/upload', array(
140 array(
141 'methods' => \WP_REST_Server::EDITABLE,
142 'callback' => array($this, 'attach_file'),
143 'permission_callback' => array($this, 'get_permission')
144 )
145 ));
146
147 register_rest_route($namespace, '/importer/(?P<id>\d+)/file-process', array(
148 array(
149 'methods' => \WP_REST_Server::CREATABLE,
150 'callback' => array($this, 'get_file_process'),
151 'permission_callback' => array($this, 'get_permission')
152 )
153 ));
154
155 register_rest_route($namespace, '/importer/(?P<id>\d+)/file-preview', array(
156 array(
157 'methods' => \WP_REST_Server::CREATABLE,
158 'callback' => array($this, 'get_file_preview'),
159 'permission_callback' => array($this, 'get_permission')
160 )
161 ));
162
163 register_rest_route($namespace, '/importer/(?P<id>\d+)/preview', array(
164 array(
165 'methods' => \WP_REST_Server::CREATABLE,
166 'callback' => array($this, 'get_preview'),
167 'permission_callback' => array($this, 'get_permission')
168 )
169 ));
170
171 register_rest_route($namespace, '/importer/(?P<id>\d+)/init', array(
172 array(
173 'methods' => \WP_REST_Server::READABLE,
174 'callback' => array($this, 'init_import'),
175 'permission_callback' => array($this, 'get_permission')
176 )
177 ));
178
179 register_rest_route($namespace, '/importer/(?P<id>\d+)/run', array(
180 array(
181 'methods' => \WP_REST_Server::CREATABLE,
182 'callback' => array($this, 'run_import'),
183 'permission_callback' => array($this, 'get_permission')
184 )
185 ));
186
187 register_rest_route($namespace, '/importer/(?P<id>\d+)/pause', array(
188 array(
189 'methods' => \WP_REST_Server::CREATABLE,
190 'callback' => array($this, 'pause_import'),
191 'permission_callback' => array($this, 'get_permission')
192 )
193 ));
194
195 register_rest_route($namespace, '/importer/(?P<id>\d+)/stop', array(
196 array(
197 'methods' => \WP_REST_Server::CREATABLE,
198 'callback' => array($this, 'stop_import'),
199 'permission_callback' => array($this, 'get_permission')
200 )
201 ));
202
203 register_rest_route($namespace, '/importer/(?P<id>\d+)/field', array(
204 array(
205 'methods' => \WP_REST_Server::CREATABLE,
206 'callback' => array($this, 'get_template_field_options'),
207 'permission_callback' => array($this, 'get_permission')
208 )
209 ));
210
211 register_rest_route($namespace, '/importer/(?P<id>\d+)/logs/(?P<session>\S+)', array(
212 array(
213 'methods' => \WP_REST_Server::READABLE,
214 'callback' => array($this, 'get_log'),
215 'permission_callback' => array($this, 'get_permission')
216 )
217 ));
218
219 register_rest_route($namespace, '/importer/(?P<id>\d+)/logs', array(
220 array(
221 'methods' => \WP_REST_Server::READABLE,
222 'callback' => array($this, 'get_logs'),
223 'permission_callback' => array($this, 'get_permission')
224 )
225 ));
226
227 register_rest_route($namespace, '/settings', array(
228 array(
229 'methods' => \WP_REST_Server::CREATABLE,
230 'callback' => array($this, 'save_settings'),
231 'permission_callback' => array($this, 'get_permission')
232 ),
233 array(
234 'methods' => \WP_REST_Server::READABLE,
235 'callback' => array($this, 'get_settings'),
236 'permission_callback' => array($this, 'get_permission')
237 ),
238 ));
239
240 register_rest_route($namespace, '/compatibility', array(
241 array(
242 'methods' => \WP_REST_Server::CREATABLE,
243 'callback' => array($this, 'save_compatibility'),
244 'permission_callback' => array($this, 'get_permission')
245 ),
246 array(
247 'methods' => \WP_REST_Server::READABLE,
248 'callback' => array($this, 'get_compatibility'),
249 'permission_callback' => array($this, 'get_permission')
250 ),
251 ));
252
253 register_rest_route($namespace, '/import-export', array(
254 array(
255 'methods' => \WP_REST_Server::CREATABLE,
256 'callback' => array($this, 'import_exporters'),
257 'permission_callback' => array($this, 'get_permission')
258 ),
259 array(
260 'methods' => \WP_REST_Server::READABLE,
261 'callback' => array($this, 'export_importers'),
262 'permission_callback' => array($this, 'get_permission')
263 ),
264 ));
265
266 register_rest_route($namespace, '/importer/(?P<id>\d+)/debug_log', array(
267 array(
268 'methods' => \WP_REST_Server::READABLE,
269 'callback' => array($this, 'get_debug_log'),
270 'permission_callback' => array($this, 'get_permission')
271 )
272 ));
273
274 register_rest_route($namespace, '/importer/(?P<id>\d+)/template', array(
275 array(
276 'methods' => \WP_REST_Server::READABLE,
277 'callback' => array($this, 'get_template'),
278 'permission_callback' => array($this, 'get_permission')
279 )
280 ));
281
282 register_rest_route($namespace, '/importer/(?P<id>\d+)/template_unique_identifiers', array(
283 array(
284 'methods' => \WP_REST_Server::READABLE,
285 'callback' => array($this, 'get_importer_unique_identifier_options'),
286 'permission_callback' => array($this, 'get_permission')
287 )
288 ));
289
290 // Exporter
291
292 register_rest_route($namespace, '/exporter', array(
293 array(
294 'methods' => \WP_REST_Server::CREATABLE,
295 'callback' => array($this, 'save_exporter'),
296 'permission_callback' => array($this, 'get_permission')
297 )
298 ));
299
300 register_rest_route($namespace, '/exporters', array(
301 array(
302 'methods' => \WP_REST_Server::READABLE,
303 'callback' => array($this, 'get_exporters'),
304 'permission_callback' => array($this, 'get_permission')
305 ),
306 ));
307
308 register_rest_route($namespace, '/exporter/(?P<id>\d+)', array(
309 array(
310 'methods' => \WP_REST_Server::READABLE,
311 'callback' => array($this, 'get_exporter'),
312 'permission_callback' => array($this, 'get_permission')
313 ),
314 array(
315 'methods' => \WP_REST_Server::EDITABLE,
316 'callback' => array($this, 'save_exporter'),
317 'permission_callback' => array($this, 'get_permission')
318 ),
319 array(
320 'methods' => \WP_REST_Server::DELETABLE,
321 'callback' => array($this, 'delete_exporter'),
322 'permission_callback' => array($this, 'get_permission')
323 ),
324 ));
325
326 register_rest_route($namespace, '/exporter/(?P<id>\d+)/init', array(
327 array(
328 'methods' => \WP_REST_Server::READABLE,
329 'callback' => array($this, 'init_export'),
330 'permission_callback' => array($this, 'get_permission')
331 )
332 ));
333
334 register_rest_route($namespace, '/exporter/(?P<id>\d+)/download-config', array(
335 array(
336 'methods' => \WP_REST_Server::READABLE,
337 'callback' => array($this, 'download_exporter_config'),
338 'permission_callback' => array($this, 'get_permission')
339 )
340 ));
341
342 register_rest_route($namespace, '/importer/read-config', array(
343 array(
344 'methods' => \WP_REST_Server::CREATABLE,
345 'callback' => array($this, 'read_exporter_config'),
346 'permission_callback' => array($this, 'get_permission')
347 )
348 ));
349
350 register_rest_route($namespace, '/exporter/(?P<id>\d+)/run', array(
351 array(
352 'methods' => \WP_REST_Server::CREATABLE,
353 'callback' => array($this, 'run_exporter'),
354 'permission_callback' => array($this, 'get_permission')
355 ),
356 ));
357
358 register_rest_route($namespace, '/exporter/status', array(
359 array(
360 'methods' => \WP_REST_Server::READABLE,
361 'callback' => array($this, 'get_exporter_status'),
362 'permission_callback' => array($this, 'get_permission')
363 ),
364 ));
365 }
366
367 public function get_permission()
368 {
369
370 if (!current_user_can('manage_options')) {
371 return new \WP_Error('rest_forbidden', __('You do not have permissions.', 'jc-importer'), array('status' => 401));
372 }
373
374 return true;
375 }
376
377 public function sanitize($data, $name = null, $path = [])
378 {
379 $path[] = $name;
380
381 if (is_array($data)) {
382 foreach ($data as $k => $v) {
383 $data[$k] = $this->sanitize($v, $k, $path);
384 }
385
386 return $data;
387 }
388
389 $full_path = implode('.', array_filter($path));
390 if (!is_null($full_path)) {
391
392 if (preg_match('/^map\.\S+/', $full_path) !== false) {
393 return $data;
394 }
395 }
396
397 if (!is_null($name)) {
398 switch ($name) {
399 case 'id':
400 return intval($data);
401 }
402 }
403
404 return sanitize_text_field($data);
405 }
406
407 public function check_rest_status(\WP_REST_Request $request)
408 {
409 $result = [];
410 // find required php ext: https://github.com/llaville/php-compat-info
411
412 $tmp_writable = wp_is_writable($this->filesystem->get_temp_directory());
413
414
415 $result = [
416 'tmp_writable' => [
417 'status' => $tmp_writable ? 'yes' : 'no',
418 'message' => $tmp_writable ? '' : sprintf(__('WordPress is unable to write to directory: %s, make sure this directory is writable.', 'jc-importer'), $this->filesystem->get_temp_directory()),
419 ],
420 'rest_enabled' => [
421 'status' => 'yes',
422 'message' => '',
423 ],
424 'ext_simplexml' => [
425 'status' => extension_loaded('SimpleXML') ? 'yes' : 'no',
426 'message' => '',
427 ],
428 'ext_mbstring' => [
429 'status' => extension_loaded('mbstring') ? 'yes' : 'no',
430 'message' => '',
431 ],
432 'ext_xmlreader' => [
433 'status' => extension_loaded('xmlreader') ? 'yes' : 'no',
434 'message' => '',
435 ],
436 'php_version' => [
437 'status' => version_compare(phpversion(), '5.5.0') ? 'yes' : 'no',
438 'message' => '',
439 ],
440 'zip_archive' => [
441 'status' => ZipArchive::has_requirements_met() ? 'yes' : 'no'
442 ]
443 ];
444 return $this->http->end_rest_success($result);
445 }
446
447 public function migrate(\WP_REST_Request $request)
448 {
449 $migration = new Migrations();
450 $result = $migration->migrate();
451
452 return $this->http->end_rest_success(__("Migration Complete.", 'jc-importer'));
453 }
454
455 public function get_importers(\WP_REST_Request $request)
456 {
457 $importers = $this->importer_manager->get_importers();
458 $result = [];
459
460 foreach ($importers as $importer) {
461 $result[] = $importer->data();
462 }
463
464 return $this->http->end_rest_success($result);
465 }
466
467 public function get_importer(\WP_REST_Request $request)
468 {
469 Logger::setRequestType('get_importer');
470
471 $id = intval($request->get_param('id'));
472 $importer_data = $this->importer_manager->get_importer($id);
473 $response = $importer_data->data('public');
474 return $this->http->end_rest_success($response);
475 }
476
477 public function save_importer(\WP_REST_Request $request)
478 {
479 Logger::setRequestType('save_importer');
480
481 $post_data = $request->get_body_params();
482 $id = isset($post_data['id']) ? intval($post_data['id']) : null;
483
484 $temp_post_data = [
485 'create' => [],
486 'update' => []
487 ];
488
489 if (isset($post_data['permissions'])) {
490 // TODO: update sanitize method to keep track of hierarchy
491 $temp_post_data['create'] = explode("\n", $post_data['permissions']['create']['fields']);
492 $temp_post_data['update'] = explode("\n", $post_data['permissions']['update']['fields']);
493 }
494
495 $post_data = $this->sanitize($post_data);
496
497 if (isset($post_data['permissions'])) {
498 // TODO: Method to bypass sanitization not needed once sanitize method has been upgraded
499 foreach (['create', 'update'] as $permission_method) {
500 $permission_fields = $temp_post_data[$permission_method];
501 if (!empty($permission_fields)) {
502 $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'))));
503 } else {
504 $post_data['permissions'][$permission_method]['fields'] = [];
505 }
506 }
507 }
508
509 $importer = new ImporterModel($id, $this->importer_manager->is_debug());
510 if (isset($post_data['datasource'])) {
511 $importer->setDatasource($post_data['datasource']);
512 }
513
514 if (isset($post_data['remote_url'])) {
515 $importer->setDatasourceSetting('remote_url', $post_data['remote_url']);
516 }
517
518 if (isset($post_data['local_url'])) {
519 $importer->setDatasourceSetting('local_url', wp_normalize_path($post_data['local_url']));
520 }
521
522 if (isset($post_data['existing_id'])) {
523 $importer->setFileId(intval($post_data['existing_id']));
524 }
525
526 if (isset($post_data['template'], $post_data['template_type'])) {
527 $importer->setTemplate($post_data['template'], $post_data['template_type']);
528
529 // Get default template options
530 $template_class = $this->importer_manager->get_template($post_data['template']);
531
532 /**
533 * @var Template $template
534 */
535 $template = $this->template_manager->load_template($template_class);
536 $template_options = $template->get_default_template_options();
537
538 if (isset($post_data['template_options'])) {
539 $template_options = array_merge($template_options, $post_data['template_options']);
540 }
541
542 // setup default enabled fields
543 $default_enabled_fields = $template->get_default_enabled_fields();
544 foreach ($default_enabled_fields as $default_enabled_field) {
545 $importer->setEnabled($default_enabled_field);
546 }
547
548 foreach ($template_options as $key => $val) {
549 $importer->setSetting($key, $val);
550 }
551 }
552
553 if (isset($post_data['name'])) {
554 $importer->setName($post_data['name']);
555 }
556
557 if (is_null($id)) {
558
559 // NOTE: New importer, should we generate field map from exporter.
560 if (isset($post_data['setup_type']) && in_array($post_data['setup_type'], ['upload', 'generate'])) {
561
562 $setup_type = $post_data['setup_type'] === 'upload' ? 'upload' : 'generate';
563 $file_type = null;
564
565 if ($setup_type === 'upload') {
566 $config = json_decode($post_data['exporter_config_file'], true);
567 $file_type = $config['data']['file_type'];
568 $fields = $config['fields'];
569 $formatted_fields = $config['formatted_fields'];
570 $file_settings = $config['data']['file_settings'];
571 } else {
572
573 /**
574 * @var \ImportWP\Common\Model\ExporterModel $exporter_data
575 */
576 $exporter_data = $this->exporter_manager->get_exporter($post_data['exporter']);
577 $fields = $this->exporter_manager->get_importer_map_fields($exporter_data);
578 $mapper = $this->exporter_manager->get_exporter_mapper($exporter_data);
579 $formatted_fields = $mapper->get_fields();
580 $file_type = $exporter_data->getFileType();
581 $file_settings = $exporter_data->getFileSettings();
582 }
583
584 if (is_null($file_type) || !in_array($file_type, ['xml', 'csv'])) {
585 return $this->http->end_rest_error('Invalid exporter file type');
586 }
587
588 $importer->setParser($file_type);
589
590 $headings = [];
591 switch ($file_type) {
592 case 'csv':
593 $headings = array_reduce($fields, function ($carry, $item) {
594 $carry[] = $item['selection'];
595 return $carry;
596 }, []);
597 $headings = array_map('trim', $headings);
598
599 if (isset($file_settings['delimiter']) && !empty($file_settings['delimiter']) && strlen($file_settings['delimiter']) === 1) {
600 $importer->setFileSetting('delimiter', $file_settings['delimiter']);
601 }
602 if (isset($file_settings['enclosure']) && !empty($file_settings['enclosure']) && strlen($file_settings['enclosure']) === 1) {
603 $importer->setFileSetting('enclosure', $file_settings['enclosure']);
604 }
605 if (isset($file_settings['escape']) && !empty($file_settings['escape']) && strlen($file_settings['escape']) === 1) {
606 $importer->setFileSetting('escape', $file_settings['escape']);
607 }
608
609 break;
610 case 'xml':
611
612 $main = false;
613 foreach ($fields as $field) {
614 if ($field['selection'] === 'main' && ($field['loop'] === true || $field['loop'] === "true")) {
615 $main = $field;
616 break;
617 }
618 }
619
620 if (!$main) {
621 return $this->http->end_rest_error("XML exporter is missing main loop.");
622 }
623
624 // generate base_path
625 $post_data['file_settings_base_path'] = '/' . implode('/', array_reverse(array_filter($this->generate_base_path($main, $fields))));
626
627 $allowed = [$main['id']];
628
629 $current_section = null;
630 $current_section_ancestors = [];
631 $current_section_map = [];
632
633 foreach ($fields as $field) {
634
635 if (in_array($field['parent'], $allowed)) {
636
637 $field_map = '/' . implode('/', array_reverse(array_filter($this->generate_base_path($field, $fields, [], $main['id']))));
638 $headings[$field_map] = $field['selection'];
639
640 if ($field['loop'] === true || $field['loop'] === 'true') {
641
642 // we need to convert all sub fields to tax_category.id ....
643 $current_section = $field['selection'];
644 $current_section_ancestors = [$field['id']];
645 } else {
646
647 if (!is_null($current_section) && in_array($field['parent'], $current_section_ancestors)) {
648
649 $current_section_ancestors[] = $field['id'];
650 $current_section_map[$field_map] = $current_section . '.' . $field['selection'];
651 } else {
652 $headings = $this->complete_section_map($headings, $current_section_map, $current_section, $formatted_fields);
653 $current_section = null;
654 $current_section_ancestors = [];
655 $current_section_map = [];
656 }
657 }
658
659 if (!in_array($field['id'], $allowed)) {
660 $allowed[] = $field['id'];
661 }
662 }
663 }
664
665 $headings = $this->complete_section_map($headings, $current_section_map, $current_section, $formatted_fields);
666
667 $tmp = [];
668 foreach ($headings as $map => $heading) {
669 $tmp[$map] = $heading;
670 }
671
672 $headings = $tmp;
673 break;
674 }
675
676 if (!empty($headings)) {
677 $template = $this->importer_manager->get_importer_template($importer);
678 $field_map = $template->generate_field_map($headings, $importer);
679 $field_map = apply_filters('iwp/importer/generate_field_map', $field_map, $headings, $importer);
680
681 $post_data['map'] = $field_map['map'];
682 $post_data['enabled'] = $field_map['enabled'];
683 }
684 }
685 }
686
687 if (isset($post_data['setting_start_row'])) {
688 $importer->setStartRow($post_data['setting_start_row']);
689 }
690
691 if (isset($post_data['setting_max_row'])) {
692 $importer->setMaxRow($post_data['setting_max_row']);
693 }
694
695 // save settings
696 foreach ($post_data as $key => $value) {
697 if (1 === preg_match('/^setting_(?<key>.*)/', $key, $matches)) {
698
699 $value = $this->sanitize_setting($value);
700 $importer->setSetting($matches['key'], $value);
701 }
702 }
703
704 if (isset($post_data['setting_import_method']) && $post_data['setting_import_method'] === 'background') {
705 delete_post_meta($importer->getId(), '_iwp_cron_scheduled');
706 delete_post_meta($importer->getId(), '_iwp_cron_last_ran');
707 }
708
709 $parser = $importer->getParser();
710
711 if (isset($post_data['file_settings_encoding'])) {
712 $importer->setFileSetting('file_encoding', $post_data['file_settings_encoding']);
713 }
714
715 if ($parser === 'csv') {
716
717 $clear_config = false;
718 if (isset($post_data['file_settings_delimiter'])) {
719
720 $old_delimiter = $importer->getFileSetting('delimiter');
721 if ($old_delimiter !== $post_data['file_settings_delimiter']) {
722 $clear_config = true;
723 }
724 $importer->setFileSetting('delimiter', $post_data['file_settings_delimiter']);
725 }
726 if (isset($post_data['file_settings_enclosure'])) {
727 $old_enclosure = $importer->getFileSetting('enclosure');
728 if ($old_enclosure !== $post_data['file_settings_enclosure']) {
729 $clear_config = true;
730 }
731 $importer->setFileSetting('enclosure', $post_data['file_settings_enclosure']);
732 }
733
734 if (isset($post_data['file_settings_escape'])) {
735 $old_escape = $importer->getFileSetting('escape');
736 if ($old_escape !== $post_data['file_settings_escape']) {
737 $clear_config = true;
738 }
739 $importer->setFileSetting('escape', $post_data['file_settings_escape']);
740 }
741
742
743 if (isset($post_data['file_settings_show_headings'])) {
744 $importer->setFileSetting('show_headings', $post_data['file_settings_show_headings'] === 'true' || $post_data['file_settings_show_headings'] === true ? true : false);
745 }
746
747 if ($clear_config) {
748 $this->importer_manager->clear_config_files($importer->getId(), true);
749 }
750 } elseif ($parser === 'xml') {
751 if (isset($post_data['file_settings_base_path'])) {
752 $importer->setFileSetting('base_path', $post_data['file_settings_base_path']);
753 }
754 }
755
756 if (isset($post_data['file_settings_setup'])) {
757 $importer->setFileSetting('setup', $post_data['file_settings_setup'] === 'true' ? true : false);
758 }
759
760 if (isset($post_data['map']) && is_array($post_data['map'])) {
761 foreach ($post_data['map'] as $key => $value) {
762
763 if (1 === preg_match('/\._index$/', $key)) {
764 $value = intval($value);
765 }
766
767 $importer->setMap($key, $value);
768 }
769 }
770
771 if (isset($post_data['enabled']) && is_array($post_data['enabled'])) {
772 foreach ($post_data['enabled'] as $key => $value) {
773 if ($value === 'true') {
774 $importer->setEnabled($key);
775 } else {
776 $importer->setEnabled($key, false);
777 }
778 }
779 }
780
781 if (isset($post_data['permissions']) && is_array($post_data['permissions'])) {
782
783 // create
784 if (isset($post_data['permissions']['create'])) {
785 $create = $post_data['permissions']['create'];
786 $importer->setPermission('create', [
787 'enabled' => isset($create['enabled']) && $create['enabled'] === 'true' ? true : false,
788 'type' => isset($create['type']) && in_array($create['type'], ['include', 'exclude'], true) ? $create['type'] : null,
789 'fields' => isset($create['fields']) ? $create['fields'] : [],
790 ]);
791 }
792
793 // update
794 if (isset($post_data['permissions']['update'])) {
795 $update = $post_data['permissions']['update'];
796 $importer->setPermission('update', [
797 'enabled' => isset($update['enabled']) && $update['enabled'] === 'true' ? true : false,
798 'type' => isset($update['type']) && in_array($update['type'], ['include', 'exclude'], true) ? $update['type'] : null,
799 'fields' => isset($update['fields']) ? $update['fields'] : [],
800 ]);
801 }
802
803 // remove
804 if (isset($post_data['permissions']['remove'])) {
805 $importer->setPermission('remove', [
806 'enabled' => $post_data['permissions']['remove']['enabled'] === 'true',
807 'trash' => $post_data['permissions']['remove']['trash'] === 'true',
808 'media' => $post_data['permissions']['remove']['media'] === 'true',
809 ]);
810 }
811 }
812
813 $result = $importer->save();
814 if (is_wp_error($result)) {
815 return $this->http->end_rest_error($result->get_error_message());
816 }
817 return $this->http->end_rest_success($importer->data());
818 }
819
820 public function generate_base_path($field, $fields, $output = [], $stop = 0)
821 {
822 $output[] = isset($field['label']) && !empty($field['label']) ? $field['label'] : $field['selection'];
823
824 if ($field['parent'] !== $stop) {
825
826 foreach ($fields as $sub_field) {
827 if ($sub_field['id'] == $field['parent']) {
828
829 return $this->generate_base_path($sub_field, $fields, $output, $stop);
830 }
831 }
832 }
833 return $output;
834 }
835
836 public function complete_section_map($headings, $current_section_map, $current_section, $fields)
837 {
838 if (is_null($current_section)) {
839 return $headings;
840 }
841
842 if ($current_section === 'custom_fields') {
843 // $headings['/test/@value'] = 'custom_fields.{/test/@key}';
844
845 $meta_key = false;
846 $meta_val = false;
847
848 if (count($current_section_map) == 2) {
849 foreach ($current_section_map as $row_map => $row_value) {
850 if ($row_value == 'custom_fields.meta_key') {
851 $meta_key = $row_map;
852 } elseif ($row_value == 'custom_fields.meta_value') {
853 $meta_val = $row_map;
854 }
855 }
856
857 if ($meta_key && $meta_val) {
858
859 // Get full list of custom fields, that can be then be used to generate a full list
860 // /custom_fields_wrapper/custom_fields[meta_key="_edit_lock"]/meta_key
861 // /custom_fields_wrapper/custom_fields[meta_key="_edit_lock"]/meta_value
862
863 // Compare the two strings and get the matching parts
864 $meta_key_parts = array_values(array_filter(explode('/', $meta_key)));
865 $meta_val_parts = array_values(array_filter(explode('/', $meta_val)));
866
867 for ($i = 0; $i < count($meta_key_parts); $i++) {
868 if ($meta_key_parts[$i] !== $meta_val_parts[$i]) {
869 break;
870 }
871 }
872
873 $start = array_slice($meta_key_parts, 0, $i);
874 $end_key = array_slice($meta_key_parts, $i);
875 $end_val = array_slice($meta_val_parts, $i);
876
877 $start_path = implode('/', $start);
878 $end_key_path = implode('/', $end_key);
879 $end_val_path = implode('/', $end_val);
880
881 $custom_field_key_list = $fields['children']['custom_fields']['fields'];
882 foreach ($custom_field_key_list as $custom_field_key) {
883
884 // $tmp_field_key = '{/' . $start_path . sprintf('[%s="%s"]', $end_key_path, $custom_field_key) . '/' . $end_key_path . '}';
885 $tmp_field_val = '/' . $start_path . sprintf('[%s="%s"]', $end_key_path, $custom_field_key) . '/' . $end_val_path;
886
887 $headings[$tmp_field_val] = sprintf('custom_fields.%s', $custom_field_key);
888 }
889
890 return $headings;
891 }
892 }
893
894 return array_merge($headings, $current_section_map);
895 }
896
897 return array_merge($headings, $current_section_map);
898 }
899
900 private function sanitize_setting($value)
901 {
902 if (is_array($value)) {
903 foreach ($value as &$tmp) {
904 $tmp = $this->sanitize_setting($tmp);
905 }
906 return $value;
907 } else {
908 if (in_array($value, ['true', 'false'], true)) {
909 if ($value === 'true') {
910 $value = true;
911 } else {
912 $value = false;
913 }
914 }
915 }
916
917 return $value;
918 }
919
920 public function get_status(\WP_REST_Request $request)
921 {
922 $this->http->set_stream_headers();
923
924 Logger::disable();
925
926 $importer_ids = $request->get_param('ids');
927
928 $result = [];
929
930 $query_data = array(
931 'post_type' => IWP_POST_TYPE,
932 'posts_per_page' => -1,
933 'fields' => 'ids'
934 );
935 if (!empty($importer_ids)) {
936 $query_data['post__in'] = $importer_ids;
937 }
938
939 $query = new \WP_Query($query_data);
940
941 foreach ($query->posts as $importer_id) {
942
943 $importer_model = $this->importer_manager->get_importer($importer_id);
944 $config = $this->importer_manager->get_config($importer_model);
945
946 $output = ImporterState::get_state($importer_id);
947 if (!$output) {
948 $output = [
949 'version' => 2,
950 'message' => 'Not setup',
951 'importer' => $importer_id
952 ];
953 } else {
954
955 $output['version'] = 2;
956 $output['message'] = $this->generate_status_message($output);
957 $output['importer'] = $importer_id;
958 $output['process'] = intval($config->get('process'));
959 }
960
961 $result[] = $this->event_handler->run('iwp/importer/status/output', [$output, $importer_model]);
962 }
963
964 echo json_encode($result) . "\n";
965 die();
966 }
967
968 /**
969 * @param array $data
970 * @return string
971 */
972 protected function generate_status_message($data)
973 {
974 $output = '';
975
976 if (!$data || !isset($data['status']) || is_null($data['status']) || empty($data['status'])) {
977 return '';
978 }
979
980 if (isset($data['status'])) {
981 switch ($data['status']) {
982 case 'cancelled':
983 $output .= __('Import cancelled', 'jc-importer');
984 break;
985 case 'complete':
986 $output .= __('Import complete', 'jc-importer');
987 break;
988 case 'error':
989 $output = $data['message'];
990 return $output;
991 break;
992 }
993 }
994
995 if (isset($data['status']) && ($data['status'] === 'running' || $data['status'] === 'processing' || $data['status'] === 'timeout') && isset($data['section'])) {
996 switch ($data['section']) {
997 case 'import':
998 case 'delete':
999 $progress = $data['progress'][$data['section']]['current_row'] . ' / ' . ($data['progress'][$data['section']]['end'] - $data['progress'][$data['section']]['start']);
1000 $output = sprintf($data['section'] === 'import' ? __('Importing: %s', 'jc-importer') : __('Deleting: %s', 'jc-importer'), $progress);
1001 break;
1002 }
1003 }
1004
1005 if (isset($data['stats'])) {
1006 if ($data['stats']['inserts'] > 0) {
1007 $output .= sprintf(__(', Inserts: %d', 'jc-importer'), $data['stats']['inserts']);
1008 }
1009 if ($data['stats']['updates'] > 0) {
1010 $output .= sprintf(__(', Updates: %d', 'jc-importer'), $data['stats']['updates']);
1011 }
1012 if ($data['stats']['deletes'] > 0) {
1013 $output .= sprintf(__(', Deletes: %d', 'jc-importer'), $data['stats']['deletes']);
1014 }
1015 if ($data['stats']['skips'] > 0) {
1016 $output .= sprintf(__(', Skips: %d', 'jc-importer'), $data['stats']['skips']);
1017 }
1018 if ($data['stats']['errors'] > 0) {
1019 $output .= sprintf(__(', Errors: %d', 'jc-importer'), $data['stats']['errors']);
1020 }
1021 }
1022
1023 if (isset($data['timestamp'], $data['updated'])) {
1024 /**
1025 * @var Util $util
1026 */
1027 $util = Container::getInstance()->get('util');
1028 $output .= sprintf(__(', Run Time: %s', 'jc-importer'), $util->format_time($data['updated'] - $data['timestamp']));
1029 } elseif (isset($data['duration'])) {
1030 /**
1031 * @var Util $util
1032 */
1033 $util = Container::getInstance()->get('util');
1034 $output .= sprintf(__(', Run Time: %s', 'jc-importer'), $util->format_time($data['duration']));
1035 }
1036
1037 return $output;
1038 }
1039
1040 public function delete_importer(\WP_REST_Request $request)
1041 {
1042 Logger::setRequestType('delete_importer');
1043
1044 $id = intval($request->get_param('id'));
1045 $this->importer_manager->delete_importer($id);
1046 return $this->http->end_rest_success(true);
1047 }
1048
1049 public function attach_file(\WP_REST_Request $request)
1050 {
1051 $id = intval($request->get_param('id'));
1052 $post_data = $this->sanitize($request->get_body_params());
1053
1054 $action = $post_data['action'];
1055
1056 $importer = $this->importer_manager->get_importer($id);
1057
1058 // TODO: Remove duplicate code, found in cron manager
1059
1060 add_filter('upload_dir', [$this->importer_manager, 'set_custom_upload_path']);
1061
1062 switch ($action) {
1063 case 'file_upload':
1064 $attachment_id = $this->importer_manager->upload_file($importer, $_FILES['file']);
1065 break;
1066 case 'file_remote':
1067 $raw_source = $post_data['remote_url'];
1068 $source = apply_filters('iwp/importer/datasource', $raw_source, $raw_source, $importer);
1069 $source = apply_filters('iwp/importer/datasource/remote', $source, $raw_source, $importer);
1070
1071 $filetype = $importer->getParser();
1072 if (is_null($filetype)) {
1073 $filetype = $post_data['filetype'];
1074 }
1075
1076 $attachment_id = $this->importer_manager->remote_file($importer, $source, $filetype, null, $importer->getId() . '-');
1077 break;
1078 case 'file_local':
1079 $raw_source = $post_data['local_url'];
1080 $source = apply_filters('iwp/importer/datasource', $raw_source, $raw_source, $importer);
1081 $source = apply_filters('iwp/importer/datasource/local', $source, $raw_source, $importer);
1082
1083 $filetype = $importer->getParser();
1084 if (is_null($filetype)) {
1085 $filetype = $post_data['filetype'];
1086 }
1087
1088 $attachment_id = $this->importer_manager->local_file($importer, $source, $filetype);
1089 break;
1090 default:
1091 $attachment_id = new \WP_Error('IWP_RM_1', __('Invalid form action', 'jc-importer'));
1092 break;
1093 }
1094
1095 remove_filter('upload_dir', [$this->importer_manager, 'set_custom_upload_path']);
1096
1097 if (is_wp_error($attachment_id)) {
1098 return $this->http->end_rest_error($attachment_id->get_error_message());
1099 }
1100
1101 return $this->http->end_rest_success($importer->data());
1102 }
1103
1104 public function get_file_process(\WP_REST_Request $request)
1105 {
1106 try {
1107 $id = intval($request->get_param('id'));
1108 $post_data = $this->sanitize($request->get_body_params());
1109
1110 $importer = $this->importer_manager->get_importer($id);
1111 if (!$importer) {
1112 return $this->http->end_rest_error(__('Invalid importer', 'jc-importer'));
1113 }
1114
1115 $this->importer_manager->clear_config_files($id, true);
1116 $parser = $importer->getParser();
1117
1118 if ('xml' === $parser) {
1119
1120 $nodes = $this->importer_manager->process_xml_file($id, true);
1121 $importer->setFileSetting('nodes', $nodes);
1122 } elseif ('csv' === $parser) {
1123
1124 $delimiter = isset($post_data['delimiter']) ? $post_data['delimiter'] : null;
1125 if (is_null($delimiter)) {
1126 $delimiter = $importer->getFileSetting('delimiter', ',');
1127 }
1128
1129 $enclosure = isset($post_data['enclosure']) ? $post_data['enclosure'] : null;
1130 if (is_null($enclosure)) {
1131 $enclosure = $importer->getFileSetting('enclosure', '"');
1132 }
1133
1134 $escape = isset($post_data['escape']) ? $post_data['escape'] : null;
1135 if (is_null($escape)) {
1136 $escape = $importer->getFileSetting('escape', '\\');
1137 }
1138
1139 $count = $this->importer_manager->process_csv_file($id, $delimiter, $enclosure, true);
1140
1141 // Need fresh importer, due to process_csv_file importer modifications might get overwritten.
1142 $importer = $this->importer_manager->get_importer($id);
1143 $importer->setFileSetting('count', $count);
1144 } else {
1145
1146 do_action('iwp/file-process/' . $parser, $importer, $post_data);
1147 }
1148
1149 $importer->setFileSetting('processed', true);
1150 $importer->save();
1151
1152 return $this->http->end_rest_success($importer->data());
1153 } catch (\Exception $e) {
1154 return $this->http->end_rest_error($e->getMessage());
1155 }
1156 }
1157
1158 public function get_file_preview(\WP_REST_Request $request)
1159 {
1160 try {
1161 $id = intval($request->get_param('id'));
1162 $post_data = $this->sanitize($request->get_body_params());
1163 $importer = $this->importer_manager->get_importer($id);
1164
1165 $import_file_exists = $this->filesystem->file_exists($importer->getFile());
1166 if (is_wp_error($import_file_exists)) {
1167 throw new \Exception(sprintf(__("Unable to load preview, %s", 'jc-importer'), $import_file_exists->get_error_message()));
1168 }
1169
1170 $record_index = isset($post_data['record']) ? $post_data['record'] : null;
1171 if (is_null($record_index)) {
1172 $record_index = 0;
1173 }
1174
1175 // Temp set file_encoding
1176 if (isset($post_data['file_encoding'])) {
1177 $importer->setFileSetting('file_encoding', $post_data['file_encoding']);
1178 }
1179
1180 if ($importer->getParser() === 'xml') {
1181
1182 $config = $this->importer_manager->get_config($importer, true);
1183 $file = $this->importer_manager->get_xml_file($importer, $config);
1184
1185 $base_path = $post_data['base_path'];
1186 if (!is_null($base_path)) {
1187 $file->setRecordPath($base_path);
1188 }
1189
1190 $preview = new XMLPreview($file, $base_path);
1191 $result = $preview->data();
1192 if (is_wp_error($result)) {
1193 return $this->http->end_rest_error($result);
1194 }
1195
1196 return $this->http->end_rest_success($result[0]);
1197 } elseif ($importer->getParser() === 'csv') {
1198
1199 $config = $this->importer_manager->get_config($importer, true);
1200 $file = $this->importer_manager->get_csv_file($importer, $config);
1201
1202 $clear_config = false;
1203 if (!is_null($post_data['delimiter']) && $post_data['delimiter'] !== $file->getDelimiter()) {
1204 $clear_config = true;
1205 } elseif (!is_null($post_data['enclosure']) && $post_data['enclosure'] !== $file->getEnclosure()) {
1206 $clear_config = true;
1207 } elseif (!is_null($post_data['escape']) && $post_data['escape'] !== $file->getEscape()) {
1208 $clear_config = true;
1209 }
1210
1211 if ($clear_config) {
1212 $this->importer_manager->clear_config_files($importer->getId(), true);
1213 $config = $this->importer_manager->get_config($importer, true);
1214 $file = $this->importer_manager->get_csv_file($importer, $config);
1215 }
1216
1217 $delimiter = $post_data['delimiter'];
1218 if (!is_null($delimiter)) {
1219 $file->setDelimiter($delimiter);
1220 }
1221
1222 $enclosure = $post_data['enclosure'];
1223 if (!is_null($enclosure)) {
1224 $file->setEnclosure($enclosure);
1225 }
1226
1227 $escape = $post_data['escape'];
1228 if (!is_null($escape)) {
1229 $file->setEscape($escape);
1230 }
1231
1232 // parse bool param from string
1233 $show_headings = $post_data['show_headings'];
1234 if (in_array($show_headings, ['true', 'false'])) {
1235 $show_headings = $show_headings === 'true' ? true : false;
1236 }
1237
1238 if (is_null($show_headings)) {
1239 $show_headings = $importer->getFileSetting('show_headings');
1240 }
1241
1242 $preview = new CSVPreview($file);
1243 $result = $preview->data($record_index, $show_headings);
1244 if (is_wp_error($result)) {
1245 return $this->http->end_rest_error($result);
1246 }
1247
1248 return $this->http->end_rest_success($result);
1249 } else {
1250
1251 $result = apply_filters('iwp/file-preview/' . $importer->getParser(), null, $importer);
1252 if (is_wp_error($result)) {
1253 return $this->http->end_rest_error($result);
1254 }
1255
1256 return $this->http->end_rest_success($result);
1257 }
1258 } catch (\Exception $error) {
1259 return $this->http->end_rest_error($error->getMessage());
1260 }
1261 }
1262
1263 public function get_preview(\WP_REST_Request $request)
1264 {
1265 // if no fields have been posted send all previews
1266 try {
1267 $id = intval($request->get_param('id'));
1268 $importer = $this->importer_manager->get_importer($id);
1269 $import_file_exists = $this->filesystem->file_exists($importer->getFile());
1270 if (is_wp_error($import_file_exists)) {
1271 throw new \Exception(sprintf(__("Unable to load preview, %s", 'jc-importer'), $import_file_exists->get_error_message()));
1272 }
1273
1274 $parser = $importer->getParser();
1275
1276 $fields = $this->sanitize($request->get_body_params());
1277 if (is_null($fields) || empty($fields)) {
1278 $fields = $importer->getMap();
1279 }
1280
1281 if ('xml' === $parser) {
1282 $result = $this->importer_manager->preview_xml_file($importer, $fields);
1283 } elseif ('csv' === $parser) {
1284 $row = 0;
1285 if ($importer->getFileSetting('show_headings') === true) {
1286 $row = 1;
1287 }
1288 $result = $this->importer_manager->preview_csv_file($importer, $fields, $row);
1289 } else {
1290
1291 $result = apply_filters('iwp/record-preview/' . $parser, [], $importer, $fields);
1292 if (is_wp_error($result)) {
1293 return $this->http->end_rest_error($result);
1294 }
1295
1296 return $this->http->end_rest_success($result);
1297 }
1298
1299 return $this->http->end_rest_success($result);
1300 } catch (\Exception $error) {
1301 return $this->http->end_rest_error($error->getMessage());
1302 }
1303 }
1304
1305 /**
1306 * Trigger a new import.
1307 *
1308 * @param \WP_REST_Request $request
1309 * @return array
1310 */
1311 public function init_import(\WP_REST_Request $request)
1312 {
1313 $id = intval($request->get_param('id'));
1314
1315 Logger::setRequestType('rest::init_import');
1316
1317 Logger::write('init_import -start', $id);
1318
1319 $importer_data = $this->importer_manager->get_importer($id);
1320
1321 // clear existing
1322 ImporterState::clear_options($importer_data->getId());
1323
1324 // This is used for storing version on imported records
1325 $session_id = md5($importer_data->getId() . time());
1326 update_post_meta($importer_data->getId(), '_iwp_session', $session_id);
1327
1328 if ('background' === $importer_data->getSetting('import_method')) {
1329 update_post_meta($importer_data->getId(), '_iwp_cron_scheduled', current_time('timestamp'));
1330 delete_post_meta($importer_data->getId(), '_iwp_cron_last_ran');
1331 }
1332
1333 Logger::clearRequestType();
1334
1335 return $this->http->end_rest_success([
1336 'session' => $session_id
1337 ]);
1338 }
1339
1340 public function run_import(\WP_REST_Request $request)
1341 {
1342 $this->http->set_stream_headers();
1343
1344 Logger::setRequestType('rest::run_import');
1345
1346 $session = $request->get_param('session');
1347 $user = uniqid('iwp', true);
1348 $id = intval($request->get_param('id'));
1349 Logger::write('run_import -session=' . $session, $id);
1350
1351 $importer_data = $this->importer_manager->get_importer($id);
1352 Logger::write('run_import -import=start', $id);
1353
1354 $state = $this->importer_manager->import($importer_data->getId(), $user, $session);
1355 $state['message'] = $this->generate_status_message($state);
1356
1357 Logger::clearRequestType();
1358
1359 return $this->http->end_rest_success($state);
1360 }
1361
1362 public function pause_import(\WP_REST_Request $request)
1363 {
1364 $id = intval($request->get_param('id'));
1365 $paused = $this->sanitize($request->get_param('paused'), 'paused');
1366
1367 $state = $this->importer_manager->pause_import($id, $paused);
1368
1369 return $this->http->end_rest_success($state);
1370 }
1371
1372 public function stop_import(\WP_REST_Request $request)
1373 {
1374 $id = intval($request->get_param('id'));
1375
1376 $state = $this->importer_manager->stop_import($id);
1377
1378 return $this->http->end_rest_success($state);
1379 }
1380
1381 /**
1382 * Get field options for current template
1383 *
1384 * @param \WP_REST_Request $request
1385 * @return void
1386 */
1387 public function get_template_field_options(\WP_REST_Request $request)
1388 {
1389 try {
1390
1391 $id = intval($request->get_param('id'));
1392 $field = $this->sanitize($request->get_param('field'), 'field');
1393
1394 $importer_data = $this->importer_manager->get_importer($id);
1395 $template = $this->importer_manager->get_importer_template($id);
1396
1397 return $this->http->end_rest_success($template->get_field_options($field, $importer_data));
1398 } catch (\Exception $e) {
1399 return $this->http->end_rest_error($e->getMessage());
1400 }
1401 }
1402
1403 public function get_logs(\WP_REST_Request $request)
1404 {
1405 $id = intval($request->get_param('id'));
1406 $importer_data = $this->importer_manager->get_importer($id);
1407 $logs = $this->importer_manager->get_importer_logs($importer_data);
1408 return $this->http->end_rest_success(array_reverse($logs));
1409 }
1410
1411 public function get_log(\WP_REST_Request $request)
1412 {
1413 $id = intval($request->get_param('id'));
1414 $session = $this->sanitize($request->get_param('session'), 'session');
1415 $page = intval($request->get_param('page'));
1416 if ($page < 1) {
1417 $page = 1;
1418 }
1419 $importer_data = $this->importer_manager->get_importer($id);
1420 $log = $this->importer_manager->get_importer_log($importer_data, $session, $page, 500);
1421 $status = $this->importer_manager->get_importer_status_report($importer_data, $session);
1422 return $this->http->end_rest_success(['logs' => $log, 'status' => $status]);
1423 }
1424
1425 public function get_debug_log(\WP_REST_Request $request)
1426 {
1427 $id = intval($request->get_param('id'));
1428 $page = intval($request->get_param('page'));
1429 if ($page < 1) {
1430 $page = 1;
1431 }
1432 $importer_data = $this->importer_manager->get_importer($id);
1433 $log = $this->importer_manager->get_importer_debug_log($importer_data, $page, 100);
1434
1435 $download = Logger::getLogFile($importer_data->getId(), true);
1436 return $this->http->end_rest_success(['log' => $log, 'download' => $download]);
1437 }
1438
1439 public function save_settings(\WP_REST_Request $request)
1440 {
1441 $post_data = $request->get_body_params();
1442 $post_data = $this->sanitize($post_data);
1443
1444 $defaults = $this->properties->_default_settings();
1445
1446 $output = [];
1447
1448 foreach ($defaults as $setting => $default) {
1449 if (isset($post_data[$setting])) {
1450 if ('bool' === $default['type']) {
1451 // Handle Boolean
1452 if (is_bool($post_data[$setting])) {
1453 $output[$setting] = $post_data[$setting];
1454 } elseif (in_array($post_data[$setting], ['true', 'false'])) {
1455 $output[$setting] = $post_data[$setting] === 'true' ? true : false;
1456 } else {
1457 $output[$setting] = boolval($post_data[$setting]);
1458 }
1459 } elseif ('number' === $default['type']) {
1460 $output[$setting] = intval($post_data[$setting]);
1461 } else {
1462 $output[$setting] = $post_data[$setting];
1463 }
1464 } else {
1465 $output[$setting] = $default['value'];
1466 }
1467 }
1468
1469 update_option('iwp_settings', $output);
1470
1471 return $this->http->end_rest_success($this->properties->get_settings());
1472 }
1473
1474 public function get_settings(\WP_REST_Request $request = null)
1475 {
1476 $output = $this->properties->get_settings();
1477 return $this->http->end_rest_success($output);
1478 }
1479
1480 public function get_template(\WP_REST_Request $request = null)
1481 {
1482 Logger::setRequestType('get_template');
1483
1484 $importer_id = $request->get_param('id');
1485 $importer_model = $this->importer_manager->get_importer($importer_id);
1486 $importer_template = $importer_model->getTemplate();
1487
1488 $templates = $this->importer_manager->get_templates();
1489
1490 if (!isset($templates[$importer_template])) {
1491 return $this->http->end_rest_error(sprintf(__("Invalid template \"%s\"", 'jc-importer'), $importer_template));
1492 }
1493
1494 $template_id = $templates[$importer_template];
1495 $template_class = $this->template_manager->load_template($template_id);
1496
1497 $output = [
1498 'id' => $template_id,
1499 'label' => $template_class->get_name(),
1500 'map' => $template_class->get_fields($importer_model),
1501 'settings' => $template_class->register_settings(),
1502 'options' => $template_class->register_options(),
1503 'permission_fields' => apply_filters('iwp/template/permission_fields', $template_class->get_permission_fields($importer_model), $template_class)
1504 ];
1505 return $this->http->end_rest_success($output);
1506 }
1507
1508 public function export_importers(\WP_REST_Request $request = null)
1509 {
1510 Logger::setRequestType('export_importers');
1511
1512 $importer_ids = $request->get_param('ids');
1513
1514 $sql_ids = array_map(function ($v) {
1515 return "'" . esc_sql($v) . "'";
1516 }, $importer_ids);
1517 $sql_ids = implode(',', $sql_ids);
1518
1519 $output = [];
1520
1521 /**
1522 * @var \WPDB $wpdb
1523 */
1524 global $wpdb;
1525 $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);
1526 foreach ($results as $result) {
1527
1528 $data = unserialize($result['post_content']);
1529
1530 unset($data['file']['id']);
1531 unset($data['file']['settings']['count']);
1532 unset($data['file']['settings']['processed']);
1533
1534
1535 $row = [
1536 'name' => $result['post_title'],
1537 'data' => $data
1538 ];
1539
1540 if ($this->importer_manager->is_debug()) {
1541 $row['raw'] = base64_encode($result['post_content']);
1542 }
1543
1544 $output[] = $row;
1545 }
1546
1547 $json = json_encode($output);
1548
1549 header('Content-disposition: attachment; filename="ImportWP-export-' . date('Y-m-d') . '.json"');
1550 header('Content-type: "application/json"; charset="utf8"');
1551 header('Expires: 0');
1552 header('Cache-Control: must-revalidate');
1553 header('Pragma: public');
1554 header('Content-Length: ' . strlen($json));
1555
1556 echo $json;
1557 die();
1558 }
1559
1560 /**
1561 * Download exporter config json file.
1562 *
1563 * File is used when creating an importer to auto generate field mapping.
1564 *
1565 * @param WP_REST_Request|null $request
1566 * @return never
1567 */
1568 public function download_exporter_config(\WP_REST_Request $request = null)
1569 {
1570 $exporter_id = $request->get_param('id');
1571 $exporter_data = $this->exporter_manager->get_exporter($exporter_id);
1572 $mapper = $this->exporter_manager->get_exporter_mapper($exporter_data);
1573 $fields = $mapper->get_fields();
1574
1575
1576
1577 /**
1578 * @var \WPDB $wpdb
1579 */
1580 global $wpdb;
1581 $result = $wpdb->get_row("SELECT post_title, post_content from {$wpdb->posts} WHERE post_type='" . EWP_POST_TYPE . "' AND ID=" . intval($exporter_id), ARRAY_A);
1582
1583 $output = [
1584 'name' => $result['post_title'],
1585 'data' => unserialize($result['post_content']),
1586 'fields' => $this->exporter_manager->get_importer_map_fields($exporter_data),
1587 'formatted_fields' => $fields
1588 ];
1589
1590 $json = json_encode($output);
1591
1592 header('Content-disposition: attachment; filename="ImportWP-exporter-' . $exporter_id . '-' . date('Y-m-d') . '.json"');
1593 header('Content-type: "application/json"; charset="utf8"');
1594 header('Expires: 0');
1595 header('Cache-Control: must-revalidate');
1596 header('Pragma: public');
1597 header('Content-Length: ' . strlen($json));
1598
1599 echo $json;
1600 die();
1601 }
1602
1603 public function read_exporter_config(\WP_REST_Request $request = null)
1604 {
1605 $upload = $this->filesystem->upload_file($_FILES['file']);
1606 if (is_wp_error($upload)) {
1607 return $this->http->end_rest_error($upload->get_error_message());
1608 }
1609
1610 $json_contents = file_get_contents($upload['dest']);
1611 @unlink($upload['dest']);
1612
1613 $contents = json_decode($json_contents, true);
1614
1615 if (!isset($contents['name'], $contents['fields'], $contents['data'], $contents['data']['file_type'])) {
1616 return $this->http->end_rest_error("Invalid exporter config file.");
1617 }
1618
1619 if (!in_array($contents['data']['file_type'], ['xml', 'csv'])) {
1620 return $this->http->end_rest_error("Exporter file type is not supported.");
1621 }
1622
1623 return $this->http->end_rest_success(['exporter' => $contents]);
1624 }
1625
1626 public function get_compatibility_settings()
1627 {
1628 $output = [];
1629
1630 if (!function_exists('get_plugins')) {
1631 require_once ABSPATH . 'wp-admin/includes/plugin.php';
1632 }
1633
1634 $blacklisted = (array)get_option('iwp_compat_blacklist', []);
1635 $whitelisted = apply_filters('iwp/compat/whitelist', [
1636 'jc-importer/jc-importer.php'
1637 ]);
1638
1639 $all_plugins = get_plugins();
1640 foreach ($all_plugins as $plugin_id => $plugin_data) {
1641
1642
1643
1644 if (preg_match('/^importwp-/', $plugin_id, $matches) === 1 || in_array($plugin_id, $whitelisted)) {
1645 continue;
1646 }
1647
1648 $output[$plugin_id] = [
1649 'id' => $plugin_id,
1650 'name' => $plugin_data['Name'],
1651 'enabled' => in_array($plugin_id, $blacklisted) ? 'yes' : 'no'
1652 ];
1653 }
1654
1655 return $output;
1656 }
1657
1658 public function get_compatibility(\WP_REST_Request $request = null)
1659 {
1660 $output = $this->get_compatibility_settings();
1661
1662 return $this->http->end_rest_success($output);
1663 }
1664
1665 public function save_compatibility(\WP_REST_Request $request)
1666 {
1667 $post_data = $request->get_body_params();
1668 $post_data = $this->sanitize($post_data);
1669
1670 if (!function_exists('get_plugins')) {
1671 require_once ABSPATH . 'wp-admin/includes/plugin.php';
1672 }
1673 $plugin_keys = array_keys(get_plugins());
1674
1675
1676 $tmp = [];
1677
1678 if (isset($post_data['plugins'])) {
1679
1680 foreach ($post_data['plugins'] as $plugin_id) {
1681 if (!in_array($plugin_id, $plugin_keys)) {
1682 continue;
1683 }
1684
1685 $tmp[] = "" . $plugin_id;
1686 }
1687 }
1688
1689 update_option('iwp_compat_blacklist', $tmp);
1690
1691 $output = $this->get_compatibility_settings();
1692 return $this->http->end_rest_success($output);
1693 }
1694
1695 public function import_exporters(\WP_REST_Request $request = null)
1696 {
1697 Logger::setRequestType('import_exporters');
1698
1699 $upload = $this->filesystem->upload_file($_FILES['file']);
1700 if (is_wp_error($upload)) {
1701 return $this->http->end_rest_error($upload->get_error_message());
1702 }
1703
1704 $json_contents = file_get_contents($upload['dest']);
1705 @unlink($upload['dest']);
1706
1707 $contents = json_decode($json_contents, true);
1708 $counter = 0;
1709 $errors = [];
1710 if (!empty($contents)) {
1711 foreach ($contents as $importer) {
1712
1713 $result = wp_insert_post([
1714 'post_type' => IWP_POST_TYPE,
1715 'post_title' => $importer['name'],
1716 'post_status' => 'publish',
1717 'post_author' => get_current_user_id(),
1718 'post_content' => wp_slash(serialize($importer['data']))
1719 ], true);
1720
1721 if (is_wp_error($result)) {
1722 $errors[] = $result->get_error_message();
1723 continue;
1724 }
1725
1726 $counter++;
1727 }
1728 }
1729
1730 if (empty($counter)) {
1731 return $this->http->end_rest_error(__('No Importers found.', 'jc-importer'));
1732 }
1733
1734 return $this->http->end_rest_success(sprintf(_n("Import Complete, %d Importer", "Import Complete, %d Importers", $counter, 'jc-importer'), $counter));
1735 }
1736
1737 public function save_exporter(\WP_REST_Request $request = null)
1738 {
1739 Logger::setRequestType('save_exporter');
1740
1741 $post_data = $request->get_body_params();
1742 $id = isset($post_data['id']) ? intval($post_data['id']) : null;
1743
1744
1745 $exporter = new ExporterModel($id, $this->importer_manager->is_debug());
1746
1747 if (isset($post_data['name'])) {
1748 $exporter->setName($post_data['name']);
1749 }
1750
1751 if (isset($post_data['type'])) {
1752 $exporter->setType($post_data['type']);
1753 }
1754
1755 if (isset($post_data['file_type'])) {
1756 $exporter->setFileType($post_data['file_type']);
1757 }
1758
1759 if (isset($post_data['file_settings'])) {
1760 $exporter->setFileSettings($post_data['file_settings']);
1761 }
1762
1763 if (isset($post_data['fields']) || $id > 0) {
1764 $exporter->setFields(isset($post_data['fields']) ? $post_data['fields'] : []);
1765 }
1766
1767 if (isset($post_data['filters']) || $id > 0) {
1768 $exporter->setFilters(isset($post_data['filters']) ? $post_data['filters'] : []);
1769 }
1770
1771 if (isset($post_data['unique_identifier'])) {
1772 $exporter->setUniqueIdentifier($post_data['unique_identifier']);
1773 }
1774
1775 if (isset($post_data['export_method'])) {
1776 $exporter->setExportMethod($post_data['export_method']);
1777 }
1778
1779 if (isset($post_data['cron'])) {
1780
1781 // TODO: Save cron settings
1782
1783 $cron = $this->sanitize_setting($post_data['cron']);
1784 $exporter->setCron($cron);
1785 }
1786
1787 $result = $exporter->save();
1788 if (is_wp_error($result)) {
1789 return $this->http->end_rest_error($result->get_error_message());
1790 }
1791 return $this->http->end_rest_success($exporter->data());
1792 }
1793
1794 public function get_exporters(\WP_REST_Request $request = null)
1795 {
1796 Logger::setRequestType('get_exporters');
1797
1798 $exporters = $this->exporter_manager->get_exporters();
1799 $result = [];
1800
1801 foreach ($exporters as $exporter) {
1802 $result[] = $exporter->data();
1803 }
1804
1805 return $this->http->end_rest_success($result);
1806 }
1807
1808 public function get_exporter(\WP_REST_Request $request = null)
1809 {
1810 Logger::setRequestType('get_exporter');
1811
1812 $id = intval($request->get_param('id'));
1813 $exporter_data = $this->exporter_manager->get_exporter($id);
1814 $response = $exporter_data->data('public');
1815 return $this->http->end_rest_success($response);
1816 }
1817
1818 public function delete_exporter(\WP_REST_Request $request = null)
1819 {
1820 Logger::setRequestType('delete_exporter');
1821
1822 $id = intval($request->get_param('id'));
1823 $this->exporter_manager->delete_exporter($id);
1824 return $this->http->end_rest_success(true);
1825 }
1826
1827 /**
1828 * Trigger a new export.
1829 *
1830 * @param \WP_REST_Request $request
1831 * @return array
1832 */
1833 public function init_export(\WP_REST_Request $request)
1834 {
1835 $id = intval($request->get_param('id'));
1836
1837 Logger::setRequestType('rest::init_export');
1838
1839 Logger::write('init_import -start', $id);
1840
1841 $exporter_data = $this->exporter_manager->get_exporter($id);
1842
1843 // clear existing
1844 ExporterState::clear_options($exporter_data->getId());
1845
1846 // This is used for storing version on imported records
1847 $session_id = md5($exporter_data->getId() . time());
1848 update_post_meta($exporter_data->getId(), '_iwp_session', $session_id);
1849
1850 Logger::clearRequestType();
1851
1852 return $this->http->end_rest_success([
1853 'session' => $session_id
1854 ]);
1855 }
1856
1857 public function run_exporter(\WP_REST_Request $request)
1858 {
1859 Logger::setRequestType('run_exporter');
1860
1861 $this->http->set_stream_headers();
1862
1863 $id = intval($request->get_param('id'));
1864 $session = $request->get_param('session');
1865 $user = uniqid('iwp', true);
1866
1867 $exporter_data = $this->exporter_manager->get_exporter($id);
1868
1869 $status = $this->exporter_manager->export($exporter_data, $user, $session);
1870 echo json_encode($status);
1871 // Logger::write(__CLASS__ . '::run_import -import=end -status=' . $status->get_status(), $id);
1872
1873 // if (!empty($this->output_cache)) {
1874 // echo $this->output_cache;
1875 // $this->output_cache = '';
1876 // }
1877
1878 // $output = $status->output();
1879 // $output['message'] = $this->generate_exporter_status_message($status);
1880 // echo json_encode($output) . "\n";
1881 die();
1882 }
1883
1884 public function get_exporter_status(\WP_REST_Request $request)
1885 {
1886 Logger::setRequestType('get_exporter_status');
1887
1888 $exporter_ids = $request->get_param('ids');
1889 $result = array();
1890 $query_data = array(
1891 'post_type' => EWP_POST_TYPE,
1892 'posts_per_page' => -1,
1893 'fields' => 'ids'
1894 );
1895
1896 if (!empty($exporter_ids)) {
1897 $query_data['post__in'] = $exporter_ids;
1898 }
1899
1900 $query = new \WP_Query($query_data);
1901
1902 if (!empty($query->posts)) {
1903 foreach ($query->posts as $post_id) {
1904
1905 $exporter = $this->exporter_manager->get_exporter($post_id);
1906 $status = ExporterState::get_state($post_id);
1907 $status['exporter'] = $post_id;
1908 $status['version'] = 2;
1909
1910 if (isset($status['progress'])) {
1911 $current = $status['progress']['export']['current_row'];
1912 $end = $status['progress']['export']['end'] - $status['progress']['export']['start'];
1913 $status['progress'] = $end > 0 ? ($current / $end) * 100 : 0;
1914 } else {
1915 $status['progress'] = 0;
1916 }
1917
1918
1919 $output = '';
1920
1921 if (isset($status['status'])) {
1922 switch ($status['status']) {
1923 case 'cancelled':
1924 $output .= 'Export cancelled';
1925 break;
1926 case 'complete':
1927 $output .= 'Export complete';
1928 break;
1929 case 'processing':
1930 case 'timeout':
1931 case 'running':
1932 $output .= 'Exporting: ' . $current . '/' . $end;
1933 break;
1934 }
1935 }
1936
1937 $status['message'] = $output;
1938
1939 $result[] = $this->event_handler->run('iwp/exporter/status/output', [$status, $exporter]);
1940 }
1941 }
1942
1943 echo json_encode($result) . "\n";
1944 die();
1945 }
1946
1947 public function get_importer_unique_identifier_options(\WP_REST_Request $request)
1948 {
1949 $id = intval($request->get_param('id'));
1950 $importer_model = $this->importer_manager->get_importer($id);
1951 if (!$importer_model) {
1952 return $this->http->end_rest_error(['msg' => 'Invalid importer']);
1953 }
1954
1955 // Get default template options
1956 $template = $this->importer_manager->get_template($importer_model->getTemplate());
1957 if (is_wp_error($template)) {
1958 return $this->http->end_rest_error($template->get_error_message());
1959 }
1960
1961 $template_class = $this->template_manager->load_template($template);
1962 $unique_fields = $this->template_manager->get_template_unique_fields($template_class);
1963 $options = $template_class->get_unique_identifier_options($importer_model, $unique_fields);
1964
1965 // Only add in unqiue fields if they have not been found.
1966 // Allowing for old templates to continue to list unique identifiers.
1967 foreach ($unique_fields as $field_id) {
1968 if (!isset($options[$field_id])) {
1969 $options[$field_id] = [
1970 'value' => $field_id,
1971 'label' => $field_id,
1972 'uid' => true,
1973 'active' => true,
1974 ];
1975 }
1976 }
1977
1978 $options = array_filter($options, function ($item) {
1979 return $item['active'];
1980 });
1981
1982 return $this->http->end_rest_success(['options' => array_values($options), 'unique_fields' => array_values($unique_fields)]);
1983 }
1984 }
1985