From 43463bf2880446c6006180302780ea2bf4c7b87d Mon Sep 17 00:00:00 2001 From: Aayushkalikote Date: Tue, 20 Oct 2026 14:45:07 +0545 Subject: [PATCH] Plugins: Avoid undefined `activateUrl` key for active plugins on multisite. On multisite, `WP_Plugin_Dependencies::check_plugin_dependencies_during_ajax()` appended `networkwide=1` to `$status['activateUrl']` for every user who can manage network plugins, even when the URL was never set because the plugin is already active. This raised an "Undefined array key" warning and returned a broken `activateUrl` of `?networkwide=1`. The network-wide argument is now only added when an activation URL exists, matching the single site behavior. Follow-up to r57545. Fixes #66346. --- .../class-wp-plugin-dependencies.php | 2 +- .../ajax/wpAjaxCheckPluginDependencies.php | 80 +++++++++++++++++++ 2 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 tests/phpunit/tests/ajax/wpAjaxCheckPluginDependencies.php diff --git a/src/wp-includes/class-wp-plugin-dependencies.php b/src/wp-includes/class-wp-plugin-dependencies.php index c96b98d08b550..fab7406622a11 100644 --- a/src/wp-includes/class-wp-plugin-dependencies.php +++ b/src/wp-includes/class-wp-plugin-dependencies.php @@ -477,7 +477,7 @@ public static function check_plugin_dependencies_during_ajax() { ); } - if ( is_multisite() && current_user_can( 'manage_network_plugins' ) ) { + if ( isset( $status['activateUrl'] ) && is_multisite() && current_user_can( 'manage_network_plugins' ) ) { $status['activateUrl'] = add_query_arg( array( 'networkwide' => 1 ), $status['activateUrl'] ); } diff --git a/tests/phpunit/tests/ajax/wpAjaxCheckPluginDependencies.php b/tests/phpunit/tests/ajax/wpAjaxCheckPluginDependencies.php new file mode 100644 index 0000000000000..2b3bcbcecefed --- /dev/null +++ b/tests/phpunit/tests/ajax/wpAjaxCheckPluginDependencies.php @@ -0,0 +1,80 @@ +user->create( array( 'role' => 'administrator' ) ); + grant_super_admin( self::$super_admin_id ); + } + + public function set_up() { + parent::set_up(); + add_action( 'wp_ajax_check_plugin_dependencies', array( 'WP_Plugin_Dependencies', 'check_plugin_dependencies_during_ajax' ) ); + wp_set_current_user( self::$super_admin_id ); + } + + /** + * Runs the Ajax handler for the Hello Dolly plugin and returns the decoded response. + * + * @return array The decoded response. + */ + protected function check_hello_dolly_dependencies(): array { + $_POST['_ajax_nonce'] = wp_create_nonce( 'updates' ); + $_POST['slug'] = 'hello-dolly'; + + try { + $this->_handleAjax( 'check_plugin_dependencies' ); + } catch ( WPAjaxDieContinueException $e ) { + unset( $e ); + } + + return json_decode( $this->_last_response, true ); + } + + /** + * @ticket 66346 + */ + public function test_network_active_plugin_should_not_have_activate_url() { + update_site_option( 'active_sitewide_plugins', array( 'hello.php' => time() ) ); + + $response = $this->check_hello_dolly_dependencies(); + + $this->assertTrue( $response['success'], 'The request should succeed.' ); + $this->assertArrayNotHasKey( 'activateUrl', $response['data'], 'An active plugin should not have an activation URL.' ); + } + + /** + * @ticket 66346 + */ + public function test_inactive_plugin_should_have_network_activate_url() { + update_site_option( 'active_sitewide_plugins', array() ); + + $response = $this->check_hello_dolly_dependencies(); + + $this->assertTrue( $response['success'], 'The request should succeed.' ); + $this->assertArrayHasKey( 'activateUrl', $response['data'], 'An inactive plugin should have an activation URL.' ); + $this->assertStringContainsString( 'networkwide=1', $response['data']['activateUrl'], 'The activation URL should activate the plugin network-wide.' ); + } +}