Revamp uninstallation when WooCommerce Admin is in core. (https://github.com/woocommerce/woocommerce-admin/pull/3546)

* Don't truncate table data on uninstall if WooCommerce Admin is in core.

* DROP tables on uninstall (like core does).

* Migrate wc_ prefix options to woocommerce_.

This allows core WC to clean up when uninstalled.

* Migrate wc_ prefixed user meta fields to woocommerce_.

This allows core WC to clean up when uninstalled.

* Fix use of date().

* Update comment describing the option value filter.
This commit is contained in:
Jeff Stieler 2020-01-16 17:08:29 -07:00 committed by GitHub
parent de5d3f7c82
commit d57b99e6c7
14 changed files with 148 additions and 31 deletions

View File

@ -102,7 +102,7 @@ class OnboardingProfile extends \WC_REST_Data_Controller {
public function get_items( $request ) {
include_once WC_ABSPATH . 'includes/admin/helper/class-wc-helper-options.php';
$onboarding_data = get_option( 'wc_onboarding_profile', array() );
$onboarding_data = get_option( Onboarding::PROFILE_DATA_OPTION, array() );
$item_schema = $this->get_item_schema();
$items = array();
@ -128,8 +128,8 @@ class OnboardingProfile extends \WC_REST_Data_Controller {
public function update_items( $request ) {
$params = $request->get_json_params();
$query_args = $this->prepare_objects_query( $params );
$onboarding_data = get_option( 'wc_onboarding_profile', array() );
update_option( 'wc_onboarding_profile', array_merge( $onboarding_data, $query_args ) );
$onboarding_data = get_option( Onboarding::PROFILE_DATA_OPTION, array() );
update_option( Onboarding::PROFILE_DATA_OPTION, array_merge( $onboarding_data, $query_args ) );
$result = array(
'status' => 'success',

View File

@ -336,7 +336,7 @@ class OnboardingTasks extends \WC_REST_Data_Controller {
* @return array An array of images that have been attached to the post.
*/
private static function sideload_homepage_images( $post_id, $number_of_images ) {
$profile = get_option( 'wc_onboarding_profile', array() );
$profile = get_option( Onboarding::PROFILE_DATA_OPTION, array() );
$images_to_sideload = array();
$available_images = self::get_available_homepage_images();

View File

@ -36,6 +36,16 @@ class Onboarding {
*/
const PRODUCT_DATA_TRANSIENT = 'wc_onboarding_product_data';
/**
* Profile data option name.
*/
const PROFILE_DATA_OPTION = 'woocommerce_onboarding_profile';
/**
* Onboarding opt-in option name.
*/
const OPT_IN_OPTION = 'woocommerce_onboarding_opt_in';
/**
* Get class instance.
*/
@ -101,7 +111,7 @@ class Onboarding {
* @return bool
*/
public static function should_show_profiler() {
$onboarding_data = get_option( 'wc_onboarding_profile', array() );
$onboarding_data = get_option( self::PROFILE_DATA_OPTION, array() );
$is_completed = isset( $onboarding_data['completed'] ) && true === $onboarding_data['completed'];
@ -354,7 +364,7 @@ class Onboarding {
* @param array $settings Component settings.
*/
public function component_settings( $settings ) {
$profile = get_option( 'wc_onboarding_profile', array() );
$profile = get_option( self::PROFILE_DATA_OPTION, array() );
include_once WC_ABSPATH . 'includes/admin/helper/class-wc-helper-options.php';
$wccom_auth = \WC_Helper_Options::get( 'auth' );
@ -701,7 +711,7 @@ class Onboarding {
)
);
update_option( 'wc_onboarding_opt_in', $enabled ? 'yes' : 'no' );
update_option( self::OPT_IN_OPTION, $enabled ? 'yes' : 'no' );
wp_safe_redirect( wc_admin_url() );
exit;
}
@ -730,7 +740,7 @@ class Onboarding {
$screen->remove_help_tab( 'woocommerce_onboard_tab' );
$task_list_hidden = get_option( 'woocommerce_task_list_hidden', 'no' );
$onboarding_data = get_option( 'wc_onboarding_profile', array() );
$onboarding_data = get_option( self::PROFILE_DATA_OPTION, array() );
$is_completed = isset( $onboarding_data['completed'] ) && true === $onboarding_data['completed'];
$is_enabled = ! $is_completed;

View File

@ -20,7 +20,7 @@ class Install {
/**
* Plugin version option name.
*/
const VERSION_OPTION = 'wc_admin_version';
const VERSION_OPTION = 'woocommerce_admin_version';
/**
* DB updates and callbacks that need to be run per version.
@ -38,6 +38,22 @@ class Install {
),
);
/**
* Migrated option names mapping. New => old.
*
* @var array
*/
protected static $migrated_options = array(
'woocommerce_onboarding_profile' => 'wc_onboarding_profile',
'woocommerce_admin_install_timestamp' => 'wc_admin_install_timestamp',
'woocommerce_onboarding_opt_in' => 'wc_onboarding_opt_in',
'woocommerce_admin_import_stats' => 'wc_admin_import_stats',
'woocommerce_admin_version' => 'wc_admin_version',
'woocommerce_admin_last_orders_milestone' => 'wc_admin_last_orders_milestone',
'woocommerce_admin-wc-helper-last-refresh' => 'wc-admin-wc-helper-last-refresh',
'woocommerce_admin_report_export_status' => 'wc_admin_report_export_status',
);
/**
* Hook in tabs.
*/
@ -47,6 +63,39 @@ class Install {
// Add wc-admin report tables to list of WooCommerce tables.
add_filter( 'woocommerce_install_get_tables', array( __CLASS__, 'add_tables' ) );
// Migrate option names by filtering their default values.
// This attaches a targeted filter for each migrated option name that will retreive
// the old value and use it as the default for the new option. This default
// will be used in the first retreival of the new option.
foreach ( self::$migrated_options as $new_option => $old_option ) {
add_filter( "default_option_{$new_option}", array( __CLASS__, 'handle_option_migration' ), 10, 2 );
}
}
/**
* Migrate option values to their new keys/names.
*
* @param mixed $default Default value for the option.
* @param string $new_option Option name.
* @return mixed Migrated option value.
*/
public static function handle_option_migration( $default, $new_option ) {
if ( isset( self::$migrated_options[ $new_option ] ) ) {
// Avoid infinite loops - this filter is applied in add_option(), update_option(), and get_option().
remove_filter( "default_option_{$new_option}", array( __CLASS__, 'handle_option_migration' ), 10, 2 );
// Migrate the old option value.
$old_option_name = self::$migrated_options[ $new_option ];
$old_option_value = get_option( $old_option_name, $default );
update_option( $new_option, $old_option_value );
delete_option( $old_option_name );
return $old_option_value;
}
return $default;
}
/**
@ -107,7 +156,7 @@ class Install {
// Use add_option() here to avoid overwriting this value with each
// plugin version update. We base plugin age off of this value.
add_option( 'wc_admin_install_timestamp', time() );
add_option( 'woocommerce_admin_install_timestamp', time() );
do_action( 'woocommerce_admin_installed' );
}
@ -392,15 +441,17 @@ class Install {
}
/**
* Delete all data from tables.
* Drop WooCommerce Admin tables.
*
* @return void
*/
public static function delete_table_data() {
public static function drop_tables() {
global $wpdb;
$tables = self::get_tables();
foreach ( $tables as $table ) {
$wpdb->query( "TRUNCATE TABLE {$table}" ); // WPCS: unprepared SQL ok.
$wpdb->query( "DROP TABLE IF EXISTS {$table}" ); // WPCS: unprepared SQL ok.
}
}
}

View File

@ -9,6 +9,7 @@
namespace Automattic\WooCommerce\Admin;
use \_WP_Dependency;
use Automattic\WooCommerce\Admin\Features\Onboarding;
/**
* Loader Class.
@ -172,7 +173,7 @@ class Loader {
return false;
}
$onboarding_opt_in = 'yes' === get_option( 'wc_onboarding_opt_in', 'no' );
$onboarding_opt_in = 'yes' === get_option( Onboarding::OPT_IN_OPTION, 'no' );
$onboarding_filter_opt_in = defined( 'WOOCOMMERCE_ADMIN_ONBOARDING_ENABLED' ) && true === WOOCOMMERCE_ADMIN_ONBOARDING_ENABLED;
if ( self::is_dev() || $onboarding_filter_opt_in || $onboarding_opt_in ) {
@ -863,7 +864,7 @@ class Loader {
public static function get_user_data_values( $user ) {
$values = array();
foreach ( self::get_user_data_fields() as $field ) {
$values[ $field ] = get_user_meta( $user['id'], 'wc_admin_' . $field, true );
$values[ $field ] = self::get_user_data_field( $user['id'], $field );
}
return $values;
}
@ -885,7 +886,7 @@ class Loader {
foreach ( $values as $field => $value ) {
if ( in_array( $field, $fields, true ) ) {
$updates[ $field ] = $value;
update_user_meta( $user->ID, 'wc_admin_' . $field, $value );
self::update_user_data_field( $user->ID, $field, $value );
}
}
return $updates;
@ -902,6 +903,44 @@ class Loader {
return apply_filters( 'woocommerce_admin_get_user_data_fields', array() );
}
/**
* Helper to update user data fields.
*
* @param int $user_id User ID.
* @param string $field Field name.
* @param mixed $value Field value.
*/
public static function update_user_data_field( $user_id, $field, $value ) {
update_user_meta( $user_id, 'woocommerce_admin_' . $field, $value );
}
/**
* Helper to retrive user data fields.
*
* Migrates old key prefixes as well.
*
* @param int $user_id User ID.
* @param string $field Field name.
* @return mixed The user field value.
*/
public static function get_user_data_field( $user_id, $field ) {
$meta_value = get_user_meta( $user_id, 'woocommerce_admin_' . $field, true );
// Migrate old meta values (prefix changed from `wc_admin_` to `woocommerce_admin_`).
if ( '' === $meta_value ) {
$old_meta_value = get_user_meta( $user_id, 'wc_admin_' . $field, true );
if ( '' !== $old_meta_value ) {
self::update_user_data_field( $user_id, $field, $old_meta_value );
delete_user_meta( $user_id, 'wc_admin_' . $field );
$meta_value = $old_meta_value;
}
}
return $meta_value;
}
/**
* Injects wp-shared-settings as a dependency if it's present.
*/

View File

@ -23,10 +23,10 @@ trait NoteTraits {
*/
public static function wc_admin_active_for( $seconds ) {
// Getting install timestamp reference class-wc-admin-install.php.
$wc_admin_installed = get_option( 'wc_admin_install_timestamp', false );
$wc_admin_installed = get_option( 'woocommerce_admin_install_timestamp', false );
if ( false === $wc_admin_installed ) {
update_option( 'wc_admin_install_timestamp', time() );
update_option( 'woocommerce_admin_install_timestamp', time() );
return false;
}

View File

@ -11,6 +11,8 @@ namespace Automattic\WooCommerce\Admin\Notes;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\Features\Onboarding;
/**
* WC_Admin_Notes_Onboarding_Email_Marketing
*/
@ -30,7 +32,7 @@ class WC_Admin_Notes_Onboarding_Email_Marketing {
*/
public static function possibly_add_onboarding_email_marketing_note() {
// We want to show the email marketing note after day 2 if the profiler is complete.
$onboarding_data = get_option( 'wc_onboarding_profile', array() );
$onboarding_data = get_option( Onboarding::PROFILE_DATA_OPTION, array() );
$is_completed = isset( $onboarding_data['completed'] ) && true === $onboarding_data['completed'];
$two_days_in_seconds = 2 * DAY_IN_SECONDS;
if ( ! self::wc_admin_active_for( $two_days_in_seconds ) || ! $is_completed ) {

View File

@ -24,7 +24,7 @@ class WC_Admin_Notes_Onboarding_Profiler {
*/
public function __construct() {
add_action( 'admin_init', array( $this, 'add_reminder' ) );
add_action( 'update_option_wc_onboarding_profile', array( $this, 'update_status_on_complete' ), 10, 2 );
add_action( 'update_option_' . Onboarding::PROFILE_DATA_OPTION, array( $this, 'update_status_on_complete' ), 10, 2 );
}
/**

View File

@ -23,7 +23,7 @@ class WC_Admin_Notes_Order_Milestones {
/**
* Option key name to store last order milestone.
*/
const LAST_ORDER_MILESTONE_OPTION_KEY = 'wc_admin_last_orders_milestone';
const LAST_ORDER_MILESTONE_OPTION_KEY = 'woocommerce_admin_last_orders_milestone';
/**
* Hook to process order milestones.

View File

@ -15,7 +15,7 @@ defined( 'ABSPATH' ) || exit;
* WC_Admin_Notes_Woo_Subscriptions_Notes
*/
class WC_Admin_Notes_Woo_Subscriptions_Notes {
const LAST_REFRESH_OPTION_KEY = 'wc-admin-wc-helper-last-refresh';
const LAST_REFRESH_OPTION_KEY = 'woocommerce_admin-wc-helper-last-refresh';
const CONNECTION_NOTE_NAME = 'wc-admin-wc-helper-connection';
const SUBSCRIPTION_NOTE_NAME = 'wc-admin-wc-helper-subscription';
const NOTIFY_WHEN_DAYS_LEFT = 60;
@ -358,7 +358,7 @@ class WC_Admin_Notes_Woo_Subscriptions_Notes {
$product_name = $subscription['product_name'];
$product_page = $subscription['product_url'];
$expires = intval( $subscription['expires'] );
$expires_date = date( 'F jS', $expires );
$expires_date = gmdate( 'F jS', $expires );
$note = $this->find_note_for_product_id( $product_id );
if ( $note ) {

View File

@ -34,7 +34,7 @@ class ReportExporter {
/**
* Export status option name.
*/
const EXPORT_STATUS_OPTION = 'wc_admin_report_export_status';
const EXPORT_STATUS_OPTION = 'woocommerce_admin_report_export_status';
/**
* Export file download action.

View File

@ -11,6 +11,7 @@ defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\Schedulers\CustomersScheduler;
use Automattic\WooCommerce\Admin\Schedulers\OrdersScheduler;
use Automattic\WooCommerce\Admin\Schedulers\ImportScheduler;
/**
* ReportsSync Class.
@ -103,7 +104,7 @@ class ReportsSync {
* @param bool $skip_existing Skip exisiting records.
*/
public static function reset_import_stats( $days, $skip_existing ) {
$import_stats = get_option( 'wc_admin_import_stats', array() );
$import_stats = get_option( ImportScheduler::IMPORT_STATS_OPTION, array() );
$totals = self::get_import_totals( $days, $skip_existing );
foreach ( self::get_schedulers() as $scheduler ) {
@ -119,7 +120,7 @@ class ReportsSync {
$import_stats['imported_from'] = $current_import_date;
}
update_option( 'wc_admin_import_stats', $import_stats );
update_option( ImportScheduler::IMPORT_STATS_OPTION, $import_stats );
}
/**
@ -128,7 +129,7 @@ class ReportsSync {
* @return array
*/
public static function get_import_stats() {
$import_stats = get_option( 'wc_admin_import_stats', array() );
$import_stats = get_option( ImportScheduler::IMPORT_STATS_OPTION, array() );
$import_stats['is_importing'] = self::is_importing();
return $import_stats;
@ -175,7 +176,7 @@ class ReportsSync {
}
// Delete import options.
delete_option( 'wc_admin_import_stats' );
delete_option( ImportScheduler::IMPORT_STATS_OPTION );
return __( 'Report table data is being deleted.', 'woocommerce-admin' );
}

View File

@ -16,6 +16,11 @@ use \Automattic\WooCommerce\Admin\Schedulers\SchedulerTraits;
* ImportScheduler class.
*/
abstract class ImportScheduler {
/**
* Import stats option name.
*/
const IMPORT_STATS_OPTION = 'woocommerce_admin_import_stats';
/**
* Scheduler traits.
*/
@ -148,10 +153,10 @@ abstract class ImportScheduler {
static::import( $id );
}
$import_stats = get_option( 'wc_admin_import_stats', array() );
$import_stats = get_option( self::IMPORT_STATS_OPTION, array() );
$imported_count = absint( $import_stats[ static::$name ]['imported'] ) + count( $items->ids );
$import_stats[ static::$name ]['imported'] = $imported_count;
update_option( 'wc_admin_import_stats', $import_stats );
update_option( self::IMPORT_STATS_OPTION, $import_stats );
$properties['imported_count'] = $imported_count;

View File

@ -7,8 +7,17 @@
defined( 'WP_UNINSTALL_PLUGIN' ) || exit;
// Don't delete table data if core WooCommerce contains WooCommerce Admin.
// This could alternatively check for a specific WooCommerce version rather than a file.
if (
defined( 'WC_ABSPATH' ) &&
file_exists( WC_ABSPATH . 'packages/woocommerce-admin/woocommerce-admin.php' )
) {
return;
}
use \Automattic\WooCommerce\Admin\Install;
require_once dirname( __FILE__ ) . '/woocommerce-admin.php';
Install::delete_table_data();
Install::drop_tables();