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

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

711 lines 25.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace ImportWP\Common\Importer\Template;
4
5 use ImportWP\Common\Attachment\Attachment;
6 use ImportWP\Common\Filesystem\Filesystem;
7 use ImportWP\Common\Ftp\Ftp;
8 use ImportWP\Common\Importer\File\XMLFile;
9 use ImportWP\Common\Importer\ParsedData;
10 use ImportWP\Common\Model\ImporterModel;
11 use ImportWP\Common\Util\Logger;
12 use ImportWP\Container;
13 use ImportWP\EventHandler;
14
15 class Template extends AbstractTemplate
16 {
17 /**
18 * Template name
19 *
20 * @var string
21 */
22 protected $name;
23 /**
24 * Mapper name
25 *
26 * @var string
27 */
28 protected $mapper;
29 /**
30 * @var ImporterModel
31 */
32 protected $importer;
33
34 /**
35 * Registered data groups
36 *
37 * @var string[]
38 */
39 protected $groups;
40
41 /**
42 * List of field option callbacks
43 *
44 * @var array
45 */
46 protected $field_options = [];
47
48 protected $default_template_options = [];
49
50 /**
51 * @var \WP_Error[] $errors
52 */
53 protected $errors = [];
54
55 /**
56 * @var EventHandler $event_handler
57 */
58 private $event_handler;
59
60 /**
61 * @var boolean
62 */
63 private $featured_set = false;
64
65 public function __construct(EventHandler $event_handler)
66 {
67 $this->event_handler = $event_handler;
68 $this->event_handler->run('template.init', [$this]);
69
70 $this->groups = $this->event_handler->run('template.data_groups', [$this->groups, $this]);
71
72 // Register field options callbacks
73 $this->field_options = [
74 '*.row_base' => [$this, 'get_row_base']
75 ];
76 $this->field_options = $this->event_handler->run('template.field_option_callbacks', [$this->field_options, $this]);
77 }
78
79 /**
80 * Get xml record base
81 *
82 * @param ImporterModel $importer_model
83 * @return array
84 */
85 public function get_row_base($importer_model)
86 {
87 /**
88 * @var ImporterManager $importer_manager
89 */
90 $importer_manager = Container::getInstance()->get('importer_manager');
91 $tmp = false;
92 $results = [];
93
94 if ($importer_model->getParser() !== 'xml') {
95 return $results;
96 }
97
98 $base_path = $importer_model->getFileSetting('base_path');
99 $nodes = $importer_model->getFileSetting('nodes');
100
101 $config = $importer_manager->get_config($importer_model->getId(), $tmp);
102
103 $filePath = $importer_model->getFile();
104 $file = new XMLFile($filePath, $config);
105 $file->setRecordPath($base_path);
106 $nodes = $file->get_node_list();
107
108 foreach ($nodes as $node) {
109 if (strpos($node, $base_path) !== 0) {
110 continue;
111 }
112
113 $sub_node = substr($node, strlen($base_path));
114
115 $results[] = [
116 'value' => $sub_node,
117 'label' => $sub_node
118 ];
119 }
120
121 return $results;
122 }
123
124 public function get_name()
125 {
126 return $this->name;
127 }
128
129 public function get_mapper()
130 {
131 return $this->mapper;
132 }
133
134 public function get_importer()
135 {
136 return $this->importer;
137 }
138
139 public function get_fields(ImporterModel $importer = null)
140 {
141 $fields = $this->register();
142 $fields = $this->event_handler->run('template.fields', [$fields, $this, $importer]);
143 return $fields;
144 }
145
146 public function register()
147 {
148 return [];
149 }
150
151 public function register_hooks(ImporterModel $importer)
152 {
153 $this->importer = $importer;
154
155 add_filter('iwp/importer/before_mapper', array($this, 'pre_process'));
156 add_filter('iwp/status/record_inserted', [$this, 'display_record_info'], 10, 3);
157 add_filter('iwp/status/record_updated', [$this, 'display_record_info'], 10, 3);
158 }
159
160 public function unregister_hooks()
161 {
162 remove_filter('iwp/importer/before_mapper', array($this, 'pre_process'));
163 remove_filter('iwp/status/record_inserted', [$this, 'display_record_info'], 10, 3);
164 remove_filter('iwp/status/record_updated', [$this, 'display_record_info'], 10, 3);
165 }
166
167 /**
168 * @param string $message
169 * @param int $id
170 * @param ParsedData $data
171 * @return $string
172 */
173 public function display_record_info($message, $id, $data)
174 {
175
176 $fields = $data->getData();
177 if (!empty($fields)) {
178 $message .= ' (' . implode(', ', array_keys($fields)) . ')';
179 }
180
181 if (!empty($this->errors)) {
182 $errors = [];
183 foreach ($this->errors as $error) {
184 $errors[] = $error->get_error_message();
185 }
186
187 $message .= ', Errors: (' . implode(', ', $errors) . ')';
188 }
189
190 $this->errors = [];
191
192 return $message;
193 }
194
195 public function register_group($label, $key, $fields, $args = [])
196 {
197 if (isset($args['row_base']) && true === $args['row_base']) {
198
199 $fields = array_merge(
200 [$this->register_field('Repeater Node', 'row_base', [
201 'type' => 'text',
202 'options' => 'callback',
203 'tooltip' => 'Select the path to the parent node containing values for this record, The preview when selecting data for each record should show a single record. Please note changing this will require updating references in this record.'
204 ])],
205 $fields
206 );
207 }
208
209 return [
210 'id' => $key,
211 'heading' => $label,
212 'type' => isset($args['type']) ? $args['type'] : 'group',
213 'fields' => $fields
214 ];
215 }
216
217 public function register_core_field($label, $key, $args = [])
218 {
219 $args['core'] = true;
220 return $this->register_field($label, $key, $args);
221 }
222
223 public function register_field($label, $key, $args = [])
224 {
225 $data = [
226 'id' => $key,
227 'label' => $label,
228 'type' => isset($args['type']) ? $args['type'] : 'field',
229 'core' => isset($args['core']) ? $args['core'] : false,
230 'tooltip' => isset($args['tooltip']) ? $args['tooltip'] : false
231 ];
232
233 if (isset($args['options'])) {
234 $data['options'] = $args['options'];
235 }
236
237 if (isset($args['default'])) {
238 $data['default'] = $args['default'];
239 }
240
241 if (isset($args['condition'])) {
242 $data['condition'] = $args['condition'];
243 }
244
245 return $data;
246 }
247
248 public function register_attachment_fields($label = 'Attachments', $name = 'attachments', $field_label = 'Location', $group_args = null)
249 {
250 if (is_null($group_args)) {
251 $group_args = ['type' => 'repeatable', 'row_base' => true];
252 }
253 return $this->register_group($label, $name, [
254 $this->register_field($field_label, 'location', [
255 'tooltip' => __('The source location of the file being attached.', 'importwp')
256 ]),
257 $this->register_field('Is Featured?', '_featured', [
258 'default' => 'no',
259 'options' => [
260 ['value' => 'no', 'label' => 'No'],
261 ['value' => 'yes', 'label' => 'Yes'],
262 ],
263 'tooltip' => __('Is the attachment the featured image for the current post.', 'importwp')
264 ]),
265 $this->register_field('Download', '_download', [
266 'default' => 'remote',
267 'options' => [
268 ['value' => 'remote', 'label' => 'Remote URL'],
269 ['value' => 'ftp', 'label' => 'FTP'],
270 ['value' => 'local', 'label' => 'Local Filesystem'],
271 ['value' => 'media', 'label' => 'Media Library'],
272 ],
273 'tooltip' => __('Select how the attachment is being downloaded.', 'importwp')
274 ]),
275 $this->register_field('Host', '_ftp_host', [
276 'condition' => ['_download', '==', 'ftp'],
277 'tooltip' => __('Enter the FTP hostname', 'importwp')
278 ]),
279 $this->register_field('Username', '_ftp_user', [
280 'condition' => ['_download', '==', 'ftp'],
281 'tooltip' => __('Enter the FTP username', 'importwp')
282 ]),
283 $this->register_field('Password', '_ftp_pass', [
284 'condition' => ['_download', '==', 'ftp'],
285 'tooltip' => __('Enter the FTP password', 'importwp')
286 ]),
287 $this->register_field('Path', '_ftp_path', [
288 'condition' => ['_download', '==', 'ftp'],
289 'tooltip' => __('Enter the FTP base path, this is prefixed onto the Location field, leave empty to be ignore', 'importwp')
290 ]),
291 $this->register_field('Base URL', '_remote_url', [
292 'condition' => ['_download', '==', 'remote'],
293 'tooltip' => __('Enter the base url, this is prefixed onto the Location field, leave empty to be ignore', 'importwp')
294 ]),
295 $this->register_field('Base URL', '_local_url', [
296 'condition' => ['_download', '==', 'local'],
297 'tooltip' => __('Enter the base path from this servers root file system, this is prefixed onto the Location field, leave empty to be ignore', 'importwp')
298 ]),
299 $this->register_field('Permissions', '_enable_image_hash', [
300 'condition' => ['_download', '!=', 'media'],
301 'default' => 'yes',
302 'options' => [
303 ['value' => 'no', 'label' => 'Always download new files'],
304 ['value' => 'yes', 'label' => 'Search media library before downloading new files'],
305 ],
306 'tooltip' => __('Enable to stop duplicate images by searching the media library before downloading new files.', 'importwp')
307 ]),
308 $this->register_group('Attachment Meta', '_meta', [
309 $this->register_field('Enable Meta', '_enabled', [
310 'default' => 'no',
311 'options' => [
312 ['value' => 'no', 'label' => 'No'],
313 ['value' => 'yes', 'label' => 'Yes'],
314 ],
315 'type' => 'select',
316 'tooltip' => __('Enable/Disable the fields to import attachment meta data.', 'importwp')
317 ]),
318 $this->register_field('Alt Text', '_alt', [
319 'condition' => ['_enabled', '==', 'yes'],
320 'tooltip' => __('Image attachment alt text.', 'importwp'),
321 ]),
322 $this->register_field('Title Text', '_title', [
323 'condition' => ['_enabled', '==', 'yes'],
324 'tooltip' => __('Attachments title text.', 'importwp')
325 ]),
326 $this->register_field('Caption Text', '_caption', [
327 'condition' => ['_enabled', '==', 'yes'],
328 'tooltip' => __('Image attachments caption text.', 'importwp')
329 ]),
330 $this->register_field('Description Text', '_description', [
331 'condition' => ['_enabled', '==', 'yes'],
332 'tooltip' => __('Attachments description text.', 'importwp')
333 ])
334 ]),
335 ], $group_args);
336 }
337
338 public function get_field_options($field_name, $id)
339 {
340 $callback = false;
341 foreach ($this->field_options as $callback_field_name => $temp_callback) {
342 if (strpos($callback_field_name, '*') !== false) {
343
344 $callback_field_name = str_replace('.', '\.', $callback_field_name);
345
346 $pattern = str_replace('*', '[\S]+', $callback_field_name);
347 if (1 !== preg_match("/^{$pattern}/i", $field_name)) {
348 continue;
349 }
350
351 $callback = $temp_callback;
352 break;
353 }
354
355 if ($field_name !== $callback_field_name) {
356 continue;
357 }
358
359 $callback = $temp_callback;
360 break;
361 }
362
363 if (!$callback) {
364 return false;
365 }
366
367 if (!method_exists($callback[0], $callback[1])) {
368 return false;
369 }
370
371 if (!is_callable($callback)) {
372 return false;
373 }
374
375 return call_user_func_array($callback, [$id]);
376 }
377
378 public function get_default_template_options()
379 {
380 return $this->default_template_options;
381 }
382
383 /**
384 * Alter fields before they are parsed
385 *
386 * @param array $fields
387 * @return array
388 */
389 public function field_map($fields)
390 {
391 return $fields;
392 }
393
394 public function config_field_map($field_map)
395 {
396 $template_fields = $this->field_map($field_map);
397
398 $field_groups = [
399 'default' => [
400 'id' => 'default',
401 'fields' => []
402 ]
403 ];
404
405 foreach ($template_fields as $key => $value) {
406
407 if (empty($value) || preg_match('/\.row_base$/', $key) !== 1) {
408 continue;
409 }
410
411 $group_id = substr($key, 0, strlen($key) - strlen('.row_base'));
412
413 $field_groups[$group_id] = [
414 'id' => $group_id,
415 'base' => $value,
416 'fields' => []
417 ];
418 }
419
420 foreach ($template_fields as $field_id => $field_value) {
421
422 $found = false;
423
424 foreach ($field_groups as $group_prefix => $group_settings) {
425 if (false === strpos($field_id, $group_prefix) || preg_match('/\.row_base$/', $field_id) === 1) {
426 continue;
427 }
428
429 $field_groups[$group_prefix]['fields'][$field_id] = $field_value;
430 $found = true;
431 }
432
433 if (false === $found) {
434 $field_groups['default']['fields'][$field_id] = $field_value;
435 }
436 }
437
438 return $field_groups;
439 }
440
441 /**
442 * Process data before record is importer.
443 *
444 * Alter data that is passed to the mapper.
445 *
446 * @param ParsedData $data
447 * @return ParsedData
448 */
449 public function pre_process(ParsedData $data)
450 {
451 $data = $this->pre_process_groups($data);
452 $this->event_handler->run('template.pre_process', [$data, $this->importer, $this]);
453 return $data;
454 }
455
456 public function process($post_id, ParsedData $data, ImporterModel $importer_model)
457 {
458 $this->featured_set = false;
459 $this->event_handler->run('template.process', [$post_id, $data, $importer_model, $this]);
460 }
461
462 /**
463 * Process data after record is importer.
464 *
465 * Use data that is returned from the mapper.
466 *
467 * @param int $post_id
468 * @param ParsedData $data
469 * @return void
470 */
471 public function post_process($post_id, ParsedData $data)
472 {
473 $this->event_handler->run('template.post_process', [$post_id, $data, $this]);
474 }
475
476 public function pre_process_groups(ParsedData $data)
477 {
478 // Allows virtual groups to be registered
479 $this->groups = $this->event_handler->run('template.pre_process_groups', [$this->groups, $data, $this]);
480
481 $map = $data->getData('default');
482 foreach ($this->groups as $group) {
483 $group_map = [];
484
485 foreach ($map as $field_key => $fields_map) {
486 if (preg_match('/^' . $group . '\.(.*?)$/', $field_key) === 1) {
487 $group_map[$field_key] = $fields_map;
488 }
489 }
490
491 $data->replace($group_map, $group);
492 }
493
494 return $data;
495 }
496
497 /**
498 * Process attachment fields
499 *
500 * @param int $post_id
501 * @param array $row
502 * @param string $row_prefix
503 * @param Filesystem $filesystem
504 * @param Ftp $ftp
505 * @param Attachment $attachment
506 */
507 public function process_attachment($post_id, $row, $row_prefix, $filesystem, $ftp, $attachment)
508 {
509 $delimiter = apply_filters('iwp/value_delimiter', ',');
510 $delimiter = apply_filters('iwp/attachment/value_delimiter', $delimiter);
511 $meta_delimiter = apply_filters('iwp/attachment/meta_delimiter', $delimiter);
512
513 $locations = isset($row[$row_prefix . 'location']) ? $row[$row_prefix . 'location'] : null;
514 $location_parts = explode($delimiter, $locations);
515 $location_parts = array_filter(array_map('trim', $location_parts));
516
517 $attachment_titles = isset($row[$row_prefix . '_meta._title']) ? explode($meta_delimiter, $row[$row_prefix . '_meta._title']) : null;
518 $attachment_alts = isset($row[$row_prefix . '_meta._alt']) ? explode($meta_delimiter, $row[$row_prefix . '_meta._alt']) : null;
519 $attachment_captions = isset($row[$row_prefix . '_meta._caption']) ? explode($meta_delimiter, $row[$row_prefix . '_meta._caption']) : null;
520 $attachment_descriptions = isset($row[$row_prefix . '_meta._description']) ? explode($meta_delimiter, $row[$row_prefix . '_meta._description']) : null;
521
522 $attachment_enable_image_hash = isset($row[$row_prefix . '_enable_image_hash']) ? $row[$row_prefix . '_enable_image_hash'] : 'yes';
523
524 $attachment_ids = [];
525 $location_counter = 0;
526 foreach ($location_parts as $location) {
527
528 if (empty($location)) {
529 continue;
530 }
531
532 $download = isset($row[$row_prefix . '_download']) ? $row[$row_prefix . '_download'] : null;
533 $featured = isset($row[$row_prefix . '_featured']) ? $row[$row_prefix . '_featured'] : null;
534 $source = null;
535 $result = false;
536 $attachment_id = null;
537 $attachment_salt = '';
538
539 $location = trim($location);
540
541 switch ($download) {
542 case 'remote':
543 $base_url = isset($row[$row_prefix . '_remote_url']) ? $row[$row_prefix . '_remote_url'] : null;
544
545 // check if file hash is already stored
546 $source = $base_url . $location;
547 if (empty($source)) {
548 continue 2;
549 }
550
551 $custom_filename = apply_filters('iwp/attachment/filename', null, $source);
552
553 $attachment_id = 0;
554 if ($attachment_enable_image_hash == 'yes') {
555 $attachment_id = $attachment->get_attachment_by_hash($source);
556 }
557
558 if ($attachment_id <= 0) {
559 Logger::write(__CLASS__ . '::process__attachments -remote=' . $source . ' -filename=' . $custom_filename);
560 $result = $filesystem->download_file($source, null, null, $custom_filename);
561 }
562 break;
563 case 'ftp':
564 $ftp_user = isset($row[$row_prefix . '_ftp_user']) ? $row[$row_prefix . '_ftp_user'] : null;
565 $ftp_host = isset($row[$row_prefix . '_ftp_host']) ? $row[$row_prefix . '_ftp_host'] : null;
566 $ftp_pass = isset($row[$row_prefix . '_ftp_pass']) ? $row[$row_prefix . '_ftp_pass'] : null;
567 $base_url = isset($row[$row_prefix . '_ftp_path']) ? $row[$row_prefix . '_ftp_path'] : null;
568
569 // check if file hash is already stored
570 $source = $base_url . $location;
571 if (empty($source)) {
572 continue 2;
573 }
574
575 $custom_filename = apply_filters('iwp/attachment/filename', null, $source);
576
577 $attachment_id = 0;
578 if ($attachment_enable_image_hash == 'yes') {
579 $attachment_id = $attachment->get_attachment_by_hash($source);
580 }
581
582 if ($attachment_id <= 0) {
583 Logger::write(__CLASS__ . '::process__attachments -ftp=' . $source . ' -filename=' . $custom_filename);
584 $result = $ftp->download_file($source, $ftp_host, $ftp_user, $ftp_pass, $custom_filename);
585 }
586 break;
587 case 'local':
588 $base_url = isset($row[$row_prefix . '_local_url']) ? $row[$row_prefix . '_local_url'] : null;
589
590 // check if file hash is already stored
591 $source = $base_url . $location;
592 if (empty($source)) {
593 continue 2;
594 }
595
596 $custom_filename = apply_filters('iwp/attachment/filename', null, $source);
597 $attachment_salt = file_exists($source) ? md5_file($source) : '';
598 $attachment_id = 0;
599 if ($attachment_enable_image_hash == 'yes') {
600 $attachment_id = $attachment->get_attachment_by_hash($source, $attachment_salt);
601 }
602 if ($attachment_id <= 0) {
603 Logger::write(__CLASS__ . '::process__attachments -local=' . $source . ' -filename=' . $custom_filename);
604 $result = $filesystem->copy_file($source, null, $custom_filename);
605 }
606 break;
607 case 'media':
608 $source = $location;
609 if (empty($source)) {
610 continue 2;
611 }
612
613 $attachment_id = $attachment->attachment_partial_url_to_postid($source);
614 Logger::write(__CLASS__ . '::process__attachments -media=' . $source);
615
616 break;
617 }
618
619 $meta_enabled = isset($row[$row_prefix . '_meta._enabled']) && $row[$row_prefix . '_meta._enabled'] === 'yes' ? true : false;
620
621 // insert attachment
622 if ($attachment_id <= 0) {
623
624 if (is_wp_error($result)) {
625 Logger::write(__CLASS__ . '::process__attachments -error=' . $result->get_error_message());
626 $this->errors[] = $result;
627 continue;
628 }
629
630 if (!$result) {
631 continue;
632 }
633
634 $attachment_args = [];
635 if ($meta_enabled) {
636 $attachment_args['title'] = isset($attachment_titles[$location_counter]) ? $attachment_titles[$location_counter] : null;
637 $attachment_args['alt'] = isset($attachment_alts[$location_counter]) ? $attachment_alts[$location_counter] : null;
638 $attachment_args['caption'] = isset($attachment_captions[$location_counter]) ? $attachment_captions[$location_counter] : null;
639 $attachment_args['description'] = isset($attachment_descriptions[$location_counter]) ? $attachment_descriptions[$location_counter] : null;;
640 }
641
642 $attachment_id = $attachment->insert_attachment($post_id, $result['dest'], $result['mime'], $attachment_args);
643 if (is_wp_error($attachment_id)) {
644 Logger::write(__CLASS__ . '::process__attachments -error=' . $attachment_id->get_error_message());
645 continue;
646 }
647
648 $attachment->generate_image_sizes($attachment_id, $result['dest']);
649 $attachment->store_attachment_hash($attachment_id, $source, $attachment_salt);
650 } else {
651 // Update existing attachment meta
652 if ($meta_enabled) {
653 $post_data = [];
654
655 if (isset($attachment_titles[$location_counter])) {
656 $post_data['post_title'] = $attachment_titles[$location_counter];
657 }
658
659 if (isset($attachment_descriptions[$location_counter])) {
660 $post_data['post_content'] = $attachment_descriptions[$location_counter];
661 }
662
663 if (isset($attachment_captions[$location_counter])) {
664 $post_data['post_excerpt'] = $attachment_captions[$location_counter];
665 }
666
667 if (!empty($post_data)) {
668 $post_data['ID'] = $attachment_id;
669 wp_update_post($post_data);
670 }
671
672 if (isset($attachment_alts[$location_counter])) {
673 update_post_meta($attachment_id, '_wp_attachment_image_alt', $attachment_alts[$location_counter]);
674 }
675 }
676 }
677
678 $attachment_ids[] = $attachment_id;
679 $attachment_url = wp_get_attachment_url($attachment_id);
680 $this->_attachments[] = $attachment_url;
681
682 Logger::write(__CLASS__ . '::process__attachments -id=' . $attachment_id . ' -url=' . $attachment_url);
683
684 // set featured
685 if ('yes' === $featured && false === $this->featured_set) {
686 update_post_meta($post_id, '_thumbnail_id', $attachment_id);
687 $this->featured_set = true;
688 }
689
690 $location_counter++;
691 }
692
693 return $attachment_ids;
694 }
695
696 /**
697 * Add error message
698 *
699 * @param string|\WP_Error $error
700 * @return void
701 */
702 public function add_error($error)
703 {
704 if (!is_wp_error($error)) {
705 $error = new \WP_Error('IWP_TEMP_ERR', $error);
706 }
707
708 $this->errors[] = $error;
709 }
710 }
711