Commit 5e08a9556a9 for woocommerce
commit 5e08a9556a9077db7f1c7b4d591871bace06774c
Author: Chris Lilitsas <1105590+xristos3490@users.noreply.github.com>
Date: Wed Sep 9 21:43:23 2026 +0300
Expose stock notifications settings through the REST settings API (#68464)
* fix: expose stock notifications settings outside admin for REST API
* fix: keep stock notifications product edit hooks admin-only
* test: drop redundant hook cleanup, WP test case restores hooks itself
* test: wire stock notifications services before asserting settings outside admin
diff --git a/plugins/woocommerce/changelog/fix-wooplug-7691-stock-notifications-rest-settings b/plugins/woocommerce/changelog/fix-wooplug-7691-stock-notifications-rest-settings
new file mode 100644
index 00000000000..316c41832b6
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-wooplug-7691-stock-notifications-rest-settings
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Expose the Customer Stock Notifications settings through the REST settings API by registering them outside the admin context.
diff --git a/plugins/woocommerce/src/Internal/StockNotifications/Admin/AdminManager.php b/plugins/woocommerce/src/Internal/StockNotifications/Admin/AdminManager.php
index 59f1a0aef1f..a2defecd7b7 100644
--- a/plugins/woocommerce/src/Internal/StockNotifications/Admin/AdminManager.php
+++ b/plugins/woocommerce/src/Internal/StockNotifications/Admin/AdminManager.php
@@ -5,7 +5,6 @@ declare( strict_types = 1 );
namespace Automattic\WooCommerce\Internal\StockNotifications\Admin;
use Automattic\WooCommerce\Internal\StockNotifications\Admin\MenusController;
-use Automattic\WooCommerce\Internal\StockNotifications\Admin\SettingsController;
use Automattic\Jetpack\Constants;
/**
@@ -27,7 +26,6 @@ class AdminManager {
$container = wc_get_container();
$container->get( MenusController::class );
- $container->get( SettingsController::class );
}
/**
diff --git a/plugins/woocommerce/src/Internal/StockNotifications/Admin/SettingsController.php b/plugins/woocommerce/src/Internal/StockNotifications/Admin/SettingsController.php
index 8edbc1de0b5..d25f649086a 100644
--- a/plugins/woocommerce/src/Internal/StockNotifications/Admin/SettingsController.php
+++ b/plugins/woocommerce/src/Internal/StockNotifications/Admin/SettingsController.php
@@ -26,6 +26,12 @@ class SettingsController {
// Add the My Account endpoint setting to the Advanced tab.
add_filter( 'woocommerce_get_settings_advanced', array( $this, 'add_my_account_endpoint_setting' ), 100, 2 );
+ // The product edit hooks stay admin-only: process_product_object() reads $_POST and
+ // checks an admin nonce, so it must not run for cron or CLI code that fires the hook.
+ if ( ! is_admin() ) {
+ return;
+ }
+
// Display admin notices about incompatible settings combinations.
add_action( 'admin_notices', array( $this, 'output_admin_notices' ) );
diff --git a/plugins/woocommerce/src/Internal/StockNotifications/StockNotifications.php b/plugins/woocommerce/src/Internal/StockNotifications/StockNotifications.php
index cb3de9750d8..aba1fd9d521 100644
--- a/plugins/woocommerce/src/Internal/StockNotifications/StockNotifications.php
+++ b/plugins/woocommerce/src/Internal/StockNotifications/StockNotifications.php
@@ -13,6 +13,7 @@ use Automattic\WooCommerce\Internal\StockNotifications\Privacy\PrivacyEraser;
use Automattic\WooCommerce\Internal\StockNotifications\Emails\EmailManager;
use Automattic\WooCommerce\Internal\StockNotifications\AsyncTasks\NotificationsProcessor;
use Automattic\WooCommerce\Internal\StockNotifications\Admin\AdminManager;
+use Automattic\WooCommerce\Internal\StockNotifications\Admin\SettingsController;
use Automattic\WooCommerce\Internal\StockNotifications\Frontend\MyAccountEndpoint;
use Automattic\WooCommerce\Internal\StockNotifications\Frontend\ProductPageIntegration;
use Automattic\WooCommerce\Internal\StockNotifications\Frontend\FormHandlerService;
@@ -124,6 +125,10 @@ class StockNotifications implements RegisterHooksInterface {
$container->get( NotificationManagementService::class );
$container->get( MyAccountEndpoint::class );
+ // The settings filters must attach outside admin too, or the REST settings
+ // API (`is_admin()` is false there) never sees the feature's settings.
+ $container->get( SettingsController::class );
+
if ( is_admin() ) {
$container->get( AdminManager::class );
}
diff --git a/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Admin/SettingsControllerTests.php b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Admin/SettingsControllerTests.php
index 28186483b89..d522865ddc1 100644
--- a/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Admin/SettingsControllerTests.php
+++ b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Admin/SettingsControllerTests.php
@@ -3,7 +3,7 @@
declare( strict_types = 1 );
namespace Automattic\WooCommerce\Tests\Internal\StockNotifications\Admin;
-use Automattic\WooCommerce\Internal\StockNotifications\Admin\SettingsController as StockNotificationsSettings;
+use Automattic\WooCommerce\Internal\StockNotifications\Admin\SettingsController;
use Automattic\WooCommerce\Internal\StockNotifications\Frontend\MyAccountEndpoint;
use WC_Settings_Advanced;
use WC_Settings_Products;
@@ -16,7 +16,7 @@ class SettingsControllerTests extends \WC_Settings_Unit_Test_Case {
/**
* The controller whose hooks the tests exercise.
*
- * @var StockNotificationsSettings
+ * @var SettingsController
*/
private $controller;
@@ -25,9 +25,8 @@ class SettingsControllerTests extends \WC_Settings_Unit_Test_Case {
*/
public function setUp(): void {
parent::setUp();
- // Only AdminManager resolves the controller, and only in admin context. Built directly
- // rather than through the container, whose cached instance loses its hooks after the first test.
- $this->controller = new StockNotificationsSettings();
+ // Built directly rather than through the container, whose cached instance loses its hooks after the first test.
+ $this->controller = new SettingsController();
}
/**
@@ -74,6 +73,39 @@ class SettingsControllerTests extends \WC_Settings_Unit_Test_Case {
$this->assertEquals( $expected, $setting_ids_and_types );
}
+ /**
+ * @testdox Outside admin, only the settings filters are attached.
+ */
+ public function test_only_settings_filters_are_attached_outside_admin(): void {
+ $this->assertFalse( is_admin() );
+
+ $sut = new SettingsController();
+
+ $this->assertSame( 100, has_filter( 'woocommerce_get_sections_products', array( $sut, 'add_customer_stock_notifications_section' ) ) );
+ $this->assertSame( 100, has_filter( 'woocommerce_get_settings_products', array( $sut, 'add_customer_stock_notifications_settings' ) ) );
+ $this->assertSame( 100, has_filter( 'woocommerce_get_settings_advanced', array( $sut, 'add_my_account_endpoint_setting' ) ) );
+ $this->assertFalse( has_action( 'admin_notices', array( $sut, 'output_admin_notices' ) ) );
+ $this->assertFalse( has_action( 'woocommerce_product_options_stock_status', array( $sut, 'add_disable_stock_notifications_checkbox' ) ) );
+ $this->assertFalse( has_action( 'woocommerce_admin_process_product_object', array( $sut, 'process_product_object' ) ) );
+ }
+
+ /**
+ * @testdox In admin, the product edit and notice hooks are attached as well.
+ */
+ public function test_admin_hooks_are_attached_in_admin(): void {
+ set_current_screen( 'edit-post' );
+ $this->assertTrue( is_admin() );
+
+ $sut = new SettingsController();
+
+ $this->assertSame( 100, has_filter( 'woocommerce_get_sections_products', array( $sut, 'add_customer_stock_notifications_section' ) ) );
+ $this->assertSame( 100, has_filter( 'woocommerce_get_settings_products', array( $sut, 'add_customer_stock_notifications_settings' ) ) );
+ $this->assertSame( 100, has_filter( 'woocommerce_get_settings_advanced', array( $sut, 'add_my_account_endpoint_setting' ) ) );
+ $this->assertSame( 10, has_action( 'admin_notices', array( $sut, 'output_admin_notices' ) ) );
+ $this->assertSame( 20, has_action( 'woocommerce_product_options_stock_status', array( $sut, 'add_disable_stock_notifications_checkbox' ) ) );
+ $this->assertSame( 10, has_action( 'woocommerce_admin_process_product_object', array( $sut, 'process_product_object' ) ) );
+ }
+
/**
* @testdox The My Account endpoint setting is added to the Advanced tab, inside the account endpoints group, right after Downloads.
*/
diff --git a/plugins/woocommerce/tests/php/src/Internal/StockNotifications/StockNotificationsTests.php b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/StockNotificationsTests.php
index f2bebdf100b..ce9b0150d3a 100644
--- a/plugins/woocommerce/tests/php/src/Internal/StockNotifications/StockNotificationsTests.php
+++ b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/StockNotificationsTests.php
@@ -6,6 +6,8 @@ namespace Automattic\WooCommerce\Tests\Internal\StockNotifications;
use Automattic\WooCommerce\Internal\Features\FeaturesController;
use Automattic\WooCommerce\Internal\StockNotifications\DataRetentionController;
use Automattic\WooCommerce\Internal\StockNotifications\StockNotifications;
+use WC_Admin_Settings;
+use WC_Settings_Products;
/**
* StockNotifications controller tests.
@@ -85,6 +87,32 @@ class StockNotificationsTests extends \WC_Unit_Test_Case {
}
}
+ /**
+ * @testdox The settings are registered outside admin, so the REST settings API can see them.
+ */
+ public function test_settings_are_registered_outside_admin(): void {
+ $this->assertFalse( is_admin(), 'This test must run outside the admin context, as REST requests do.' );
+ $this->init_stock_notifications_services();
+
+ $products_page = null;
+ foreach ( WC_Admin_Settings::get_settings_pages() as $page ) {
+ if ( $page instanceof WC_Settings_Products ) {
+ $products_page = $page;
+ break;
+ }
+ }
+ $this->assertNotNull( $products_page, 'The Products settings page should be registered.' );
+
+ $this->assertArrayHasKey( 'customer_stock_notifications', $products_page->get_sections() );
+
+ $setting_ids = array_column( $products_page->get_settings_for_section( 'customer_stock_notifications' ), 'id' );
+ $this->assertContains( 'woocommerce_customer_stock_notifications_allow_signups', $setting_ids );
+ $this->assertContains( 'woocommerce_customer_stock_notifications_require_double_opt_in', $setting_ids );
+ $this->assertContains( 'woocommerce_customer_stock_notifications_require_account', $setting_ids );
+ $this->assertContains( 'woocommerce_customer_stock_notifications_create_account_on_signup', $setting_ids );
+ $this->assertContains( 'woocommerce_customer_stock_notifications_unverified_deletions_days_threshold', $setting_ids );
+ }
+
/**
* @testdox Changes to unrelated features are ignored.
*/