Below is a differential between the unpatched vulnerable code and the patched update, for reference.
--- a/wp-all-import/acc/breakdance-promo.php
+++ b/wp-all-import/acc/breakdance-promo.php
@@ -2,16 +2,18 @@
namespace WPAIBreakdance;
+if ( ! defined( 'ABSPATH' ) ) exit;
+
function echoPromoCard() {
$promoCardStyles = file_get_contents(__DIR__ . "/promo-card-styles.css");
?>
<style>
- <?php echo $promoCardStyles; ?>
+ <?php echo $promoCardStyles; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Static CSS loaded from local trusted file. ?>
</style>
<div class='wpai-breakdance-promo-card'>
-
+
<div class='promo-breakdance-logo-wrapper'>
- <a href='https://breakdance.com/' target='_blank'><img class='promo-breakdance-logo' width='120' src='<?php echo WP_ALL_IMPORT_ROOT_URL . '/acc/breakdance-logo-black.png'; ?>' /></a>
+ <a href='https://breakdance.com/' target='_blank'><img class='promo-breakdance-logo' width='120' src='<?php echo esc_url( WP_ALL_IMPORT_ROOT_URL . '/acc/breakdance-logo-black.png' ); ?>' /></a>
</div>
<div class='promo-heading'>The Ultimate Visual Website Builder for WordPress</div>
--- a/wp-all-import/actions/add_attachment.php
+++ b/wp-all-import/actions/add_attachment.php
@@ -1,4 +1,5 @@
<?php
+if ( ! defined( 'ABSPATH' ) ) exit;
/**
* Fires once an attachment has been added.
*
--- a/wp-all-import/actions/admin_head.php
+++ b/wp-all-import/actions/admin_head.php
@@ -1,4 +1,5 @@
<?php
+if ( ! defined( 'ABSPATH' ) ) exit;
function pmxi_admin_head(){
?>
<style type="text/css">
@@ -32,9 +33,9 @@
?>
<script type="text/javascript">
- var ajaxurl = '<?php echo admin_url( "admin-ajax.php" ); ?>';
+ var ajaxurl = '<?php echo esc_url( admin_url( "admin-ajax.php" ) ); ?>';
var import_action = '<?php echo esc_js($get_params["action"]); ?>';
- var wp_all_import_security = '<?php echo wp_unslash($wp_all_import_ajax_nonce); ?>';
+ var wp_all_import_security = '<?php echo esc_js(wp_unslash($wp_all_import_ajax_nonce)); ?>';
</script>
<?php
}
No newline at end of file
--- a/wp-all-import/actions/admin_init.php
+++ b/wp-all-import/actions/admin_init.php
@@ -1,7 +1,10 @@
<?php
+if ( ! defined( 'ABSPATH' ) ) exit;
function pmxi_admin_init(){
wp_enqueue_script('wp-all-import-script', WP_ALL_IMPORT_ROOT_URL . '/static/js/wp-all-import.js', array('jquery'), PMXI_VERSION, true);
+ // phpcs:ignore Squiz.PHP.DiscouragedFunctions.Discouraged
@ini_set('mysql.connect_timeout', 300);
+ // phpcs:ignore Squiz.PHP.DiscouragedFunctions.Discouraged
@ini_set('default_socket_timeout', 300);
}
No newline at end of file
--- a/wp-all-import/actions/admin_menu.php
+++ b/wp-all-import/actions/admin_menu.php
@@ -1,4 +1,5 @@
<?php
+if ( ! defined( 'ABSPATH' ) ) exit;
/**
* Register plugin specific admin menu
*/
@@ -11,17 +12,17 @@
if (current_user_can( PMXI_Plugin::$capabilities )) { // admin management options
$wpai_menu = array(
- array('pmxi-admin-import', __('New Import', 'pmxi_plugin')),
- array('pmxi-admin-manage' , __('Manage Imports', 'pmxi_plugin')),
- array('pmxi-admin-settings', __('Settings', 'pmxi_plugin')),
- array('pmxi-admin-help', __('Support', 'pmxi_plugin')),
- array('pmxi-admin-partners', __('Partner Discounts', 'pmxi_plugin')),
- array('pmxi-admin-history', __('History', 'pmxi_plugin')),
+ array('pmxi-admin-import', __('New Import', 'wp-all-import')),
+ array('pmxi-admin-manage' , __('Manage Imports', 'wp-all-import')),
+ array('pmxi-admin-settings', __('Settings', 'wp-all-import')),
+ array('pmxi-admin-help', __('Support', 'wp-all-import')),
+ array('pmxi-admin-partners', __('Partner Discounts', 'wp-all-import')),
+ array('pmxi-admin-history', __('History', 'wp-all-import')),
);
$wpai_menu = apply_filters('pmxi_admin_menu', $wpai_menu);
- add_menu_page(__('WP All Import', 'wp_all_import_plugin'), __('All Import', 'wp_all_import_plugin'), PMXI_Plugin::$capabilities, 'pmxi-admin-home', array(PMXI_Plugin::getInstance(), 'adminDispatcher'), 'data:image/svg+xml;base64,' . $icon_base64, 112);
+ add_menu_page(__('WP All Import', 'wp-all-import'), __('All Import', 'wp-all-import'), PMXI_Plugin::$capabilities, 'pmxi-admin-home', array(PMXI_Plugin::getInstance(), 'adminDispatcher'), 'data:image/svg+xml;base64,' . $icon_base64, 112);
// workaround to rename 1st option to `Home`
$submenu['pmxi-admin-home'] = array();
--- a/wp-all-import/actions/admin_notices.php
+++ b/wp-all-import/actions/admin_notices.php
@@ -1,14 +1,19 @@
<?php
+if ( ! defined( 'ABSPATH' ) ) exit;
function pmxi_admin_notices() {
// compare woocommerce add-on version
if ( class_exists( 'PMWI_Plugin' ) and ( defined('PMWI_VERSION') and version_compare(PMWI_VERSION, '2.1.4') < 0 and PMWI_EDITION == 'paid' or defined('PMWI_FREE_VERSION') and version_compare(PMWI_FREE_VERSION, '1.2.2') < 0 and PMWI_EDITION == 'free') ) {
?>
<div class="error"><p>
- <?php printf(
- __('<b>%s Plugin</b>: Please update your WP All Import WooCommerce add-on to the latest version', 'wp_all_import_plugin'),
- PMWI_Plugin::getInstance()->getName()
+ <?php echo wp_kses(
+ sprintf(
+ /* translators: %s: plugin name */
+ __('<b>%s Plugin</b>: Please update your WP All Import WooCommerce add-on to the latest version', 'wp-all-import'),
+ esc_html(PMWI_Plugin::getInstance()->getName())
+ ),
+ array('b' => array())
) ?>
</p></div>
<?php
@@ -33,9 +38,13 @@
if ( class_exists( 'PMAI_Plugin' ) and defined('PMAI_VERSION') and version_compare(PMAI_VERSION, '3.0.0-beta1') < 0 and PMAI_EDITION == 'paid' ) {
?>
<div class="error"><p>
- <?php printf(
- __('<b>%s Plugin</b>: Please update your WP All Import ACF add-on to the latest version', 'wp_all_import_plugin'),
- PMAI_Plugin::getInstance()->getName()
+ <?php echo wp_kses(
+ sprintf(
+ /* translators: %s: plugin name */
+ __('<b>%s Plugin</b>: Please update your WP All Import ACF add-on to the latest version', 'wp-all-import'),
+ esc_html(PMAI_Plugin::getInstance()->getName())
+ ),
+ array('b' => array())
) ?>
</p></div>
<?php
@@ -50,9 +59,13 @@
if ( class_exists( 'PMLCA_Plugin' ) and defined('PMLCA_VERSION') and version_compare(PMLCA_VERSION, '1.0.0-beta1') < 0 and PMLCA_EDITION == 'paid' ) {
?>
<div class="error"><p>
- <?php printf(
- __('<b>%s Plugin</b>: Please update your WP All Import Linkcloak add-on to the latest version', 'wp_all_import_plugin'),
- PMLCA_Plugin::getInstance()->getName()
+ <?php echo wp_kses(
+ sprintf(
+ /* translators: %s: plugin name */
+ __('<b>%s Plugin</b>: Please update your WP All Import Linkcloak add-on to the latest version', 'wp-all-import'),
+ esc_html(PMLCA_Plugin::getInstance()->getName())
+ ),
+ array('b' => array())
) ?>
</p></div>
<?php
@@ -67,9 +80,13 @@
if ( class_exists( 'PMUI_Plugin' ) and defined('PMUI_VERSION') and version_compare(PMUI_VERSION, '1.0.0-beta1') < 0 and PMUI_EDITION == 'paid' ) {
?>
<div class="error"><p>
- <?php printf(
- __('<b>%s Plugin</b>: Please update your WP All Import User add-on to the latest version', 'wp_all_import_plugin'),
- PMUI_Plugin::getInstance()->getName()
+ <?php echo wp_kses(
+ sprintf(
+ /* translators: %s: plugin name */
+ __('<b>%s Plugin</b>: Please update your WP All Import User add-on to the latest version', 'wp-all-import'),
+ esc_html(PMUI_Plugin::getInstance()->getName())
+ ),
+ array('b' => array())
) ?>
</p></div>
<?php
@@ -84,9 +101,13 @@
if ( class_exists( 'PMLI_Plugin' ) and defined('PMLI_VERSION') and version_compare(PMLI_VERSION, '1.0.0-beta1') < 0 and PMLI_EDITION == 'paid' ) {
?>
<div class="error"><p>
- <?php printf(
- __('<b>%s Plugin</b>: Please update your WP All Import WPML add-on to the latest version', 'wp_all_import_plugin'),
- PMLI_Plugin::getInstance()->getName()
+ <?php echo wp_kses(
+ sprintf(
+ /* translators: %s: plugin name */
+ __('<b>%s Plugin</b>: Please update your WP All Import WPML add-on to the latest version', 'wp-all-import'),
+ esc_html(PMLI_Plugin::getInstance()->getName())
+ ),
+ array('b' => array())
) ?>
</p></div>
<?php
@@ -114,16 +135,16 @@
foreach ($warnings as $code) {
switch ($code) {
case 1:
- $m = __('<strong>Warning:</strong> your title is blank.', 'wp_all_import_plugin');
+ $m = __('<strong>Warning:</strong> your title is blank.', 'wp-all-import');
break;
case 2:
- $m = __('<strong>Warning:</strong> your content is blank.', 'wp_all_import_plugin');
+ $m = __('<strong>Warning:</strong> your content is blank.', 'wp-all-import');
break;
case 3:
- $m = __('<strong>Warning:</strong> You must <a href="https://www.wpallimport.com/checkout/?edd_action=add_to_cart&download_id=5839966&edd_options%5Bprice_id%5D=1&discount=welcome-upgrade-99&utm_source=import-plugin-free&utm_medium=upgrade-notice&utm_campaign=images" target="_blank">upgrade to the Pro edition of WP All Import</a> to import images. The settings you configured in the images section will be ignored.', 'wp_all_import_plugin');
+ $m = __('<strong>Warning:</strong> You must <a href="https://www.wpallimport.com/checkout/?edd_action=add_to_cart&download_id=5839966&edd_options%5Bprice_id%5D=1&discount=welcome-upgrade-99&utm_source=import-plugin-free&utm_medium=upgrade-notice&utm_campaign=images" target="_blank">upgrade to the Pro edition of WP All Import</a> to import images. The settings you configured in the images section will be ignored.', 'wp-all-import');
break;
case 4:
- $m = __('<strong>Warning:</strong> You must <a href="https://www.wpallimport.com/checkout/?edd_action=add_to_cart&download_id=5839966&edd_options%5Bprice_id%5D=1&discount=welcome-upgrade-99&utm_source=import-plugin-free&utm_medium=upgrade-notice&utm_campaign=custom-fields" target="_blank">upgrade to the Pro edition of WP All Import</a> to import data to Custom Fields. The settings you configured in the Custom Fields section will be ignored.', 'wp_all_import_plugin');
+ $m = __('<strong>Warning:</strong> You must <a href="https://www.wpallimport.com/checkout/?edd_action=add_to_cart&download_id=5839966&edd_options%5Bprice_id%5D=1&discount=welcome-upgrade-99&utm_source=import-plugin-free&utm_medium=upgrade-notice&utm_campaign=custom-fields" target="_blank">upgrade to the Pro edition of WP All Import</a> to import data to Custom Fields. The settings you configured in the Custom Fields section will be ignored.', 'wp-all-import');
break;
default:
$m = false;
--- a/wp-all-import/actions/attachment_updated.php
+++ b/wp-all-import/actions/attachment_updated.php
@@ -1,4 +1,5 @@
<?php
+if ( ! defined( 'ABSPATH' ) ) exit;
/**
* Fires once an existing attachment has been updated.
*
--- a/wp-all-import/actions/delete_post.php
+++ b/wp-all-import/actions/delete_post.php
@@ -1,5 +1,6 @@
<?php
+if ( ! defined( 'ABSPATH' ) ) exit;
function pmxi_delete_post($post_id) {
if (!empty($post_id) && is_numeric($post_id)){
$post = new PMXI_Post_Record();
--- a/wp-all-import/actions/pmxi_after_xml_import.php
+++ b/wp-all-import/actions/pmxi_after_xml_import.php
@@ -1,4 +1,5 @@
<?php
+if ( ! defined( 'ABSPATH' ) ) exit;
function pmxi_pmxi_after_xml_import( $import_id, $import )
{
if ($import->options['custom_type'] == 'taxonomies') {
@@ -49,6 +50,7 @@
}
$recount_terms_after_import = TRUE;
+ // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
$recount_terms_after_import = apply_filters('wp_all_import_recount_terms_after_import', $recount_terms_after_import, $import_id);
if ($recount_terms_after_import) {
// Update term count after import process is complete.
@@ -76,6 +78,7 @@
// Re-count post comments.
if ( in_array($import->options['custom_type'], array('comments', 'woo_reviews')) ) {
$recount_comments_after_import = TRUE;
+ // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
$recount_comments_after_import = apply_filters('wp_all_import_recount_comments_after_import', $recount_comments_after_import, $import_id);
if ($recount_comments_after_import) {
$comment_posts = get_option('wp_all_import_comment_posts_' . $import_id);
--- a/wp-all-import/actions/pmxi_before_xml_import.php
+++ b/wp-all-import/actions/pmxi_before_xml_import.php
@@ -1,4 +1,5 @@
<?php
+if ( ! defined( 'ABSPATH' ) ) exit;
function pmxi_pmxi_before_xml_import( $import_id ) {
delete_option('wp_all_import_taxonomies_hierarchy_' . $import_id);
@@ -13,7 +14,8 @@
$current_hash = get_option('_wp_all_import_functions_hash_' . $import_id, false);
if ($functions_hash !== $current_hash) {
global $wpdb;
- $wpdb->query( 'DELETE FROM ' . $wpdb->prefix . 'pmxi_hash WHERE import_id = ' . $import_id );
+ // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
+ $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}pmxi_hash WHERE import_id = %d", (int) $import_id ) );
update_option('_wp_all_import_functions_hash_' . $import_id, $functions_hash, false);
}
}
--- a/wp-all-import/actions/pmxi_extend_options_custom_fields.php
+++ b/wp-all-import/actions/pmxi_extend_options_custom_fields.php
@@ -1,4 +1,5 @@
<?php
+if ( ! defined( 'ABSPATH' ) ) exit;
/**
* @param $post_type
* @param $post
@@ -25,6 +26,7 @@
$groups = acf_get_local_field_groups();
}
} else {
+ // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
$groups = apply_filters('acf/get_field_groups', array());
}
--- a/wp-all-import/actions/wp_ajax_auto_detect_cf.php
+++ b/wp-all-import/actions/wp_ajax_auto_detect_cf.php
@@ -1,12 +1,13 @@
<?php
+if ( ! defined( 'ABSPATH' ) ) exit;
function pmxi_wp_ajax_auto_detect_cf(){
if ( ! check_ajax_referer( 'wp_all_import_secure', 'security', false )){
- exit( json_encode(array('result' => array(), 'msg' => __('Security check', 'wp_all_import_plugin'))) );
+ exit( json_encode(array('result' => array(), 'msg' => __('Security check', 'wp-all-import'))) );
}
if ( ! current_user_can( PMXI_Plugin::$capabilities ) ){
- exit( json_encode(array('result' => array(), 'msg' => __('Security check', 'wp_all_import_plugin'))) );
+ exit( json_encode(array('result' => array(), 'msg' => __('Security check', 'wp-all-import'))) );
}
$input = new PMXI_Input();
@@ -34,6 +35,7 @@
$fields = $input->post('fields', array());
break;
default:
+ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.NotPrepared
$results = $wpdb->get_results($wpdb->prepare("SELECT DISTINCT meta_key FROM ". $table_prefix ."posts, ". $table_prefix ."postmeta WHERE post_type = %s AND ". $table_prefix ."posts.ID = ". $table_prefix ."postmeta.post_id", $post_type), ARRAY_A);
if (!empty($results) && !is_wp_error($results)){
foreach ($results as $key => $value) {
@@ -51,25 +53,19 @@
switch ($post_type){
case 'import_users':
case 'shop_customer':
- $values = $wpdb->get_results("
- SELECT DISTINCT usermeta.meta_value
- FROM ".$wpdb->usermeta." as usermeta
- WHERE usermeta.meta_key='".$field."'
- ", ARRAY_A);
+ // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
+ $values = $wpdb->get_results($wpdb->prepare("SELECT DISTINCT usermeta.meta_value FROM {$wpdb->usermeta} as usermeta WHERE usermeta.meta_key = %s", $field), ARRAY_A);
+ // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
break;
case 'taxonomies':
- $values = $wpdb->get_results("
- SELECT DISTINCT termmeta.meta_value
- FROM ".$wpdb->termmeta." as termmeta
- WHERE termmeta.meta_key='".$field."'
- ", ARRAY_A);
+ // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
+ $values = $wpdb->get_results($wpdb->prepare("SELECT DISTINCT termmeta.meta_value FROM {$wpdb->termmeta} as termmeta WHERE termmeta.meta_key = %s", $field), ARRAY_A);
+ // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
break;
default:
- $values = $wpdb->get_results("
- SELECT DISTINCT postmeta.meta_value
- FROM ".$wpdb->postmeta." as postmeta
- WHERE postmeta.meta_key='".$field."'
- ", ARRAY_A);
+ // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
+ $values = $wpdb->get_results($wpdb->prepare("SELECT DISTINCT postmeta.meta_value FROM {$wpdb->postmeta} as postmeta WHERE postmeta.meta_key = %s", $field), ARRAY_A);
+ // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
break;
}
@@ -99,18 +95,21 @@
case 'taxonomies':
$custom_type = new stdClass();
$custom_type->labels = new stdClass();
- $custom_type->labels->singular_name = __('Taxonomy Term', 'wp_all_import_plugin');
+ $custom_type->labels->singular_name = __('Taxonomy Term', 'wp-all-import');
break;
default:
$custom_type = get_post_type_object( $post_type );
break;
}
- $msg = sprintf(__('No Custom Fields are present in your database for %s', 'wp_all_import_plugin'), esc_attr($custom_type->labels->name));
+ /* translators: see placeholders in the string below */
+ $msg = sprintf(__('No Custom Fields are present in your database for %s', 'wp-all-import'), esc_attr($custom_type->labels->name));
}
elseif (count($result) === 1)
- $msg = sprintf(__('%s field was automatically detected.', 'wp_all_import_plugin'), count($result));
+ /* translators: see placeholders in the string below */
+ $msg = sprintf(__('%s field was automatically detected.', 'wp-all-import'), count($result));
else{
- $msg = sprintf(__('%s fields were automatically detected.', 'wp_all_import_plugin'), count($result));
+ /* translators: see placeholders in the string below */
+ $msg = sprintf(__('%s fields were automatically detected.', 'wp-all-import'), count($result));
}
exit( json_encode(array('result' => $result, 'msg' => $msg)) );
--- a/wp-all-import/actions/wp_ajax_auto_detect_sf.php
+++ b/wp-all-import/actions/wp_ajax_auto_detect_sf.php
@@ -1,12 +1,13 @@
<?php
+if ( ! defined( 'ABSPATH' ) ) exit;
function pmxi_wp_ajax_auto_detect_sf(){
if ( ! check_ajax_referer( 'wp_all_import_secure', 'security', false )){
- exit( json_encode(array('result' => array(), 'msg' => __('Security check', 'wp_all_import_plugin'))) );
+ exit( json_encode(array('result' => array(), 'msg' => __('Security check', 'wp-all-import'))) );
}
if ( ! current_user_can( PMXI_Plugin::$capabilities ) ){
- exit( json_encode(array('result' => array(), 'msg' => __('Security check', 'wp_all_import_plugin'))) );
+ exit( json_encode(array('result' => array(), 'msg' => __('Security check', 'wp-all-import'))) );
}
$input = new PMXI_Input();
@@ -21,25 +22,19 @@
switch ($post_type){
case 'import_users':
case 'shop_customer':
- $values = $wpdb->get_results("
- SELECT DISTINCT usermeta.meta_value
- FROM ".$wpdb->usermeta." as usermeta
- WHERE usermeta.meta_key='".$fieldName."'
- ", ARRAY_A);
+ // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
+ $values = $wpdb->get_results($wpdb->prepare("SELECT DISTINCT usermeta.meta_value FROM {$wpdb->usermeta} as usermeta WHERE usermeta.meta_key = %s", $fieldName), ARRAY_A);
+ // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
break;
case 'taxonomies':
- $values = $wpdb->get_results("
- SELECT DISTINCT termmeta.meta_value
- FROM ".$wpdb->termmeta." as termmeta
- WHERE termmeta.meta_key='".$fieldName."'
- ", ARRAY_A);
+ // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
+ $values = $wpdb->get_results($wpdb->prepare("SELECT DISTINCT termmeta.meta_value FROM {$wpdb->termmeta} as termmeta WHERE termmeta.meta_key = %s", $fieldName), ARRAY_A);
+ // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
break;
default:
- $values = $wpdb->get_results("
- SELECT DISTINCT postmeta.meta_value
- FROM ".$wpdb->postmeta." as postmeta
- WHERE postmeta.meta_key='".$fieldName."'
- ", ARRAY_A);
+ // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
+ $values = $wpdb->get_results($wpdb->prepare("SELECT DISTINCT postmeta.meta_value FROM {$wpdb->postmeta} as postmeta WHERE postmeta.meta_key = %s", $fieldName), ARRAY_A);
+ // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
break;
}
--- a/wp-all-import/actions/wp_ajax_delete_import.php
+++ b/wp-all-import/actions/wp_ajax_delete_import.php
@@ -1,12 +1,13 @@
<?php
+if ( ! defined( 'ABSPATH' ) ) exit;
function pmxi_wp_ajax_delete_import(){
if ( ! check_ajax_referer( 'wp_all_import_secure', 'security', false )){
- exit( json_encode(array('result' => false, 'msg' => __('Security check', 'wp_all_import_plugin'))) );
+ exit( json_encode(array('result' => false, 'msg' => __('Security check', 'wp-all-import'))) );
}
if ( ! current_user_can( PMXI_Plugin::$capabilities ) ){
- exit( json_encode(array('result' => false, 'msg' => __('Security check', 'wp_all_import_plugin'))) );
+ exit( json_encode(array('result' => false, 'msg' => __('Security check', 'wp-all-import'))) );
}
$input = new PMXI_Input();
@@ -36,11 +37,11 @@
case 'import_users':
$custom_type = new stdClass();
- $custom_type->label = __('Users', 'wp_all_import_plugin');
+ $custom_type->label = __('Users', 'wp-all-import');
break;
case 'shop_customer':
$custom_type = new stdClass();
- $custom_type->label = __('Customers', 'wp_all_import_plugin');
+ $custom_type->label = __('Customers', 'wp-all-import');
break;
default:
$custom_type = get_post_type_object( $import['options']['custom_type'] );
@@ -53,13 +54,15 @@
}
if ( $params['is_delete_import'] and ! $params['is_delete_posts'] ) {
- $response['redirect'] = esc_url_raw(add_query_arg('pmxi_nt', urlencode(__('Import deleted', 'wp_all_import_plugin')), $params['base_url']));
+ $response['redirect'] = esc_url_raw(add_query_arg('pmxi_nt', urlencode(__('Import deleted', 'wp-all-import')), $params['base_url']));
} elseif( ! $params['is_delete_import'] and $params['is_delete_posts']) {
- $response['redirect'] = esc_url_raw(add_query_arg('pmxi_nt', urlencode(sprintf(__('All associated %s deleted.', 'wp_all_import_plugin'), $cpt_name)), $params['base_url']));
+ /* translators: see placeholders in the string below */
+ $response['redirect'] = esc_url_raw(add_query_arg('pmxi_nt', urlencode(sprintf(__('All associated %s deleted.', 'wp-all-import'), $cpt_name)), $params['base_url']));
} elseif( $params['is_delete_import'] and $params['is_delete_posts']) {
- $response['redirect'] = esc_url_raw(add_query_arg('pmxi_nt', urlencode(sprintf(__('Import and all associated %s deleted.', 'wp_all_import_plugin'), $cpt_name)), $params['base_url']));
+ /* translators: see placeholders in the string below */
+ $response['redirect'] = esc_url_raw(add_query_arg('pmxi_nt', urlencode(sprintf(__('Import and all associated %s deleted.', 'wp-all-import'), $cpt_name)), $params['base_url']));
} else {
- $response['redirect'] = esc_url_raw(add_query_arg('pmxi_nt', urlencode(__('Nothing to delete.', 'wp_all_import_plugin')), $params['base_url']));
+ $response['redirect'] = esc_url_raw(add_query_arg('pmxi_nt', urlencode(__('Nothing to delete.', 'wp-all-import')), $params['base_url']));
exit( json_encode( $response ));
}
@@ -77,7 +80,8 @@
$is_all_records_deleted = $import->deletePostsAjax( ! $params['is_delete_posts'], $params['is_delete_images'], $params['is_delete_attachments'] );
$response['result'] = (empty($params['import_ids'][$key + 1])) ? $is_all_records_deleted : false;
- $response['msg'] = sprintf(__('Import #%d - %d records deleted', 'wp_all_import_plugin'), intval($import->id), intval($import->deleted));
+ /* translators: see placeholders in the string below */
+ $response['msg'] = sprintf(__('Import #%1$d - %2$d records deleted', 'wp-all-import'), intval($import->id), intval($import->deleted));
if ( $is_all_records_deleted === true )
{
--- a/wp-all-import/actions/wp_ajax_dismiss_notifications.php
+++ b/wp-all-import/actions/wp_ajax_dismiss_notifications.php
@@ -1,12 +1,13 @@
<?php
+if ( ! defined( 'ABSPATH' ) ) exit;
function pmxi_wp_ajax_dismiss_notifications(){
if ( ! check_ajax_referer( 'wp_all_import_secure', 'security', false )){
- exit( json_encode(array('result' => false, 'msg' => __('Security check', 'wp_all_import_plugin'))) );
+ exit( json_encode(array('result' => false, 'msg' => __('Security check', 'wp-all-import'))) );
}
if ( ! current_user_can( PMXI_Plugin::$capabilities ) ){
- exit( json_encode(array('result' => false, 'msg' => __('Security check', 'wp_all_import_plugin'))) );
+ exit( json_encode(array('result' => false, 'msg' => __('Security check', 'wp-all-import'))) );
}
if (isset($_POST['addon']) ) {
--- a/wp-all-import/actions/wp_ajax_import_failed.php
+++ b/wp-all-import/actions/wp_ajax_import_failed.php
@@ -1,12 +1,13 @@
<?php
+if ( ! defined( 'ABSPATH' ) ) exit;
function pmxi_wp_ajax_import_failed(){
if ( ! check_ajax_referer( 'wp_all_import_secure', 'security', false )){
- exit( json_encode(array('result' => false, 'msg' => __('Security check', 'wp_all_import_plugin'))) );
+ exit( json_encode(array('result' => false, 'msg' => __('Security check', 'wp-all-import'))) );
}
if ( ! current_user_can( PMXI_Plugin::$capabilities ) ){
- exit( json_encode(array('result' => false, 'msg' => __('Security check', 'wp_all_import_plugin'))) );
+ exit( json_encode(array('result' => false, 'msg' => __('Security check', 'wp-all-import'))) );
}
$result = false;
@@ -16,9 +17,9 @@
if ( ! $import->isEmpty()) {
$import->set(array(
'executing' => 0,
- 'last_activity' => date('Y-m-d H:i:s'),
+ 'last_activity' => gmdate('Y-m-d H:i:s'),
'failed' => 1,
- 'failed_on' => date('Y-m-d H:i:s')
+ 'failed_on' => gmdate('Y-m-d H:i:s')
))->save();
$result = true;
do_action('pmxi_import_failed', intval($_POST['id']));
--- a/wp-all-import/actions/wp_ajax_test_images.php
+++ b/wp-all-import/actions/wp_ajax_test_images.php
@@ -1,13 +1,14 @@
<?php
+if ( ! defined( 'ABSPATH' ) ) exit;
function pmxi_wp_ajax_test_images(){
if ( ! check_ajax_referer( 'wp_all_import_secure', 'security', false )){
- exit( json_encode(array('result' => array(), 'failed_msgs' => array(__('Security check', 'wp_all_import_plugin')))));
+ exit( json_encode(array('result' => array(), 'failed_msgs' => array(__('Security check', 'wp-all-import')))));
}
if ( ! current_user_can( PMXI_Plugin::$capabilities ) ){
- exit( json_encode(array('result' => array(), 'failed_msgs' => array(__('Security check', 'wp_all_import_plugin')))));
+ exit( json_encode(array('result' => array(), 'failed_msgs' => array(__('Security check', 'wp-all-import')))));
}
$input = new PMXI_Input();
@@ -27,9 +28,11 @@
$failed_msgs = array();
+ // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_is_writable
if ( ! @is_writable($targetDir) )
{
- $failed_msgs[] = sprintf(__('Uploads folder `%s` is not writable.', 'wp_all_import_plugin'), $targetDir);
+ /* translators: see placeholders in the string below */
+ $failed_msgs[] = sprintf(__('Uploads folder `%s` is not writable.', 'wp-all-import'), $targetDir);
}
else
{
@@ -43,28 +46,33 @@
foreach ($post['imgs'] as $img)
{
if ( preg_match('%^(http|https|ftp|ftps)%i', $img)){
- $failed_msgs[] = sprintf(__('Use image name instead of URL `%s`.', 'wp_all_import_plugin'), esc_url($img));
+ /* translators: see placeholders in the string below */
+ $failed_msgs[] = sprintf(__('Use image name instead of URL `%s`.', 'wp-all-import'), esc_url($img));
continue;
}
if ( @file_exists($imgs_basedir . $img) ){
if (@is_readable($imgs_basedir . $img)){
$success_images++;
} else{
- $failed_msgs[] = sprintf(__('File `%s` isn't readable', 'wp_all_import_plugin'), preg_replace('%.*/wp-content%', '/wp-content', esc_attr($imgs_basedir . $img)));
+ /* translators: see placeholders in the string below */
+ $failed_msgs[] = sprintf(__('File `%s` isn't readable', 'wp-all-import'), preg_replace('%.*/wp-content%', '/wp-content', esc_attr($imgs_basedir . $img)));
}
}
else{
- $failed_msgs[] = sprintf(__('File `%s` doesn't exist', 'wp_all_import_plugin'), preg_replace('%.*/wp-content%', '/wp-content', esc_attr($imgs_basedir . $img)));
+ /* translators: see placeholders in the string below */
+ $failed_msgs[] = sprintf(__('File `%s` doesn't exist', 'wp-all-import'), preg_replace('%.*/wp-content%', '/wp-content', esc_attr($imgs_basedir . $img)));
}
}
}
if ((int)$success_images === 1)
{
- $success_msg = sprintf(__('%d image was successfully retrieved from `%s`', 'wp_all_import_plugin'), intval($success_images), preg_replace('%.*/wp-content%', '/wp-content', esc_attr($wp_uploads['basedir']) . DIRECTORY_SEPARATOR . PMXI_Plugin::FILES_DIRECTORY));
+ /* translators: see placeholders in the string below */
+ $success_msg = sprintf(__('%1$d image was successfully retrieved from `%2$s`', 'wp-all-import'), intval($success_images), preg_replace('%.*/wp-content%', '/wp-content', esc_attr($wp_uploads['basedir']) . DIRECTORY_SEPARATOR . PMXI_Plugin::FILES_DIRECTORY));
}
elseif ((int)$success_images > 1)
{
- $success_msg = sprintf(__('%d images were successfully retrieved from `%s`', 'wp_all_import_plugin'), intval($success_images), preg_replace('%.*/wp-content%', '/wp-content', esc_attr($wp_uploads['basedir']) . DIRECTORY_SEPARATOR . PMXI_Plugin::FILES_DIRECTORY));
+ /* translators: see placeholders in the string below */
+ $success_msg = sprintf(__('%1$d images were successfully retrieved from `%2$s`', 'wp-all-import'), intval($success_images), preg_replace('%.*/wp-content%', '/wp-content', esc_attr($wp_uploads['basedir']) . DIRECTORY_SEPARATOR . PMXI_Plugin::FILES_DIRECTORY));
}
break;
@@ -82,6 +90,7 @@
$img_ext = pmxi_getExtensionFromStr($img);
$default_extension = pmxi_getExtension($bn);
+ // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
$image_name = apply_filters("wp_all_import_image_filename", urldecode(sanitize_file_name((($img_ext) ? str_replace("." . $default_extension, "", $bn) : $bn))) . (("" != $img_ext) ? '.' . $img_ext : ''));
$attch = wp_all_import_get_image_from_gallery($image_name, $targetDir);
@@ -92,18 +101,21 @@
}
else
{
- $failed_msgs[] = sprintf(__('Image `%s` not found in media library.', 'wp_all_import_plugin'), esc_attr($image_name));
+ /* translators: see placeholders in the string below */
+ $failed_msgs[] = sprintf(__('Image `%s` not found in media library.', 'wp-all-import'), esc_attr($image_name));
}
}
}
if ((int)$success_images === 1)
{
- $success_msg = sprintf(__('%d image was successfully found in media gallery', 'wp_all_import_plugin'), intval($success_images));
+ /* translators: see placeholders in the string below */
+ $success_msg = sprintf(__('%d image was successfully found in media gallery', 'wp-all-import'), intval($success_images));
}
elseif ((int)$success_images > 1)
{
- $success_msg = sprintf(__('%d images were successfully found in media gallery', 'wp_all_import_plugin'), intval($success_images));
+ /* translators: see placeholders in the string below */
+ $success_msg = sprintf(__('%d images were successfully found in media gallery', 'wp-all-import'), intval($success_images));
}
break;
@@ -117,7 +129,8 @@
foreach ($post['imgs'] as $img)
{
if ( ! preg_match('%^(http|https|ftp|ftps)%i', $img)){
- $failed_msgs[] = sprintf(__('URL `%s` is not valid.', 'wp_all_import_plugin'), esc_url($img));
+ /* translators: see placeholders in the string below */
+ $failed_msgs[] = sprintf(__('URL `%s` is not valid.', 'wp-all-import'), esc_url($img));
continue;
}
@@ -132,24 +145,28 @@
$get_ctx = stream_context_create(array('http' => array('timeout' => 5)));
if ( (is_wp_error($request) or $request === false) and ! @file_put_contents($image_filepath, @file_get_contents($img, false, $get_ctx))) {
- $failed_msgs[] = (is_wp_error($request)) ? $request->get_error_message() : sprintf(__('File `%s` cannot be saved locally', 'wp_all_import_plugin'), esc_url($img));
+ /* translators: see placeholders in the string below */
+ $failed_msgs[] = (is_wp_error($request)) ? $request->get_error_message() : sprintf(__('File `%s` cannot be saved locally', 'wp-all-import'), esc_url($img));
} elseif( ! ($image_info = apply_filters('pmxi_getimagesize', @getimagesize($image_filepath), $image_filepath)) or ! in_array($image_info[2], wp_all_import_supported_image_types())) {
- $failed_msgs[] = sprintf(__('File `%s` is not a valid image.', 'wp_all_import_plugin'), esc_url($img));
+ /* translators: see placeholders in the string below */
+ $failed_msgs[] = sprintf(__('File `%s` is not a valid image.', 'wp-all-import'), esc_url($img));
} else {
$success_images++;
}
- @unlink($image_filepath);
+ wp_delete_file($image_filepath);
}
}
$time = time() - $start;
if ((int)$success_images === 1)
{
- $success_msg = sprintf(__('%d image was successfully downloaded in %s seconds', 'wp_all_import_plugin'), intval($success_images), number_format($time, 2));
+ /* translators: see placeholders in the string below */
+ $success_msg = sprintf(__('%1$d image was successfully downloaded in %2$s seconds', 'wp-all-import'), intval($success_images), number_format($time, 2));
}
elseif ((int)$success_images > 1)
{
- $success_msg = sprintf(__('%d images were successfully downloaded in %s seconds', 'wp_all_import_plugin'), intval($success_images), number_format($time, 2));
+ /* translators: see placeholders in the string below */
+ $success_msg = sprintf(__('%1$d images were successfully downloaded in %2$s seconds', 'wp-all-import'), intval($success_images), number_format($time, 2));
}
break;
--- a/wp-all-import/actions/wp_ajax_wpai_dismiss_review_modal.php
+++ b/wp-all-import/actions/wp_ajax_wpai_dismiss_review_modal.php
@@ -1,13 +1,14 @@
<?php
+if ( ! defined( 'ABSPATH' ) ) exit;
function pmxi_wp_ajax_wpai_dismiss_review_modal(){
if ( ! check_ajax_referer( 'wp_all_import_secure', 'security', false )){
- exit( json_encode(array('html' => __('Security check', 'wp_all_import_plugin'))) );
+ exit( json_encode(array('html' => __('Security check', 'wp-all-import'))) );
}
if ( ! current_user_can( PMXI_Plugin::$capabilities ) ){
- exit( json_encode(array('html' => __('Security check', 'wp_all_import_plugin'))) );
+ exit( json_encode(array('html' => __('Security check', 'wp-all-import'))) );
}
$reviewLogic = new WpaiReviewsReviewLogic();
--- a/wp-all-import/actions/wp_ajax_wpai_send_feedback.php
+++ b/wp-all-import/actions/wp_ajax_wpai_send_feedback.php
@@ -1,13 +1,14 @@
<?php
+if ( ! defined( 'ABSPATH' ) ) exit;
function pmxi_wp_ajax_wpai_send_feedback(){
if ( ! check_ajax_referer( 'wp_all_import_secure', 'security', false )){
- exit( json_encode(array('html' => __('Security check', 'wp_all_import_plugin'))) );
+ exit( json_encode(array('html' => __('Security check', 'wp-all-import'))) );
}
if ( ! current_user_can( PMXI_Plugin::$capabilities ) ){
- exit( json_encode(array('html' => __('Security check', 'wp_all_import_plugin'))) );
+ exit( json_encode(array('html' => __('Security check', 'wp-all-import'))) );
}
$reviewLogic = new WpaiReviewsReviewLogic();
--- a/wp-all-import/actions/wpmu_new_blog.php
+++ b/wp-all-import/actions/wpmu_new_blog.php
@@ -1,5 +1,6 @@
<?php
+if ( ! defined( 'ABSPATH' ) ) exit;
function pmxi_wpmu_new_blog($blog_id, $user_id, $domain, $path, $site_id, $meta)
{
// create/update required database tables
@@ -18,7 +19,7 @@
// sync data between plugin tables and wordpress (mostly for the case when plugin is reactivated)
$post = new PMXI_Post_Record();
- $wpdb->query('DELETE FROM ' . $post->getTable() . ' WHERE post_id NOT IN (SELECT ID FROM ' . $wpdb->posts . ')');
+ $wpdb->query('DELETE FROM ' . $post->getTable() . ' WHERE post_id NOT IN (SELECT ID FROM ' . $wpdb->posts . ')'); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,PluginCheck.Security.DirectDB.UnescapedDBParameter
switch_to_blog($old_blog);
return;
--- a/wp-all-import/addon-api/autoload.php
+++ b/wp-all-import/addon-api/autoload.php
@@ -0,0 +1,49 @@
+<?php
+
+namespace WpaiAddonAPI;
+
+if ( ! defined( 'ABSPATH' ) ) exit;
+
+require WP_ALL_IMPORT_ROOT_DIR . '/addon-api/classes/helpers.php';
+
+class PMXI_Addon_Autoloader {
+ use Singleton;
+
+ public function __construct() {
+ spl_autoload_register([$this, 'autoload']);
+
+ foreach ($this->modules() as $module) {
+ $module::getInstance();
+ }
+ }
+
+ public function modules() {
+ return [PMXI_Addon_Admin::class, PMXI_Addon_Rest::class];
+ }
+
+ public function loadIfFound(string $path) {
+ $path = WP_ALL_IMPORT_ROOT_DIR . '/addon-api/' . $path . '.php';
+
+ if (file_exists($path)) {
+ require_once $path;
+ }
+ }
+
+ public function autoload($class) {
+ if ( strpos( $class, 'PMXI_Addon_' ) === false ) {
+ return;
+ }
+
+ $parts = explode('\', $class);
+ $className = end($parts);
+ $className = str_replace('PMXI_Addon_', '', $className);
+ $className = str_replace('_', '-', $className);
+ $className = strtolower($className);
+ $className = str_replace('-field', '', $className); // E.g. Rename "text-field" to "text"
+
+ $this->loadIfFound('classes/' . $className);
+ $this->loadIfFound('fields/' . $className);
+ }
+}
+
+PMXI_Addon_Autoloader::getInstance();
--- a/wp-all-import/addon-api/classes/admin.php
+++ b/wp-all-import/addon-api/classes/admin.php
@@ -0,0 +1,117 @@
+<?php
+namespace WpaiAddonAPI;
+
+if ( ! defined( 'ABSPATH' ) ) exit;
+
+class PMXI_Addon_Admin {
+ use Singleton;
+
+ public string $url = WP_ALL_IMPORT_ROOT_URL . '/addon-api';
+
+ public function __construct() {
+ add_action( 'pmxi_extend_options_custom_fields', [ $this, 'render' ], 10, 2 );
+ add_action( 'pmxi_reimport', [ $this, 'renderUpdateScreen' ], 10, 2 );
+ add_action( 'pmxi_confirm_data_to_import', [ $this, 'renderConfirmDataToImport' ], 10, 2 );
+
+ add_action( 'admin_enqueue_scripts', [ $this, 'enqueue' ] );
+ add_filter( 'script_loader_tag', [ $this, 'add_type_attribute' ], 10, 3 );
+ }
+
+ public function enqueue() {
+ // Loaded as an ES module; a stray module on unrelated admin pages
+ // invalidates WordPress core's import map in Firefox (strict ordering),
+ // breaking screens such as the WP 7.0 Connectors page. Only load it on
+ // WP All Import's own pages, where it is used.
+ if ( ! $this->is_wp_all_import_page() ) {
+ return;
+ }
+
+ wp_enqueue_script( 'pmxi-datepicker', $this->url . '/static/vendor/air-datepicker/air-datepicker.min.js', array(), PMXI_VERSION, true );
+ wp_enqueue_style( 'pmxi-datepicker', $this->url . '/static/vendor/air-datepicker/air-datepicker.min.css', array(), PMXI_VERSION );
+
+ wp_enqueue_style( 'pmxi-addon-admin-style', $this->url . '/static/css/admin.css', array(), PMXI_VERSION );
+ wp_enqueue_script( 'pmxi-addon-admin-script', $this->url . '/static/js/admin.js', array(), PMXI_VERSION, true );
+ wp_localize_script( 'pmxi-addon-admin-script', 'pmxiAddon', [
+ 'ajaxUrl' => get_rest_url( null, 'wp-all-import/v1/addon/fields' ),
+ ] );
+ }
+
+ private function is_wp_all_import_page(): bool {
+ // phpcs:ignore WordPress.Security.NonceVerification.Recommended
+ if ( empty( $_GET['page'] ) || ! is_string( $_GET['page'] ) ) {
+ return false;
+ }
+
+ // phpcs:ignore WordPress.Security.NonceVerification.Recommended
+ return strpos( sanitize_key( wp_unslash( $_GET['page'] ) ), 'pmxi-' ) === 0;
+ }
+
+ public function add_type_attribute( $tag, $handle, $src ) {
+ if ( 'pmxi-addon-admin-script' !== $handle ) {
+ return $tag;
+ }
+ $tag = str_replace( ' src', ' type="module" src', $tag );
+
+ return $tag;
+ }
+
+ /**
+ * Render something on the import page
+ *
+ * @param string $type
+ * @param array $importOptions
+ *
+ * @return void
+ */
+ public function render( string $type, array $importOptions ) {
+ $subtype = $importOptions['taxonomy_type'];
+ $addons = PMXI_Addon_Manager::get_addons();
+
+ if ( empty( $addons ) ) {
+ return;
+ }
+
+ foreach ( $addons as $addon ) {
+ $view = PMXI_Addon_View::create( $addon->slug, $type, $subtype );
+ $view->renderTabs( $importOptions );
+ }
+ }
+
+ /**
+ * Render the update screen
+ *
+ * @param string $type
+ * @param array $importOptions
+ *
+ * @return void
+ */
+
+ public function renderUpdateScreen( string $type, array $importOptions ) {
+ $subtype = $importOptions['taxonomy_type'];
+ $addons = PMXI_Addon_Manager::get_addons();
+
+ if ( empty( $addons ) ) {
+ return;
+ }
+
+ foreach ( $addons as $addon ) {
+ $view = PMXI_Addon_View::create( $addon->slug, $type, $subtype );
+ $view->renderUpdate( $importOptions );
+ }
+ }
+
+ public function renderConfirmDataToImport( bool $isWizard, array $importOptions ) {
+ $type = $importOptions['custom_type'];
+ $subtype = $importOptions['taxonomy_type'];
+ $addons = PMXI_Addon_Manager::get_addons();
+
+ if ( empty( $addons ) ) {
+ return;
+ }
+
+ foreach ( $addons as $addon ) {
+ $view = PMXI_Addon_View::create( $addon->slug, $type, $subtype );
+ $view->renderConfirmDataToImport( $importOptions );
+ }
+ }
+}
--- a/wp-all-import/addon-api/classes/base.php
+++ b/wp-all-import/addon-api/classes/base.php
@@ -0,0 +1,528 @@
+<?php
+namespace WpaiAddonAPI;
+
+if ( ! defined( 'ABSPATH' ) ) exit;
+
+abstract class PMXI_Addon_Base {
+ use HasError, HasRegistration;
+
+ public PMXI_Addon_Importer $importer;
+
+ public $slug = 'not-implemented'; // Must be implemented by end-user
+ public $version = '0.0.0'; // Must be implemented by end-user
+ public $rootDir = ''; // Must be implemented by end-user
+
+ // Extra fields created by the addon
+ public $fields = [];
+
+ // Cast values to something else without having to create a custom field
+ public $casts = [];
+
+ // Add tooltips to fields
+ public $hints = [];
+
+ // Getters
+ abstract public function name(): string;
+
+ abstract public function description(): string;
+
+ public function __construct() {
+ $this->preflight();
+ $this->registerAsAddon();
+
+ $this->importer = PMXI_Addon_Importer::from( $this );
+ $this->initEed();
+
+ add_action('init', function() {
+ $this->hints = [
+ 'time' => __( 'Use any format supported by the PHP strtotime function.', 'wp-all-import' ),
+ 'date' => __( 'Use any format supported by the PHP strtotime function.', 'wp-all-import' ),
+ 'datetime' => __( 'Use any format supported by the PHP strtotime function.', 'wp-all-import' ),
+ 'iconpicker' => __( 'Specify the icon class name - e.g. fa-user.', 'wp-all-import' ),
+ 'colorpicker' => __( 'Specify the hex code the color preceded with a # - e.g. #ea5f1a.', 'wp-all-import' ),
+ 'media' => __( 'Specify the URL to the image or file.', 'wp-all-import' ),
+ 'post' => __( 'Enter the ID, slug, or Title. Separate multiple entries with separator character.', 'wp-all-import' ),
+ 'user' => __( 'Enter the ID, username, or email for the existing user.', 'wp-all-import' ),
+ 'map' => __( 'WP All Import will first try to get your Google Maps API key from the add-on you're using. If that fails you must enter the key under 'Google Maps Settings' below.', 'wp-all-import' )
+ ];
+ });
+
+ add_filter( 'wp_all_import_addon_parse', [ $this, 'registerParseFunction' ] );
+ add_filter( 'wp_all_import_addon_import', [ $this, 'registerImportFunction' ] );
+ add_filter( 'wp_all_import_addon_saved_post', [ $this, 'registerPostSavedFunction' ] );
+ add_filter( 'pmxi_custom_types', [ $this, 'registerCustomTypes' ], 2, 5 );
+ add_filter( 'pmxi_options_options', [ $this, 'registerDefaultOptions' ] );
+ add_filter( 'pmxi_save_options', [ $this, 'updateOptions' ] );
+ add_filter( 'pmxi_custom_field_to_delete', [ $this, 'canDeleteField' ], 99, 5 );
+ add_filter( 'pmxi_visible_template_sections', [ $this, 'getVisibleSections' ], 99, 2 );
+ add_filter( 'pmxi_hidden_data_to_update_options', [ $this, 'getHiddenChooseDataToUpdateOptions' ], 99, 2 );
+ add_filter( 'pmxi_disabled_delete_missing_options', [ $this, 'getDisabledDeleteMissingOptions' ], 99, 2 );
+ add_filter( 'pmxi_hidden_delete_missing_options', [ $this, 'getHiddenDeleteMissingOptions' ], 99, 2 );
+ add_filter( 'pmxi_status_of_removed_options', [ $this, 'getStatusOfRemovedOptions' ], 99, 2 );
+ add_filter( 'pmxi_fire_hooks', [ $this, 'shouldFirePostHooks' ], 10, 2 );
+ add_filter( 'pmxi_types_current_type_supports_title', [ $this, 'supportsTitle' ], 99, 2 );
+ }
+
+ /**
+ * Path to the plugin file relative to the plugins directory.
+ */
+ public function getPluginPath() {
+ return $this->rootDir . '/plugin.php';
+ }
+
+ /**
+ * Do stuff before the plugin is activated
+ *
+ * @return void
+ */
+ public function preflight() {
+ $results = $this->canRun();
+
+ if ( is_wp_error( $results ) ) {
+ $this->showErrorAndDeactivate( $results->get_error_message() );
+ }
+ }
+
+ /**
+ * Determine if the addon can be used for the current import type.
+ *
+ * @param string $importType
+ * @param $options
+ *
+ * @return bool
+ */
+ public function isAvailableForType( string $importType, $options ) {
+ $customTypes = array_keys($this->getCustomTypes());
+ $types = $this->availableForTypes();
+
+ $unprefixedTypes = array_values( array_filter( $types, fn( $type ) => $type[0] !== '-' ) );
+ // If the type is prefixed with a dash, it means the addon not available for it
+ $shouldSkip = in_array( '-' . $importType, $types );
+
+
+ if ( in_array( $importType, $customTypes ) ) {
+ return true;
+ }
+
+ if ( $importType === 'taxonomies' ) {
+ $taxonomy = $options['taxonomy_type'];
+
+ return count( $unprefixedTypes ) === 0 || in_array( 'taxonomy:' . $taxonomy, $types ) || in_array( 'taxonomies', $types );
+ }
+
+ if ( $shouldSkip ) {
+ return false;
+ }
+
+ return count( $unprefixedTypes ) === 0 || in_array( $importType, $types );
+ }
+
+ /**
+ * Provide an interface for developers to create custom importers for Non-WordPress data.
+ *
+ * @param $options
+ *
+ * @return class-string<PMXI_Addon_Importer>|null
+ */
+ public function getCustomImporter( $options ) {
+ return null;
+ }
+
+ /**
+ * List of post types and taxonomies the plugin is available for.
+ * Leave empty to make it available for all post types.
+ *
+ * @return string[]
+ */
+ public function availableForTypes() {
+ return [];
+ }
+
+ /**
+ * Was this import type created by this addon?
+ *
+ * @param string $importType
+ * @param $options
+ *
+ * @return bool
+ */
+ public function ownsImportType( string $importType, $options ) {
+ $types = array_keys( $this->getCustomTypes() );
+ return in_array( $importType, $types );
+ }
+
+ /**
+ * Register Custom Types that are not part of WordPress core.
+ * - This is useful for plugins that have their own data structures.
+ * - Prefix the key with the plugin slug to avoid conflicts.
+ *
+ * @return string[]
+ */
+ public function getCustomTypes() {
+ return [];
+ }
+
+ /**
+ * The function called by the add_filter hook.
+ *
+ * @return string[]
+ */
+ public function registerCustomTypes( $types, $section ) {
+ return array_merge( $types, $this->getCustomTypes() );
+ }
+
+ /**
+ * Show or hide sections based on the import type.
+ *
+ * @param $sections
+ * @param $type
+ *
+ * @return mixed
+ */
+ public function getVisibleSections( $sections, $type ) {
+ return $sections;
+ }
+
+ /**
+ * Show or hide sections based on the import type in "Choose Which Data to Update" options.
+ *
+ * @param $options
+ * @param $type
+ *
+ * @return mixed
+ */
+ public function getHiddenChooseDataToUpdateOptions( $options, $type ) {
+ return $options;
+ }
+
+ /**
+ * Enable or Disable delete sections based on the import type in "What do you want to do with those..." options.
+ *
+ * @param $options
+ * @param $type
+ *
+ * @return mixed
+ */
+ public function getDisabledDeleteMissingOptions( $options, $type ) {
+ return $options;
+ }
+
+ /**
+ * Show or hide delete sections based on the import type in "What do you want to do with those..." options.
+ *
+ * @param $options
+ * @param $type
+ *
+ * @return mixed
+ */
+ public function getHiddenDeleteMissingOptions( $options, $type ) {
+ return $options;
+ }
+
+ /**
+ * Modify statuses based on the import type in "Change status of removed...".
+ *
+ * @param $options
+ * @param $type
+ *
+ * @return mixed
+ */
+ public function getStatusOfRemovedOptions( $options, $type ) {
+ return $options;
+ }
+
+ /**
+ * Decide if we should fire important hooks after custom fields are added.
+ * This is only applicable custom post type.
+ *
+ * @param bool $should_fire
+ * @param string $type
+ *
+ * @return bool
+ */
+ public function shouldFirePostHooks( bool $should_fire, string $type ) {
+ return $should_fire;
+ }
+
+ /**
+ * Determine if the import type supports a title field.
+ *
+ * @param bool $supports
+ * @param string $type
+ *
+ * @return bool
+ */
+ public function supportsTitle(bool $supports, string $type) {
+ return $supports;
+ }
+
+ /**
+ * @return true
+ */
+ public function isAccordionClosed(string $type, ?string $subtype = null) {
+ return true;
+ }
+
+ /**
+ * Allow addons to initialize their own EED classes. Empty by default.
+ *
+ * @return void
+ */
+ public function initEed() {}
+
+ /**
+ * Determine if the plugin can run on the current site otherwise disable it.
+ *
+ * @return bool|WP_Error
+ */
+ abstract public function canRun();
+
+ /**
+ * Get fields by import type (post, term, user, etc.) and taxonomy (if applicable)
+ *
+ * @param string $type
+ * @param string|null $subtype
+ *
+ * @return mixed
+ */
+ abstract public static function fields( string $type, ?string $subtype = null );
+
+ /**
+ * Get groups by import type (post, term, user, etc.) and taxonomy (if applicable)
+ *
+ * @param string $type
+ * @param string|null $subtype
+ *
+ * @return mixed
+ */
+ abstract public static function groups( string $type, ?string $subtype = null );
+
+ /**
+ * Import fields to the database
+ */
+ abstract public static function import(
+ int $id,
+ array $fields,
+ array $values,
+ PMXI_Import_Record $record,
+ array $post,
+ $logger
+ );
+
+ /**
+ * Potentially change the class of a field at runtime
+ *
+ * @param array $field
+ * @param class-string<PMXI_Addon_Field> $class
+ */
+ public function resolveFieldClass( $field, $class ) {
+ return $class;
+ }
+
+ /**
+ * Internal method to simplify the import function for end-users.
+ *
+ * @param array $importData
+ * @param array $parsedData
+ */
+ public function transformImport( array $importData, array $parsedData ) {
+ $params = $this->importer->simplify( $importData, $parsedData );
+ if ( ! $params ) {
+ return;
+ }
+ call_user_func_array( [ $this, 'import' ], $params );
+ }
+
+ /**
+ * Parse the data from the XML file
+ *
+ * @param array $data
+ *
+ * @return array
+ */
+ public function parse( array $data ) {
+ $type = $data['import']->options['custom_type'];
+ $subtype = $data['import']->options['taxonomy_type'];
+ $defaults = $this->importer->defaults( $type, $subtype );
+
+ return PMXI_Addon_Parser::from( $this, $data, $defaults );
+ }
+
+ /**
+ * Called after the post has been saved
+ */
+ public function postSaved( array $importData ) {
+ }
+
+ public function defaultOptions( string $type, ?string $subtype = null ) {
+ return $this->importer->defaults( $type, $subtype );
+ }
+
+ public function defaultUpdateOptions() {
+ return [
+ 'is_update' => true,
+ 'update_logic' => 'full_update',
+ 'fields_list' => [],
+ 'fields_only_list' => [],
+ 'fields_except_list' => [],
+ ];
+ }
+
+ public function updateOptions( $options ) {
+ if ( ! isset( $options['update_addons'][ $this->slug ] ) ) {
+ return $options;
+ }
+
+ $post = $options['update_addons'][ $this->slug ];
+
+ if ( $post['update_logic'] === 'only' && ! empty( $post['fields_only_list'] ) ) {
+ $post['fields_list'] = explode( ",", $post['fields_only_list'] );
+ } elseif ( $post['update_logic'] == 'all_except' && ! empty( $post['fields_except_list'] ) ) {
+ $post['fields_list'] = explode( ",", $post['fields_except_list'] );
+ }
+
+ $options['update_addons'][ $this->slug ] = $post;
+
+ return $options;
+ }
+
+ /**
+ * @param bool $field_to_delete
+ * @param int $pid
+ * @param string $post_type
+ * @param array $options
+ * @param string $cur_meta_key
+ *
+ * @return bool
+ */
+ public function canDeleteField( $field_to_delete, $pid, $post_type, $options, $cur_meta_key ) {
+ $type = $options['custom_type'];
+ $subtype = $options['taxonomy_type'];
+ $data = $options[ $this->slug ] ?? null;
+ $groups = $options[ $this->slug . '_groups' ] ?? [];
+
+ $fields = $this->getImportFields( $type, $subtype, $groups, $data );
+ $field = $this->getFieldByKey( $fields, $cur_meta_key );
+
+ if ( ! $field ) {
+ return apply_filters( "pmxi_is_{$this->slug}_update_allowed", $field_to_delete, $cur_meta_key, $options );
+ }
+
+ $canDelete = $this->importer->canDeleteField( $field, $options );
+
+ return apply_filters( "pmxi_is_{$this->slug}_update_allowed", $canDelete, $cur_meta_key, $options );
+ }
+
+ public function getImportFields( $type, $subtype, $groups, $data ) {
+ // Filter out fields that don't exist in the parsed data
+ $fields = array_filter(
+ $this->fields( $type, $subtype ),
+ fn( $field ) => isset( $data[ $field['key'] ] )
+ );
+
+ // Filter out fields from disabled groups
+ $fields = array_filter(
+ $fields,
+ fn( $field ) => in_array( $field['group'], $groups )
+ );
+
+ return array_values( $fields );
+ }
+
+ // Fields and groups helpers
+
+ public function getFieldByKey( $fields, $key ) {
+ $index = array_column( $fields, 'key' );
+ $map = array_flip( $index );
+
+ return $fields[ $map[ $key ] ?? null ] ?? null;
+ }
+
+ /**
+ * @param string $groupId
+ * @param string $type
+ * @param string|null $subtype
+ *
+ * @return array
+ */
+ public static function getFieldsByGroup( string $groupId, string $type, ?string $subtype = null ) {
+ return pipe( static::fields( $type, $subtype ), [
+ fn( $fields ) => array_filter( $fields, fn( $field ) => $field['group'] === $groupId ),
+ fn( $fields ) => array_values( $fields )
+ ] );
+ }
+
+ public static function getGroupById( string $groupId, string $type, ?string $subtype = null ) {
+ return pipe( static::groups( $type, $subtype ), [
+ fn( $groups ) => array_filter( $groups, fn( $group ) => $group['id'] === $groupId ),
+ fn( $groups ) => array_values( $groups )[0]
+ ] );
+ }
+}
+
+trait HasRegistration {
+ // Todo: Maybe refactor this by using the PMXI_Admin_Addons class
+ public function registerAsAddon() {
+ add_filter(
+ 'pmxi_new_addons'