From 58684262a9ea7b52db827539eda4520933d38d4e Mon Sep 17 00:00:00 2001 From: ArkaPrabhaChowdhury Date: Thu, 16 Jul 2026 12:26:57 +0530 Subject: [PATCH] Upgrade/Install: Select the main plugin file after installation --- .../includes/class-plugin-upgrader.php | 21 +++- tests/phpunit/tests/admin/pluginUpgrader.php | 105 ++++++++++++++++++ 2 files changed, 122 insertions(+), 4 deletions(-) create mode 100644 tests/phpunit/tests/admin/pluginUpgrader.php diff --git a/src/wp-admin/includes/class-plugin-upgrader.php b/src/wp-admin/includes/class-plugin-upgrader.php index f5290ff5258f6..fae6d1df8623c 100644 --- a/src/wp-admin/includes/class-plugin-upgrader.php +++ b/src/wp-admin/includes/class-plugin-upgrader.php @@ -541,12 +541,25 @@ public function plugin_info() { return false; } - // Assume the requested plugin is the first in the list. - $plugin_files = array_keys( $plugin ); + $plugin_files = array_keys( $plugin ); + $plugin_file = $plugin_files[0]; + $expected_plugin_file = $this->result['destination_name'] . '.php'; - return $this->result['destination_name'] . '/' . $plugin_files[0]; - } + // Prefer a root-level plugin file that matches the plugin directory name. + if ( in_array( $expected_plugin_file, $plugin_files, true ) ) { + $plugin_file = $expected_plugin_file; + } else { + // Otherwise, use the first root-level plugin file. + foreach ( $plugin_files as $candidate_plugin_file ) { + if ( ! str_contains( $candidate_plugin_file, '/' ) ) { + $plugin_file = $candidate_plugin_file; + break; + } + } + } + return $this->result['destination_name'] . '/' . $plugin_file; + } /** * Deactivates a plugin before it is upgraded. * diff --git a/tests/phpunit/tests/admin/pluginUpgrader.php b/tests/phpunit/tests/admin/pluginUpgrader.php new file mode 100644 index 0000000000000..f9bee2ab31cbf --- /dev/null +++ b/tests/phpunit/tests/admin/pluginUpgrader.php @@ -0,0 +1,105 @@ + array( + 'main.php' => 'Z Main Plugin', + 'embedded/embedded.php' => 'A Embedded Plugin', + ), + 'plugin-upgrader-slug-test' => array( + 'another.php' => 'A Another Plugin', + 'plugin-upgrader-slug-test.php' => 'Z Slug Plugin', + ), + ); + + /** + * Creates the test plugins before any tests run. + */ + public static function set_up_before_class() { + parent::set_up_before_class(); + + require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; + require_once ABSPATH . 'wp-admin/includes/class-plugin-upgrader.php'; + + foreach ( self::$test_plugins as $directory => $files ) { + foreach ( $files as $file => $name ) { + $path = WP_PLUGIN_DIR . "/$directory/$file"; + + wp_mkdir_p( dirname( $path ) ); + file_put_contents( $path, " $files ) { + foreach ( $files as $file => $name ) { + unlink( WP_PLUGIN_DIR . "/$directory/$file" ); + } + + $subdirectories = glob( WP_PLUGIN_DIR . "/$directory/*", GLOB_ONLYDIR ); + foreach ( $subdirectories as $subdirectory ) { + rmdir( $subdirectory ); + } + + rmdir( WP_PLUGIN_DIR . "/$directory" ); + } + + wp_clean_plugins_cache( false ); + + parent::tear_down_after_class(); + } + + /** + * Tests that the main plugin file is selected for the activation link. + * + * @ticket 22287 + * + * @covers Plugin_Upgrader::plugin_info + * + * @dataProvider data_plugin_info + * + * @param string $destination_name Plugin destination directory name. + * @param string $expected Expected main plugin file. + */ + public function test_plugin_info_should_select_main_plugin( $destination_name, $expected ) { + wp_clean_plugins_cache( false ); + + $upgrader = new Plugin_Upgrader(); + $upgrader->result = array( 'destination_name' => $destination_name ); + + $this->assertSame( $expected, $upgrader->plugin_info() ); + } + + /** + * Data provider. + * + * @return array[] + */ + public function data_plugin_info() { + return array( + 'nested plugin' => array( + 'destination_name' => 'plugin-upgrader-nested-test', + 'expected' => 'plugin-upgrader-nested-test/main.php', + ), + 'matching slug file' => array( + 'destination_name' => 'plugin-upgrader-slug-test', + 'expected' => 'plugin-upgrader-slug-test/plugin-upgrader-slug-test.php', + ), + ); + } +}