Merge pull request #26454 from woocommerce/enhancement/verify-db
Add `verify_base_db` method to check if all base tables are present.
This commit is contained in:
commit
6564847802
|
@ -40,6 +40,7 @@ class WC_Admin_Notices {
|
||||||
'maxmind_license_key' => 'maxmind_missing_license_key_notice',
|
'maxmind_license_key' => 'maxmind_missing_license_key_notice',
|
||||||
'redirect_download_method' => 'redirect_download_method_notice',
|
'redirect_download_method' => 'redirect_download_method_notice',
|
||||||
'uploads_directory_is_unprotected' => 'uploads_directory_is_unprotected_notice',
|
'uploads_directory_is_unprotected' => 'uploads_directory_is_unprotected_notice',
|
||||||
|
'base_tables_missing' => 'base_tables_missing_notice',
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -506,6 +507,21 @@ class WC_Admin_Notices {
|
||||||
include dirname( __FILE__ ) . '/views/html-notice-uploads-directory-is-unprotected.php';
|
include dirname( __FILE__ ) . '/views/html-notice-uploads-directory-is-unprotected.php';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notice about base tables missing.
|
||||||
|
*/
|
||||||
|
public static function base_tables_missing_notice() {
|
||||||
|
$notice_dismissed = apply_filters(
|
||||||
|
'woocommerce_hide_base_tables_missing_nag',
|
||||||
|
get_user_meta( get_current_user_id(), 'dismissed_base_tables_missing_notice', true )
|
||||||
|
);
|
||||||
|
if ( $notice_dismissed ) {
|
||||||
|
self::remove_notice( 'base_tables_missing' );
|
||||||
|
}
|
||||||
|
|
||||||
|
include dirname( __FILE__ ) . '/views/html-notice-base-table-missing.php';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if the store is running SSL.
|
* Determine if the store is running SSL.
|
||||||
*
|
*
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Admin View: Notice - Base table missing.
|
||||||
|
*
|
||||||
|
* @package WooCommerce\Admin
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
|
?>
|
||||||
|
<div class="updated woocommerce-message">
|
||||||
|
<a class="woocommerce-message-close notice-dismiss" href="<?php echo esc_url( wp_nonce_url( add_query_arg( 'wc-hide-notice', 'base_tables_missing' ), 'woocommerce_hide_notices_nonce', '_wc_notice_nonce' ) ); ?>">
|
||||||
|
<?php esc_html_e( 'Dismiss', 'woocommerce' ); ?>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<strong><?php esc_html_e( 'Database tables missing', 'woocommerce' ); ?></strong>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<?php
|
||||||
|
$verify_db_tool_available = array_key_exists( 'verify_db_tables', WC_Admin_Status::get_tools() );
|
||||||
|
$missing_tables = get_option( 'woocommerce_schema_missing_tables' );
|
||||||
|
if ( $verify_db_tool_available ) {
|
||||||
|
echo wp_kses_post(
|
||||||
|
sprintf(
|
||||||
|
/* translators: %1%s: Missing tables (seperated by ",") %2$s: Link to check again */
|
||||||
|
__( 'One or more tables required for WooCommerce to function are missing, some features may not work as expected. Missing tables: %1$s. <a href="%2$s">Check again.</a>', 'woocommerce' ),
|
||||||
|
esc_html( implode( ', ', $missing_tables ) ),
|
||||||
|
wp_nonce_url( admin_url( 'admin.php?page=wc-status&tab=tools&action=verify_db_tables' ), 'debug_action' )
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
echo wp_kses_post(
|
||||||
|
sprintf(
|
||||||
|
/* translators: %1%s: Missing tables (seperated by ",") */
|
||||||
|
__( 'One or more tables required for WooCommerce to function are missing, some features may not work as expected. Missing tables: %1$s.', 'woocommerce' ),
|
||||||
|
esc_html( implode( ', ', $missing_tables ) )
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</p>
|
||||||
|
</div>
|
|
@ -287,6 +287,7 @@ class WC_Install {
|
||||||
WC()->wpdb_table_fix();
|
WC()->wpdb_table_fix();
|
||||||
self::remove_admin_notices();
|
self::remove_admin_notices();
|
||||||
self::create_tables();
|
self::create_tables();
|
||||||
|
self::verify_base_tables();
|
||||||
self::create_options();
|
self::create_options();
|
||||||
self::create_roles();
|
self::create_roles();
|
||||||
self::setup_environment();
|
self::setup_environment();
|
||||||
|
@ -303,6 +304,43 @@ class WC_Install {
|
||||||
do_action( 'woocommerce_installed' );
|
do_action( 'woocommerce_installed' );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if all the base tables are present.
|
||||||
|
*
|
||||||
|
* @param bool $modify_notice Whether to modify notice based on if all tables are present.
|
||||||
|
* @param bool $execute Whether to execute get_schema queries as well.
|
||||||
|
*
|
||||||
|
* @return array List of querues.
|
||||||
|
*/
|
||||||
|
public static function verify_base_tables( $modify_notice = true, $execute = false ) {
|
||||||
|
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
|
||||||
|
|
||||||
|
if ( $execute ) {
|
||||||
|
self::create_tables();
|
||||||
|
}
|
||||||
|
$queries = dbDelta( self::get_schema(), false );
|
||||||
|
$missing_tables = array();
|
||||||
|
foreach ( $queries as $table_name => $result ) {
|
||||||
|
if ( "Created table $table_name" === $result ) {
|
||||||
|
$missing_tables[] = $table_name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( 0 < count( $missing_tables ) ) {
|
||||||
|
if ( $modify_notice ) {
|
||||||
|
WC_Admin_Notices::add_notice( 'base_tables_missing' );
|
||||||
|
}
|
||||||
|
update_option( 'woocommerce_schema_missing_tables', $missing_tables );
|
||||||
|
} else {
|
||||||
|
if ( $modify_notice ) {
|
||||||
|
WC_Admin_Notices::remove_notice( 'base_tables_missing' );
|
||||||
|
}
|
||||||
|
update_option( 'woocommerce_schema_version', WC()->db_version );
|
||||||
|
delete_option( 'woocommerce_schema_missing_tables' );
|
||||||
|
}
|
||||||
|
return $missing_tables;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reset any notices added to admin.
|
* Reset any notices added to admin.
|
||||||
*
|
*
|
||||||
|
@ -621,6 +659,11 @@ class WC_Install {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set up the database tables which the plugin needs to function.
|
* Set up the database tables which the plugin needs to function.
|
||||||
|
* WARNING: If you are modifying this method, make sure that its safe to call regardless of the state of database.
|
||||||
|
*
|
||||||
|
* This is called from `install` method and is executed in-sync when WC is installed or updated. This can also be called optionally from `verify_base_tables`.
|
||||||
|
*
|
||||||
|
* TODO: Add all crucial tables that we have created from workers in the past.
|
||||||
*
|
*
|
||||||
* Tables:
|
* Tables:
|
||||||
* woocommerce_attribute_taxonomies - Table for storing attribute taxonomies - these are user defined
|
* woocommerce_attribute_taxonomies - Table for storing attribute taxonomies - these are user defined
|
||||||
|
|
|
@ -22,6 +22,15 @@ final class WooCommerce {
|
||||||
*/
|
*/
|
||||||
public $version = '4.3.0';
|
public $version = '4.3.0';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WooCommerce Schema version.
|
||||||
|
*
|
||||||
|
* @since 4.3 started with version string 430.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public $db_version = '430';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The single instance of the class.
|
* The single instance of the class.
|
||||||
*
|
*
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Class WCInstallTest file.
|
||||||
|
*
|
||||||
|
* @package WooCommerce|Tests|WCInstallTest.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Automattic\WooCommerce;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class WC_Tests_WC_Helper.
|
||||||
|
*/
|
||||||
|
class WCInstallTest extends \WC_Unit_Test_Case {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test if verify base table can detect missing table and adds/remove a notice.
|
||||||
|
*/
|
||||||
|
public function test_verify_base_tables_adds_and_remove_notice() {
|
||||||
|
global $wpdb;
|
||||||
|
|
||||||
|
// Remove drop filter because we do want to drop temp table if it exists.
|
||||||
|
// This filter was added to only allow dropping temporary tables which will then be rollbacked after the test.
|
||||||
|
remove_filter( 'query', array( $this, '_drop_temporary_tables' ) );
|
||||||
|
|
||||||
|
$original_table_name = "{$wpdb->prefix}wc_tax_rate_classes";
|
||||||
|
$changed_table_name = "{$wpdb->prefix}wc_tax_rate_classes_2";
|
||||||
|
$clear_query = 'DROP TABLE IF EXISTS %s;';
|
||||||
|
$rename_table_query = 'RENAME TABLE %s to %s;';
|
||||||
|
|
||||||
|
// Workaround to call a private function.
|
||||||
|
$schema = function () {
|
||||||
|
return static::get_schema();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Rename a base table to simulate it as non-existing.
|
||||||
|
dbDelta( $schema->call( new \WC_Install() ) ); // Restore correct state.
|
||||||
|
$wpdb->query( sprintf( $clear_query, $changed_table_name ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
||||||
|
$wpdb->query( sprintf( $rename_table_query, $original_table_name, $changed_table_name ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
||||||
|
|
||||||
|
$missing_tables = \WC_Install::verify_base_tables();
|
||||||
|
|
||||||
|
$wpdb->query( sprintf( $rename_table_query, $changed_table_name, $original_table_name ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
||||||
|
add_filter( 'query', array( $this, '_drop_temporary_tables' ) );
|
||||||
|
|
||||||
|
$this->assertContains( $original_table_name, $missing_tables );
|
||||||
|
$this->assertContains( 'base_tables_missing', \WC_Admin_Notices::get_notices() );
|
||||||
|
|
||||||
|
// Ideally, no missing table anymore because we have switched back table name.
|
||||||
|
$missing_tables = \WC_Install::verify_base_tables();
|
||||||
|
|
||||||
|
$this->assertNotContains( $original_table_name, $missing_tables );
|
||||||
|
$this->assertNotContains( 'base_tables_missing', \WC_Admin_Notices::get_notices() );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test if verify base table can fix the table as well.
|
||||||
|
*/
|
||||||
|
public function test_verify_base_tables_fix_tables() {
|
||||||
|
global $wpdb;
|
||||||
|
|
||||||
|
// Remove drop filter because we do want to drop temp table if it exists.
|
||||||
|
// This filter was added to only allow dropping temporary tables which will then be rollbacked after the test.
|
||||||
|
remove_filter( 'query', array( $this, '_drop_temporary_tables' ) );
|
||||||
|
|
||||||
|
$original_table_name = "{$wpdb->prefix}wc_tax_rate_classes";
|
||||||
|
$changed_table_name = "{$wpdb->prefix}wc_tax_rate_classes_2";
|
||||||
|
$clear_query = 'DROP TABLE IF EXISTS %s;';
|
||||||
|
$rename_table_query = 'RENAME TABLE %s to %s;';
|
||||||
|
|
||||||
|
// Workaround to call a private function.
|
||||||
|
$schema = function () {
|
||||||
|
return static::get_schema();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Rename a base table to simulate it as non-existing.
|
||||||
|
dbDelta( $schema->call( new \WC_Install() ) ); // Restore correct state.
|
||||||
|
$wpdb->query( sprintf( $clear_query, $changed_table_name ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
||||||
|
$wpdb->query( sprintf( $rename_table_query, $original_table_name, $changed_table_name ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
||||||
|
|
||||||
|
$missing_tables = \WC_Install::verify_base_tables( true, true );
|
||||||
|
|
||||||
|
$wpdb->query( sprintf( $clear_query, $original_table_name ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
||||||
|
$wpdb->query( sprintf( $rename_table_query, $changed_table_name, $original_table_name ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
||||||
|
add_filter( 'query', array( $this, '_drop_temporary_tables' ) );
|
||||||
|
|
||||||
|
// Ideally, no missing table because verify base tables created the table as well.
|
||||||
|
$this->assertNotContains( $original_table_name, $missing_tables );
|
||||||
|
$this->assertNotContains( 'base_tables_missing', \WC_Admin_Notices::get_notices() );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue