Make HPOS feature and "Delete custom order tables" tool work nicely together (#35357)
* Make linter happy by (very painfully) introducing Yoda conditions. * Change how the COT related tools work combined with the COT feature. - Enabling the feature will now automatically create the tables if they don't exist already. - In fact, enabling the feature is now the only way to create the tables. - The "Delete tables" tool will be shown as disabled in the feature is enabled. * Add changelog file * Fix code sniffer issues
This commit is contained in:
parent
be73733be4
commit
0b4811d83e
|
@ -0,0 +1,4 @@
|
||||||
|
Significance: patch
|
||||||
|
Type: update
|
||||||
|
|
||||||
|
Automatically create the custom order tables if the corresponding feature is enabled
|
|
@ -102,6 +102,7 @@ class CustomOrdersTableController {
|
||||||
self::add_filter( DataSynchronizer::PENDING_SYNCHRONIZATION_FINISHED_ACTION, array( $this, 'process_sync_finished' ), 10, 0 );
|
self::add_filter( DataSynchronizer::PENDING_SYNCHRONIZATION_FINISHED_ACTION, array( $this, 'process_sync_finished' ), 10, 0 );
|
||||||
self::add_action( 'woocommerce_update_options_advanced_custom_data_stores', array( $this, 'process_options_updated' ), 10, 0 );
|
self::add_action( 'woocommerce_update_options_advanced_custom_data_stores', array( $this, 'process_options_updated' ), 10, 0 );
|
||||||
self::add_action( 'woocommerce_after_register_post_type', array( $this, 'register_post_type_for_order_placeholders' ), 10, 0 );
|
self::add_action( 'woocommerce_after_register_post_type', array( $this, 'register_post_type_for_order_placeholders' ), 10, 0 );
|
||||||
|
self::add_action( FeaturesController::FEATURE_ENABLED_CHANGED_ACTION, array( $this, 'handle_feature_enabled_changed' ), 10, 2 );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -225,57 +226,51 @@ class CustomOrdersTableController {
|
||||||
* @return array The updated array of tools-
|
* @return array The updated array of tools-
|
||||||
*/
|
*/
|
||||||
private function add_initiate_regeneration_entry_to_tools_array( array $tools_array ): array {
|
private function add_initiate_regeneration_entry_to_tools_array( array $tools_array ): array {
|
||||||
if ( ! $this->is_feature_visible() ) {
|
if ( ! $this->data_synchronizer->check_orders_table_exists() ) {
|
||||||
return $tools_array;
|
return $tools_array;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( $this->data_synchronizer->check_orders_table_exists() ) {
|
if ( $this->is_feature_visible() ) {
|
||||||
$tools_array['delete_custom_orders_table'] = array(
|
$disabled = true;
|
||||||
'name' => __( 'Delete the custom orders tables', 'woocommerce' ),
|
$message = __( 'This will delete the custom orders tables. The tables can be deleted only if the "High-Performance order storage" feature is disabled (via Settings > Advanced > Features).', 'woocommerce' );
|
||||||
'desc' => sprintf(
|
|
||||||
'<strong class="red">%1$s</strong> %2$s',
|
|
||||||
__( 'Note:', 'woocommerce' ),
|
|
||||||
__( 'This will delete the custom orders tables. The tables can be deleted only if they are not not in use (via Settings > Advanced > Custom data stores). You can create them again at any time with the "Create the custom orders tables" tool.', 'woocommerce' )
|
|
||||||
),
|
|
||||||
'requires_refresh' => true,
|
|
||||||
'callback' => function () {
|
|
||||||
$this->delete_custom_orders_tables();
|
|
||||||
return __( 'Custom orders tables have been deleted.', 'woocommerce' );
|
|
||||||
},
|
|
||||||
'button' => __( 'Delete', 'woocommerce' ),
|
|
||||||
'disabled' => $this->custom_orders_table_usage_is_enabled(),
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
$tools_array['create_custom_orders_table'] = array(
|
$disabled = false;
|
||||||
'name' => __( 'Create the custom orders tables', 'woocommerce' ),
|
$message = __( 'This will delete the custom orders tables. To create them again enable the "High-Performance order storage" feature (via Settings > Advanced > Features).', 'woocommerce' );
|
||||||
'desc' => __( 'This tool will create the custom orders tables. Once created you can go to WooCommerce > Settings > Advanced > Custom data stores and configure the usage of the tables.', 'woocommerce' ),
|
|
||||||
'requires_refresh' => true,
|
|
||||||
'callback' => function() {
|
|
||||||
$this->create_custom_orders_tables();
|
|
||||||
return __( 'Custom orders tables have been created. You can now go to WooCommerce > Settings > Advanced > Custom data stores.', 'woocommerce' );
|
|
||||||
},
|
|
||||||
'button' => __( 'Create', 'woocommerce' ),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$tools_array['delete_custom_orders_table'] = array(
|
||||||
|
'name' => __( 'Delete the custom orders tables', 'woocommerce' ),
|
||||||
|
'desc' => sprintf(
|
||||||
|
'<strong class="red">%1$s</strong> %2$s',
|
||||||
|
__( 'Note:', 'woocommerce' ),
|
||||||
|
$message
|
||||||
|
),
|
||||||
|
'requires_refresh' => true,
|
||||||
|
'callback' => function () {
|
||||||
|
$this->features_controller->change_feature_enable( 'custom_order_tables', false );
|
||||||
|
$this->delete_custom_orders_tables();
|
||||||
|
return __( 'Custom orders tables have been deleted.', 'woocommerce' );
|
||||||
|
},
|
||||||
|
'button' => __( 'Delete', 'woocommerce' ),
|
||||||
|
'disabled' => $disabled,
|
||||||
|
);
|
||||||
|
|
||||||
return $tools_array;
|
return $tools_array;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create the custom orders tables in response to the user pressing the tool button.
|
* Create the custom orders tables in response to the user pressing the tool button.
|
||||||
*
|
*
|
||||||
|
* @param bool $verify_nonce True to perform the nonce verification, false to skip it.
|
||||||
|
*
|
||||||
* @throws \Exception Can't create the tables.
|
* @throws \Exception Can't create the tables.
|
||||||
*/
|
*/
|
||||||
private function create_custom_orders_tables() {
|
private function create_custom_orders_tables( bool $verify_nonce = true ) {
|
||||||
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput
|
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput
|
||||||
if ( ! isset( $_REQUEST['_wpnonce'] ) || wp_verify_nonce( $_REQUEST['_wpnonce'], 'debug_action' ) === false ) {
|
if ( $verify_nonce && ( ! isset( $_REQUEST['_wpnonce'] ) || wp_verify_nonce( $_REQUEST['_wpnonce'], 'debug_action' ) === false ) ) {
|
||||||
throw new \Exception( 'Invalid nonce' );
|
throw new \Exception( 'Invalid nonce' );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ! $this->is_feature_visible() ) {
|
|
||||||
throw new \Exception( "Can't create the custom orders tables: the feature isn't enabled" );
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->data_synchronizer->create_database_tables();
|
$this->data_synchronizer->create_database_tables();
|
||||||
update_option( self::CUSTOM_ORDERS_TABLE_USAGE_ENABLED_OPTION, 'no' );
|
update_option( self::CUSTOM_ORDERS_TABLE_USAGE_ENABLED_OPTION, 'no' );
|
||||||
}
|
}
|
||||||
|
@ -319,116 +314,103 @@ class CustomOrdersTableController {
|
||||||
* @return array The updated settings array.
|
* @return array The updated settings array.
|
||||||
*/
|
*/
|
||||||
private function get_settings( array $settings, string $section_id ): array {
|
private function get_settings( array $settings, string $section_id ): array {
|
||||||
if ( ! $this->is_feature_visible() || $section_id !== 'custom_data_stores' ) {
|
if ( ! $this->is_feature_visible() || 'custom_data_stores' !== $section_id ) {
|
||||||
return $settings;
|
return $settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( $this->data_synchronizer->check_orders_table_exists() ) {
|
$settings[] = array(
|
||||||
$settings[] = array(
|
'title' => __( 'Custom orders tables', 'woocommerce' ),
|
||||||
'title' => __( 'Custom orders tables', 'woocommerce' ),
|
'type' => 'title',
|
||||||
'type' => 'title',
|
'id' => 'cot-title',
|
||||||
'id' => 'cot-title',
|
'desc' => sprintf(
|
||||||
'desc' => sprintf(
|
/* translators: %1$s = <strong> tag, %2$s = </strong> tag. */
|
||||||
/* translators: %1$s = <strong> tag, %2$s = </strong> tag. */
|
__( '%1$sWARNING:%2$s This feature is currently under development and may cause database instability. For contributors only.', 'woocommerce' ),
|
||||||
__( '%1$sWARNING:%2$s This feature is currently under development and may cause database instability. For contributors only.', 'woocommerce' ),
|
'<strong>',
|
||||||
'<strong>',
|
'</strong>'
|
||||||
'</strong>'
|
),
|
||||||
),
|
);
|
||||||
);
|
|
||||||
|
|
||||||
$sync_status = $this->data_synchronizer->get_sync_status();
|
$sync_status = $this->data_synchronizer->get_sync_status();
|
||||||
$sync_is_pending = $sync_status['current_pending_count'] !== 0;
|
$sync_is_pending = 0 !== $sync_status['current_pending_count'];
|
||||||
|
|
||||||
$settings[] = array(
|
$settings[] = array(
|
||||||
'title' => __( 'Data store for orders', 'woocommerce' ),
|
'title' => __( 'Data store for orders', 'woocommerce' ),
|
||||||
'id' => self::CUSTOM_ORDERS_TABLE_USAGE_ENABLED_OPTION,
|
'id' => self::CUSTOM_ORDERS_TABLE_USAGE_ENABLED_OPTION,
|
||||||
'default' => 'no',
|
'default' => 'no',
|
||||||
'type' => 'radio',
|
'type' => 'radio',
|
||||||
'options' => array(
|
'options' => array(
|
||||||
'yes' => __( 'Use the WooCommerce orders tables', 'woocommerce' ),
|
'yes' => __( 'Use the WooCommerce orders tables', 'woocommerce' ),
|
||||||
'no' => __( 'Use the WordPress posts table', 'woocommerce' ),
|
'no' => __( 'Use the WordPress posts table', 'woocommerce' ),
|
||||||
),
|
),
|
||||||
'checkboxgroup' => 'start',
|
'checkboxgroup' => 'start',
|
||||||
'disabled' => $sync_is_pending ? array( 'yes', 'no' ) : array(),
|
'disabled' => $sync_is_pending ? array( 'yes', 'no' ) : array(),
|
||||||
);
|
);
|
||||||
|
|
||||||
if ( $sync_is_pending ) {
|
if ( $sync_is_pending ) {
|
||||||
$initial_pending_count = $sync_status['initial_pending_count'];
|
$initial_pending_count = $sync_status['initial_pending_count'];
|
||||||
$current_pending_count = $sync_status['current_pending_count'];
|
$current_pending_count = $sync_status['current_pending_count'];
|
||||||
if ( $initial_pending_count ) {
|
if ( $initial_pending_count ) {
|
||||||
$text =
|
$text =
|
||||||
sprintf(
|
sprintf(
|
||||||
/* translators: %1$s=current number of orders pending sync, %2$s=initial number of orders pending sync */
|
/* translators: %1$s=current number of orders pending sync, %2$s=initial number of orders pending sync */
|
||||||
_n( 'There\'s %1$s order (out of a total of %2$s) pending sync!', 'There are %1$s orders (out of a total of %2$s) pending sync!', $current_pending_count, 'woocommerce' ),
|
_n( 'There\'s %1$s order (out of a total of %2$s) pending sync!', 'There are %1$s orders (out of a total of %2$s) pending sync!', $current_pending_count, 'woocommerce' ),
|
||||||
$current_pending_count,
|
$current_pending_count,
|
||||||
$initial_pending_count
|
$initial_pending_count
|
||||||
);
|
|
||||||
} else {
|
|
||||||
$text =
|
|
||||||
/* translators: %s=initial number of orders pending sync */
|
|
||||||
sprintf( _n( 'There\'s %s order pending sync!', 'There are %s orders pending sync!', $current_pending_count, 'woocommerce' ), $current_pending_count, 'woocommerce' );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( $this->batch_processing_controller->is_enqueued( get_class( $this->data_synchronizer ) ) ) {
|
|
||||||
$text .= __( "<br/>Synchronization for these orders is currently in progress.<br/>The authoritative table can't be changed until sync completes.", 'woocommerce' );
|
|
||||||
} else {
|
|
||||||
$text .= __( "<br/>The authoritative table can't be changed until these orders are synchronized.", 'woocommerce' );
|
|
||||||
}
|
|
||||||
|
|
||||||
$settings[] = array(
|
|
||||||
'type' => 'info',
|
|
||||||
'id' => 'cot-out-of-sync-warning',
|
|
||||||
'css' => 'color: #C00000',
|
|
||||||
'text' => $text,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
$settings[] = array(
|
|
||||||
'desc' => __( 'Keep the posts table and the orders tables synchronized', 'woocommerce' ),
|
|
||||||
'id' => DataSynchronizer::ORDERS_DATA_SYNC_ENABLED_OPTION,
|
|
||||||
'type' => 'checkbox',
|
|
||||||
);
|
|
||||||
|
|
||||||
if ( $sync_is_pending ) {
|
|
||||||
if ( $this->data_synchronizer->data_sync_is_enabled() ) {
|
|
||||||
$message = $this->custom_orders_table_usage_is_enabled() ?
|
|
||||||
__( 'Switch to using the posts table as the authoritative data store for orders when sync finishes', 'woocommerce' ) :
|
|
||||||
__( 'Switch to using the orders table as the authoritative data store for orders when sync finishes', 'woocommerce' );
|
|
||||||
$settings[] = array(
|
|
||||||
'desc' => $message,
|
|
||||||
'id' => self::AUTO_FLIP_AUTHORITATIVE_TABLE_ROLES_OPTION,
|
|
||||||
'type' => 'checkbox',
|
|
||||||
);
|
);
|
||||||
}
|
} else {
|
||||||
|
$text =
|
||||||
|
/* translators: %s=initial number of orders pending sync */
|
||||||
|
sprintf( _n( 'There\'s %s order pending sync!', 'There are %s orders pending sync!', $current_pending_count, 'woocommerce' ), $current_pending_count, 'woocommerce' );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $this->batch_processing_controller->is_enqueued( get_class( $this->data_synchronizer ) ) ) {
|
||||||
|
$text .= __( "<br/>Synchronization for these orders is currently in progress.<br/>The authoritative table can't be changed until sync completes.", 'woocommerce' );
|
||||||
|
} else {
|
||||||
|
$text .= __( "<br/>The authoritative table can't be changed until these orders are synchronized.", 'woocommerce' );
|
||||||
}
|
}
|
||||||
|
|
||||||
$settings[] = array(
|
$settings[] = array(
|
||||||
'desc' => __( 'Use database transactions for the orders data synchronization', 'woocommerce' ),
|
'type' => 'info',
|
||||||
'id' => self::USE_DB_TRANSACTIONS_OPTION,
|
'id' => 'cot-out-of-sync-warning',
|
||||||
'type' => 'checkbox',
|
'css' => 'color: #C00000',
|
||||||
);
|
'text' => $text,
|
||||||
|
|
||||||
$isolation_level_names = self::get_valid_transaction_isolation_levels();
|
|
||||||
$settings[] = array(
|
|
||||||
'desc' => __( 'Database transaction isolation level to use', 'woocommerce' ),
|
|
||||||
'id' => self::DB_TRANSACTIONS_ISOLATION_LEVEL_OPTION,
|
|
||||||
'type' => 'select',
|
|
||||||
'options' => array_combine( $isolation_level_names, $isolation_level_names ),
|
|
||||||
'default' => self::DEFAULT_DB_TRANSACTIONS_ISOLATION_LEVEL,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
$settings[] = array(
|
|
||||||
'title' => __( 'Custom orders tables', 'woocommerce' ),
|
|
||||||
'type' => 'title',
|
|
||||||
'desc' => sprintf(
|
|
||||||
/* translators: %1$s = <em> tag, %2$s = </em> tag. */
|
|
||||||
__( 'Create the tables first by going to %1$sWooCommerce > Status > Tools%2$s and running %1$sCreate the custom orders tables%2$s.', 'woocommerce' ),
|
|
||||||
'<em>',
|
|
||||||
'</em>'
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$settings[] = array(
|
||||||
|
'desc' => __( 'Keep the posts table and the orders tables synchronized', 'woocommerce' ),
|
||||||
|
'id' => DataSynchronizer::ORDERS_DATA_SYNC_ENABLED_OPTION,
|
||||||
|
'type' => 'checkbox',
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( $sync_is_pending ) {
|
||||||
|
if ( $this->data_synchronizer->data_sync_is_enabled() ) {
|
||||||
|
$message = $this->custom_orders_table_usage_is_enabled() ?
|
||||||
|
__( 'Switch to using the posts table as the authoritative data store for orders when sync finishes', 'woocommerce' ) :
|
||||||
|
__( 'Switch to using the orders table as the authoritative data store for orders when sync finishes', 'woocommerce' );
|
||||||
|
$settings[] = array(
|
||||||
|
'desc' => $message,
|
||||||
|
'id' => self::AUTO_FLIP_AUTHORITATIVE_TABLE_ROLES_OPTION,
|
||||||
|
'type' => 'checkbox',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$settings[] = array(
|
||||||
|
'desc' => __( 'Use database transactions for the orders data synchronization', 'woocommerce' ),
|
||||||
|
'id' => self::USE_DB_TRANSACTIONS_OPTION,
|
||||||
|
'type' => 'checkbox',
|
||||||
|
);
|
||||||
|
|
||||||
|
$isolation_level_names = self::get_valid_transaction_isolation_levels();
|
||||||
|
$settings[] = array(
|
||||||
|
'desc' => __( 'Database transaction isolation level to use', 'woocommerce' ),
|
||||||
|
'id' => self::DB_TRANSACTIONS_ISOLATION_LEVEL_OPTION,
|
||||||
|
'type' => 'select',
|
||||||
|
'options' => array_combine( $isolation_level_names, $isolation_level_names ),
|
||||||
|
'default' => self::DEFAULT_DB_TRANSACTIONS_ISOLATION_LEVEL,
|
||||||
|
);
|
||||||
|
|
||||||
$settings[] = array( 'type' => 'sectionend' );
|
$settings[] = array( 'type' => 'sectionend' );
|
||||||
|
|
||||||
return $settings;
|
return $settings;
|
||||||
|
@ -456,7 +438,7 @@ class CustomOrdersTableController {
|
||||||
* @param mixed $value New value of the setting.
|
* @param mixed $value New value of the setting.
|
||||||
*/
|
*/
|
||||||
private function process_updated_option( $option, $old_value, $value ) {
|
private function process_updated_option( $option, $old_value, $value ) {
|
||||||
if ( $option === DataSynchronizer::ORDERS_DATA_SYNC_ENABLED_OPTION && $value === 'no' ) {
|
if ( DataSynchronizer::ORDERS_DATA_SYNC_ENABLED_OPTION === $option && 'no' === $value ) {
|
||||||
$this->data_synchronizer->cleanup_synchronization_state();
|
$this->data_synchronizer->cleanup_synchronization_state();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -472,7 +454,7 @@ class CustomOrdersTableController {
|
||||||
* @throws \Exception Attempt to change the authoritative orders table while orders sync is pending.
|
* @throws \Exception Attempt to change the authoritative orders table while orders sync is pending.
|
||||||
*/
|
*/
|
||||||
private function process_pre_update_option( $value, $option, $old_value ) {
|
private function process_pre_update_option( $value, $option, $old_value ) {
|
||||||
if ( $option !== self::CUSTOM_ORDERS_TABLE_USAGE_ENABLED_OPTION || $value === $old_value || $old_value === false ) {
|
if ( self::CUSTOM_ORDERS_TABLE_USAGE_ENABLED_OPTION !== $option || $value === $old_value || false === $old_value ) {
|
||||||
return $value;
|
return $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -537,6 +519,24 @@ class CustomOrdersTableController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the 'woocommerce_feature_enabled_changed' action,
|
||||||
|
* if the custom orders table feature is enabled create the database tables if they don't exist.
|
||||||
|
*
|
||||||
|
* @param string $feature_id The id of the feature that is being enabled or disabled.
|
||||||
|
* @param bool $is_enabled True if the feature is being enabled, false if it's being disabled.
|
||||||
|
*/
|
||||||
|
private function handle_feature_enabled_changed( $feature_id, $is_enabled ): void {
|
||||||
|
if ( 'custom_order_tables' !== $feature_id || ! $is_enabled ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! $this->data_synchronizer->check_orders_table_exists() ) {
|
||||||
|
update_option( DataSynchronizer::ORDERS_DATA_SYNC_ENABLED_OPTION, 'no' );
|
||||||
|
$this->create_custom_orders_tables( false );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler for the woocommerce_after_register_post_type post,
|
* Handler for the woocommerce_after_register_post_type post,
|
||||||
* registers the post type for placeholder orders.
|
* registers the post type for placeholder orders.
|
||||||
|
|
|
@ -280,6 +280,7 @@ WHERE
|
||||||
AND orders.id IS NULL",
|
AND orders.id IS NULL",
|
||||||
$order_post_types
|
$order_post_types
|
||||||
);
|
);
|
||||||
|
// phpcs:enable WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
|
||||||
break;
|
break;
|
||||||
case self::ID_TYPE_MISSING_IN_POSTS_TABLE:
|
case self::ID_TYPE_MISSING_IN_POSTS_TABLE:
|
||||||
$sql = "
|
$sql = "
|
||||||
|
|
Loading…
Reference in New Issue