PluginProbe
WP All Export – Drag & Drop Export to Any Custom CSV, XML & Excel / 1.4.5
WP All Export – Drag & Drop Export to Any Custom CSV, XML & Excel v1.4.5
trunk 0.9.0 0.9.1 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.2.0 1.2.1 1.2.10 1.2.2 1.2.3 1.2.4 All 57 releases
wp-all-export / wp-all-export.php
wp-all-export.php
1,044 lines 41.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /*
3 Plugin Name: WP All Export
4 Plugin URI: http://www.wpallimport.com/upgrade-to-wp-all-export-pro/?utm_source=export-plugin-free&utm_medium=wp-plugins-page&utm_campaign=upgrade-to-pro
5 Description: Export any post type to a CSV or XML file. Edit the exported data, and then re-import it later using WP All Import.
6 Version: 1.4.5
7 Author: Soflyy
8 */
9
10 require_once(__DIR__.'/classes/CdataStrategyFactory.php');
11
12 if( ! defined( 'PMXE_SESSION_COOKIE' ) )
13 define( 'PMXE_SESSION_COOKIE', '_pmxe_session' );
14
15 // Enable error reporting in development
16 if(getenv('WPAE_DEV')) {
17 error_reporting(E_ALL ^ E_DEPRECATED );
18 ini_set('display_errors', 1);
19 // xdebug_disable();
20 }
21
22 /**
23 * Plugin root dir with forward slashes as directory separator regardless of actuall DIRECTORY_SEPARATOR value
24 * @var string
25 */
26 define('PMXE_ROOT_DIR', str_replace('\\', '/', dirname(__FILE__)));
27 /**
28 * Plugin root url for referencing static content
29 * @var string
30 */
31 define('PMXE_ROOT_URL', rtrim(plugin_dir_url(__FILE__), '/'));
32
33 if ( class_exists('PMXE_Plugin') and PMXE_EDITION == "paid"){
34
35 function pmxe_notice(){
36
37 ?>
38 <div class="error">
39 <p>
40 <?php printf(esc_html__('Please de-activate and remove the free version of the WP All Export before activating the paid version.', 'wp_all_export_plugin')); ?>
41 </p>
42 </div>
43 <?php
44
45 deactivate_plugins( str_replace('\\', '/', dirname(__FILE__)) . '/wp-all-export.php');
46
47 }
48
49 add_action('admin_notices', 'pmxe_notice');
50
51 }
52 else {
53
54 /**
55 * Plugin prefix for making names unique (be aware that this variable is used in conjunction with naming convention,
56 * i.e. in order to change it one must not only modify this constant but also rename all constants, classes and functions which
57 * names composed using this prefix)
58 * @var string
59 */
60 define('PMXE_PREFIX', 'pmxe_');
61
62 define('PMXE_VERSION', '1.4.5');
63
64 define('PMXE_ASSETS_VERSION', '-1.0.2');
65
66 define('PMXE_EDITION', 'free');
67
68 /**
69 * Plugin root uploads folder name
70 * @var string
71 */
72 define('WP_ALL_EXPORT_UPLOADS_BASE_DIRECTORY', 'wpallexport');
73 /**
74 * Plugin uploads folder name
75 * @var string
76 */
77 define('WP_ALL_EXPORT_UPLOADS_DIRECTORY', WP_ALL_EXPORT_UPLOADS_BASE_DIRECTORY . DIRECTORY_SEPARATOR . 'exports');
78
79 /**
80 * Plugin temp folder name
81 * @var string
82 */
83 define('WP_ALL_EXPORT_TEMP_DIRECTORY', WP_ALL_EXPORT_UPLOADS_BASE_DIRECTORY . DIRECTORY_SEPARATOR . 'temp');
84
85 /**
86 * Plugin temp folder name
87 * @var string
88 */
89 define('WP_ALL_EXPORT_CRON_DIRECTORY', WP_ALL_EXPORT_UPLOADS_BASE_DIRECTORY . DIRECTORY_SEPARATOR . 'exports');
90
91 /**
92 * Main plugin file, Introduces MVC pattern
93 *
94 * @singletone
95 * @author Pavel Kulbakin <p.kulbakin@gmail.com>
96 */
97 final class PMXE_Plugin {
98 /**
99 * Singletone instance
100 * @var PMXE_Plugin
101 */
102 protected static $instance;
103
104 /**
105 * Plugin options
106 * @var array
107 */
108 protected $options = array();
109
110 /**
111 * Plugin root dir
112 * @var string
113 */
114 const ROOT_DIR = PMXE_ROOT_DIR;
115 /**
116 * Plugin root URL
117 * @var string
118 */
119 const ROOT_URL = PMXE_ROOT_URL;
120 /**
121 * Prefix used for names of shortcodes, action handlers, filter functions etc.
122 * @var string
123 */
124 const PREFIX = PMXE_PREFIX;
125 /**
126 * Plugin file path
127 * @var string
128 */
129 const FILE = __FILE__;
130 /**
131 * Max allowed file size (bytes) to import in default mode
132 * @var int
133 */
134 const LARGE_SIZE = 0; // all files will importing in large import mode
135
136 /**
137 * WP All Import temp folder
138 * @var string
139 */
140 const TEMP_DIRECTORY = WP_ALL_EXPORT_TEMP_DIRECTORY;
141 /**
142 * WP All Import uploads folder
143 * @var string
144 */
145 const UPLOADS_DIRECTORY = WP_ALL_EXPORT_UPLOADS_DIRECTORY;
146 /**
147 * WP All Import uploads folder
148 * @var string
149 */
150 const CRON_DIRECTORY = WP_ALL_EXPORT_CRON_DIRECTORY;
151
152 const LANGUAGE_DOMAIN = 'wp_all_export_plugin';
153
154 public static $session = null;
155
156 public static $capabilities = 'install_plugins';
157
158 private static $hasActiveSchedulingLicense = null;
159
160 /** @var \Wpae\App\Service\Addons\AddonService */
161 private $addons;
162
163 public static $cache_key = '';
164
165 /**
166 * Class constructor containing dispatching logic
167 * @param string $rootDir Plugin root dir
168 * @param string $pluginFilePath Plugin main file
169 */
170 protected function __construct() {
171
172 if(!is_multisite() || defined('WPAI_WPAE_ALLOW_INSECURE_MULTISITE') && 1 === WPAI_WPAE_ALLOW_INSECURE_MULTISITE){
173 self::$capabilities = 'manage_options';
174 }
175
176 require_once (self::ROOT_DIR . '/classes/installer.php');
177
178 $installer = new PMXE_Installer();
179 $installer->checkActivationConditions();
180
181 $plugin_basename = plugin_basename( __FILE__ );
182
183 self::$cache_key = md5( 'edd_plugin_' . sanitize_key( $plugin_basename ) . '_version_info' );
184
185 // uncaught exception doesn't prevent plugin from being activated, therefore replace it with fatal error so it does
186 //set_exception_handler(create_function('$e', 'trigger_error($e->getMessage(), E_USER_ERROR);'));
187
188 // register autoloading method
189 spl_autoload_register(array($this, 'autoload'));
190
191 // register helpers
192 if (is_dir(self::ROOT_DIR . '/helpers')) foreach (PMXE_Helper::safe_glob(self::ROOT_DIR . '/helpers/*.php', PMXE_Helper::GLOB_RECURSE | PMXE_Helper::GLOB_PATH) as $filePath) {
193 require_once $filePath;
194 }
195
196 $this->addons = new \Wpae\App\Service\Addons\AddonService();
197
198 // init plugin options
199 $option_name = get_class($this) . '_Options';
200 $options_default = PMXE_Config::createFromFile(self::ROOT_DIR . '/config/options.php')->toArray();
201 $current_options = get_option($option_name, array());
202 $this->options = array_intersect_key($current_options, $options_default) + $options_default;
203 $this->options = array_intersect_key($options_default, array_flip(array('info_api_url'))) + $this->options; // make sure hidden options apply upon plugin reactivation
204 if ('' == $this->options['cron_job_key']) $this->options['cron_job_key'] = wp_all_export_url_title(wp_all_export_rand_char(12));
205
206 if ($current_options !== $this->options) {
207 update_option($option_name, $this->options);
208 }
209 register_activation_hook(self::FILE, array($this, 'activation'));
210
211 // register action handlers
212 if (is_dir(self::ROOT_DIR . '/actions')) if (is_dir(self::ROOT_DIR . '/actions')) foreach (PMXE_Helper::safe_glob(self::ROOT_DIR . '/actions/*.php', PMXE_Helper::GLOB_RECURSE | PMXE_Helper::GLOB_PATH) as $filePath) {
213 require_once $filePath;
214 $function = $actionName = basename($filePath, '.php');
215 if (preg_match('%^(.+?)[_-](\d+)$%', $actionName, $m)) {
216 $actionName = $m[1];
217 $priority = intval($m[2]);
218 } else {
219 $priority = 10;
220 }
221 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)
222 }
223
224 add_action("admin_enqueue_scripts", [$this, 'add_admin_scripts']);
225
226 // register filter handlers
227 if (is_dir(self::ROOT_DIR . '/filters')) foreach (PMXE_Helper::safe_glob(self::ROOT_DIR . '/filters/*.php', PMXE_Helper::GLOB_RECURSE | PMXE_Helper::GLOB_PATH) as $filePath) {
228 require_once $filePath;
229 $function = $actionName = basename($filePath, '.php');
230 if (preg_match('%^(.+?)[_-](\d+)$%', $actionName, $m)) {
231 $actionName = $m[1];
232 $priority = intval($m[2]);
233 } else {
234 $priority = 10;
235 }
236 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)
237 }
238
239 // register shortcodes handlers
240 if (is_dir(self::ROOT_DIR . '/shortcodes')) foreach (PMXE_Helper::safe_glob(self::ROOT_DIR . '/shortcodes/*.php', PMXE_Helper::GLOB_RECURSE | PMXE_Helper::GLOB_PATH) as $filePath) {
241 $tag = strtolower(str_replace('/', '_', preg_replace('%^' . preg_quote(self::ROOT_DIR . '/shortcodes/', '%') . '|\.php$%', '', $filePath)));
242 add_shortcode($tag, array($this, 'shortcodeDispatcher'));
243 }
244
245 // register admin page pre-dispatcher
246 add_action('admin_init', array($this, 'adminInit'), 11);
247 add_action('admin_init', array($this, 'fix_db_schema'), 10);
248 add_action('init', array($this, 'init'), 10);
249 }
250
251 public function add_admin_scripts() {
252 $cm_settings['codeEditor'] = wp_enqueue_code_editor(['type' => 'php']);
253
254 // Use our modified function if user has disabled the syntax editor.
255 if(false === $cm_settings['codeEditor']){
256 $cm_settings['codeEditor'] = wpae_wp_enqueue_code_editor(['type' => 'php']);
257 }
258
259 wp_localize_script('jquery', 'wpae_cm_settings', $cm_settings);
260 }
261
262 /**
263 * Return singletone instance
264 * @return PMXE_Plugin
265 */
266 static public function getInstance() {
267 if (self::$instance == NULL) {
268 self::$instance = new self();
269 }
270 return self::$instance;
271 }
272
273 static public function getSchedulingName(){
274 return 'Automatic Scheduling';
275 }
276
277 static public function hasActiveSchedulingLicense() {
278
279 if(is_null(self::$hasActiveSchedulingLicense)) {
280 $scheduling = \Wpae\Scheduling\Scheduling::create();
281 $hasActiveSchedulingLicense = $scheduling->checkLicense();
282 self::$hasActiveSchedulingLicense = $hasActiveSchedulingLicense;
283 }
284
285 return self::$hasActiveSchedulingLicense;
286 }
287
288 /**
289 * Common logic for requestin plugin info fields
290 */
291 public function __call($method, $args) {
292 if (preg_match('%^get(.+)%i', $method, $mtch)) {
293 $info = get_plugin_data(self::FILE);
294 if (isset($info[$mtch[1]])) {
295 return $info[$mtch[1]];
296 }
297 }
298 throw new Exception("Requested method " . get_class($this) . "::$method doesn't exist.");
299 }
300
301 /**
302 * Get path to plagin dir relative to wordpress root
303 * @param bool[optional] $noForwardSlash Whether path should be returned withot forwarding slash
304 * @return string
305 */
306 public function getRelativePath($noForwardSlash = false) {
307 $wp_root = str_replace('\\', '/', ABSPATH);
308 return ($noForwardSlash ? '' : '/') . str_replace($wp_root, '', self::ROOT_DIR);
309 }
310
311 /**
312 * Check whether plugin is activated as network one
313 * @return bool
314 */
315 public function isNetwork() {
316 if ( !is_multisite() )
317 return false;
318
319 $plugins = get_site_option('active_sitewide_plugins');
320 if (isset($plugins[plugin_basename(self::FILE)]))
321 return true;
322
323 return false;
324 }
325
326 /**
327 * Check whether permalinks is enabled
328 * @return bool
329 */
330 public function isPermalinks() {
331 global $wp_rewrite;
332
333 return $wp_rewrite->using_permalinks();
334 }
335
336 /**
337 * Return prefix for plugin database tables
338 * @return string
339 */
340 public function getTablePrefix() {
341 global $wpdb;
342
343 //return ($this->isNetwork() ? $wpdb->base_prefix : $wpdb->prefix) . self::PREFIX;
344 return $wpdb->prefix . self::PREFIX;
345 }
346
347 /**
348 * Return prefix for wordpress database tables
349 * @return string
350 */
351 public function getWPPrefix() {
352 global $wpdb;
353 return ($this->isNetwork()) ? $wpdb->base_prefix : $wpdb->prefix;
354 }
355
356 public function init(){
357 $this->load_plugin_textdomain();
358 }
359
360 public function showNoticeAndDisablePlugin($message){
361 $this->showNotice($message);
362 deactivate_plugins( str_replace('\\', '/', dirname(__FILE__)) . '/wp-all-export.php');
363 }
364
365 public function showNotice($message)
366 {
367 $notice = new \Wpae\WordPress\AdminErrorNotice($message);
368 $notice->render();
369 }
370
371 public function showDismissibleNotice($message, $noticeId)
372 {
373 $notice = new \Wpae\WordPress\SitewideAdminDismissibleNotice($message, $noticeId);
374 if (!$notice->isDismissed()) {
375 $notice->render();
376 }
377 }
378
379 /**
380 * pre-dispatching logic for admin page controllers
381 */
382 public function adminInit() {
383
384 if(!wp_doing_ajax()) {
385
386 $addons_not_included = get_option('wp_all_export_free_addons_not_included', false);
387
388 if (!$addons_not_included && current_user_can('manage_options') && (!XmlExportEngine::get_addons_service()->isAcfAddonActive() || !XmlExportEngine::get_addons_service()->isWooCommerceAddonActive())) {
389
390 $this->showDismissibleNotice('<h1 style="padding-top:0">Important Notice Regarding WP All Export</h1><br><strong>WP All Export now requires paid add-ons to export ACF and WooCommerce data.<br/>We are providing these Pro add-ons to everyone who was using WP All Export before the change, free of charge. Please contact support for further assistance: <a href="https://www.wpallimport.com/support/" target="_blank">https://www.wpallimport.com/support/</a>', 'wpae_free_export_addons_notice');
391 }
392
393 // create history folder
394 $uploads = wp_upload_dir();
395
396 $wpallimportDirs = array(WP_ALL_EXPORT_UPLOADS_BASE_DIRECTORY, self::TEMP_DIRECTORY, self::UPLOADS_DIRECTORY, self::CRON_DIRECTORY);
397
398 foreach ($wpallimportDirs as $destination) {
399
400 $dir = $uploads['basedir'] . DIRECTORY_SEPARATOR . $destination;
401
402 if (!is_dir($dir)) wp_mkdir_p($dir);
403
404 if (!@file_exists($dir . DIRECTORY_SEPARATOR . 'index.php')) @touch($dir . DIRECTORY_SEPARATOR . 'index.php');
405
406 }
407
408 if (!is_dir($uploads['basedir'] . DIRECTORY_SEPARATOR . WP_ALL_EXPORT_UPLOADS_BASE_DIRECTORY) or !is_writable($uploads['basedir'] . DIRECTORY_SEPARATOR . WP_ALL_EXPORT_UPLOADS_BASE_DIRECTORY)) {
409 $this->showNoticeAndDisablePlugin(sprintf(esc_html__('Uploads folder %s must be writable', 'wp_all_export_plugin'), $uploads['basedir'] . DIRECTORY_SEPARATOR . WP_ALL_EXPORT_UPLOADS_BASE_DIRECTORY));
410 }
411
412 if (!is_dir($uploads['basedir'] . DIRECTORY_SEPARATOR . self::UPLOADS_DIRECTORY) or !is_writable($uploads['basedir'] . DIRECTORY_SEPARATOR . self::UPLOADS_DIRECTORY)) {
413 $this->showNoticeAndDisablePlugin(sprintf(esc_html__('Uploads folder %s must be writable', 'wp_all_export_plugin'), $uploads['basedir'] . DIRECTORY_SEPARATOR . self::UPLOADS_DIRECTORY));
414 }
415
416 if (!$addons_not_included && $this->addons->userExportsExistAndAddonNotInstalled() && current_user_can('manage_options')) {
417 $this->showDismissibleNotice(__('<strong style="font-size:16px">A Configured Export Requires the User Export Add-On</strong><p>Your User exports will not be able to run until you install the User Export Add-On. That add-on is available from <a href="https://wordpress.org/plugins/export-wp-users-xml-csv/" target="_blank">wordpress.org</a>.</p>', PMXE_Plugin::LANGUAGE_DOMAIN), 'wpae_user_addon_not_installed_notice');
418 }
419
420 if (!$addons_not_included && $this->addons->wooCommerceExportsExistAndAddonNotInstalled() && current_user_can('manage_options') && \class_exists('WooCommerce')) {
421 $this->showDismissibleNotice(__('<strong style="font-size:16px">A Configured Export Requires the WooCommerce Export Add-On Pro</strong><p>Your Products, Orders, and Coupons exports will not be able to run until you install the WooCommerce Export Add-On Pro. That add-on is available to those who were using WP All Export Free before this requirement.</p>', PMXE_Plugin::LANGUAGE_DOMAIN)
422 . '<p><a class="button button-primary" href="https://wpallimport.com/portal/downloads" target="_blank">' . __('Download Add-On', PMXE_Plugin::LANGUAGE_DOMAIN) . '</a></p>', 'wpae_woocommerce_addon_not_installed_notice');
423 }
424
425 if (!$addons_not_included && $this->addons->acfExportsExistAndNotInstalled() && current_user_can('manage_options')) {
426 $this->showDismissibleNotice(__('<strong style="font-size:16px">A Configured Export Requires the ACF Export Add-On Pro</strong><p>Exports that contain ACF fields will not be able to run until you install the ACF Export Add-On Pro. That add-on is available to those who were using WP All Export Free before this requirement.</p>', PMXE_Plugin::LANGUAGE_DOMAIN)
427 . '<p><a class="button button-primary" href="https://wpallimport.com/portal/downloads" target="_blank">' . __('Download Add-On', PMXE_Plugin::LANGUAGE_DOMAIN) . '</a></p>', 'wpae_acf_addon_not_installed_notice');
428 }
429 }
430
431 self::$session = new PMXE_Handler();
432
433 $input = new PMXE_Input();
434 $page = strtolower($input->getpost('page', ''));
435
436 if (preg_match('%^' . preg_quote(str_replace('_', '-', self::PREFIX), '%') . '([\w-]+)$%', $page)) {
437
438 $action = strtolower($input->getpost('action', 'index'));
439
440 // capitalize prefix and first letters of class name parts
441 $controllerName = preg_replace_callback('%(^' . preg_quote(self::PREFIX, '%') . '|_).%', array($this, "replace_callback"),str_replace('-', '_', $page));
442 $actionName = str_replace('-', '_', $action);
443 if (method_exists($controllerName, $actionName)) {
444
445 if ( ! get_current_user_id() or ! current_user_can(self::$capabilities)) {
446 // This nonce is not valid.
447 die( 'Security check' );
448
449 } else {
450
451 $this->_admin_current_screen = (object)array(
452 'id' => $controllerName,
453 'base' => $controllerName,
454 'action' => $actionName,
455 'is_ajax' => strpos($_SERVER["HTTP_ACCEPT"], 'json') !== false,
456 'is_network' => is_network_admin(),
457 'is_user' => is_user_admin(),
458 );
459 add_filter('current_screen', array($this, 'getAdminCurrentScreen'));
460 add_filter('admin_body_class',
461 function($admin_body_class) {
462 return $admin_body_class.' wpallexport-plugin';
463 }
464 );
465
466 $controller = new $controllerName();
467 if ( ! $controller instanceof PMXE_Controller_Admin) {
468 throw new Exception("Administration page `$page` matches to a wrong controller type.");
469 }
470
471 $reviewsUI = new \Wpae\Reviews\ReviewsUI();
472
473 add_action('admin_notices', [$reviewsUI, 'render']);
474
475 if($controller instanceof PMXE_Admin_Manage && ($action == 'update' || $action == 'template' || $action == 'options') && isset($_GET['id'])) {
476 $addons = new \Wpae\App\Service\Addons\AddonService();
477 $exportId = intval($_GET['id']);
478
479 $export = new \PMXE_Export_Record();
480 $export->getById($exportId);
481
482 $cpt = $export->options['cpt'];
483 if (!is_array($cpt)) {
484 $cpt = array($cpt);
485 }
486
487 if(isset($export->options['export_type']) && $export->options['export_type'] === 'advanced') {
488
489 if(!XmlExportEngine::get_addons_service()->isWooCommerceAddonActive() && strpos($export->options['wp_query'], 'product') !== false && \class_exists('WooCommerce')) {
490 die(\__('The WooCommerce Export Add-On Pro is required to run this export. If you already own it, you can download the add-on here: <a href="http://www.wpallimport.com/portal/downloads" target="_blank">http://www.wpallimport.com/portal/downloads</a>', \PMXE_Plugin::LANGUAGE_DOMAIN));
491 }
492 else if( (!XmlExportEngine::get_addons_service()->isWooCommerceAddonActive() || !XmlExportEngine::get_addons_service()->isWooCommerceOrderAddonActive() ) && strpos($export->options['wp_query'], 'shop_order') !== false) {
493 die(\__('The WooCommerce Export Add-On Pro is required to run this export. If you already own it, you can download the add-on here: <a href="http://www.wpallimport.com/portal/downloads" target="_blank">http://www.wpallimport.com/portal/downloads</a>', \PMXE_Plugin::LANGUAGE_DOMAIN));
494 }
495 else if(!XmlExportEngine::get_addons_service()->isWooCommerceAddonActive() && strpos($export->options['wp_query'], 'shop_coupon') !== false) {
496 die(\__('The WooCommerce Export Add-On Pro is required to run this export. If you already own it, you can download the add-on here: <a href="http://www.wpallimport.com/portal/downloads" target="_blank">http://www.wpallimport.com/portal/downloads</a>', \PMXE_Plugin::LANGUAGE_DOMAIN));
497 }
498 }
499
500 if (
501 ((in_array('users', $cpt) || in_array('shop_customer', $cpt)) && !$addons->isUserAddonActive()) ||
502 ($export->options['export_type'] == 'advanced' && $export->options['wp_query_selector'] == 'wp_user_query' && !$addons->isUserAddonActive())
503 ) {
504 die(\__('The User Export Add-On Pro is required to run this export. You can download the add-on here: <a href="http://www.wpallimport.com/portal/" target="_blank">http://www.wpallimport.com/portal/</a>', \PMXE_Plugin::LANGUAGE_DOMAIN));
505 }
506
507 if (
508 (
509 (
510 ( in_array( 'product', $cpt ) && \class_exists('WooCommerce') && ! XmlExportEngine::get_addons_service()->isWooCommerceProductAddonActive() ) ||
511 ( in_array( 'shop_order', $cpt ) && ! XmlExportEngine::get_addons_service()->isWooCommerceOrderAddonActive() ) ||
512 in_array( 'shop_review', $cpt ) ||
513 in_array( 'shop_coupon', $cpt )
514 ) && ! $addons->isWooCommerceAddonActive()
515 ) ||
516 ( $export->options['export_type'] == 'advanced' && $export->options['wp_query_selector'] == 'wp_user_query' && ! $addons->isUserAddonActive() )
517 ) {
518 die( \__( 'The WooCommerce Export Add-On Pro is required to run this export. You can download the add-on here: <a href="http://www.wpallimport.com/portal/" target="_blank">http://www.wpallimport.com/portal/</a>', \PMXE_Plugin::LANGUAGE_DOMAIN ) );
519 }
520
521 if(in_array('acf', $export->options['cc_type']) && !$addons->isAcfAddonActive()) {
522 die(\__('The ACF Export Add-On Pro is required to run this export. You can download the add-on here: <a href="http://www.wpallimport.com/portal/" target="_blank">http://www.wpallimport.com/portal/</a>', \PMXE_Plugin::LANGUAGE_DOMAIN));
523 }
524 }
525
526
527 if ($this->_admin_current_screen->is_ajax) { // ajax request
528 $controller->$action();
529 do_action('wpallexport_action_after');
530 die(); // stop processing since we want to output only what controller is randered, nothing in addition
531 } elseif ( ! $controller->isInline) {
532 @ob_start();
533 $controller->$action();
534 self::$buffer = @ob_get_clean();
535 } else {
536 self::$buffer_callback = array($controller, $action);
537 }
538 }
539
540 } else { // redirect to dashboard if requested page and/or action don't exist
541 wp_redirect(admin_url()); die();
542 }
543
544 }
545 }
546
547
548 /**
549 * Dispatch shorttag: create corresponding controller instance and call its index method
550 * @param array $args Shortcode tag attributes
551 * @param string $content Shortcode tag content
552 * @param string $tag Shortcode tag name which is being dispatched
553 * @return string
554 * @throws Exception
555 */
556 public function shortcodeDispatcher($args, $content, $tag) {
557
558 $controllerName = self::PREFIX . preg_replace_callback('%(^|_).%', array($this, "replace_callback"), $tag);// capitalize first letters of class name parts and add prefix
559 $controller = new $controllerName();
560 if ( ! $controller instanceof PMXE_Controller) {
561 throw new Exception("Shortcode `$tag` matches to a wrong controller type.");
562 }
563 ob_start();
564 $controller->index($args, $content);
565 return ob_get_clean();
566 }
567
568 static $buffer = NULL;
569 static $buffer_callback = NULL;
570
571 /**
572 * Dispatch admin page: call corresponding controller based on get parameter `page`
573 * The method is called twice: 1st time as handler `parse_header` action and then as admin menu item handler
574 * @param string $page
575 * @param string $action
576 * @throws Exception
577 * @internal param $string [optional] $page When $page set to empty string ealier buffered content is outputted, otherwise controller is called based on $page value
578 */
579 public function adminDispatcher($page = '', $action = 'index') {
580 if ('' === $page) {
581 if ( ! is_null(self::$buffer)) {
582 echo '<div class="wrap">';
583 // Contents are sanitized at a lower level
584 echo self::$buffer;
585 do_action('wpallexport_action_after');
586 echo '</div>';
587 } elseif ( ! is_null(self::$buffer_callback)) {
588 echo '<div class="wrap">';
589 call_user_func(self::$buffer_callback);
590 do_action('wpallexport_action_after');
591 echo '</div>';
592 } else {
593 throw new Exception('There is no previousely buffered content to display.');
594 }
595 }
596 }
597
598 public function replace_callback($matches){
599 return strtoupper($matches[0]);
600 }
601
602 protected $_admin_current_screen = NULL;
603 public function getAdminCurrentScreen()
604 {
605 return $this->_admin_current_screen;
606 }
607
608 /**
609 * Autoloader
610 * It's assumed class name consists of prefix folloed by its name which in turn corresponds to location of source file
611 * if `_` symbols replaced by directory path separator. File name consists of prefix folloed by last part in class name (i.e.
612 * symbols after last `_` in class name)
613 * When class has prefix it's source is looked in `models`, `controllers`, `shortcodes` folders, otherwise it looked in `core` or `library` folder
614 *
615 * @param string $className
616 * @return bool
617 */
618 public function autoload($className) {
619
620 $is_prefix = false;
621 $filePath = str_replace('_', '/', preg_replace('%^' . preg_quote(self::PREFIX, '%') . '%', '', strtolower($className), 1, $is_prefix)) . '.php';
622 if ( ! $is_prefix) { // also check file with original letter case
623 $filePathAlt = $className . '.php';
624 }
625 foreach ($is_prefix ? array('models', 'controllers', 'shortcodes', 'classes') : array('libraries') as $subdir) {
626 $path = self::ROOT_DIR . '/' . $subdir . '/' . $filePath;
627 if (is_file($path)) {
628 require_once $path;
629 return TRUE;
630 }
631 if (!$is_prefix) {
632 if (strpos($className, '_') !== false) {
633 $filePathAlt = $this->lreplace('_', DIRECTORY_SEPARATOR, $filePathAlt);
634 }
635
636 $pathAlt = self::ROOT_DIR . DIRECTORY_SEPARATOR . $subdir . DIRECTORY_SEPARATOR . $filePathAlt;
637
638 if (is_file($pathAlt)) {
639 require_once $pathAlt;
640 return TRUE;
641 }
642 }
643 }
644 if($className === 'CdataStrategyFactory') {
645 //TODO: Move this to a namespace
646 require_once (self::ROOT_DIR . '/classes/CdataStrategyFactory.php');
647 }
648
649
650 if(strpos($className, '\\') !== false){
651
652 // project-specific namespace prefix
653 $prefix = 'Wpae\\';
654
655 // base directory for the namespace prefix
656 $base_dir = self::ROOT_DIR . '/src/';
657
658 // does the class use the namespace prefix?
659 $len = strlen($prefix);
660 if (strncmp($prefix, $className, $len) !== 0) {
661 // no, move to the next registered autoloader
662 return false;
663 }
664
665 // get the relative class name
666 $relative_class = substr($className, $len);
667
668 // replace the namespace prefix with the base directory, replace namespace
669 // separators with directory separators in the relative class name, append
670 // with .php
671 $file = $base_dir . str_replace('\\', '/', $relative_class) . '.php';
672
673 // if the file exists, require it
674 if (file_exists($file)) {
675 require_once $file;
676 }
677 }
678
679 return FALSE;
680 }
681
682 /**
683 * Get plugin option
684 * @param string [optional] $option Parameter to return, all array of options is returned if not set
685 * @return mixed
686 * @throws Exception
687 */
688 public function getOption($option = NULL) {
689 $options = apply_filters('wp_all_export_config_options', $this->options);
690 if (is_null($option)) {
691 return $options;
692 } else if (isset($options[$option])) {
693 return $options[$option];
694 } else {
695 throw new Exception("Specified option is not defined for the plugin");
696 }
697 }
698
699 /**
700 * Update plugin option value
701 * @param string $option Parameter name or array of name => value pairs
702 * @param null $value
703 * @return array
704 * @throws Exception
705 * @internal param $mixed [optional] $value New value for the option, if not set than 1st parameter is supposed to be array of name => value pairs
706 */
707 public function updateOption($option, $value = NULL) {
708 is_null($value) or $option = array($option => $value);
709 if (array_diff_key($option, $this->options)) {
710 throw new Exception("Specified option is not defined for the plugin");
711 }
712 $this->options = $option + $this->options;
713 update_option(get_class($this) . '_Options', $this->options);
714
715 return $this->options;
716 }
717
718 /**
719 * Plugin activation logic
720 */
721 public function activation() {
722 // uncaught exception doesn't prevent plugin from being activated, therefore replace it with fatal error so it does
723 set_exception_handler(function($e) {trigger_error($e->getMessage(), E_USER_ERROR); });
724
725 // create plugin options
726 $option_name = get_class($this) . '_Options';
727 $options_default = PMXE_Config::createFromFile(self::ROOT_DIR . '/config/options.php')->toArray();
728 $wpai_options = get_option($option_name, false);
729 if ( ! $wpai_options ) update_option($option_name, $options_default);
730
731 // create/update required database tables
732 require_once ABSPATH . 'wp-admin/includes/upgrade.php';
733 require self::ROOT_DIR . '/schema.php';
734 global $wpdb;
735
736 if (function_exists('is_multisite') && is_multisite()) {
737 // check if it is a network activation - if so, run the activation function for each blog id
738 if (isset($_GET['networkwide']) && ($_GET['networkwide'] == 1)) {
739 $old_blog = $wpdb->blogid;
740 // Get all blog ids
741 $blogids = $wpdb->get_col("SELECT blog_id FROM $wpdb->blogs");
742 foreach ($blogids as $blog_id) {
743 switch_to_blog($blog_id);
744 require self::ROOT_DIR . '/schema.php';
745 dbDelta($plugin_queries);
746 }
747 switch_to_blog($old_blog);
748 return;
749 }
750 }
751
752 dbDelta($plugin_queries);
753
754 }
755
756 /**
757 * Load Localisation files.
758 *
759 * Note: the first-loaded translation file overrides any following ones if the same translation is present
760 *
761 * @access public
762 * @return void
763 */
764 public function load_plugin_textdomain() {
765
766 $locale = apply_filters( 'plugin_locale', get_locale(), 'wp_all_export_plugin' );
767
768 load_plugin_textdomain( 'wp_all_export_plugin', false, dirname( plugin_basename( __FILE__ ) ) . "/i18n/languages" );
769 }
770
771 public function fix_db_schema(){
772
773 global $wpdb;
774
775 $db_version_old = get_option('wp_all_export_db_version');
776 $installed_ver = get_option('wp_all_export_free_db_version');
777
778 // We leave the old option so if it doesn't exist then this was installed after the export addons release.
779 // If it does exist we make sure it's not a Pro version.
780 if(!$db_version_old || version_compare($db_version_old, '1.2.10') == 1) {
781 update_option("wp_all_export_free_addons_not_included", true);
782 }
783
784 if ( $installed_ver == PMXE_VERSION ) return true;
785
786 // Declare variable to avoid nuisance notices when charset and collate aren't set.
787 $charset_collate = '';
788
789 if ( ! empty($wpdb->charset))
790 $charset_collate = "DEFAULT CHARACTER SET $wpdb->charset";
791 if ( ! empty($wpdb->collate))
792 $charset_collate .= " COLLATE $wpdb->collate";
793
794 $table_prefix = $this->getTablePrefix();
795
796 $wpdb->query("CREATE TABLE IF NOT EXISTS {$table_prefix}templates (
797 id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
798 name VARCHAR(200) NOT NULL DEFAULT '',
799 options LONGTEXT,
800 PRIMARY KEY (id)
801 ) $charset_collate;");
802
803 $wpdb->query("CREATE TABLE IF NOT EXISTS {$table_prefix}posts (
804 id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
805 post_id BIGINT(20) UNSIGNED NOT NULL,
806 export_id BIGINT(20) UNSIGNED NOT NULL,
807 iteration BIGINT(20) NOT NULL DEFAULT 0,
808 PRIMARY KEY (id)
809 ) $charset_collate;");
810
811 $googleCatsTableExists = $wpdb->query("SHOW TABLES LIKE '{$table_prefix}google_cats'");
812 if(!$googleCatsTableExists) {
813 require_once self::ROOT_DIR . '/schema.php';
814 $wpdb->query($googleCatsQueryCreate);
815 $wpdb->query($googleCatsQueryData);
816 }
817
818 $table = $this->getTablePrefix() . 'exports';
819 $tablefields = $wpdb->get_results("DESCRIBE {$table};");
820 $iteration = false;
821 $parent_id = false;
822 $export_post_type = false;
823 $created_at = false;
824
825 // Check if field exists
826 foreach ($tablefields as $tablefield) {
827 if ('iteration' == $tablefield->Field) $iteration = true;
828 if ('parent_id' == $tablefield->Field) $parent_id = true;
829 if ('export_post_type' == $tablefield->Field) $export_post_type = true;
830 if ('created_at' == $tablefield->Field) $created_at = true;
831 }
832
833 if ( ! $iteration ){
834 $wpdb->query("ALTER TABLE {$table} ADD `iteration` BIGINT(20) NOT NULL DEFAULT 0;");
835 }
836 if ( ! $parent_id ){
837 $wpdb->query("ALTER TABLE {$table} ADD `parent_id` BIGINT(20) NOT NULL DEFAULT 0;");
838 }
839 if ( ! $export_post_type ){
840 $wpdb->query("ALTER TABLE {$table} ADD `export_post_type` TEXT NOT NULL DEFAULT '';");
841 }
842
843 if ( ! $created_at ){
844 $wpdb->query("ALTER TABLE {$table} ADD `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP;");
845 $wpdb->query("UPDATE {$table} SET `created_at` = `registered_on` WHERE 1");
846 }
847
848
849 update_option( "wp_all_export_free_db_version", PMXE_VERSION );
850 }
851
852 /**
853 * Determine is current export was created before current version
854 */
855 public static function isExistingExport( $checkVersion = false ){
856
857 $input = new PMXE_Input();
858 $export_id = $input->get('id', 0);
859
860 if (empty($export_id)) $export_id = $input->get('export_id', 0);
861
862 // ID not found means this is new export
863 if (empty($export_id)) return false;
864
865 if ( ! $checkVersion ) $checkVersion = PMXE_VERSION;
866
867 $export = new PMXE_Export_Record();
868 $export->getById($export_id);
869 if ( ! $export->isEmpty() && (empty($export->options['created_at_version']) || version_compare($export->options['created_at_version'], $checkVersion) < 0 )){
870 return true;
871 }
872
873 return false;
874 }
875
876 /**
877 * Determine is current export is first time running
878 */
879 public static function isNewExport(){
880
881 $input = new PMXE_Input();
882 $export_id = $input->get('id', 0);
883
884 if (empty($export_id)) $export_id = $input->get('export_id', 0);
885
886 if (empty($export_id)) $export_id = XmlExportEngine::$exportID;
887
888 // ID not found means this is new export
889 if (empty($export_id)) return true;
890
891 $export = new PMXE_Export_Record();
892 $export->getById($export_id);
893 if ( ! $export->isEmpty() && ! $export->iteration ){
894 return true;
895 }
896
897 return false;
898 }
899
900 /**
901 * Method returns default import options, main utility of the method is to avoid warnings when new
902 * option is introduced but already registered imports don't have it
903 */
904 public static function get_default_import_options() {
905 return array(
906 'cpt' => array(),
907 'whereclause' => '',
908 'joinclause' => '',
909 'filter_rules_hierarhy' => '',
910 'product_matching_mode' => 'parent',
911 'order_item_per_row' => 1,
912 'order_item_fill_empty_columns' => 1,
913 'filepath' => '',
914 'current_filepath' => '',
915 'bundlepath' => '',
916 'export_type' => 'specific',
917 'wp_query' => '',
918 'wp_query_selector' => 'wp_query',
919 'is_user_export' => false,
920 'is_comment_export' => false,
921 'export_to' => 'csv',
922 'export_to_sheet' => 'csv',
923 'delimiter' => ',',
924 'encoding' => 'UTF-8',
925 'is_generate_templates' => 1,
926 'is_generate_import' => 1,
927 'import_id' => 0,
928 'template_name' => '',
929 'is_scheduled' => 0,
930 'scheduled_period' => '',
931 'scheduled_email' => '',
932 'cc_label' => array(),
933 'cc_type' => array(),
934 'cc_value' => array(),
935 'cc_name' => array(),
936 'cc_php' => array(),
937 'cc_code' => array(),
938 'cc_sql' => array(),
939 'cc_options' => array(),
940 'cc_settings' => array(),
941 'friendly_name' => '',
942 'fields' => array('default', 'other', 'cf', 'cats'),
943 'ids' => array(),
944 'rules' => array(),
945 'records_per_iteration' => 50,
946 'include_bom' => 1,
947 'include_functions' => 1,
948 'split_large_exports' => 0,
949 'split_large_exports_count' => 10000,
950 'split_files_list' => array(),
951 'main_xml_tag' => 'data',
952 'record_xml_tag' => 'post',
953 'save_template_as' => 0,
954 'name' => '',
955 'export_only_new_stuff' => 0,
956 'export_only_modified_stuff' => 0,
957 'creata_a_new_export_file' => 0,
958 'attachment_list' => array(),
959 'order_include_poducts' => 0,
960 'order_include_all_poducts' => 0,
961 'order_include_coupons' => 0,
962 'order_include_all_coupons' => 0,
963 'order_include_customers' => 0,
964 'order_include_all_customers' => 0,
965 'migration' => '',
966 'xml_template_type' => 'simple',
967 'custom_xml_template' => '',
968 'custom_xml_template_header' => '',
969 'custom_xml_template_loop' => '',
970 'custom_xml_template_footer' => '',
971 'custom_xml_template_options' => array(),
972 'custom_xml_cdata_logic' => 'auto',
973 'show_cdata_in_preview' => 0,
974 'taxonomy_to_export' => '',
975 'created_at_version' => '',
976 'export_variations' => XmlExportEngine::VARIABLE_PRODUCTS_EXPORT_PARENT_AND_VARIATION,
977 'export_variations_title' => XmlExportEngine::VARIATION_USE_PARENT_TITLE,
978 'include_header_row' => 1,
979 'wpml_lang' => 'all',
980 'enable_export_scheduling' => 'false',
981 'scheduling_enable' => false,
982 'scheduling_weekly_days' => '',
983 'scheduling_run_on' => 'weekly',
984 'scheduling_monthly_day' => '',
985 'scheduling_times' => array(),
986 'scheduling_timezone' => 'UTC',
987 'sub_post_type_to_export' => ''
988
989 );
990 }
991
992 public static function is_ajax(){
993 return (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && !empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') ? true : false ;
994 }
995
996 /**
997 * @param $value
998 * @return string
999 */
1000 public static function encode($value){
1001 $salt = defined('AUTH_SALT') ? AUTH_SALT : wp_salt();
1002 return base64_encode(md5($salt) . $value . md5(md5($salt)));
1003 }
1004
1005 /**
1006 * @param $encoded
1007 * @return mixed
1008 */
1009 public static function decode($encoded){
1010 $salt = defined('AUTH_SALT') ? AUTH_SALT : wp_salt();
1011 return preg_match('/^[a-f0-9]{32}$/', $encoded) ? $encoded : str_replace(array(md5($salt), md5(md5($salt))), '', base64_decode($encoded));
1012 }
1013
1014 /**
1015 * Replace last occurence of string
1016 * Used in autoloader, that's not muved in string class
1017 *
1018 * @param $search
1019 * @param $replace
1020 * @param $subject
1021 * @return mixed
1022 */
1023 private function lreplace($search, $replace, $subject){
1024 $pos = strrpos($subject, $search);
1025 if($pos !== false){
1026 $subject = substr_replace($subject, $replace, $pos, strlen($search));
1027 }
1028 return $subject;
1029 }
1030
1031 public static function hposEnabled()
1032 {
1033 return class_exists('Automattic\WooCommerce\Utilities\OrderUtil') && \Automattic\WooCommerce\Utilities\OrderUtil::custom_orders_table_usage_is_enabled();
1034 }
1035 }
1036
1037 PMXE_Plugin::getInstance();
1038
1039 // Include the api front controller
1040 include_once('wpae_api.php');
1041
1042 }
1043
1044