{ __(
- 'Connect your account to get updates, manage your subscriptions, and get seamless support. Once connected, your Woo.com subscriptions will appear here.',
+ "Connect your store to Woo.com using the Woo.com Update Manager. Once connected, you'll be able to manage your subscriptions, receive product updates, and access streamlined support from this screen.",
'woocommerce'
) }
+
+
+
+
+
+ );
+ }
+
+ return null;
+}
diff --git a/plugins/woocommerce-admin/client/marketplace/components/woo-update-manager-plugin/plugin-install-notice.tsx b/plugins/woocommerce-admin/client/marketplace/components/woo-update-manager-plugin/plugin-install-notice.tsx
new file mode 100644
index 00000000000..4533dbdbd56
--- /dev/null
+++ b/plugins/woocommerce-admin/client/marketplace/components/woo-update-manager-plugin/plugin-install-notice.tsx
@@ -0,0 +1,84 @@
+/**
+ * External dependencies
+ */
+import { Button, Notice } from '@wordpress/components';
+import { __ } from '@wordpress/i18n';
+/**
+ * Internal dependencies
+ */
+import sanitizeHTML from '../../../lib/sanitize-html';
+import { getAdminSetting } from '../../../utils/admin-settings';
+import {
+ WP_ADMIN_PLUGIN_LIST_URL,
+ WOO_CONNECT_PLUGIN_DOWNLOAD_URL,
+} from '../constants';
+import './woo-update-manager-plugin.scss';
+
+export default function PluginInstallNotice() {
+ const wccomSettings = getAdminSetting( 'wccomHelper', {} );
+ if ( ! wccomSettings?.isConnected ) {
+ return null;
+ }
+
+ if (
+ ! wccomSettings?.wooUpdateManagerActive &&
+ ! wccomSettings?.wooUpdateManagerInstalled
+ ) {
+ return (
+
+
+ Woo.com Update Manager to continue receiving the updates and streamlined support included in your Woo.com subscriptions. Alternatively, you can download and install it manually.',
+ 'woocommerce'
+ )
+ ) }
+ >
+
+
+
+
+
+
+ );
+ } else if (
+ wccomSettings?.wooUpdateManagerInstalled &&
+ ! wccomSettings?.wooUpdateManagerActive
+ ) {
+ return (
+
+
+ Woo.com Update Manager to continue receiving the updates and streamlined support included in your Woo.com subscriptions.',
+ 'woocommerce'
+ )
+ ) }
+ >
+
+
+
+
+
+ );
+ }
+
+ return null;
+}
diff --git a/plugins/woocommerce-admin/client/marketplace/components/woo-update-manager-plugin/woo-update-manager-plugin.scss b/plugins/woocommerce-admin/client/marketplace/components/woo-update-manager-plugin/woo-update-manager-plugin.scss
new file mode 100644
index 00000000000..d2aa47cfc69
--- /dev/null
+++ b/plugins/woocommerce-admin/client/marketplace/components/woo-update-manager-plugin/woo-update-manager-plugin.scss
@@ -0,0 +1,67 @@
+@import '@wordpress/base-styles/_colors.native.scss';
+@import '../../stylesheets/_variables.scss';
+
+.woocommerce-marketplace__woo-update-manager-plugin__notices {
+ .components-notice {
+ margin-left: 0;
+ margin-right: 0;
+ background-color: #fff;
+ box-shadow: 0 2px 6px 0 rgba($gray-100, 0.05);
+ border: 1px solid var(--gutenberg-gray-100, #f0f0f0);
+ padding-right: $grid-unit-15;
+ position: relative;
+
+ &::before {
+ content: '';
+ display: block;
+ width: 4px;
+ height: 100%;
+ background-color: var(--wp-admin-theme-color, #007cba);
+ position: absolute;
+ left: 0;
+ top: 0;
+ bottom: 0;
+ }
+
+ &.is-error::before {
+ background-color: $alert-red;
+ }
+
+ .components-notice__content {
+ align-items: center;
+ gap: $grid-unit-15;
+ padding: 0;
+ margin-left: 12px;
+ }
+
+ .components-notice__buttons {
+ margin-top: 12px;
+ }
+ }
+
+ .components-button {
+ &.is-link {
+ text-decoration: none;
+ padding: 6px 12px;
+ }
+ }
+}
+
+.woocommerce-marketplace__woo-update-manager-plugin__notices {
+ margin-bottom: $grid-unit-50;
+}
+
+.woocommerce-marketplace__discover {
+ .woocommerce-marketplace__woo-update-manager-plugin__notices {
+ margin-bottom: 0;
+ }
+}
+
+.woocommerce-marketplace__header-account-modal {
+ .components-button {
+ &.is-link {
+ text-decoration: none;
+ padding: 6px 12px;
+ }
+ }
+}
diff --git a/plugins/woocommerce/changelog/remove-updates-for-woo.com-extensions b/plugins/woocommerce/changelog/remove-updates-for-woo.com-extensions
new file mode 100644
index 00000000000..0fdd0a0de82
--- /dev/null
+++ b/plugins/woocommerce/changelog/remove-updates-for-woo.com-extensions
@@ -0,0 +1,4 @@
+Significance: major
+Type: update
+
+Remove the ability to update Woo.com plugins that are not available under WordPress.org plugin directory
diff --git a/plugins/woocommerce/includes/admin/helper/class-wc-helper-admin.php b/plugins/woocommerce/includes/admin/helper/class-wc-helper-admin.php
index 6d58b6eea51..fe3dab53335 100644
--- a/plugins/woocommerce/includes/admin/helper/class-wc-helper-admin.php
+++ b/plugins/woocommerce/includes/admin/helper/class-wc-helper-admin.php
@@ -49,13 +49,17 @@ class WC_Helper_Admin {
);
$settings['wccomHelper'] = array(
- 'isConnected' => WC_Helper::is_site_connected(),
- 'connectURL' => self::get_connection_url(),
- 'userEmail' => $auth_user_email,
- 'userAvatar' => get_avatar_url( $auth_user_email, array( 'size' => '48' ) ),
- 'storeCountry' => wc_get_base_location()['country'],
- 'inAppPurchaseURLParams' => WC_Admin_Addons::get_in_app_purchase_url_params(),
- 'installedProducts' => $installed_products,
+ 'isConnected' => WC_Helper::is_site_connected(),
+ 'connectURL' => self::get_connection_url(),
+ 'userEmail' => $auth_user_email,
+ 'userAvatar' => get_avatar_url( $auth_user_email, array( 'size' => '48' ) ),
+ 'storeCountry' => wc_get_base_location()['country'],
+ 'inAppPurchaseURLParams' => WC_Admin_Addons::get_in_app_purchase_url_params(),
+ 'installedProducts' => $installed_products,
+ 'wooUpdateManagerInstalled' => WC_Woo_Update_Manager_Plugin::is_plugin_installed(),
+ 'wooUpdateManagerActive' => WC_Woo_Update_Manager_Plugin::is_plugin_active(),
+ 'wooUpdateManagerInstallUrl' => WC_Woo_Update_Manager_Plugin::generate_install_url(),
+ 'wooUpdateManagerPluginSlug' => WC_Woo_Update_Manager_Plugin::WOO_UPDATE_MANAGER_SLUG,
);
return $settings;
diff --git a/plugins/woocommerce/includes/admin/helper/class-wc-helper-api.php b/plugins/woocommerce/includes/admin/helper/class-wc-helper-api.php
index 8798aaf8f6f..911d6d206f9 100644
--- a/plugins/woocommerce/includes/admin/helper/class-wc-helper-api.php
+++ b/plugins/woocommerce/includes/admin/helper/class-wc-helper-api.php
@@ -67,6 +67,61 @@ class WC_Helper_API {
return wp_safe_remote_request( $url, $args );
}
+ /**
+ * Create signature for a request.
+ *
+ * @param string $access_token_secret The access token secret.
+ * @param string $url The URL to add the access token and signature to.
+ * @param string $method The request method.
+ * @param array $body The body of the request.
+ * @return string The signature.
+ */
+ private static function create_request_signature( string $access_token_secret, string $url, string $method, $body = null ): string {
+
+ $request_uri = wp_parse_url( $url, PHP_URL_PATH );
+ $query_string = wp_parse_url( $url, PHP_URL_QUERY );
+
+ if ( is_string( $query_string ) ) {
+ $request_uri .= '?' . $query_string;
+ }
+
+ $data = array(
+ 'host' => wp_parse_url( $url, PHP_URL_HOST ),
+ 'request_uri' => $request_uri,
+ 'method' => $method,
+ );
+
+ if ( ! empty( $body ) ) {
+ $data['body'] = $body;
+ }
+
+ return hash_hmac( 'sha256', wp_json_encode( $data ), $access_token_secret );
+ }
+
+ /**
+ * Add the access token and signature to the provided URL.
+ *
+ * @param string $url The URL to add the access token and signature to.
+ * @return string
+ */
+ public static function add_auth_parameters( string $url ): string {
+ $auth = WC_Helper_Options::get( 'auth' );
+
+ if ( empty( $auth['access_token'] ) || empty( $auth['access_token_secret'] ) ) {
+ return false;
+ }
+
+ $signature = self::create_request_signature( (string) $auth['access_token_secret'], $url, 'GET' );
+
+ return add_query_arg(
+ array(
+ 'token' => $auth['access_token'],
+ 'signature' => $signature,
+ ),
+ $url
+ );
+ }
+
/**
* Adds authentication headers to an HTTP request.
*
@@ -81,24 +136,13 @@ class WC_Helper_API {
return false;
}
- $request_uri = parse_url( $url, PHP_URL_PATH );
- $query_string = parse_url( $url, PHP_URL_QUERY );
-
- if ( is_string( $query_string ) ) {
- $request_uri .= '?' . $query_string;
- }
-
- $data = array(
- 'host' => parse_url( $url, PHP_URL_HOST ),
- 'request_uri' => $request_uri,
- 'method' => ! empty( $args['method'] ) ? $args['method'] : 'GET',
+ $signature = self::create_request_signature(
+ (string) $auth['access_token_secret'],
+ $url,
+ ! empty( $args['method'] ) ? $args['method'] : 'GET',
+ $args['body'] ?? null
);
- if ( ! empty( $args['body'] ) ) {
- $data['body'] = $args['body'];
- }
-
- $signature = hash_hmac( 'sha256', json_encode( $data ), $auth['access_token_secret'] );
if ( empty( $args['headers'] ) ) {
$args['headers'] = array();
}
diff --git a/plugins/woocommerce/includes/admin/helper/class-wc-helper-subscriptions-api.php b/plugins/woocommerce/includes/admin/helper/class-wc-helper-subscriptions-api.php
index c19f57fc0a8..09b1b4d1ae2 100644
--- a/plugins/woocommerce/includes/admin/helper/class-wc-helper-subscriptions-api.php
+++ b/plugins/woocommerce/includes/admin/helper/class-wc-helper-subscriptions-api.php
@@ -300,7 +300,10 @@ class WC_Helper_Subscriptions_API {
);
}
- $install_url = WC_Helper::get_subscription_install_url( $subscription['product_key'] );
+ $install_url = WC_Helper::get_subscription_install_url(
+ $subscription['product_key'],
+ $subscription['product_slug']
+ );
if ( ! $install_url ) {
wp_send_json_error(
diff --git a/plugins/woocommerce/includes/admin/helper/class-wc-helper-updater.php b/plugins/woocommerce/includes/admin/helper/class-wc-helper-updater.php
index 4d583d5dde9..4cd3b6886c9 100644
--- a/plugins/woocommerce/includes/admin/helper/class-wc-helper-updater.php
+++ b/plugins/woocommerce/includes/admin/helper/class-wc-helper-updater.php
@@ -26,6 +26,16 @@ class WC_Helper_Updater {
add_action( 'pre_set_site_transient_update_themes', array( __CLASS__, 'transient_update_themes' ), 21, 1 );
add_action( 'upgrader_process_complete', array( __CLASS__, 'upgrader_process_complete' ) );
add_action( 'upgrader_pre_download', array( __CLASS__, 'block_expired_updates' ), 10, 2 );
+ add_action( 'plugins_loaded', array( __CLASS__, 'add_hook_for_modifying_update_notices' ) );
+ }
+
+ /**
+ * Add the hook for modifying default WPCore update notices on the plugins management page.
+ */
+ public static function add_hook_for_modifying_update_notices() {
+ if ( ! WC_Woo_Update_Manager_Plugin::is_plugin_active() ) {
+ add_action( 'load-plugins.php', array( __CLASS__, 'setup_update_plugins_messages' ), 11 );
+ }
}
/**
@@ -53,21 +63,25 @@ class WC_Helper_Updater {
'plugin' => $filename,
'new_version' => $data['version'],
'url' => $data['url'],
- 'package' => $data['package'],
+ 'package' => '',
'upgrade_notice' => $data['upgrade_notice'],
);
+ /**
+ * Filters the Woo plugin data before saving it in transient used for updates.
+ *
+ * @since 8.7.0
+ *
+ * @param array $item Plugin item to modify.
+ * @param array $data Subscription data fetched from Helper API for the plugin.
+ * @param int $product_id Woo product id assigned to the plugin.
+ */
+ $item = apply_filters( 'update_woo_com_subscription_details', $item, $data, $plugin['_product_id'] );
+
if ( isset( $data['requires_php'] ) ) {
$item['requires_php'] = $data['requires_php'];
}
- // We don't want to deliver a valid upgrade package when their subscription has expired.
- // To avoid the generic "no_package" error that empty strings give, we will store an
- // indication of expiration for the `upgrader_pre_download` filter to error on.
- if ( ! self::_has_active_subscription( $plugin['_product_id'] ) ) {
- $item['package'] = 'woocommerce-com-expired-' . $plugin['_product_id'];
- }
-
if ( $transient instanceof stdClass ) {
if ( version_compare( $plugin['Version'], $data['version'], '<' ) ) {
$transient->response[ $filename ] = (object) $item;
@@ -113,9 +127,16 @@ class WC_Helper_Updater {
'package' => '',
);
- if ( self::_has_active_subscription( $theme['_product_id'] ) ) {
- $item['package'] = $data['package'];
- }
+ /**
+ * Filters the Woo plugin data before saving it in transient used for updates.
+ *
+ * @since 8.7.0
+ *
+ * @param array $item Plugin item to modify.
+ * @param array $data Subscription data fetched from Helper API for the plugin.
+ * @param int $product_id Woo product id assigned to the plugin.
+ */
+ $item = apply_filters( 'update_woo_com_subscription_details', $item, $data, $theme['_product_id'] );
if ( version_compare( $theme['Version'], $data['version'], '<' ) ) {
$transient->response[ $slug ] = $item;
@@ -128,6 +149,53 @@ class WC_Helper_Updater {
return $transient;
}
+ /**
+ * Runs on load-plugins.php, adds a hook to show a custom plugin update message for Woo.com hosted plugins.
+ *
+ * @return void.
+ */
+ public static function setup_update_plugins_messages() {
+ foreach ( WC_Helper::get_local_woo_plugins() as $plugin ) {
+ $filename = $plugin['_filename'];
+ add_action( 'in_plugin_update_message-' . $filename, array( __CLASS__, 'add_install_marketplace_plugin_message' ), 10, 2 );
+ }
+ }
+
+ /**
+ * Runs on in_plugin_update_message-{file-name}, show a message to install the Woo Marketplace plugin, on plugin update notification,
+ * if the Woo Marketplace plugin isn't already installed.
+ *
+ * @param object $plugin_data TAn array of plugin metadata.
+ * @param object $response An object of metadata about the available plugin update.
+ *
+ * @return void.
+ */
+ public static function add_install_marketplace_plugin_message( $plugin_data, $response ) {
+ if ( ! empty( $response->package ) || WC_Woo_Update_Manager_Plugin::is_plugin_active() ) {
+ return;
+ }
+
+ if ( ! WC_Woo_Update_Manager_Plugin::is_plugin_installed() ) {
+ printf(
+ wp_kses(
+ /* translators: 1: Woo Update Manager plugin install URL */
+ __( ' Install Woo.com Update Manager to update.', 'woocommerce' ),
+ array(
+ 'a' => array(
+ 'href' => array(),
+ ),
+ )
+ ),
+ esc_url( WC_Woo_Update_Manager_Plugin::generate_install_url() ),
+ );
+ return;
+ }
+
+ if ( ! WC_Woo_Update_Manager_Plugin::is_plugin_active() ) {
+ echo esc_html_e( ' Activate Woo.com Update Manager to update.', 'woocommerce' );
+ }
+ }
+
/**
* Get update data for all plugins.
*
@@ -236,8 +304,8 @@ class WC_Helper_Updater {
}
// Scan local plugins which may or may not have a subscription.
- $plugins = WC_Helper::get_local_woo_plugins();
- $active_woo_plugins = array_intersect( array_keys( $plugins ), get_option( 'active_plugins', array() ) );
+ $plugins = WC_Helper::get_local_woo_plugins();
+ $active_woo_plugins = array_intersect( array_keys( $plugins ), get_option( 'active_plugins', array() ) );
/*
* Use only plugins that are subscribed to the automatic translations updates.
@@ -245,6 +313,11 @@ class WC_Helper_Updater {
$active_for_translations = array_filter(
$active_woo_plugins,
function( $plugin ) use ( $plugins ) {
+ /**
+ * Filters the plugins that are subscribed to the automatic translations updates.
+ *
+ * @since 3.7.0
+ */
return apply_filters( 'woocommerce_translations_updates_for_' . $plugins[ $plugin ]['slug'], false );
}
);
@@ -267,16 +340,16 @@ class WC_Helper_Updater {
);
foreach ( $active_for_translations as $active_plugin ) {
- $plugin = $plugins[ $active_plugin ];
+ $plugin = $plugins[ $active_plugin ];
$request_body['plugins'][ $plugin['slug'] ] = array( 'version' => $plugin['Version'] );
}
$raw_response = wp_remote_post(
'https://translate.wordpress.com/api/translations-updates/woocommerce',
array(
- 'body' => json_encode( $request_body ),
- 'headers' => array( 'Content-Type: application/json' ),
- 'timeout' => $timeout,
+ 'body' => wp_json_encode( $request_body ),
+ 'headers' => array( 'Content-Type: application/json' ),
+ 'timeout' => $timeout,
)
);
@@ -367,44 +440,6 @@ class WC_Helper_Updater {
return $data['products'];
}
- /**
- * Check for an active subscription.
- *
- * Checks a given product id against all subscriptions on
- * the current site. Returns true if at least one active
- * subscription is found.
- *
- * @param int $product_id The product id to look for.
- *
- * @return bool True if active subscription found.
- */
- private static function _has_active_subscription( $product_id ) {
- if ( ! isset( $auth ) ) {
- $auth = WC_Helper_Options::get( 'auth' );
- }
-
- if ( ! isset( $subscriptions ) ) {
- $subscriptions = WC_Helper::get_subscriptions();
- }
-
- if ( empty( $auth['site_id'] ) || empty( $subscriptions ) ) {
- return false;
- }
-
- // Check for an active subscription.
- foreach ( $subscriptions as $subscription ) {
- if ( $subscription['product_id'] != $product_id ) {
- continue;
- }
-
- if ( in_array( absint( $auth['site_id'] ), $subscription['connections'] ) ) {
- return true;
- }
- }
-
- return false;
- }
-
/**
* Get the number of products that have updates.
*
diff --git a/plugins/woocommerce/includes/admin/helper/class-wc-helper.php b/plugins/woocommerce/includes/admin/helper/class-wc-helper.php
index fd264411104..10e0ac60e7c 100644
--- a/plugins/woocommerce/includes/admin/helper/class-wc-helper.php
+++ b/plugins/woocommerce/includes/admin/helper/class-wc-helper.php
@@ -54,14 +54,15 @@ class WC_Helper {
* Include supporting helper classes.
*/
protected static function includes() {
- include_once dirname( __FILE__ ) . '/class-wc-helper-options.php';
- include_once dirname( __FILE__ ) . '/class-wc-helper-api.php';
- include_once dirname( __FILE__ ) . '/class-wc-helper-updater.php';
- include_once dirname( __FILE__ ) . '/class-wc-helper-plugin-info.php';
- include_once dirname( __FILE__ ) . '/class-wc-helper-compat.php';
- include_once dirname( __FILE__ ) . '/class-wc-helper-admin.php';
- include_once dirname( __FILE__ ) . '/class-wc-helper-subscriptions-api.php';
- include_once dirname( __FILE__ ) . '/class-wc-helper-orders-api.php';
+ include_once __DIR__ . '/class-wc-helper-options.php';
+ include_once __DIR__ . '/class-wc-helper-api.php';
+ include_once __DIR__ . '/class-wc-woo-update-manager-plugin.php';
+ include_once __DIR__ . '/class-wc-helper-updater.php';
+ include_once __DIR__ . '/class-wc-plugin-api-updater.php';
+ include_once __DIR__ . '/class-wc-helper-compat.php';
+ include_once __DIR__ . '/class-wc-helper-admin.php';
+ include_once __DIR__ . '/class-wc-helper-subscriptions-api.php';
+ include_once __DIR__ . '/class-wc-helper-orders-api.php';
}
/**
@@ -832,6 +833,7 @@ class WC_Helper {
)
)
: '',
+ 'wum-installed' => WC_Woo_Update_Manager_Plugin::is_plugin_installed() ? '1' : '0',
),
WC_Helper_API::url( 'oauth/authorize' )
);
@@ -1193,34 +1195,18 @@ class WC_Helper {
* Get a subscriptions install URL.
*
* @param string $product_key Subscription product key.
+ * @param string $product_slug Subscription product slug.
* @return string
*/
- public static function get_subscription_install_url( $product_key ) {
- $install_url_response = WC_Helper_API::post(
- 'install-url',
+ public static function get_subscription_install_url( $product_key, $product_slug ) {
+ $install_url = add_query_arg(
array(
- 'authenticated' => true,
- 'body' => wp_json_encode(
- array(
- 'product_key' => $product_key,
- 'wc_version' => WC()->version,
- )
- ),
- )
+ 'product-key' => $product_key,
+ ),
+ self::get_install_base_url() . "{$product_slug}/"
);
- $code = wp_remote_retrieve_response_code( $install_url_response );
- if ( 200 !== $code ) {
- self::log( sprintf( 'Install URL API call returned a non-200 response code (%d)', $code ) );
- return '';
- }
-
- $body = json_decode( wp_remote_retrieve_body( $install_url_response ), true );
- if ( empty( $body['data']['url'] ) ) {
- self::log( sprintf( 'Install URL API call returned an invalid body: %s', wp_remote_retrieve_body( $install_url_response ) ) );
- return '';
- }
- return $body['data']['url'];
+ return WC_Helper_API::add_auth_parameters( $install_url );
}
/**
@@ -1517,6 +1503,8 @@ class WC_Helper {
$source = 'inbox-notes';
elseif ( stripos( $request_uri, 'admin-ajax.php' ) ) :
$source = 'heartbeat-api';
+ elseif ( stripos( $request_uri, 'installer' ) ) :
+ $source = 'wccom-site-installer';
elseif ( defined( 'WP_CLI' ) && WP_CLI ) :
$source = 'wc-cli';
endif;
@@ -2257,6 +2245,22 @@ class WC_Helper {
self::_flush_subscriptions_cache();
self::_flush_updates_cache();
}
+
+ /**
+ * Get base URL for plugin auto installer.
+ *
+ * @return string
+ */
+ public static function get_install_base_url() {
+ /**
+ * Filter the base URL used to install the Woo hosted plugins.
+ *
+ * @since 8.7.0
+ */
+ $woo_com_base_url = apply_filters( 'woo_com_base_url', 'https://woo.com/' );
+
+ return $woo_com_base_url . 'auto-install-init/';
+ }
}
WC_Helper::load();
diff --git a/plugins/woocommerce/includes/admin/helper/class-wc-helper-plugin-info.php b/plugins/woocommerce/includes/admin/helper/class-wc-plugin-api-updater.php
similarity index 81%
rename from plugins/woocommerce/includes/admin/helper/class-wc-helper-plugin-info.php
rename to plugins/woocommerce/includes/admin/helper/class-wc-plugin-api-updater.php
index a33de0a5f13..bbd2d21168f 100644
--- a/plugins/woocommerce/includes/admin/helper/class-wc-helper-plugin-info.php
+++ b/plugins/woocommerce/includes/admin/helper/class-wc-plugin-api-updater.php
@@ -1,21 +1,17 @@
slug ) ) {
return $response;
}
- // Only for slugs that start with woo-
+ // Only for slugs that start with woocommerce-com-.
if ( 0 !== strpos( $args->slug, 'woocommerce-com-' ) ) {
return $response;
}
@@ -105,4 +103,4 @@ class WC_Helper_Plugin_Info {
}
}
-WC_Helper_Plugin_Info::load();
+WC_Plugin_Api_Updater::load();
diff --git a/plugins/woocommerce/includes/admin/helper/class-wc-woo-update-manager-plugin.php b/plugins/woocommerce/includes/admin/helper/class-wc-woo-update-manager-plugin.php
new file mode 100644
index 00000000000..7054a4dbdae
--- /dev/null
+++ b/plugins/woocommerce/includes/admin/helper/class-wc-woo-update-manager-plugin.php
@@ -0,0 +1,130 @@
+
+
+
+
+ activate the Woo.com Update Manager to continue receiving the updates and streamlined support included in your Woo.com subscriptions.',
+ 'woocommerce'
+ ),
+ esc_url( admin_url( 'plugins.php' ) ),
+ )
+ );
+ ?>
+
+
diff --git a/plugins/woocommerce/includes/admin/helper/views/html-notice-woo-updater-not-installed.php b/plugins/woocommerce/includes/admin/helper/views/html-notice-woo-updater-not-installed.php
new file mode 100644
index 00000000000..a897e71f47a
--- /dev/null
+++ b/plugins/woocommerce/includes/admin/helper/views/html-notice-woo-updater-not-installed.php
@@ -0,0 +1,27 @@
+
+
+
+
+ Install the Woo.com Update Manager to continue receiving the updates and streamlined support included in your Woo.com subscriptions. Alternatively, you can download and install it manually.',
+ 'woocommerce'
+ ),
+ esc_url( WC_Woo_Update_Manager_Plugin::generate_install_url() ),
+ esc_url( WC_Woo_Update_Manager_Plugin::WOO_UPDATE_MANAGER_DOWNLOAD_URL )
+ )
+ );
+ ?>
+
+
diff --git a/plugins/woocommerce/includes/wccom-site/class-wc-wccom-site.php b/plugins/woocommerce/includes/wccom-site/class-wc-wccom-site.php
index f97cfcc75c3..68a9e25ee8d 100644
--- a/plugins/woocommerce/includes/wccom-site/class-wc-wccom-site.php
+++ b/plugins/woocommerce/includes/wccom-site/class-wc-wccom-site.php
@@ -223,6 +223,7 @@ class WC_WCCOM_Site {
require_once WC_ABSPATH . 'includes/wccom-site/rest-api/endpoints/abstract-wc-rest-wccom-site-controller.php';
require_once WC_ABSPATH . 'includes/wccom-site/rest-api/endpoints/class-wc-rest-wccom-site-installer-controller.php';
require_once WC_ABSPATH . 'includes/wccom-site/rest-api/endpoints/class-wc-rest-wccom-site-ssr-controller.php';
+ require_once WC_ABSPATH . 'includes/wccom-site/rest-api/endpoints/class-wc-rest-wccom-site-status-controller.php';
require_once WC_ABSPATH . 'includes/wccom-site/installation/class-wc-wccom-site-installation-state.php';
require_once WC_ABSPATH . 'includes/wccom-site/installation/class-wc-wccom-site-installation-state-storage.php';
@@ -238,6 +239,7 @@ class WC_WCCOM_Site {
$namespaces['wccom-site/v2'] = array(
'installer' => 'WC_REST_WCCOM_Site_Installer_Controller',
'ssr' => 'WC_REST_WCCOM_Site_SSR_Controller',
+ 'status' => 'WC_REST_WCCOM_Site_Status_Controller',
);
return $namespaces;
diff --git a/plugins/woocommerce/includes/wccom-site/rest-api/endpoints/class-wc-rest-wccom-site-status-controller.php b/plugins/woocommerce/includes/wccom-site/rest-api/endpoints/class-wc-rest-wccom-site-status-controller.php
new file mode 100644
index 00000000000..eb8fd26d2e4
--- /dev/null
+++ b/plugins/woocommerce/includes/wccom-site/rest-api/endpoints/class-wc-rest-wccom-site-status-controller.php
@@ -0,0 +1,78 @@
+namespace,
+ '/' . $this->rest_base,
+ array(
+ array(
+ 'methods' => WP_REST_Server::READABLE,
+ 'callback' => array( $this, 'handle_status_request' ),
+ 'permission_callback' => array( $this, 'check_permission' ),
+ ),
+ ),
+ );
+ }
+
+ /**
+ * Check whether user has permission to access controller's endpoints.
+ *
+ * @since 8.7.0
+ * @param WP_USER $user User object.
+ * @return bool
+ */
+ public function user_has_permission( $user ): bool {
+ return user_can( $user, 'install_plugins' ) && user_can( $user, 'activate_plugins' );
+ }
+
+ /**
+ * Get the status details of the site.
+ *
+ * @since 8.7.0
+ * @param WP_REST_Request $request Full details about the request.
+ * @return WP_REST_Response
+ */
+ public function handle_status_request( $request ) {
+
+ return rest_ensure_response(
+ array(
+ 'success' => true,
+ 'data' => array(
+ 'wc_version' => WC()->version,
+ 'woo_update_manager_installed' => WC_Woo_Update_Manager_Plugin::is_plugin_installed(),
+ 'woo_update_manager_active' => WC_Woo_Update_Manager_Plugin::is_plugin_active(),
+ ),
+ )
+ );
+ }
+}
diff --git a/plugins/woocommerce/src/Admin/PageController.php b/plugins/woocommerce/src/Admin/PageController.php
index 3a38577cd4a..c44525cef5c 100644
--- a/plugins/woocommerce/src/Admin/PageController.php
+++ b/plugins/woocommerce/src/Admin/PageController.php
@@ -167,16 +167,17 @@ class PageController {
return apply_filters( 'woocommerce_navigation_get_breadcrumbs', array( '' ), $current_page );
}
- $current_page['title'] = (array) $current_page['title'];
- if ( 1 === count( $current_page['title'] ) ) {
- $breadcrumbs = $current_page['title'];
+ $page_title = ! empty( $current_page['page_title'] ) ? $current_page['page_title'] : $current_page['title'];
+ $page_title = (array) $page_title;
+ if ( 1 === count( $page_title ) ) {
+ $breadcrumbs = $page_title;
} else {
// If this page has multiple title pieces, only link the first one.
$breadcrumbs = array_merge(
array(
- array( $current_page['path'], reset( $current_page['title'] ) ),
+ array( $current_page['path'], reset( $page_title ) ),
),
- array_slice( $current_page['title'], 1 )
+ array_slice( $page_title, 1 )
);
}
@@ -438,6 +439,7 @@ class PageController {
'id' => null,
'parent' => null,
'title' => '',
+ 'page_title' => '',
'capability' => 'view_woocommerce_reports',
'path' => '',
'icon' => '',
@@ -455,9 +457,13 @@ class PageController {
$options['position'] = intval( round( $options['position'] ) );
}
+ if ( empty( $options['page_title'] ) ) {
+ $options['page_title'] = $options['title'];
+ }
+
if ( is_null( $options['parent'] ) ) {
add_menu_page(
- $options['title'],
+ $options['page_title'],
$options['title'],
$options['capability'],
$options['path'],
@@ -470,7 +476,7 @@ class PageController {
// @todo check for null path.
add_submenu_page(
$parent_path,
- $options['title'],
+ $options['page_title'],
$options['title'],
$options['capability'],
$options['path'],
diff --git a/plugins/woocommerce/src/Internal/Admin/Marketplace.php b/plugins/woocommerce/src/Internal/Admin/Marketplace.php
index 198a4de0de7..888a3e7600d 100644
--- a/plugins/woocommerce/src/Internal/Admin/Marketplace.php
+++ b/plugins/woocommerce/src/Internal/Admin/Marketplace.php
@@ -7,6 +7,8 @@ namespace Automattic\WooCommerce\Internal\Admin;
use Automattic\WooCommerce\Utilities\FeaturesUtil;
use Automattic\WooCommerce\Internal\Features\FeaturesController;
+use WC_Helper_Updater;
+use WC_Woo_Update_Manager_Plugin;
/**
* Contains backend logic for the Marketplace feature.
@@ -17,6 +19,8 @@ class Marketplace {
/**
* Class initialization, to be executed when the class is resolved by the container.
+ *
+ * @internal
*/
final public function init() {
if ( false === FeaturesUtil::feature_is_enabled( 'marketplace' ) ) {
@@ -55,10 +59,11 @@ class Marketplace {
public static function get_marketplace_pages() {
$marketplace_pages = array(
array(
- 'id' => 'woocommerce-marketplace',
- 'parent' => 'woocommerce',
- 'title' => __( 'Extensions', 'woocommerce' ),
- 'path' => '/extensions',
+ 'id' => 'woocommerce-marketplace',
+ 'parent' => 'woocommerce',
+ 'title' => __( 'Extensions', 'woocommerce' ) . self::get_marketplace_update_count_html(),
+ 'page_title' => __( 'Extensions', 'woocommerce' ),
+ 'path' => '/extensions',
),
);
@@ -70,6 +75,29 @@ class Marketplace {
return apply_filters( 'woocommerce_marketplace_menu_items', $marketplace_pages );
}
+ /**
+ * Create the menu bubble for extensions menu based on number of updates available.
+ *
+ * @return string
+ */
+ private static function get_marketplace_update_count_html() {
+ $count = WC_Helper_Updater::get_updates_count();
+ if ( empty( $count ) ) {
+ $count = 0;
+ }
+
+ $count = intval( $count );
+ if ( ! WC_Woo_Update_Manager_Plugin::is_plugin_installed() || ! WC_Woo_Update_Manager_Plugin::is_plugin_active() ) {
+ ++$count;
+ }
+
+ if ( 0 === $count ) {
+ return '';
+ }
+
+ return sprintf( ' %d', $count, number_format_i18n( $count ) );
+ }
+
/**
* Enqueue update script.
*