From 8d365439e40df155e19e43f5b1fae90b50fa3deb Mon Sep 17 00:00:00 2001 From: Vedansh Mishra Date: Sat, 3 Oct 2026 07:09:29 +0000 Subject: [PATCH] Connectors: Support filesystem credentials when installing plugins. When the filesystem method requires credentials that have not been stored, the REST API plugins endpoint cannot install plugins, so installing a connector plugin from Settings > Connectors failed with a generic error instead of showing the "Connection Information" modal. This loads the `updates` script on the Connectors screen, prints the filesystem credentials modal and admin notice templates, and exposes a `filesystemCredentialsRequired` flag to the Connectors script module. This allows the client to install plugins via `wp.updates.installPlugin()`, matching Plugins > Add Plugin. The client-side changes are in Gutenberg: https://github.com/WordPress/gutenberg/pull/78367 See #65223. --- src/wp-admin/options-connectors.php | 6 +++ src/wp-includes/connectors.php | 21 ++++++++ ...ConnectorsGetConnectorScriptModuleData.php | 49 +++++++++++++++++++ 3 files changed, 76 insertions(+) diff --git a/src/wp-admin/options-connectors.php b/src/wp-admin/options-connectors.php index ddad78f4f135f..236c35412129f 100644 --- a/src/wp-admin/options-connectors.php +++ b/src/wp-admin/options-connectors.php @@ -32,9 +32,15 @@ // Set parent file for menu highlighting. $parent_file = 'options-general.php'; +// Used to install connector plugins when filesystem credentials are required. +wp_enqueue_script( 'updates' ); + require_once ABSPATH . 'wp-admin/admin-header.php'; // Render the Connectors page. wp_options_connectors_wp_admin_render_page(); +wp_print_request_filesystem_credentials_modal(); +wp_print_admin_notice_templates(); + require_once ABSPATH . 'wp-admin/admin-footer.php'; diff --git a/src/wp-includes/connectors.php b/src/wp-includes/connectors.php index 9aed5f1f4d7aa..8c6b3a6664c5e 100644 --- a/src/wp-includes/connectors.php +++ b/src/wp-includes/connectors.php @@ -898,6 +898,7 @@ function _wp_connectors_pass_default_keys_to_ai_client(): void { * Exposes connector settings to the connectors-wp-admin script module. * * @since 7.0.0 + * @since 7.2.0 Added the `filesystemCredentialsRequired` flag. * @access private * * @param array $data Existing script module data. @@ -910,6 +911,10 @@ function _wp_connectors_get_connector_script_module_data( array $data ): array { require_once ABSPATH . 'wp-admin/includes/plugin.php'; } + if ( ! function_exists( 'request_filesystem_credentials' ) ) { + require_once ABSPATH . 'wp-admin/includes/file.php'; + } + $connectors = array(); foreach ( wp_get_connectors() as $connector_id => $connector_data ) { $auth = $connector_data['authentication']; @@ -964,6 +969,22 @@ function _wp_connectors_get_connector_script_module_data( array $data ): array { ksort( $connectors ); $data['connectors'] = $connectors; $data['isFileModDisabled'] = ! wp_is_file_mod_allowed( 'install_plugins' ); + + /* + * The REST API plugins endpoint cannot install plugins when the filesystem + * method requires credentials that have not been stored. In that case, the + * client installs through `wp.updates.installPlugin()` instead, which prompts + * for the credentials with the "Connection Information" modal. + */ + $data['filesystemCredentialsRequired'] = false; + if ( 'direct' !== get_filesystem_method() ) { + ob_start(); + $filesystem_credentials_are_stored = request_filesystem_credentials( self_admin_url() ); + ob_end_clean(); + + $data['filesystemCredentialsRequired'] = ! $filesystem_credentials_are_stored; + } + return $data; } add_filter( 'script_module_data_options-connectors-wp-admin', '_wp_connectors_get_connector_script_module_data' ); diff --git a/tests/phpunit/tests/connectors/wpConnectorsGetConnectorScriptModuleData.php b/tests/phpunit/tests/connectors/wpConnectorsGetConnectorScriptModuleData.php index b9b5014affdd1..7512bcf502227 100644 --- a/tests/phpunit/tests/connectors/wpConnectorsGetConnectorScriptModuleData.php +++ b/tests/phpunit/tests/connectors/wpConnectorsGetConnectorScriptModuleData.php @@ -209,4 +209,53 @@ public function test_environment_variable_password_may_contain_colons(): void { $this->assertSame( 'env', $auth['keySource'] ); $this->assertTrue( $auth['isConnected'] ); } + + /** + * @ticket 65223 + */ + public function test_filesystem_credentials_not_required_for_direct_filesystem_method(): void { + add_filter( + 'filesystem_method', + static function () { + return 'direct'; + } + ); + + $data = _wp_connectors_get_connector_script_module_data( array() ); + + $this->assertFalse( $data['filesystemCredentialsRequired'] ); + } + + /** + * @ticket 65223 + */ + public function test_filesystem_credentials_required_when_credentials_are_not_stored(): void { + add_filter( + 'filesystem_method', + static function () { + return 'ftpext'; + } + ); + + $data = _wp_connectors_get_connector_script_module_data( array() ); + + $this->assertTrue( $data['filesystemCredentialsRequired'] ); + } + + /** + * @ticket 65223 + */ + public function test_filesystem_credentials_not_required_when_credentials_are_stored(): void { + add_filter( + 'filesystem_method', + static function () { + return 'ftpext'; + } + ); + add_filter( 'request_filesystem_credentials', '__return_true' ); + + $data = _wp_connectors_get_connector_script_module_data( array() ); + + $this->assertFalse( $data['filesystemCredentialsRequired'] ); + } }