PluginProbe ʕ •ᴥ•ʔ
WP All Import – Drag & Drop Import for CSV, XML, Excel & Google Sheets / 4.1.0
WP All Import – Drag & Drop Import for CSV, XML, Excel & Google Sheets v4.1.0
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 / plugin.php
wp-all-import Last commit date
acc 3 weeks ago actions 3 weeks ago addon-api 3 weeks ago classes 3 weeks ago config 3 weeks ago controllers 3 weeks ago filters 3 weeks ago helpers 3 weeks ago i18n 3 weeks ago libraries 3 weeks ago models 3 weeks ago shortcodes 11 years ago src 3 weeks ago static 3 weeks ago vendor 3 months ago views 3 weeks ago banner-772x250.png 4 years ago composer.json 3 weeks ago composer.lock 3 weeks ago plugin.php 3 weeks ago readme.txt 3 weeks ago schema.php 3 weeks ago screenshot-1.png 11 years ago screenshot-2.png 11 years ago screenshot-3.png 11 years ago screenshot-4.png 11 years ago
plugin.php
1541 lines
1 <?php
2 if ( ! defined( 'ABSPATH' ) ) exit;
3 /*
4 Plugin Name: WP All Import
5 Plugin URI: https://www.wpallimport.com/wordpress-xml-csv-import/?utm_source=import-plugin-free&utm_medium=wp-plugins-page&utm_campaign=upgrade-to-pro
6 Description: The most powerful solution for importing XML and CSV files to WordPress. Create Posts and Pages with content from any XML or CSV file. A paid upgrade to WP All Import Pro is available for support and additional features.
7 Version: 4.1.0
8 Author: Soflyy
9 Requires PHP: 7.4
10 Text Domain: wp-all-import
11 Domain Path: /i18n/languages
12 License: GPLv3
13 License URI: https://www.gnu.org/licenses/gpl-3.0.html
14 */
15
16 /**
17 * Plugin root dir with forward slashes as directory separator regardless of actuall DIRECTORY_SEPARATOR value
18 * @var string
19 */
20 define('WP_ALL_IMPORT_ROOT_DIR', str_replace('\\', '/', dirname(__FILE__)));
21 /**
22 * Plugin root url for referencing static content
23 * @var string
24 */
25 define('WP_ALL_IMPORT_ROOT_URL', rtrim(plugin_dir_url(__FILE__), '/'));
26 /**
27 * Plugin prefix for making names unique (be aware that this variable is used in conjuction with naming convention,
28 * i.e. in order to change it one must not only modify this constant but also rename all constants, classes and functions which
29 * names composed using this prefix)
30 * @var string
31 */
32 define('WP_ALL_IMPORT_PREFIX', 'pmxi_');
33
34 define('PMXI_VERSION', '4.1.0');
35
36 define('PMXI_EDITION', 'free');
37
38 /**
39 * Plugin root uploads folder name
40 * @var string
41 */
42 define('WP_ALL_IMPORT_UPLOADS_BASE_DIRECTORY', 'wpallimport');
43 /**
44 * Plugin logs folder name
45 * @var string
46 */
47 define('WP_ALL_IMPORT_LOGS_DIRECTORY', WP_ALL_IMPORT_UPLOADS_BASE_DIRECTORY . DIRECTORY_SEPARATOR . 'logs');
48 /**
49 * Plugin files folder name
50 * @var string
51 */
52 define('WP_ALL_IMPORT_FILES_DIRECTORY', WP_ALL_IMPORT_UPLOADS_BASE_DIRECTORY . DIRECTORY_SEPARATOR . 'files');
53 /**
54 * Plugin uploads folder name
55 * @var string
56 */
57 define('WP_ALL_IMPORT_UPLOADS_DIRECTORY', WP_ALL_IMPORT_UPLOADS_BASE_DIRECTORY . DIRECTORY_SEPARATOR . 'uploads');
58 /**
59 * Plugin history folder name
60 * @var string
61 */
62 define('WP_ALL_IMPORT_HISTORY_DIRECTORY', WP_ALL_IMPORT_UPLOADS_BASE_DIRECTORY . DIRECTORY_SEPARATOR . 'history');
63 /**
64 * Plugin temp folder name
65 * @var string
66 */
67 define('WP_ALL_IMPORT_TEMP_DIRECTORY', WP_ALL_IMPORT_UPLOADS_BASE_DIRECTORY . DIRECTORY_SEPARATOR . 'temp');
68
69 /**
70 * Main plugin file, Introduces MVC pattern
71 *
72 * @singletone
73 * @author Maksym Tsypliakov <maksym.tsypliakov@gmail.com>
74 */
75 final class PMXI_Plugin {
76 /**
77 * Singletone instance
78 * @var PMXI_Plugin
79 */
80 protected static $instance;
81
82 /**
83 * Plugin options
84 * @var array
85 */
86 protected $options = array();
87
88 /**
89 * Plugin root dir
90 * @var string
91 */
92 const ROOT_DIR = WP_ALL_IMPORT_ROOT_DIR;
93 /**
94 * Plugin root URL
95 * @var string
96 */
97 const ROOT_URL = WP_ALL_IMPORT_ROOT_URL;
98 /**
99 * Prefix used for names of shortcodes, action handlers, filter functions etc.
100 * @var string
101 */
102 const PREFIX = WP_ALL_IMPORT_PREFIX;
103 /**
104 * Plugin file path
105 * @var string
106 */
107 const FILE = __FILE__;
108 /**
109 * Max allowed file size (bytes) to import in default mode
110 * @var int
111 */
112 const LARGE_SIZE = 0; // all files will importing in large import mode
113
114 public static $session = null;
115
116 public static $is_csv = false;
117
118 public static $csv_path = false;
119
120 /**
121 * @var bool
122 */
123 public static $is_php_allowed = true;
124
125 public static $capabilities = 'setup_network';
126
127 /**
128 * WP All Import logs folder
129 * @var string
130 */
131 const LOGS_DIRECTORY = WP_ALL_IMPORT_LOGS_DIRECTORY;
132 /**
133 * WP All Import files folder
134 * @var string
135 */
136 const FILES_DIRECTORY = WP_ALL_IMPORT_FILES_DIRECTORY;
137 /**
138 * WP All Import temp folder
139 * @var string
140 */
141 const TEMP_DIRECTORY = WP_ALL_IMPORT_TEMP_DIRECTORY;
142 /**
143 * WP All Import uploads folder
144 * @var string
145 */
146 const UPLOADS_DIRECTORY = WP_ALL_IMPORT_UPLOADS_DIRECTORY;
147
148 /**
149 * WP All Import history folder
150 * @var string
151 */
152 const HISTORY_DIRECTORY = WP_ALL_IMPORT_HISTORY_DIRECTORY;
153
154 /**
155 * Language domain key.
156 */
157 const LANGUAGE_DOMAIN = 'wp-all-import';
158
159 /**
160 * Return singletone instance
161 * @return PMXI_Plugin
162 */
163 static public function getInstance() {
164 if (self::$instance == NULL) {
165 self::$instance = new self();
166 }
167 return self::$instance;
168 }
169
170 static public function getEddName(){
171 return 'WP All Import';
172 }
173
174 /**
175 * Common logic for requestin plugin info fields
176 */
177 public function __call($method, $args) {
178 if (preg_match('%^get(.+)%i', $method, $mtch)) {
179 $info = get_plugin_data(self::FILE);
180 if (isset($info[$mtch[1]])) {
181 return $info[$mtch[1]];
182 }
183 }
184 throw new Exception(esc_html("Requested method " . get_class($this) . "::$method doesn't exist."));
185 }
186
187 /**
188 * Get path to plagin dir relative to wordpress root
189 * @param bool[optional] $noForwardSlash Whether path should be returned withot forwarding slash
190 * @return string
191 */
192 public function getRelativePath($noForwardSlash = false) {
193 $wp_root = str_replace('\\', '/', ABSPATH);
194 return ($noForwardSlash ? '' : '/') . str_replace($wp_root, '', self::ROOT_DIR);
195 }
196
197 /**
198 * Check whether plugin is activated as network one
199 * @return bool
200 */
201 public function isNetwork() {
202 if ( !is_multisite() )
203 return false;
204
205 $plugins = get_site_option('active_sitewide_plugins');
206 if (isset($plugins[plugin_basename(self::FILE)]))
207 return true;
208
209 return false;
210 }
211
212 /**
213 * Check whether permalinks is enabled
214 * @return bool
215 */
216 public function isPermalinks() {
217 global $wp_rewrite;
218
219 return $wp_rewrite->using_permalinks();
220 }
221
222 /**
223 * Return prefix for plugin database tables
224 * @return string
225 */
226 public function getTablePrefix() {
227 global $wpdb;
228
229 //return ($this->isNetwork() ? $wpdb->base_prefix : $wpdb->prefix) . self::PREFIX;
230 return $wpdb->prefix . self::PREFIX;
231 }
232
233 /**
234 * Return prefix for wordpress database tables
235 * @return string
236 */
237 public function getWPPrefix() {
238 global $wpdb;
239 return ($this->isNetwork()) ? $wpdb->base_prefix : $wpdb->prefix;
240 }
241
242 /**
243 * Class constructor containing dispatching logic
244 * @param string $rootDir Plugin root dir
245 * @param string $pluginFilePath Plugin main file
246 */
247 protected function __construct() {
248
249 if(defined('WPAI_WPAE_ALLOW_INSECURE_MULTISITE') && 1 === WPAI_WPAE_ALLOW_INSECURE_MULTISITE){
250 self::$capabilities = 'manage_options';
251 }
252
253 require_once self::ROOT_DIR . '/addon-api/autoload.php';
254
255 // register autoloading method
256 spl_autoload_register(array($this, 'autoload'));
257 require_once 'vendor/autoload.php';
258
259 // require acc code
260 if (is_dir(self::ROOT_DIR . '/acc')) foreach (PMXI_Helper::safe_glob(self::ROOT_DIR . '/acc/*.php', PMXI_Helper::GLOB_RECURSE | PMXI_Helper::GLOB_PATH) as $filePath) {
261 require_once $filePath;
262 }
263
264 // register helpers
265 if (is_dir(self::ROOT_DIR . '/helpers')) foreach (PMXI_Helper::safe_glob(self::ROOT_DIR . '/helpers/*.php', PMXI_Helper::GLOB_RECURSE | PMXI_Helper::GLOB_PATH) as $filePath) {
266 require_once $filePath;
267 }
268
269 // init plugin options
270 $option_name = get_class($this) . '_Options';
271 $options_default = PMXI_Config::createFromFile(self::ROOT_DIR . '/config/options.php')->toArray();
272 $current_options = get_option($option_name, array());
273 if (empty($current_options)) {
274 $current_options = array();
275 }
276
277 $this->options = array_intersect_key($current_options, $options_default) + $options_default;
278 $this->options = array_intersect_key($options_default, array_flip(array('info_api_url'))) + $this->options; // make sure hidden options apply upon plugin reactivation
279 if ('' == $this->options['cron_job_key']) {
280 $this->options['cron_job_key'] = wp_all_import_url_title(wp_all_import_rand_char(12));
281 }
282
283 if ($current_options !== $this->options) {
284 update_option($option_name, $this->options, false);
285 }
286
287 register_activation_hook(self::FILE, array($this, 'activation'));
288
289 // register action handlers
290 if (is_dir(self::ROOT_DIR . '/actions')) if (is_dir(self::ROOT_DIR . '/actions')) foreach (PMXI_Helper::safe_glob(self::ROOT_DIR . '/actions/*.php', PMXI_Helper::GLOB_RECURSE | PMXI_Helper::GLOB_PATH) as $filePath) {
291 require_once $filePath;
292 $function = $actionName = basename($filePath, '.php');
293 if (preg_match('%^(.+?)[_-](\d+)$%', $actionName, $m)) {
294 $actionName = $m[1];
295 $priority = intval($m[2]);
296 } else {
297 $priority = 10;
298 }
299 add_action($actionName, self::PREFIX . str_replace('-', '_', $function), $priority, 99); // since we don't know at this point how many parameters each plugin expects, we make sure they will be provided with all of them (it's unlikely any developer will specify more than 99 parameters in a function)
300 }
301
302 // register filter handlers
303 if (is_dir(self::ROOT_DIR . '/filters')) foreach (PMXI_Helper::safe_glob(self::ROOT_DIR . '/filters/*.php', PMXI_Helper::GLOB_RECURSE | PMXI_Helper::GLOB_PATH) as $filePath) {
304 require_once $filePath;
305 $function = $actionName = basename($filePath, '.php');
306 if (preg_match('%^(.+?)[_-](\d+)$%', $actionName, $m)) {
307 $actionName = $m[1];
308 $priority = intval($m[2]);
309 } else {
310 $priority = 10;
311 }
312 add_filter($actionName, self::PREFIX . str_replace('-', '_', $function), $priority, 99); // since we don't know at this point how many parameters each plugin expects, we make sure they will be provided with all of them (it's unlikely any developer will specify more than 99 parameters in a function)
313 }
314
315 // register admin page pre-dispatcher
316 add_action('admin_init', array($this, 'adminInit'));
317 add_action('admin_init', array($this, 'fix_options'));
318 add_action('init', array($this, 'init'));
319
320 add_action( 'plugins_loaded', array( $this, 'setup_allimport_dir' ) );
321
322 }
323
324 /**
325 * Determines is process running from cli.
326 *
327 * @return bool
328 */
329 public function isCli() {
330 return php_sapi_name() === 'cli';
331 }
332
333 /**
334 * Setup required directory.
335 */
336 public function setup_allimport_dir() {
337 // create history folder
338 $uploads = wp_upload_dir();
339
340 $wpallimportDirs = array( WP_ALL_IMPORT_UPLOADS_BASE_DIRECTORY, self::LOGS_DIRECTORY, self::FILES_DIRECTORY, self::TEMP_DIRECTORY, self::UPLOADS_DIRECTORY, self::HISTORY_DIRECTORY);
341
342 foreach ($wpallimportDirs as $destination) {
343
344 $dir = $uploads['basedir'] . DIRECTORY_SEPARATOR . $destination;
345
346 if ( !is_dir($dir)) wp_mkdir_p($dir);
347
348 if ( ! @file_exists($dir . DIRECTORY_SEPARATOR . 'index.php') ) @touch( $dir . DIRECTORY_SEPARATOR . 'index.php' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_touch
349
350 }
351 }
352
353 public function init(){
354 $this->load_plugin_textdomain();
355 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
356 self::$is_php_allowed = apply_filters('wp_all_import_is_php_allowed', self::wpai_determine_php_allowed());
357 }
358
359 /**
360 * Determine if PHP execution should be allowed based on WordPress security constants.
361 *
362 * Only restricts PHP execution when BOTH DISALLOW_FILE_EDIT and DISALLOW_FILE_MODS are set to true.
363 * This is the only configuration that creates a true security boundary where the plugin would
364 * grant NEW code execution capabilities that don't already exist.
365 *
366 * @return bool True if PHP execution is allowed, false otherwise.
367 */
368 public static function wpai_determine_php_allowed() {
369 // Only restrict if BOTH security constants are set
370 // This is the only configuration that creates a true security boundary
371 if (defined('DISALLOW_FILE_EDIT') && DISALLOW_FILE_EDIT &&
372 defined('DISALLOW_FILE_MODS') && DISALLOW_FILE_MODS) {
373 return false;
374 }
375
376 // Otherwise allow - user has other code execution vectors anyway
377 // - If only DISALLOW_FILE_EDIT is set: admin can still install plugins (file manager, code snippets, etc.)
378 // - If only DISALLOW_FILE_MODS is set: admin can still edit theme/plugin files directly
379 // - If neither is set: admin has full code execution capabilities
380 return true;
381 }
382
383 public function plugin_row_meta($links, $file)
384 {
385 if ( $file == plugin_basename( __FILE__ ) ) {
386 $row_meta = array(
387 'pro' => '<a href="http://www.wpallimport.com" target="_blank" title="' . esc_attr( __( 'WP All Import Pro Version', 'wp-all-import' ) ) . '">' . __( 'Pro Version', 'wp-all-import' ) . '</a>',
388 );
389
390 return array_merge( $links, $row_meta );
391 }
392
393 return (array) $links;
394 }
395
396
397 /**
398 * convert imports options
399 * compatibility with version 3.2.3
400 */
401 public function fix_options(){
402
403 global $wpdb;
404
405 $imports = new PMXI_Import_List();
406 $post = new PMXI_Post_Record();
407
408 $templates = new PMXI_Template_List();
409 $template = new PMXI_Template_Record();
410
411 $is_migrated = get_option('pmxi_is_migrated');
412
413 $uploads = wp_upload_dir();
414
415 if ( empty($is_migrated) or version_compare($is_migrated, PMXI_VERSION) < 0 ){ //PMXI_VERSION
416
417 $commit_migration = true;
418
419 if ( empty($is_migrated) ){ // plugin version less than 4.0.0
420
421 if ( is_dir($uploads['basedir'] . '/wpallimport_history') ) {
422 wp_all_import_rmdir($uploads['basedir'] . '/wpallimport_history');
423 }
424 if ( is_dir($uploads['basedir'] . '/wpallimport_logs') ) {
425 wp_all_import_rmdir($uploads['basedir'] . '/wpallimport_logs');
426 }
427
428 foreach ($imports->setColumns($imports->getTable() . '.*')->getBy(array('id !=' => ''))->convertRecords() as $imp){
429
430 $imp->getById($imp->id);
431
432 if ( ! $imp->isEmpty() and ! empty($imp->template)){
433
434 $options = array_merge($imp->options, $imp->template);
435
436 $this->ver_4_transition_fix($options);
437
438 $imp->set(array(
439 'options' => $options
440 ))->update();
441
442 if ($imp->type == 'file'){
443 $imp->set(array(
444 'path' => $uploads['basedir'] . DIRECTORY_SEPARATOR . self::FILES_DIRECTORY . DIRECTORY_SEPARATOR . basename($imp->path)
445 ))->update();
446 }
447 }
448 }
449
450 foreach ($templates->setColumns($templates->getTable() . '.*')->getBy(array('id !=' => ''))->convertRecords() as $tpl){
451
452 $tpl->getById($tpl->id);
453
454 if ( ! $tpl->isEmpty() and ! empty($tpl->title) ) {
455
456 $opt = ( empty($tpl->options) ) ? array() : $tpl->options;
457
458 $options = array_merge($opt, array(
459 'title' => $tpl->title,
460 'content' => $tpl->content,
461 'is_keep_linebreaks' => $tpl->is_keep_linebreaks,
462 'is_leave_html' => $tpl->is_leave_html,
463 'fix_characters' => $tpl->fix_characters
464 ));
465
466 $this->ver_4_transition_fix($options);
467
468 $tpl->set(array(
469 'options' => $options
470 ))->update();
471
472 }
473
474 }
475
476 $commit_migration = $this->fix_db_schema(); // feature to version 4.0.0
477
478 }
479 else {
480
481 $commit_migration = $this->fix_db_schema();
482
483 foreach ($imports->setColumns($imports->getTable() . '.*')->getBy(array('id !=' => ''))->convertRecords() as $imp){
484
485 $imp->getById($imp->id);
486
487 if ( ! $imp->isEmpty() ){
488
489 $options = $imp->options;
490
491 $this->ver_4x_transition_fix($options, $is_migrated);
492
493 $imp->set(array(
494 'options' => $options
495 ))->update();
496 }
497 }
498
499 foreach ($templates->setColumns($templates->getTable() . '.*')->getBy(array('id !=' => ''))->convertRecords() as $tpl){
500
501 $tpl->getById($tpl->id);
502
503 if ( ! $tpl->isEmpty() ) {
504
505 $options = ( empty($tpl->options) ) ? array() : $tpl->options;
506
507 $this->ver_4x_transition_fix($options, $is_migrated);
508
509 $tpl->set(array(
510 'options' => $options
511 ))->update();
512
513 }
514
515 }
516 }
517 if ($commit_migration) update_option('pmxi_is_migrated', PMXI_VERSION);
518 }
519 }
520
521 public function ver_4_transition_fix( &$options ){
522
523 $options['wizard_type'] = ($options['duplicate_matching'] == 'auto') ? 'new' : 'matching';
524
525 if ($options['download_images']){
526 $options['download_images'] = 'yes';
527 $options['download_featured_image'] = $options['featured_image'];
528 $options['featured_image'] = '';
529 $options['download_featured_delim'] = $options['featured_delim'];
530 $options['featured_delim'] = '';
531 }
532
533 if ($options['set_image_meta_data']){
534 $options['set_image_meta_title'] = 1;
535 $options['set_image_meta_caption'] = 1;
536 $options['set_image_meta_alt'] = 1;
537 $options['set_image_meta_description'] = 1;
538 }
539
540 if ("" == $options['custom_type']) $options['custom_type'] = $options['type'];
541
542 $exclude_taxonomies = (class_exists('PMWI_Plugin')) ? array('post_format', 'product_type') : array('post_format');
543 $post_taxonomies = array_diff_key(get_taxonomies_by_object_type(array($options['custom_type']), 'object'), array_flip($exclude_taxonomies));
544
545 $options['tax_logic'] = array();
546 $options['tax_assing'] = array();
547 $options['tax_multiple_xpath'] = array();
548 $options['tax_multiple_delim'] = array();
549 $options['tax_hierarchical_logic_entire'] = array();
550 $options['tax_hierarchical_logic_manual'] = array();
551
552 if ( ! empty($post_taxonomies)):
553 foreach ($post_taxonomies as $ctx):
554
555 $options['tax_logic'][$ctx->name] = ($ctx->hierarchical) ? 'hierarchical' : 'multiple';
556
557 if ($ctx->name == 'category'){
558 $options['post_taxonomies']['category'] = $options['categories'];
559 }
560 elseif ($ctx->name == 'post_tag' ){
561 $options['tax_assing']['post_tag'] = 1;
562 $options['tax_multiple_xpath']['post_tag'] = $options['tags'];
563 $options['tax_multiple_delim']['post_tag'] = $options['tags_delim'];
564 }
565
566 if ( ! empty($options['post_taxonomies'][$ctx->name])){
567
568 $taxonomies_hierarchy = json_decode($options['post_taxonomies'][$ctx->name], true);
569 $options['tax_assing'][$ctx->name] = (!empty($taxonomies_hierarchy[0]['assign'])) ? 1 : 0;
570
571 if ($options['tax_logic'][$ctx->name] == 'multiple') {
572 $options['tax_multiple_xpath'][$ctx->name] = (!empty($taxonomies_hierarchy[0]['xpath'])) ? $taxonomies_hierarchy[0]['xpath'] : '';
573 $options['tax_multiple_delim'][$ctx->name] = (!empty($taxonomies_hierarchy[0]['delim'])) ? $taxonomies_hierarchy[0]['delim'] : '';
574 }
575 else{
576 $options['tax_hierarchical_logic_manual'][$ctx->name] = 1;
577 }
578 }
579
580 endforeach;
581 endif;
582 }
583
584 public function ver_4x_transition_fix(&$options, $version){
585 if ( version_compare($version, '3.7.2') < 0 ) {
586 $options['delete_missing_logic'] = 'import';
587 $options['is_send_removed_to_trash'] = 0;
588 $options['status_of_removed_products'] = 'outofstock';
589 if (empty($options['is_delete_missing']) || (!empty($options['is_update_missing_cf']) || !empty($options['set_missing_to_draft']) || !empty($options['missing_records_stock_status']))) {
590 $options['delete_missing_action'] = 'keep';
591 if ($options['set_missing_to_draft']) {
592 $options['is_change_post_status_of_removed'] = 1;
593 $options['status_of_removed'] = 'draft';
594 }
595 } else {
596 $options['delete_missing_action'] = 'remove';
597 }
598 // Set default option if no other options selected.
599 if (empty($options['is_update_missing_cf']) && empty($options['is_change_post_status_of_removed']) && empty($options['missing_records_stock_status'])) {
600 $options['is_send_removed_to_trash'] = 1;
601 }
602 $options['is_delete_attachments'] = !$options['is_keep_attachments'];
603 $options['is_delete_imgs'] = !$options['is_keep_imgs'];
604 }
605 }
606
607 /**
608 * pre-dispatching logic for admin page controllers
609 */
610 public function adminInit() {
611
612 self::$session = new PMXI_Handler();
613
614 $input = new PMXI_Input();
615 $page = strtolower($input->getpost('page', ''));
616
617 if (preg_match('%^' . preg_quote(str_replace('_', '-', self::PREFIX), '%') . '([\w-]+)$%', $page)) {
618 //$this->adminDispatcher($page, strtolower($input->getpost('action', 'index')));
619
620 $action = strtolower($input->getpost('action', 'index'));
621
622 // capitalize prefix and first letters of class name parts
623 $controllerName = preg_replace_callback('%(^' . preg_quote(self::PREFIX, '%') . '|_).%', array($this, "replace_callback"),str_replace('-', '_', $page));
624 $actionName = str_replace('-', '_', $action);
625 if (method_exists($controllerName, $actionName)) {
626
627 @ini_set("max_input_time", PMXI_Plugin::getInstance()->getOption('max_input_time')); // phpcs:ignore Squiz.PHP.DiscouragedFunctions.Discouraged
628 @ini_set("max_execution_time", str_replace('-1','0',PMXI_Plugin::getInstance()->getOption('max_execution_time'))); // phpcs:ignore Squiz.PHP.DiscouragedFunctions.Discouraged
629
630 if ( ! get_current_user_id() or ! current_user_can( self::$capabilities )) {
631 // This nonce is not valid.
632 die( 'Security check' );
633
634 } else {
635
636 $this->_admin_current_screen = (object)array(
637 'id' => $controllerName,
638 'base' => $controllerName,
639 'action' => $actionName,
640 'is_ajax' => strpos(sanitize_text_field(wp_unslash($_SERVER["HTTP_ACCEPT"] ?? '')), 'json') !== false,
641 'is_network' => is_network_admin(),
642 'is_user' => is_user_admin(),
643 );
644 add_filter('current_screen', array($this, 'getAdminCurrentScreen'));
645 add_filter('admin_body_class', array($this, 'getAdminBodyClass'), 10, 1);
646
647 $controller = new $controllerName();
648 if ( ! $controller instanceof PMXI_Controller_Admin) {
649 throw new Exception(esc_html("Administration page `$page` matches to a wrong controller type."));
650 }
651
652 $reviewsUI = new \Wpai\Reviews\ReviewsUI();
653 add_action('admin_notices', [$reviewsUI, 'render']);
654
655 if ($this->_admin_current_screen->is_ajax) { // ajax request
656 $controller->$action();
657 do_action('pmxi_action_after');
658 wp_die(); // stop processing since we want to output only what controller is randered, nothing in addition
659 } elseif ( ! $controller->isInline) {
660 @ob_start();
661 $controller->$action();
662 self::$buffer = @ob_get_clean();
663 } else {
664 self::$buffer_callback = array($controller, $action);
665 }
666
667 }
668
669 } else { // redirect to dashboard if requested page and/or action don't exist
670 // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect
671 wp_redirect(admin_url()); die();
672 }
673 }
674 }
675
676 public function getAdminBodyClass($class){
677 return $class . ' wpallimport-plugin';
678 }
679
680 /**
681 * Dispatch shorttag: create corresponding controller instance and call its index method
682 * @param array $args Shortcode tag attributes
683 * @param string $content Shortcode tag content
684 * @param string $tag Shortcode tag name which is being dispatched
685 * @return string
686 */
687 public function shortcodeDispatcher($args, $content, $tag) {
688
689 $controllerName = self::PREFIX . preg_replace_callback('%(^|_).%', array($this, "replace_callback"), $tag);// capitalize first letters of class name parts and add prefix
690 $controller = new $controllerName();
691 if ( ! $controller instanceof PMXI_Controller) {
692 throw new Exception(esc_html("Shortcode `$tag` matches to a wrong controller type."));
693 }
694 ob_start();
695 $controller->index($args, $content);
696 return ob_get_clean();
697 }
698
699 static $buffer = NULL;
700 static $buffer_callback = NULL;
701
702 /**
703 * Dispatch admin page: call corresponding controller based on get parameter `page`
704 * The method is called twice: 1st time as handler `parse_header` action and then as admin menu item handler
705 * @param string[optional] $page When $page set to empty string ealier buffered content is outputted, otherwise controller is called based on $page value
706 */
707 public function adminDispatcher($page = '', $action = 'index') {
708
709 if ('' === $page) {
710 if ( ! is_null(self::$buffer)) {
711 echo '<div class="wrap">';
712
713 echo self::$buffer; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Buffer contains pre-rendered HTML from the controller.
714
715 do_action('pmxi_action_after');
716 echo '</div>';
717 } elseif ( ! is_null(self::$buffer_callback)) {
718 echo '<div class="wrap">';
719 call_user_func(self::$buffer_callback);
720 do_action('pmxi_action_after');
721 echo '</div>';
722 } else {
723 throw new Exception(esc_html('There is no previousely buffered content to display.'));
724 }
725 }
726
727 }
728
729 public function replace_callback($matches){
730 return strtoupper($matches[0]);
731 }
732
733 protected $_admin_current_screen = NULL;
734 public function getAdminCurrentScreen()
735 {
736 return $this->_admin_current_screen;
737 }
738
739 /**
740 * Autoloader
741 * It's assumed class name consists of prefix folloed by its name which in turn corresponds to location of source file
742 * if `_` symbols replaced by directory path separator. File name consists of prefix folloed by last part in class name (i.e.
743 * symbols after last `_` in class name)
744 * When class has prefix it's source is looked in `models`, `controllers`, `shortcodes` folders, otherwise it looked in `core` or `library` folder
745 *
746 * @param string $className
747 * @return bool
748 */
749 public function autoload($className) {
750 $is_prefix = false;
751 $filePath = str_replace('_', '/', preg_replace('%^' . preg_quote(self::PREFIX, '%') . '%', '', strtolower($className), 1, $is_prefix)) . '.php';
752 if ( ! $is_prefix) { // also check file with original letter case
753 $filePathAlt = $className . '.php';
754 }
755 foreach ($is_prefix ? array('models', 'controllers', 'shortcodes', 'classes') : array('libraries') as $subdir) {
756 $path = self::ROOT_DIR . '/' . $subdir . '/' . $filePath;
757 if (is_file($path)) {
758 require $path;
759 return TRUE;
760 }
761 if ( ! $is_prefix) {
762 $pathAlt = self::ROOT_DIR . '/' . $subdir . '/' . $filePathAlt;
763 if (is_file($pathAlt)) {
764 require $pathAlt;
765 return TRUE;
766 }
767 }
768 }
769
770 if(strpos($className, '\\') !== false){
771
772 // project-specific namespace prefix
773 $prefix = 'Wpai\\';
774
775 // base directory for the namespace prefix
776 $base_dir = self::ROOT_DIR . '/src/';
777
778 // does the class use the namespace prefix?
779 $len = strlen($prefix);
780 if (strncmp($prefix, $className, $len) !== 0) {
781 // no, move to the next registered autoloader
782 return false;
783 }
784
785 // get the relative class name
786 $relative_class = substr($className, $len);
787
788 // replace the namespace prefix with the base directory, replace namespace
789 // separators with directory separators in the relative class name, append
790 // with .php
791 $file = $base_dir . str_replace('\\', '/', $relative_class) . '.php';
792
793 // if the file exists, require it
794 if (file_exists($file)) {
795 require_once $file;
796 return true;
797 }
798 }
799
800 return FALSE;
801 }
802
803 /**
804 * Get plugin option
805 * @param string[optional] $option Parameter to return, all array of options is returned if not set
806 * @return mixed
807 */
808 public function getOption($option = NULL) {
809 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
810 $options = apply_filters('wp_all_import_config_options', $this->options);
811 if (is_null($option)) {
812 return $options;
813 } else if (isset($options[$option])) {
814 return $options[$option];
815 } else {
816 throw new Exception(esc_html("Specified option is not defined for the plugin"));
817 }
818 }
819 /**
820 * Update plugin option value
821 * @param string $option Parameter name or array of name => value pairs
822 * @param mixed[optional] $value New value for the option, if not set than 1st parameter is supposed to be array of name => value pairs
823 * @return array
824 */
825 public function updateOption($option, $value = NULL) {
826 is_null($value) or $option = array($option => $value);
827 if (array_diff_key($option, $this->options)) {
828 throw new Exception(esc_html("Specified option is not defined for the plugin"));
829 }
830 $this->options = $option + $this->options;
831 update_option(get_class($this) . '_Options', $this->options);
832
833 return $this->options;
834 }
835
836 /**
837 * Plugin activation logic
838 */
839 public function activation() {
840 // uncaught exception doesn't prevent plugin from being activated, therefore replace it with fatal error so it does
841 $exception_handler = function($e){
842 trigger_error(esc_html($e->getMessage()), E_USER_ERROR); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error -- Fatal error raised from activation exception handler.
843 };
844 set_exception_handler($exception_handler);
845
846 // create plugin options
847 $option_name = get_class($this) . '_Options';
848 $options_default = PMXI_Config::createFromFile(self::ROOT_DIR . '/config/options.php')->toArray();
849 $wpai_options = get_option($option_name, false);
850 if ( ! $wpai_options ) update_option($option_name, $options_default);
851
852 // create/update required database tables
853 require_once ABSPATH . 'wp-admin/includes/upgrade.php';
854 require self::ROOT_DIR . '/schema.php';
855 global $wpdb;
856
857 if (function_exists('is_multisite') && is_multisite()) {
858 // check if it is a network activation - if so, run the activation function for each blog id
859 if (isset($_GET['networkwide']) && (intval($_GET['networkwide']) == 1)) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
860 $old_blog = $wpdb->blogid;
861 // Get all blog ids
862 $blogids = $wpdb->get_col("SELECT blog_id FROM $wpdb->blogs"); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared
863 foreach ($blogids as $blog_id) {
864 switch_to_blog($blog_id);
865 require self::ROOT_DIR . '/schema.php';
866 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
867 dbDelta($plugin_queries);
868
869 // sync data between plugin tables and wordpress (mostly for the case when plugin is reactivated)
870
871 //$post = new PMXI_Post_Record();
872 //$wpdb->query('DELETE FROM ' . $post->getTable() . ' WHERE post_id NOT IN (SELECT ID FROM ' . $wpdb->posts . ')');
873 $post = new PMXI_Post_Record();
874 $import = new PMXI_Import_Record();
875 $imports_list = $wpdb->get_results('SELECT id FROM ' . $import->getTable() . ''); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,PluginCheck.Security.DirectDB.UnescapedDBParameter
876
877 if ( ! empty($imports_list) ) {
878
879 $user_imports = array();
880 $post_imports = array();
881
882 foreach ($imports_list as $import_entry) {
883 $import_id = $import_entry->id;
884 $import = $import->getById($import_id);
885 $import_options = \pmxi_maybe_unserialize($import->options);
886 $import_type = $import_options['custom_type'];
887 if ( in_array($import_type, array('import_users', 'shop_customer')) ) {
888 $user_imports[] = $import_id;
889 } else {
890 $post_imports[] = $import_id;
891 }
892 }
893
894 if ( ! empty($user_imports) ) {
895 $user_table = $wpdb->base_prefix . 'users';
896 $user_imports = array_map('intval', $user_imports);
897 $user_query = 'DELETE FROM ' . $post->getTable() . ' WHERE import_id IN (' . implode(',', $user_imports) . ') AND post_id NOT IN (SELECT ID FROM ' . $user_table . ')';
898 $wpdb->query($user_query); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,PluginCheck.Security.DirectDB.UnescapedDBParameter
899 }
900
901 if ( ! empty($post_imports) ) {
902 $post_imports = array_map('intval', $post_imports);
903 $post_query = 'DELETE FROM ' . $post->getTable() . ' WHERE import_id IN (' . implode(',', $post_imports) . ') AND post_id NOT IN (SELECT ID FROM ' . $wpdb->posts . ')';
904 $wpdb->query($post_query); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,PluginCheck.Security.DirectDB.UnescapedDBParameter
905 }
906
907 }
908
909 }
910 switch_to_blog($old_blog);
911 return;
912 }
913 }
914
915 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
916 dbDelta($plugin_queries);
917
918 // sync data between plugin tables and wordpress (mostly for the case when plugin is reactivated)
919
920 //$post = new PMXI_Post_Record();
921 //$wpdb->query('DELETE FROM ' . $post->getTable() . ' WHERE post_id NOT IN (SELECT ID FROM ' . $wpdb->posts . ')');
922
923 $post = new PMXI_Post_Record();
924 $import = new PMXI_Import_Record();
925 $imports_list = $wpdb->get_results('SELECT id FROM ' . $import->getTable() . ''); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,PluginCheck.Security.DirectDB.UnescapedDBParameter
926
927 if ( ! empty($imports_list) ) {
928
929 $user_imports = array();
930 $post_imports = array();
931
932 foreach ($imports_list as $import_entry) {
933 $import_id = $import_entry->id;
934 $import = $import->getById($import_id);
935 $import_options = \pmxi_maybe_unserialize($import->options);
936 $import_type = $import_options['custom_type'];
937 if ( in_array($import_type, array('import_users', 'shop_customer')) ) {
938 $user_imports[] = $import_id;
939 } else {
940 $post_imports[] = $import_id;
941 }
942 }
943
944 if ( ! empty($user_imports) ) {
945 $user_table = $wpdb->base_prefix . 'users';
946 $user_imports = array_map('intval', $user_imports);
947 $user_query = 'DELETE FROM ' . $post->getTable() . ' WHERE import_id IN (' . implode(',', $user_imports) . ') AND post_id NOT IN (SELECT ID FROM ' . $user_table . ')';
948 $wpdb->query($user_query); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,PluginCheck.Security.DirectDB.UnescapedDBParameter
949 }
950
951 if ( ! empty($post_imports) ) {
952 $post_imports = array_map('intval', $post_imports);
953 $post_query = 'DELETE FROM ' . $post->getTable() . ' WHERE import_id IN (' . implode(',', $post_imports) . ') AND post_id NOT IN (SELECT ID FROM ' . $wpdb->posts . ')';
954 $wpdb->query($post_query); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,PluginCheck.Security.DirectDB.UnescapedDBParameter
955 }
956
957 }
958
959 }
960
961 /**
962 * Load Localisation files.
963 *
964 * Note: the first-loaded translation file overrides any following ones if the same translation is present
965 *
966 * @access public
967 * @return void
968 */
969 public function load_plugin_textdomain() {
970 load_plugin_textdomain( 'wp-all-import', false, dirname( plugin_basename( __FILE__ ) ) . '/i18n/languages' );
971 }
972
973 public function fix_db_schema(){
974
975 $uploads = wp_upload_dir();
976
977 if ( ! is_dir($uploads['basedir'] . DIRECTORY_SEPARATOR . self::LOGS_DIRECTORY) or ! is_writable($uploads['basedir'] . DIRECTORY_SEPARATOR . self::LOGS_DIRECTORY)) { // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_is_writable
978 /* translators: %s: logs folder path */
979 die(sprintf(esc_html__('Uploads folder %s must be writable', 'wp-all-import'), esc_attr($uploads['basedir'] . DIRECTORY_SEPARATOR . self::LOGS_DIRECTORY)));
980 }
981
982 if ( ! is_dir($uploads['basedir'] . DIRECTORY_SEPARATOR . WP_ALL_IMPORT_UPLOADS_BASE_DIRECTORY) or ! is_writable($uploads['basedir'] . DIRECTORY_SEPARATOR . WP_ALL_IMPORT_UPLOADS_BASE_DIRECTORY)) { // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_is_writable
983 /* translators: %s: uploads base folder path */
984 die(sprintf(esc_html__('Uploads folder %s must be writable', 'wp-all-import'), esc_attr($uploads['basedir'] . DIRECTORY_SEPARATOR . WP_ALL_IMPORT_UPLOADS_BASE_DIRECTORY)));
985 }
986
987 // create/update required database tables
988 require_once ABSPATH . 'wp-admin/includes/upgrade.php';
989 require self::ROOT_DIR . '/schema.php';
990 global $wpdb;
991
992 if (function_exists('is_multisite') && is_multisite()) {
993 // check if it is a network activation - if so, run the activation function for each blog id
994 if (isset($_GET['networkwide']) && (intval($_GET['networkwide']) == 1)) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
995 $old_blog = $wpdb->blogid;
996 // Get all blog ids
997 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
998 $blogids = $wpdb->get_col("SELECT blog_id FROM $wpdb->blogs");
999 foreach ($blogids as $blog_id) {
1000 switch_to_blog($blog_id);
1001 require self::ROOT_DIR . '/schema.php';
1002 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
1003 dbDelta($plugin_queries);
1004
1005 // sync data between plugin tables and wordpress (mostly for the case when plugin is reactivated)
1006
1007 $post = new PMXI_Post_Record();
1008 $wpdb->query('DELETE FROM ' . $post->getTable() . ' WHERE post_id NOT IN (SELECT ID FROM ' . $wpdb->posts .') AND post_id NOT IN ( SELECT ID FROM ' . $wpdb->users . ') AND post_id NOT IN ( SELECT term_taxonomy_id FROM ' . $wpdb->term_taxonomy . ')'); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,PluginCheck.Security.DirectDB.UnescapedDBParameter
1009 }
1010 switch_to_blog($old_blog);
1011 return;
1012 }
1013 }
1014
1015 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
1016 dbDelta($plugin_queries);
1017
1018 // do not execute ALTER TABLE queries if sql user doesn't have ALTER privileges
1019 $grands = $wpdb->get_results("SELECT * FROM information_schema.user_privileges WHERE grantee LIKE \"'" . DB_USER . "'%\" AND PRIVILEGE_TYPE = 'ALTER' AND IS_GRANTABLE = 'YES';"); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
1020
1021 $table = $table = $this->getTablePrefix() . 'files';
1022
1023 $tablefields = $wpdb->get_results("DESCRIBE {$table};"); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.SchemaChange
1024 // For every field in the table
1025 foreach ($tablefields as $tablefield) {
1026 if ('contents' == $tablefield->Field) {
1027 $list = new PMXI_File_List();
1028 for ($i = 1; $list->getBy(NULL, 'id', $i, 1)->count(); $i++) {
1029 foreach ($list->convertRecords() as $file) {
1030 $file->save(); // resave file for file to be stored in uploads folder
1031 }
1032 }
1033
1034 if (!empty($grands)) $wpdb->query("ALTER TABLE {$table} DROP " . $tablefield->Field); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.SchemaChange
1035
1036 break;
1037 }
1038 }
1039
1040 // alter images table
1041 $table = $this->getTablePrefix() . 'images';
1042 $tablefields = $wpdb->get_results("DESCRIBE {$table};"); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.SchemaChange
1043 $fields_to_alter = array(
1044 'image_url',
1045 'image_filename'
1046 );
1047
1048 // Check if field exists
1049 foreach ($tablefields as $tablefield) {
1050 if (in_array($tablefield->Field, $fields_to_alter)){
1051 $fields_to_alter = array_diff($fields_to_alter, array($tablefield->Field));
1052 }
1053 }
1054
1055 if ( ! empty($fields_to_alter) ){
1056
1057 if (empty($grands)) return false;
1058
1059 foreach ($fields_to_alter as $field) {
1060 switch ($field) {
1061 case 'image_url':
1062 $wpdb->query("ALTER TABLE {$table} ADD `image_url` TEXT;"); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.SchemaChange
1063 break;
1064 case 'image_filename':
1065 $wpdb->query("ALTER TABLE {$table} ADD `image_filename` TEXT;"); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.SchemaChange
1066 break;
1067 default:
1068 # code...
1069 break;
1070 }
1071 }
1072 }
1073
1074 $table = $this->getTablePrefix() . 'imports';
1075 $tablefields = $wpdb->get_results("DESCRIBE {$table};"); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.SchemaChange
1076 $fields_to_alter = array(
1077 'parent_import_id',
1078 'iteration',
1079 'deleted',
1080 'changed_missing',
1081 'executing',
1082 'canceled',
1083 'canceled_on',
1084 'failed',
1085 'failed_on',
1086 'settings_update_on',
1087 'last_activity'
1088 );
1089
1090 // Check if field exists
1091 foreach ($tablefields as $tablefield) {
1092 if (in_array($tablefield->Field, $fields_to_alter)){
1093 $fields_to_alter = array_diff($fields_to_alter, array($tablefield->Field));
1094 }
1095 }
1096
1097 if ( ! empty($fields_to_alter) ){
1098
1099 if (empty($grands)) return false;
1100
1101 // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.SchemaChange
1102 foreach ($fields_to_alter as $field) {
1103 switch ($field) {
1104 case 'parent_import_id':
1105 $wpdb->query("ALTER TABLE {$table} ADD `parent_import_id` BIGINT(20) NOT NULL DEFAULT 0;");
1106 break;
1107 case 'iteration':
1108 $wpdb->query("ALTER TABLE {$table} ADD `iteration` BIGINT(20) NOT NULL DEFAULT 0;");
1109 break;
1110 case 'deleted':
1111 $wpdb->query("ALTER TABLE {$table} ADD `deleted` BIGINT(20) NOT NULL DEFAULT 0;");
1112 break;
1113 case 'changed_missing':
1114 $wpdb->query("ALTER TABLE {$table} ADD `changed_missing` BIGINT(20) NOT NULL DEFAULT 0;");
1115 break;
1116 case 'executing':
1117 $wpdb->query("ALTER TABLE {$table} ADD `executing` BOOL NOT NULL DEFAULT 0;");
1118 break;
1119 case 'canceled':
1120 $wpdb->query("ALTER TABLE {$table} ADD `canceled` BOOL NOT NULL DEFAULT 0;");
1121 break;
1122 case 'canceled_on':
1123 $wpdb->query("ALTER TABLE {$table} ADD `canceled_on` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00';");
1124 break;
1125 case 'failed':
1126 $wpdb->query("ALTER TABLE {$table} ADD `failed` BOOL NOT NULL DEFAULT 0;");
1127 break;
1128 case 'failed_on':
1129 $wpdb->query("ALTER TABLE {$table} ADD `failed_on` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00';");
1130 break;
1131 case 'settings_update_on':
1132 $wpdb->query("ALTER TABLE {$table} ADD `settings_update_on` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00';");
1133 break;
1134 case 'last_activity':
1135 $wpdb->query("ALTER TABLE {$table} ADD `last_activity` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00';");
1136 break;
1137
1138 default:
1139 # code...
1140 break;
1141 }
1142 }
1143 // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.SchemaChange
1144 }
1145
1146 $table = $this->getTablePrefix() . 'posts';
1147 // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
1148 $tablefields = $wpdb->get_results("DESCRIBE {$table};");
1149 // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
1150 $iteration = false;
1151 $specified = false;
1152
1153 // Check if field exists
1154 foreach ($tablefields as $tablefield) {
1155 if ('iteration' == $tablefield->Field) $iteration = true;
1156 if ('specified' == $tablefield->Field) $specified = true;
1157 }
1158
1159 if (!$iteration){
1160
1161 if (empty($grands)) {
1162 ?>
1163 <div class="error"><p>
1164 <?php
1165 printf(
1166 /* translators: 1: plugin name, 2: SQL user */
1167 esc_html__('<b>%1$s Plugin</b>: Current sql user %2$s doesn\'t have ALTER privileges', 'wp-all-import'),
1168 esc_attr(self::getInstance()->getName()), esc_attr(DB_USER)
1169 ) ?>
1170 </p></div>
1171 <?php
1172 return false;
1173 }
1174
1175 // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.SchemaChange
1176 $wpdb->query("ALTER TABLE {$table} ADD `iteration` BIGINT(20) NOT NULL DEFAULT 0;");
1177
1178 // Add indexing to pmxi_posts.post_id and pmxi_posts.import_id fields.
1179 $wpdb->query("ALTER TABLE {$table} ADD INDEX `post_id`(`post_id`);");
1180 $wpdb->query("ALTER TABLE {$table} ADD INDEX `import_id`(`import_id`)");
1181 // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.SchemaChange
1182 }
1183
1184 if (!$specified and !empty($grands))
1185 {
1186 // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.SchemaChange
1187 $wpdb->query("ALTER TABLE {$table} ADD `specified` BOOL NOT NULL DEFAULT 0;");
1188 // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.SchemaChange
1189 }
1190
1191 if ( ! empty($wpdb->charset))
1192 $charset_collate = "DEFAULT CHARACTER SET $wpdb->charset";
1193 if ( ! empty($wpdb->collate))
1194 $charset_collate .= " COLLATE $wpdb->collate";
1195
1196 $table_prefix = $this->getTablePrefix();
1197
1198 // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.SchemaChange
1199 $wpdb->query("CREATE TABLE IF NOT EXISTS {$table_prefix}history (
1200 id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
1201 import_id BIGINT(20) UNSIGNED NOT NULL,
1202 type ENUM('manual','processing','trigger','continue','') NOT NULL DEFAULT '',
1203 time_run TEXT,
1204 date DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
1205 summary TEXT,
1206 PRIMARY KEY (id)
1207 ) $charset_collate;");
1208 // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.SchemaChange
1209
1210 return true;
1211 }
1212
1213 /**
1214 * Method returns default import options, main utility of the method is to avoid warnings when new
1215 * option is introduced but already registered imports don't have it
1216 */
1217 public static function get_default_import_options() {
1218 return array(
1219 'type' => 'post',
1220 'is_override_post_type' => 0,
1221 'post_type_xpath' => '',
1222 'deligate' => '',
1223 'wizard_type' => 'new',
1224 'ftp_host' => '',
1225 'ftp_path' => '',
1226 'ftp_root' => '/',
1227 'ftp_port' => 21,
1228 'ftp_username' => '',
1229 'ftp_password' => '',
1230 'ftp_private_key' => '',
1231 'custom_type' => '',
1232 'featured_delim' => ',',
1233 'atch_delim' => ',',
1234 'is_search_existing_attach' => 0,
1235 'post_taxonomies' => array(),
1236 'parent' => 0,
1237 'is_multiple_page_parent' => 'yes',
1238 'single_page_parent' => '',
1239 'order' => 0,
1240 'status' => 'publish',
1241 'page_template' => 'default',
1242 'is_multiple_page_template' => 'yes',
1243 'single_page_template' => '',
1244 'page_taxonomies' => array(),
1245 'date_type' => 'specific',
1246 'date' => 'now',
1247 'date_start' => 'now',
1248 'date_end' => 'now',
1249 'custom_name' => array(),
1250 'custom_value' => array(),
1251 'custom_format' => array(),
1252 'custom_mapping' => array(),
1253 'serialized_values' => array(),
1254 'custom_mapping_rules' => array(),
1255 'comment_status' => 'open',
1256 'comment_status_xpath' => '',
1257 'ping_status' => 'open',
1258 'ping_status_xpath' => '',
1259 'create_draft' => 'no',
1260 'author' => '',
1261 'post_excerpt' => '',
1262 'post_slug' => '',
1263 'attachments' => '',
1264 'is_import_specified' => 0,
1265 'import_specified' => '',
1266 'is_delete_source' => 0,
1267 'is_cloak' => 0,
1268 'unique_key' => '',
1269 'tmp_unique_key' => '',
1270 'feed_type' => 'auto',
1271 'search_existing_images' => 1,
1272
1273 'create_new_records' => 1,
1274 'is_selective_hashing' => 0,
1275 'is_delete_missing' => 0,
1276 'delete_missing_logic' => 'import',
1277 'delete_missing_action' => 'keep',
1278 'is_send_removed_to_trash' => 0,
1279 'is_change_post_status_of_removed' => 1,
1280 'status_of_removed' => 'draft',
1281 'status_of_removed_products' => 'outofstock',
1282 'set_missing_to_draft' => 0,
1283 'is_update_missing_cf' => 0,
1284 'update_missing_cf_name' => [],
1285 'update_missing_cf_value' => [],
1286
1287 'is_keep_former_posts' => 'no',
1288 'is_update_status' => 1,
1289 'is_update_content' => 1,
1290 'is_update_title' => 1,
1291 'is_update_slug' => 1,
1292 'is_update_excerpt' => 1,
1293 'is_update_categories' => 1,
1294 'is_update_author' => 1,
1295 'is_update_comment_status' => 1,
1296 'is_update_ping_status' => 1,
1297 'is_update_post_type' => 1,
1298 'is_update_post_format' => 1,
1299 'update_categories_logic' => 'full_update',
1300 'taxonomies_list' => array(),
1301 'taxonomies_only_list' => array(),
1302 'taxonomies_except_list' => array(),
1303 'do_not_create_terms' => 0,
1304 'is_update_attachments' => 1,
1305 'is_update_images' => 1,
1306 'update_images_logic' => 'full_update',
1307 'is_update_dates' => 1,
1308 'is_update_menu_order' => 1,
1309 'is_update_parent' => 1,
1310 'is_keep_attachments' => 0,
1311 'is_delete_attachments' => 0,
1312 'is_keep_imgs' => 0,
1313 'is_delete_imgs' => 0,
1314 'do_not_remove_images' => 1,
1315
1316 'is_update_custom_fields' => 1,
1317 'update_custom_fields_logic' => 'full_update',
1318 'custom_fields_list' => array(),
1319 'custom_fields_only_list' => array(),
1320 'custom_fields_except_list' => array(),
1321
1322 'duplicate_matching' => 'auto',
1323 'duplicate_indicator' => 'title',
1324 'custom_duplicate_name' => '',
1325 'custom_duplicate_value' => '',
1326 'is_update_previous' => 0,
1327 'is_scheduled' => '',
1328 'scheduled_period' => '',
1329 'friendly_name' => '',
1330 'records_per_request' => 20,
1331 'auto_rename_images' => 0,
1332 'auto_rename_images_suffix' => '',
1333 'images_name' => 'filename',
1334 'post_format' => 'standard',
1335 'post_format_xpath' => '',
1336 'encoding' => 'UTF-8',
1337 'delimiter' => '',
1338 'image_meta_title' => '',
1339 'image_meta_title_delim' => ',',
1340 'image_meta_caption' => '',
1341 'image_meta_caption_delim' => ',',
1342 'image_meta_alt' => '',
1343 'image_meta_alt_delim' => ',',
1344 'image_meta_description' => '',
1345 'image_meta_description_delim' => ',',
1346 'image_meta_description_delim_logic' => 'separate',
1347 'status_xpath' => '',
1348 'download_images' => 'yes',
1349 'converted_options' => 0,
1350 'update_all_data' => 'yes',
1351 'is_fast_mode' => 0,
1352 'chuncking' => 1,
1353 'import_processing' => 'ajax',
1354 'processing_iteration_logic' => 'auto',
1355 'save_template_as' => 0,
1356
1357 'title' => '',
1358 'content' => '',
1359 'name' => '',
1360 'is_keep_linebreaks' => 1,
1361 'is_leave_html' => 0,
1362 'fix_characters' => 0,
1363 'pid_xpath' => '',
1364 'slug_xpath' => '',
1365 'title_xpath' => '',
1366
1367 'featured_image' => '',
1368 'download_featured_image' => '',
1369 'download_featured_delim' => ',',
1370 'gallery_featured_image' => '',
1371 'gallery_featured_delim' => ',',
1372 'is_featured' => 1,
1373 'is_featured_xpath' => '',
1374 'set_image_meta_title' => 0,
1375 'set_image_meta_caption' => 0,
1376 'set_image_meta_alt' => 0,
1377 'set_image_meta_description' => 0,
1378 'auto_set_extension' => 0,
1379 'new_extension' => '',
1380 'tax_logic' => array(),
1381 'tax_assing' => array(),
1382 'term_assing' => array(),
1383 'multiple_term_assing' => array(),
1384 'tax_hierarchical_assing' => array(),
1385 'tax_hierarchical_last_level_assign' => array(),
1386 'tax_single_xpath' => array(),
1387 'tax_multiple_xpath' => array(),
1388 'tax_hierarchical_xpath' => array(),
1389 'tax_multiple_delim' => array(),
1390 'tax_hierarchical_delim' => array(),
1391 'tax_manualhierarchy_delim' => array(),
1392 'tax_hierarchical_logic_entire' => array(),
1393 'tax_hierarchical_logic_manual' => array(),
1394 'tax_enable_mapping' => array(),
1395 'tax_is_full_search_single' => array(),
1396 'tax_is_full_search_multiple' => array(),
1397 'tax_assign_to_one_term_single' => array(),
1398 'tax_assign_to_one_term_multiple' => array(),
1399 'tax_mapping' => array(),
1400 'tax_logic_mapping' => array(),
1401 'is_tax_hierarchical_group_delim' => array(),
1402 'tax_hierarchical_group_delim' => array(),
1403 'nested_files' => array(),
1404 'xml_reader_engine' => 0,
1405 'taxonomy_type' => '',
1406 'taxonomy_parent' => '',
1407 'taxonomy_slug' => 'auto',
1408 'taxonomy_slug_xpath' => '',
1409 'taxonomy_display_type' => '',
1410 'taxonomy_display_type_xpath' => '',
1411 'import_img_tags' => 0,
1412 'search_existing_images_logic' => 'by_url',
1413 'enable_import_scheduling' => 'false',
1414 'scheduling_enable' => false,
1415 'scheduling_weekly_days' => '',
1416 'scheduling_run_on' => 'weekly',
1417 'scheduling_monthly_day' => '',
1418 'scheduling_times' => array(),
1419 'scheduling_timezone' => 'UTC',
1420 'is_update_comment_post_id' => 1,
1421 'is_update_comment_author' => 1,
1422 'is_update_comment_author_email' => 1,
1423 'is_update_comment_author_url' => 1,
1424 'is_update_comment_author_IP' => 1,
1425 'is_update_comment_karma' => 1,
1426 'is_update_comment_approved' => 1,
1427 'is_update_comment_verified' => 1,
1428 'is_update_comment_rating' => 1,
1429 'is_update_comment_agent' => 1,
1430 'is_update_comment_user_id' => 1,
1431 'is_update_comment_type' => 1,
1432 'is_update_comments' => 1,
1433 'update_comments_logic' => 'full_update',
1434 'comment_author' => '',
1435 'comment_author_email' => '',
1436 'comment_author_url' => '',
1437 'comment_author_IP' => '',
1438 'comment_karma' => '',
1439 'comment_parent' => '',
1440 'comment_approved' => '1',
1441 'comment_approved_xpath' => '',
1442 'comment_verified' => '1',
1443 'comment_verified_xpath' => '',
1444 'comment_agent' => '',
1445 'comment_type' => '',
1446 'comment_type_xpath' => '',
1447 'comment_user_id' => 'email',
1448 'comment_user_id_xpath' => '',
1449 'comment_post' => '',
1450 'comment_rating' => '',
1451 'comments_repeater_mode' => 'csv',
1452 'comments_repeater_mode_separator' => '|',
1453 'comments_repeater_mode_foreach' => '',
1454 'comments' => array(
1455 'content' => '',
1456 'author' => '',
1457 'author_email' => '',
1458 'author_url' => '',
1459 'author_ip' => '',
1460 'karma' => '',
1461 'approved' => '',
1462 'type' => '',
1463 'date' => 'now'
1464 ),
1465
1466 // Excel Alternative Processing
1467 'use_alternative_excel_processing' => 0,
1468
1469 );
1470 }
1471
1472 /*
1473 * Convert csv to xml
1474 */
1475 public static function csv_to_xml($csv_url){
1476
1477 include_once(self::ROOT_DIR.'/libraries/XmlImportCsvParse.php');
1478
1479 $csv = new PMXI_CsvParser($csv_url);
1480
1481 $wp_uploads = wp_upload_dir();
1482 $tmpname = wp_unique_filename($wp_uploads['path'], str_replace("csv", "xml", basename($csv_url)));
1483 $xml_file = $wp_uploads['path'] .'/'. $tmpname;
1484 file_put_contents($xml_file, $csv->toXML());
1485 return $xml_file;
1486
1487 }
1488
1489 /**
1490 * @return bool
1491 */
1492 public static function is_ajax(){
1493 return strpos(sanitize_text_field(wp_unslash($_SERVER["HTTP_ACCEPT"] ?? '')), 'json') !== false;
1494 }
1495
1496 /**
1497 * Returns ID of current import.
1498 *
1499 * @return int|bool
1500 */
1501 public static function getCurrentImportId() {
1502 $input = new PMXI_Input();
1503 $import_id = $input->get('id');
1504 if (empty($import_id)) {
1505 $import_id = $input->get('import_id');
1506 }
1507 return $import_id;
1508 }
1509
1510 /**
1511 * @param $message
1512 */
1513 public function showNoticeAndDisablePlugin($message){
1514 $this->showNotice($message);
1515 deactivate_plugins( str_replace('\\', '/', dirname(__FILE__)) . '/wp-all-import-pro.php');
1516 }
1517
1518 /**
1519 * @param $message
1520 */
1521 public function showNotice($message) {
1522 $notice = new \Wpai\WordPress\AdminErrorNotice($message);
1523 $notice->render();
1524 }
1525
1526 /**
1527 * @param $message
1528 * @param $noticeId
1529 */
1530 public function showDismissibleNotice($message, $noticeId) {
1531 $notice = new \Wpai\WordPress\AdminDismissibleNotice($message, $noticeId);
1532 if(!$notice->isDismissed()) {
1533 $notice->render();
1534 }
1535 }
1536
1537
1538 }
1539
1540 PMXI_Plugin::getInstance();
1541