From 5253dc282acce9baf8bf46e6ab6a5aa13366e21c Mon Sep 17 00:00:00 2001 From: Benito Alba Molina <272526523+benitoalba@users.noreply.github.com> Date: Fri, 31 Jul 2026 03:22:31 +0200 Subject: [PATCH] Plugin Dependencies: Recognize network-active dependencies See https://core.trac.wordpress.org/ticket/65759. --- src/wp-admin/includes/plugin-install.php | 9 +-- .../WpGetPluginActionButton_Test.php | 63 ++++++++++++++++++- 2 files changed, 63 insertions(+), 9 deletions(-) diff --git a/src/wp-admin/includes/plugin-install.php b/src/wp-admin/includes/plugin-install.php index dc232f1df7018..ba6c0e1c07a98 100644 --- a/src/wp-admin/includes/plugin-install.php +++ b/src/wp-admin/includes/plugin-install.php @@ -914,7 +914,6 @@ function wp_get_plugin_action_button( $name, $data, $compatible_php, $compatible // Determine the status of plugin dependencies. $installed_plugins = get_plugins(); - $active_plugins = get_option( 'active_plugins', array() ); $plugin_dependencies_count = count( $requires_plugins ); $installed_plugin_dependencies_count = 0; $active_plugin_dependencies_count = 0; @@ -922,12 +921,10 @@ function wp_get_plugin_action_button( $name, $data, $compatible_php, $compatible foreach ( array_keys( $installed_plugins ) as $installed_plugin_file ) { if ( str_contains( $installed_plugin_file, '/' ) && explode( '/', $installed_plugin_file )[0] === $dependency ) { ++$installed_plugin_dependencies_count; - } - } - foreach ( $active_plugins as $active_plugin_file ) { - if ( str_contains( $active_plugin_file, '/' ) && explode( '/', $active_plugin_file )[0] === $dependency ) { - ++$active_plugin_dependencies_count; + if ( is_plugin_active( $installed_plugin_file ) ) { + ++$active_plugin_dependencies_count; + } } } } diff --git a/tests/phpunit/tests/admin/includes/plugin-install/WpGetPluginActionButton_Test.php b/tests/phpunit/tests/admin/includes/plugin-install/WpGetPluginActionButton_Test.php index ce358850dd2d0..7a7c02c6fc448 100644 --- a/tests/phpunit/tests/admin/includes/plugin-install/WpGetPluginActionButton_Test.php +++ b/tests/phpunit/tests/admin/includes/plugin-install/WpGetPluginActionButton_Test.php @@ -31,6 +31,13 @@ class Admin_Includes_Plugin_Install_WpGetPluginActionButton_Test extends WP_Unit */ private static $test_plugin; + /** + * Test dependency plugin data. + * + * @var stdClass + */ + private static $dependency_plugin; + /** * Sets up properties and adds a test plugin before any tests run. */ @@ -42,19 +49,33 @@ public static function set_up_before_class() { $role_name = 'wp_get_plugin_action_button-test-role'; add_role( $role_name, 'Test Role' ); - self::$role = get_role( $role_name ); - self::$user_id = self::factory()->user->create( array( 'role' => $role_name ) ); - self::$test_plugin = (object) array( + self::$role = get_role( $role_name ); + self::$user_id = self::factory()->user->create( array( 'role' => $role_name ) ); + self::$test_plugin = (object) array( 'name' => 'My Plugin', 'slug' => 'my-plugin', 'version' => '1.0.0', ); + self::$dependency_plugin = (object) array( + 'name' => 'My Dependency Plugin', + 'slug' => 'my-dependency-plugin', + 'version' => '1.0.0', + 'file' => 'my-dependency-plugin/my_dependency_plugin.php', + ); mkdir( WP_PLUGIN_DIR . '/' . self::$test_plugin->slug ); file_put_contents( WP_PLUGIN_DIR . '/' . self::$test_plugin->slug . '/my_plugin.php', "name . "\n* Version: " . self::$test_plugin->version . "\n*/" ); + + mkdir( WP_PLUGIN_DIR . '/' . self::$dependency_plugin->slug ); + file_put_contents( + WP_PLUGIN_DIR . '/' . self::$dependency_plugin->file, + "name . "\n* Version: " . self::$dependency_plugin->version . "\n*/" + ); + + wp_clean_plugins_cache( false ); } /** @@ -67,6 +88,11 @@ public static function tear_down_after_class() { unlink( WP_PLUGIN_DIR . '/' . self::$test_plugin->slug . '/my_plugin.php' ); rmdir( WP_PLUGIN_DIR . '/' . self::$test_plugin->slug ); + + unlink( WP_PLUGIN_DIR . '/' . self::$dependency_plugin->file ); + rmdir( WP_PLUGIN_DIR . '/' . self::$dependency_plugin->slug ); + + wp_clean_plugins_cache( false ); } /** @@ -152,4 +178,35 @@ public function test_should_not_return_empty_string_with_proper_capabilities_mul $this->assertIsString( $actual, 'A string should be returned.' ); $this->assertNotEmpty( $actual, 'An empty string should not be returned.' ); } + + /** + * Tests that the Activate button is enabled when a dependency is network-active. + * + * @ticket 65759 + * + * @group ms-required + */ + public function test_should_enable_activate_button_when_dependency_is_network_active() { + $test_plugin = clone self::$test_plugin; + $test_plugin->requires_plugins = array( self::$dependency_plugin->slug ); + + wp_set_current_user( self::$user_id ); + grant_super_admin( self::$user_id ); + + $activation_result = activate_plugin( self::$dependency_plugin->file, '', true ); + + $actual = wp_get_plugin_action_button( + $test_plugin->name, + $test_plugin, + true, + true + ); + + deactivate_plugins( self::$dependency_plugin->file, true, true ); + revoke_super_admin( self::$user_id ); + + $this->assertNull( $activation_result, 'The dependency plugin should be network-activated.' ); + $this->assertStringContainsString( 'activate-now', $actual, 'An enabled Activate button should be returned.' ); + $this->assertStringNotContainsString( 'button-disabled', $actual, 'The Activate button should not be disabled.' ); + } }