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:
Vedanshu Jain 2020-06-05 15:15:39 +05:30 committed by GitHub
commit 6564847802
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 203 additions and 0 deletions

View File

@ -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.
* *

View File

@ -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>

View File

@ -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

View File

@ -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.
* *

View File

@ -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() );
}
}