Commit ab92f0b2c69 for woocommerce
commit ab92f0b2c698a19d5445d93d17d3b318fac1906d
Author: Hannah Tinkler <hannah.tinkler@gmail.com>
Date: Wed Jul 1 13:47:58 2026 +0100
Always enable push notifications by deprecating the feature flag (#66088)
* Always enable push notifications by deprecating the feature flag.
- Add deprecated_since/deprecated_value to the push_notifications feature so feature_is_enabled() returns true and ignores the stored option
- Add wc_update_10902 migration to delete the now-dead option, clearing the stale 'no' persisted by the 10.5.0 autoload migration
- Register the cleanup migration under a new 10.9.2 DB update key
* Remove deprecated feature-flag check from push notifications gate.
- Drop the FeaturesUtil::feature_is_enabled('push_notifications') call in should_be_enabled(); the flag is now deprecated and always enabled, and the public API logged a deprecation notice on every request
- Gate enablement on the Jetpack connection only, matching the marketplace/point_of_sale deprecation pattern
- Update tests and shared trait: drop the obsolete feature-disabled test and now-dead FeaturesController mock scaffolding
* Honour explicit 'no' option as a manual push notifications kill switch.
- Read woocommerce_feature_push_notifications_enabled directly via get_option() in should_be_enabled(), bypassing FeaturesUtil to avoid the deprecation notice and the always-true deprecated value
- Lets a store force the deprecated feature off (e.g. fall back to Jetpack) even though it has no settings UI
* Replace push notifications kill-switch option with a filter.
- Gate enhanced push notifications on the woocommerce_enhanced_push_notifications_disabled filter (default false) instead of reading the deprecated option directly
- Early-return when disabled, skipping the Jetpack connection lookup
- Keep the Jetpack connection requirement so the filter can only disable, never force-enable a store that can't receive notifications
* Ensure disabled flag is a bool.
diff --git a/plugins/woocommerce/includes/class-wc-install.php b/plugins/woocommerce/includes/class-wc-install.php
index ca28da6f03c..5fbc277ece6 100644
--- a/plugins/woocommerce/includes/class-wc-install.php
+++ b/plugins/woocommerce/includes/class-wc-install.php
@@ -336,6 +336,9 @@ class WC_Install {
'10.9.0' => array(
'wc_update_1090_remove_task_list_reminder_bar_hidden_option',
),
+ '10.9.2' => array(
+ 'wc_update_10902_remove_deprecated_push_notifications_option',
+ ),
'11.0.0' => array(
'wc_update_1100_enable_point_of_sale_feature',
),
diff --git a/plugins/woocommerce/includes/wc-update-functions.php b/plugins/woocommerce/includes/wc-update-functions.php
index 1971606d70e..ce08857e1bd 100644
--- a/plugins/woocommerce/includes/wc-update-functions.php
+++ b/plugins/woocommerce/includes/wc-update-functions.php
@@ -3550,6 +3550,23 @@ function wc_update_1090_remove_task_list_reminder_bar_hidden_option() {
delete_option( 'woocommerce_task_list_reminder_bar_hidden' );
}
+/**
+ * Remove the deprecated push_notifications feature option from the database.
+ *
+ * The push_notifications feature flag was deprecated in 10.9.2 and is now always enabled.
+ * The option is no longer needed as FeaturesUtil::feature_is_enabled('push_notifications')
+ * returns the deprecated_value directly without reading from the database. Removing it also
+ * clears the stale "no" value that wc_update_1050_enable_autoload_options() persisted for
+ * stores upgrading across the 10.5.0 boundary.
+ *
+ * @since 10.9.2
+ *
+ * @return void
+ */
+function wc_update_10902_remove_deprecated_push_notifications_option(): void {
+ delete_option( 'woocommerce_feature_push_notifications_enabled' );
+}
+
/**
* Set the stored value of the point_of_sale feature flag to enabled.
*
diff --git a/plugins/woocommerce/src/Internal/Features/FeaturesController.php b/plugins/woocommerce/src/Internal/Features/FeaturesController.php
index f52725987d8..5177ff130e8 100644
--- a/plugins/woocommerce/src/Internal/Features/FeaturesController.php
+++ b/plugins/woocommerce/src/Internal/Features/FeaturesController.php
@@ -632,11 +632,13 @@ class FeaturesController {
'Enable push notifications for the WooCommerce mobile apps to receive order notifications and store updates.',
'woocommerce'
),
+ 'is_experimental' => false,
'enabled_by_default' => true,
- 'is_experimental' => true,
'disable_ui' => true,
- 'skip_compatibility_checks' => false,
+ 'skip_compatibility_checks' => true,
'default_plugin_compatibility' => FeaturePluginCompatibility::COMPATIBLE,
+ 'deprecated_since' => '10.9.2',
+ 'deprecated_value' => true,
),
'rest_api_caching' => array(
'name' => __( 'REST API Caching', 'woocommerce' ),
diff --git a/plugins/woocommerce/src/Internal/PushNotifications/PushNotifications.php b/plugins/woocommerce/src/Internal/PushNotifications/PushNotifications.php
index a80250d3f53..233b9911b1f 100644
--- a/plugins/woocommerce/src/Internal/PushNotifications/PushNotifications.php
+++ b/plugins/woocommerce/src/Internal/PushNotifications/PushNotifications.php
@@ -19,7 +19,6 @@ use Automattic\WooCommerce\Internal\PushNotifications\Triggers\NewReviewNotifica
use Automattic\WooCommerce\Internal\PushNotifications\Triggers\StockNotificationRecoveryHandler;
use Automattic\WooCommerce\Internal\PushNotifications\Triggers\StockNotificationTrigger;
use Automattic\WooCommerce\Proxies\LegacyProxy;
-use Automattic\WooCommerce\Utilities\FeaturesUtil;
use WC_Logger;
use Exception;
@@ -124,9 +123,8 @@ class PushNotifications {
/**
* Determines if local push notification functionality should be enabled.
- * Push notifications require both the feature flag to be enabled and
- * Jetpack to be connected. Memoize the value so we only check once per
- * request.
+ * Push notifications require Jetpack to be connected. Memoize the value so
+ * we only check once per request.
*
* @return bool
*
@@ -137,7 +135,24 @@ class PushNotifications {
return $this->enabled;
}
- if ( ! FeaturesUtil::feature_is_enabled( self::FEATURE_NAME ) ) {
+ $feature_disabled = wc_string_to_bool(
+ /**
+ * Filters whether enhanced push notifications should be disabled.
+ *
+ * The feature was previously controlled by a now-deprecated feature
+ * flag. It is now enabled by default for all compatible users, but this
+ * filter lets a store force it off (e.g. to fall back to Jetpack Sync
+ * if something isn't working). The feature also requires a Jetpack
+ * connection, which is checked separately below.
+ *
+ * @since 10.9.2
+ *
+ * @param bool $disabled Whether enhanced push notifications are disabled. Defaults to false.
+ */
+ apply_filters( 'woocommerce_enhanced_push_notifications_disabled', false )
+ );
+
+ if ( $feature_disabled ) {
$this->enabled = false;
return $this->enabled;
}
diff --git a/plugins/woocommerce/tests/php/src/Internal/PushNotifications/Controllers/NotificationPreferencesRestControllerTest.php b/plugins/woocommerce/tests/php/src/Internal/PushNotifications/Controllers/NotificationPreferencesRestControllerTest.php
index 785924f9f2c..f6c6e8adf29 100644
--- a/plugins/woocommerce/tests/php/src/Internal/PushNotifications/Controllers/NotificationPreferencesRestControllerTest.php
+++ b/plugins/woocommerce/tests/php/src/Internal/PushNotifications/Controllers/NotificationPreferencesRestControllerTest.php
@@ -43,7 +43,6 @@ class NotificationPreferencesRestControllerTest extends WC_REST_Unit_Test_Case {
public function setUp(): void {
parent::setUp();
- $this->set_up_features_controller_mock();
$this->reset_push_notifications_cache();
$this->user_id = $this->factory->user->create( array( 'role' => 'shop_manager' ) );
diff --git a/plugins/woocommerce/tests/php/src/Internal/PushNotifications/Controllers/PushTokenRestControllerTest.php b/plugins/woocommerce/tests/php/src/Internal/PushNotifications/Controllers/PushTokenRestControllerTest.php
index 7ab63d883c7..cfa2ef153e6 100644
--- a/plugins/woocommerce/tests/php/src/Internal/PushNotifications/Controllers/PushTokenRestControllerTest.php
+++ b/plugins/woocommerce/tests/php/src/Internal/PushNotifications/Controllers/PushTokenRestControllerTest.php
@@ -62,7 +62,6 @@ class PushTokenRestControllerTest extends WC_REST_Unit_Test_Case {
public function setUp(): void {
parent::setUp();
- $this->set_up_features_controller_mock();
$this->reset_push_notifications_cache();
( new PushTokenRestController() )->register_routes();
diff --git a/plugins/woocommerce/tests/php/src/Internal/PushNotifications/Helpers/PushNotificationsTestTrait.php b/plugins/woocommerce/tests/php/src/Internal/PushNotifications/Helpers/PushNotificationsTestTrait.php
index de37a767efd..99b887304c2 100644
--- a/plugins/woocommerce/tests/php/src/Internal/PushNotifications/Helpers/PushNotificationsTestTrait.php
+++ b/plugins/woocommerce/tests/php/src/Internal/PushNotifications/Helpers/PushNotificationsTestTrait.php
@@ -5,7 +5,6 @@ declare( strict_types = 1 );
namespace Automattic\WooCommerce\Tests\Internal\PushNotifications\Helpers;
use Automattic\Jetpack\Connection\Manager as JetpackConnectionManager;
-use Automattic\WooCommerce\Internal\Features\FeaturesController;
use Automattic\WooCommerce\Internal\PushNotifications\PushNotifications;
use Automattic\WooCommerce\Proxies\LegacyProxy;
use PHPUnit\Framework\MockObject\MockObject;
@@ -14,10 +13,9 @@ use ReflectionClass;
/**
* Shared test helpers for the PushNotifications module.
*
- * Mocks the Jetpack connection state, the FeaturesController, and resets the
- * memoized enablement flag on the container's `PushNotifications` instance —
- * the three things every push-notifications-related controller test needs in
- * setUp.
+ * Mocks the Jetpack connection state and resets the memoized enablement flag on
+ * the container's `PushNotifications` instance — the two things every
+ * push-notifications-related controller test needs in setUp.
*
* @package WooCommerce\Tests\PushNotifications
*/
@@ -27,11 +25,6 @@ trait PushNotificationsTestTrait {
*/
protected $jetpack_connection_manager_mock;
- /**
- * @var FeaturesController|MockObject|null
- */
- protected $features_controller_mock;
-
/**
* Mocks the JetpackConnectionManager so its `is_connected()` returns the
* supplied value, and resets the PushNotifications enablement cache so
@@ -58,28 +51,6 @@ trait PushNotificationsTestTrait {
$this->reset_push_notifications_cache();
}
- /**
- * Sets up the FeaturesController mock so the `push_notifications` feature
- * reports as enabled (and only that feature).
- */
- protected function set_up_features_controller_mock() {
- $this->features_controller_mock = $this
- ->getMockBuilder( FeaturesController::class )
- ->disableOriginalConstructor()
- ->onlyMethods( array( 'feature_is_enabled' ) )
- ->getMock();
-
- $this->features_controller_mock
- ->method( 'feature_is_enabled' )
- ->willReturnCallback(
- function ( $feature_id ) {
- return PushNotifications::FEATURE_NAME === $feature_id;
- }
- );
-
- wc_get_container()->replace( FeaturesController::class, $this->features_controller_mock );
- }
-
/**
* Resets the cached enablement state on the container's PushNotifications
* instance so subsequent `should_be_enabled()` calls re-evaluate.
diff --git a/plugins/woocommerce/tests/php/src/Internal/PushNotifications/PushNotificationsTest.php b/plugins/woocommerce/tests/php/src/Internal/PushNotifications/PushNotificationsTest.php
index c7e8a560855..c07b5c91360 100644
--- a/plugins/woocommerce/tests/php/src/Internal/PushNotifications/PushNotificationsTest.php
+++ b/plugins/woocommerce/tests/php/src/Internal/PushNotifications/PushNotificationsTest.php
@@ -5,7 +5,6 @@ declare(strict_types=1);
namespace Automattic\WooCommerce\Tests\Internal\PushNotifications;
use Automattic\Jetpack\Connection\Manager as JetpackConnectionManager;
-use Automattic\WooCommerce\Internal\Features\FeaturesController;
use Automattic\WooCommerce\Internal\PushNotifications\Entities\PushToken;
use Automattic\WooCommerce\Internal\PushNotifications\PushNotifications;
use Automattic\WooCommerce\Proxies\LegacyProxy;
@@ -25,20 +24,6 @@ class PushNotificationsTest extends WC_Unit_Test_Case {
*/
private $jetpack_connection_manager_mock;
- /**
- * @var FeaturesController|MockObject
- */
- private $features_controller_mock;
-
- /**
- * Set up the test case.
- */
- public function setUp(): void {
- parent::setUp();
-
- $this->set_up_features_controller_mock();
- }
-
/**
* Tear down the test case.
*/
@@ -58,54 +43,56 @@ class PushNotificationsTest extends WC_Unit_Test_Case {
}
/**
- * @testdox Tests the functionality is disabled if the feature flag is
- * disabled.
+ * @testdox Tests the functionality is enabled when Jetpack is connected.
*/
- public function test_it_can_tell_push_notifications_should_not_be_enabled_if_feature_is_disabled() {
- $this->set_up_features_controller_mock( false );
+ public function test_it_can_tell_push_notifications_should_be_enabled_if_jetpack_is_connected() {
$this->set_up_jetpack_connection_manager_mock( array( 'is_connected' ) );
$this->jetpack_connection_manager_mock
- ->expects( $this->never() )
- ->method( 'is_connected' );
+ ->expects( $this->once() )
+ ->method( 'is_connected' )
+ ->willReturn( true );
$push_notifications = new PushNotifications();
- $this->assertFalse( $push_notifications->should_be_enabled() );
+ $this->assertTrue( $push_notifications->should_be_enabled() );
}
/**
- * @testdox Tests the functionality is enabled if feature flag is enabled
- * and Jetpack is connected.
+ * @testdox Tests the functionality is disabled if Jetpack is not connected
+ * even when feature flag is enabled.
*/
- public function test_it_can_tell_push_notifications_should_be_enabled_if_jetpack_is_connected() {
+ public function test_it_can_tell_push_notifications_should_not_be_enabled_if_jetpack_is_not_connected() {
$this->set_up_jetpack_connection_manager_mock( array( 'is_connected' ) );
$this->jetpack_connection_manager_mock
->expects( $this->once() )
->method( 'is_connected' )
- ->willReturn( true );
+ ->willReturn( false );
$push_notifications = new PushNotifications();
- $this->assertTrue( $push_notifications->should_be_enabled() );
+ $this->assertFalse( $push_notifications->should_be_enabled() );
}
/**
- * @testdox Tests the functionality is disabled if Jetpack is not connected
- * even when feature flag is enabled.
+ * @testdox Tests the functionality can be manually disabled via the
+ * woocommerce_enhanced_push_notifications_disabled filter, skipping the Jetpack connection check.
*/
- public function test_it_can_tell_push_notifications_should_not_be_enabled_if_jetpack_is_not_connected() {
+ public function test_it_can_tell_push_notifications_should_not_be_enabled_when_disabled_via_filter() {
$this->set_up_jetpack_connection_manager_mock( array( 'is_connected' ) );
$this->jetpack_connection_manager_mock
- ->expects( $this->once() )
- ->method( 'is_connected' )
- ->willReturn( false );
+ ->expects( $this->never() )
+ ->method( 'is_connected' );
+
+ add_filter( 'woocommerce_enhanced_push_notifications_disabled', '__return_true' );
$push_notifications = new PushNotifications();
$this->assertFalse( $push_notifications->should_be_enabled() );
+
+ remove_filter( 'woocommerce_enhanced_push_notifications_disabled', '__return_true' );
}
/**
@@ -122,7 +109,6 @@ class PushNotificationsTest extends WC_Unit_Test_Case {
);
$this->register_legacy_proxy_function_mocks( array( 'wc_get_logger' => fn () => $logger_mock ) );
- $this->set_up_features_controller_mock( true );
$this->set_up_jetpack_connection_manager_mock( array( 'is_connected' ) );
$this->jetpack_connection_manager_mock
@@ -218,10 +204,15 @@ class PushNotificationsTest extends WC_Unit_Test_Case {
}
/**
- * @testdox Tests that on_init does not register post types when disabled.
+ * @testdox Tests that on_init does not register post types when Jetpack is not connected.
*/
public function test_on_init_does_not_register_post_types_when_disabled() {
- $this->set_up_features_controller_mock( false );
+ $this->set_up_jetpack_connection_manager_mock( array( 'is_connected' ) );
+
+ $this->jetpack_connection_manager_mock
+ ->expects( $this->once() )
+ ->method( 'is_connected' )
+ ->willReturn( false );
$push_notifications = new PushNotifications();
$push_notifications->on_init();
@@ -232,29 +223,6 @@ class PushNotificationsTest extends WC_Unit_Test_Case {
);
}
- /**
- * Sets up the FeaturesController mock.
- *
- * @param bool $feature_enabled Whether the push_notifications feature should be enabled.
- */
- private function set_up_features_controller_mock( bool $feature_enabled = true ) {
- $this->features_controller_mock = $this
- ->getMockBuilder( FeaturesController::class )
- ->disableOriginalConstructor()
- ->onlyMethods( array( 'feature_is_enabled' ) )
- ->getMock();
-
- $this->features_controller_mock
- ->method( 'feature_is_enabled' )
- ->willReturnCallback(
- function ( $feature_id ) use ( $feature_enabled ) {
- return PushNotifications::FEATURE_NAME === $feature_id ? $feature_enabled : false;
- }
- );
-
- wc_get_container()->replace( FeaturesController::class, $this->features_controller_mock );
- }
-
/**
* Sets up the Jetpack connection manager mocking.
*