Add subscribe notices for products without subscription (#51060)

- Add a new notice to the plugins list for products used without subscriptions reminding them to purchase.
- Remove notices for expired and expiring subscriptions from WooCommerce settings screens.
- Change link for subscribe and renew to add the product directly to cart.

---------

Co-authored-by: github-actions <github-actions@github.com>
This commit is contained in:
Vishnu Gopal 2024-09-13 10:33:54 +05:30 committed by GitHub
parent d7ccebc004
commit 411d9ae78c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 221 additions and 18 deletions

View File

@ -197,6 +197,9 @@ export default function Content(): JSX.Element {
{ selectedTab !== 'business-services' && (
<SubscriptionsExpiredExpiringNotice type="expiring" />
) }
{ selectedTab !== 'business-services' && (
<SubscriptionsExpiredExpiringNotice type="missing" />
) }
{ renderContent() }
</div>

View File

@ -39,6 +39,12 @@ export default function SubscriptionsExpiredExpiringNotice(
dismissed:
'woo_subscription_expiring_notice_in_marketplace_dismissed',
},
'woo-subscription-missing-notice': {
shown: 'woo_subscription_missing_notice_in_marketplace_shown',
clicked: 'woo_subscription_missing_notice_in_marketplace_clicked',
dismissed:
'woo_subscription_missing_notice_in_marketplace_dismissed',
},
};
let notice = null;
@ -51,6 +57,9 @@ export default function SubscriptionsExpiredExpiringNotice(
} else if ( type === 'expiring' ) {
notice = wccomSettings?.subscription_expiring_notice || {};
notice_id = 'woo-subscription-expiring-notice';
} else if ( type === 'missing' ) {
notice = wccomSettings?.subscription_missing_notice || {};
notice_id = 'woo-subscription-missing-notice';
} else {
return null;
}

View File

@ -0,0 +1,20 @@
/**
* External dependencies
*/
import domReady from '@wordpress/dom-ready';
import { recordEvent } from '@woocommerce/tracks';
domReady( () => {
const purchaseSubscriptionLink = document.querySelectorAll(
'.woocommerce-purchase-subscription'
);
if ( purchaseSubscriptionLink.length > 0 ) {
recordEvent( 'woo_purchase_subscription_in_plugins_shown' );
purchaseSubscriptionLink.forEach( ( link ) => {
link.addEventListener( 'click', function () {
recordEvent( 'woo_purchase_subscription_in_plugins_clicked' );
} );
} );
}
} );

View File

@ -0,0 +1,4 @@
Significance: patch
Type: fix
Add a new notice to the plugins list for products used without subscriptions reminding them to purchase.

View File

@ -85,6 +85,7 @@ class WC_Helper_Admin {
if ( WC_Helper::is_site_connected() ) {
$settings['wccomHelper']['subscription_expired_notice'] = PluginsHelper::get_expired_subscription_notice( false );
$settings['wccomHelper']['subscription_expiring_notice'] = PluginsHelper::get_expiring_subscription_notice( false );
$settings['wccomHelper']['subscription_missing_notice'] = PluginsHelper::get_missing_subscription_notice();
}
return $settings;

View File

@ -40,11 +40,13 @@ class WC_Helper_Updater {
}
if ( WC_Helper::is_site_connected() ) {
add_action( 'load-plugins.php', array( __CLASS__, 'setup_message_for_expired_and_expiring_subscriptions' ), 11 );
add_action( 'load-plugins.php', array( __CLASS__, 'setup_message_for_plugins_without_subscription' ), 11 );
}
}
/**
* Add the hook for modifying default WPCore update notices on the plugins management page.
* This is for plugins with expired or expiring subscriptions.
*/
public static function setup_message_for_expired_and_expiring_subscriptions() {
foreach ( WC_Helper::get_local_woo_plugins() as $plugin ) {
@ -52,6 +54,16 @@ class WC_Helper_Updater {
}
}
/**
* Add the hook for modifying default WPCore update notices on the plugins management page.
* This is for plugins without a subscription.
*/
public static function setup_message_for_plugins_without_subscription() {
foreach ( WC_Helper::get_local_woo_plugins() as $plugin ) {
add_action( 'in_plugin_update_message-' . $plugin['_filename'], array( __CLASS__, 'display_notice_for_plugins_without_subscription' ), 10, 2 );
}
}
/**
* Runs in a cron thread, or in a visitor thread if triggered
* by _maybe_update_plugins(), or in an auto-update thread.
@ -294,10 +306,11 @@ class WC_Helper_Updater {
$renew_link = add_query_arg(
array(
'add-to-cart' => $product_id,
'utm_source' => 'pu',
'utm_campaign' => 'pu_plugin_screen_renew',
),
PluginsHelper::WOO_SUBSCRIPTION_PAGE_URL
PluginsHelper::WOO_CART_PAGE_URL
);
/* translators: 1: Product regular price */
@ -340,6 +353,52 @@ class WC_Helper_Updater {
}
}
/**
* Runs on in_plugin_update_message-{file-name}, show a message if plugin is without a subscription.
* Only Woo local plugins are passed to this function.
*
* @see setup_message_for_plugins_without_subscription
* @param object $plugin_data An array of plugin metadata.
* @param object $response An object of metadata about the available plugin update.
*
* @return void.
*/
public static function display_notice_for_plugins_without_subscription( $plugin_data, $response ) {
// Extract product ID from the response.
$product_id = preg_replace( '/[^0-9]/', '', $response->id );
if ( WC_Helper::has_product_subscription( $product_id ) ) {
return;
}
// Prepare the expiry notice based on subscription status.
$purchase_link = add_query_arg(
array(
'add-to-cart' => $product_id,
'utm_source' => 'pu',
'utm_campaign' => 'pu_plugin_screen_purchase',
),
PluginsHelper::WOO_CART_PAGE_URL,
);
$notice = sprintf(
/* translators: 1: URL to My Subscriptions page */
__( ' You don\'t have a subscription, <a href="%1$s" class="woocommerce-purchase-subscription">subscribe</a> to update.', 'woocommerce' ),
esc_url( $purchase_link ),
);
// Display the expiry notice.
echo wp_kses(
$notice,
array(
'a' => array(
'href' => array(),
'class' => array(),
),
)
);
}
/**
* Get update data for all plugins.
*

View File

@ -33,17 +33,24 @@ if ( ! function_exists( 'get_plugins' ) ) {
class PluginsHelper {
/**
* Indicates whether the expiration notice for subscriptions can be displayed.
* Subscription notices in Woo screens are shown in clear priority order, first
* expired, and if those don't exist, expiring, and finally if none of those exist,
* then missing. This keeps track of whether we can show the next set of notices.
*
* @var bool
*/
public static $can_show_expiring_subs_notice = true;
public static $subscription_usage_notices_already_shown = false;
/**
* The URL for the WooCommerce subscription page.
*/
const WOO_SUBSCRIPTION_PAGE_URL = 'https://woocommerce.com/my-account/my-subscriptions/';
/**
* The URL for the WooCommerce.com cart page.
*/
const WOO_CART_PAGE_URL = 'https://woocommerce.com/cart/';
/**
* The URL for the WooCommerce.com add payment method page.
*/
@ -59,6 +66,11 @@ class PluginsHelper {
*/
const DISMISS_EXPIRING_SUBS_NOTICE = 'woo_subscription_expiring_notice_dismiss';
/**
* Meta key for dismissing missing subscription notices
*/
const DISMISS_MISSING_SUBS_NOTICE = 'woo_subscription_missing_notice_dismiss';
/**
* Initialize hooks.
*/
@ -67,10 +79,7 @@ class PluginsHelper {
add_action( 'woocommerce_plugins_install_and_activate_async_callback', array( __CLASS__, 'install_and_activate_plugins_async_callback' ), 10, 2 );
add_action( 'woocommerce_plugins_activate_callback', array( __CLASS__, 'activate_plugins' ), 10, 2 );
add_action( 'admin_notices', array( __CLASS__, 'maybe_show_connect_notice_in_plugin_list' ) );
add_action( 'admin_notices', array( __CLASS__, 'maybe_show_expired_subscriptions_notice' ), 10 );
add_action( 'admin_notices', array( __CLASS__, 'maybe_show_expiring_subscriptions_notice' ), 11 );
add_action( 'admin_enqueue_scripts', array( __CLASS__, 'maybe_enqueue_scripts_for_connect_notice' ) );
add_action( 'admin_enqueue_scripts', array( __CLASS__, 'maybe_enqueue_scripts_for_subscription_notice' ) );
add_action( 'admin_enqueue_scripts', array( __CLASS__, 'maybe_enqueue_scripts_for_notices_in_plugins' ) );
}
@ -659,6 +668,7 @@ class PluginsHelper {
wp_enqueue_script( 'woo-plugin-update-connect-notice' );
wp_enqueue_script( 'woo-enable-autorenew' );
wp_enqueue_script( 'woo-renew-subscription' );
wp_enqueue_script( 'woo-purchase-subscription' );
}
/**
@ -733,11 +743,15 @@ class PluginsHelper {
* @return array notice data to return. Contains type, parsed_message and product_id.
*/
public static function get_subscriptions_notice_data( array $all_subs, array $subs_to_show, int $total, array $messages, string $type ) {
$utm_campaign = 'expired' === $type ?
'pu_settings_screen_renew' :
( 'missing' === $type ? 'pu_settings_screen_purchase' : 'pu_settings_screen_enable_autorenew' );
if ( 1 < $total ) {
$hyperlink_url = add_query_arg(
array(
'utm_source' => 'pu',
'utm_campaign' => 'expired' === $type ? 'pu_settings_screen_renew' : 'pu_settings_screen_enable_autorenew',
'utm_campaign' => $utm_campaign,
),
self::WOO_SUBSCRIPTION_PAGE_URL
@ -750,10 +764,18 @@ class PluginsHelper {
esc_attr( $total ),
);
// All product ids.
$product_ids = array_map(
function ( $sub ) {
return $sub['product_id'];
},
$subs_to_show
);
return array(
'type' => 'different_subscriptions',
'parsed_message' => $parsed_message,
'product_id' => '',
'product_ids' => $product_ids,
);
}
@ -769,8 +791,9 @@ class PluginsHelper {
)
);
$message_key = $has_multiple_subs_for_product ? 'multiple_manage' : 'single_manage';
$renew_string = __( 'Renew', 'woocommerce' );
$message_key = $has_multiple_subs_for_product ? 'multiple_manage' : 'single_manage';
$renew_string = __( 'Renew', 'woocommerce' );
$subscribe_string = __( 'Subscribe', 'woocommerce' );
if ( isset( $subscription['product_regular_price'] ) ) {
/* translators: 1: Product price */
$renew_string = sprintf( __( 'Renew for %1$s', 'woocommerce' ), $subscription['product_regular_price'] );
@ -781,7 +804,7 @@ class PluginsHelper {
'product_id' => $product_id,
'type' => $type,
'utm_source' => 'pu',
'utm_campaign' => 'expired' === $type ? 'pu_settings_screen_renew' : 'pu_settings_screen_enable_autorenew',
'utm_campaign' => $utm_campaign,
),
self::WOO_SUBSCRIPTION_PAGE_URL
@ -798,7 +821,8 @@ class PluginsHelper {
esc_attr( $subscription['product_name'] ),
esc_attr( $expiry_date ),
esc_url( $hyperlink_url ),
esc_attr( $renew_string ),
// Show subscribe for missing subscriptions, renew otherwise.
'missing' === $type ? esc_attr( $subscribe_string ) : esc_attr( $renew_string ),
);
return array(
@ -826,7 +850,7 @@ class PluginsHelper {
return array();
}
if ( ! self::$can_show_expiring_subs_notice ) {
if ( self::$subscription_usage_notices_already_shown ) {
return array();
}
@ -851,6 +875,9 @@ class PluginsHelper {
$total_expiring_subscriptions = count( $expiring_subscriptions );
// Don't show missing notice if there are expiring subscriptions.
self::$subscription_usage_notices_already_shown = true;
// When payment method is missing on WooCommerce.com.
$helper_notices = WC_Helper::get_notices();
if ( ! empty( $helper_notices['missing_payment_method_notice'] ) ) {
@ -927,8 +954,8 @@ class PluginsHelper {
return array();
}
$total_expired_subscriptions = count( $expired_subscriptions );
self::$can_show_expiring_subs_notice = false;
$total_expired_subscriptions = count( $expired_subscriptions );
self::$subscription_usage_notices_already_shown = true;
$notice_data = self::get_subscriptions_notice_data(
$subscriptions,
@ -947,17 +974,17 @@ class PluginsHelper {
$button_link = add_query_arg(
array(
'add-to-cart' => $notice_data['product_ids'],
'utm_source' => 'pu',
'utm_campaign' => $allowed_link ? 'pu_settings_screen_renew' : 'pu_in_apps_screen_renew',
),
self::WOO_SUBSCRIPTION_PAGE_URL
self::WOO_CART_PAGE_URL
);
if ( in_array( $notice_data['type'], array( 'single_manage', 'multiple_manage' ), true ) ) {
$button_link = add_query_arg(
array(
'product_id' => $notice_data['product_id'],
'type' => 'expiring',
'add-to-cart' => $notice_data['product_id'],
),
$button_link
);
@ -970,6 +997,86 @@ class PluginsHelper {
);
}
/**
* Get formatted notice information for missing subscription.
*
* @return array notice information.
*/
public static function get_missing_subscription_notice() {
if ( ! WC_Helper::is_site_connected() ) {
return array();
}
if ( self::$subscription_usage_notices_already_shown ) {
return array();
}
if ( ! self::should_show_notice( self::DISMISS_MISSING_SUBS_NOTICE ) ) {
return array();
}
$subscriptions = WC_Helper::get_subscription_list_data();
$missing_subscriptions = array_filter(
$subscriptions,
function ( $sub ) {
return ( ! empty( $sub['local']['installed'] ) && empty( $sub['product_key'] ) );
},
);
// Remove WUM from missing subscriptions list.
$missing_subscriptions = array_filter(
$missing_subscriptions,
function ( $sub ) {
return 'woo-update-manager' !== $sub['zip_slug'];
}
);
if ( ! $missing_subscriptions ) {
return array();
}
$total_missing_subscriptions = count( $missing_subscriptions );
$notice_data = self::get_subscriptions_notice_data(
$subscriptions,
$missing_subscriptions,
$total_missing_subscriptions,
array(
/* translators: 1) product name */
'single_manage' => __( 'You don\'t have a subscription for <strong>%1$s</strong>. Subscribe to receive updates and streamlined support.', 'woocommerce' ),
/* translators: 1) total expired subscriptions */
'different_subscriptions' => __( 'You don\'t have subscriptions for <strong>%1$s Woo extensions</strong>. Subscribe to receive updates and streamlined support.', 'woocommerce' ),
),
'missing',
);
$button_link = add_query_arg(
array(
'add-to-cart' => $notice_data['product_ids'],
'utm_source' => 'pu',
'utm_campaign' => 'pu_in_apps_screen_purchase',
),
self::WOO_CART_PAGE_URL
);
if ( in_array( $notice_data['type'], array( 'single_manage', 'multiple_manage' ), true ) ) {
$button_link = add_query_arg(
array(
'add-to-cart' => $notice_data['product_id'],
),
$button_link
);
}
$button_text = __( 'Subscribe', 'woocommerce' );
return array(
'description' => $notice_data['parsed_message'],
'button_text' => $button_text,
'button_link' => $button_link,
);
}
/**
* Determine whether a specific notice should be shown to the current user.
*