Skip to content

Commit

Permalink
Install the Legacy REST API plugin on WooCommerce upgrade if needed (#…
Browse files Browse the repository at this point in the history
…45570)

The plugin is installed and activated if it's not installed already
and either the Legacy REST API is installed in the site
or there's at least one webhook that uses the Legacy REST API
code for the payload (disabled webhooks also count).

Also the WC_Admin_Notices::remove_notices method is added.

---------

Co-authored-by: Corey McKrill <916023+coreymckrill@users.noreply.github.com>
  • Loading branch information
Konamiman and coreymckrill committed Mar 20, 2024
1 parent 3c15ced commit 1adeaf2
Show file tree
Hide file tree
Showing 5 changed files with 231 additions and 56 deletions.
4 changes: 4 additions & 0 deletions plugins/woocommerce/changelog/pr-45570
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: add

Install the Legacy REST API plugin on WooCommerce upgrade if needed
50 changes: 36 additions & 14 deletions plugins/woocommerce/includes/admin/class-wc-admin-notices.php
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,28 @@ public static function remove_notice( $name, $force_save = false ) {
}
}

/**
* Remove a given set of notices.
*
* An array of notice names or a regular expression string can be passed, in the later case
* all the notices whose name matches the regular expression will be removed.
*
* @param array|string $names_array_or_regex An array of notice names, or a string representing a regular expression.
* @param bool $force_save Force saving inside this method instead of at the 'shutdown'.
* @return void
*/
public static function remove_notices( $names_array_or_regex, $force_save = false ) {
if ( ! is_array( $names_array_or_regex ) ) {
$names_array_or_regex = array_filter( self::get_notices(), fn( $notice_name ) => 1 === preg_match( $names_array_or_regex, $notice_name ) );
}
self::set_notices( array_diff( self::get_notices(), $names_array_or_regex ) );

if ( $force_save ) {
// Adding early save to prevent more race conditions with notices.
self::store_notices();
}
}

/**
* See if a notice is being shown.
*
Expand Down Expand Up @@ -391,7 +413,7 @@ public static function output_custom_notices() {
$notice_html = get_option( 'woocommerce_admin_notice_' . $notice );

if ( $notice_html ) {
include dirname( __FILE__ ) . '/views/html-notice-custom.php';
include __DIR__ . '/views/html-notice-custom.php';
}
}
}
Expand All @@ -413,12 +435,12 @@ public static function update_notice() {

// phpcs:ignore WordPress.Security.NonceVerification.Recommended
if ( $next_scheduled_date || ! empty( $_GET['do_update_woocommerce'] ) ) {
include dirname( __FILE__ ) . '/views/html-notice-updating.php';
include __DIR__ . '/views/html-notice-updating.php';
} else {
include dirname( __FILE__ ) . '/views/html-notice-update.php';
include __DIR__ . '/views/html-notice-update.php';
}
} else {
include dirname( __FILE__ ) . '/views/html-notice-updated.php';
include __DIR__ . '/views/html-notice-updated.php';
}
}

Expand Down Expand Up @@ -463,7 +485,7 @@ public static function template_file_check_notice() {
}

if ( $outdated ) {
include dirname( __FILE__ ) . '/views/html-notice-template-check.php';
include __DIR__ . '/views/html-notice-template-check.php';
} else {
self::remove_notice( 'template_files' );
}
Expand All @@ -486,7 +508,7 @@ public static function legacy_shipping_notice() {
}

if ( $enabled ) {
include dirname( __FILE__ ) . '/views/html-notice-legacy-shipping.php';
include __DIR__ . '/views/html-notice-legacy-shipping.php';
} else {
self::remove_notice( 'template_files' );
}
Expand All @@ -502,7 +524,7 @@ public static function no_shipping_methods_notice() {
$method_count = wc_get_shipping_method_count();

if ( $product_count->publish > 0 && 0 === $method_count ) {
include dirname( __FILE__ ) . '/views/html-notice-no-shipping-methods.php';
include __DIR__ . '/views/html-notice-no-shipping-methods.php';
}

if ( $method_count > 0 ) {
Expand All @@ -515,7 +537,7 @@ public static function no_shipping_methods_notice() {
* Notice shown when regenerating thumbnails background process is running.
*/
public static function regenerating_thumbnails_notice() {
include dirname( __FILE__ ) . '/views/html-notice-regenerating-thumbnails.php';
include __DIR__ . '/views/html-notice-regenerating-thumbnails.php';
}

