From 03be585f675b87710df85a388ecc51033ece0824 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Mon, 13 Apr 2026 17:35:22 +0100 Subject: [PATCH 1/6] Connectors: Gate non-AI setting auto-registration --- src/wp-includes/connectors.php | 16 +- .../wpRegisterDefaultConnectorSettings.php | 237 ++++++++++++++++++ 2 files changed, 250 insertions(+), 3 deletions(-) create mode 100644 tests/phpunit/tests/connectors/wpRegisterDefaultConnectorSettings.php diff --git a/src/wp-includes/connectors.php b/src/wp-includes/connectors.php index 63e018074fd58..993020b6f679c 100644 --- a/src/wp-includes/connectors.php +++ b/src/wp-includes/connectors.php @@ -552,9 +552,19 @@ function _wp_register_default_connector_settings(): void { continue; } - // For AI providers, skip if the provider is not in the AI Client registry. - if ( 'ai_provider' === $connector_data['type'] && ! $ai_registry->hasProvider( $connector_id ) ) { - continue; + if ( 'ai_provider' === $connector_data['type'] ) { + // For AI providers, skip if the provider is not in the AI Client registry. + if ( ! $ai_registry->hasProvider( $connector_id ) ) { + continue; + } + } else { + if ( ! isset( $connector_data['plugin']['is_active'] ) || ! is_callable( $connector_data['plugin']['is_active'] ) ) { + continue; + } + + if ( ! call_user_func( $connector_data['plugin']['is_active'] ) ) { + continue; + } } register_setting( diff --git a/tests/phpunit/tests/connectors/wpRegisterDefaultConnectorSettings.php b/tests/phpunit/tests/connectors/wpRegisterDefaultConnectorSettings.php new file mode 100644 index 0000000000000..42723adbb3cd9 --- /dev/null +++ b/tests/phpunit/tests/connectors/wpRegisterDefaultConnectorSettings.php @@ -0,0 +1,237 @@ +original_registry = WP_Connector_Registry::get_instance(); + } + + /** + * Restores the original registry and connector settings after each test. + */ + public function tear_down(): void { + foreach ( array_unique( $this->touched_settings ) as $setting_name ) { + unregister_setting( 'connectors', $setting_name ); + } + + $this->set_registry_instance( $this->original_registry ); + + if ( null !== $this->original_registry ) { + _wp_register_default_connector_settings(); + } + + parent::tear_down(); + } + + /** + * @ticket 64730 + */ + public function test_ai_connector_settings_still_auto_register(): void { + $setting_name = 'connectors_ai_openai_api_key'; + $this->touch_setting( $setting_name ); + unregister_setting( 'connectors', $setting_name ); + + $openai = wp_get_connector( 'openai' ); + $this->assertIsArray( $openai ); + + $registry = new WP_Connector_Registry(); + $this->set_registered_connectors( + $registry, + array( + 'openai' => $openai, + ) + ); + $this->set_registry_instance( $registry ); + + _wp_register_default_connector_settings(); + + $this->assertArrayHasKey( $setting_name, get_registered_settings() ); + } + + /** + * @ticket 64730 + */ + public function test_non_ai_connector_settings_auto_register_when_plugin_is_active_returns_true(): void { + $setting_name = 'connectors_spam_filtering_test_active_api_key'; + $this->touch_setting( $setting_name ); + + $registry = $this->create_non_ai_registry( + 'test-active', + $setting_name, + static function (): bool { + return true; + } + ); + $this->set_registry_instance( $registry ); + + _wp_register_default_connector_settings(); + + $this->assertArrayHasKey( $setting_name, get_registered_settings() ); + } + + /** + * @ticket 64730 + */ + public function test_non_ai_connector_settings_do_not_auto_register_when_plugin_is_active_missing(): void { + $setting_name = 'connectors_spam_filtering_test_missing_api_key'; + $this->touch_setting( $setting_name ); + + $registry = $this->create_non_ai_registry( 'test-missing', $setting_name ); + $this->set_registry_instance( $registry ); + + _wp_register_default_connector_settings(); + + $this->assertArrayNotHasKey( $setting_name, get_registered_settings() ); + } + + /** + * @ticket 64730 + */ + public function test_non_ai_connector_settings_do_not_auto_register_when_plugin_is_active_returns_false(): void { + $setting_name = 'connectors_spam_filtering_test_inactive_api_key'; + $this->touch_setting( $setting_name ); + + $registry = $this->create_non_ai_registry( + 'test-inactive', + $setting_name, + static function (): bool { + return false; + } + ); + $this->set_registry_instance( $registry ); + + _wp_register_default_connector_settings(); + + $this->assertArrayNotHasKey( $setting_name, get_registered_settings() ); + } + + /** + * @ticket 64730 + */ + public function test_non_ai_connector_settings_do_not_auto_register_when_plugin_is_active_not_callable(): void { + $setting_name = 'connectors_spam_filtering_test_invalid_api_key'; + $this->touch_setting( $setting_name ); + + $registry = $this->create_non_ai_registry( 'test-invalid', $setting_name, 'not_a_callback' ); + $this->set_registry_instance( $registry ); + + _wp_register_default_connector_settings(); + + $this->assertArrayNotHasKey( $setting_name, get_registered_settings() ); + } + + /** + * Creates a registry containing a single non-AI connector. + * + * @param string $connector_id Connector ID. + * @param string $setting_name Setting name. + * @param mixed $plugin_status Optional. Value to inject into plugin.is_active. + * @return WP_Connector_Registry + */ + private function create_non_ai_registry( string $connector_id, string $setting_name, $plugin_status = null ): WP_Connector_Registry { + $registry = new WP_Connector_Registry(); + $registry->register( + $connector_id, + array( + 'name' => 'Test Connector', + 'description' => 'A test connector.', + 'type' => 'spam_filtering', + 'plugin' => array( + 'file' => 'test-plugin/test-plugin.php', + ), + 'authentication' => array( + 'method' => 'api_key', + 'credentials_url' => 'https://example.com/keys', + 'setting_name' => $setting_name, + ), + ) + ); + + $connectors = $this->get_registered_connectors( $registry ); + + if ( null !== $plugin_status ) { + $connectors[ $connector_id ]['plugin']['is_active'] = $plugin_status; + } + + $this->set_registered_connectors( $registry, $connectors ); + + return $registry; + } + + /** + * Gets the registered connectors from a registry instance. + * + * @param WP_Connector_Registry $registry Connector registry. + * @return array + */ + private function get_registered_connectors( WP_Connector_Registry $registry ): array { + $property = new ReflectionProperty( WP_Connector_Registry::class, 'registered_connectors' ); + if ( method_exists( $property, 'setAccessible' ) ) { + $property->setAccessible( true ); + } + + return $property->getValue( $registry ); + } + + /** + * Sets the registered connectors for a registry instance. + * + * @param WP_Connector_Registry $registry Connector registry. + * @param array $connectors Connector data. + */ + private function set_registered_connectors( WP_Connector_Registry $registry, array $connectors ): void { + $property = new ReflectionProperty( WP_Connector_Registry::class, 'registered_connectors' ); + if ( method_exists( $property, 'setAccessible' ) ) { + $property->setAccessible( true ); + } + + $property->setValue( $registry, $connectors ); + } + + /** + * Sets the static registry instance. + * + * @param WP_Connector_Registry|null $registry Connector registry instance. + */ + private function set_registry_instance( ?WP_Connector_Registry $registry ): void { + $property = new ReflectionProperty( WP_Connector_Registry::class, 'instance' ); + if ( method_exists( $property, 'setAccessible' ) ) { + $property->setAccessible( true ); + } + + $property->setValue( null, $registry ); + } + + /** + * Tracks a setting name for cleanup. + * + * @param string $setting_name Setting name. + */ + private function touch_setting( string $setting_name ): void { + $this->touched_settings[] = $setting_name; + } +} From bc391694e4f7507c498ee2ba4a4b88736a7d8b02 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Thu, 16 Apr 2026 07:16:58 +0100 Subject: [PATCH 2/6] test updates --- .../wpRegisterDefaultConnectorSettings.php | 63 ++++++++++--------- 1 file changed, 34 insertions(+), 29 deletions(-) diff --git a/tests/phpunit/tests/connectors/wpRegisterDefaultConnectorSettings.php b/tests/phpunit/tests/connectors/wpRegisterDefaultConnectorSettings.php index 42723adbb3cd9..88fc06b4cf97d 100644 --- a/tests/phpunit/tests/connectors/wpRegisterDefaultConnectorSettings.php +++ b/tests/phpunit/tests/connectors/wpRegisterDefaultConnectorSettings.php @@ -1,5 +1,7 @@ original_registry = WP_Connector_Registry::get_instance(); + + global $wp_registered_settings; + $this->original_registered_settings = $wp_registered_settings; } /** - * Restores the original registry and connector settings after each test. + * Restores the original registry and registered settings after each test. */ public function tear_down(): void { - foreach ( array_unique( $this->touched_settings ) as $setting_name ) { - unregister_setting( 'connectors', $setting_name ); - } + global $wp_registered_settings; + $wp_registered_settings = $this->original_registered_settings; $this->set_registry_instance( $this->original_registry ); - if ( null !== $this->original_registry ) { - _wp_register_default_connector_settings(); - } - parent::tear_down(); } @@ -51,18 +69,17 @@ public function tear_down(): void { * @ticket 64730 */ public function test_ai_connector_settings_still_auto_register(): void { - $setting_name = 'connectors_ai_openai_api_key'; - $this->touch_setting( $setting_name ); + $setting_name = 'connectors_ai_mock_connectors_test_api_key'; unregister_setting( 'connectors', $setting_name ); - $openai = wp_get_connector( 'openai' ); - $this->assertIsArray( $openai ); + $mock = wp_get_connector( 'mock-connectors-test' ); + $this->assertIsArray( $mock ); $registry = new WP_Connector_Registry(); $this->set_registered_connectors( $registry, array( - 'openai' => $openai, + 'mock-connectors-test' => $mock, ) ); $this->set_registry_instance( $registry ); @@ -77,7 +94,6 @@ public function test_ai_connector_settings_still_auto_register(): void { */ public function test_non_ai_connector_settings_auto_register_when_plugin_is_active_returns_true(): void { $setting_name = 'connectors_spam_filtering_test_active_api_key'; - $this->touch_setting( $setting_name ); $registry = $this->create_non_ai_registry( 'test-active', @@ -98,7 +114,6 @@ static function (): bool { */ public function test_non_ai_connector_settings_do_not_auto_register_when_plugin_is_active_missing(): void { $setting_name = 'connectors_spam_filtering_test_missing_api_key'; - $this->touch_setting( $setting_name ); $registry = $this->create_non_ai_registry( 'test-missing', $setting_name ); $this->set_registry_instance( $registry ); @@ -113,7 +128,6 @@ public function test_non_ai_connector_settings_do_not_auto_register_when_plugin_ */ public function test_non_ai_connector_settings_do_not_auto_register_when_plugin_is_active_returns_false(): void { $setting_name = 'connectors_spam_filtering_test_inactive_api_key'; - $this->touch_setting( $setting_name ); $registry = $this->create_non_ai_registry( 'test-inactive', @@ -134,7 +148,6 @@ static function (): bool { */ public function test_non_ai_connector_settings_do_not_auto_register_when_plugin_is_active_not_callable(): void { $setting_name = 'connectors_spam_filtering_test_invalid_api_key'; - $this->touch_setting( $setting_name ); $registry = $this->create_non_ai_registry( 'test-invalid', $setting_name, 'not_a_callback' ); $this->set_registry_instance( $registry ); @@ -226,12 +239,4 @@ private function set_registry_instance( ?WP_Connector_Registry $registry ): void $property->setValue( null, $registry ); } - /** - * Tracks a setting name for cleanup. - * - * @param string $setting_name Setting name. - */ - private function touch_setting( string $setting_name ): void { - $this->touched_settings[] = $setting_name; - } } From 44ef30bf49755c78b72efdf17293b9ab783b4bfb Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Thu, 16 Apr 2026 07:33:54 +0100 Subject: [PATCH 3/6] test fixes --- .../wpRegisterDefaultConnectorSettings.php | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/tests/phpunit/tests/connectors/wpRegisterDefaultConnectorSettings.php b/tests/phpunit/tests/connectors/wpRegisterDefaultConnectorSettings.php index 88fc06b4cf97d..7c928a33a585c 100644 --- a/tests/phpunit/tests/connectors/wpRegisterDefaultConnectorSettings.php +++ b/tests/phpunit/tests/connectors/wpRegisterDefaultConnectorSettings.php @@ -203,10 +203,6 @@ private function create_non_ai_registry( string $connector_id, string $setting_n */ private function get_registered_connectors( WP_Connector_Registry $registry ): array { $property = new ReflectionProperty( WP_Connector_Registry::class, 'registered_connectors' ); - if ( method_exists( $property, 'setAccessible' ) ) { - $property->setAccessible( true ); - } - return $property->getValue( $registry ); } @@ -218,10 +214,6 @@ private function get_registered_connectors( WP_Connector_Registry $registry ): a */ private function set_registered_connectors( WP_Connector_Registry $registry, array $connectors ): void { $property = new ReflectionProperty( WP_Connector_Registry::class, 'registered_connectors' ); - if ( method_exists( $property, 'setAccessible' ) ) { - $property->setAccessible( true ); - } - $property->setValue( $registry, $connectors ); } @@ -232,11 +224,6 @@ private function set_registered_connectors( WP_Connector_Registry $registry, arr */ private function set_registry_instance( ?WP_Connector_Registry $registry ): void { $property = new ReflectionProperty( WP_Connector_Registry::class, 'instance' ); - if ( method_exists( $property, 'setAccessible' ) ) { - $property->setAccessible( true ); - } - $property->setValue( null, $registry ); } - } From 823173f469408562d8eeb20a13121f82a2c0bae3 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Thu, 16 Apr 2026 17:22:16 +0100 Subject: [PATCH 4/6] remove test --- .../wpRegisterDefaultConnectorSettings.php | 229 ------------------ 1 file changed, 229 deletions(-) delete mode 100644 tests/phpunit/tests/connectors/wpRegisterDefaultConnectorSettings.php diff --git a/tests/phpunit/tests/connectors/wpRegisterDefaultConnectorSettings.php b/tests/phpunit/tests/connectors/wpRegisterDefaultConnectorSettings.php deleted file mode 100644 index 7c928a33a585c..0000000000000 --- a/tests/phpunit/tests/connectors/wpRegisterDefaultConnectorSettings.php +++ /dev/null @@ -1,229 +0,0 @@ -original_registry = WP_Connector_Registry::get_instance(); - - global $wp_registered_settings; - $this->original_registered_settings = $wp_registered_settings; - } - - /** - * Restores the original registry and registered settings after each test. - */ - public function tear_down(): void { - global $wp_registered_settings; - $wp_registered_settings = $this->original_registered_settings; - - $this->set_registry_instance( $this->original_registry ); - - parent::tear_down(); - } - - /** - * @ticket 64730 - */ - public function test_ai_connector_settings_still_auto_register(): void { - $setting_name = 'connectors_ai_mock_connectors_test_api_key'; - unregister_setting( 'connectors', $setting_name ); - - $mock = wp_get_connector( 'mock-connectors-test' ); - $this->assertIsArray( $mock ); - - $registry = new WP_Connector_Registry(); - $this->set_registered_connectors( - $registry, - array( - 'mock-connectors-test' => $mock, - ) - ); - $this->set_registry_instance( $registry ); - - _wp_register_default_connector_settings(); - - $this->assertArrayHasKey( $setting_name, get_registered_settings() ); - } - - /** - * @ticket 64730 - */ - public function test_non_ai_connector_settings_auto_register_when_plugin_is_active_returns_true(): void { - $setting_name = 'connectors_spam_filtering_test_active_api_key'; - - $registry = $this->create_non_ai_registry( - 'test-active', - $setting_name, - static function (): bool { - return true; - } - ); - $this->set_registry_instance( $registry ); - - _wp_register_default_connector_settings(); - - $this->assertArrayHasKey( $setting_name, get_registered_settings() ); - } - - /** - * @ticket 64730 - */ - public function test_non_ai_connector_settings_do_not_auto_register_when_plugin_is_active_missing(): void { - $setting_name = 'connectors_spam_filtering_test_missing_api_key'; - - $registry = $this->create_non_ai_registry( 'test-missing', $setting_name ); - $this->set_registry_instance( $registry ); - - _wp_register_default_connector_settings(); - - $this->assertArrayNotHasKey( $setting_name, get_registered_settings() ); - } - - /** - * @ticket 64730 - */ - public function test_non_ai_connector_settings_do_not_auto_register_when_plugin_is_active_returns_false(): void { - $setting_name = 'connectors_spam_filtering_test_inactive_api_key'; - - $registry = $this->create_non_ai_registry( - 'test-inactive', - $setting_name, - static function (): bool { - return false; - } - ); - $this->set_registry_instance( $registry ); - - _wp_register_default_connector_settings(); - - $this->assertArrayNotHasKey( $setting_name, get_registered_settings() ); - } - - /** - * @ticket 64730 - */ - public function test_non_ai_connector_settings_do_not_auto_register_when_plugin_is_active_not_callable(): void { - $setting_name = 'connectors_spam_filtering_test_invalid_api_key'; - - $registry = $this->create_non_ai_registry( 'test-invalid', $setting_name, 'not_a_callback' ); - $this->set_registry_instance( $registry ); - - _wp_register_default_connector_settings(); - - $this->assertArrayNotHasKey( $setting_name, get_registered_settings() ); - } - - /** - * Creates a registry containing a single non-AI connector. - * - * @param string $connector_id Connector ID. - * @param string $setting_name Setting name. - * @param mixed $plugin_status Optional. Value to inject into plugin.is_active. - * @return WP_Connector_Registry - */ - private function create_non_ai_registry( string $connector_id, string $setting_name, $plugin_status = null ): WP_Connector_Registry { - $registry = new WP_Connector_Registry(); - $registry->register( - $connector_id, - array( - 'name' => 'Test Connector', - 'description' => 'A test connector.', - 'type' => 'spam_filtering', - 'plugin' => array( - 'file' => 'test-plugin/test-plugin.php', - ), - 'authentication' => array( - 'method' => 'api_key', - 'credentials_url' => 'https://example.com/keys', - 'setting_name' => $setting_name, - ), - ) - ); - - $connectors = $this->get_registered_connectors( $registry ); - - if ( null !== $plugin_status ) { - $connectors[ $connector_id ]['plugin']['is_active'] = $plugin_status; - } - - $this->set_registered_connectors( $registry, $connectors ); - - return $registry; - } - - /** - * Gets the registered connectors from a registry instance. - * - * @param WP_Connector_Registry $registry Connector registry. - * @return array - */ - private function get_registered_connectors( WP_Connector_Registry $registry ): array { - $property = new ReflectionProperty( WP_Connector_Registry::class, 'registered_connectors' ); - return $property->getValue( $registry ); - } - - /** - * Sets the registered connectors for a registry instance. - * - * @param WP_Connector_Registry $registry Connector registry. - * @param array $connectors Connector data. - */ - private function set_registered_connectors( WP_Connector_Registry $registry, array $connectors ): void { - $property = new ReflectionProperty( WP_Connector_Registry::class, 'registered_connectors' ); - $property->setValue( $registry, $connectors ); - } - - /** - * Sets the static registry instance. - * - * @param WP_Connector_Registry|null $registry Connector registry instance. - */ - private function set_registry_instance( ?WP_Connector_Registry $registry ): void { - $property = new ReflectionProperty( WP_Connector_Registry::class, 'instance' ); - $property->setValue( null, $registry ); - } -} From 74a2ca6ee904f7d3526e2da495916db5f5413955 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Thu, 16 Apr 2026 17:35:32 +0100 Subject: [PATCH 5/6] Simple test --- .../wpRegisterDefaultConnectorSettings.php | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 tests/phpunit/tests/connectors/wpRegisterDefaultConnectorSettings.php diff --git a/tests/phpunit/tests/connectors/wpRegisterDefaultConnectorSettings.php b/tests/phpunit/tests/connectors/wpRegisterDefaultConnectorSettings.php new file mode 100644 index 0000000000000..3a695a4f950a7 --- /dev/null +++ b/tests/phpunit/tests/connectors/wpRegisterDefaultConnectorSettings.php @@ -0,0 +1,49 @@ +is_registered( self::CONNECTOR_ID ) ) { + $registry->unregister( self::CONNECTOR_ID ); + } + + unregister_setting( 'connectors', self::SETTING_NAME ); + + parent::tear_down(); + } + + /** + * @ticket 64730 + */ + public function test_non_ai_connector_skipped_when_is_active_missing(): void { + WP_Connector_Registry::get_instance()->register( + self::CONNECTOR_ID, + array( + 'name' => 'Test Non-AI Connector', + 'description' => '', + 'type' => 'spam_filtering', + 'authentication' => array( + 'method' => 'api_key', + 'setting_name' => self::SETTING_NAME, + ), + ) + ); + + _wp_register_default_connector_settings(); + + $this->assertArrayNotHasKey( self::SETTING_NAME, get_registered_settings() ); + } +} From 8093a56e0782f777027b89b0762d4fb020341eca Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Thu, 16 Apr 2026 17:55:54 +0100 Subject: [PATCH 6/6] Isolate registered settings in connector gate test --- .../wpRegisterDefaultConnectorSettings.php | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/tests/phpunit/tests/connectors/wpRegisterDefaultConnectorSettings.php b/tests/phpunit/tests/connectors/wpRegisterDefaultConnectorSettings.php index 3a695a4f950a7..19ba81ba8ac2b 100644 --- a/tests/phpunit/tests/connectors/wpRegisterDefaultConnectorSettings.php +++ b/tests/phpunit/tests/connectors/wpRegisterDefaultConnectorSettings.php @@ -8,11 +8,28 @@ */ class Tests_Connectors_WpRegisterDefaultConnectorSettings extends WP_UnitTestCase { - const CONNECTOR_ID = 'wp_test_non_ai_connector'; - const SETTING_NAME = 'connectors_test_non_ai_api_key'; + const CONNECTOR_ID = 'wp_test_non_ai_connector'; + const SETTING_NAME = 'connectors_test_non_ai_api_key'; /** - * Removes the test connector and setting after each test. + * Snapshot of registered settings before each test. + * + * @var array + */ + private array $original_registered_settings = array(); + + /** + * Snapshots the registered settings before each test. + */ + public function set_up(): void { + parent::set_up(); + + global $wp_registered_settings; + $this->original_registered_settings = $wp_registered_settings; + } + + /** + * Removes the test connector and restores registered settings. */ public function tear_down(): void { $registry = WP_Connector_Registry::get_instance(); @@ -20,7 +37,8 @@ public function tear_down(): void { $registry->unregister( self::CONNECTOR_ID ); } - unregister_setting( 'connectors', self::SETTING_NAME ); + global $wp_registered_settings; + $wp_registered_settings = $this->original_registered_settings; parent::tear_down(); }