WordPress multisite is an awesome tool, but has its limitations and caveats, ESPECIALLY when you’re dealing with very large networks. At WebDevStudios, we use this snippet to prevent the ‘My Sites’ menu from querying every site on the network for which the user is a member. This isn’t so bad for a small network, or for users which are members of only a few blogs/sites, but if you have a Super Admin who is a member of most, if not all, the sites, you’ll definitely want this snippet.
<?php /** * Loading all sites menu for large multisite * is inefficient and bad news */ function large_network_remove_wp_admin_bar_my_sites_menu() { remove_action( 'admin_bar_menu', 'wp_admin_bar_my_sites_menu', 20 ); } add_action( 'add_admin_bar_menus', 'large_network_remove_wp_admin_bar_my_sites_menu' ); /** * Let's replace that menu with one that has * links to the network dashboard instead. */ function large_network_replacement_my_sites_menu( $wp_admin_bar ) { if ( ! current_user_can( 'manage_network' ) ) { // bail.. no network menu for you! return; } $wp_admin_bar->add_menu( array( 'id' => 'prefix-my-sites', 'title' => __( 'My Sites' ), 'href' => admin_url( 'my-sites.php' ), )); $wp_admin_bar->add_menu( array( 'id' => 'prefix-network-admin', 'parent' => 'prefix-my-sites', 'title' => __( 'Network Dashboard' ), 'href' => network_admin_url(), )); $wp_admin_bar->add_menu( array( 'id' => 'prefix-network-sites', 'parent' => 'prefix-my-sites', 'title' => __( 'Network Sites' ), 'href' => network_admin_url( 'sites.php' ), )); $wp_admin_bar->add_menu( array( 'id' => 'prefix-network-users', 'parent' => 'prefix-my-sites', 'title' => __( 'Network Users' ), 'href' => network_admin_url( 'users.php' ), )); } add_action( 'admin_bar_menu', 'large_network_replacement_my_sites_menu', 20 );
Thanks to Brad Parbs for the replacement menu snippet.
UPDATE: There are still a few performance concerns when a WordPress user is logged in and a member of many sites (like in the thousands). I created a new ticket on WordPress trac, and hopefully it gets some momentum, but until then, Sergey Biryukov provided me with a useful snippet which helps with the issue of super-admins being a ‘member’ of thousands of sites.
<?php /** * Remove all site capability keys when retrieving user meta * This prevents `get_blog_details` lookups for all the sites * a super admin is a member or admin * * @link https://core.trac.wordpress.org/ticket/31746#comment:1 get_blogs_of_user() can be very slow when a user is a member of thousands of sites */ function large_network_skip_get_blogs_of_user_with_many_sites( $null, $object_id, $meta_key ) { global $wpdb; // Don't proceed if fetching a specific meta key, or the current user is not a super admin if ( $meta_key || ! is_super_admin() ) { return $null; } // Ok, then fetch all the user meta (remove this filter to do so) remove_filter( 'get_user_metadata', __FUNCTION__, 10, 4 ); $keys = get_user_meta( $object_id ); add_filter( 'get_user_metadata', __FUNCTION__, 10, 4 ); // And loop through them foreach ( $keys as $key => $value ) { // Ignore non-capability meta keys if ( 'capabilities' !== substr( $key, -12 ) ) { continue; } // Ignore meta keys w/o the base_prefix if ( $wpdb->base_prefix && 0 !== strpos( $key, $wpdb->base_prefix ) ) { continue; } // Attempt to get the blog id from the string $blog_id = str_replace( array( $wpdb->base_prefix, '_capabilities' ), '', $key ); // If it's not numeric, ignore this too if ( ! is_numeric( $blog_id ) ) { continue; } // Ok, let's black-list this capability meta from being sent back unset( $keys[ $key ] ); } return $keys; } add_filter( 'get_user_metadata', 'large_network_skip_get_blogs_of_user_with_many_sites', 10, 3 );