PluginProbe ʕ •ᴥ•ʔ
WP All Import – Drag & Drop Import for CSV, XML, Excel & Google Sheets / trunk
WP All Import – Drag & Drop Import for CSV, XML, Excel & Google Sheets vtrunk
3.9.5 3.9.6 4.0.0 4.0.1 4.1.0 trunk 2.12 2.13 2.14 3.0 3.0.1 3.0.2 3.0.3 3.0.4 3.1.0 3.1.1 3.1.2 3.1.3 3.1.4 3.1.5 3.2.0 3.2.1 3.2.2 3.2.3 3.2.4 3.2.5 3.2.6 3.2.7 3.2.8 3.2.9 3.3.0 3.3.1 3.3.2 3.3.3 3.3.4 3.3.5 3.3.6 3.3.7 3.3.8 3.3.9 3.4.0 3.4.1 3.4.2 3.4.3 3.4.4 3.4.5 3.4.6 3.4.7 3.4.8 3.4.9 3.5.0 3.5.1 3.5.2 3.5.3 3.5.4 3.5.5 3.5.6 3.5.7 3.5.8 3.5.9 3.6.0 3.6.1 3.6.2 3.6.3 3.6.4 3.6.5 3.6.6 3.6.7 3.6.8 3.6.9 3.7.0 3.7.1 3.7.2 3.7.3 3.7.3-beta-1.0 3.7.4 3.7.4-beta-1.0 3.7.5 3.7.6 3.7.7 3.7.8 3.7.9 3.8.0 3.9.0 3.9.1 3.9.2 3.9.3 3.9.4
wp-all-import / controllers / admin / import.php
wp-all-import / controllers / admin Last commit date
addons.php 3 weeks ago help.php 8 years ago history.php 3 weeks ago home.php 8 years ago import.php 3 weeks ago manage.php 3 weeks ago partners.php 3 weeks ago settings.php 3 weeks ago
import.php
3165 lines
1 <?php
2 // phpcs:disable WordPress.NamingConventions.PrefixAllGlobals
3 /**
4 * Import configuration wizard
5 *
6 * @author Pavel Kulbakin <p.kulbakin@gmail.com>
7 */
8
9 class PMXI_Admin_Import extends PMXI_Controller_Admin {
10 protected $isWizard = true; // indicates whether controller is in wizard mode (otherwize it called to be deligated an edit action)
11 protected $isTemplateEdit = false; // indicates whether controlled is deligated by manage imports controller
12
13 protected function init() {
14 parent::init();
15
16 if ('PMXI_Admin_Manage' == PMXI_Plugin::getInstance()->getAdminCurrentScreen()->base) { // prereqisites are not checked when flow control is deligated
17 $id = $this->input->get('id');
18 $this->data['import'] = $import = new PMXI_Import_Record();
19 if ( ! $id or $import->getById($id)->isEmpty()) { // specified import is not found
20 // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect
21 wp_redirect(esc_url_raw(add_query_arg('page', 'pmxi-admin-manage', admin_url('admin.php')))); die();
22 }
23 $this->isWizard = false;
24
25 } else {
26 $action = PMXI_Plugin::getInstance()->getAdminCurrentScreen()->action;
27 $this->_step_ready($action);
28 $this->isInline = 'process' == $action;
29 }
30
31 XmlImportConfig::getInstance()->setCacheDirectory(sys_get_temp_dir());
32
33 // preserve id parameter as part of baseUrl
34 $id = $this->input->get('id') and $this->baseUrl = add_query_arg('id', $id, $this->baseUrl);
35
36 $this->baseUrl = apply_filters('pmxi_base_url', $this->baseUrl);
37 }
38
39 public function set($var, $val)
40 {
41 $this->{$var} = $val;
42 }
43 public function get($var)
44 {
45 return $this->{$var};
46 }
47
48 /**
49 * Checks whether corresponding step of wizard is complete
50 * @param string $action
51 */
52 protected function _step_ready($action) {
53 // step #1: xml selction - has no prerequisites
54 if ('index' == $action) return true;
55
56 // step #2: element selection
57 $this->data['dom'] = $dom = new DOMDocument('1.0', PMXI_Plugin::$session->encoding);
58 $this->data['update_previous'] = $update_previous = new PMXI_Import_Record();
59 $old = libxml_use_internal_errors(true);
60
61 $xml = $this->get_xml();
62
63 if (empty($xml) and in_array($action, array('process')) ){
64 ! empty( PMXI_Plugin::$session->update_previous ) and $update_previous->getById(PMXI_Plugin::$session->update_previous);
65 return true;
66 }
67
68 if ( ! PMXI_Plugin::$session->has_session()
69 or ! empty( PMXI_Plugin::$session->update_previous ) and $update_previous->getById(PMXI_Plugin::$session->update_previous)->isEmpty()
70 or empty($xml)
71 or ! @$dom->loadXML($xml)// FIX: libxml xpath doesn't handle default namespace properly, so remove it upon XML load
72 ) {
73 if ( ! PMXI_Plugin::is_ajax() ){
74 $this->errors->add('form-validation', __('WP All Import lost track of where you are.<br/><br/>Maybe you cleared your cookies or maybe it is just a temporary issue on your or your web host\'s end.', 'wp-all-import'));
75 wp_redirect_or_javascript(esc_url_raw($this->baseUrl)); die();
76 }
77 }
78
79 libxml_use_internal_errors($old);
80 if ('element' == $action) return true;
81 if ('evaluate' == $action) return true;
82 if ('evaluate_variations' == $action) return true;
83
84 // step #3: template
85 $xpath = new DOMXPath($dom);
86 $this->data['elements'] = $elements = @$xpath->query(PMXI_Plugin::$session->xpath ?? '');
87
88 if ('preview' == $action or 'tag' == $action or 'preview_images' == $action or 'preview_taxonomies' == $action or 'preview_images' == $action) return true;
89
90 if ( ! PMXI_Plugin::$session->get('xpath', false) or empty($elements) or ! $elements->length) {
91 $this->errors->add('form-validation', __('There are no elements to import based on your XPath.<br/><br/>If you are in Step 2, you probably specified filtering options that don’t match any elements present in your file.<br/>If you are seeing this error elsewhere, it means that while the XPath expression for your initial import matched some elements in your file previously, there are now zero elements in the file that match this expression.<br/>You can edit the XPath for your import by going to the Manage Imports -> Import Settings page.', 'wp-all-import'));
92 wp_redirect_or_javascript(esc_url_raw(add_query_arg('action', 'element', $this->baseUrl))); die();
93 }
94
95 if ('template' == $action or 'preview' == $action or 'tag' == $action) return true;
96
97 // step #4: options
98 if ( empty( PMXI_Plugin::$session->options ) ) {
99 wp_redirect_or_javascript(esc_url_raw(add_query_arg('action', 'template', $this->baseUrl))); die();
100 }
101 if ('options' == $action) return true;
102
103 if ( empty( PMXI_Plugin::$session->options ) ) {
104 // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect
105 wp_redirect(esc_url_raw(add_query_arg('action', 'options', $this->baseUrl))); die();
106 }
107 }
108
109 /**
110 * Step #1: Choose File
111 */
112 public function index() {
113
114 $action = $this->input->get('action');
115
116 $this->data['reimported_import'] = $import = new PMXI_Import_Record();
117 $this->data['id'] = $id = $this->input->get('id');
118 $this->data['parent_import'] = $parent_import = $this->input->get('parent_import', 0);
119 $parent_import_record = new PMXI_Import_Record();
120
121 $DefaultOptions = array(
122 'type' => '',
123 'wizard_type' => 'new',
124 'custom_type' => 'post',
125 'show_hidden_cpt' => 0,
126 'feed_type' => '',
127 'url' => '',
128 'ftp_host' => '',
129 'ftp_path' => '',
130 'ftp_root' => '/',
131 'ftp_port' => '21',
132 'ftp_username' => '',
133 'ftp_password' => '',
134 'ftp_private_key' => '',
135 'file' => '',
136 'reimport' => '',
137 'is_update_previous' => $id ? 1 : 0,
138 'update_previous' => $id,
139 'xpath' => '/',
140 'filepath' => '',
141 'root_element' => '',
142 'downloaded' => '',
143 'auto_generate' => 0,
144 'template' => false ,
145 'taxonomy_type' => ''
146 );
147
148 $DefaultOptions = apply_filters('wp_all_import_default_options', $DefaultOptions);
149
150 if ($parent_import and ! $parent_import_record->getById($parent_import)->isEmpty()){
151 $DefaultOptions['custom_type'] = $parent_import_record->options['custom_type'];
152 }
153
154 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
155 if ( $id ) { // update requested but corresponding import is not found
156 if ( $import->getById($id)->isEmpty() ) {
157 if ( ! empty($_GET['deligate']) and sanitize_key($_GET['deligate']) == 'wpallexport' ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
158 // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect
159 wp_redirect(esc_url_raw(add_query_arg('pmxi_nt', array('error' => urlencode(__('The import associated with this export has been deleted.', 'wp-all-import')), 'updated' => urlencode(__('Please re-run your export by clicking Run Export on the All Export -> Manage Exports page. Then try your import again.', 'wp-all-import'))), remove_query_arg('id', $this->baseUrl)))); die();
160 } else {
161 // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect
162 wp_redirect(esc_url_raw(add_query_arg('pmxi_nt', array('error' => urlencode(__('This import has been deleted.', 'wp-all-import'))), remove_query_arg('id', $this->baseUrl)))); die();
163 }
164 } else {
165 $DefaultOptions['custom_type'] = $import->options['custom_type'];
166 }
167 }
168
169 if ( ! in_array($action, array('index'))) {
170 PMXI_Plugin::$session->clean_session();
171 } else {
172 $DefaultOptions = (PMXI_Plugin::$session->has_session() && !empty(PMXI_Plugin::$session->first_step) ? PMXI_Plugin::$session->first_step : array()) + $DefaultOptions;
173 }
174
175 $this->data['post'] = $post = $this->input->post( $DefaultOptions );
176
177 if ( ! class_exists('DOMDocument') or ! class_exists('XMLReader') ) {
178 $this->errors->add('form-validation', __('Required PHP components are missing.<br/><br/>WP All Import requires DOMDocument, XMLReader, and XMLWriter PHP modules to be installed.<br/>These are standard features of PHP, and are necessary for WP All Import to read the files you are trying to import.<br/>Please contact your web hosting provider and ask them to install and activate the DOMDocument, XMLReader, and XMLWriter PHP modules.', 'wp-all-import'));
179 }
180
181 $this->data['upload_validation'] = false;
182
183 if ($this->input->post('is_submitted') and ! $this->errors->get_error_codes()) {
184
185 check_admin_referer('choose-file', '_wpnonce_choose-file');
186
187 if ('upload' == $this->input->post('type')) {
188 $uploader = new PMXI_Upload($post['filepath'], $this->errors, rtrim(str_replace(basename($post['filepath']), '', $post['filepath']), '/'));
189 $upload_result = $uploader->upload();
190 if ($upload_result instanceof WP_Error) {
191 $this->errors = $upload_result;
192 } else {
193 $source = $upload_result['source'];
194 $filePath = $upload_result['filePath'];
195 $post['template'] = $upload_result['template'];
196 PMXI_Plugin::$is_csv = $upload_result['is_csv'];
197 if ( ! empty($upload_result['root_element']))
198 $post['root_element'] = $upload_result['root_element'];
199 }
200 } elseif ('url' == $this->input->post('type')) {
201 $post['url'] = trim($post['url']);
202 if ( ! empty($post['downloaded']) ){
203 $downloaded = json_decode($post['downloaded'], true);
204 $source = $downloaded['source'];
205 $filePath = $downloaded['filePath'];
206 $post['template'] = $downloaded['template'];
207 PMXI_Plugin::$csv_path = $downloaded['csv_path'];
208 PMXI_Plugin::$is_csv = $downloaded['is_csv'];
209 if ( ! empty($downloaded['root_element']))
210 $post['root_element'] = $downloaded['root_element'];
211 $post['feed_type'] = $downloaded['feed_type'];
212 } else {
213 $uploader = new PMXI_Upload($post['url'], $this->errors);
214 $upload_result = $uploader->url($post['feed_type']);
215 if ($upload_result instanceof WP_Error) {
216 $this->errors = $upload_result;
217 } else {
218 $source = $upload_result['source'];
219 $filePath = $upload_result['filePath'];
220 $post['template'] = $upload_result['template'];
221 PMXI_Plugin::$csv_path = $upload_result['csv_path'];
222 PMXI_Plugin::$is_csv = $upload_result['is_csv'];
223 if ( ! empty($upload_result['root_element']))
224 $post['root_element'] = $upload_result['root_element'];
225 $post['feed_type'] = $upload_result['feed_type'];
226 }
227 }
228 } elseif ( 'ftp' == $this->input->post('type')) {
229 if ( ! empty($post['downloaded']) ){
230 $downloaded = json_decode($post['downloaded'], true);
231 $source = $downloaded['source'];
232
233 $filePath = $downloaded['filePath'];
234 $post['template'] = $downloaded['template'];
235 PMXI_Plugin::$csv_path = $downloaded['csv_path'];
236 PMXI_Plugin::$is_csv = $downloaded['is_csv'];
237 if ( ! empty($downloaded['root_element']))
238 $post['root_element'] = $downloaded['root_element'];
239 $post['feed_type'] = $downloaded['feed_type'];
240 } else {
241 try {
242 $files = PMXI_FTPFetcher::fetch($post);
243 $uploader = new PMXI_Upload($files[0], $this->errors, rtrim(str_replace(basename($files[0]), '', $files[0]), '/'));
244 $upload_result = $uploader->upload();
245 if (!$this->errors->get_error_codes()) {
246 $source = $upload_result['source'];
247 $filePath = $upload_result['filePath'];
248 $post['template'] = $upload_result['template'];
249 PMXI_Plugin::$csv_path = $upload_result['csv_path'];
250 PMXI_Plugin::$is_csv = $upload_result['is_csv'];
251 if ( ! empty($upload_result['root_element']))
252 $post['root_element'] = $upload_result['root_element'];
253 $post['feed_type'] = $upload_result['feed_type'] ?? '';
254 }
255 } catch (Exception $e) {
256 $this->errors->add('form-validation', $e->getMessage());
257 }
258 }
259 $source['type'] = 'ftp';
260 } elseif ('file' == $this->input->post('type')) {
261 // Free version - existing file selection is not supported
262 $this->errors->add('form-validation', __('Using existing files is not supported in the free edition. Please upgrade to Pro to use this feature.', 'wp-all-import'));
263 }
264
265 if ($this->input->post('is_submitted') and '' == $this->input->post('custom_type')) {
266 $this->errors->add('form-validation', __('Select an item type to import the data', 'wp-all-import'));
267 }
268 if ($post['is_update_previous'] and empty($post['update_previous'])) {
269 $this->errors->add('form-validation', __('Previous import for update must be selected to proceed with a new one', 'wp-all-import'));
270 }
271 if ( 'taxonomies' == $this->input->post('custom_type') ){
272 // Free version - taxonomy imports are not supported
273 $this->errors->add('form-validation', __('Importing taxonomies is not supported in the free edition. Please upgrade to Pro to use this feature.', 'wp-all-import'));
274 }
275 $this->data['detection_feed_extension'] = false;
276 $elements_cloud = array();
277
278 // phpcs:ignore Squiz.PHP.DiscouragedFunctions.Discouraged
279 @set_time_limit(0);
280 $deligate = $this->input->get('deligate', false);
281 $redirect_to_template = false;
282 $importRecord = new PMXI_Import_Record();
283
284 switch ( $deligate ) {
285 case 'wpallexport':
286 $import_id = $this->input->get('id', 0);
287 $importRecord->clear();
288 $importRecord->getById($import_id);
289 if ( ! $importRecord->isEmpty() and ! empty($importRecord->options['unique_key'])) {
290 $importRecord->set(array(
291 'path' => wp_all_import_get_relative_path($filePath),
292 'parent_import_id' => 0
293 ))->save();
294 $post['is_update_previous'] = 1;
295 $post['update_previous'] = $importRecord->id;
296 $redirect_to_template = true;
297 }
298
299 if ( $importRecord->isEmpty() ){
300 $this->errors->add('form-validation', __('File is no longer in the correct format', 'wp-all-import'));
301 } elseif (empty($importRecord->options['unique_key'])) {
302 $this->errors->add('form-validation', __('Certain columns are required to be present in your file to enable it to be re-imported with WP All Import. These columns are missing. Re-export your file using WP All Export, and don\'t delete any of the columns when editing it. Then, re-import will work correctly.', 'wp-all-import'));
303 } elseif($importRecord->options['custom_type'] == 'import_users' && ! class_exists('PMUI_Plugin')){
304 $this->errors->add('form-validation', __('<p>The import template you are using requires the User Add-On.</p><a href="https://www.wpallimport.com/import-wordpress-users/?utm_source=wordpress.org&utm_medium=wpai-import-template&utm_campaign=free+wp+all+export+plugin" target="_blank">Purchase the User Add-On</a>', 'wp-all-import'));
305 } elseif($importRecord->options['custom_type'] == 'shop_customer' && ! class_exists('PMUI_Plugin')){
306 $this->errors->add('form-validation', __('<p>The import template you are using requires the User Add-On.</p><a href="https://www.wpallimport.com/import-wordpress-users/?utm_source=wordpress.org&utm_medium=wpai-import-template&utm_campaign=free+wp+all+export+plugin" target="_blank">Purchase the User Add-On</a>', 'wp-all-import'));
307 }
308 break;
309
310 default:
311 # code...
312 break;
313 }
314
315 $local_paths = !empty($local_paths) ? $local_paths : array($filePath);
316
317 foreach ($local_paths as $key => $path) {
318 if ( @file_exists($path) ){
319 $file = new PMXI_Chunk($path, array('element' => $post['root_element'], 'get_cloud' => true));
320 if ( ! empty($file->options['element']) ) {
321 $xpath = "/" . $file->options['element'];
322 $elements_cloud = $file->cloud;
323 if ( ! empty($elements_cloud) and class_exists('PMXE_Plugin') and ! $importRecord->isEmpty() ) {
324 $is_file_valid = apply_filters('wp_all_import_is_exported_file_valid', true, $importRecord->options['export_id'], $elements_cloud);
325 if ( ! $is_file_valid ) {
326 $this->errors->add('form-validation', __('Certain columns are required to be present in your file to enable it to be re-imported with WP All Import. These columns are missing. Re-export your file using WP All Export, and don\'t delete any of the columns when editing it. Then, re-import will work correctly.', 'wp-all-import'));
327 }
328 }
329 if ( ($redirect_to_template or $post['auto_generate']) and ! $this->errors->get_error_codes() ) {
330 // loop through the file until all lines are read
331 while ($xml = $file->read()) {
332 if ( ! empty($xml) ) {
333 //PMXI_Import_Record::preprocessXml($xml);
334 $xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" . "\n" . $xml;
335 $dom = new DOMDocument('1.0', 'UTF-8');
336 $old = libxml_use_internal_errors(true);
337 $dom->loadXML($xml);
338 libxml_use_internal_errors($old);
339 $dxpath = new DOMXPath($dom);
340
341 if (($elements = @$dxpath->query($xpath)) and $elements->length) {
342 if ( empty($chunks) ) {
343 $chunks = 0;
344 }
345 $chunks += $elements->length;
346 unset($dom, $dxpath, $elements);
347 }
348 }
349 }
350 //unset($file);
351 }
352 break;
353 }
354 } else {
355 $this->errors->add('form-validation', __('Unable to download feed resource.', 'wp-all-import'));
356 }
357 }
358
359 if ( ! $this->errors->get_error_codes() ) {
360
361 // xml is valid
362 $source['root_element'] = $file->options['element'];
363 $source['first_import'] = gmdate("Y-m-d H:i:s");
364
365 PMXI_Plugin::$session->clean_session();
366
367 $session_data = array(
368 'filePath' => $filePath,
369 'parent_import_id' => $parent_import,
370 'xpath' => (!empty($xpath)) ? $xpath : '',
371 'feed_type' => $post['feed_type'],
372 'wizard_type' => $post['wizard_type'],
373 'custom_type' => $post['custom_type'],
374 'taxonomy_type' => $post['taxonomy_type'],
375 'ftp_host' => $post['ftp_host'],
376 'ftp_path' => $post['ftp_path'],
377 'ftp_root' => $post['ftp_root'],
378 'ftp_port' => $post['ftp_port'],
379 'ftp_username' => $post['ftp_username'],
380 'ftp_password' => $post['ftp_password'],
381 'ftp_private_key' => $post['ftp_private_key'],
382 'source' => $source,
383 'encoding' => 'UTF-8',
384 'is_csv' => PMXI_Plugin::$is_csv,
385 'csv_path' => PMXI_Plugin::$csv_path,
386 'chunk_number' => 1,
387 'log' => '',
388 'processing' => 0,
389 'queue_chunk_number' => 0,
390 'count' => (isset($chunks)) ? $chunks : 0,
391 'warnings' => 0,
392 'errors' => 0,
393 'start_time' => 0,
394 'local_paths' => (!empty($local_paths)) ? $local_paths : array(), // ftp import local copies of remote files
395 'csv_paths' => array(PMXI_Plugin::$csv_path), // ftp import local copies of remote CSV files
396 'action' => 'import',
397 'elements_cloud' => (!empty($elements_cloud)) ? $elements_cloud : array(),
398 'pointer' => 1,
399 'deligate' => $deligate,
400 'first_step' => $post
401 );
402
403 // apply options from WP All Export bundle
404 if ( ! empty($post['template'])) {
405 $templates = json_decode($post['template'], true);
406 $template_options = \pmxi_maybe_unserialize($templates[0]['options']);
407
408 // Use the custom_type from the template if it exists and the form submission is the default 'post'
409 // This handles the case where bundle skip flow submits before AJAX completes
410 if (!empty($template_options['custom_type']) && $post['custom_type'] == 'post') {
411 $post['custom_type'] = $template_options['custom_type'];
412 }
413
414 $template_options['type'] = ($post['custom_type'] == 'page') ? 'page' : 'post';
415 $template_options['custom_type'] = $post['custom_type'];
416 $template_options['wizard_type'] = $post['wizard_type'];
417 if ($post['wizard_type'] == 'new') {
418 $template_options['create_new_records'] = 1;
419 }
420 // Ensure duplicate_matching is set correctly based on wizard_type for WP All Export bundle
421 if ($post['wizard_type'] == 'new') {
422 // For new items, use auto matching (unique identifier)
423 $template_options['duplicate_matching'] = 'auto';
424 } elseif ($post['wizard_type'] == 'matching') {
425 // For existing items, use manual matching (title, custom field, etc.)
426 $template_options['duplicate_matching'] = 'manual';
427
428 // For product imports, default to SKU matching if custom field is selected but not configured
429 if ($post['custom_type'] == 'product' &&
430 isset($post['duplicate_indicator']) && $post['duplicate_indicator'] == 'custom field' &&
431 (empty($post['custom_duplicate_name']) || empty($post['custom_duplicate_value']))) {
432 $template_options['duplicate_indicator'] = 'custom field';
433 $template_options['custom_duplicate_name'] = '_sku';
434 }
435 }
436 $this->data['post'] = $template_options;
437 PMXI_Plugin::$session->set('options', $template_options);
438 }
439
440 foreach ($session_data as $key => $value) {
441 PMXI_Plugin::$session->set( $key, $value );
442 }
443
444 $update_previous = new PMXI_Import_Record();
445 if ($post['is_update_previous'] and ! $update_previous->getById($post['update_previous'])->isEmpty()) {
446 PMXI_Plugin::$session->set('update_previous', $update_previous->id);
447 PMXI_Plugin::$session->set('xpath', $update_previous->xpath);
448 PMXI_Plugin::$session->set('options', $update_previous->options);
449 } else {
450 PMXI_Plugin::$session->set('update_previous', '');
451 }
452
453 PMXI_Plugin::$session->save_data();
454
455 $xml = $this->get_xml();
456
457 if ( empty($xml) ) {
458 $this->errors->add('upload-validation', __('Please confirm you are importing a valid feed.<br/> Often, feed providers distribute feeds with invalid data, improperly wrapped HTML, line breaks where they should not be, faulty character encodings, syntax errors in the XML, and other issues.<br/><br/>WP All Import has checks in place to automatically fix some of the most common problems, but we can’t catch every single one.<br/><br/>It is also possible that there is a bug in WP All Import, and the problem is not with the feed.<br/><br/>If you need assistance, please contact support – <a href="mailto:support@wpallimport.com">support@wpallimport.com</a> – with your XML/CSV file. We will identify the problem and release a bug fix if necessary.', 'wp-all-import'));
459 $this->data['upload_validation'] = true;
460 } elseif( $redirect_to_template ) {
461 // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect
462 wp_redirect(esc_url_raw(add_query_arg('action', 'template', $this->baseUrl))); die();
463 } elseif( $post['auto_generate'] ) {
464 // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect
465 wp_redirect(esc_url_raw(add_query_arg('action', 'options', $this->baseUrl))); die();
466 } else {
467 // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect
468 wp_redirect(esc_url_raw(add_query_arg('action', 'element', $this->baseUrl))); die();
469 }
470 } else if ('url' == $this->input->post('type') and !empty($this->errors)) {
471 $this->errors->add('form-validation', __('WP All Import unable to detect file type.<br/><br/>WP All Import not able to determine what type of file you are importing. Make sure your file extension is correct for the file type you are importing.<br/> Please choose the correct file type from the dropdown below, or try adding &type=xml or &type=csv to the end of the URL, for example http://example.com/export-products.php?&type=xml', 'wp-all-import'));
472 $this->data['detection_feed_extension'] = true;
473 } else {
474 $this->errors->add('upload-validation', __('Please confirm you are importing a valid feed.<br/> Often, feed providers distribute feeds with invalid data, improperly wrapped HTML, line breaks where they should not be, faulty character encodings, syntax errors in the XML, and other issues.<br/><br/>WP All Import has checks in place to automatically fix some of the most common problems, but we can’t catch every single one.<br/><br/>It is also possible that there is a bug in WP All Import, and the problem is not with the feed.<br/><br/>If you need assistance, please contact support – <a href="mailto:support@wpallimport.com">support@wpallimport.com</a> – with your XML/CSV file. We will identify the problem and release a bug fix if necessary.', 'wp-all-import'));
475 $this->data['upload_validation'] = true;
476 }
477 do_action("pmxi_get_file", $filePath);
478 }
479 if ($this->input->post('is_submitted') and $this->errors->get_error_codes()) {
480 PMXI_Plugin::$session->clean_session();
481 }
482 $this->render();
483 }
484
485 /**
486 * Step #2: Choose elements
487 */
488 public function element() {
489 $xpath = new DOMXPath($this->data['dom']);
490 $post = $this->input->post(array('xpath' => ''));
491 $this->data['post'] =& $post;
492 $this->data['elements_cloud'] = PMXI_Plugin::$session->elements_cloud;
493 $this->data['is_csv'] = PMXI_Plugin::$session->is_csv;
494
495 if ($this->input->post('is_submitted')) {
496 check_admin_referer('choose-elements', '_wpnonce_choose-elements');
497 if ('' == $post['xpath']) {
498 $this->errors->add('form-validation', __('No elements selected', 'wp-all-import'));
499 } else {
500 $node_list = @ $xpath->query($post['xpath']); // make sure only element selection is allowed; prevent parsing warning to be displayed
501 if (FALSE === $node_list) {
502 $this->errors->add('form-validation', __('Your XPath is not valid.', 'wp-all-import'));
503 } else {
504 foreach ($node_list as $el) {
505 if ( ! $el instanceof DOMElement) {
506 $this->errors->add('form-validation', __('XPath must match only elements', 'wp-all-import'));
507 break;
508 };
509 }
510 }
511 }
512 if ( ! $this->errors->get_error_codes()) {
513 // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect
514 wp_redirect(esc_url_raw(apply_filters('pmxi_element_redirect' , add_query_arg('action', 'template', $this->baseUrl)))); die();
515 }
516 } else {
517 if ( PMXI_Plugin::$session->xpath ) {
518 $post['xpath'] = PMXI_Plugin::$session->xpath;
519 $this->data['elements'] = $elements = $xpath->query($post['xpath']);
520 $this->data['is_show_warning'] = true;
521 foreach ($elements as $element) {
522 if ($element instanceof DOMElement) {
523 foreach ($element->childNodes as $child) {
524 if ($child instanceof DOMElement) {
525 $this->data['is_show_warning'] = false;
526 }
527 }
528 break;
529 }
530 }
531 if ( ! $elements->length and ! empty( PMXI_Plugin::$session->update_previous ) ) {
532 $_GET['pmxi_nt'] = __('Warning: No matching elements found for XPath expression from the import being updated. It probably means that new XML file has different format. Though you can update XPath, procceed only if you sure about update operation being valid.', 'wp-all-import');
533 }
534 } else {
535 // suggest 1st repeating element as default selection
536 $post['xpath'] = PMXI_Render::xml_find_repeating($this->data['dom']->documentElement);
537 if ( ! empty($post['xpath'])){
538 $this->data['elements'] = $elements = $xpath->query($post['xpath']);
539 }
540 }
541 }
542 // workaround to prevent rendered XML representation to eat memory since it has to be stored in memory when output is bufferred
543 $this->render();
544 }
545
546 /**
547 * Helper to evaluate xpath and return matching elements as direct paths for javascript side to highlight them
548 */
549 public function evaluate() {
550
551 if ( ! PMXI_Plugin::getInstance()->getAdminCurrentScreen()->is_ajax) { // call is only valid when send with ajax
552 // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect
553 wp_redirect(esc_url_raw(add_query_arg('action', 'element', $this->baseUrl))); die();
554 }
555
556 // HTTP headers for no cache etc
557 header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
558 header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
559 header("Cache-Control: no-store, no-cache, must-revalidate");
560 header("Cache-Control: post-check=0, pre-check=0", false);
561 header("Pragma: no-cache");
562
563 $xpath = new DOMXPath($this->data['dom']);
564 $post = $this->input->post(array('xpath' => '', 'show_element' => 1, 'root_element' => PMXI_Plugin::$session->source['root_element'], 'delimiter' => '', 'is_csv' => 0));
565 $wp_uploads = wp_upload_dir();
566
567 if ( ! check_ajax_referer( 'wp_all_import_secure', 'security', false )) {
568 $this->errors->add('form-validation', __('Security check', 'wp-all-import'));
569 } elseif ('' == $post['xpath']) {
570 $this->errors->add('form-validation', __('Your XPath is empty.<br/><br/>Please enter an XPath expression.', 'wp-all-import'));
571 } else {
572
573 $source = PMXI_Plugin::$session->get('source');
574
575 // counting selected elements
576 if ('' != $post['delimiter'] and $post['delimiter'] != PMXI_Plugin::$session->is_csv ) {
577
578 include_once(PMXI_Plugin::ROOT_DIR.'/libraries/XmlImportCsvParse.php');
579
580 PMXI_Plugin::$session->set('is_csv', $post['delimiter']);
581
582 wp_all_import_remove_source(PMXI_Plugin::$session->filePath, false);
583
584 $csv = new PMXI_CsvParser( array(
585 'filename' => PMXI_Plugin::$session->get('csv_path'),
586 'xpath' => '',
587 'delimiter' => $post['delimiter'],
588 'targetDir' => rtrim(str_replace(basename(PMXI_Plugin::$session->filePath), '', PMXI_Plugin::$session->filePath), '/')
589 ));
590
591 $filePath = $csv->xml_path;
592 PMXI_Plugin::$session->set('filePath', $filePath);
593 PMXI_Plugin::$session->set('local_paths', array($filePath));
594
595 }
596
597 // counting selected elements
598 PMXI_Plugin::$session->set('xpath', $post['xpath']);
599
600 $current_xpath = '';
601
602 if ($post['show_element'] == 1) {
603 PMXI_Plugin::$session->set('count', $this->data['node_list_count'] = 0);
604 } else {
605 $this->data['node_list_count'] = PMXI_Plugin::$session->count;
606 }
607
608 $xpath_elements = explode('[', $post['xpath']);
609 $xpath_parts = explode('/', $xpath_elements[0]);
610
611 $source['root_element'] = $xpath_parts[1];
612
613 PMXI_Plugin::$session->set('source', $source);
614
615 PMXI_Plugin::$session->save_data();
616
617 $loop = 0;
618
619 foreach (PMXI_Plugin::$session->local_paths as $key => $path) {
620
621 $file = new PMXI_Chunk($path, array('element' => $source['root_element'], 'encoding' => PMXI_Plugin::$session->encoding));
622
623 // loop through the file until all lines are read
624 while ($xml = $file->read()) {
625
626 if ( ! empty($xml) )
627 {
628 //PMXI_Import_Record::preprocessXml($xml);
629 $xml = "<?xml version=\"1.0\" encoding=\"". PMXI_Plugin::$session->encoding ."\"?>" . "\n" . $xml;
630
631 $dom = new DOMDocument('1.0', PMXI_Plugin::$session->encoding);
632 $old = libxml_use_internal_errors(true);
633 $dom->loadXML($xml);
634 libxml_use_internal_errors($old);
635 $xpath = new DOMXPath($dom);
636
637 if (($elements = @$xpath->query($post['xpath'])) and $elements->length){
638
639 if ( $post['show_element'] == 1 ){
640 $this->data['node_list_count'] += $elements->length;
641 if (!$loop) $this->data['dom'] = $dom;
642 }
643
644 $loop += $elements->length;
645
646 if ( $post['show_element'] > 1 and $loop == $post['show_element']) {
647 $this->data['dom'] = $dom;
648 break(2);
649 }
650
651 unset($dom, $xpath, $elements);
652 }
653 }
654 }
655 unset($file);
656
657 PMXI_Plugin::$session->set('count', $this->data['node_list_count']);
658 }
659 if ( ! $this->data['node_list_count']) {
660 $this->errors->add('form-validation', __('There are no elements to import based on your XPath.<br/><br/>If you are in Step 2, you probably specified filtering options that don’t match any elements present in your file.<br/>If you are seeing this error elsewhere, it means that while the XPath expression for your initial import matched some elements in your file previously, there are now zero elements in the file that match this expression.<br/>You can edit the XPath for your import by going to the Manage Imports -> Import Settings page.', 'wp-all-import'));
661 }
662 }
663
664 $this->data['show_element'] = $post['show_element'];
665
666 PMXI_Plugin::$session->save_data();
667
668 $this->data['is_csv'] = $post['is_csv'];
669
670 ob_start();
671 if ( ! $this->errors->get_error_codes()) {
672 $xpath = new DOMXPath($this->data['dom']);
673 $this->data['elements'] = $elements = @ $xpath->query($post['xpath']); // prevent parsing warning to be displayed
674 $paths = array(); $this->data['paths'] =& $paths;
675 if (PMXI_Plugin::getInstance()->getOption('highlight_limit') and $elements->length <= PMXI_Plugin::getInstance()->getOption('highlight_limit')) {
676 foreach ($elements as $el) {
677 if ( ! $el instanceof DOMElement) continue;
678 $p = PMXI_Render::get_xml_path($el, $xpath) and $paths[] = $p;
679 }
680 }
681 $this->render();
682 } else {
683 $this->error();
684 }
685
686 $html = ob_get_clean();
687
688 ob_start();
689
690 if ( ! empty($elements->length) ) PMXI_Render::render_xml_elements_for_filtring($elements->item(0));
691
692 $render_element = ob_get_clean();
693
694 exit( json_encode( array('result' => true, 'html' => $html, 'root_element' => $source['root_element'], 'count' => $this->data['node_list_count'], 'render_element' => $render_element )));
695 }
696
697 /**
698 * Helper to evaluate xpath and return matching elements as direct paths for javascript side to highlight them
699 */
700 public function evaluate_variations() {
701 if ( ! PMXI_Plugin::getInstance()->getAdminCurrentScreen()->is_ajax) { // call is only valid when send with ajax
702 // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect
703 wp_redirect(esc_url_raw(add_query_arg('action', 'element', $this->baseUrl))); die();
704 }
705
706 $post = $this->input->post(array('xpath' => '', 'show_element' => 1, 'root_element' => (!empty(PMXI_Plugin::$session->source['root_element'])) ? PMXI_Plugin::$session->source['root_element'] : '', 'tagno' => 0, 'parent_tagno' => 1));
707 $wp_uploads = wp_upload_dir();
708
709 $this->get_xml( $post['parent_tagno'], true );
710
711 $xpath = new DOMXPath($this->data['dom']);
712
713 $this->data['tagno'] = max(intval($this->input->getpost('tagno', 1)), 0);
714
715 if ('' == $post['xpath']) {
716 $this->errors->add('form-validation', __('Your XPath is empty.<br/><br/>Please enter an XPath expression.', 'wp-all-import'));
717 } else {
718 $post['xpath'] = '/' . ((!empty($this->data['update_previous']->root_element)) ? $this->data['update_previous']->root_element : PMXI_Plugin::$session->source['root_element']) .'/'. ltrim(trim(str_replace("[*]","",$post['xpath']),'{}'), '/');
719 // in default mode
720 $this->data['variation_elements'] = $elements = @ $xpath->query($post['xpath']); // prevent parsing warning to be displayed
721 $this->data['variation_list_count'] = $elements->length;
722 if (FALSE === $elements) {
723 $this->errors->add('form-validation', __('Your XPath is not valid.', 'wp-all-import'));
724 } elseif ( ! $elements->length) {
725 $this->errors->add('form-validation', __('No matching variations found for XPath specified', 'wp-all-import'));
726 } else {
727 foreach ($elements as $el) {
728 if ( ! $el instanceof DOMElement) {
729 $this->errors->add('form-validation', __('XPath must match only elements', 'wp-all-import'));
730 break;
731 };
732 }
733 }
734 }
735
736 ob_start();
737
738 if ( ! $this->errors->get_error_codes()) {
739
740 $paths = array(); $this->data['paths'] =& $paths;
741 if (PMXI_Plugin::getInstance()->getOption('highlight_limit') and $elements->length <= PMXI_Plugin::getInstance()->getOption('highlight_limit')) {
742 foreach ($elements as $el) {
743 if ( ! $el instanceof DOMElement) continue;
744
745 $p = PMXI_Render::get_xml_path($el, $xpath) and $paths[] = $p;
746 }
747 }
748
749 $this->render();
750 } else {
751 $this->error();
752 }
753
754 exit( json_encode(array('html' => ob_get_clean())) );
755 }
756
757 /**
758 * Preview selected xml tag (called with ajax from `template` step)
759 */
760 public function tag( $is_json = true ) {
761
762 if ($is_json) check_ajax_referer( 'wp_all_import_secure', 'security' );
763
764 if (empty($this->data['elements']->length)) {
765 $update_previous = new PMXI_Import_Record();
766 $id = $this->input->get('id');
767 if ($id and $update_previous->getById($id)) {
768 PMXI_Plugin::$session->set('update_previous', $update_previous->id);
769 PMXI_Plugin::$session->set('xpath', $update_previous->xpath);
770 PMXI_Plugin::$session->set('options', $update_previous->options);
771 $history = new PMXI_File_List();
772 $history->setColumns('id', 'name', 'registered_on', 'path')->getBy(array('import_id' => $update_previous->id), 'id DESC');
773
774 if ($history->count()){
775 $history_file = new PMXI_File_Record();
776 $history_file->getBy('id', $history[0]['id']);
777
778 if ( PMXI_Plugin::$session->has_session() ){
779 PMXI_Plugin::$session->set('filePath', wp_all_import_get_absolute_path($history_file->path));
780 PMXI_Plugin::$session->set('count', $update_previous->count);
781 PMXI_Plugin::$session->set('encoding', ( ! empty($update_previous->options['encoding'])) ? $update_previous->options['encoding'] : 'UTF-8');
782 PMXI_Plugin::$session->save_data();
783 }
784 }
785 } else {
786 unset(PMXI_Plugin::$session->update_previous);
787 }
788 }
789
790 $this->data['tagno'] = max(intval($this->input->getpost('tagno', 1)), 1);
791
792 $this->data['import_action'] = $this->input->getpost('import_action', false);
793
794 if ($this->data['tagno']) {
795 $local_paths = ( ! empty(PMXI_Plugin::$session->local_paths) ) ? PMXI_Plugin::$session->local_paths : array(PMXI_Plugin::$session->filePath);
796 PMXI_Plugin::$session->set('local_paths', $local_paths);
797 $loop = 0;
798 $xpath_value = $this->input->getpost('xpath', PMXI_Plugin::$session->xpath);
799 if ($xpath_value !== PMXI_Plugin::$session->xpath) {
800 $this->data['tagno'] = 1;
801 PMXI_Plugin::$session->set('xpath', $xpath_value);
802 PMXI_Plugin::$session->save_data();
803 }
804 $this->data['node_list_count'] = $this->data['tagno'] == 1 ? 0 : PMXI_Plugin::$session->count;
805 foreach ($local_paths as $key => $path) {
806 if (@file_exists($path)){
807 $file = new PMXI_Chunk($path, array(
808 'element' => PMXI_Plugin::$session->source['root_element'],
809 'encoding' => PMXI_Plugin::$session->encoding
810 ));
811 // loop through the file until all lines are read
812 while ($xml = $file->read()) {
813 if ( ! empty($xml) ) {
814 //PMXI_Import_Record::preprocessXml($xml);
815 $xml = "<?xml version=\"1.0\" encoding=\"". PMXI_Plugin::$session->encoding ."\"?>" . "\n" . $xml;
816 $dom = new DOMDocument('1.0', PMXI_Plugin::$session->encoding);
817 $old = libxml_use_internal_errors(true);
818 $dom->loadXML($xml);
819 libxml_use_internal_errors($old);
820 $xpath = new DOMXPath($dom);
821 if (($elements = @$xpath->query($xpath_value)) and $elements->length){
822
823 if ( $this->data['tagno'] == 1 ){
824 $this->data['node_list_count'] += $elements->length;
825 PMXI_Plugin::$session->set('count', $this->data['node_list_count']);
826 if (!$loop) $this->data['dom'] = $dom;
827 }
828
829 $loop += $elements->length;
830
831 if ($loop == $this->data['tagno']) {
832 $this->data['elements'] = $elements;
833 }
834
835 if ( $this->data['tagno'] > 1 and ($loop == $this->data['tagno'] or $loop == PMXI_Plugin::$session->count)) {
836 /* Merge nested XML/CSV files */
837 /*$nested_files = json_decode(PMXI_Plugin::$session->options['nested_files'], true);
838 if ( ! empty($nested_files) ){
839 $merger = new PMXI_Nested($dom, $nested_files, $xml, PMXI_Plugin::$session->xpath);
840 $merger->merge();
841 $xml = $merger->get_xml();
842 unset($merger);
843 }*/
844 unset($dom, $xpath, $elements);
845 break(2);
846 }
847 unset($dom, $xpath, $elements);
848 }
849 }
850 }
851 unset($file);
852 }
853 }
854 }
855
856 if ( $is_json ) {
857 ob_start();
858 $this->render();
859 exit( json_encode(array('html' => ob_get_clean())) );
860 }
861 else $this->render();
862 }
863
864 /**
865 * Preview future post based on current template and tag (called with ajax from `template` step)
866 */
867 public function preview() {
868
869 if ( ! PMXI_Plugin::getInstance()->getAdminCurrentScreen()->is_ajax) { // call is only valid when send with ajax
870 exit('Nice try!');
871 }
872
873 if ( ! check_ajax_referer( 'wp_all_import_preview', 'security', false )){
874 $this->errors->add('form-validation', __('Security check', 'wp-all-import'));
875 }
876
877 if ( ! $this->errors->get_error_codes()) {
878
879 $post = $this->input->post(array(
880 'title' => '',
881 'content' => '',
882 'is_keep_linebreaks' => 0,
883 'is_leave_html' => 0,
884 'fix_characters' => 0,
885 'import_encoding' => 'UTF-8',
886 'tagno' => 0
887 ));
888 $wp_uploads = wp_upload_dir();
889
890 $this->data['tagno'] = $tagno = min(max(intval($this->input->getpost('tagno', 1)), 1), PMXI_Plugin::$session->count);
891
892 $xml = '';
893
894 $local_paths = ( ! empty(PMXI_Plugin::$session->local_paths) ) ? PMXI_Plugin::$session->local_paths : array(PMXI_Plugin::$session->filePath);
895
896 $loop = 1;
897 foreach ($local_paths as $key => $path) {
898
899 if (PMXI_Plugin::$session->encoding != $post['import_encoding'] and ! empty(PMXI_Plugin::$session->csv_paths[$key])){
900 // conver CSV to XML with selected encoding
901 include_once(PMXI_Plugin::ROOT_DIR.'/libraries/XmlImportCsvParse.php');
902 $csv = new PMXI_CsvParser(array(
903 'filename' => PMXI_Plugin::$session->csv_paths[$key],
904 'xpath' => '',
905 'delimiter' => PMXI_Plugin::$is_csv,
906 'encoding' => $post['import_encoding'],
907 'xml_path' => $path
908 ));
909 }
910
911 $file = new PMXI_Chunk($path, array(
912 'element' => PMXI_Plugin::$session->source['root_element'],
913 'encoding' => $post['import_encoding']
914 ));
915
916 // loop through the file until all lines are read
917 while ($xml = $file->read()) {
918
919 if ( ! empty($xml) ) {
920 //PMXI_Import_Record::preprocessXml($xml);
921 $xml = "<?xml version=\"1.0\" encoding=\"". $post['import_encoding'] ."\"?>" . "\n" . $xml;
922
923 $dom = new DOMDocument('1.0', $post['import_encoding']);
924 $old = libxml_use_internal_errors(true);
925 $dom->loadXML($xml); // FIX: libxml xpath doesn't handle default namespace properly, so remove it upon XML load
926 libxml_use_internal_errors($old);
927 $xpath = new DOMXPath($dom);
928 if (($this->data['elements'] = $elements = @$xpath->query(PMXI_Plugin::$session->xpath)) and $elements->length){
929
930 if ( $loop == $tagno ){
931 /* Merge nested XML/CSV files */
932 /*$nested_files = json_decode(PMXI_Plugin::$session->options['nested_files'], true);
933 if ( ! empty($nested_files) ){
934 $merger = new PMXI_Nested($dom, $nested_files, $xml, PMXI_Plugin::$session->xpath);
935 $merger->merge();
936 $xml = $merger->get_xml();
937 unset($merger);
938 }*/
939 unset($dom, $xpath, $elements);
940 break(2);
941 }
942 unset($dom, $xpath, $elements);
943 $loop++;
944 }
945 }
946 }
947 unset($file);
948 }
949
950 $xpath = "(" . PMXI_Plugin::$session->xpath . ")[1]";
951
952 // validate root XPath
953 try{
954 list($this->data['title']) = XmlImportParser::factory($xml, $xpath, $post['title'], $file)->parse(); wp_delete_file($file);
955 }
956 catch(XmlImportException $e){
957 $xpath = PMXI_Plugin::$session->xpath;
958 }
959
960 PMXI_Plugin::$session->set('encoding', $post['import_encoding']);
961 PMXI_Plugin::$session->save_data();
962
963 $functions = $wp_uploads['basedir'] . DIRECTORY_SEPARATOR . WP_ALL_IMPORT_UPLOADS_BASE_DIRECTORY . DIRECTORY_SEPARATOR . 'functions.php';
964 $functions = apply_filters( 'import_functions_file_path', $functions );
965 if ( @file_exists($functions) && PMXI_Plugin::$is_php_allowed)
966 require_once $functions;
967
968 // validate
969 try {
970 if (empty($xml)){
971 $this->errors->add('form-validation', __('WP All Import lost track of where you are.<br/><br/>Maybe you cleared your cookies or maybe it is just a temporary issue on your web host\'s end.', 'wp-all-import'));
972 } elseif (empty($post['title'])) {
973 $this->errors->add('form-validation', __('<strong>Warning</strong>: your title is blank.', 'wp-all-import'));
974 $this->data['title'] = "";
975 } else {
976 list($this->data['title']) = XmlImportParser::factory($xml, $xpath, $post['title'], $file)->parse(); wp_delete_file($file);
977 if ( ! isset($this->data['title']) || '' == strval(trim(strip_tags($this->data['title'], '<img><input><textarea><iframe><object><embed>'))) || '' == wp_all_import_filter_html_kses(($post['is_leave_html']) ? html_entity_decode($this->data['title']) : $this->data['title'])) {
978 $this->errors->add('xml-parsing', __('<strong>Warning</strong>: resulting post title is empty', 'wp-all-import'));
979 }
980 else $this->data['title'] = wp_all_import_filter_html_kses(($post['is_leave_html']) ? html_entity_decode($this->data['title']) : $this->data['title']);
981 }
982 } catch (XmlImportException $e) {
983 /* translators: see placeholders in the string below */
984 $this->errors->add('form-validation', sprintf(__('Error parsing title: %s', 'wp-all-import'), $e->getMessage()));
985 }
986 try {
987 if (empty($xml)){
988 $this->errors->add('form-validation', __('WP All Import lost track of where you are.<br/><br/>Maybe you cleared your cookies or maybe it is just a temporary issue on your web host\'s end.', 'wp-all-import'));
989 } elseif (empty($post['content']) && wp_all_import_is_title_required(PMXI_Plugin::$session->options['custom_type'])) {
990 $this->errors->add('form-validation', __('<strong>Warning</strong>: your content is blank.', 'wp-all-import'));
991 $this->data['content'] = "";
992 } else {
993 list($this->data['content']) = XmlImportParser::factory($post['is_keep_linebreaks'] ? $xml : preg_replace('%\r\n?|\n%', ' ', $xml), $xpath, $post['content'], $file)->parse(); wp_delete_file($file);
994 if ( ! isset($this->data['content']) || '' == strval(trim(strip_tags($this->data['content'], '<img><input><textarea><iframe><object><embed>'))) || '' == wp_all_import_filter_html_kses(($post['is_leave_html']) ? html_entity_decode($this->data['content']) : $this->data['content'])) {
995 $this->errors->add('xml-parsing', __('<strong>Warning</strong>: resulting post content is empty', 'wp-all-import'));
996 }
997 else $this->data['content'] = wp_all_import_filter_html_kses(($post['is_leave_html']) ? html_entity_decode($this->data['content']) : $this->data['content']);
998 }
999 } catch (XmlImportException $e) {
1000 /* translators: see placeholders in the string below */
1001 $this->errors->add('form-validation', sprintf(__('Error parsing content: %s', 'wp-all-import'), $e->getMessage()));
1002 }
1003 }
1004
1005 ob_start();
1006 $this->render();
1007 exit( json_encode(array('html' => ob_get_clean())) );
1008 }
1009
1010 /**
1011 * Preview future post images based on current template and tag (called with ajax from `template` step)
1012 */
1013 public function preview_images() {
1014 if ( ! PMXI_Plugin::getInstance()->getAdminCurrentScreen()->is_ajax) { // call is only valid when send with ajax
1015 exit('Nice try!');
1016 }
1017
1018 if ( ! check_ajax_referer( 'wp_all_import_preview', 'security', false )){
1019 $this->errors->add('form-validation', __('Security check', 'wp-all-import'));
1020 }
1021
1022 if ( ! $this->errors->get_error_codes()) {
1023
1024 $get = $this->data['get'] = $this->input->get(array(
1025 'slug' => ''
1026 ));
1027
1028 $post = $this->data['post'] = $this->input->post(array(
1029 $get['slug'] . 'download_images' => 'no',
1030 $get['slug'] . 'featured_delim' => '',
1031 $get['slug'] . 'featured_image' => '',
1032 $get['slug'] . 'download_featured_delim' => '',
1033 $get['slug'] . 'download_featured_image' => '',
1034 $get['slug'] . 'gallery_featured_delim' => '',
1035 $get['slug'] . 'gallery_featured_image' => '',
1036 'import_encoding' => 'UTF-8',
1037 'tagno' => 0
1038 ));
1039
1040 $wp_uploads = wp_upload_dir();
1041
1042 $this->data['tagno'] = $tagno = min(max(intval($this->input->getpost('tagno', 1)), 1), PMXI_Plugin::$session->count);
1043
1044 $xml = '';
1045
1046 $local_paths = (!empty(PMXI_Plugin::$session->local_paths)) ? PMXI_Plugin::$session->local_paths : array(PMXI_Plugin::$session->filePath);
1047
1048 $loop = 1;
1049 foreach ($local_paths as $key => $path) {
1050
1051 if (PMXI_Plugin::$session->encoding != $post['import_encoding'] and ! empty(PMXI_Plugin::$session->csv_paths[$key])){
1052 // convert CSV to XML with selected encoding
1053 include_once(PMXI_Plugin::ROOT_DIR.'/libraries/XmlImportCsvParse.php');
1054 $csv = new PMXI_CsvParser(array(
1055 'filename' => PMXI_Plugin::$session->csv_paths[$key],
1056 'xpath' => '',
1057 'delimiter' => PMXI_Plugin::$is_csv,
1058 'encoding' => $post['import_encoding'],
1059 'xml_path' => $path
1060 ));
1061 }
1062
1063 $file = new PMXI_Chunk($path, array('element' => (!empty($this->data['update_previous']->root_element)) ? $this->data['update_previous']->root_element : PMXI_Plugin::$session->source['root_element'], 'encoding' => $post['import_encoding']));
1064
1065 // loop through the file until all lines are read
1066 while ($xml = $file->read()) {
1067 if (!empty($xml)) {
1068 //PMXI_Import_Record::preprocessXml($xml);
1069 $xml = "<?xml version=\"1.0\" encoding=\"". $post['import_encoding'] ."\"?>" . "\n" . $xml;
1070
1071 $dom = new DOMDocument('1.0', $post['import_encoding']);
1072 $old = libxml_use_internal_errors(true);
1073 $dom->loadXML($xml); // FIX: libxml xpath doesn't handle default namespace properly, so remove it upon XML load
1074 libxml_use_internal_errors($old);
1075 $xpath = new DOMXPath($dom);
1076 if (($this->data['elements'] = $elements = @$xpath->query(PMXI_Plugin::$session->xpath)) and $elements->length){
1077
1078 if ( $loop == $tagno ){
1079 /* Merge nested XML/CSV files */
1080 /*$nested_files = json_decode(PMXI_Plugin::$session->options['nested_files'], true);
1081 if ( ! empty($nested_files) ){
1082 $merger = new PMXI_Nested($dom, $nested_files, $xml, PMXI_Plugin::$session->xpath);
1083 $merger->merge();
1084 $xml = $merger->get_xml();
1085 unset($merger);
1086 } */
1087 unset($dom, $xpath, $elements);
1088 break(2);
1089 }
1090 unset($dom, $xpath, $elements);
1091 $loop++;
1092 }
1093 }
1094 }
1095 unset($file);
1096 }
1097 //$this->data['tagno'] = $tagno = 1;
1098
1099 $xpath = "(" . PMXI_Plugin::$session->xpath . ")[1]";
1100
1101 PMXI_Plugin::$session->set('encoding', $post['import_encoding']);
1102 PMXI_Plugin::$session->save_data();
1103
1104 $functions = $wp_uploads['basedir'] . DIRECTORY_SEPARATOR . WP_ALL_IMPORT_UPLOADS_BASE_DIRECTORY . DIRECTORY_SEPARATOR . 'functions.php';
1105 $functions = apply_filters( 'import_functions_file_path', $functions );
1106 if ( @file_exists($functions) && PMXI_Plugin::$is_php_allowed)
1107 require_once $functions;
1108
1109 // validate
1110 try {
1111 $this->data['featured_images'] = false;
1112 if (empty($xml)){
1113 $this->errors->add('form-validation', __('WP All Import lost track of where you are.<br/><br/>Maybe you cleared your cookies or maybe it is just a temporary issue on your web host\'s end.', 'wp-all-import'));
1114 } else {
1115 switch ($post[$get['slug'] . 'download_images']) {
1116 case 'no':
1117 $featured_image = $post[$get['slug'] . 'featured_image'];
1118 break;
1119 case 'gallery':
1120 $featured_image = $post[$get['slug'] . 'gallery_featured_image'];
1121 break;
1122 default: // yes
1123 $featured_image = $post[$get['slug'] . 'download_featured_image'];
1124 break;
1125 }
1126
1127 if (empty($featured_image)){
1128 $this->data['featured_images'] = '';
1129 } else {
1130 list($this->data['featured_images']) = XmlImportParser::factory($xml, $xpath, $featured_image, $file)->parse(); wp_delete_file($file);
1131 }
1132 }
1133 } catch (XmlImportException $e) {
1134 /* translators: see placeholders in the string below */
1135 $this->errors->add('form-validation', sprintf(__('Error parsing: %s', 'wp-all-import'), $e->getMessage()));
1136 }
1137 }
1138
1139 ob_start();
1140 $this->render();
1141 exit( json_encode(array('html' => ob_get_clean())) );
1142 }
1143
1144 /**
1145 * Preview taxonomies hierarchy based on current template and tag (called with ajax from `template` step)
1146 */
1147 public function preview_taxonomies() {
1148
1149 if ( ! PMXI_Plugin::getInstance()->getAdminCurrentScreen()->is_ajax) { // call is only valid when send with ajax
1150 exit('Nice try!');
1151 }
1152
1153 if ( ! check_ajax_referer( 'wp_all_import_preview', 'security', false )){
1154 $this->errors->add('form-validation', __('Security check', 'wp-all-import'));
1155 }
1156
1157 if ( ! $this->errors->get_error_codes()) {
1158
1159 $post = $this->data['post'] = $this->input->post(array(
1160 'tax_logic' => '',
1161 'tax_hierarchical_logic_entire' => '',
1162 'tax_hierarchical_xpath' => '',
1163 'tax_hierarchical_delim' => '>',
1164 'is_tax_hierarchical_group_delim' => 0,
1165 'tax_hierarchical_group_delim'=> '|',
1166 'tax_enable_mapping' => '',
1167 'tax_mapping' => '',
1168 'tax_logic_mapping' => '',
1169 'import_encoding' => 'UTF-8',
1170 'tagno' => 0
1171 ));
1172
1173 $this->data['tagno'] = $tagno = min(max(intval($this->input->getpost('tagno', 1)), 1), PMXI_Plugin::$session->count);
1174
1175 $xml = '';
1176
1177 $local_paths = (!empty(PMXI_Plugin::$session->local_paths)) ? PMXI_Plugin::$session->local_paths : array(PMXI_Plugin::$session->filePath);
1178
1179 $loop = 1;
1180 foreach ($local_paths as $key => $path) {
1181 if (PMXI_Plugin::$session->encoding != $post['import_encoding'] and ! empty(PMXI_Plugin::$session->csv_paths[$key])){
1182 // conver CSV to XML with selected encoding
1183 include_once(PMXI_Plugin::ROOT_DIR.'/libraries/XmlImportCsvParse.php');
1184 $csv = new PMXI_CsvParser(array(
1185 'filename' => PMXI_Plugin::$session->csv_paths[$key],
1186 'xpath' => '',
1187 'delimiter' => PMXI_Plugin::$is_csv,
1188 'encoding' => $post['import_encoding'],
1189 'xml_path' => $path
1190 ));
1191 }
1192 $file = new PMXI_Chunk($path, array('element' => (!empty($this->data['update_previous']->root_element)) ? $this->data['update_previous']->root_element : PMXI_Plugin::$session->source['root_element'], 'encoding' => $post['import_encoding']));
1193 // loop through the file until all lines are read
1194 while ($xml = $file->read()) {
1195 if (!empty($xml)) {
1196 //PMXI_Import_Record::preprocessXml($xml);
1197 $xml = "<?xml version=\"1.0\" encoding=\"". $post['import_encoding'] ."\"?>" . "\n" . $xml;
1198 $dom = new DOMDocument('1.0', $post['import_encoding']);
1199 $old = libxml_use_internal_errors(true);
1200 $dom->loadXML($xml); // FIX: libxml xpath doesn't handle default namespace properly, so remove it upon XML load
1201 libxml_use_internal_errors($old);
1202 $xpath = new DOMXPath($dom);
1203 if (($this->data['elements'] = $elements = @$xpath->query(PMXI_Plugin::$session->xpath)) and $elements->length){
1204
1205 if ( $loop == $tagno ){
1206 /* Merge nested XML/CSV files */
1207 /*$nested_files = json_decode(PMXI_Plugin::$session->options['nested_files'], true);
1208 if ( ! empty($nested_files) ){
1209 $merger = new PMXI_Nested($dom, $nested_files, $xml, PMXI_Plugin::$session->xpath);
1210 $merger->merge();
1211 $xml = $merger->get_xml();
1212 unset($merger);
1213 } */
1214 unset($dom, $xpath, $elements);
1215 break(2);
1216 }
1217 unset($dom, $xpath, $elements);
1218 $loop++;
1219 }
1220 }
1221 }
1222 unset($file);
1223 }
1224 //$this->data['tagno'] = $tagno = 1;
1225
1226 $xpath = "(" . PMXI_Plugin::$session->xpath . ")[1]";
1227
1228 PMXI_Plugin::$session->set('encoding', $post['import_encoding']);
1229 PMXI_Plugin::$session->save_data();
1230
1231 $wp_uploads = wp_upload_dir();
1232 $functions = $wp_uploads['basedir'] . DIRECTORY_SEPARATOR . WP_ALL_IMPORT_UPLOADS_BASE_DIRECTORY . DIRECTORY_SEPARATOR . 'functions.php';
1233 $functions = apply_filters( 'import_functions_file_path', $functions );
1234 if ( @file_exists($functions) && PMXI_Plugin::$is_php_allowed)
1235 require_once $functions;
1236
1237 // validate
1238 try {
1239 if (empty($xml)){
1240 $this->errors->add('form-validation', __('Error parsing: String could not be parsed as XML', 'wp-all-import'));
1241 } else{
1242 $data_to_preview = false;
1243 $this->data['tax_hierarchical'] = array();
1244 foreach ($post['tax_logic'] as $ctx => $logic) {
1245 if ( $logic == 'hierarchical' and ! empty($post['tax_hierarchical_logic_entire'][$ctx]) and is_array($post['tax_hierarchical_xpath'][$ctx])){
1246 foreach ($post['tax_hierarchical_xpath'][$ctx] as $ctx_path) { if (empty($ctx_path)) continue;
1247 list($d) = XmlImportParser::factory($xml, $xpath, $ctx_path, $file)->parse(); wp_delete_file($file);
1248 if ($post['is_tax_hierarchical_group_delim'][$ctx] and !empty($post['tax_hierarchical_group_delim'][$ctx])){
1249 // apply mapping rules before splitting via separator symbol
1250 if ( ! empty($post['tax_enable_mapping'][$ctx]) and ! empty($post['tax_logic_mapping'][$ctx]) ){
1251 if ( ! empty($post['tax_mapping'][$ctx])){
1252 $mapping_rules = json_decode($post['tax_mapping'][$ctx], true);
1253 if ( ! empty( $mapping_rules) ){
1254 foreach ($mapping_rules as $rule) {
1255 if ( ! empty($rule[trim($d)])){
1256 $d = trim($rule[trim($d)]);
1257 break;
1258 }
1259 }
1260 }
1261 }
1262 }
1263 $hierarchy_groups = explode($post['tax_hierarchical_group_delim'][$ctx], $d);
1264 if (!empty($hierarchy_groups) and is_array($hierarchy_groups)){
1265 foreach ($hierarchy_groups as $key => $group) {
1266 $this->data['tax_hierarchical'][$ctx][] = $group;
1267 }
1268 }
1269 }
1270 else{
1271 $this->data['tax_hierarchical'][$ctx][] = $d;
1272 }
1273 }
1274 $data_to_preview = true;
1275 }
1276 }
1277 if ( ! $data_to_preview )
1278 $this->errors->add('form-validation', __('There is no data to preview', 'wp-all-import'));
1279 }
1280 } catch (XmlImportException $e) {
1281 /* translators: see placeholders in the string below */
1282 $this->errors->add('form-validation', sprintf(__('Error parsing: %s', 'wp-all-import'), $e->getMessage()));
1283 }
1284 }
1285
1286 ob_start();
1287 $this->render();
1288 exit( json_encode(array('html' => ob_get_clean())) );
1289 }
1290
1291 /**
1292 * Preview prices based on current template and tag (called with ajax from `template` step)
1293 */
1294 public function preview_prices() {
1295
1296 if ( ! PMXI_Plugin::getInstance()->getAdminCurrentScreen()->is_ajax) { // call is only valid when send with ajax
1297 exit('Nice try!');
1298 }
1299
1300 if ( ! check_ajax_referer( 'wp_all_import_preview', 'security', false )){
1301 $this->errors->add('form-validation', __('Security check', 'wp-all-import'));
1302 }
1303
1304 if ( ! $this->errors->get_error_codes()) {
1305
1306 $post = $this->data['post'] = $this->input->post(array(
1307 'single_product_regular_price' => '',
1308 'single_product_sale_price' => '',
1309 'disable_prepare_price' => 0,
1310 'prepare_price_to_woo_format' => 0,
1311 'convert_decimal_separator' => 1,
1312 'single_product_regular_price_adjust' => '',
1313 'single_product_regular_price_adjust_type' => '%',
1314 'single_product_sale_price_adjust' => '',
1315 'single_product_sale_price_adjust_type' => '%',
1316 'import_encoding' => 'UTF-8',
1317 'tagno' => 0
1318 ));
1319
1320 $this->data['tagno'] = $tagno = min(max(intval($this->input->getpost('tagno', 1)), 1), PMXI_Plugin::$session->count);
1321
1322 $xml = '';
1323
1324 $local_paths = (!empty(PMXI_Plugin::$session->local_paths)) ? PMXI_Plugin::$session->local_paths : array(PMXI_Plugin::$session->filePath);
1325
1326 $loop = 1;
1327 foreach ($local_paths as $key => $path) {
1328
1329 if (PMXI_Plugin::$session->encoding != $post['import_encoding'] and ! empty(PMXI_Plugin::$session->csv_paths[$key])){
1330 // conver CSV to XML with selected encoding
1331 include_once(PMXI_Plugin::ROOT_DIR.'/libraries/XmlImportCsvParse.php');
1332
1333 $csv = new PMXI_CsvParser(array(
1334 'filename' => PMXI_Plugin::$session->csv_paths[$key],
1335 'xpath' => '',
1336 'delimiter' => PMXI_Plugin::$is_csv,
1337 'encoding' => $post['import_encoding'],
1338 'xml_path' => $path
1339 ));
1340 }
1341
1342 $file = new PMXI_Chunk($path, array('element' => (!empty($this->data['update_previous']->root_element)) ? $this->data['update_previous']->root_element : PMXI_Plugin::$session->source['root_element'], 'encoding' => $post['import_encoding']));
1343
1344 // loop through the file until all lines are read
1345 while ($xml = $file->read()) {
1346 if (!empty($xml))
1347 {
1348 //PMXI_Import_Record::preprocessXml($xml);
1349 $xml = "<?xml version=\"1.0\" encoding=\"". $post['import_encoding'] ."\"?>" . "\n" . $xml;
1350
1351 $dom = new DOMDocument('1.0', $post['import_encoding']);
1352 $old = libxml_use_internal_errors(true);
1353 $dom->loadXML($xml); // FIX: libxml xpath doesn't handle default namespace properly, so remove it upon XML load
1354 libxml_use_internal_errors($old);
1355 $xpath = new DOMXPath($dom);
1356 if (($this->data['elements'] = $elements = @$xpath->query(PMXI_Plugin::$session->xpath)) and $elements->length){
1357
1358 if ( $loop == $tagno ){
1359 /* Merge nested XML/CSV files */
1360 /*$nested_files = json_decode(PMXI_Plugin::$session->options['nested_files'], true);
1361 if ( ! empty($nested_files) ){
1362 $merger = new PMXI_Nested($dom, $nested_files, $xml, PMXI_Plugin::$session->xpath);
1363 $merger->merge();
1364 $xml = $merger->get_xml();
1365 unset($merger);
1366 } */
1367 unset($dom, $xpath, $elements);
1368 break(2);
1369 }
1370 unset($dom, $xpath, $elements);
1371 $loop++;
1372 }
1373 }
1374 }
1375 unset($file);
1376 }
1377 //$this->data['tagno'] = $tagno = 1;
1378
1379 $xpath = "(" . PMXI_Plugin::$session->xpath . ")[1]";
1380
1381 PMXI_Plugin::$session->set('encoding', $post['import_encoding']);
1382 PMXI_Plugin::$session->save_data();
1383
1384 $wp_uploads = wp_upload_dir();
1385 $functions = $wp_uploads['basedir'] . DIRECTORY_SEPARATOR . WP_ALL_IMPORT_UPLOADS_BASE_DIRECTORY . DIRECTORY_SEPARATOR . 'functions.php';
1386 $functions = apply_filters( 'import_functions_file_path', $functions );
1387 if ( @file_exists($functions) && PMXI_Plugin::$is_php_allowed)
1388 require_once $functions;
1389
1390 // validate
1391 try {
1392 if (empty($xml)){
1393 $this->errors->add('form-validation', __('Error parsing: String could not be parsed as XML', 'wp-all-import'));
1394 } else{
1395 $data_to_preview = false;
1396
1397 if ("" != $post['single_product_regular_price']){
1398 list($this->data['product_regular_price']) = XmlImportParser::factory($xml, $xpath, $post['single_product_regular_price'], $file)->parse(); wp_delete_file($file);
1399 $this->data['product_regular_price'] = pmwi_adjust_price(pmwi_prepare_price($this->data['product_regular_price'], $post['disable_prepare_price'], $post['prepare_price_to_woo_format'], $post['convert_decimal_separator']), 'regular_price', $post);
1400 $data_to_preview = true;
1401
1402 }
1403 if ("" != $post['single_product_sale_price']){
1404 list($this->data['product_sale_price']) = XmlImportParser::factory($xml, $xpath, $post['single_product_sale_price'], $file)->parse(); wp_delete_file($file);
1405 $this->data['product_sale_price'] = pmwi_adjust_price(pmwi_prepare_price($this->data['product_sale_price'], $post['disable_prepare_price'], $post['prepare_price_to_woo_format'], $post['convert_decimal_separator']), 'sale_price', $post);
1406 $data_to_preview = true;
1407
1408 }
1409
1410 if ( ! $data_to_preview )
1411 $this->errors->add('form-validation', __('There is no data to preview', 'wp-all-import'));
1412 }
1413 } catch (XmlImportException $e) {
1414 /* translators: see placeholders in the string below */
1415 $this->errors->add('form-validation', sprintf(__('Error parsing: %s', 'wp-all-import'), $e->getMessage()));
1416 }
1417 }
1418
1419 ob_start();
1420 $this->render();
1421 exit( json_encode(array('html' => ob_get_clean())) );
1422 }
1423
1424 /**
1425 * Step #3: Choose template
1426 */
1427 public function template() {
1428
1429 $template = new PMXI_Template_Record();
1430
1431 $default = PMXI_Plugin::get_default_import_options();
1432
1433 if ($this->isWizard) {
1434 $this->data['source_type'] = PMXI_Plugin::$session->source['type'];
1435
1436 foreach (PMXI_Admin_Addons::get_active_addons() as $class) {
1437 if (class_exists($class)) $default += call_user_func(array($class, "get_default_import_options"));
1438 }
1439 $default['wizard_type'] = PMXI_Plugin::$session->wizard_type;
1440 if (empty($default['custom_type'])) $default['custom_type'] = PMXI_Plugin::$session->custom_type;
1441 if (empty($default['taxonomy_type'])) $default['taxonomy_type'] = PMXI_Plugin::$session->taxonomy_type;
1442 if (empty($default['delimiter'])) $default['delimiter'] = PMXI_Plugin::$session->is_csv;
1443 if (empty($default['ftp_host'])) $default['ftp_host'] = PMXI_Plugin::$session->ftp_host;
1444 if (empty($default['ftp_path'])) $default['ftp_path'] = PMXI_Plugin::$session->ftp_path;
1445 $default['ftp_root'] = PMXI_Plugin::$session->ftp_root;
1446 if (empty($default['ftp_username'])) $default['ftp_username'] = PMXI_Plugin::$session->ftp_username;
1447 if (empty($default['ftp_password'])) $default['ftp_password'] = PMXI_Plugin::$session->ftp_password;
1448 if (empty($default['ftp_private_key'])) $default['ftp_private_key'] = PMXI_Plugin::$session->ftp_private_key;
1449 $default['ftp_port'] = PMXI_Plugin::$session->ftp_port;
1450
1451 $DefaultOptions = (isset(PMXI_Plugin::$session->options)) ? array_replace_recursive($default, PMXI_Plugin::$session->options) : $default;
1452
1453 // Apply WooCommerce product defaults after session merge if values are still empty
1454 if ($DefaultOptions['custom_type'] == "product" and class_exists('PMWI_Plugin')) {
1455 if (empty($DefaultOptions['duplicate_indicator'])) {
1456 $DefaultOptions['duplicate_indicator'] = 'custom field';
1457 }
1458 if (empty($DefaultOptions['custom_duplicate_name'])) {
1459 $DefaultOptions['custom_duplicate_name'] = '_sku';
1460 }
1461 }
1462
1463 // Apply alternative Excel processing setting from session if available
1464 if (PMXI_Plugin::$session->get('use_alternative_excel_processing')) {
1465 $DefaultOptions['use_alternative_excel_processing'] = 1;
1466 }
1467
1468 $DefaultOptions['xpath'] = '';
1469
1470 $post = $this->input->post( apply_filters('pmxi_options_options', $DefaultOptions, $this->isWizard) );
1471
1472 } else {
1473 $this->data['dom'] = new DOMDocument('1.0', 'UTF-8');
1474 $this->data['is_csv'] = PMXI_Plugin::$session->is_csv;
1475
1476 $this->data['source_type'] = $this->data['import']->type;
1477 foreach (PMXI_Admin_Addons::get_active_addons() as $class) {
1478 if (class_exists($class)) $default += call_user_func(array($class, "get_default_import_options"));
1479 }
1480 $DefaultOptions = (is_array($this->data['import']->options)) ? array_replace_recursive($default, $this->data['import']->options) : $default;
1481 $source = array(
1482 'name' => $this->data['import']->name,
1483 'type' => $this->data['import']->type,
1484 'path' => wp_all_import_get_relative_path($this->data['import']->path),
1485 'root_element' => $this->data['import']->root_element,
1486 );
1487 PMXI_Plugin::$session->set('source', $source);
1488 $post = $this->input->post( apply_filters('pmxi_options_options', $DefaultOptions, $this->isWizard) );
1489 $post['xpath'] = $this->data['import']->xpath;
1490
1491 // phpcs:ignore WordPress.Security.NonceVerification.Missing
1492 $xml = $this->get_xml();
1493 if ( ! empty($xml) ) {
1494 @$this->data['dom']->loadXML($xml);
1495 $xpath = new DOMXPath($this->data['dom']);
1496 $this->data['elements'] = $elements = $xpath->query($post['xpath']);
1497 }
1498 }
1499
1500 $max_input_vars = @ini_get('max_input_vars');
1501
1502 if(ctype_digit($max_input_vars) && count($_POST, COUNT_RECURSIVE) >= $max_input_vars) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
1503 /* translators: see placeholders in the string below */
1504 $this->errors->add('form-validation', sprintf(__('You\'ve reached your max_input_vars limit of %d. Please increase this.', 'wp-all-import'), $max_input_vars));
1505 }
1506
1507 $this->data['post'] =& $post;
1508
1509 PMXI_Plugin::$session->set('options', $post);
1510 PMXI_Plugin::$session->set('is_loaded_template', '');
1511
1512 if (($load_template = $this->input->post('load_template'))) { // init form with template selected
1513 if ( ! $template->getById($load_template)->isEmpty()) {
1514
1515 $template_options = $template->options;
1516 $template_options['type'] = $post['type'];
1517 $template_options['custom_type'] = $post['custom_type'];
1518 $template_options['taxonomy_type'] = $post['taxonomy_type'];
1519 $template_options['wizard_type'] = $post['wizard_type'];
1520 $template_options['delimiter'] = $post['delimiter'];
1521 $template_options['ftp_host'] = $post['ftp_host'];
1522 $template_options['ftp_path'] = $post['ftp_path'];
1523 $template_options['ftp_root'] = $post['ftp_root'];
1524 $template_options['ftp_port'] = $post['ftp_port'];
1525 $template_options['ftp_username'] = $post['ftp_username'];
1526 $template_options['ftp_password'] = $post['ftp_password'];
1527 $template_options['ftp_private_key'] = $post['ftp_private_key'];
1528
1529 if ($this->isWizard and $post['wizard_type'] == 'new') {
1530 $template_options['create_new_records'] = 1;
1531 }
1532 if ($this->isWizard) {
1533 $template_options['delimiter'] = PMXI_Plugin::$session->is_csv;
1534 }
1535
1536 $this->data['post'] = $template_options;
1537 PMXI_Plugin::$session->set('is_loaded_template', $load_template);
1538 PMXI_Plugin::$session->set('options', $template_options);
1539 }
1540
1541 } elseif ($this->input->post('is_submitted')) { // save template submission
1542
1543 check_admin_referer('template', '_wpnonce_template');
1544
1545 $wp_uploads = wp_upload_dir();
1546 $functions = $wp_uploads['basedir'] . DIRECTORY_SEPARATOR . WP_ALL_IMPORT_UPLOADS_BASE_DIRECTORY . DIRECTORY_SEPARATOR . 'functions.php';
1547 $functions = apply_filters( 'import_functions_file_path', $functions );
1548 if ( @file_exists($functions) && PMXI_Plugin::$is_php_allowed)
1549 require_once $functions;
1550
1551 if (!empty($post['title'])) {
1552 $this->_validate_template($post['title'], 'Post title');
1553 }
1554 elseif ( ! in_array($post['custom_type'], array('shop_order', 'taxonomies', 'import_users', 'shop_customer', 'comments', 'woo_reviews', 'gf_entries')) ){
1555 $this->warnings->add('1', __('<strong>Warning:</strong> your title is blank.', 'wp-all-import'));
1556 }
1557
1558 if (!empty($post['content'])) {
1559 $this->_validate_template($post['content'], 'Post content');
1560 }
1561 elseif ( ! in_array($post['custom_type'], array('shop_order', 'taxonomies', 'import_users', 'shop_customer', 'comments', 'woo_reviews', 'gf_entries')) ){
1562 $this->warnings->add('2', __('<strong>Warning:</strong> your content is blank.', 'wp-all-import'));
1563 }
1564
1565 if ( ! $this->errors->get_error_codes()) {
1566
1567 // Attributes fields logic
1568 $post = apply_filters('pmxi_save_options', $post);
1569
1570 // validate post excerpt
1571 if ( ! empty($post['post_excerpt'])) $this->_validate_template($post['post_excerpt'], __('Excerpt', 'wp-all-import'));
1572 // validate images
1573 if ( $post['download_images'] == 'yes') {
1574 if ( ! empty($post['download_featured_image'])) $this->_validate_template($post['download_featured_image'], __('Images', 'wp-all-import'));
1575 } else {
1576 if ( ! empty($post['featured_image'])) $this->_validate_template($post['featured_image'], __('Images', 'wp-all-import'));
1577 }
1578 // validate images meta data
1579 foreach (array('title', 'caption', 'alt', 'decription') as $section) {
1580 if ( !empty($post['set_image_meta_' . $section]) )
1581 {
1582 if ( ! empty($post['image_meta_' . $section])) {
1583 /* translators: %s: image meta field name (title, caption, alt, description) */
1584 $this->_validate_template($post['image_meta_' . $section], sprintf(__('Images meta %s', 'wp-all-import'), $section));
1585 }
1586 }
1587 }
1588
1589 // Strip stub rows so they can't feed an empty template to Pro's
1590 // parser after a Free→Pro upgrade.
1591 if (!empty($post['custom_name']) && is_array($post['custom_name'])) {
1592 $keep = array_flip(array_keys(array_filter($post['custom_name'], static function ($v) {
1593 return is_string($v) && trim($v) !== '';
1594 })));
1595 $post['custom_name'] = array_intersect_key($post['custom_name'], $keep);
1596 if (!empty($post['custom_value']) && is_array($post['custom_value'])) {
1597 $post['custom_value'] = array_intersect_key($post['custom_value'], $keep);
1598 }
1599 }
1600
1601 if ( $post['type'] == "post" and $post['custom_type'] == "product" and class_exists('PMWI_Plugin')){
1602 // remove entires where both custom_name and custom_value are empty
1603 $not_empty = array_flip(array_values(array_merge(array_keys(array_filter($post['attribute_name'], 'strlen')), array_keys(array_filter($post['attribute_value'], 'strlen')))));
1604 $post['attribute_name'] = array_intersect_key($post['attribute_name'], $not_empty);
1605 $post['attribute_value'] = array_intersect_key($post['attribute_value'], $not_empty);
1606 // validate
1607 if (array_keys(array_filter($post['attribute_name'], 'strlen')) != array_keys(array_filter($post['attribute_value'], 'strlen'))) {
1608 $this->errors->add('form-validation', __('Both name and value must be set for all woocommerce attributes', 'wp-all-import'));
1609 } else {
1610 foreach ($post['attribute_name'] as $attribute_name) {
1611 $this->_validate_template($attribute_name, __('Attribute Field Name', 'wp-all-import'));
1612 }
1613 foreach ($post['attribute_value'] as $custom_value) {
1614 $this->_validate_template($custom_value, __('Attribute Field Value', 'wp-all-import'));
1615 }
1616 }
1617 }
1618
1619 if ('post' == $post['type'] && isset($post['tags'])) {
1620 /*'' == $post['categories'] or $this->_validate_template($post['categories'], __('Categories', 'wp-all-import'));*/
1621 '' == $post['tags'] or $this->_validate_template($post['tags'], __('Tags', 'wp-all-import'));
1622 }
1623 if ('specific' == $post['date_type']) {
1624 '' == $post['date'] or $this->_validate_template($post['date'], __('Date', 'wp-all-import'));
1625 } else {
1626 '' == $post['date_start'] or $this->_validate_template($post['date_start'], __('Start Date', 'wp-all-import'));
1627 '' == $post['date_end'] or $this->_validate_template($post['date_end'], __('Start Date', 'wp-all-import'));
1628 }
1629
1630 $this->errors = apply_filters('pmxi_options_validation', $this->errors, $post, isset($this->data['import']) ? $this->data['import'] : false);
1631
1632 if ( ! $this->errors->get_error_codes()) { // no validation errors found
1633 // assign some defaults
1634 '' !== $post['date'] or $post['date'] = 'now';
1635 '' !== $post['date_start'] or $post['date_start'] = 'now';
1636 '' !== $post['date_end'] or $post['date_end'] = 'now';
1637
1638 if ( ! empty($post['name']) and !empty($post['save_template_as']) ) { // save template in database
1639 $template->getByName($post['name'])->set(array(
1640 'name' => $post['name'],
1641 'is_keep_linebreaks' => $post['is_keep_linebreaks'],
1642 'is_leave_html' => $post['is_leave_html'],
1643 'fix_characters' => $post['fix_characters'],
1644 'options' => $post
1645 ))->save();
1646 PMXI_Plugin::$session->set('saved_template', $template->id);
1647 }
1648
1649 if ($this->isWizard) {
1650 PMXI_Plugin::$session->set('options', $post);
1651 PMXI_Plugin::$session->save_data();
1652
1653 $DefaultOptions = array();
1654 $DefaultOptions['tmp_unique_key'] = $this->findUniqueKey();
1655
1656
1657 if(!PMXI_Plugin::$session->get('update_previous')) {
1658 $import = new PMXI_Import_Record();
1659 $import->set(
1660 (empty(PMXI_Plugin::$session->source) ? array() : PMXI_Plugin::$session->source)
1661 + array(
1662 'xpath' => PMXI_Plugin::$session->xpath,
1663 'options' => $DefaultOptions + PMXI_Plugin::$session->options,
1664 'count' => PMXI_Plugin::$session->count,
1665 'friendly_name' => wp_all_import_clear_xss(PMXI_Plugin::$session->options['friendly_name']),
1666 'feed_type' => PMXI_Plugin::$session->feed_type,
1667 'parent_import_id' => ($this->data['update_previous']->isEmpty()) ? PMXI_Plugin::$session->parent_import_id : $this->data['update_previous']->parent_import_id,
1668 'queue_chunk_number' => 0,
1669 'triggered' => 0,
1670 'processing' => 0,
1671 'executing' => 0,
1672 'iteration' => (!empty($import->iteration)) ? $import->iteration : 0
1673 )
1674 )->save();
1675
1676 $history_file = new PMXI_File_Record();
1677 $history_file->set(array(
1678 'name' => $import->name,
1679 'import_id' => $import->id,
1680 'path' => wp_all_import_get_relative_path(PMXI_Plugin::$session->filePath),
1681 'registered_on' => gmdate('Y-m-d H:i:s'),
1682 ))->save();
1683
1684
1685 $this->data['update_previous'] = $import;
1686 PMXI_Plugin::$session->set('update_previous', $import->id);
1687 PMXI_Plugin::$session->set('import_id', $import->id);
1688 PMXI_Plugin::$session->set('import', $import);
1689 PMXI_Plugin::$session->save_data();
1690 }
1691
1692 // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect
1693 wp_redirect(esc_url_raw(add_query_arg('action', 'options', $this->baseUrl))); die();
1694
1695 } else {
1696 $this->data['import']->set(array( 'options' => $post, 'settings_update_on' => gmdate('Y-m-d H:i:s')))->update();
1697 $args = array(
1698 'page' => 'pmxi-admin-manage',
1699 'pmxi_nt' => urlencode(__('Template updated', 'wp-all-import'))
1700 );
1701
1702 if ($this->warnings->get_error_codes())
1703 $args['warnings'] = implode(',', $this->warnings->get_error_codes());
1704
1705 // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect
1706 wp_redirect(esc_url_raw(add_query_arg( $args + array_intersect_key($_GET, array_flip($this->baseUrlParamNames)) ,admin_url('admin.php'))));
1707 die();
1708 }
1709 }
1710
1711 }
1712 }
1713
1714 PMXI_Plugin::$session->save_data();
1715
1716 global $wpdb;
1717
1718 switch ($post['custom_type']){
1719 case 'import_users':
1720 case 'shop_customer':
1721 // Get All meta keys in the system
1722 $this->data['meta_keys'] = array();
1723 $meta_keys = new PMXI_Model_List();
1724 $meta_keys->setTable($wpdb->usermeta);
1725 $meta_keys->setColumns('umeta_id', 'meta_key')->getBy(NULL, "umeta_id", NULL, NULL, "meta_key");
1726 $hide_fields = array('first_name', 'last_name', 'nickname', 'description', PMXI_Plugin::getInstance()->getWPPrefix() . 'capabilities');
1727 if ( ! empty($meta_keys) and $meta_keys->count() ){
1728 foreach ($meta_keys as $meta_key) { if (in_array($meta_key['meta_key'], $hide_fields) or strpos($meta_key['meta_key'], '_wp') === 0) continue;
1729 $this->data['meta_keys'][] = $meta_key['meta_key'];
1730 }
1731 }
1732 break;
1733 case 'taxonomies':
1734 // Free version - taxonomy imports are not supported
1735 $this->data['meta_keys'] = array();
1736 break;
1737 case 'woo_reviews':
1738 case 'comments':
1739 // Get All meta keys in the system
1740 $this->data['meta_keys'] = array();
1741 $meta_keys = new PMXI_Model_List();
1742 $meta_keys->setTable(PMXI_Plugin::getInstance()->getWPPrefix() . 'commentmeta');
1743 $meta_keys->setColumns('meta_id', 'meta_key')->getBy(NULL, "meta_id", NULL, NULL, "meta_key");
1744 $hide_fields = array();
1745 if ( ! empty($meta_keys) and $meta_keys->count() ){
1746 foreach ($meta_keys as $meta_key) { if (in_array($meta_key['meta_key'], $hide_fields)) continue;
1747 $this->data['meta_keys'][] = $meta_key['meta_key'];
1748 }
1749 }
1750 break;
1751 default:
1752
1753 // Get all meta keys for requested post type
1754 $this->data['meta_keys'] = array();
1755 $hide_fields = array('_edit_lock', '_edit_last', '_wp_trash_meta_status', '_wp_trash_meta_time');
1756
1757 $records = get_posts( array('post_type' => $post['custom_type'], 'post_status' => 'any') );
1758 if ( ! empty($records)){
1759 foreach ($records as $record) {
1760 $record_meta = get_post_meta($record->ID, '');
1761 if ( ! empty($record_meta)){
1762 foreach ($record_meta as $record_meta_key => $record_meta_value) {
1763 if ( ! in_array($record_meta_key, $this->data['meta_keys']) and ! in_array($record_meta_key, $hide_fields)) $this->data['meta_keys'][] = $record_meta_key;
1764 }
1765 }
1766 }
1767 }
1768
1769 if ($post['custom_type'] == 'product') {
1770 $records = get_posts( array('post_type' => 'product_variation', 'post_status' => 'any') );
1771 if ( ! empty($records)){
1772 foreach ($records as $record) {
1773 $record_meta = get_post_meta($record->ID, '');
1774 if ( ! empty($record_meta)){
1775 foreach ($record_meta as $record_meta_key => $record_meta_value) {
1776 if ( ! in_array($record_meta_key, $this->data['meta_keys']) and ! in_array($record_meta_key, $hide_fields)) $this->data['meta_keys'][] = $record_meta_key;
1777 }
1778 }
1779 }
1780 }
1781 }
1782
1783 // Get existing product attributes
1784 $existing_attributes = $wpdb->get_results("SELECT meta_value FROM $wpdb->postmeta WHERE meta_key = '_product_attributes' LIMIT 0 , 50" ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
1785 $this->data['existing_attributes'] = array();
1786 if ( ! empty($existing_attributes)){
1787 foreach ($existing_attributes as $key => $existing_attribute) {
1788 $existing_attribute = \pmxi_maybe_unserialize($existing_attribute->meta_value);
1789 if (!empty($existing_attribute) and is_array($existing_attribute)):
1790 foreach ($existing_attribute as $key => $value) {
1791 if (strpos($key, "pa_") === false and ! in_array($key, $this->data['existing_attributes'])) $this->data['existing_attributes'][] = $key;
1792 }
1793 endif;
1794 }
1795 }
1796
1797 break;
1798 }
1799
1800 if (user_can_richedit()) {
1801 wp_enqueue_script('editor');
1802 }
1803 wp_enqueue_script('word-count');
1804 add_thickbox();
1805 wp_enqueue_script('media-upload');
1806 wp_enqueue_script('quicktags');
1807
1808
1809
1810 $this->render();
1811 }
1812
1813 protected function _validate_template($text, $field_title) {
1814 try {
1815 if ($text != ''){
1816 $scanner = new XmlImportTemplateScanner();
1817 $tokens = $scanner->scan(new XmlImportStringReader($text));
1818 $parser = new XmlImportTemplateParser($tokens);
1819 $parser->parse();
1820 }
1821 } catch (XmlImportException $e) {
1822 /* translators: see placeholders in the string below */
1823 $this->errors->add('form-validation', sprintf(__('%1$s template is invalid: %2$s', 'wp-all-import'), $field_title, $e->getMessage()));
1824 }
1825 }
1826
1827 /**
1828 * Step #4: Options
1829 */
1830 public function options() {
1831
1832 $default = PMXI_Plugin::get_default_import_options();
1833
1834 if ($this->isWizard) {
1835 if (!PMXI_Plugin::$session->get('update_previous')) {
1836
1837 $import = new PMXI_Import_Record();
1838
1839 $friendly_name = ( ! empty(PMXI_Plugin::$session->options['friendly_name']) ) ? PMXI_Plugin::$session->options['friendly_name'] : '';
1840
1841 $import->set(
1842 (empty(PMXI_Plugin::$session->source) ? array() : PMXI_Plugin::$session->source)
1843 + array(
1844 'xpath' => PMXI_Plugin::$session->xpath,
1845 'options' => PMXI_Plugin::$session->options,
1846 'count' => PMXI_Plugin::$session->count,
1847 'friendly_name' => wp_all_import_clear_xss($friendly_name),
1848 'feed_type' => PMXI_Plugin::$session->feed_type,
1849 'parent_import_id' => ($this->data['update_previous']->isEmpty()) ? PMXI_Plugin::$session->parent_import_id : $this->data['update_previous']->parent_import_id,
1850 'queue_chunk_number' => 0,
1851 'triggered' => 0,
1852 'processing' => 0,
1853 'executing' => 0,
1854 'iteration' => (!empty($import->iteration)) ? $import->iteration : 0
1855 )
1856 )->save();
1857
1858 $history_file = new PMXI_File_Record();
1859 $history_file->set(array(
1860 'name' => $import->name,
1861 'import_id' => $import->id,
1862 'path' => wp_all_import_get_relative_path(PMXI_Plugin::$session->filePath),
1863 'registered_on' => gmdate('Y-m-d H:i:s'),
1864 ))->save();
1865
1866
1867 $this->data['update_previous'] = $import;
1868
1869 PMXI_Plugin::$session->set('update_previous', $import->id);
1870 PMXI_Plugin::$session->set('import_id', $import->id);
1871 PMXI_Plugin::$session->set('import', $import);
1872 PMXI_Plugin::$session->save_data();
1873 }
1874 $this->data['source_type'] = PMXI_Plugin::$session->source['type'];
1875
1876 foreach (PMXI_Admin_Addons::get_active_addons() as $class) {
1877 if (class_exists($class)) $default += call_user_func(array($class, "get_default_import_options"));
1878 }
1879
1880 $DefaultOptions = array_replace_recursive($default, (isset(PMXI_Plugin::$session->options) ? PMXI_Plugin::$session->options : array()));
1881
1882 if ( ! in_array(PMXI_Plugin::$session->options['custom_type'], array('import_users', 'shop_customer', 'shop_order', 'comments', 'woo_reviews', 'gf_entries')) ){
1883 if (empty(PMXI_Plugin::$session->options['title']))
1884 $this->warnings->add('form-validation', __('<strong>Warning:</strong> your title is blank.', 'wp-all-import'));
1885 }
1886
1887 $DefaultOptions['tmp_unique_key'] = $this->findUniqueKey();
1888
1889 if ($DefaultOptions['custom_type'] == "product" and class_exists('PMWI_Plugin') and $DefaultOptions['wizard_type'] != 'new'){
1890 $DefaultOptions['duplicate_indicator'] = empty($DefaultOptions['duplicate_indicator']) ? 'custom field' : $DefaultOptions['duplicate_indicator'];
1891 $DefaultOptions['custom_duplicate_name'] = empty($DefaultOptions['custom_duplicate_name']) ? '_sku' : $DefaultOptions['custom_duplicate_name'];
1892 }
1893
1894 $DefaultOptions['wizard_type'] = PMXI_Plugin::$session->wizard_type;
1895 if (empty($DefaultOptions['custom_type'])) $DefaultOptions['custom_type'] = PMXI_Plugin::$session->custom_type;
1896 if (empty($DefaultOptions['taxonomy_type'])) $DefaultOptions['taxonomy_type'] = PMXI_Plugin::$session->taxonomy_type;
1897 if (empty($DefaultOptions['delimiter'])) $DefaultOptions['delimiter'] = PMXI_Plugin::$session->is_csv;
1898 if (empty($DefaultOptions['ftp_host'])) $DefaultOptions['ftp_host'] = PMXI_Plugin::$session->ftp_host;
1899 if (empty($DefaultOptions['ftp_path'])) $DefaultOptions['ftp_path'] = PMXI_Plugin::$session->ftp_path;
1900 if (empty($DefaultOptions['ftp_root'])) $DefaultOptions['ftp_root'] = PMXI_Plugin::$session->ftp_root;
1901 if (empty($DefaultOptions['ftp_port'])) $DefaultOptions['ftp_port'] = PMXI_Plugin::$session->ftp_port;
1902 if (empty($DefaultOptions['ftp_username'])) $DefaultOptions['ftp_username'] = PMXI_Plugin::$session->ftp_username;
1903 if (empty($DefaultOptions['ftp_password'])) $DefaultOptions['ftp_password'] = PMXI_Plugin::$session->ftp_password;
1904 if (empty($DefaultOptions['ftp_private_key'])) $DefaultOptions['ftp_private_key'] = PMXI_Plugin::$session->ftp_private_key;
1905
1906 $post = $this->input->post( $DefaultOptions );
1907
1908 } else {
1909
1910 $this->data['source_type'] = $this->data['import']->type;
1911 foreach (PMXI_Admin_Addons::get_active_addons() as $class) {
1912 if (class_exists($class)) $default += call_user_func(array($class, "get_default_import_options"));
1913 }
1914
1915 $DefaultOptions = (is_array($this->data['import']->options)) ? array_replace_recursive($default, $this->data['import']->options) : $default;
1916
1917 $source = array(
1918 'name' => $this->data['import']->name,
1919 'type' => $this->data['import']->type,
1920 'path' => wp_all_import_get_relative_path($this->data['import']->path),
1921 'root_element' => $this->data['import']->root_element
1922 );
1923
1924 PMXI_Plugin::$session->set('source', $source);
1925
1926 $post = $this->input->post( $DefaultOptions );
1927
1928 }
1929
1930 $this->data['post'] =& $post;
1931
1932 PMXI_Plugin::$session->set('options', $post);
1933
1934 if ($this->input->post('is_submitted')) {
1935
1936 check_admin_referer('options', '_wpnonce_options');
1937
1938 $wp_uploads = wp_upload_dir();
1939 $functions = $wp_uploads['basedir'] . DIRECTORY_SEPARATOR . WP_ALL_IMPORT_UPLOADS_BASE_DIRECTORY . DIRECTORY_SEPARATOR . 'functions.php';
1940 $functions = apply_filters( 'import_functions_file_path', $functions );
1941 if ( @file_exists($functions) && PMXI_Plugin::$is_php_allowed)
1942 require_once $functions;
1943
1944
1945 // Block delete missing entirely for existing items (duplicate_matching != 'auto')
1946 if ( 'auto' != $post['duplicate_matching'] && ! empty($post['is_delete_missing']) ) {
1947 $this->errors->add('delete-missing-validation', __('Selected options are not supported in free edition.', 'wp-all-import'));
1948 }
1949
1950 if ($post['is_delete_missing'] && !empty($post['delete_missing_logic']) && $post['delete_missing_logic'] != 'import') {
1951 $this->errors->add('delete-missing-validation', __('Selected options are not supported in free edition.', 'wp-all-import'));
1952 }
1953
1954 if ($post['is_delete_missing'] && !empty($post['delete_missing_action']) && $post['delete_missing_action'] == 'keep') {
1955 if (empty($post['is_change_post_status_of_removed']) && empty($post['is_update_missing_cf']) && empty($post['missing_records_stock_status']) && empty($post['is_send_removed_to_trash'])) {
1956 $this->errors->add('delete-missing-validation', __('At least one option must be selected.', 'wp-all-import'));
1957 }
1958 if (!empty($post['is_send_removed_to_trash'])) {
1959 $this->errors->add('delete-missing-validation', __('Selected options are not supported in free edition.', 'wp-all-import'));
1960 }
1961 }
1962
1963 if ($post['is_import_specified']) {
1964 if (empty($post['import_specified'])) {
1965 $this->errors->add('form-validation', __('Records to import must be specified or uncheck `Import only specified records` option to process all records', 'wp-all-import'));
1966 } else {
1967 $chanks = preg_split('% *, *%', $post['import_specified']);
1968 foreach ($chanks as $chank) {
1969 if ( ! preg_match('%^([1-9]\d*)( *- *([1-9]\d*))?$%', $chank, $mtch)) {
1970 $this->errors->add('form-validation', __('Wrong format of `Import only specified records` value', 'wp-all-import'));
1971 break;
1972 } elseif (isset($mtch[3]) and intval($mtch[3]) > PMXI_Plugin::$session->count) {
1973 $this->errors->add('form-validation', __('One of the numbers in `Import only specified records` value exceeds record quantity in XML file', 'wp-all-import'));
1974 break;
1975 } elseif (preg_match('%^(\d+)-(\d+)$%', $chank, $mtch) && intval($mtch[1]) > intval($mtch[2])) {
1976 $this->errors->add('form-validation', __('Wrong format of `Import only specified records` value', 'wp-all-import'));
1977 }
1978 }
1979 }
1980 }
1981 if ('manual' != $post['duplicate_matching'] and '' == $post['unique_key']) {
1982 $this->errors->add('form-validation', __('Unique ID is currently empty and must be set. If you are not sure what to use as a Unique ID, click Auto-detect.', 'wp-all-import'));
1983 } elseif ('manual' != $post['duplicate_matching']) {
1984 $this->_validate_template($post['unique_key'], __('Post Unique Key', 'wp-all-import'));
1985 }
1986 if ( 'manual' == $post['duplicate_matching'] and 'custom field' == $post['duplicate_indicator']){
1987 if ('' == $post['custom_duplicate_name'])
1988 $this->errors->add('form-validation', __('Custom field name must be specified.', 'wp-all-import'));
1989 if ('' == $post['custom_duplicate_value'])
1990 $this->errors->add('form-validation', __('Custom field value must be specified.', 'wp-all-import'));
1991 }
1992 if ( 'manual' == $post['duplicate_matching'] ){
1993 if ( 'pid' == $post['duplicate_indicator'] && '' == $post['pid_xpath'] ){
1994 if ($post['custom_type'] == 'gf_entries') {
1995 $this->errors->add('form-validation', __('Entry ID must be specified.', 'wp-all-import'));
1996 } else {
1997 $this->errors->add('form-validation', __('Post ID must be specified.', 'wp-all-import'));
1998 }
1999 }
2000 if ( 'taxonomies' == $post['custom_type'] ){
2001 if ( 'title' == $post['duplicate_indicator'] && '' == $post['title_xpath'] ){
2002 $this->errors->add('form-validation', __('Term name must be specified.', 'wp-all-import'));
2003 }
2004 if ( 'slug' == $post['duplicate_indicator'] && '' == $post['slug_xpath'] ){
2005 $this->errors->add('form-validation', __('Term slug must be specified.', 'wp-all-import'));
2006 }
2007 }
2008 }
2009
2010 // Categories/taxonomies logic
2011 if ($post['update_categories_logic'] == 'only' and ! empty($post['taxonomies_only_list'])){
2012 $post['taxonomies_list'] = explode(",", $post['taxonomies_only_list']);
2013 }
2014 elseif ($post['update_categories_logic'] == 'all_except' and ! empty($post['taxonomies_except_list'])){
2015 $post['taxonomies_list'] = explode(",", $post['taxonomies_except_list']);
2016 }
2017
2018 // Custom fields logic
2019 if ($post['update_custom_fields_logic'] == 'only' and ! empty($post['custom_fields_only_list'])){
2020 $post['custom_fields_list'] = explode(",", $post['custom_fields_only_list']);
2021 }
2022 elseif ($post['update_custom_fields_logic'] == 'all_except' and ! empty($post['custom_fields_except_list']) ){
2023 $post['custom_fields_list'] = explode(",", $post['custom_fields_except_list']);
2024 }
2025
2026 $upload_result = false;
2027
2028 if ( ! $this->isWizard) {
2029
2030 // updating csv delimiter
2031 if ( $post['delimiter'] != $this->data['import']->options['delimiter'] ){
2032 $import_options = $this->data['import']->options;
2033 $import_options['delimiter'] = $post['delimiter'];
2034 $this->data['import']->set('options', $import_options)->save();
2035 }
2036
2037 // File Path validation
2038 switch ($this->input->post('new_type')){
2039 case 'upload':
2040 $filePath = $this->input->post('filepath');
2041 if ($this->data['import']['path'] != $filePath){
2042 $uploader = new PMXI_Upload($filePath, $this->errors);
2043 $upload_result = $uploader->upload();
2044 }
2045 break;
2046 case 'url':
2047 $filePath = $this->input->post('url');
2048
2049 if ($this->data['import']['path'] != $filePath){
2050
2051 $filesXML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<data><node></node></data>";
2052
2053 $filePath = apply_filters('wp_all_import_feed_url', wp_all_import_sanitize_url($filePath));
2054
2055 $filePaths = XmlImportParser::factory($filesXML, '/data/node', $filePath, $file)->parse(); $tmp_files[] = $file;
2056
2057 foreach ($tmp_files as $tmp_file) { // remove all temporary files created
2058 wp_delete_file($tmp_file);
2059 }
2060
2061 $file_to_import = $filePath;
2062
2063 if ( ! empty($filePaths) and is_array($filePaths) ) {
2064 $file_to_import = array_shift($filePaths);
2065 }
2066
2067 $uploader = new PMXI_Upload($file_to_import, $this->errors);
2068 $upload_result = $uploader->url($this->data['import']->feed_type, $filePath);
2069 }
2070
2071 break;
2072 case 'file':
2073 // Free version - existing file selection is not supported
2074 $this->errors->add('form-validation', __('Using existing files is not supported in the free edition. Please upgrade to Pro to use this feature.', 'wp-all-import'));
2075 break;
2076 case 'ftp':
2077 $filePath = $this->data['import']['path'];
2078 $ftp_host = $this->input->post('ftp_host');
2079 $ftp_port = $this->input->post('ftp_port');
2080 $ftp_path = $this->input->post('ftp_path');
2081 $ftp_root = $this->input->post('ftp_root');
2082 $ftp_username = $this->input->post('ftp_username');
2083 $ftp_password = $this->input->post('ftp_password');
2084 $ftp_private_key = $this->input->post('ftp_private_key');
2085 if ($ftp_host !== $this->data['import']['options']['ftp_host'] ||
2086 $ftp_path !== $this->data['import']['options']['ftp_path'] ||
2087 $ftp_root !== $this->data['import']['options']['ftp_root'] ||
2088 $ftp_port !== $this->data['import']['options']['ftp_port'] ||
2089 $ftp_username !== $this->data['import']['options']['ftp_username'] ||
2090 $ftp_password !== $this->data['import']['options']['ftp_password'] || $ftp_private_key !== $this->data['import']['options']['ftp_private_key']) {
2091 try {
2092 $files = PMXI_FTPFetcher::fetch([
2093 'ftp_host' => $ftp_host,
2094 'ftp_path' => $ftp_path,
2095 'ftp_root' => $ftp_root,
2096 'ftp_port' => $ftp_port,
2097 'ftp_username' => $ftp_username,
2098 'ftp_password' => $ftp_password,
2099 'ftp_private_key' => $ftp_private_key,
2100 ]);
2101 $uploader = new PMXI_Upload($files[0], $this->errors, rtrim(str_replace(basename($files[0]), '', $files[0]), '/'));
2102 $upload_result = $uploader->upload();
2103 } catch (Exception $e) {
2104 $this->errors->add('form-validation', $e->getMessage());
2105 }
2106 }
2107 break;
2108 default:
2109 $this->errors->add('form-validation', __('WP All Import doesn\'t support this import type.', 'wp-all-import'));
2110 break;
2111 }
2112
2113 $is_validate_file = apply_filters('wp_all_import_is_validate_file_options_update', true, $this->data['import']->id);
2114
2115 if (!$this->errors->get_error_codes() && $upload_result !== false and $this->data['import']['path'] != $filePath and $is_validate_file) {
2116
2117 $file = new PMXI_Chunk($upload_result['filePath'], array('element' => ( ! empty($this->data['import']->root_element)) ? $this->data['import']->root_element : ''));
2118
2119 $this->data['is_404'] = $file->is_404;
2120
2121 $root_element = '';
2122 if ( ! empty($file->options['element']) ) {
2123
2124 $root_element = $file->options['element'];
2125
2126 $baseXpath = $this->data['import']->xpath;
2127
2128 $loop = 0;
2129
2130 // loop through the file until all lines are read
2131 while ($xml = $file->read()) {
2132
2133 if ( ! empty($xml) ) {
2134 //PMXI_Import_Record::preprocessXml($xml);
2135 $xml = "<?xml version=\"1.0\" encoding=\"". $this->data['import']['options']['encoding'] ."\"?>" . "\n" . $xml;
2136
2137 $dom = new DOMDocument('1.0', $this->data['import']['options']['encoding']);
2138 $old = libxml_use_internal_errors(true);
2139 $dom->loadXML($xml);
2140 libxml_use_internal_errors($old);
2141 $xpath = new DOMXPath($dom);
2142
2143 if (($elements = @$xpath->query($baseXpath)) and $elements->length) $loop += $elements->length;
2144 unset($dom, $xpath, $elements);
2145
2146 }
2147 }
2148 unset($file);
2149
2150 if ( (int) $loop === 0 ){
2151
2152 $this->warnings->add('root-element-validation', __('<strong>Warning:</strong> this import file does not have the same structure as the last file associated with this import. WP All Import won\'t be able to import this file with your current settings. You\'ll probably need to adjust your XPath in the "Configure Advanced Settings" box below, and reconfigure your import by clicking "Edit" on the Manage Imports page.', 'wp-all-import'));
2153
2154 $file = new PMXI_Chunk($upload_result['filePath'], array('element' => ( ! empty($upload_result['root_element'])) ? $upload_result['root_element'] : ''));
2155
2156 if ( ! empty($file->options['element']) ) {
2157
2158 $root_element = $file->options['element'];
2159
2160 $baseXpath = '/' . $upload_result['root_element'];
2161
2162 $loop = 0;
2163
2164 // loop through the file until all lines are read
2165 while ($xml = $file->read()) {
2166 if ( ! empty($xml) ) {
2167 //PMXI_Import_Record::preprocessXml($xml);
2168 $xml = "<?xml version=\"1.0\" encoding=\"". $this->data['import']['options']['encoding'] ."\"?>" . "\n" . $xml;
2169 $dom = new DOMDocument('1.0', $this->data['import']['options']['encoding']);
2170 $old = libxml_use_internal_errors(true);
2171 $dom->loadXML($xml);
2172 libxml_use_internal_errors($old);
2173 $xpath = new DOMXPath($dom);
2174
2175 if (($elements = @$xpath->query($baseXpath)) and $elements->length) $loop += $elements->length;
2176 unset($dom, $xpath, $elements);
2177 }
2178 }
2179 unset($file);
2180 if ($loop) $this->data['import']->set(array('count' => $loop))->save();
2181 }
2182 }
2183 $upload_result['root_element'] = $root_element;
2184 } else {
2185 $this->warnings->add('root-element-validation', __('Root element not found for uploaded feed.', 'wp-all-import'));
2186 }
2187 }
2188 }
2189
2190 $this->errors = apply_filters('pmxi_options_validation', $this->errors, $post, isset($this->data['import']) ? $this->data['import'] : false);
2191
2192 if ( ! $this->errors->get_error_codes()) { // no validation errors found
2193
2194 // Attributes fields logic
2195 $post = apply_filters('pmxi_save_options', $post, $this->isWizard);
2196
2197 if ($this->isWizard) {
2198
2199 PMXI_Plugin::$session->set('options', $post);
2200
2201 PMXI_Plugin::$session->save_data();
2202
2203 if($this->data['update_previous']) {
2204 $this->data['update_previous']->set('options', $post)->save();
2205 }
2206
2207 // update import template with final settings
2208 if ( PMXI_Plugin::$session->saved_template ){
2209 $template = new PMXI_Template_Record();
2210 $template->getById(PMXI_Plugin::$session->saved_template)->set(array(
2211 'options' => $post
2212 ))->save();
2213 }
2214
2215 if ( ! $this->input->post('save_only')) {
2216 // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect
2217 wp_redirect(esc_url_raw(add_query_arg('action', 'confirm', $this->baseUrl))); die();
2218 } else {
2219 $import = $this->data['update_previous'];
2220 $is_update = ! $import->isEmpty();
2221 $import->set(
2222 PMXI_Plugin::$session->source
2223 + array(
2224 'xpath' => PMXI_Plugin::$session->xpath,
2225 'options' => PMXI_Plugin::$session->options,
2226 'count' => PMXI_Plugin::$session->count,
2227 'friendly_name' => wp_all_import_clear_xss($this->data['post']['friendly_name']),
2228 )
2229 )->save();
2230
2231 // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect
2232 wp_redirect(esc_url_raw(add_query_arg(array('page' => 'pmxi-admin-manage', 'pmxi_nt' => urlencode($is_update ? __('Import updated', 'wp-all-import') : __('Import created', 'wp-all-import'))), admin_url('admin.php')))); die();
2233 }
2234
2235 } else {
2236
2237 $xpath = $this->input->post('xpath');
2238
2239 $toUpdate = array(
2240 'friendly_name' => wp_all_import_clear_xss($this->data['post']['friendly_name']),
2241 'xpath' => $this->input->post('xpath'),
2242 'settings_update_on' => gmdate('Y-m-d H:i:s')
2243 );
2244
2245 // detecting root element
2246 if ( $xpath != $this->data['import']->xpath ){
2247 $xpath_elements = explode('[', $xpath);
2248 $xpath_parts = explode('/', $xpath_elements[0]);
2249 $toUpdate['root_element'] = $xpath_parts[1];
2250 }
2251
2252 $this->data['import']->set('options', $post)->set( $toUpdate )->save();
2253
2254 // set new import file
2255
2256 switch ($this->input->post('new_type')){
2257 case 'upload':
2258 $filePath = $this->input->post('filepath');
2259 $source = array(
2260 'name' => basename($filePath),
2261 'type' => 'upload',
2262 'path' => $filePath,
2263 );
2264 break;
2265 case 'url':
2266 $filePath = $this->input->post('url');
2267
2268 $filePath = apply_filters('wp_all_import_feed_url', wp_all_import_sanitize_url($filePath));
2269
2270 $source = array(
2271 'name' => basename(wp_parse_url($filePath, PHP_URL_PATH)),
2272 'type' => 'url',
2273 'path' => $filePath,
2274 );
2275 break;
2276 case 'file':
2277 // Free version - existing file selection is not supported
2278 $this->errors->add('form-validation', __('Using existing files is not supported in the free edition. Please upgrade to Pro to use this feature.', 'wp-all-import'));
2279 break;
2280 case 'ftp':
2281 $filePath = empty($upload_result) ? $filePath : $upload_result['filePath'];
2282 $source = array(
2283 'name' => basename($filePath),
2284 'type' => 'ftp',
2285 'path' => $filePath,
2286 );
2287 break;
2288 }
2289
2290 $source['path'] = wp_all_import_get_relative_path($source['path']);
2291
2292 // if new file is successfully uploaded
2293 if (!empty($upload_result['filePath'])){
2294 // unlink previous files
2295 $history = new PMXI_File_List();
2296 $history->setColumns('id', 'name', 'registered_on', 'path')->getBy(array('import_id' => $this->data['import']->id), 'id DESC');
2297 if ($history->count()){
2298 foreach ($history as $file){
2299 $history_file_path = wp_all_import_get_absolute_path($file['path']);
2300 if ( @file_exists($history_file_path) and $history_file_path != $upload_result['filePath'] ){
2301 if (in_array($this->data['import']->type, array('upload')))
2302 wp_all_import_remove_source($history_file_path, false);
2303 else
2304 wp_all_import_remove_source($history_file_path);
2305 }
2306 $history_file = new PMXI_File_Record();
2307 $history_file->getBy('id', $file['id']);
2308 if ( ! $history_file->isEmpty()) $history_file->delete( $history_file_path != $upload_result['filePath'] );
2309 }
2310 }
2311
2312 $history_file = new PMXI_File_Record();
2313 $history_file->set(array(
2314 'name' => $this->data['import']->name,
2315 'import_id' => $this->data['import']->id,
2316 'path' => wp_all_import_get_relative_path($upload_result['filePath']),
2317 'registered_on' => gmdate('Y-m-d H:i:s')
2318 ))->save();
2319 }
2320
2321 if ( ! $this->warnings->get_error_codes()) {
2322 $this->data['import']->set($source)->save();
2323 // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect
2324 wp_redirect(esc_url_raw(add_query_arg(array('page' => 'pmxi-admin-manage', 'pmxi_nt' => urlencode(__('Configuration updated', 'wp-all-import'))) + array_intersect_key($_GET, array_flip($this->baseUrlParamNames)), admin_url('admin.php')))); die();
2325 } else {
2326 $source['root_element'] = $upload_result['root_element'];
2327 PMXI_Plugin::$session->set('source', $source);
2328 $this->data['import']->set( array_merge($source, array('xpath' => '/' . $upload_result['root_element'])) )->save();
2329 }
2330 }
2331 }
2332 }
2333
2334 global $wpdb;
2335
2336 $this->data['existing_meta_keys'] = array();
2337
2338 switch ($post['custom_type']){
2339 case 'import_users':
2340 case 'shop_customer':
2341 // Get All meta keys in the system
2342 $this->data['meta_keys'] = array();
2343 $meta_keys = new PMXI_Model_List();
2344 $meta_keys->setTable($wpdb->usermeta);
2345 $meta_keys->setColumns('umeta_id', 'meta_key')->getBy(NULL, "umeta_id", NULL, NULL, "meta_key");
2346 $hide_fields = array('first_name', 'last_name', 'nickname', 'description', PMXI_Plugin::getInstance()->getWPPrefix() . 'capabilities');
2347 if ( ! empty($meta_keys) and $meta_keys->count() ){
2348 foreach ($meta_keys as $meta_key) { if (in_array($meta_key['meta_key'], $hide_fields) or strpos($meta_key['meta_key'], '_wp') === 0) continue;
2349 $this->data['existing_meta_keys'][] = $meta_key['meta_key'];
2350 }
2351 }
2352 break;
2353 case 'taxonomies':
2354 // Free version - taxonomy imports are not supported
2355 $this->data['existing_meta_keys'] = array();
2356 break;
2357 case 'comments':
2358 case 'woo_reviews':
2359 // Get All meta keys in the system
2360 $this->data['meta_keys'] = array();
2361 $meta_keys = new PMXI_Model_List();
2362 $meta_keys->setTable(PMXI_Plugin::getInstance()->getWPPrefix() . 'commentmeta');
2363 $meta_keys->setColumns('meta_id', 'meta_key')->getBy(NULL, "meta_id", NULL, NULL, "meta_key");
2364 $hide_fields = array();
2365 if ( ! empty($meta_keys) and $meta_keys->count() ){
2366 foreach ($meta_keys as $meta_key) { if (in_array($meta_key['meta_key'], $hide_fields)) continue;
2367 $this->data['existing_meta_keys'][] = $meta_key['meta_key'];
2368 }
2369 }
2370 break;
2371 default:
2372
2373 // Get all meta keys for requested post type
2374 $hide_fields = array('_edit_lock', '_edit_last', '_wp_trash_meta_status', '_wp_trash_meta_time');
2375
2376 if ( $post['custom_type'] == 'product' ) {
2377 $records = get_posts( array('post_type' => array('product', 'product_variation')) );
2378 } else {
2379 $records = get_posts( array('post_type' => $post['custom_type']) );
2380 }
2381
2382 if ( ! empty($records)){
2383 foreach ($records as $record) {
2384 $record_meta = get_post_meta($record->ID, '');
2385 if ( ! empty($record_meta)){
2386 foreach ($record_meta as $record_meta_key => $record_meta_value) {
2387 if ( ! in_array($record_meta_key, $this->data['existing_meta_keys']) and ! in_array($record_meta_key, $hide_fields)) $this->data['existing_meta_keys'][] = $record_meta_key;
2388 }
2389 }
2390 }
2391 }
2392
2393 $this->data['existing_meta_keys'] = apply_filters('wp_all_import_existing_meta_keys', $this->data['existing_meta_keys'], $post['custom_type']);
2394
2395 // Get existing product attributes
2396 $existing_attributes = $wpdb->get_results("SELECT meta_value FROM $wpdb->postmeta WHERE meta_key = '_product_attributes' LIMIT 0 , 50" ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
2397 $this->data['existing_attributes'] = array();
2398 if ( ! empty($existing_attributes)){
2399 foreach ($existing_attributes as $key => $existing_attribute) {
2400 $existing_attribute = \pmxi_maybe_unserialize($existing_attribute->meta_value);
2401 if (!empty($existing_attribute) and is_array($existing_attribute)):
2402 foreach ($existing_attribute as $key => $value) {
2403 if (strpos($key, "pa_") === false and ! in_array($key, $this->data['existing_attributes'])) $this->data['existing_attributes'][] = $key;
2404 }
2405 endif;
2406 }
2407 }
2408 break;
2409 }
2410
2411 $this->render();
2412 }
2413
2414 /**
2415 * Step #5: Confirm & Run Import
2416 */
2417 public function confirm(){
2418
2419 $default = PMXI_Plugin::get_default_import_options();
2420
2421 $this->data['source'] = PMXI_Plugin::$session->source;
2422 $this->data['locfilePath'] = PMXI_Plugin::$session->filePath;
2423 $this->data['count'] = PMXI_Plugin::$session->count;
2424 $this->data['xpath'] = PMXI_Plugin::$session->xpath;
2425 if (empty($this->data['import'])){
2426 $this->data['import'] = $this->data['update_previous'];
2427 }
2428 $this->data['import_id_val'] = PMXI_Plugin::$session->update_previous;
2429 $this->data['isWizard'] = true;
2430 $DefaultOptions = (isset(PMXI_Plugin::$session->options) ? PMXI_Plugin::$session->options : array()) + $default;
2431 foreach (PMXI_Admin_Addons::get_active_addons() as $class) {
2432 if (class_exists($class)) $DefaultOptions += call_user_func(array($class, "get_default_import_options"));
2433 }
2434
2435 if ($this->isWizard and ! in_array(PMXI_Plugin::$session->options['custom_type'], array('import_users', 'shop_customer', 'shop_order', 'comments', 'woo_reviews', 'gf_entries'))){
2436 if (empty(PMXI_Plugin::$session->options['title']))
2437 $this->warnings->add('form-validation', __('<strong>Warning:</strong> your title is blank.', 'wp-all-import'));
2438 }
2439
2440 $this->data['post'] =& $DefaultOptions;
2441
2442 if ($this->input->post('is_confirmed')) {
2443
2444 check_admin_referer('confirm', '_wpnonce_confirm');
2445
2446 $continue = $this->input->post('is_continue', 'no');
2447
2448 if ($continue == 'yes'){
2449 PMXI_Plugin::$session->set('action', 'continue');
2450 PMXI_Plugin::$session->save_data();
2451 }
2452
2453 if ( ! $this->errors->get_error_codes()) { // no validation errors found
2454 // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect
2455 wp_redirect(esc_url_raw(add_query_arg('action', 'process', $this->baseUrl))); die();
2456 }
2457
2458 }
2459
2460 $this->render();
2461 }
2462
2463 /**
2464 * Import processing step (status console)
2465 */
2466 public function process($save_history = true) {
2467
2468 $wp_uploads = wp_upload_dir();
2469 $import = $this->data['update_previous'];
2470 $history_log = new PMXI_History_Record();
2471 $input = new PMXI_Input();
2472
2473 if ( ! empty(PMXI_Plugin::$session->history_id) ) {
2474 $history_log->getById(PMXI_Plugin::$session->history_id);
2475 }
2476
2477 $log_storage = (int) PMXI_Plugin::getInstance()->getOption('log_storage');
2478
2479 if ( ! PMXI_Plugin::is_ajax() ) {
2480
2481 $import->set(
2482 (empty(PMXI_Plugin::$session->source) ? array() : PMXI_Plugin::$session->source)
2483 + array(
2484 'xpath' => PMXI_Plugin::$session->xpath,
2485 'options' => ($this->data['update_previous']->isEmpty()) ? PMXI_Plugin::$session->options : $import->options + PMXI_Plugin::$session->options,
2486 'count' => PMXI_Plugin::$session->count,
2487 'friendly_name' => wp_all_import_clear_xss(PMXI_Plugin::$session->options['friendly_name']),
2488 'feed_type' => PMXI_Plugin::$session->feed_type,
2489 'parent_import_id' => ($this->data['update_previous']->isEmpty()) ? PMXI_Plugin::$session->parent_import_id : $this->data['update_previous']->parent_import_id,
2490 'queue_chunk_number' => 0,
2491 'triggered' => 0,
2492 'processing' => 0,
2493 'executing' => 1,
2494 'iteration' => ( ! empty($import->iteration) ) ? $import->iteration : 0
2495 )
2496 )->save();
2497
2498 if ( PMXI_Plugin::$session->action != 'continue' ){
2499 // store import info in database
2500 $import->set(array(
2501 'imported' => 0,
2502 'created' => 0,
2503 'updated' => 0,
2504 'skipped' => 0,
2505 'deleted' => 0,
2506 'changed_missing' => 0
2507 ))->update();
2508 }
2509
2510 // Add history log.
2511 $custom_type = wp_all_import_custom_type_labels($import->options['custom_type'], $import->options['taxonomy_type']);
2512
2513 // Unlink previous logs.
2514 $by = array();
2515 $by[] = array(array('import_id' => $import->id), 'AND');
2516 $historyLogs = new PMXI_History_List();
2517 $historyLogs->setColumns('id', 'import_id', 'type', 'date')->getBy($by, 'id ASC');
2518 if ($historyLogs->count() and $historyLogs->count() >= $log_storage ){
2519 $logsToRemove = $historyLogs->count() - $log_storage;
2520 foreach ($historyLogs as $i => $file){
2521 $historyRecord = new PMXI_History_Record();
2522 $historyRecord->getBy('id', $file['id']);
2523 if ( ! $historyRecord->isEmpty()) $historyRecord->delete(); // unlink history file only
2524 if ($i == $logsToRemove)
2525 break;
2526 }
2527 }
2528
2529 /* translators: see placeholders in the string below */
2530 $log_msg = sprintf(__("%1\$d %2\$s created %3\$d updated %4\$d skipped", "wp-all-import"), $import->created, ( ($import->created == 1) ? $custom_type->labels->singular_name : $custom_type->labels->name ), $import->updated, $import->skipped);
2531 if ($import->options['is_delete_missing']) {
2532 if (empty($import->options['delete_missing_action']) || $import->options['delete_missing_action'] != 'remove') {
2533 /* translators: see placeholders in the string below */
2534 $log_msg = sprintf(__("%1\$d %2\$s created %3\$d updated %4\$d changed missing %5\$d skipped", "wp-all-import"), $import->created, ( ($import->created == 1) ? $custom_type->labels->singular_name : $custom_type->labels->name ), $import->updated, $import->changed_missing, $import->skipped);
2535 } else {
2536 /* translators: see placeholders in the string below */
2537 $log_msg = sprintf(__("%1\$d %2\$s created %3\$d updated %4\$d deleted %5\$d skipped", "wp-all-import"), $import->created, ( ($import->created == 1) ? $custom_type->labels->singular_name : $custom_type->labels->name ), $import->updated, $import->deleted, $import->skipped);
2538 }
2539 }
2540 $history_log->set(array(
2541 'import_id' => $import->id,
2542 'date' => gmdate('Y-m-d H:i:s'),
2543 'type' => ( PMXI_Plugin::$session->action != 'continue' ) ? 'manual' : 'continue',
2544 'summary' => $log_msg
2545 ))->save();
2546
2547 PMXI_Plugin::$session->set('history_id', $history_log->id);
2548
2549 foreach ( get_taxonomies() as $tax ) {
2550 delete_transient("pmxi_{$tax}_terms");
2551 }
2552
2553 $functions = $wp_uploads['basedir'] . DIRECTORY_SEPARATOR . WP_ALL_IMPORT_UPLOADS_BASE_DIRECTORY . DIRECTORY_SEPARATOR . 'functions.php';
2554 $functions = apply_filters( 'import_functions_file_path', $functions );
2555 if ( @file_exists($functions) && PMXI_Plugin::$is_php_allowed)
2556 require_once $functions;
2557
2558 PMXI_Plugin::$session->set('update_previous', $import->id);
2559
2560 if (empty($import->options['encoding'])){
2561 $currentOptions = $import->options;
2562 $currentOptions['encoding'] = 'UTF-8';
2563 $import->set(array(
2564 'options' => $currentOptions
2565 ))->update();
2566 }
2567
2568 // unlink previous files
2569 $history = new PMXI_File_List();
2570 $history->setColumns('id', 'name', 'registered_on', 'path')->getBy(array('import_id' => $import->id), 'id DESC');
2571 if ($history->count()){
2572 foreach ($history as $file){
2573 $history_file_path = wp_all_import_get_absolute_path($file['path']);
2574 if ( @file_exists($history_file_path) and $history_file_path != PMXI_Plugin::$session->filePath ){
2575 if (in_array($import->type, array('upload')))
2576 wp_all_import_remove_source($history_file_path, false);
2577 else
2578 wp_all_import_remove_source($history_file_path);
2579 }
2580 $history_file = new PMXI_File_Record();
2581 $history_file->getBy('id', $file['id']);
2582 if ( ! $history_file->isEmpty()) $history_file->delete( $history_file_path != PMXI_Plugin::$session->filePath );
2583 }
2584 }
2585
2586 if ($save_history){
2587 $history_file = new PMXI_File_Record();
2588 $history_file->set(array(
2589 'name' => $import->name,
2590 'import_id' => $import->id,
2591 'path' => wp_all_import_get_relative_path(PMXI_Plugin::$session->filePath),
2592 'registered_on' => gmdate('Y-m-d H:i:s')
2593 ))->save();
2594 }
2595
2596 do_action( 'pmxi_before_xml_import', $import->id );
2597
2598 /*
2599 Split file up into 1000 record chunks.
2600 This option will decrease the amount of slowdown experienced at the end of large imports.
2601 The slowdown is partially caused by the need for WP All Import to read deeper and deeper into the file on each successive iteration.
2602 Splitting the file into pieces means that, for example, instead of having to read 19000 records into a 20000 record file when importing the last 1000 records,
2603 WP All Import will just split it into 20 chunks, and then read the last chunk from the beginning.
2604 */
2605 if ( $import->count > PMXI_Plugin::getInstance()->getOption('large_feed_limit') and $import->options['chuncking'] ){
2606
2607 $chunk_files = array();
2608
2609 if ( ! empty(PMXI_Plugin::$session->local_paths)) {
2610
2611 $records_count = 0;
2612 $chunk_records_count = 0;
2613
2614 $feed = "<?xml version=\"1.0\" encoding=\"". $import->options['encoding'] ."\"?>" . "\n" . "<pmxi_records>";
2615
2616 foreach (PMXI_Plugin::$session->local_paths as $key => $path) {
2617
2618 $file = new PMXI_Chunk($path, array('element' => $import->root_element, 'encoding' => $import->options['encoding']));
2619 // Loop through the file until all lines are read.
2620 while ($xml = $file->read()) {
2621
2622 if ( ! empty($xml) ) {
2623 //PMXI_Import_Record::preprocessXml($xml);
2624 $chunk = "<?xml version=\"1.0\" encoding=\"". $import->options['encoding'] ."\"?>" . "\n" . $xml;
2625
2626 $dom = new DOMDocument('1.0', $import->options['encoding']);
2627 $old = libxml_use_internal_errors(true);
2628 $dom->loadXML($chunk); // FIX: libxml xpath doesn't handle default namespace properly, so remove it upon XML load
2629 libxml_use_internal_errors($old);
2630 $xpath = new DOMXPath($dom);
2631
2632 if ($elements = @$xpath->query($import->xpath) and $elements->length){
2633 $records_count += $elements->length;
2634 $chunk_records_count += $elements->length;
2635 $feed .= $xml;
2636 }
2637 }
2638
2639 if ( $chunk_records_count == PMXI_Plugin::getInstance()->getOption('large_feed_limit') or $records_count == $import->count ){
2640 $feed .= "</pmxi_records>";
2641 $chunk_file_path = wp_all_import_secure_file($wp_uploads['basedir'] . DIRECTORY_SEPARATOR . PMXI_Plugin::TEMP_DIRECTORY, $import->id) . DIRECTORY_SEPARATOR . "pmxi_chunk_" . count($chunk_files) . "_" . basename($path);
2642 file_put_contents($chunk_file_path, $feed);
2643 $chunk_files[] = $chunk_file_path;
2644 $chunk_records_count = 0;
2645 $feed = "<?xml version=\"1.0\" encoding=\"". $import->options['encoding'] ."\"?>" . "\n" . "<pmxi_records>";
2646 }
2647 }
2648 }
2649 PMXI_Plugin::$session->set('local_paths', $chunk_files);
2650 }
2651 }
2652
2653 PMXI_Plugin::$session->save_data();
2654
2655 if ( $log_storage ){
2656 $log_file = wp_all_import_secure_file( $wp_uploads['basedir'] . DIRECTORY_SEPARATOR . PMXI_Plugin::LOGS_DIRECTORY, $history_log->id ) . DIRECTORY_SEPARATOR . $history_log->id . '.html';
2657 if ( PMXI_Plugin::$session->action != 'continue'){
2658 if (file_exists($log_file)) {
2659 wp_all_import_remove_source($log_file, false);
2660 }
2661 }
2662 }
2663
2664 $this->data['ajax_processing'] = true;
2665
2666 $this->render();
2667 wp_ob_end_flush_all(); flush();
2668 // phpcs:ignore Squiz.PHP.DiscouragedFunctions.Discouraged
2669 @set_time_limit(0);
2670
2671 $import_id = $input->get('id', 0);
2672
2673 if ( ! $import_id ) {
2674 PMXI_Plugin::$session->convertData($import->id);
2675 }
2676 }
2677 elseif (empty($import->id)) {
2678 $import = new PMXI_Import_Record();
2679 $import_id = $input->get('id', PMXI_Plugin::$session->update_previous);
2680 $import->getById($import_id);
2681 }
2682
2683 $ajax_processing = true;
2684
2685 if ( PMXI_Plugin::is_ajax() and $ajax_processing and ! check_ajax_referer( 'wp_all_import_secure', 'security', false )){
2686 exit( esc_html__('Security check', 'wp-all-import') );
2687 }
2688
2689 if ($ajax_processing) {
2690 $logger = function($m) {echo "<div class='progress-msg'>[". esc_html(gmdate("H:i:s")) ."] ".wp_all_import_filter_html_kses($m)."</div>\n";flush();}; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- wp_all_import_filter_html_kses sanitizes via wp_kses before output.
2691 } else {
2692 $logger = function($m) {echo "<div class='progress-msg'>".wp_all_import_filter_html_kses($m)."</div>\n"; if ( "" != wp_strip_all_tags(wp_all_import_strip_tags_content(wp_all_import_filter_html_kses($m)))) { PMXI_Plugin::$session->log .= "<p>".wp_strip_all_tags(wp_all_import_strip_tags_content(wp_all_import_filter_html_kses($m)))."</p>"; flush(); }}; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- wp_all_import_filter_html_kses sanitizes via wp_kses before output.
2693 }
2694
2695 $logger = apply_filters('wp_all_import_logger', $logger);
2696
2697 PMXI_Plugin::$session->set('start_time', (empty(PMXI_Plugin::$session->start_time)) ? time() : PMXI_Plugin::$session->start_time);
2698
2699 $is_reset_cache = apply_filters('wp_all_import_reset_cache_before_import', false, $import->id);
2700
2701 if ($is_reset_cache) {
2702 wp_cache_flush();
2703 }
2704
2705 wp_defer_term_counting(true);
2706 wp_defer_comment_counting(true);
2707
2708 if ( PMXI_Plugin::is_ajax() or ! $ajax_processing ) {
2709
2710 $functions = $wp_uploads['basedir'] . DIRECTORY_SEPARATOR . WP_ALL_IMPORT_UPLOADS_BASE_DIRECTORY . DIRECTORY_SEPARATOR . 'functions.php';
2711 $functions = apply_filters( 'import_functions_file_path', $functions );
2712 if ( @file_exists($functions) && PMXI_Plugin::$is_php_allowed)
2713 require_once $functions;
2714
2715 $iteration_start_time = time();
2716
2717 if ( $log_storage ) {
2718 $log_file = wp_all_import_secure_file( $wp_uploads['basedir'] . DIRECTORY_SEPARATOR . PMXI_Plugin::LOGS_DIRECTORY, $history_log->id ) . DIRECTORY_SEPARATOR . $history_log->id . '.html';
2719 }
2720
2721 if ( $ajax_processing ) {
2722 // HTTP headers for no cache etc
2723 header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
2724 header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
2725 header("Cache-Control: no-store, no-cache, must-revalidate");
2726 header("Cache-Control: post-check=0, pre-check=0", false);
2727 header("Pragma: no-cache");
2728 }
2729
2730 $loop = 0;
2731 $pointer = 0;
2732 $records = array();
2733
2734 if ($import->options['is_import_specified']) {
2735 $import_specified_option = apply_filters('wp_all_import_specified_records', $import->options['import_specified'], $import->id, false);
2736 foreach (preg_split('% *, *%', $import_specified_option, -1, PREG_SPLIT_NO_EMPTY) as $chank) {
2737 if (preg_match('%^(\d+)-(\d+)$%', $chank, $mtch)) {
2738 $records = array_merge($records, range(intval($mtch[1]), intval($mtch[2])));
2739 } else {
2740 $records = array_merge($records, array(intval($chank)));
2741 }
2742 }
2743 }
2744
2745 $records_to_import = (empty($records) || $import->options['is_delete_missing']) ? $import->count : $records[count($records) -1];
2746
2747 $failures = $input->get('failures', 0);
2748
2749 // Auto decrease records per iteration option.
2750 if ($failures) {
2751 $options = $import->options;
2752 $options['records_per_request'] = (ceil($options['records_per_request']/2)) ? ceil($options['records_per_request']/2) : 1;
2753 $import->set(array('options' => $options))->update();
2754 }
2755
2756 $records_per_request = ( ! $ajax_processing and $import->options['records_per_request'] < 50 ) ? 50 : $import->options['records_per_request'];
2757
2758 if (!empty(PMXI_Plugin::$session->local_paths)) {
2759
2760 if (!empty($records) && $import->queue_chunk_number < $records[0] && strpos($import->xpath, "[") === false && ! $import->options['is_delete_missing']) {
2761 $pointer = $records[0];
2762 }
2763 $chunk_records_count = PMXI_Plugin::getInstance()->getOption('large_feed_limit');
2764 $feed = "<?xml version=\"1.0\" encoding=\"". $import->options['encoding'] ."\"?>" . "\n" . "<pmxi_records>";
2765
2766 foreach (PMXI_Plugin::$session->local_paths as $key => $path) {
2767 $import_done = ($import->imported + $import->skipped == $records_to_import ) ? true : false;
2768 if ($import_done) {
2769 foreach (PMXI_Plugin::$session->local_paths as $chunk_file) {
2770 if (strpos($chunk_file, "pmxi_chunk_") !== false and @file_exists($chunk_file)) wp_all_import_remove_source($chunk_file, false);
2771 }
2772 PMXI_Plugin::$session->set('local_paths', array());
2773 PMXI_Plugin::$session->save_data();
2774 break;
2775 }
2776 // Set XMLReader pointer to first value of specified records option.
2777 if ( ! empty($records) && $import->queue_chunk_number < $records[0] && strpos($import->xpath, "[") === false && ! $import->options['is_delete_missing']) {
2778 if ($import->options['chuncking'] && $pointer > $chunk_records_count) {
2779 $pointer -= $chunk_records_count;
2780 if (strpos($path, "pmxi_chunk_") !== false and @file_exists($path)) {
2781 wp_delete_file($path);
2782 }
2783 PMXI_Plugin::$session->set('chunk_number', $import->queue_chunk_number + $chunk_records_count);
2784 $lp = PMXI_Plugin::$session->local_paths;
2785 array_shift($lp);
2786 PMXI_Plugin::$session->set('local_paths', $lp);
2787 PMXI_Plugin::$session->save_data();
2788
2789 $import->set(array(
2790 'skipped' => $import->skipped + $chunk_records_count,
2791 'queue_chunk_number' => $import->queue_chunk_number + $chunk_records_count
2792 ))->save();
2793 continue;
2794 }
2795 PMXI_Plugin::$session->set('chunk_number', $import->queue_chunk_number + $pointer);
2796 PMXI_Plugin::$session->set('pointer', $pointer);
2797 PMXI_Plugin::$session->save_data();
2798 $import->set(array(
2799 'skipped' => $import->skipped + $pointer - 1,
2800 'queue_chunk_number' => $import->queue_chunk_number + $pointer
2801 ))->save();
2802 $pointer = 0;
2803 }
2804 $file = new PMXI_Chunk($path, array(
2805 'element' => $import->root_element,
2806 'encoding' => $import->options['encoding'],
2807 'pointer' => PMXI_Plugin::$session->pointer,
2808 'filter' => true
2809 ));
2810 // Loop through the file until all lines are read.
2811 while ($xml = $file->read() and empty($import->canceled) ) {
2812 if (!empty($xml)) {
2813 $chunk = "<?xml version=\"1.0\" encoding=\"". $import->options['encoding'] ."\"?>" . "\n" . $xml;
2814 $dom = new DOMDocument('1.0', $import->options['encoding']);
2815 $old = libxml_use_internal_errors(true);
2816 $dom->loadXML($chunk); // FIX: libxml xpath doesn't handle default namespace properly, so remove it upon XML load
2817 libxml_use_internal_errors($old);
2818 $xpath = new DOMXPath($dom);
2819 $pointer++;
2820 if (($this->data['elements'] = $elements = @$xpath->query($import->xpath)) and $elements->length) {
2821 // Continue action.
2822 if ( $import->imported + $import->skipped >= PMXI_Plugin::$session->chunk_number + $elements->length - 1 ){
2823 PMXI_Plugin::$session->set('chunk_number', PMXI_Plugin::$session->chunk_number + $elements->length);
2824 PMXI_Plugin::$session->save_data();
2825 continue;
2826 }
2827 if ( ! $loop and $ajax_processing ) {
2828 ob_start();
2829 }
2830 $feed .= $xml; $loop += $elements->length;
2831 $processed_records = $import->imported + $import->skipped;
2832 if ( $loop == $records_per_request or $processed_records + $loop == $records_to_import or $processed_records == $records_to_import) {
2833 $feed .= "</pmxi_records>";
2834 $import->process($feed, $logger, PMXI_Plugin::$session->chunk_number, false, '/pmxi_records', $loop);
2835 unset($dom, $xpath);
2836 if ( ! $ajax_processing ){
2837 $feed = "<?xml version=\"1.0\" encoding=\"". $import->options['encoding'] ."\"?>" . "\n" . "<pmxi_records>";
2838 $loop = 0;
2839 } else {
2840 if ( ! $history_log->isEmpty()) {
2841 $custom_type = wp_all_import_custom_type_labels($import->options['custom_type'], $import->options['taxonomy_type']);
2842 /* translators: see placeholders in the string below */
2843 $log_msg = sprintf(__("%1\$d %2\$s created %3\$d updated %4\$d skipped", "wp-all-import"), $import->created, ( ($import->created == 1) ? $custom_type->labels->singular_name : $custom_type->labels->name ), $import->updated, $import->skipped);
2844 if ($import->options['is_delete_missing']) {
2845 if (empty($import->options['delete_missing_action']) || $import->options['delete_missing_action'] != 'remove') {
2846 /* translators: see placeholders in the string below */
2847 $log_msg = sprintf(__("%1\$d %2\$s created %3\$d updated %4\$d changed missing %5\$d skipped", "wp-all-import"), $import->created, ( ($import->created == 1) ? $custom_type->labels->singular_name : $custom_type->labels->name ), $import->updated, $import->changed_missing, $import->skipped);
2848 } else {
2849 /* translators: see placeholders in the string below */
2850 $log_msg = sprintf(__("%1\$d %2\$s created %3\$d updated %4\$d deleted %5\$d skipped", "wp-all-import"), $import->created, ( ($import->created == 1) ? $custom_type->labels->singular_name : $custom_type->labels->name ), $import->updated, $import->deleted, $import->skipped);
2851 }
2852 }
2853
2854 $history_log->set(array(
2855 'time_run' => time() - strtotime($history_log->date),
2856 'summary' => $log_msg
2857 ))->update();
2858 }
2859 unset($file);
2860 PMXI_Plugin::$session->set('pointer', PMXI_Plugin::$session->pointer + $pointer);
2861 PMXI_Plugin::$session->save_data();
2862
2863 $log_data = ob_get_clean();
2864 if ($log_storage) {
2865 // phpcs:disable WordPress.WP.AlternativeFunctions.file_system_operations_fopen, WordPress.WP.AlternativeFunctions.file_system_operations_fwrite, WordPress.WP.AlternativeFunctions.file_system_operations_fclose
2866 $log = @fopen($log_file, 'a+');
2867 if ( is_resource( $log ) ) {
2868 @fwrite($log, $log_data);
2869 @fclose($log);
2870 }
2871 // phpcs:enable WordPress.WP.AlternativeFunctions.file_system_operations_fopen, WordPress.WP.AlternativeFunctions.file_system_operations_fwrite, WordPress.WP.AlternativeFunctions.file_system_operations_fclose
2872 }
2873 $iteration_execution_time = time() - $iteration_start_time;
2874 wp_send_json(array(
2875 'imported' => $import->imported,
2876 'created' => $import->created,
2877 'updated' => $import->updated,
2878 'skipped' => $import->skipped,
2879 'skipped_by_hash' => PMXI_Plugin::$session->skipped,
2880 'deleted' => $import->deleted,
2881 'changed_missing' => $import->changed_missing,
2882 'percentage' => ceil(($processed_records/$import->count) * 100),
2883 'warnings' => PMXI_Plugin::$session->warnings,
2884 'errors' => PMXI_Plugin::$session->errors,
2885 'log' => $log_data,
2886 'done' => false,
2887 'records_per_request' => $records_per_request,
2888 'iteration_execution_time' => $iteration_execution_time
2889 ));
2890 }
2891 }
2892 }
2893 }
2894 }
2895 // Move to the next file, set pointer to first element.
2896 if ( $ajax_processing ) {
2897 if (strpos($path, "pmxi_chunk_") !== false and @file_exists($path)) {
2898 wp_delete_file($path);
2899 }
2900 PMXI_Plugin::$session->set('pointer', 1);
2901 $pointer = 0;
2902 $lp = PMXI_Plugin::$session->local_paths;
2903 array_shift($lp);
2904 PMXI_Plugin::$session->set('local_paths', $lp);
2905 PMXI_Plugin::$session->save_data();
2906 }
2907 else break;
2908 }
2909 }
2910 }
2911
2912 // Delete missing records.
2913 if ( ( PMXI_Plugin::is_ajax() and empty(PMXI_Plugin::$session->local_paths) ) or ! $ajax_processing ) {
2914 ob_start();
2915 $is_all_records_deleted = $import->delete_missing_records($logger, $import->iteration);
2916 $log_data = ob_get_clean();
2917 if ($log_storage) {
2918 // phpcs:disable WordPress.WP.AlternativeFunctions.file_system_operations_fopen, WordPress.WP.AlternativeFunctions.file_system_operations_fwrite, WordPress.WP.AlternativeFunctions.file_system_operations_fclose
2919 $log = @fopen($log_file, 'a+');
2920 if ( is_resource( $log ) ) {
2921 @fwrite($log, $log_data);
2922 @fclose($log);
2923 }
2924 // phpcs:enable WordPress.WP.AlternativeFunctions.file_system_operations_fopen, WordPress.WP.AlternativeFunctions.file_system_operations_fwrite, WordPress.WP.AlternativeFunctions.file_system_operations_fclose
2925 }
2926 $iteration_execution_time = time() - $iteration_start_time;
2927 if ( $ajax_processing and ! $is_all_records_deleted ) {
2928 wp_send_json(array(
2929 'imported' => $import->imported,
2930 'created' => $import->created,
2931 'updated' => $import->updated,
2932 'skipped' => $import->skipped,
2933 'skipped_by_hash' => PMXI_Plugin::$session->skipped,
2934 'deleted' => $import->deleted,
2935 'changed_missing' => $import->changed_missing,
2936 'percentage' => 99,
2937 'warnings' => PMXI_Plugin::$session->warnings,
2938 'errors' => PMXI_Plugin::$session->errors,
2939 'log' => $log_data,
2940 'done' => false,
2941 'records_per_request' => $records_per_request,
2942 'iteration_execution_time' => $iteration_execution_time
2943 ));
2944 }
2945 }
2946
2947 if ( ( PMXI_Plugin::is_ajax() and empty(PMXI_Plugin::$session->local_paths) ) or ! $ajax_processing or ! empty($import->canceled) ) {
2948 $import->set(array(
2949 'processing' => 0, // unlock cron requests
2950 'triggered' => 0,
2951 'queue_chunk_number' => 0,
2952 'registered_on' => gmdate('Y-m-d H:i:s'),
2953 'iteration' => ++$import->iteration
2954 ))->update();
2955
2956 foreach ( get_taxonomies() as $tax ) {
2957 delete_option( "{$tax}_children" );
2958 _get_term_hierarchy( $tax );
2959 }
2960
2961 $import->set(array(
2962 'registered_on' => gmdate('Y-m-d H:i:s'),
2963 'executing' => 0
2964 ))->update();
2965
2966 wp_defer_term_counting(false);
2967 wp_defer_comment_counting(false);
2968
2969 // add history log
2970 $custom_type = wp_all_import_custom_type_labels($import->options['custom_type'], $import->options['taxonomy_type']);
2971 /* translators: see placeholders in the string below */
2972 $log_msg = sprintf(__("%1\$d %2\$s created %3\$d updated %4\$d skipped", "wp-all-import"), $import->created, ( ($import->created == 1) ? $custom_type->labels->singular_name : $custom_type->labels->name ), $import->updated, $import->skipped);
2973 if ($import->options['is_delete_missing']) {
2974 if (empty($import->options['delete_missing_action']) || $import->options['delete_missing_action'] != 'remove') {
2975 /* translators: see placeholders in the string below */
2976 $log_msg = sprintf(__("%1\$d %2\$s created %3\$d updated %4\$d changed missing %5\$d skipped", "wp-all-import"), $import->created, ( ($import->created == 1) ? $custom_type->labels->singular_name : $custom_type->labels->name ), $import->updated, $import->changed_missing, $import->skipped);
2977 } else {
2978 /* translators: see placeholders in the string below */
2979 $log_msg = sprintf(__("%1\$d %2\$s created %3\$d updated %4\$d deleted %5\$d skipped", "wp-all-import"), $import->created, ( ($import->created == 1) ? $custom_type->labels->singular_name : $custom_type->labels->name ), $import->updated, $import->deleted, $import->skipped);
2980 }
2981 }
2982
2983 $history_log->set(array(
2984 'time_run' => time() - strtotime($history_log->date),
2985 'summary' => $log_msg
2986 ))->update();
2987
2988 // clear import session
2989 PMXI_Plugin::$session->clean_session($import->id); // clear session data (prevent from reimporting the same data on page refresh)
2990
2991 // [indicate in header process is complete]
2992 $msg = esc_js( ( ! empty($import->canceled) ) ? __('Canceled', 'wp-all-import') : __('Complete', 'wp-all-import') );
2993
2994 if ( $ajax_processing ) ob_start();
2995
2996 do_action( 'pmxi_after_xml_import', $import->id, $import );
2997
2998 $import->delete_source( $logger );
2999 $import->options['is_import_specified'] and $logger and call_user_func($logger, 'Done');
3000
3001 ?>
3002 <script type="text/javascript">
3003 //<![CDATA[
3004 (function($){
3005 $('#status').html('<?php echo esc_js($msg); ?>');
3006 window.onbeforeunload = false;
3007 })(jQuery);
3008 //]]>
3009 </script>
3010 <?php
3011 // [/indicate in header process is complete]
3012
3013 if ( $ajax_processing ) {
3014
3015 wp_send_json(array(
3016 'imported' => $import->imported,
3017 'created' => $import->created,
3018 'updated' => $import->updated,
3019 'skipped' => $import->skipped,
3020 'skipped_by_hash' => PMXI_Plugin::$session->skipped,
3021 'deleted' => $import->deleted,
3022 'changed_missing' => $import->changed_missing,
3023 'percentage' => 100,
3024 'warnings' => PMXI_Plugin::$session->warnings,
3025 'errors' => PMXI_Plugin::$session->errors,
3026 'log' => ob_get_clean(),
3027 'done' => true,
3028 'records_per_request' => $import->options['records_per_request']
3029 ));
3030
3031 }
3032 }
3033 }
3034
3035 protected $_unique_key = array();
3036 protected function find_unique_key($el){
3037 if ($el->hasChildNodes()) {
3038 if ($el->childNodes->length) {
3039 foreach ($el->childNodes as $child) {
3040 if ($child instanceof DOMElement) {
3041 if (!in_array($child->nodeName, $this->_unique_key)) $this->_unique_key[] = $child->nodeName;
3042 $this->find_unique_key($child);
3043 }
3044 }
3045 }
3046 }
3047 }
3048
3049 protected function get_xml( $tagno = 0, $debug = false ){
3050 $xml = '';
3051 $update_previous = new PMXI_Import_Record();
3052
3053 if ($this->input->get('id')) {
3054 $update_previous->getById($this->input->get('id'));
3055 }
3056
3057 if ( ! empty(PMXI_Plugin::$session->update_previous) ) $update_previous->getById(PMXI_Plugin::$session->update_previous);
3058
3059 $local_paths = (empty(PMXI_Plugin::$session->local_paths)) ? array() : PMXI_Plugin::$session->local_paths;
3060
3061 if ( empty($local_paths) and ! $update_previous->isEmpty() ){
3062 $history_file = new PMXI_File_Record();
3063 $history_file->getBy( array('import_id' => $update_previous->id), 'id DESC' );
3064 $local_paths = ( ! $history_file->isEmpty() ) ? array(wp_all_import_get_absolute_path($history_file->path)) : array();
3065 }
3066
3067 if ( ! empty($local_paths)) {
3068
3069 $loop = 0;
3070
3071 foreach ( $local_paths as $key => $path ) {
3072
3073 if ( @file_exists($path) ){
3074
3075 $root_element = ( ! $update_previous->isEmpty() ) ? $update_previous->root_element : PMXI_Plugin::$session->source['root_element'];
3076
3077 $import_xpath = ( ! $update_previous->isEmpty() ) ? $update_previous->xpath : PMXI_Plugin::$session->xpath;
3078
3079 $encoding = PMXI_Plugin::$session->encoding ?? 'UTF-8';
3080
3081 $file = new PMXI_Chunk($path, array('element' => $root_element, 'encoding' => PMXI_Plugin::$session->encoding) );
3082
3083 while ($xml = $file->read()) {
3084
3085 if ( ! empty($xml) ) {
3086
3087 //PMXI_Import_Record::preprocessXml($xml);
3088 $xml = "<?xml version=\"1.0\" encoding=\"". $encoding ."\"?>" . "\n" . $xml;
3089
3090 if ( $import_xpath ){
3091 $dom = new DOMDocument( '1.0', $encoding );
3092 $old = libxml_use_internal_errors(true);
3093 $dom->loadXML($xml);
3094 libxml_use_internal_errors($old);
3095 $xpath = new DOMXPath($dom);
3096 if (($elements = $xpath->query($import_xpath)) and $elements->length){
3097 $this->data['dom'] = $dom;
3098 $loop++;
3099 if ( ! $tagno or $loop == $tagno ) break;
3100 }
3101 }
3102 else break;
3103 }
3104 }
3105 unset($file);
3106 }
3107 }
3108 }
3109 return $xml;
3110 }
3111
3112 /**
3113 * @return string
3114 */
3115 private function findUniqueKey()
3116 {
3117 $uniqueKey = '';
3118
3119 if (empty(PMXI_Plugin::$session->options['unique_key'])) {
3120
3121 $keys_black_list = array('programurl');
3122
3123 if ( empty(PMXI_Plugin::$session->deligate) ) {
3124 if (PMXI_Plugin::$session->options['custom_type'] == 'import_users') {
3125 $uniqueKey = PMXI_Plugin::$session->options['pmui']['login'];
3126 } elseif (PMXI_Plugin::$session->options['custom_type'] == 'shop_customer') {
3127 $uniqueKey = PMXI_Plugin::$session->options['pmsci_customer']['login'];
3128 } else {
3129 $uniqueKey = PMXI_Plugin::$session->options['title'];
3130 }
3131 }
3132
3133 // auto searching ID element
3134 if (!empty($this->data['dom']) and empty(PMXI_Plugin::$session->deligate)) {
3135 $dom = empty($this->data['dom']->documentElement) ? $this->data['dom'] : $this->data['dom']->documentElement;
3136 $this->find_unique_key($dom);
3137 if (!empty($this->_unique_key)) {
3138 foreach ($keys_black_list as $key => $value) {
3139 $uniqueKey = str_replace('{' . $value . '[1]}', "", $uniqueKey);
3140 }
3141 foreach ($this->_unique_key as $key) {
3142 if (stripos($key, 'id') !== false) {
3143 $uniqueKey .= ' - {' . $key . '[1]}';
3144 break;
3145 }
3146 }
3147 foreach ($this->_unique_key as $key) {
3148 if (stripos($key, 'url') !== false or stripos($key, 'sku') !== false or stripos($key, 'ref') !== false) {
3149 if (!in_array($key, $keys_black_list)) {
3150 $uniqueKey .= ' - {' . $key . '[1]}';
3151 break;
3152 }
3153 }
3154 }
3155 }
3156 $uniqueKey = apply_filters('pmxi_unique_key', $uniqueKey, PMXI_Plugin::$session->options);
3157 }
3158 } else {
3159 $uniqueKey = PMXI_Plugin::$session->options['unique_key'];
3160 }
3161
3162 return $uniqueKey;
3163 }
3164 }
3165