From 3d831e92700592b274e2c4f2c804e82c6add729a Mon Sep 17 00:00:00 2001 From: Borislav Angelov Date: Thu, 4 Jun 2026 13:25:30 +0200 Subject: [PATCH] Add multisite (Network Admin) support for the admin page Register the Plugin Check screen on the network_admin_menu hook under "Settings" (settings.php), since the "Tools" menu does not exist in the Network Admin. Also surface the plugin action link in the Network Admin plugins list via the network_admin_plugin_action_links filter, pointing to the settings.php-based URL. Fixes #64 Co-Authored-By: Claude Opus 4.8 (1M context) --- includes/Admin/Admin_Page.php | 68 ++++++++++++++++++- .../phpunit/tests/Admin/Admin_Page_Tests.php | 59 ++++++++++++++++ 2 files changed, 125 insertions(+), 2 deletions(-) diff --git a/includes/Admin/Admin_Page.php b/includes/Admin/Admin_Page.php index 5dd4881e8..7517e0a9e 100644 --- a/includes/Admin/Admin_Page.php +++ b/includes/Admin/Admin_Page.php @@ -56,7 +56,9 @@ public function __construct( Admin_AJAX $admin_ajax ) { */ public function add_hooks() { add_action( 'admin_menu', array( $this, 'add_and_initialize_page' ) ); + add_action( 'network_admin_menu', array( $this, 'add_and_initialize_network_page' ) ); add_filter( 'plugin_action_links', array( $this, 'filter_plugin_action_links' ), 10, 4 ); + add_filter( 'network_admin_plugin_action_links', array( $this, 'filter_network_admin_plugin_action_links' ), 10, 4 ); add_action( 'admin_enqueue_scripts', array( $this, 'add_jump_to_line_code_editor' ) ); $this->admin_ajax->add_hooks(); @@ -87,6 +89,35 @@ public function add_and_initialize_page() { add_action( 'load-' . $this->get_hook_suffix(), array( $this, 'initialize_page' ) ); } + /** + * Adds the admin page under the network "Settings" menu. + * + * The "Tools" menu does not exist in the Network Admin, so the screen is + * added under "Settings" (settings.php) instead. + * + * @since 2.1.0 + */ + public function add_network_page() { + $this->hook_suffix = add_submenu_page( + 'settings.php', + __( 'Plugin Check', 'plugin-check' ), + __( 'Plugin Check', 'plugin-check' ), + 'manage_network_plugins', + 'plugin-check', + array( $this, 'render_page' ) + ); + } + + /** + * Adds and initializes the admin page under the network "Settings" menu. + * + * @since 2.1.0 + */ + public function add_and_initialize_network_page() { + $this->add_network_page(); + add_action( 'load-' . $this->get_hook_suffix(), array( $this, 'initialize_page' ) ); + } + /** * Initializes page hooks. * @@ -336,6 +367,39 @@ static function ( Check $check ) { * @return array The modified list of actions. */ public function filter_plugin_action_links( $actions, $plugin_file, $plugin_data, $context ) { + return $this->add_check_action_link( $actions, $plugin_file, $context, admin_url( 'tools.php?page=plugin-check' ) ); + } + + /** + * Adds "check this plugin" link in the Network Admin plugins list table. + * + * In the Network Admin the page lives under "Settings" (settings.php), so + * the link URL differs from the regular WP Admin. + * + * @since 2.1.0 + * + * @param array $actions List of actions. + * @param string $plugin_file Plugin main file. + * @param array $plugin_data An array of plugin data. + * @param string $context The plugin context. + * @return array The modified list of actions. + */ + public function filter_network_admin_plugin_action_links( $actions, $plugin_file, $plugin_data, $context ) { + return $this->add_check_action_link( $actions, $plugin_file, $context, network_admin_url( 'settings.php?page=plugin-check' ) ); + } + + /** + * Adds the "check this plugin" action link using the given page URL. + * + * @since 2.1.0 + * + * @param array $actions List of actions. + * @param string $plugin_file Plugin main file. + * @param string $context The plugin context. + * @param string $page_url The Plugin Check page URL to link to. + * @return array The modified list of actions. + */ + private function add_check_action_link( $actions, $plugin_file, $context, $page_url ) { if ( in_array( $context, array( 'mustuse', 'dropins' ), true ) ) { return $actions; @@ -345,7 +409,7 @@ public function filter_plugin_action_links( $actions, $plugin_file, $plugin_data if ( $plugin_check_base_name === $plugin_file ) { $actions[] = sprintf( '%2$s', - esc_url( admin_url( 'tools.php?page=plugin-check' ) ), + esc_url( $page_url ), esc_html__( 'Check a plugin', 'plugin-check' ) ); return $actions; @@ -354,7 +418,7 @@ public function filter_plugin_action_links( $actions, $plugin_file, $plugin_data if ( current_user_can( 'activate_plugins' ) ) { $actions[] = sprintf( '%2$s', - esc_url( admin_url( "tools.php?page=plugin-check&plugin={$plugin_file}" ) ), + esc_url( $page_url . "&plugin={$plugin_file}" ), esc_html__( 'Check this plugin', 'plugin-check' ) ); } diff --git a/tests/phpunit/tests/Admin/Admin_Page_Tests.php b/tests/phpunit/tests/Admin/Admin_Page_Tests.php index 68feffcfd..496861a5b 100644 --- a/tests/phpunit/tests/Admin/Admin_Page_Tests.php +++ b/tests/phpunit/tests/Admin/Admin_Page_Tests.php @@ -25,7 +25,39 @@ public function set_up() { public function test_add_hooks() { $this->admin_page->add_hooks(); $this->assertEquals( 10, has_action( 'admin_menu', array( $this->admin_page, 'add_and_initialize_page' ) ) ); + $this->assertEquals( 10, has_action( 'network_admin_menu', array( $this->admin_page, 'add_and_initialize_network_page' ) ) ); $this->assertEquals( 10, has_filter( 'plugin_action_links', array( $this->admin_page, 'filter_plugin_action_links' ) ) ); + $this->assertEquals( 10, has_filter( 'network_admin_plugin_action_links', array( $this->admin_page, 'filter_network_admin_plugin_action_links' ) ) ); + } + + public function test_add_and_initialize_network_page() { + + if ( ! is_multisite() ) { + $this->markTestSkipped( 'The Network Admin is only available on multisite.' ); + } + + global $_parent_pages; + + $current_screen = get_current_screen(); + + $admin_user = self::factory()->user->create( array( 'role' => 'administrator' ) ); + grant_super_admin( $admin_user ); + wp_set_current_user( $admin_user ); + + set_current_screen( 'dashboard-network' ); + + $this->admin_page->add_and_initialize_network_page(); + $page_hook = $this->admin_page->get_hook_suffix(); + $parent_pages = $_parent_pages; + + set_current_screen( $current_screen ); + + $this->assertNotEmpty( $page_hook ); + + // In the Network Admin the page lives under "Settings" (settings.php). + $this->assertArrayHasKey( 'plugin-check', $parent_pages ); + $this->assertEquals( 'settings.php', $parent_pages['plugin-check'] ); + $this->assertNotFalse( has_action( "load-{$page_hook}", array( $this->admin_page, 'initialize_page' ) ) ); } public function test_add_and_initialize_page() { @@ -156,6 +188,33 @@ public function test_filter_plugin_action_links() { ); } + public function test_filter_network_admin_plugin_action_links() { + + $base_file = 'akismet/akismet.php'; + + $action_links = $this->admin_page->filter_network_admin_plugin_action_links( array(), $base_file, array(), 'all' ); + $this->assertEmpty( $action_links ); + + /** Administrator check */ + $admin_user = self::factory()->user->create( array( 'role' => 'administrator' ) ); + + if ( is_multisite() ) { + grant_super_admin( $admin_user ); + } + wp_set_current_user( $admin_user ); + $action_links = $this->admin_page->filter_network_admin_plugin_action_links( array(), $base_file, array(), 'all' ); + + // In the Network Admin the link points to settings.php instead of tools.php. + $this->assertEquals( + sprintf( + '%2$s', + esc_url( network_admin_url( "settings.php?page=plugin-check&plugin={$base_file}" ) ), + esc_html__( 'Check this plugin', 'plugin-check' ) + ), + $action_links[0] + ); + } + public function test_filter_plugin_action_links_for_plugin_checker_check_link() { $base_file = 'plugin-check/plugin.php';