/**
Expand All @@ -526,7 +548,7 @@ public static function secure_connection_notice() {
return;
}

include dirname( __FILE__ ) . '/views/html-notice-secure-connection.php';
include __DIR__ . '/views/html-notice-secure-connection.php';
}

/**
Expand All @@ -541,7 +563,7 @@ public static function regenerating_lookup_table_notice() {
return;
}

include dirname( __FILE__ ) . '/views/html-notice-regenerating-lookup-table.php';
include __DIR__ . '/views/html-notice-regenerating-lookup-table.php';
}

/**
Expand Down Expand Up @@ -628,7 +650,7 @@ public static function maxmind_missing_license_key_notice() {
return;
}

include dirname( __FILE__ ) . '/views/html-notice-maxmind-license-key.php';
include __DIR__ . '/views/html-notice-maxmind-license-key.php';
}

/**
Expand All @@ -642,7 +664,7 @@ public static function redirect_download_method_notice() {
return;
}

include dirname( __FILE__ ) . '/views/html-notice-redirect-only-download.php';
include __DIR__ . '/views/html-notice-redirect-only-download.php';
}

/**
Expand All @@ -656,7 +678,7 @@ public static function uploads_directory_is_unprotected_notice() {
return;
}

include dirname( __FILE__ ) . '/views/html-notice-uploads-directory-is-unprotected.php';
include __DIR__ . '/views/html-notice-uploads-directory-is-unprotected.php';
}

/**
Expand All @@ -671,7 +693,7 @@ public static function base_tables_missing_notice() {
self::remove_notice( 'base_tables_missing' );
}

include dirname( __FILE__ ) . '/views/html-notice-base-table-missing.php';
include __DIR__ . '/views/html-notice-base-table-missing.php';
}

/**
Expand Down
144 changes: 139 additions & 5 deletions plugins/woocommerce/includes/class-wc-install.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use Automattic\WooCommerce\Internal\WCCom\ConnectionHelper as WCConnectionHelper;
use Automattic\WooCommerce\Internal\Traits\AccessiblePrivateMethods;
use Automattic\WooCommerce\Utilities\OrderUtil;
use Automattic\WooCommerce\Internal\Utilities\PluginInstaller;

defined( 'ABSPATH' ) || exit;

Expand Down Expand Up @@ -281,6 +282,7 @@ public static function init() {
add_filter( 'wpmu_drop_tables', array( __CLASS__, 'wpmu_drop_tables' ) );
add_filter( 'cron_schedules', array( __CLASS__, 'cron_schedules' ) );
self::add_action( 'admin_init', array( __CLASS__, 'newly_installed' ) );
self::add_action( 'woocommerce_activate_legacy_rest_api_plugin', array( __CLASS__, 'maybe_install_legacy_api_plugin' ) );
}

/**
Expand Down Expand Up @@ -363,7 +365,7 @@ public static function run_manual_database_update() {
* @since 3.6.0
*/
public static function run_update_callback( $update_callback ) {
include_once dirname( __FILE__ ) . '/wc-update-functions.php';
include_once __DIR__ . '/wc-update-functions.php';

if ( is_callable( $update_callback ) ) {
self::run_update_callback_start( $update_callback );
Expand Down Expand Up @@ -453,6 +455,7 @@ public static function install() {
self::update_wc_version();
self::maybe_update_db_version();
self::maybe_set_store_id();
self::maybe_install_legacy_api_plugin();

delete_transient( 'wc_installing' );

Expand Down Expand Up @@ -550,7 +553,8 @@ public static function verify_base_tables( $modify_notice = true, $execute = fal
* @since 3.2.0
*/
private static function remove_admin_notices() {
include_once dirname( __FILE__ ) . '/admin/class-wc-admin-notices.php';
include_once __DIR__ . '/admin/class-wc-admin-notices.php';

WC_Admin_Notices::remove_all_notices();
}

Expand Down Expand Up @@ -679,7 +683,7 @@ private static function update() {
),
'woocommerce-db-updates'
);
$loop++;
++$loop;
}
}
}
Expand Down Expand Up @@ -799,7 +803,7 @@ public static function create_pages() {
// Set the locale to the store locale to ensure pages are created in the correct language.
wc_switch_to_site_locale();

include_once dirname( __FILE__ ) . '/admin/wc-admin-functions.php';
include_once __DIR__ . '/admin/wc-admin-functions.php';

/**
* Determines the cart shortcode tag used for the cart page.
Expand Down Expand Up @@ -887,7 +891,7 @@ public static function create_pages() {
*/
private static function create_options() {
// Include settings so that we can run through defaults.
include_once dirname( __FILE__ ) . '/admin/class-wc-admin-settings.php';
include_once __DIR__ . '/admin/class-wc-admin-settings.php';

$settings = WC_Admin_Settings::get_settings_pages();

Expand Down Expand Up @@ -1163,6 +1167,136 @@ public static function create_terms() {
}
}

/**
* Install and activate the WooCommerce Legacy REST API plugin from the WordPress.org directory if all the following is true:
*
* 1. We are in a WooCommerce upgrade process (not a new install).
* 2. The 'woocommerce_skip_legacy_rest_api_plugin_auto_install' filter returns false (which is the default).
* 3. The plugin is not installed and active already (but see note about multisite below).
* 4. The Legacy REST API is enabled in the site OR the site has at least one webhook defined that uses the Legacy REST API payload format (disabled webhooks also count).
*
* In multisite setups it could happen that the plugin was installed by an installation process performed in another site.
* In this case we check if the plugin was autoinstalled in such a way, and if so we activate it if the conditions are fulfilled.
*/
private static function maybe_install_legacy_api_plugin() {
if ( self::is_new_install() ) {
return;
}

/**
* Filter to skip the automatic installation of the WooCommerce Legacy REST API plugin
* from the WordPress.org plugins directory.
*
* @since 8.8.0
*
* @param bool $skip_auto_install False, defaulting to "don't skip the plugin automatic installation".
* @returns bool True to skip the plugin automatic installation, false to install the plugin if necessary.
*/
if ( apply_filters( 'woocommerce_skip_legacy_rest_api_plugin_auto_install', false ) ) {
return;
}

if ( ( 'yes' !== get_option( 'woocommerce_api_enabled' ) &&
0 === wc_get_container()->get( Automattic\WooCommerce\Internal\Utilities\WebhookUtil::class )->get_legacy_webhooks_count( true ) ) ) {
return;
}

$plugin_name = 'woocommerce-legacy-rest-api/woocommerce-legacy-rest-api.php';

wp_clean_plugins_cache();
if ( ! function_exists( 'get_plugins' ) ) {
require_once ABSPATH . 'wp-admin/includes/plugin.php';
}
if ( isset( get_plugins()[ $plugin_name ] ) ) {
if ( ! ( get_site_option( 'woocommerce_autoinstalled_plugins', array() )[ $plugin_name ] ?? null ) ) {
// The plugin was installed manually so let's not interfere.
return;
}

if ( in_array( $plugin_name, wp_get_active_and_valid_plugins(), true ) ) {
return;
}

// The plugin was automatically installed in a different installation process - can happen in multisite.
$install_ok = true;
} else {
$install_result = wc_get_container()->get( PluginInstaller::class )->install_plugin(
'https://downloads.wordpress.org/plugin/woocommerce-legacy-rest-api.latest-stable.zip',
array(
'info_link' => 'https://developer.woo.com/2023/10/03/the-legacy-rest-api-will-move-to-a-dedicated-extension-in-woocommerce-9-0/',
)
);

if ( $install_result['already_installing'] ?? null ) {
// The plugin is in the process of being installed already (can happen in multisite),
// but we still need to activate it for ourselves once it's installed.
as_schedule_single_action( time() + 10, 'woocommerce_activate_legacy_rest_api_plugin' );
return;
}

$install_ok = $install_result['install_ok'];
}

$plugin_page_url = 'https://wordpress.org/plugins/woocommerce-legacy-rest-api/';
$blog_post_url = 'https://developer.woo.com/2023/10/03/the-legacy-rest-api-will-move-to-a-dedicated-extension-in-woocommerce-9-0/';
$site_legacy_api_settings_url = get_admin_url( null, '/admin.php?page=wc-settings&tab=advanced&section=legacy_api' );
$site_webhooks_settings_url = get_admin_url( null, '/admin.php?page=wc-settings&tab=advanced&section=webhooks' );
$site_logs_url = get_admin_url( null, '/admin.php?page=wc-status&tab=logs' );

if ( $install_ok ) {
$activation_result = activate_plugin( $plugin_name );
if ( $activation_result instanceof \WP_Error ) {
$message = sprintf(
/* translators: 1 = URL of Legacy REST API plugin page, 2 = URL of Legacy API settings in current site, 3 = URL of webhooks settings in current site, 4 = URL of logs page in current site, 5 = URL of plugins page in current site, 6 = URL of blog post about the Legacy REST API removal */
__( '⚠️ WooCommerce installed <a href="%1$s">the Legacy REST API plugin</a> because this site has <a href="%2$s">the Legacy REST API enabled</a> or has <a href="%3$s">legacy webhooks defined</a>, but it failed to activate it (see error details in <a href="%4$s">the WooCommerce logs</a>). Please go to <a href="%5$s">the plugins page</a> and activate it manually. <a href="%6$s">More information</a>', 'woocommerce' ),
$plugin_page_url,
$site_legacy_api_settings_url,
$site_webhooks_settings_url,
$site_logs_url,
get_admin_url( null, '/plugins.php' ),
$blog_post_url
);
$notice_name = 'woocommerce_legacy_rest_api_plugin_activation_failed';
wc_get_logger()->error(
__( 'WooCommerce installed the Legacy REST API plugin but failed to activate it, see context for more details.', 'woocommerce' ),
array(
'source' => 'plugin_auto_installs',
'error' => $activation_result,
)
);
} else {
$message = sprintf(
/* translators: 1 = URL of Legacy REST API plugin page, 2 = URL of Legacy API settings in current site, 3 = URL of webhooks settings in current site, 4 = URL of blog post about the Legacy REST API removal */
__( 'ℹ️ WooCommerce installed and activated <a href="%1$s">the Legacy REST API plugin</a> because this site has <a href="%2$s">the Legacy REST API enabled</a> or has <a href="%3$s">legacy webhooks defined</a>. <a href="%4$s">More information</a>', 'woocommerce' ),
$plugin_page_url,
$site_legacy_api_settings_url,
$site_webhooks_settings_url,
$blog_post_url
);
$notice_name = 'woocommerce_legacy_rest_api_plugin_activated';
wc_get_logger()->info( 'WooCommerce activated the Legacy REST API plugin in this site.', array( 'source' => 'plugin_auto_installs' ) );
}

\WC_Admin_Notices::add_custom_notice( $notice_name, $message );
} else {
$message = sprintf(
/* translators: 1 = URL of Legacy REST API plugin page, 2 = URL of Legacy API settings in current site, 3 = URL of webhooks settings in current site, 4 = URL of logs page in current site, 5 = URL of blog post about the Legacy REST API removal */
__( '⚠️ WooCommerce attempted to install <a href="%1$s">the Legacy REST API plugin</a> because this site has <a href="%2$s">the Legacy REST API enabled</a> or has <a href="%3$s">legacy webhooks defined</a>, but the installation failed (see error details in <a href="%4$s">the WooCommerce logs</a>). Please install and activate the plugin manually. <a href="%5$s">More information</a>', 'woocommerce' ),
$plugin_page_url,
$site_legacy_api_settings_url,
$site_webhooks_settings_url,
$site_logs_url,
$blog_post_url
);

\WC_Admin_Notices::add_custom_notice( 'woocommerce_legacy_rest_api_plugin_install_failed', $message );

// Note that we aren't adding an entry to the error log because PluginInstaller->install_plugin will have done that already.
}

\WC_Admin_Notices::store_notices();
}

/**
* 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.
Expand Down

0 comments on commit 1adeaf2

Please sign in to comment.