PluginProbe
Import WP – CSV & XML Import Export for WordPress / 2.15.0
Import WP – CSV & XML Import Export for WordPress v2.15.0
2.15.1 2.15.0 2.14.24 2.14.23 2.7.0 2.7.1 2.7.10 2.7.11 2.7.12 2.7.13 2.7.14 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 2.7.8 2.7.9 2.8.0 2.8.1 2.8.2 2.8.3 2.9.0 2.9.1 All 144 releases
jc-importer / class / Common / Rest / RestManager.php

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

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