From 1a7fd67256eece1bd14a24ba10ca9e9406d75ed7 Mon Sep 17 00:00:00 2001 From: David Stone Date: Mon, 10 Aug 2026 14:36:01 -0600 Subject: [PATCH 1/2] wip: optimize front-end My Sites toolbar --- inc/class-settings.php | 12 +++ inc/managers/class-site-manager.php | 72 ++++++++++++++++++ .../WP_Ultimo/Managers/Site_Manager_Test.php | 74 +++++++++++++++++++ tests/WP_Ultimo/Settings_Test.php | 15 ++++ 4 files changed, 173 insertions(+) diff --git a/inc/class-settings.php b/inc/class-settings.php index 07caa53b..ed71f297 100644 --- a/inc/class-settings.php +++ b/inc/class-settings.php @@ -1409,6 +1409,17 @@ public function default_sections(): void { ] ); + $this->add_field( + 'sites', + 'optimize_frontend_my_sites_toolbar', + [ + 'title' => __('Optimize the front-end My Sites toolbar', 'ultimate-multisite'), + 'desc' => __('Show only the current site in the front-end toolbar to reduce processing on large networks. The complete site list remains available from the My Sites administration page.', 'ultimate-multisite'), + 'type' => 'toggle', + 'default' => 0, + ] + ); + $this->add_field( 'sites', 'wordpress_features_heading', @@ -2177,6 +2188,7 @@ public static function get_setting_defaults(): array { // Sites 'enable_visits_limiting' => 1, 'enable_screenshot_generator' => 1, + 'optimize_frontend_my_sites_toolbar' => 0, 'menu_items_plugin' => 1, 'add_new_users' => 1, 'allow_template_switching' => 1, diff --git a/inc/managers/class-site-manager.php b/inc/managers/class-site-manager.php index 78cb2a54..39d31e51 100644 --- a/inc/managers/class-site-manager.php +++ b/inc/managers/class-site-manager.php @@ -1163,6 +1163,10 @@ public function hide_customer_sites_from_super_admin_list($sites, $user_id, $all return $sites; } + if ($this->is_frontend_my_sites_toolbar_request()) { + return $this->get_current_site_for_frontend_my_sites_toolbar(); + } + $keys = get_user_meta($user_id); if (empty($keys)) { @@ -1271,6 +1275,74 @@ public function hide_customer_sites_from_super_admin_list($sites, $user_id, $all return apply_filters('get_blogs_of_user', $sites, $user_id, $all); // phpcs:ignore } + /** + * Determines whether get_blogs_of_user is preparing the front-end My Sites toolbar. + * + * The pre_get_blogs_of_user filter is also used by wp-admin, REST requests, and + * application code. Checking the WP_Admin_Bar initialization call keeps the + * optimization limited to the nested toolbar request instead of limiting every + * front-end get_blogs_of_user() call. + * + * @since 2.15.0 + * @param array|null $backtrace Call stack override used by tests. + * @return bool + */ + protected function is_frontend_my_sites_toolbar_request($backtrace = null) { + + if ( + ! wu_get_setting('optimize_frontend_my_sites_toolbar', false) + || is_admin() + || wp_doing_ajax() + || wp_doing_cron() + || (defined('REST_REQUEST') && REST_REQUEST) + || (defined('WP_CLI') && WP_CLI) + || ! is_user_logged_in() + || ! is_admin_bar_showing() + ) { + return false; + } + + $backtrace = $backtrace ?? debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 12); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_debug_backtrace + + foreach ($backtrace as $call) { + if ('WP_Admin_Bar' === ($call['class'] ?? '') && 'initialize' === ($call['function'] ?? '')) { + return true; + } + } + + return false; + } + + /** + * Returns the current site in the format expected by the My Sites toolbar. + * + * @since 2.15.0 + * @return object[] + */ + protected function get_current_site_for_frontend_my_sites_toolbar() { + + $site = get_site(get_current_blog_id()); + + if ( ! $site) { + return []; + } + + return [ + $site->id => (object) [ + 'userblog_id' => $site->id, + 'blogname' => $site->blogname, + 'domain' => $site->domain, + 'path' => $site->path, + 'site_id' => $site->network_id, + 'siteurl' => $site->siteurl, + 'archived' => $site->archived, + 'mature' => $site->mature, + 'spam' => $site->spam, + 'deleted' => $site->deleted, + ], + ]; + } + /** * Delete pending sites from non-pending memberships * diff --git a/tests/WP_Ultimo/Managers/Site_Manager_Test.php b/tests/WP_Ultimo/Managers/Site_Manager_Test.php index 155b9168..5445dead 100644 --- a/tests/WP_Ultimo/Managers/Site_Manager_Test.php +++ b/tests/WP_Ultimo/Managers/Site_Manager_Test.php @@ -2958,6 +2958,80 @@ public function test_hide_customer_sites_applies_get_blogs_of_user_filter(): voi wp_set_current_user(0); } + /** + * Test front-end My Sites toolbar optimization requires the admin bar call stack. + */ + public function test_frontend_my_sites_toolbar_optimization_requires_admin_bar_initialization(): void { + + $manager = $this->get_manager_instance(); + $user_id = $this->factory()->user->create(['role' => 'administrator']); + $method = new \ReflectionMethod(Site_Manager::class, 'is_frontend_my_sites_toolbar_request'); + $filter = function ($value, $setting) { + return 'optimize_frontend_my_sites_toolbar' === $setting ? 1 : $value; + }; + + wp_set_current_user($user_id); + add_filter('wu_get_setting', $filter, 10, 2); + add_filter('show_admin_bar', '__return_true'); + + try { + $this->assertTrue( + $method->invoke($manager, [ + [ + 'class' => 'WP_Admin_Bar', + 'function' => 'initialize', + ], + ]) + ); + $this->assertFalse($method->invoke($manager, [])); + } finally { + remove_filter('wu_get_setting', $filter, 10); + remove_filter('show_admin_bar', '__return_true'); + wp_set_current_user(0); + } + } + + /** + * Test front-end My Sites toolbar optimization remains disabled by default. + */ + public function test_frontend_my_sites_toolbar_optimization_is_disabled_by_default(): void { + + $manager = $this->get_manager_instance(); + $user_id = $this->factory()->user->create(['role' => 'administrator']); + $method = new \ReflectionMethod(Site_Manager::class, 'is_frontend_my_sites_toolbar_request'); + + wp_set_current_user($user_id); + add_filter('show_admin_bar', '__return_true'); + + try { + $this->assertFalse( + $method->invoke($manager, [ + [ + 'class' => 'WP_Admin_Bar', + 'function' => 'initialize', + ], + ]) + ); + } finally { + remove_filter('show_admin_bar', '__return_true'); + wp_set_current_user(0); + } + } + + /** + * Test the toolbar optimization supplies only the active site. + */ + public function test_frontend_my_sites_toolbar_optimization_returns_current_site_only(): void { + + $manager = $this->get_manager_instance(); + $method = new \ReflectionMethod(Site_Manager::class, 'get_current_site_for_frontend_my_sites_toolbar'); + $sites = $method->invoke($manager); + + $this->assertCount(1, $sites); + $this->assertArrayHasKey(get_current_blog_id(), $sites); + $this->assertSame(get_current_blog_id(), $sites[ get_current_blog_id() ]->userblog_id); + } + // ======================================================================== // init – demo-related hooks // ======================================================================== diff --git a/tests/WP_Ultimo/Settings_Test.php b/tests/WP_Ultimo/Settings_Test.php index aec02f0e..2430504a 100644 --- a/tests/WP_Ultimo/Settings_Test.php +++ b/tests/WP_Ultimo/Settings_Test.php @@ -396,6 +396,21 @@ public function test_get_setting_defaults_disables_passwordless_login() { $this->assertSame(0, $defaults['use_passwordless_login']); } + public function test_get_setting_defaults_disables_frontend_my_sites_toolbar_optimization() { + $defaults = Settings::get_setting_defaults(); + $this->assertArrayHasKey('optimize_frontend_my_sites_toolbar', $defaults); + $this->assertSame(0, $defaults['optimize_frontend_my_sites_toolbar']); + } + + public function test_sites_section_has_frontend_my_sites_toolbar_optimization_field() { + $section = $this->settings->get_section('sites'); + $field = $section['fields']['optimize_frontend_my_sites_toolbar']; + + $this->assertSame('toggle', $field['type']); + $this->assertSame(0, $field['default']); + $this->assertStringContainsString('My Sites administration page', $field['desc']); + } + // ------------------------------------------------------------------ // General section fields // ------------------------------------------------------------------ From 7f697cbb6069292f400a5fb4fd00dba0c3167c97 Mon Sep 17 00:00:00 2001 From: David Stone Date: Mon, 10 Aug 2026 14:46:47 -0600 Subject: [PATCH 2/2] fix: preserve My Sites toolbar filter --- inc/managers/class-site-manager.php | 4 +- .../WP_Ultimo/Managers/Site_Manager_Test.php | 43 +++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/inc/managers/class-site-manager.php b/inc/managers/class-site-manager.php index 39d31e51..149d6fa7 100644 --- a/inc/managers/class-site-manager.php +++ b/inc/managers/class-site-manager.php @@ -1164,7 +1164,9 @@ public function hide_customer_sites_from_super_admin_list($sites, $user_id, $all } if ($this->is_frontend_my_sites_toolbar_request()) { - return $this->get_current_site_for_frontend_my_sites_toolbar(); + $sites = $this->get_current_site_for_frontend_my_sites_toolbar(); + + return apply_filters('get_blogs_of_user', $sites, $user_id, $all); // phpcs:ignore } $keys = get_user_meta($user_id); diff --git a/tests/WP_Ultimo/Managers/Site_Manager_Test.php b/tests/WP_Ultimo/Managers/Site_Manager_Test.php index 5445dead..c0b70f48 100644 --- a/tests/WP_Ultimo/Managers/Site_Manager_Test.php +++ b/tests/WP_Ultimo/Managers/Site_Manager_Test.php @@ -9,6 +9,17 @@ use WP_Ultimo\Managers\Site_Manager; +// phpcs:disable Generic.Files.OneObjectStructurePerFile.MultipleFound +class Frontend_My_Sites_Toolbar_Test_Manager extends Site_Manager { + + protected function is_frontend_my_sites_toolbar_request($backtrace = null) { + + return true; + } +} +// phpcs:enable Generic.Files.OneObjectStructurePerFile.MultipleFound + +// phpcs:ignore Generic.Files.OneObjectStructurePerFile.MultipleFound class Site_Manager_Test extends \WP_UnitTestCase { use Manager_Test_Trait; @@ -2958,6 +2969,38 @@ public function test_hide_customer_sites_applies_get_blogs_of_user_filter(): voi wp_set_current_user(0); } + /** + * Test the front-end My Sites toolbar optimization applies get_blogs_of_user filter. + */ + public function test_frontend_my_sites_toolbar_optimization_applies_get_blogs_of_user_filter(): void { + + $reflection = new \ReflectionClass(Frontend_My_Sites_Toolbar_Test_Manager::class); + $manager = $reflection->newInstanceWithoutConstructor(); + $user_id = $this->factory()->user->create(['role' => 'administrator']); + $filter = function ($sites, $filter_user_id, $all) { + + unset($all); + $sites['filtered'] = (object) ['userblog_id' => $filter_user_id]; + + return $sites; + }; + + grant_super_admin($user_id); + wp_set_current_user($user_id); + add_filter('get_blogs_of_user', $filter, 10, 3); + + try { + $sites = $manager->hide_customer_sites_from_super_admin_list([], $user_id, false); + + $this->assertArrayHasKey('filtered', $sites); + $this->assertSame($user_id, $sites['filtered']->userblog_id); + } finally { + remove_filter('get_blogs_of_user', $filter, 10); + revoke_super_admin($user_id); + wp_set_current_user(0); + } + } + /** * Test front-end My Sites toolbar optimization requires the admin bar call stack. */