From 280451bae4c8cf12fcd332c609cc3e8ee3b85e82 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Mon, 30 Mar 2026 18:05:57 +0100 Subject: [PATCH 1/7] Connectors: Replace plugin.slug with plugin.file in connector registration Use the plugin's main file path (e.g. 'akismet/akismet.php') instead of just the slug. This lets the script module data function use file_exists() and is_plugin_active() directly, removing the get_plugins() slug-to-file map. The frontend derives the slug from the file path when needed. --- .../class-wp-connector-registry.php | 5 ++- src/wp-includes/connectors.php | 40 ++++++++----------- 2 files changed, 19 insertions(+), 26 deletions(-) diff --git a/src/wp-includes/class-wp-connector-registry.php b/src/wp-includes/class-wp-connector-registry.php index 18a5f80c94dbd..314bc7e455d84 100644 --- a/src/wp-includes/class-wp-connector-registry.php +++ b/src/wp-includes/class-wp-connector-registry.php @@ -40,7 +40,7 @@ * env_var_name?: non-empty-string * }, * plugin?: array{ - * slug: non-empty-string + * file: non-empty-string * } * } */ @@ -109,7 +109,8 @@ final class WP_Connector_Registry { * @type array $plugin { * Optional. Plugin data for install/activate UI. * - * @type string $slug The WordPress.org plugin slug. + * @type string $file The plugin's main file path relative to the plugins + * directory (e.g. 'akismet/akismet.php'). * } * } * @return array|null The registered connector data on success, null on failure. diff --git a/src/wp-includes/connectors.php b/src/wp-includes/connectors.php index 06683ccaaa25c..20f72fcb2265c 100644 --- a/src/wp-includes/connectors.php +++ b/src/wp-includes/connectors.php @@ -58,7 +58,8 @@ function wp_is_connector_registered( string $id ): bool { * @type array $plugin { * Optional. Plugin data for install/activate UI. * - * @type string $slug The WordPress.org plugin slug. + * @type string $file The plugin's main file path relative to the plugins + * directory (e.g. 'akismet/akismet.php'). * } * } * @phpstan-return ?array{ @@ -74,7 +75,7 @@ function wp_is_connector_registered( string $id ): bool { * env_var_name?: non-empty-string * }, * plugin?: array{ - * slug: non-empty-string + * file: non-empty-string * } * } */ @@ -118,7 +119,8 @@ function wp_get_connector( string $id ): ?array { * @type array $plugin { * Optional. Plugin data for install/activate UI. * - * @type string $slug The WordPress.org plugin slug. + * @type string $file The plugin's main file path relative to the plugins + * directory (e.g. 'akismet/akismet.php'). * } * } * } @@ -135,7 +137,7 @@ function wp_get_connector( string $id ): ?array { * env_var_name?: non-empty-string * }, * plugin?: array{ - * slug: non-empty-string + * file: non-empty-string * } * }> */ @@ -256,7 +258,7 @@ function _wp_connectors_register_default_ai_providers( WP_Connector_Registry $re 'description' => __( 'Text generation with Claude.' ), 'type' => 'ai_provider', 'plugin' => array( - 'slug' => 'ai-provider-for-anthropic', + 'file' => 'ai-provider-for-anthropic/plugin.php', ), 'authentication' => array( 'method' => 'api_key', @@ -268,7 +270,7 @@ function _wp_connectors_register_default_ai_providers( WP_Connector_Registry $re 'description' => __( 'Text and image generation with Gemini and Imagen.' ), 'type' => 'ai_provider', 'plugin' => array( - 'slug' => 'ai-provider-for-google', + 'file' => 'ai-provider-for-google/plugin.php', ), 'authentication' => array( 'method' => 'api_key', @@ -280,7 +282,7 @@ function _wp_connectors_register_default_ai_providers( WP_Connector_Registry $re 'description' => __( 'Text and image generation with GPT and Dall-E.' ), 'type' => 'ai_provider', 'plugin' => array( - 'slug' => 'ai-provider-for-openai', + 'file' => 'ai-provider-for-openai/plugin.php', ), 'authentication' => array( 'method' => 'api_key', @@ -636,15 +638,9 @@ function _wp_connectors_pass_default_keys_to_ai_client(): void { function _wp_connectors_get_connector_script_module_data( array $data ): array { $registry = AiClient::defaultRegistry(); - // Build a slug-to-file map for plugin installation status. - if ( ! function_exists( 'get_plugins' ) ) { + if ( ! function_exists( 'is_plugin_active' ) ) { require_once ABSPATH . 'wp-admin/includes/plugin.php'; } - $plugin_files_by_slug = array(); - foreach ( array_keys( get_plugins() ) as $plugin_file ) { - $slug = str_contains( $plugin_file, '/' ) ? dirname( $plugin_file ) : str_replace( '.php', '', $plugin_file ); - $plugin_files_by_slug[ $slug ] = $plugin_file; - } $connectors = array(); foreach ( wp_get_connectors() as $connector_id => $connector_data ) { @@ -676,18 +672,14 @@ function _wp_connectors_get_connector_script_module_data( array $data ): array { 'authentication' => $auth_out, ); - if ( ! empty( $connector_data['plugin']['slug'] ) ) { - $plugin_slug = $connector_data['plugin']['slug']; - $plugin_file = $plugin_files_by_slug[ $plugin_slug ] ?? null; - - $is_installed = null !== $plugin_file; - $is_activated = $is_installed && is_plugin_active( $plugin_file ); + if ( ! empty( $connector_data['plugin']['file'] ) ) { + $file = $connector_data['plugin']['file']; + $is_installed = file_exists( WP_PLUGIN_DIR . '/' . $file ); + $is_activated = $is_installed && is_plugin_active( $file ); $connector_out['plugin'] = array( - 'slug' => $plugin_slug, - 'pluginFile' => $is_installed - ? ( str_ends_with( $plugin_file, '.php' ) ? substr( $plugin_file, 0, -4 ) : $plugin_file ) - : null, + 'pluginFile' => str_ends_with( $file, '.php' ) ? substr( $file, 0, -4 ) : $file, + 'isInstalled' => $is_installed, 'isActivated' => $is_activated, ); } From 3010bcb7d1618eb74867c588082d403c498c7240 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Mon, 30 Mar 2026 18:37:08 +0100 Subject: [PATCH 2/7] Rename pluginFile to file in plugin JS output --- src/wp-includes/connectors.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/connectors.php b/src/wp-includes/connectors.php index 20f72fcb2265c..59c0c1889c8f2 100644 --- a/src/wp-includes/connectors.php +++ b/src/wp-includes/connectors.php @@ -678,7 +678,7 @@ function _wp_connectors_get_connector_script_module_data( array $data ): array { $is_activated = $is_installed && is_plugin_active( $file ); $connector_out['plugin'] = array( - 'pluginFile' => str_ends_with( $file, '.php' ) ? substr( $file, 0, -4 ) : $file, + 'file' => str_ends_with( $file, '.php' ) ? substr( $file, 0, -4 ) : $file, 'isInstalled' => $is_installed, 'isActivated' => $is_activated, ); From 05777f58ce94ef8338b372302a440ebfdfd3852f Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Tue, 31 Mar 2026 17:38:00 +0100 Subject: [PATCH 3/7] Update src/wp-includes/class-wp-connector-registry.php Co-authored-by: Mukesh Panchal --- src/wp-includes/class-wp-connector-registry.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/class-wp-connector-registry.php b/src/wp-includes/class-wp-connector-registry.php index 314bc7e455d84..7d0005ec75cc0 100644 --- a/src/wp-includes/class-wp-connector-registry.php +++ b/src/wp-includes/class-wp-connector-registry.php @@ -110,7 +110,7 @@ final class WP_Connector_Registry { * Optional. Plugin data for install/activate UI. * * @type string $file The plugin's main file path relative to the plugins - * directory (e.g. 'akismet/akismet.php'). + * directory (e.g. 'akismet/akismet.php'). * } * } * @return array|null The registered connector data on success, null on failure. From 9ef04ac1e0845e0b1c780262636c27df9201d763 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Tue, 31 Mar 2026 17:38:10 +0100 Subject: [PATCH 4/7] Update src/wp-includes/connectors.php Co-authored-by: Mukesh Panchal --- src/wp-includes/connectors.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/connectors.php b/src/wp-includes/connectors.php index 59c0c1889c8f2..5d13d83d444a6 100644 --- a/src/wp-includes/connectors.php +++ b/src/wp-includes/connectors.php @@ -59,7 +59,7 @@ function wp_is_connector_registered( string $id ): bool { * Optional. Plugin data for install/activate UI. * * @type string $file The plugin's main file path relative to the plugins - * directory (e.g. 'akismet/akismet.php'). + * directory (e.g. 'akismet/akismet.php'). * } * } * @phpstan-return ?array{ From c695600c40af8ca186086dd1210b2957e2ce1b9c Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Tue, 31 Mar 2026 17:59:41 +0100 Subject: [PATCH 5/7] feedback --- src/wp-includes/class-wp-connector-registry.php | 6 +++--- src/wp-includes/connectors.php | 4 ++-- tests/phpunit/tests/connectors/wpConnectorRegistry.php | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/wp-includes/class-wp-connector-registry.php b/src/wp-includes/class-wp-connector-registry.php index 7d0005ec75cc0..d7643360efeeb 100644 --- a/src/wp-includes/class-wp-connector-registry.php +++ b/src/wp-includes/class-wp-connector-registry.php @@ -110,7 +110,7 @@ final class WP_Connector_Registry { * Optional. Plugin data for install/activate UI. * * @type string $file The plugin's main file path relative to the plugins - * directory (e.g. 'akismet/akismet.php'). + * directory (e.g. 'akismet/akismet.php' or 'hello.php'). * } * } * @return array|null The registered connector data on success, null on failure. @@ -243,8 +243,8 @@ public function register( string $id, array $args ): ?array { } } - if ( ! empty( $args['plugin'] ) && is_array( $args['plugin'] ) ) { - $connector['plugin'] = $args['plugin']; + if ( ! empty( $args['plugin'] ) && is_array( $args['plugin'] ) && ! empty( $args['plugin']['file'] ) ) { + $connector['plugin'] = array( 'file' => $args['plugin']['file'] ); } $this->registered_connectors[ $id ] = $connector; diff --git a/src/wp-includes/connectors.php b/src/wp-includes/connectors.php index 5d13d83d444a6..17f929073956e 100644 --- a/src/wp-includes/connectors.php +++ b/src/wp-includes/connectors.php @@ -59,7 +59,7 @@ function wp_is_connector_registered( string $id ): bool { * Optional. Plugin data for install/activate UI. * * @type string $file The plugin's main file path relative to the plugins - * directory (e.g. 'akismet/akismet.php'). + * directory (e.g. 'akismet/akismet.php' or 'hello.php'). * } * } * @phpstan-return ?array{ @@ -678,7 +678,7 @@ function _wp_connectors_get_connector_script_module_data( array $data ): array { $is_activated = $is_installed && is_plugin_active( $file ); $connector_out['plugin'] = array( - 'file' => str_ends_with( $file, '.php' ) ? substr( $file, 0, -4 ) : $file, + 'file' => $file, 'isInstalled' => $is_installed, 'isActivated' => $is_activated, ); diff --git a/tests/phpunit/tests/connectors/wpConnectorRegistry.php b/tests/phpunit/tests/connectors/wpConnectorRegistry.php index cab030d930dcd..d1a46dc0981fe 100644 --- a/tests/phpunit/tests/connectors/wpConnectorRegistry.php +++ b/tests/phpunit/tests/connectors/wpConnectorRegistry.php @@ -294,12 +294,12 @@ public function test_register_omits_logo_url_when_empty() { */ public function test_register_includes_plugin_data() { $args = self::$default_args; - $args['plugin'] = array( 'slug' => 'my-plugin' ); + $args['plugin'] = array( 'file' => 'my-plugin/my-plugin.php' ); $result = $this->registry->register( 'with-plugin', $args ); $this->assertArrayHasKey( 'plugin', $result ); - $this->assertSame( array( 'slug' => 'my-plugin' ), $result['plugin'] ); + $this->assertSame( array( 'file' => 'my-plugin/my-plugin.php' ), $result['plugin'] ); } /** From f345615e84805ba3f5f230491bd8f99371d43f9e Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Wed, 1 Apr 2026 10:47:46 +0100 Subject: [PATCH 6/7] Update src/wp-includes/connectors.php Co-authored-by: Mukesh Panchal --- src/wp-includes/connectors.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/connectors.php b/src/wp-includes/connectors.php index 17f929073956e..b3bd4636dbc27 100644 --- a/src/wp-includes/connectors.php +++ b/src/wp-includes/connectors.php @@ -120,7 +120,7 @@ function wp_get_connector( string $id ): ?array { * Optional. Plugin data for install/activate UI. * * @type string $file The plugin's main file path relative to the plugins - * directory (e.g. 'akismet/akismet.php'). + * directory (e.g. 'akismet/akismet.php' or 'hello.php'). * } * } * } From 38ab4ff00b75398b95cf2b90a17e5fd570b4e68d Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Wed, 1 Apr 2026 15:05:58 +0100 Subject: [PATCH 7/7] normalize path --- src/wp-includes/connectors.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/connectors.php b/src/wp-includes/connectors.php index b3bd4636dbc27..68c8b4c1570d0 100644 --- a/src/wp-includes/connectors.php +++ b/src/wp-includes/connectors.php @@ -674,7 +674,7 @@ function _wp_connectors_get_connector_script_module_data( array $data ): array { if ( ! empty( $connector_data['plugin']['file'] ) ) { $file = $connector_data['plugin']['file']; - $is_installed = file_exists( WP_PLUGIN_DIR . '/' . $file ); + $is_installed = file_exists( wp_normalize_path( WP_PLUGIN_DIR . '/' . $file ) ); $is_activated = $is_installed && is_plugin_active( $file ); $connector_out['plugin'] = array(