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