Skip to content

Commit 1ca586c

Browse files
Connectors: Gate non-AI setting auto-registration
1 parent aa72dfe commit 1ca586c

2 files changed

Lines changed: 250 additions & 3 deletions

File tree

src/wp-includes/connectors.php

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -552,9 +552,19 @@ function _wp_register_default_connector_settings(): void {
552552
continue;
553553
}
554554

555-
// For AI providers, skip if the provider is not in the AI Client registry.
556-
if ( 'ai_provider' === $connector_data['type'] && ! $ai_registry->hasProvider( $connector_id ) ) {
557-
continue;
555+
if ( 'ai_provider' === $connector_data['type'] ) {
556+
// For AI providers, skip if the provider is not in the AI Client registry.
557+
if ( ! $ai_registry->hasProvider( $connector_id ) ) {
558+
continue;
559+
}
560+
} else {
561+
if ( ! isset( $connector_data['plugin']['is_active'] ) || ! is_callable( $connector_data['plugin']['is_active'] ) ) {
562+
continue;
563+
}
564+
565+
if ( ! call_user_func( $connector_data['plugin']['is_active'] ) ) {
566+
continue;
567+
}
558568
}
559569

560570
register_setting(
Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
<?php
2+
3+
/**
4+
* Tests for _wp_register_default_connector_settings().
5+
*
6+
* @group connectors
7+
* @covers ::_wp_register_default_connector_settings
8+
*/
9+
class Tests_Connectors_WpRegisterDefaultConnectorSettings extends WP_UnitTestCase {
10+
11+
/**
12+
* Original connector registry instance.
13+
*
14+
* @var WP_Connector_Registry|null
15+
*/
16+
private ?WP_Connector_Registry $original_registry;
17+
18+
/**
19+
* Setting names touched during a test.
20+
*
21+
* @var string[]
22+
*/
23+
private array $touched_settings = array();
24+
25+
/**
26+
* Stores the original registry before each test.
27+
*/
28+
public function set_up(): void {
29+
parent::set_up();
30+
$this->original_registry = WP_Connector_Registry::get_instance();
31+
}
32+
33+
/**
34+
* Restores the original registry and connector settings after each test.
35+
*/
36+
public function tear_down(): void {
37+
foreach ( array_unique( $this->touched_settings ) as $setting_name ) {
38+
unregister_setting( 'connectors', $setting_name );
39+
}
40+
41+
$this->set_registry_instance( $this->original_registry );
42+
43+
if ( null !== $this->original_registry ) {
44+
_wp_register_default_connector_settings();
45+
}
46+
47+
parent::tear_down();
48+
}
49+
50+
/**
51+
* @ticket 64730
52+
*/
53+
public function test_ai_connector_settings_still_auto_register(): void {
54+
$setting_name = 'connectors_ai_openai_api_key';
55+
$this->touch_setting( $setting_name );
56+
unregister_setting( 'connectors', $setting_name );
57+
58+
$openai = wp_get_connector( 'openai' );
59+
$this->assertIsArray( $openai );
60+
61+
$registry = new WP_Connector_Registry();
62+
$this->set_registered_connectors(
63+
$registry,
64+
array(
65+
'openai' => $openai,
66+
)
67+
);
68+
$this->set_registry_instance( $registry );
69+
70+
_wp_register_default_connector_settings();
71+
72+
$this->assertArrayHasKey( $setting_name, get_registered_settings() );
73+
}
74+
75+
/**
76+
* @ticket 64730
77+
*/
78+
public function test_non_ai_connector_settings_auto_register_when_plugin_is_active_returns_true(): void {
79+
$setting_name = 'connectors_spam_filtering_test_active_api_key';
80+
$this->touch_setting( $setting_name );
81+
82+
$registry = $this->create_non_ai_registry(
83+
'test-active',
84+
$setting_name,
85+
static function (): bool {
86+
return true;
87+
}
88+
);
89+
$this->set_registry_instance( $registry );
90+
91+
_wp_register_default_connector_settings();
92+
93+
$this->assertArrayHasKey( $setting_name, get_registered_settings() );
94+
}
95+
96+
/**
97+
* @ticket 64730
98+
*/
99+
public function test_non_ai_connector_settings_do_not_auto_register_when_plugin_is_active_missing(): void {
100+
$setting_name = 'connectors_spam_filtering_test_missing_api_key';
101+
$this->touch_setting( $setting_name );
102+
103+
$registry = $this->create_non_ai_registry( 'test-missing', $setting_name );
104+
$this->set_registry_instance( $registry );
105+
106+
_wp_register_default_connector_settings();
107+
108+
$this->assertArrayNotHasKey( $setting_name, get_registered_settings() );
109+
}
110+
111+
/**
112+
* @ticket 64730
113+
*/
114+
public function test_non_ai_connector_settings_do_not_auto_register_when_plugin_is_active_returns_false(): void {
115+
$setting_name = 'connectors_spam_filtering_test_inactive_api_key';
116+
$this->touch_setting( $setting_name );
117+
118+
$registry = $this->create_non_ai_registry(
119+
'test-inactive',
120+
$setting_name,
121+
static function (): bool {
122+
return false;
123+
}
124+
);
125+
$this->set_registry_instance( $registry );
126+
127+
_wp_register_default_connector_settings();
128+
129+
$this->assertArrayNotHasKey( $setting_name, get_registered_settings() );
130+
}
131+
132+
/**
133+
* @ticket 64730
134+
*/
135+
public function test_non_ai_connector_settings_do_not_auto_register_when_plugin_is_active_not_callable(): void {
136+
$setting_name = 'connectors_spam_filtering_test_invalid_api_key';
137+
$this->touch_setting( $setting_name );
138+
139+
$registry = $this->create_non_ai_registry( 'test-invalid', $setting_name, 'not_a_callback' );
140+
$this->set_registry_instance( $registry );
141+
142+
_wp_register_default_connector_settings();
143+
144+
$this->assertArrayNotHasKey( $setting_name, get_registered_settings() );
145+
}
146+
147+
/**
148+
* Creates a registry containing a single non-AI connector.
149+
*
150+
* @param string $connector_id Connector ID.
151+
* @param string $setting_name Setting name.
152+
* @param mixed $plugin_status Optional. Value to inject into plugin.is_active.
153+
* @return WP_Connector_Registry
154+
*/
155+
private function create_non_ai_registry( string $connector_id, string $setting_name, $plugin_status = null ): WP_Connector_Registry {
156+
$registry = new WP_Connector_Registry();
157+
$registry->register(
158+
$connector_id,
159+
array(
160+
'name' => 'Test Connector',
161+
'description' => 'A test connector.',
162+
'type' => 'spam_filtering',
163+
'plugin' => array(
164+
'file' => 'test-plugin/test-plugin.php',
165+
),
166+
'authentication' => array(
167+
'method' => 'api_key',
168+
'credentials_url' => 'https://example.com/keys',
169+
'setting_name' => $setting_name,
170+
),
171+
)
172+
);
173+
174+
$connectors = $this->get_registered_connectors( $registry );
175+
176+
if ( null !== $plugin_status ) {
177+
$connectors[ $connector_id ]['plugin']['is_active'] = $plugin_status;
178+
}
179+
180+
$this->set_registered_connectors( $registry, $connectors );
181+
182+
return $registry;
183+
}
184+
185+
/**
186+
* Gets the registered connectors from a registry instance.
187+
*
188+
* @param WP_Connector_Registry $registry Connector registry.
189+
* @return array<string, array>
190+
*/
191+
private function get_registered_connectors( WP_Connector_Registry $registry ): array {
192+
$property = new ReflectionProperty( WP_Connector_Registry::class, 'registered_connectors' );
193+
if ( method_exists( $property, 'setAccessible' ) ) {
194+
$property->setAccessible( true );
195+
}
196+
197+
return $property->getValue( $registry );
198+
}
199+
200+
/**
201+
* Sets the registered connectors for a registry instance.
202+
*
203+
* @param WP_Connector_Registry $registry Connector registry.
204+
* @param array<string, array> $connectors Connector data.
205+
*/
206+
private function set_registered_connectors( WP_Connector_Registry $registry, array $connectors ): void {
207+
$property = new ReflectionProperty( WP_Connector_Registry::class, 'registered_connectors' );
208+
if ( method_exists( $property, 'setAccessible' ) ) {
209+
$property->setAccessible( true );
210+
}
211+
212+
$property->setValue( $registry, $connectors );
213+
}
214+
215+
/**
216+
* Sets the static registry instance.
217+
*
218+
* @param WP_Connector_Registry|null $registry Connector registry instance.
219+
*/
220+
private function set_registry_instance( ?WP_Connector_Registry $registry ): void {
221+
$property = new ReflectionProperty( WP_Connector_Registry::class, 'instance' );
222+
if ( method_exists( $property, 'setAccessible' ) ) {
223+
$property->setAccessible( true );
224+
}
225+
226+
$property->setValue( null, $registry );
227+
}
228+
229+
/**
230+
* Tracks a setting name for cleanup.
231+
*
232+
* @param string $setting_name Setting name.
233+
*/
234+
private function touch_setting( string $setting_name ): void {
235+
$this->touched_settings[] = $setting_name;
236+
}
237+
}

0 commit comments

Comments
 (0)