From 3753ec96014f862dd5941e380891636b8fa1d353 Mon Sep 17 00:00:00 2001 From: Gautam Mehta Date: Tue, 21 Apr 2026 17:34:56 +0530 Subject: [PATCH 01/23] feat: add file mod status check and display notice for disabled installations --- .../connectors/default-connectors.php | 9 ++- routes/connectors-home/default-connectors.tsx | 26 ++++++--- routes/connectors-home/stage.tsx | 56 ++++++++++++++++++- routes/connectors-home/style.scss | 17 ++++++ 4 files changed, 97 insertions(+), 11 deletions(-) diff --git a/lib/experimental/connectors/default-connectors.php b/lib/experimental/connectors/default-connectors.php index 3f71f7b46eef06..4dc956bd9e4dc2 100644 --- a/lib/experimental/connectors/default-connectors.php +++ b/lib/experimental/connectors/default-connectors.php @@ -554,7 +554,14 @@ function _gutenberg_get_connector_script_module_data( array $data ): array { $connectors[ $connector_id ] = $connector_out; } ksort( $connectors ); - $data['connectors'] = $connectors; + + $is_file_mods_disabled = defined( 'DISALLOW_FILE_MODS' ) && DISALLOW_FILE_MODS; + if ( function_exists( 'wp_is_file_mod_allowed' ) ) { + $is_file_mods_disabled = ! wp_is_file_mod_allowed( 'install_plugins' ); + } + + $data['connectors'] = $connectors; + $data['isFileModsDisabled'] = $is_file_mods_disabled; return $data; } remove_filter( 'script_module_data_options-connectors-wp-admin', '_wp_connectors_get_connector_script_module_data' ); diff --git a/routes/connectors-home/default-connectors.tsx b/routes/connectors-home/default-connectors.tsx index 1ab02370bd8a36..688a05c13a5348 100644 --- a/routes/connectors-home/default-connectors.tsx +++ b/routes/connectors-home/default-connectors.tsx @@ -43,22 +43,34 @@ interface ConnectorData { authentication: NonNullable< ConnectorConfig[ 'authentication' ] >; } -/** - * Reads connector data passed from PHP via the script module data mechanism. - */ -export function getConnectorData(): Record< string, ConnectorData > { +interface ConnectorScriptModuleData { + connectors?: Record< string, ConnectorData >; + isFileModsDisabled?: boolean; +} + +function getConnectorScriptModuleData(): ConnectorScriptModuleData { try { - const parsed = JSON.parse( + return JSON.parse( document.getElementById( 'wp-script-module-data-options-connectors-wp-admin' - )?.textContent ?? '' + )?.textContent ?? '{}' ); - return parsed?.connectors ?? {}; } catch { return {}; } } +/** + * Reads connector data passed from PHP via the script module data mechanism. + */ +export function getConnectorData(): Record< string, ConnectorData > { + return getConnectorScriptModuleData().connectors ?? {}; +} + +export function getIsFileModsDisabled(): boolean { + return !! getConnectorScriptModuleData().isFileModsDisabled; +} + const CONNECTOR_LOGOS: Record< string, React.ComponentType > = { google: GeminiLogo, openai: OpenAILogo, diff --git a/routes/connectors-home/stage.tsx b/routes/connectors-home/stage.tsx index c15e3da3dee43c..43837f00f8bb05 100644 --- a/routes/connectors-home/stage.tsx +++ b/routes/connectors-home/stage.tsx @@ -4,6 +4,7 @@ import { Page } from '@wordpress/admin-ui'; import { Button, + Notice, __experimentalHeading as Heading, __experimentalText as WCText, __experimentalVStack as VStack, @@ -14,7 +15,7 @@ import { } from '@wordpress/connectors'; import { useSelect } from '@wordpress/data'; import { createInterpolateElement } from '@wordpress/element'; -import { __ } from '@wordpress/i18n'; +import { __, sprintf } from '@wordpress/i18n'; import { store as coreStore } from '@wordpress/core-data'; /** @@ -22,7 +23,10 @@ import { store as coreStore } from '@wordpress/core-data'; */ import './style.scss'; import { AiPluginCallout } from './ai-plugin-callout'; -import { registerDefaultConnectors } from './default-connectors'; +import { + getIsFileModsDisabled, + registerDefaultConnectors, +} from './default-connectors'; import { unlock } from '../lock-unlock'; const { store } = unlock( connectorsPrivateApis ); @@ -31,6 +35,8 @@ const { store } = unlock( connectorsPrivateApis ); registerDefaultConnectors(); function ConnectorsPage() { + const isFileModsDisabled = getIsFileModsDisabled(); + const { connectors, canInstallPlugins } = useSelect( ( select ) => ( { connectors: unlock( select( store ) ).getConnectors(), @@ -45,6 +51,21 @@ function ConnectorsPage() { const renderableConnectors = connectors.filter( ( connector: ConnectorConfig ) => connector.render ); + const aiProviderPluginSlugs = Array.from( + new Set( + connectors + .filter( + ( connector: ConnectorConfig ) => + connector.type === 'ai_provider' + ) + .map( + ( connector: ConnectorConfig ) => + connector.plugin?.file?.split( '/' )[ 0 ] + ) + .filter( ( slug ): slug is string => !! slug ) + ) + ).sort(); + const manualInstallPluginSlugs = [ 'ai', ...aiProviderPluginSlugs ]; const isEmpty = renderableConnectors.length === 0; return ( @@ -59,6 +80,35 @@ function ConnectorsPage() { isEmpty ? ' connectors-page--empty' : '' }` } > + { isFileModsDisabled && ( + +

+ { __( + 'Plugin installation from wp-admin is disabled because DISALLOW_FILE_MODS is enabled. Install the AI plugin and any AI provider plugins manually using your normal deployment workflow.' + ) } +

+

{ __( 'WP-CLI examples:' ) }

+ +
+ ) } { isEmpty ? ( ) } - { canInstallPlugins && ( + { canInstallPlugins && ! isFileModsDisabled && (

{ createInterpolateElement( __( diff --git a/routes/connectors-home/style.scss b/routes/connectors-home/style.scss index a0a6d87533c6ba..f3b5d9bc9f4b40 100644 --- a/routes/connectors-home/style.scss +++ b/routes/connectors-home/style.scss @@ -29,6 +29,23 @@ $sticky-header-clearance: 120px; scroll-margin-top: $sticky-header-clearance; } + &__file-mods-notice { + margin: 16px 0; + + p { + margin: 0; + } + + ul { + margin: 8px 0 0; + padding-inline-start: 20px; + } + + code { + font-size: 12px; + } + } + &--empty { flex-grow: 1; display: flex; From 659170990bd6b6e02e30170bfc3cb9e95b5713f8 Mon Sep 17 00:00:00 2001 From: Gautam Mehta Date: Wed, 22 Apr 2026 11:52:42 +0530 Subject: [PATCH 02/23] feat: enhance plugin callout button visibility based on installation status --- routes/connectors-home/ai-plugin-callout.tsx | 54 ++++++++++---------- 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/routes/connectors-home/ai-plugin-callout.tsx b/routes/connectors-home/ai-plugin-callout.tsx index 2431dfeff2ae86..aa63a8e02e6a37 100644 --- a/routes/connectors-home/ai-plugin-callout.tsx +++ b/routes/connectors-home/ai-plugin-callout.tsx @@ -197,11 +197,6 @@ export function AiPluginCallout() { return null; } - // Not installed and no permissions to install. - if ( pluginStatus === 'not-installed' && canInstallPlugins === false ) { - return null; - } - // Installed but can't activate (no manage permissions). if ( pluginStatus === 'inactive' && canManagePlugins === false ) { return null; @@ -215,6 +210,8 @@ export function AiPluginCallout() { ( ! initialHasConnectedProvider || justActivated ); const showInstallActivate = pluginStatus === 'not-installed' || pluginStatus === 'inactive'; + const hideButtons = + pluginStatus === 'not-installed' && canInstallPlugins === false; const getMessage = () => { if ( isJustConnected ) { @@ -262,29 +259,30 @@ export function AiPluginCallout() { a: , } ) }

- { showInstallActivate ? ( - - ) : ( - - ) } + { ! hideButtons && + ( showInstallActivate ? ( + + ) : ( + + ) ) } From e6ee93b458fef6f13e2db63bd140956bcb8773c9 Mon Sep 17 00:00:00 2001 From: Gautam Mehta Date: Thu, 23 Apr 2026 12:34:22 +0530 Subject: [PATCH 03/23] feat: update connector search link based on file mod status and always show --- routes/connectors-home/stage.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/routes/connectors-home/stage.tsx b/routes/connectors-home/stage.tsx index 43837f00f8bb05..8ae0465232f3ac 100644 --- a/routes/connectors-home/stage.tsx +++ b/routes/connectors-home/stage.tsx @@ -67,6 +67,10 @@ function ConnectorsPage() { ).sort(); const manualInstallPluginSlugs = [ 'ai', ...aiProviderPluginSlugs ]; const isEmpty = renderableConnectors.length === 0; + const searchUrl = + canInstallPlugins && ! isFileModsDisabled + ? 'plugin-install.php?s=connector&tab=search&type=tag' + : 'https://wordpress.org/plugins/search/ai-connectors/'; return ( ) } - { canInstallPlugins && ! isFileModsDisabled && (

{ createInterpolateElement( __( @@ -168,12 +171,11 @@ function ConnectorsPage() { { a: ( // eslint-disable-next-line jsx-a11y/anchor-has-content - + ), } ) }

- ) }
); From 74813ab8386a017ae0c61387fe2be36f36fea271 Mon Sep 17 00:00:00 2001 From: Gautam Mehta Date: Thu, 23 Apr 2026 12:37:21 +0530 Subject: [PATCH 04/23] feat: update notice status to "notice" from "warning" --- routes/connectors-home/stage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routes/connectors-home/stage.tsx b/routes/connectors-home/stage.tsx index 8ae0465232f3ac..ab16411bea3bcf 100644 --- a/routes/connectors-home/stage.tsx +++ b/routes/connectors-home/stage.tsx @@ -86,7 +86,7 @@ function ConnectorsPage() { > { isFileModsDisabled && ( From 2dcd6a6792dd4175301f3f13b87497d53a04ef0b Mon Sep 17 00:00:00 2001 From: Gautam Mehta Date: Fri, 24 Apr 2026 16:53:59 +0530 Subject: [PATCH 05/23] feat: update connector search link to use translation function for better localization --- routes/connectors-home/stage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routes/connectors-home/stage.tsx b/routes/connectors-home/stage.tsx index ab16411bea3bcf..ef91c657f569cc 100644 --- a/routes/connectors-home/stage.tsx +++ b/routes/connectors-home/stage.tsx @@ -70,7 +70,7 @@ function ConnectorsPage() { const searchUrl = canInstallPlugins && ! isFileModsDisabled ? 'plugin-install.php?s=connector&tab=search&type=tag' - : 'https://wordpress.org/plugins/search/ai-connectors/'; + : __( 'https://wordpress.org/plugins/search/ai-connectors/' ); return ( Date: Fri, 24 Apr 2026 17:00:15 +0530 Subject: [PATCH 06/23] feat: simplify plugin installation notice for better clarity --- routes/connectors-home/stage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routes/connectors-home/stage.tsx b/routes/connectors-home/stage.tsx index ef91c657f569cc..92732781a7a2ae 100644 --- a/routes/connectors-home/stage.tsx +++ b/routes/connectors-home/stage.tsx @@ -92,7 +92,7 @@ function ConnectorsPage() { >

{ __( - 'Plugin installation from wp-admin is disabled because DISALLOW_FILE_MODS is enabled. Install the AI plugin and any AI provider plugins manually using your normal deployment workflow.' + 'Plugins cannot be installed here due to your site configuration. Install them manually using your normal deployment workflow.' ) }

{ __( 'WP-CLI examples:' ) }

From bd8a86743a1456a8f68398131d5824dac8b154b7 Mon Sep 17 00:00:00 2001 From: Gautam Mehta Date: Mon, 27 Apr 2026 14:35:04 +0530 Subject: [PATCH 07/23] feat: enhance connectors page to include AI plugin activation status and filter manual install plugins accordingly --- routes/connectors-home/stage.tsx | 38 +++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/routes/connectors-home/stage.tsx b/routes/connectors-home/stage.tsx index 92732781a7a2ae..cf1396897485e3 100644 --- a/routes/connectors-home/stage.tsx +++ b/routes/connectors-home/stage.tsx @@ -37,14 +37,23 @@ registerDefaultConnectors(); function ConnectorsPage() { const isFileModsDisabled = getIsFileModsDisabled(); - const { connectors, canInstallPlugins } = useSelect( - ( select ) => ( { + const { connectors, canInstallPlugins, isAiPluginActive } = useSelect( + ( select ) => { + const coreSelect = select( coreStore ); + const aiPlugin = coreSelect.getEntityRecord( + 'root', + 'plugin', + 'ai/ai' + ) as { status: string } | undefined; + return { connectors: unlock( select( store ) ).getConnectors(), - canInstallPlugins: select( coreStore ).canUser( 'create', { + canInstallPlugins: coreSelect.canUser( 'create', { kind: 'root', name: 'plugin', } ), - } ), + isAiPluginActive: aiPlugin?.status === 'active', + }; + }, [] ); @@ -65,7 +74,24 @@ function ConnectorsPage() { .filter( ( slug ): slug is string => !! slug ) ) ).sort(); - const manualInstallPluginSlugs = [ 'ai', ...aiProviderPluginSlugs ]; + const activatedPluginSlugs = new Set( + connectors + .filter( + ( connector: ConnectorConfig ) => connector.plugin?.isActivated + ) + .map( + ( connector: ConnectorConfig ) => + connector.plugin?.file?.split( '/' )[ 0 ] + ) + .filter( ( slug: string | undefined ): slug is string => !! slug ) + ); + // AI provider connectors are only registered when the 'ai' plugin is active. + if ( isAiPluginActive ) { + activatedPluginSlugs.add( 'ai' ); + } + const manualInstallPluginSlugs = [ 'ai', ...aiProviderPluginSlugs ].filter( + ( slug ) => ! activatedPluginSlugs.has( slug ) + ); const isEmpty = renderableConnectors.length === 0; const searchUrl = canInstallPlugins && ! isFileModsDisabled @@ -84,7 +110,7 @@ function ConnectorsPage() { isEmpty ? ' connectors-page--empty' : '' }` } > - { isFileModsDisabled && ( + { isFileModsDisabled && manualInstallPluginSlugs.length > 0 && ( Date: Tue, 28 Apr 2026 11:30:59 +0530 Subject: [PATCH 08/23] feat: enhance WP-CLI examples section with collapsible details for improved user experience --- routes/connectors-home/stage.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/routes/connectors-home/stage.tsx b/routes/connectors-home/stage.tsx index cf1396897485e3..ce83f062933442 100644 --- a/routes/connectors-home/stage.tsx +++ b/routes/connectors-home/stage.tsx @@ -121,7 +121,8 @@ function ConnectorsPage() { 'Plugins cannot be installed here due to your site configuration. Install them manually using your normal deployment workflow.' ) }

-

{ __( 'WP-CLI examples:' ) }

+
+ { __( 'WP-CLI examples' ) }
    { manualInstallPluginSlugs.map( ( slug ) => { const command = `wp plugin install ${ slug } --activate`; @@ -137,6 +138,7 @@ function ConnectorsPage() { ); } ) }
+
) } { isEmpty ? ( From b3b05edb338fd19da83cd8df98b10db49eaf1017 Mon Sep 17 00:00:00 2001 From: Gautam Mehta Date: Tue, 28 Apr 2026 18:24:51 +0530 Subject: [PATCH 09/23] feat: enhance manual installation notice to include permission check --- routes/connectors-home/stage.tsx | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/routes/connectors-home/stage.tsx b/routes/connectors-home/stage.tsx index ce83f062933442..7a0d636903bd53 100644 --- a/routes/connectors-home/stage.tsx +++ b/routes/connectors-home/stage.tsx @@ -110,21 +110,27 @@ function ConnectorsPage() { isEmpty ? ' connectors-page--empty' : '' }` } > - { isFileModsDisabled && manualInstallPluginSlugs.length > 0 && ( + { manualInstallPluginSlugs.length > 0 && + ( isFileModsDisabled || ! canInstallPlugins ) && ( + { isFileModsDisabled ? ( + <>

{ __( 'Plugins cannot be installed here due to your site configuration. Install them manually using your normal deployment workflow.' ) }

- { __( 'WP-CLI examples' ) } + + { __( 'WP-CLI examples' ) } +
    - { manualInstallPluginSlugs.map( ( slug ) => { + { manualInstallPluginSlugs.map( + ( slug ) => { const command = `wp plugin install ${ slug } --activate`; return (
  • @@ -133,12 +139,23 @@ function ConnectorsPage() { __( '%s:' ), slug ) }{ ' ' } - { command } + + { command } +
  • ); - } ) } + } + ) }
+ + ) : ( +

+ { __( + 'You do not have permission to install plugins. Please ask a site administrator to install them for you.' + ) } +

+ ) }
) } { isEmpty ? ( From 3fbd434fa482f8526b264a71073829d4a4b4dcb1 Mon Sep 17 00:00:00 2001 From: Gautam Mehta Date: Thu, 7 May 2026 18:52:50 +0530 Subject: [PATCH 10/23] feat: refactor notice component to use newer component --- routes/connectors-home/stage.tsx | 126 ++++++++++++++++--------------- 1 file changed, 64 insertions(+), 62 deletions(-) diff --git a/routes/connectors-home/stage.tsx b/routes/connectors-home/stage.tsx index 7a0d636903bd53..f69f6cea06ea16 100644 --- a/routes/connectors-home/stage.tsx +++ b/routes/connectors-home/stage.tsx @@ -4,7 +4,6 @@ import { Page } from '@wordpress/admin-ui'; import { Button, - Notice, __experimentalHeading as Heading, __experimentalText as WCText, __experimentalVStack as VStack, @@ -17,6 +16,8 @@ import { useSelect } from '@wordpress/data'; import { createInterpolateElement } from '@wordpress/element'; import { __, sprintf } from '@wordpress/i18n'; import { store as coreStore } from '@wordpress/core-data'; +// eslint-disable-next-line @wordpress/use-recommended-components +import { Notice } from '@wordpress/ui'; /** * Internal dependencies @@ -46,11 +47,11 @@ function ConnectorsPage() { 'ai/ai' ) as { status: string } | undefined; return { - connectors: unlock( select( store ) ).getConnectors(), + connectors: unlock( select( store ) ).getConnectors(), canInstallPlugins: coreSelect.canUser( 'create', { - kind: 'root', - name: 'plugin', - } ), + kind: 'root', + name: 'plugin', + } ), isAiPluginActive: aiPlugin?.status === 'active', }; }, @@ -112,52 +113,53 @@ function ConnectorsPage() { > { manualInstallPluginSlugs.length > 0 && ( isFileModsDisabled || ! canInstallPlugins ) && ( - - { isFileModsDisabled ? ( - <> -

- { __( - 'Plugins cannot be installed here due to your site configuration. Install them manually using your normal deployment workflow.' - ) } -

-
- - { __( 'WP-CLI examples' ) } - -
    - { manualInstallPluginSlugs.map( - ( slug ) => { - const command = `wp plugin install ${ slug } --activate`; - return ( -
  • - { sprintf( - /* translators: %s: Plugin slug. */ - __( '%s:' ), - slug - ) }{ ' ' } - - { command } - -
  • - ); - } + + + { isFileModsDisabled ? ( + <> +

    + { __( + 'Plugins cannot be installed here due to your site configuration. Install them manually using your normal deployment workflow.' ) } -

-
- - ) : ( -

- { __( - 'You do not have permission to install plugins. Please ask a site administrator to install them for you.' - ) } -

- ) } -
- ) } +

+
+ + { __( 'WP-CLI examples' ) } + +
    + { manualInstallPluginSlugs.map( + ( slug ) => { + const command = `wp plugin install ${ slug } --activate`; + return ( +
  • + { sprintf( + /* translators: %s: Plugin slug. */ + __( '%s:' ), + slug + ) }{ ' ' } + + { command } + +
  • + ); + } + ) } +
+
+ + ) : ( +

+ { __( + 'You do not have permission to install plugins. Please ask a site administrator to install them for you.' + ) } +

+ ) } + + + ) } { isEmpty ? ( ) } -

- { createInterpolateElement( - __( - 'If the connector you need is not listed, search the plugin directory to see if a connector is available.' - ), - { - a: ( - // eslint-disable-next-line jsx-a11y/anchor-has-content +

+ { createInterpolateElement( + __( + 'If the connector you need is not listed, search the plugin directory to see if a connector is available.' + ), + { + a: ( + // eslint-disable-next-line jsx-a11y/anchor-has-content - ), - } - ) } -

+ ), + } + ) } +

); From 3b85c02b27a0fb4017b2eaf7413ff7234c4122fa Mon Sep 17 00:00:00 2001 From: Aki Hamano <54422211+t-hamano@users.noreply.github.com> Date: Fri, 8 May 2026 19:22:33 +0900 Subject: [PATCH 11/23] Avoid string concatenation --- routes/connectors-home/stage.tsx | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/routes/connectors-home/stage.tsx b/routes/connectors-home/stage.tsx index f69f6cea06ea16..62a0f67d02126b 100644 --- a/routes/connectors-home/stage.tsx +++ b/routes/connectors-home/stage.tsx @@ -135,14 +135,21 @@ function ConnectorsPage() { const command = `wp plugin install ${ slug } --activate`; return (
  • - { sprintf( - /* translators: %s: Plugin slug. */ - __( '%s:' ), - slug - ) }{ ' ' } - - { command } - + { createInterpolateElement( + sprintf( + /* translators: 1: Plugin slug. 2: WP-CLI command. */ + __( + '%1$s: %2$s' + ), + slug, + command + ), + { + code: ( + + ), + } + ) }
  • ); } From 9fe9e43ea40f0bf344d26b2e5ad5b02c26a1f945 Mon Sep 17 00:00:00 2001 From: Aki Hamano <54422211+t-hamano@users.noreply.github.com> Date: Fri, 8 May 2026 19:51:31 +0900 Subject: [PATCH 12/23] Remove unnecessary top margin --- routes/connectors-home/style.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routes/connectors-home/style.scss b/routes/connectors-home/style.scss index f3b5d9bc9f4b40..71027148b1f52c 100644 --- a/routes/connectors-home/style.scss +++ b/routes/connectors-home/style.scss @@ -30,7 +30,7 @@ $sticky-header-clearance: 120px; } &__file-mods-notice { - margin: 16px 0; + margin-bottom: 16px; p { margin: 0; From b955d23ad533cf9195f33a31185cbdc5a829853c Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Fri, 8 May 2026 15:17:14 +0100 Subject: [PATCH 13/23] Apply suggestions from code review Co-authored-by: Weston Ruter --- lib/experimental/connectors/default-connectors.php | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/lib/experimental/connectors/default-connectors.php b/lib/experimental/connectors/default-connectors.php index 4dc956bd9e4dc2..6d939f93314f0e 100644 --- a/lib/experimental/connectors/default-connectors.php +++ b/lib/experimental/connectors/default-connectors.php @@ -554,14 +554,8 @@ function _gutenberg_get_connector_script_module_data( array $data ): array { $connectors[ $connector_id ] = $connector_out; } ksort( $connectors ); - - $is_file_mods_disabled = defined( 'DISALLOW_FILE_MODS' ) && DISALLOW_FILE_MODS; - if ( function_exists( 'wp_is_file_mod_allowed' ) ) { - $is_file_mods_disabled = ! wp_is_file_mod_allowed( 'install_plugins' ); - } - $data['connectors'] = $connectors; - $data['isFileModsDisabled'] = $is_file_mods_disabled; + $data['isFileModsDisabled'] = ! wp_is_file_mod_allowed( 'install_plugins' ); return $data; } remove_filter( 'script_module_data_options-connectors-wp-admin', '_wp_connectors_get_connector_script_module_data' ); From 1f3091eff4b53a442b2f15fe03f66a9169c969d4 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Fri, 8 May 2026 16:49:26 +0200 Subject: [PATCH 14/23] feat: drop WP-CLI examples and link to plugin directory for unavailable plugins --- routes/connectors-home/default-connectors.tsx | 15 +++++- routes/connectors-home/stage.tsx | 53 +++---------------- routes/connectors-home/style.scss | 13 ----- 3 files changed, 20 insertions(+), 61 deletions(-) diff --git a/routes/connectors-home/default-connectors.tsx b/routes/connectors-home/default-connectors.tsx index 688a05c13a5348..c632410280ef42 100644 --- a/routes/connectors-home/default-connectors.tsx +++ b/routes/connectors-home/default-connectors.tsx @@ -13,7 +13,7 @@ import { } from '@wordpress/connectors'; import { select } from '@wordpress/data'; import { __ } from '@wordpress/i18n'; -import { Badge } from '@wordpress/ui'; +import { Badge, Link } from '@wordpress/ui'; /** * Internal dependencies @@ -108,6 +108,12 @@ const ConnectedBadge = () => ( ); +const PluginDirectoryLink = ( { slug }: { slug: string } ) => ( + + { __( 'Learn more' ) } + +); + const UnavailableActionBadge = () => { __( 'Not available' ) }; function ApiKeyConnector( { @@ -178,7 +184,12 @@ function ApiKeyConnector( { actionArea={ { isConnected && } - { showUnavailableBadge && } + { showUnavailableBadge && + ( pluginSlug ? ( + + ) : ( + + ) ) } { showActionButton && ( ) } - + } > { isExpanded && pluginStatus === 'active' && ( diff --git a/tools/eslint/suppressions.json b/tools/eslint/suppressions.json index bf65f223980395..c727b76a9f5447 100644 --- a/tools/eslint/suppressions.json +++ b/tools/eslint/suppressions.json @@ -1621,11 +1621,6 @@ "count": 1 } }, - "routes/connectors-home/default-connectors.tsx": { - "@wordpress/use-recommended-components": { - "count": 1 - } - }, "routes/connectors-home/stage.tsx": { "@wordpress/no-non-module-stylesheet-imports": { "count": 1 From 40e1a3e74ed146323108aaabb4de79e5be752a74 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Fri, 8 May 2026 17:28:03 +0200 Subject: [PATCH 18/23] test: update connectors capability tests for Learn more link --- test/e2e/specs/admin/connectors.spec.js | 17 ++++++++++++----- tools/eslint/suppressions.json | 5 +++++ 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/test/e2e/specs/admin/connectors.spec.js b/test/e2e/specs/admin/connectors.spec.js index 0e891d67a10cbe..b617237b6ab733 100644 --- a/test/e2e/specs/admin/connectors.spec.js +++ b/test/e2e/specs/admin/connectors.spec.js @@ -461,11 +461,13 @@ test.describe( 'Connectors', () => { slug: 'gutenberg-test-connectors-never-installed', name: 'Test Install Required Connector', action: 'Install', + pluginSlug: 'gutenberg-test-connectors-never-installed', }; const activateRequiredConnector = { slug: 'hello', name: 'Test Activate Required Connector', action: 'Activate', + pluginSlug: 'hello', }; const clearCapabilityRestriction = async ( requestUtils ) => { await requestUtils.rest( { @@ -519,7 +521,7 @@ test.describe( 'Connectors', () => { CONNECTORS_PAGE_QUERY ); - for ( const { slug, name, action } of [ + for ( const { slug, name, action, pluginSlug } of [ installRequiredConnector, activateRequiredConnector, ] ) { @@ -528,9 +530,14 @@ test.describe( 'Connectors', () => { await expect( card.getByRole( 'heading', { name, level: 2 } ) ).toBeVisible(); - await expect( - card.getByText( 'Not available' ) - ).toBeVisible(); + const learnMoreLink = card.getByRole( 'link', { + name: 'Learn more', + } ); + await expect( learnMoreLink ).toBeVisible(); + await expect( learnMoreLink ).toHaveAttribute( + 'href', + `https://wordpress.org/plugins/${ pluginSlug }/` + ); await expect( card.getByRole( 'button', { name: action } ) ).toBeHidden(); @@ -540,7 +547,7 @@ test.describe( 'Connectors', () => { page.getByRole( 'link', { name: 'search the plugin directory', } ) - ).toBeHidden(); + ).toBeVisible(); } ); } ); } ); diff --git a/tools/eslint/suppressions.json b/tools/eslint/suppressions.json index c727b76a9f5447..bf65f223980395 100644 --- a/tools/eslint/suppressions.json +++ b/tools/eslint/suppressions.json @@ -1621,6 +1621,11 @@ "count": 1 } }, + "routes/connectors-home/default-connectors.tsx": { + "@wordpress/use-recommended-components": { + "count": 1 + } + }, "routes/connectors-home/stage.tsx": { "@wordpress/no-non-module-stylesheet-imports": { "count": 1 From 64041e0df4d31ac6165b26c97bdc1c5505ac456d Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Fri, 8 May 2026 17:34:35 +0200 Subject: [PATCH 19/23] Revert HStack to Stack change to minimize PR diff --- routes/connectors-home/default-connectors.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/routes/connectors-home/default-connectors.tsx b/routes/connectors-home/default-connectors.tsx index a6a746c0bf5b63..9ed2994824adc8 100644 --- a/routes/connectors-home/default-connectors.tsx +++ b/routes/connectors-home/default-connectors.tsx @@ -1,7 +1,7 @@ /** * WordPress dependencies */ -import { Button } from '@wordpress/components'; +import { __experimentalHStack as HStack, Button } from '@wordpress/components'; import { useRef } from '@wordpress/element'; import { __experimentalRegisterConnector as registerConnector, @@ -13,7 +13,7 @@ import { } from '@wordpress/connectors'; import { select } from '@wordpress/data'; import { __, sprintf } from '@wordpress/i18n'; -import { Badge, Link, Stack } from '@wordpress/ui'; +import { Badge, Link } from '@wordpress/ui'; /** * Internal dependencies @@ -189,7 +189,7 @@ function ApiKeyConnector( { name={ name } description={ description } actionArea={ - + { isConnected && } { showUnavailableBadge && ( pluginSlug ? ( @@ -214,7 +214,7 @@ function ApiKeyConnector( { { getButtonLabel() } ) } - + } > { isExpanded && pluginStatus === 'active' && ( From 7027f7b3d5921711ff4956c7d4030fe4f698dee8 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Fri, 8 May 2026 17:37:13 +0200 Subject: [PATCH 20/23] Remove wordpress.org search fallback; hide link when restricted --- routes/connectors-home/stage.tsx | 30 ++++++++++++------------- test/e2e/specs/admin/connectors.spec.js | 2 +- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/routes/connectors-home/stage.tsx b/routes/connectors-home/stage.tsx index 24b71407018ec3..9c4b5068a66d6b 100644 --- a/routes/connectors-home/stage.tsx +++ b/routes/connectors-home/stage.tsx @@ -93,10 +93,6 @@ function ConnectorsPage() { ( slug ) => ! installedPluginSlugs.has( slug ) ); const isEmpty = renderableConnectors.length === 0; - const searchUrl = - canInstallPlugins && ! isFileModsDisabled - ? 'plugin-install.php?s=connector&tab=search&type=tag' - : __( 'https://wordpress.org/plugins/search/ai-connectors/' ); return ( ) } -

    + { createInterpolateElement( + __( + 'If the connector you need is not listed, search the plugin directory to see if a connector is available.' ), - } - ) } -

    + { + a: ( + // eslint-disable-next-line jsx-a11y/anchor-has-content + + ), + } + ) } +

    + ) } ); diff --git a/test/e2e/specs/admin/connectors.spec.js b/test/e2e/specs/admin/connectors.spec.js index b617237b6ab733..d8368261bed5d7 100644 --- a/test/e2e/specs/admin/connectors.spec.js +++ b/test/e2e/specs/admin/connectors.spec.js @@ -547,7 +547,7 @@ test.describe( 'Connectors', () => { page.getByRole( 'link', { name: 'search the plugin directory', } ) - ).toBeVisible(); + ).toBeHidden(); } ); } ); } ); From 3e82fd2f68ed4b9a81c53996807818484610ff00 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Fri, 8 May 2026 17:44:05 +0200 Subject: [PATCH 21/23] Rename isFileModsDisabled to isFileModDisabled --- lib/experimental/connectors/default-connectors.php | 2 +- routes/connectors-home/default-connectors.tsx | 6 +++--- routes/connectors-home/stage.tsx | 10 +++++----- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/experimental/connectors/default-connectors.php b/lib/experimental/connectors/default-connectors.php index 6d939f93314f0e..3087f693a9115a 100644 --- a/lib/experimental/connectors/default-connectors.php +++ b/lib/experimental/connectors/default-connectors.php @@ -555,7 +555,7 @@ function _gutenberg_get_connector_script_module_data( array $data ): array { } ksort( $connectors ); $data['connectors'] = $connectors; - $data['isFileModsDisabled'] = ! wp_is_file_mod_allowed( 'install_plugins' ); + $data['isFileModDisabled'] = ! wp_is_file_mod_allowed( 'install_plugins' ); return $data; } remove_filter( 'script_module_data_options-connectors-wp-admin', '_wp_connectors_get_connector_script_module_data' ); diff --git a/routes/connectors-home/default-connectors.tsx b/routes/connectors-home/default-connectors.tsx index 9ed2994824adc8..2f2dec2db0e9e7 100644 --- a/routes/connectors-home/default-connectors.tsx +++ b/routes/connectors-home/default-connectors.tsx @@ -45,7 +45,7 @@ interface ConnectorData { interface ConnectorScriptModuleData { connectors?: Record< string, ConnectorData >; - isFileModsDisabled?: boolean; + isFileModDisabled?: boolean; } function getConnectorScriptModuleData(): ConnectorScriptModuleData { @@ -67,8 +67,8 @@ export function getConnectorData(): Record< string, ConnectorData > { return getConnectorScriptModuleData().connectors ?? {}; } -export function getIsFileModsDisabled(): boolean { - return !! getConnectorScriptModuleData().isFileModsDisabled; +export function getIsFileModDisabled(): boolean { + return !! getConnectorScriptModuleData().isFileModDisabled; } const CONNECTOR_LOGOS: Record< string, React.ComponentType > = { diff --git a/routes/connectors-home/stage.tsx b/routes/connectors-home/stage.tsx index 9c4b5068a66d6b..cfe6479ac3c3c5 100644 --- a/routes/connectors-home/stage.tsx +++ b/routes/connectors-home/stage.tsx @@ -25,7 +25,7 @@ import { Notice } from '@wordpress/ui'; import './style.scss'; import { AiPluginCallout } from './ai-plugin-callout'; import { - getIsFileModsDisabled, + getIsFileModDisabled, registerDefaultConnectors, } from './default-connectors'; import { unlock } from '../lock-unlock'; @@ -36,7 +36,7 @@ const { store } = unlock( connectorsPrivateApis ); registerDefaultConnectors(); function ConnectorsPage() { - const isFileModsDisabled = getIsFileModsDisabled(); + const isFileModDisabled = getIsFileModDisabled(); const { connectors, canInstallPlugins, isAiPluginInstalled } = useSelect( ( select ) => { @@ -107,13 +107,13 @@ function ConnectorsPage() { }` } > { manualInstallPluginSlugs.length > 0 && - ( isFileModsDisabled || ! canInstallPlugins ) && ( + ( isFileModDisabled || ! canInstallPlugins ) && ( - { isFileModsDisabled + { isFileModDisabled ? __( 'Plugins cannot be installed here due to your site configuration. Install them manually using your normal deployment workflow.' ) @@ -173,7 +173,7 @@ function ConnectorsPage() { ) } - { canInstallPlugins && ! isFileModsDisabled && ( + { canInstallPlugins && ! isFileModDisabled && (

    { createInterpolateElement( __( From 23f2ee5ae099cac3ce05ba19305974bd3f06d7e0 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Fri, 8 May 2026 08:47:05 -0700 Subject: [PATCH 22/23] Fix variable assignment alignment PHPCS issue --- lib/experimental/connectors/default-connectors.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/experimental/connectors/default-connectors.php b/lib/experimental/connectors/default-connectors.php index 3087f693a9115a..d12ed5391e7a72 100644 --- a/lib/experimental/connectors/default-connectors.php +++ b/lib/experimental/connectors/default-connectors.php @@ -554,7 +554,7 @@ function _gutenberg_get_connector_script_module_data( array $data ): array { $connectors[ $connector_id ] = $connector_out; } ksort( $connectors ); - $data['connectors'] = $connectors; + $data['connectors'] = $connectors; $data['isFileModDisabled'] = ! wp_is_file_mod_allowed( 'install_plugins' ); return $data; } From df811bee0f14bbaad8c74ce9b6caa9ee4d866e15 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Fri, 8 May 2026 17:50:14 +0200 Subject: [PATCH 23/23] Add backport changelog for core PR 11779 --- backport-changelog/7.0/11779.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 backport-changelog/7.0/11779.md diff --git a/backport-changelog/7.0/11779.md b/backport-changelog/7.0/11779.md new file mode 100644 index 00000000000000..08557620ebadf9 --- /dev/null +++ b/backport-changelog/7.0/11779.md @@ -0,0 +1,3 @@ +https://github.com/WordPress/wordpress-develop/pull/11779 + +* https://github.com/WordPress/gutenberg/pull/77521

    - { createInterpolateElement( - __( - 'If the connector you need is not listed, search the plugin directory to see if a connector is available.' - ), - { - a: ( - // eslint-disable-next-line jsx-a11y/anchor-has-content - + { canInstallPlugins && ! isFileModsDisabled && ( +