13
Multisite

Multisite is an extremely powerful hidden feature that comes included with WordPress. Disabled by default, it is not the kind of feature that everyone will want to use and requires some additional configuration to make work smoothly.

Other projects and platforms often refer to this type of thing as multitenancy. Multisite allows any WordPress installation to be responsible for easily managing an unlimited number of sites inside it, traditionally called a network of sites. This network can have open registration of users, sites, or both or neither. There are a ton of specialty Multisite plugins to help customize everything, from signups to domain aliasing and redirection.

Each site in your Multisite network can have separate plugins and themes activated. They each are responsible for their own unique content, and user roles and capabilities are still assigned on a per‐site basis.

As a plugin developer, you will need to understand what features are available when working with Multisite in WordPress so you know if and how to best support those features in the plugins you build.

Just because Multisite is built in to WordPress does not mean that it is identical to everything you are already familiar with. There are quite a few little things that make it just different enough to require planning for, and you don't want your plugins to not work correctly for someone who happens to be activating them in a Multisite environment.

TERMINOLOGY

The most important new terms in Multisite are global, installation, network, and site. A site is a single site inside a network of sites. A network is a group of sites. The installation is everything together. Global generally refers to users and user metadata but may also refer to Must‐Use plugins, database drop‐ins, or anything else that spans the entire environment.

When you first activate Multisite, you will have one network and one site in your installation. You will have unrestricted access to the entire installation, including the ability to create new sites. If you want to have multiple networks within your installation, you will need to install a plugin to enable this additional hidden functionality.

When developing plugins for Multisite, you need to determine whether you want them to work across the entire installation, across a single network, or only in a single site. For example, you may want to retrieve posts from one site in the network to be used inside others, or you may want to create a network‐wide option for your plugin.

All sites in your installation have a status. The status is important and can determine whether the site is viewable by the public. The following are the available site statuses in Multisite:

  • Public: The site is public if the privacy setting is set to enable search engines.
  • Archived: The site has been archived and is not available to the public.
  • Mature: The site is flagged as being for a mature audience.
  • Spam: The site is considered spam and is not available to the public.
  • Deleted: The site is flagged for deletion and is not available to the public.

The only two statuses that don't remove the site from public viewing are Public and Mature. Mature can be used if you want to allow mature sites in your network but need a way to warn users prior to them viewing the content. Public is based on the privacy settings and whether search engines are allowed to index the site.

Part of setting up Multisite means having to choose how domains for sites will be accessed: either as subdomains (site1.example.com) or as subdirectories (example .com/site1). You can even set a fully qualified domain name for each site (example1.com) so that visitors to your sites will have no idea they are all powered by a single install of WordPress. Future versions of WordPress and Multisite plan to remove this strict requirement, as servers can now be configured to accept generic requests allowing for WordPress to identify sites from any configuration type.

As you can imagine, this is an extremely powerful feature in WordPress. There is no limit to the number of sites WordPress can run; the only restriction is the resources available on your hosting environment. WordPress.com is the largest Multisite installation of WordPress, powering millions of sites together in one giant installation.

ADVANTAGES OF MULTISITE

Running Multisite for your websites offers many advantages. The most obvious one is you have only a single install of WordPress to manage. This makes life much easier when keeping everything up to date. If you have 50 sites and a plugin update is released, updating it affects all sites in your installation. If each site were a separate install of WordPress, you would have to update the plugin 50 times.

Another advantage to Multisite is the ease with which you can aggregate content across your network. For example, if you have 50 sites in your network, you could easily aggregate all those posts to your main blog to showcase your network of sites. If the sites were separate installs of WordPress, it would take quite a bit more work to aggregate that content.

Managing sites in Multisite is also versatile. You can easily limit disk space usage on each site. You can dictate what file types are allowed for uploading along with file size limits. You can even lock down plugins and themes from being activated by the other site administrators.

ENABLING MULTISITE IN WORDPRESS

Installing WordPress Multisite is straightforward. One of the great features of Multisite is that it can be enabled prior to installing WordPress, or anytime thereafter. If you decide to convert your WordPress site into Multisite a year down the road, you can easily do so.

The first step to enabling Multisite is to modify your wp‐config.php file. This file contains your database connection settings and other important configuration options. To enable Multisite, you need to add the following line before where it says /* That's all, stop editing! Happy blogging. */:

define( 'WP_ALLOW_MULTISITE', true );

Adding this line to your wp‐config.php file enables the Tools ➪ Network menu options, as shown in Figure 13‐1.

Screenshot displaying the Network menu options in the Tools page, with the Settings option highlighted.

FIGURE 13‐1: Tools ➪ Network menu options

Visiting this new menu option takes you to the Create a Network admin page. If you have not done so already, you will be required to disable all plugins prior to enabling Multisite. This must be done to ensure that nothing unexpected is happening while the installation process is running.

Here you can find detailed instructions on the necessary steps to complete the Multisite installation. In this tutorial, you will configure Multisite to work with subdirectories, so if you plan to use subdomains, be sure to follow the installation instructions closely as the code may differ slightly.

If you are unsure whether to use subdomains or subdirectories, we strongly recommend starting with subdirectories, as the lookups and pathing for finding sites are much more flexible. If this feature ever gets removed, subdirectories will be the default installation type.

Next you need to add the following code to your wp‐config.php file. Note that this is example code, and the DOMAIN_CURRENT_SITE constant would contain your website's domain in place of example.com.

$base = '/';
define( 'MULTISITE', true );
define( 'SUBDOMAIN_INSTALL', false );
define( 'DOMAIN_CURRENT_SITE', 'example.com' );
define( 'PATH_CURRENT_SITE', '/' );
define( 'SITE_ID_CURRENT_SITE', 1 );
define( 'BLOG_ID_CURRENT_SITE', 1 );

If you are using Apache for your web server, the final step is to modify your .htaccess file in the root directory of your WordPress installation. Replace the existing WordPress rules with the following code:

RewriteEngine On
RewriteBase /
RewriteRule ^index.php$ - [L]
 
# uploaded files
RewriteRule ^([_0-9a-zA-Z-]+/)?files/(.+) wp-includes/ms-files.php?file=$2 [L]
 
# add a trailing slash to /wp-admin
RewriteRule ^([_0-9a-zA-Z-]+/)?wp-admin$ $1wp-admin/ [R=301,L]
 
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
RewriteRule ^([_0-9a-zA-Z-]+/)?(wp-(content|admin|includes).*) $2 [L]
RewriteRule ^([_0-9a-zA-Z-]+/)?(.*.php)$ $2 [L]
RewriteRule . index.php [L]

If you are using Nginx, please review the following page:

wordpress.org/support/article/nginx

After making the required changes, you may be required to log back into WordPress. Multisite is now enabled and installed and ready to use!

MULTISITE FUNCTIONS

When Multisite is enabled, an entirely new set of features and functions become available for plugin developers to take advantage of. Understanding what functions are available can help you include Multisite‐specific functionality in the plugins you create. It can also help to understand how you can make your plugins Multisite‐compatible from the beginning.

The Site ID

Every site has a unique ID. This ID will be used in just about every Multisite‐specific function you use and is how WordPress determines what site you want to work with. The ID is also used in the prefix of the database tables for each respective site.

For example, if you enable Multisite and create a second site in your network, WordPress creates several database tables prefixed like wp_2_posts, where wp_ is the table prefix you defined when installing WordPress, and 2_ is the blog ID of the new site. As you create additional sites, WordPress creates additional database tables in the same manner.

The blog ID is stored in the global variable $blog_id, as shown here:

<?php
global $blog_id;
echo 'Current blog ID: ' . $blog_id;
?>

The $blog_id global variable does exist in standard WordPress but will always be 1. In Multisite mode, the blog ID will be the ID of the blog the current user is viewing.

Common Functions

When working with WordPress Multisite, you can take advantage of some common functions. The first function is called is_multisite() and determines whether Multisite support is enabled. Look at the following example:

<?php
if ( is_multisite() ) {
 echo 'Multisite is enabled';
}
?>

As you can see, this function does not accept any parameters. It simply checks whether Multisite is enabled in WordPress and returns True if so and False if not. Anytime you plan on using Multisite‐specific functions in WordPress, it's imperative that you use this function to verify that Multisite is running. If Multisite is not running, the default Multisite functions will not be available for use in WordPress, resulting in fatal errors in your plugin.

Another useful function for retrieving network site posts is get_blog_post(). This function retrieves a post from any site in the network and is also a good example of a function you can study to better understand how switching between sites works.

<?php get_blog_post( $blog_id, $post_id ); ?>

The function accepts two parameters: $site_id and $post_id.

<?php
//set blog and post ID
$multisite_blog_id = 2;
$multisite_post_id = 1;
 
//load the post data
$post_details = get_blog_post(
 $multisite_blog_id, $multisite_post_id );
 
//display the post title and content
var_dump( $post_details );
?>

This example assumes you have a site with an ID of 2 and you want to retrieve post ID 1. This is a quick‐and‐easy way to retrieve a post from any site in your network.

It can also be useful to retrieve specific information about a site you are working with. To retrieve site information, you can use the get_blog_details() function.

<?php get_blog_details( $fields, $get_all ); ?>

The function accepts two parameters.

  • $fields: A blog ID, a blog name, or an array of fields to query against
  • $get_all: Whether to retrieve all details

This function returns an object containing all public variables stored in the wp_blogs table. You can also retrieve a single, specific variable.

<?php
$blog_details = get_blog_details( 1 );
var_dump( $blog_details );
?>

Running the preceding code will produce the following object output:

WP_Site Object
(
    [blog_id] => 1
    [site_id] => 1
    [domain] => example.com
    [path] => /
    [registered] => 2010-10-31 19:14:59
    [last_updated] => 2010-11-11 14:19:34
    [public] => 1
    [archived] => 0
    [mature] => 0
    [spam] => 0
    [deleted] => 0
    [lang_id] => 0
    [blogname] => Example Website
    [siteurl] => http://example.com
    [post_count] => 420
)

As you can see, there is a lot of valuable data returned about the site specified. You can also retrieve a single option value by stating the name to return, as shown here:

<?php
echo 'Total post count: ' . get_blog_details( 1 )->post_count;
?>

It can also be helpful to retrieve a list of all available sites in your Multisite network. A helpful function to accomplish this is the get_sites() function. This function will retrieve a list of sites in your network matching the requested arguments. Let's look at the following example:

<?php
//get a list of all public site IDs
$args = array (
    'public' => '1'
);
 
$sites = get_sites( $args );
 
if ( is_array( $sites ) ) {
 
    //loop through the site IDs
    foreach ($sites as $site) {
     
        //display each site name
        echo get_blog_details( $site->blog_id )->blogname . '<br/>';
     
    }
 
}
?>

This is a basic example showing you how to retrieve all network sites that are marked public. The get_sites() function accepts a number of arguments, which you can view a full list of at developer.wordpress.org/reference/functions/get_sites.

Switching and Restoring Sites

One major advantage to using Multisite is how easy it is to aggregate content and other data between different sites in your network.

You can use two primary functions to pull data from sites in your network. The first of these functions is switch_to_blog(). This function enables you to switch to any site in your network.

<?php switch_to_blog( $site_id ); ?>

The function accepts one parameter.

  • $site_id: The ID of the site you want to switch to

The second function is restore_current_blog(). This function restores the user to the current site. You should always execute this function after calling switch_to_blog(). If not, everything that processes after the switch will pull from the site you switched to, not the current site. This can mess up your widgets, site settings, and more.

Now look at an example. In this example, you create a custom settings page and display posts from blog ID 3.

<?php
add_action( 'admin_menu', 'pdev_multisite_switch_menu' );
             
function pdev_multisite_switch_menu() {
             
    //create custom top-level menu
    add_menu_page( 'Multisite Switch', 'Multisite Switch',
        'manage_options', 'pdev-network-switch', 'pdev_multisite_switch_page' );
             
}
?>

First create a custom top‐level menu. This will point to the pdev_multisite_switch_page() function, which will be the display page for the posts from site 3.

<?php
function pdev_multisite_switch_page() {
             
    if ( is_multisite() ) {
             
        //switch to blog ID 3
        switch_to_blog( 3 );
             
        //create a custom Loop
        $recent_posts = new WP_Query();
        $recent_posts->query( 'posts_per_page=5' );
             
        //start the custom Loop
        while ( $recent_posts->have_posts() ) :
            $recent_posts->the_post();
             
            //store the recent posts in a variable
            echo '<p><a href="' .get_permalink(). '">' .
                get_the_title() .'</a></p> ';
             
        endwhile;
             
        //restore the current site
        restore_current_blog();
             
    }
             
}
?>

As always, you need to verify that Multisite is enabled using the is_multisite() function check. If Multisite is not enabled, the switch_to_blog() and restore_current_blog() functions will not be available to use in your plugin. Next, use the switch_to_blog() function to switch to blog ID 3. In this case, you hard‐coded the blog ID, but this could always be a dynamic variable set by a user. Now that you've switched to the site you want to pull content from, you need to create a custom loop to retrieve the content.

To create the custom loop, you define a variable named $recent_posts and instantiate an instance of WP_Query. Next set the query parameters; in this case, you set posts_per_page to 5. This returns the five latest posts found. Now that WP_Query has been defined, it's time to execute the loop and retrieve the results. You do this with the have_posts() and the_post() functions. The custom loop will then echo out the posts found from the query.

The final step is to execute restore_current_blog(). If you did not run this function, WordPress would stay on the site you switched to. If you execute additional loops below this, they would all pull from blog ID 3 and not from the current site you are viewing.

Now when you visit the plugin settings page, the latest five posts from blog ID 3 display. Review the entire plugin, shown here:

<?php
/*
Plugin Name: Multisite Switch Example Plugin
Plugin URI: https://example.com/wordpress-plugins/my-plugin
Description: A plugin to demonstrate Multisite site switching
Version: 1.0
Author: Brad Williams
Author URI: https://wrox.com
License: GPLv2
*/
             
add_action( 'admin_menu', 'pdev_multisite_switch_menu' );
             
function pdev_multisite_switch_menu() {
             
    //create custom top-level menu
    add_menu_page( 'Multisite Switch', 'Multisite Switch',
        'manage_options',
        'pdev-network-switch', 'pdev_multisite_switch_page' );
             
}
             
function pdev_multisite_switch_page() {
             
    if ( is_multisite() ) {
             
        //switch to blog ID 3
        switch_to_blog( 3 );
             
        //create a custom Loop
        $recent_posts = new WP_Query();
        $recent_posts->query( 'posts_per_page=5' );
             
        //start the custom Loop
        while ( $recent_posts->have_posts() ) :
            $recent_posts->the_post();
             
            //store the recent posts in a variable
            echo '<p><a href="' .get_permalink(). '">' .
                get_the_title() .'</a></p>';
             
        endwhile;
             
        //restore the current site
        restore_current_blog();
             
    }
             
}

This is a basic example that demonstrates the power of the switch_to_blog() functionality in Multisite.

The switch_to_blog() function is not just limited to site content. You can also retrieve other WordPress data including widgets, sidebars, menus, and more. Basically, any data stored in the content database tables ( wp_ID_tablename) is available when using the switch_to_blog() function. Now look at a few examples. In the following example, you can assume you have a site with an ID of 3 and you want to retrieve a navigation menu from the site:

<?php
//switch to blog ID 3
switch_to_blog( 3 );
             
//display the nav menu Main Menu
wp_nav_menu( 'Main Menu' );
             
//restore the current site
restore_current_blog();
?>

First run switch_to_blog() to switch to blog ID 3. Next use the wp_nav_menu() function to display a menu named Main Menu from the site. Finally, run the restore_current_blog() function to reset to the blog you are viewing. The end result displays the nav menu Main Menu created on site 3 anywhere you run this code in your network.

As another example, you can also easily load a site’s sidebar using the same method.

<?php
//switch to blog ID 34
switch_to_blog( 34 );
             
//load the primary sidebar
get_sidebar();
             
//restore the current site
restore_current_blog();
?>

It's important to note that switch_to_blog() is database‐only. This means a site's plugins are not included in a switch. So, if site 2 has the Halloween Revenge plugin running and you switch to site 2, Halloween Revenge will not be available for use unless it is also activated on the site performing the switch.

Network Content Shortcode Examples

Now take the switch example and integrate shortcode support. This plugin enables you to add a shortcode to a post on the current site, defining what blog ID you want some other posts from, and display those posts on your current post or page.

First create a new shortcode using the add_shortcode() function, which will be introduced in Chapter 14, “The Kitchen Sink.”

<?php
add_shortcode( 'network_posts', 'pdev_multisite_network_posts' );
?>

The new shortcode will be [network_posts]. Next create the function to generate the network posts to display when the shortcode is used in a post or page.

<?php
function pdev_multisite_network_posts( $attr ) {
    extract( shortcode_atts( array(
           "site_id"    =>    '1',
           "num"        =>  '5'
           ), $attr ) );
            
    if ( is_multisite() ) {
            
        $return_posts = '';
            
        //switch to site set in the shortcode
        switch_to_blog( absint( $site_id ) );
            
        //create a custom Loop
        $recent_posts = new WP_Query();
        $recent_posts->query( 'posts_per_page=' . absint( $num ) );
             
        //start the custom Loop
        while ( $recent_posts->have_posts() ) :
           $recent_posts->the_post();
            
           //store the recent posts in a variable
           $title = get_the_title();
           $permalisk = get_the_permalink();
           $return_posts .= '<p><a href="' . $permalink '">' . $title . '</a></p>';
            
        endwhile;
             
        //restore the current site
        restore_current_blog();
             
        //return the results to display
        return $return_posts;
             
    }
}
?>

The shortcode can accept two parameters: site_id and num. This enables the user to set which site in the network to pull the posts from and how many to display. As always, check to verify that Multisite is enabled on the site before proceeding.

$return_posts is the variable that stores all the posts to return to the shortcode for display, so start by setting that variable to nothing to flush it out. Next use the switch_to_blog() function to switch to the site specified in the shortcode. If the user did not set a specific blog ID, it will default to 1.

Now it's time to create a custom loop to retrieve the posts to display. You can see that the posts_per_page parameter is set to $num, which is set in the shortcode. If the user does not set the number of posts to display, it defaults to 5. Next loop through the posts loaded and store them in $return_posts.

After the custom loop finishes running, you need to execute restore_current_blog(). This resets the site to the site you are viewing, not the site you switched to earlier. The final step is to return $return_posts. This replaces the shortcode in a post or page with the custom loop results.

Now you can easily retrieve posts from any site in your network using the shortcode such as [network_posts site_id="3" num="10"]. Review the full plugin, shown here:

<?php
/*
Plugin Name: Multisite Switch Shortcode Plugin
Plugin URI: https://example.com/wordpress-plugins/my-plugin
Description: A plugin for aggregating content using a shortcode
Version: 1.0
Author: Brad Williams
Author URI: https://wrox.com
License: GPLv2
*/
             
add_shortcode( 'network_posts', 'pdev_multisite_network_posts' );
             
function pdev_multisite_network_posts( $attr ) {
    extract( shortcode_atts( array(
            'site_id'    =>    '1',
            'num'        =>    '5'
            ), $attr ) );
             
    if ( is_multisite() ) {
             
        $return_posts = '';
             
        //switch to site set in the shortcode
        switch_to_blog( absint( $site_id ) );
             
        //create a custom Loop
        $recent_posts = new WP_Query();
        $recent_posts->query( 'posts_per_page=' .absint( $num ) );
             
        //start the custom Loop
        while ( $recent_posts->have_posts() ) :
            $recent_posts->the_post();
             
            //store the recent posts in a variable
            $return_posts .= '<p><a href="' .get_permalink().
                '">' .get_the_title() .'</a></p>';
             
        endwhile;
             
        //restore the current site
        restore_current_blog();
             
        //return the results to display
        return $return_posts;
             
    }
}

Now take the switch shortcode example to the next level and retrieve posts from multiple sites in the network and display them based on the latest post date. As in the previous example, use the add_shortcode() function to register the shortcode in your plugin.

<?php
add_shortcode( 'latest_network_posts',
    'pdev_multisite_latest_network_posts' );
?>

Next create your custom pdev_multisite_latest_network_posts() function.

<?php
function pdev_multisite_latest_network_posts() {
             
    if ( is_multisite() ) {
             
        $return_posts = '';

As always, check to verify that Multisite is enabled using the is_multisite() function. You can also set $return_posts to nothing to flush it out. Now it's time to retrieve the posts.

//get posts from current site
$local_posts = get_posts( 'numberposts=5' );
             
//switch to blog ID 3
switch_to_blog( 3 );
             
//get posts from another site
$network_posts = get_posts( 'numberposts=5' );
             
//restore the current site
restore_current_blog();

Use the get_posts() function to retrieve the latest five posts from the current site. Next switch to blog ID 3 and run the same get_posts() function to retrieve the five latest posts from that site. Notice that you are storing the returned array values in separate variables: $local_posts and $network_posts. Finally, call restore_current_blog() to reset to the current site you are on.

Now that you have five posts from each site stored in separate arrays, you need to merge them into a single array.

//merge the two arrays
$posts = array_merge( $local_posts, $network_posts );

Now that you have a single array of posts, you need to sort the posts based on the post date so that they are in proper reverse chronological order with the latest post first. Use the PHP usort() function to sort the array based on a custom comparison function you will create later.

//sort the post results by date
usort( $posts, 'pdev_multisite_sort_posts_array' );

Now that the posts are in the proper order in the array, you need to loop through the results and assign them to the $return_posts variable.

foreach ( $posts as $post ) {
             
    //store latest posts in a variable
    $return_posts .= $post->post_title .' - posted on '
        .$post->post_date .'<br/>';
             
}

Use a standard foreach PHP loop to loop through the results. Finally, return the results for display by the shortcode.

    //return the results to display
    return $return_posts;
             
    }
}

The final step is to create the custom function pdev_multisite_sort_posts_array() to sort the post array by the date that was called earlier from the usort function.

//sort the array by date
function pdev_multisite_sort_posts_array( $a, $b ) {
             
    //if dates are the same return 0
    if ($a->post_date == $b->post_date)
        return 0;
             
    //ternary operator to determine which date is newer
    return $a->post_date < $b->post_date ? 1 : -1;
             
}

This function simply compares two values and returns either a 1 or –1 based on which is greater. The usort() function sorts based on the number assigned.

Now the shortcode [latest_network_posts] will display the latest 10 posts between two sites based on the publish date. Review the entire plugin code, shown here:

<?php
/*
Plugin Name: Multisite Latest Network Posts Plugin
Plugin URI: https://example.com/wordpress-plugins/my-plugin
Description: Displays the latest posts from multiple sites
Version: 1.0
Author: Brad Williams
Author URI: https://wrox.com
License: GPLv2
*/
             
add_shortcode( 'latest_network_posts',
    'pdev_multisite_latest_network_posts' );
             
function pdev_multisite_latest_network_posts() {
             
    if ( is_multisite() ) {
             
        $return_posts = '';
             
        //get posts from current site
        $local_posts = get_posts( 'numberposts=5' );
             
        //switch to blog ID 3
        switch_to_blog( 3 );
             
        //get posts from another site
        $network_posts = get_posts( 'numberposts=5' );
             
        //restore the current site
        restore_current_blog();
             
        //merge the two arrays
        $posts = array_merge( $local_posts, $network_posts );
             
        //sort the post results by date
        usort( $posts, 'pdev_multisite_sort_posts_array' );
             
        foreach ( $posts as $post ) {
             
            //store latest posts in a variable
            $return_posts .= $post->post_title .' - posted on '
                .$post->post_date .'<br/>';
             
        }
             
        //return the results to display
        return $return_posts;
             
    }
             
}
             
//sort the array by date
function pdev_multisite_sort_posts_array( $a, $b ) {
             
    //if dates are the same return 0
    if ($a->post_date == $b->post_date)
        return 0;
             
    //ternary operator to determine which date is newer
    return $a->post_date < $b->post_date ? 1 : -1;
             
}

A Network Content Widget Example

Another common task when working in a Multisite environment is a widget to display recent posts from sites in the network. You can create a plugin with a widget to display the recent posts from any site in the network.

<?php
//widgets_init action hook to execute custom function
add_action( 'widgets_init', 'pdev_multisite_register_widget' );
             
//register our widget
function pdev_multisite_register_widget() {
    register_widget( 'pdev_multisite_widget' );
}
?>

First use the widgets_init action hook to run the custom function to register your new widget. In this example, the new widget will be registered as pdev_multisite_widget. Next create a new class using the registered widget name and extending the WP_Widget.

//pdev_multisite_widget class
class PDEV_Multisite_Widget extends WP_Widget {
 
    //process our new widget
    function __construct() {
             
        $widget_ops = array( 'classname' => 'pdev_multisite_widget',
            'description' =>
                'Display recent posts from a network site.' );
        parent::__construct( 'pdev_multisite_widget_posts',
            'Multisite Recent Posts', $widget_ops );
             
    }

You also define the widget settings. You set the widget name to Multisite Recent Posts, the description of what the widget does, and the custom class name that will be used when displaying the widget.

Now it's time to create the widget settings form. This widget contains three settings: the title, the site to load recent posts from, and the number of posts to display.

    //build our widget settings form
    function form( $instance ) {
        global $wpdb;
             
        $defaults = array( 'title' => 'Recent Posts',
            'disp_number' => '5' );
        $instance = wp_parse_args( (array) $instance, $defaults );
        $title = $instance['title'];
        $siteid = $instance['siteid'];
        $disp_number = $instance['disp_number'];

You will be making a custom database query to retrieve the network blog IDs, so you need to define $wpdb as a global variable. The widget defaults are set in the $defaults variable; in this case, you set the default title to Recent Posts and the default number of posts to display to 5. Next, the instance values are loaded, which are your widget setting values.

Now that you have loaded the widget values, you need to add the form field settings for the widget. The first field is a text field to store the widget title that will be displayed.

        //title textfield widget option
        echo '<p>Title: <input class="widefat" name="'
            .$this->get_field_name( 'title' )
            .'" type="text" value="'
            .esc_attr( $title ). '"/></p>';

As always, you want to use the proper escaping function when displaying data entered by a user, in this case esc_attr() to display the $title value.

The next field to add is a select form to set which site in the network to display recent posts from. To create this form field, you need to retrieve a list of all public blog IDs in your network. You'll use the get_sites() function to retrieve the IDs.

        //get a list of all public site IDs
        $args = array (
            'public' => '1'
        );
 
        $sites = get_sites( $args );

The function retrieves all public blog IDs in your Multisite network and returns them in an array stored in the $sites variable. Now that you have the blog IDs, you need to loop through the results to build the select list.

        if ( is_array( $sites ) ) {
 
            echo '<p>';
            echo 'Site to display recent posts';
            echo '<select name="' . $this->get_field_name( 'siteid' )
                .'" class="widefat">';
             
            //loop through the blog IDs
            foreach ( $sites as $site ) {
             
                //display each site as an option
                echo '<option value="' .$site->blog_id. '" '
                    .selected( $site->blog_id, $siteid )
                    . '>' . get_blog_details( $site->blog_id )->blogname
                    . '</option>';
             
            }
             
            echo '</select>';
            echo '</p>';
        }

Before working with an array, it's a good practice to verify that it is actually an array. You can do so using the PHP function is_array(). After you confirm that $blogs is an array, you can display the option text and select field. To display each site as an option, loop through the array values. Use the get_blog_details() function to display the site name in the option field. The $blog variable, which stores the blog ID, is set to the value of the option field.

The final form field to display is the number of posts option.

        //number to display textfield widget option
        echo '<p>Number to display: <input class="widefat" name="'
            .$this->get_field_name( 'disp_number' ). '" type="text"
            value="' .esc_attr( $disp_number ). '"/></p>';
             
    }

Just like the title option before, this is a standard text form field to store the number of posts to display. That's the final widget form field, so be sure to close out the function with }.

Next you need to save your widget settings using the update widget class function.

    //save the widget settings
    function update( $new_instance, $old_instance ) {
             
        $instance = $old_instance;
        $instance['title'] = sanitize_text_field( $new_instance['title'] );
        $instance['siteid'] = absint( $new_instance['siteid'] );
        $instance['disp_number'] =
            absint( $new_instance['disp_number'] );
             
        return $instance;
    }

The widget class will handle saving the options for you. Be sure to sanitize the widget settings. Both siteid and disp_number should always be a number, so use the absint() function to verify that the setting is a positive integer.

The final step is to display the widget.

    //display the widget
    function widget( $args, $instance ) {
        extract( $args );
             
        echo $before_widget;
             
        //load the widget options
        $title = apply_filters( 'widget_title', $instance['title'] );
        $siteid = empty( $instance['siteid'] ) ? 1 :
            $instance['siteid'];
        $disp_number = empty( $instance['disp_number'] ) ? 5 :
            $instance['disp_number'];
             
        //display the widget title
        if ( !empty( $title ) ) {
            echo $before_title . esc_html( $title ) . $after_title;
        }
             
        echo '<ul>';

First, extract the $args variable to gain access to the global theme values like $before_widget and $after_widget. Next load the widget settings. The $siteid and $disp_number variables are both using a ternary operator to set their values. This means if the option value is empty, it will be set to a default value. $siteid would default to 1, and $disp_number would default to 5.

Now display the $title, surrounded by the $before_title and $after_title global theme values. Now it's time to display the recent posts from the site saved in the widget.

        //switch to site saved
        switch_to_blog( absint( $siteid ) );
             
        //create a custom loop
        $recent_posts = new WP_Query();
        $recent_posts->query( 'posts_per_page='
            . absint( $disp_number ) );
             
        //start the custom loop
        while ( $recent_posts->have_posts() ) :
            $recent_posts->the_post();
             
            //display the recent post title with link
            echo '<li><a href="' . get_permalink() . '">'
                . get_the_title() . '</a></li>';
             
        endwhile;
             
        //restore the current site
        restore_current_blog();
             
        echo '</ul>';
        echo $after_widget;
             
    }
             
}

Using the switch_to_blog() function again, the widget switches to the site saved in the widget settings. After the site has been loaded, create a custom loop using the WP_Query class. The posts_per_page query parameter is defined by the $disp_number widget setting. The recent posts display in an unordered list using a while loop. After the loop completes, you need to restore the current site using restore_current_blog().

You now have a Multisite widget to easily display posts from any site in your network! This simple example shows the power of aggregating content throughout a Multisite network in WordPress and how easy it is to accomplish that.

<?php
/*
Plugin Name: Multisite Recent Posts Widget
Plugin URI:  https://example.com
Description: Retrieves the most recent posts in a Multisite network
Author: Brad Williams
Version: 1.0
Author URI: https://wrox.com
*/
             
//widgets_init action hook to execute custom function
add_action( 'widgets_init', 'pdev_multisite_register_widget' );
             
//register our widget
function pdev_multisite_register_widget() {
    register_widget( 'pdev_multisite_widget' );
}
             
//pdev_multisite_widget class
class PDEV_Multisite_Widget extends WP_Widget {
 
    //process our new widget
    function __construct() {
             
        $widget_ops = array( 'classname' => 'pdev_multisite_widget',
            'description' =>
                'Display recent posts from a network site.' );
        parent::__construct( 'pdev_multisite_widget_posts',
            'Multisite Recent Posts', $widget_ops );
             
    }
             
     //build our widget settings form
    function form( $instance ) {
        global $wpdb;
             
        $defaults = array( 'title' => 'Recent Posts',
            'disp_number' => '5' );
        $instance = wp_parse_args( (array) $instance, $defaults );
        $title = $instance['title'];
        $siteid = $instance['siteid'];
        $disp_number = $instance['disp_number'];
             
        //title textfield widget option
        echo '<p>Title: <input class="widefat" name="'
            .$this->get_field_name( 'title' )
            . '" type="text" value="' .esc_attr( $title )
            . '"/></p>';
             
        //get a list of all public site IDs
        $args = array (
            'public' => '1'
        );
 
        $sites = get_sites( $args );
 
        if ( is_array( $sites ) ) {
 
            echo '<p>';
            echo 'Site to display recent posts';
            echo '<select name="' .$this->get_field_name('siteid')
                .'" class="widefat">';
             
            //loop through the blog IDs
            foreach ($sites as $site) {
             
                //display each site as an option
                echo '<option value="' .$site->blog_id. '" '
                    .selected( $site->blog_id, $siteid )
                    . '>' .get_blog_details( $site->blog_id )->blogname
                    . '</option>';
             
            }
             
            echo '</select>';
            echo '</p>';
        }
             
        //number to display textfield widget option
        echo '<p>Number to display: <input class="widefat" name="'
            .$this->get_field_name( 'disp_number' ). '" type="text"
            value="' .esc_attr( $disp_number ). '"/></p>';
             
    }
             
      //save the widget settings
    function update( $new_instance, $old_instance ) {
             
        $instance = $old_instance;
        $instance['title'] = strip_tags( $new_instance['title'] );
        $instance['siteid'] = absint( $new_instance['siteid'] );
        $instance['disp_number'] =
            absint( $new_instance['disp_number'] );
             
        return $instance;
    }
             
     //display the widget
    function widget( $args, $instance ) {
        extract( $args );
             
        echo $before_widget;
             
        //load the widget options
        $title = apply_filters( 'widget_title', $instance['title'] );
        $siteid = empty( $instance['siteid'] ) ? 1 :
            $instance['siteid'];
         $disp_number = empty( $instance['disp_number'] ) ? 5 :
             $instance['disp_number'];
             
         //display the widget title
        if ( !empty( $title ) ) { echo $before_title . $title
            . $after_title; };
             
        echo '<ul>';
             
        //switch to site saved
        switch_to_blog( absint( $siteid ) );
             
        //create a custom loop
        $recent_posts = new WP_Query();
        $recent_posts->query( 'posts_per_page='
            .absint( $disp_number ) );
             
        //start the custom Loop
        while ( $recent_posts->have_posts() ) :
            $recent_posts->the_post();
             
            //display the recent post title with link
            echo '<li><a href="' .get_permalink(). '">'
                .get_the_title() .'</a></li>';
             
        endwhile;
             
        //restore the current site
        restore_current_blog();
             
        echo '</ul>';
        echo $after_widget;
             
    }
             
}

Creating a New Site

You can easily create new sites in your Multisite network in the Dashboard of WordPress. But what if you want to create a new site in your plugin? As always, there's a function for that, and it's called wpmu_create_blog().

<?php wpmu_create_blog( $domain, $path, $title,
    $user_id, $meta, $site_id); ?>

This function accepts six parameters.

  • $domain: The domain of the new site.
  • $path: The path of the new site. This is the subdirectory or subdomain name depending on which setup you use.
  • $title: The title of the new site.
  • $user_id: The user ID of the user account who will be the site admin.
  • $meta: Additional meta information.
  • $site_id: The blog ID of the site to be created.

The only required parameters are the first four; the last two are optional. If the new site is created successfully, the function returns the newly created blog ID. This function does all the heavy lifting of creating database tables, adding default content, assigning any user roles, and so on.

As you probably noticed, the function begins with wpmu_. Many of the Multisite functions were once a part of WordPress MU, prior to the merging of the two code bases in WordPress 3.0. These function names can contain wpmu, or blog, to support backward compatibility.

As an example, create a plugin that enables users to create sites in WordPress Multisite. First, create a custom top‐level menu for the plugin page.

<?php
add_action( 'admin_menu', 'pdev_multisite_create_menu' );
             
function pdev_multisite_create_menu() {
             
    //create custom top-level menu
    add_menu_page( 'Multisite Create Site Page',
        'Multisite Create Site',
        'manage_options', 'pdev-network-create',
            'pdev_multisite_create_sites_page' );
             
}
?>

Now create the function to display a form for creating a new site.

<?php
function pdev_multisite_create_sites_page() {
             
    //check if multisite is enabled
    if ( is_multisite() ) {

As always, you need to verify that Multisite is enabled before using any Multisite‐specific functions. Next add the code to retrieve the form fields submitted and create a new site in the network with the values.

<?php
//if the form was submitted lets process it
        if ( isset( $_POST['create_site'] ) ) {
             
            //populate the variables based on form values
            $domain = sanitize_text_field( $_POST['domain'] );
            $path = sanitize_text_field( $_POST['path'] );
            $title = sanitize_text_field( $_POST['title'] );
            $user_id = absint( $_POST['user_id'] );
             
            //verify the required values are set
            if ( $domain && $path && $title && $user_id ) {
             
                //create the new site in WordPress
                $new_site = wpmu_create_blog( $domain, $path,
                    $title, $user_id );
             
                //if successfully display a message
                if ( $new_site ) {
             
                    echo '<div class="notice notice-success is-dismissible">New site '
                        .$new_site. ' created successfully!</div>';
             
             
                }
             
            //if required values are not set display an error
            } else {
             
                echo '<div class="notice notice-error is-dismissible">
                    New site could not be created.
                    Required fields are missing</div>';
             
            }
             
        }
?>

First check whether $_POST['create_site'] is set. This will be set only if the form has been submitted. Next populate the variables based on the form entries. Notice you'll be using the proper escaping functions to verify that the data submitted from the form is escaped properly.

Next verify that $domain, $path, $title, and $user_id all have values because they are the required fields when creating sites using wpmu_create_blog(). If the values are not filled out, an error message displays. After you verify that all values exist, it's time to execute the wpmu_create_blog() function to create the new site. If the site is created successfully, the variable $new_site will contain the ID of the newly created site, and a success message will be displayed.

The final piece is to create the form for the new site fields.

        <div class="wrap">
            <h2>Create New Site</h2>
            <form method="post">
            <table class="form-table">
            <tr valign="top">
                <th scope="row">
                    <label for="fname">Domain</label>
                </th>
                <td><input maxlength="45" size="25" name="domain"
                    value="<?php echo DOMAIN_CURRENT_SITE; ?>"/>
                </td>
            </tr>
            <tr valign="top">
                <th scope="row"><label for="fname">Path</label></th>
                <td>
                    <input maxlength="45" size="10" name="path"/>
                </td>
            </tr>
            <tr valign="top">
                <th scope="row">
                    <label for="fname">Title</label>
                </th>
                <td>
                    <input maxlength="45" size="25" name="title"/>
                </td>
            </tr>
            <tr valign="top">
                <th scope="row">
                    <label for="fname">User ID</label>
                </th>
                <td>
                    <input maxlength="45" size="3" name="user_id"/>
                </td>
            </tr>
            <tr valign="top">
                <td>
                <input type="submit" name="create_site"
                    value="Create Site" class="button-primary"/>
             
                <input type="submit" name="reset"
                    value="Reset" class="button-secondary"/>
                </td>
            </tr>
            </table>
            </form>
        </div>

This is a fairly basic form that accepts the parameters required to create a new site.

<?php
/*
Plugin Name: Multisite Create Site Example Plugin
Plugin URI: http://example.com/wordpress-plugins/my-plugin
Description: A plugin to demonstrate creating sites in Multisite
Version: 1.0
Author: Brad Williams
Author URI: http://wrox.com
License: GPLv2
*/
             
add_action( 'admin_menu', 'pdev_multisite_create_menu' );
             
function pdev_multisite_create_menu() {
             
    //create custom top-level menu
    add_menu_page( 'Multisite Create Site Page',
        'Multisite Create Site',
        'manage_options', 'pdev-network-create',
        'pdev_multisite_create_site_settings' );
             
}
             
function pdev_multisite_create_site_settings() {
             
    //check if multisite is enabled
    if ( is_multisite() ) {
             
        //if the form was submitted lets process it
        if ( isset( $_POST['create_site'] ) ) {
             
            //populate the variables based on form values
            $domain = sanitize_text_field( $_POST['domain'] );
            $path = sanitize_text_field( $_POST['path'] );
            $title = sanitize_text_field( $_POST['title'] );
            $user_id = absint( $_POST['user_id'] );
             
            //verify the required values are set
            if ( $domain && $path && $title && $user_id ) {
             
                //create the new site in WordPress
                $new_site = wpmu_create_blog( $domain, $path,
                    $title, $user_id );
             
                //if successfully display a message
                if ( $new_site ) {
             
                    echo '<div class="notice notice-success is-dismissible">New site '
                        .$new_site. ' created successfully!</div>';
             
             
                }
             
            //if required values are not set display an error
            } else {
             
                echo '<div class="notice notice-error is-dismissible">
                    New site could not be created.
                    Required fields are missing
                    </div>';
             
            }
             
        }
        ?>
        <div class="wrap">
            <h2>Create New Site</h2>
            <form method="post">
            <table class="form-table">
            <tr valign="top">
                <th scope="row">
                    <label for="fname">Domain</label>
                </th>
                <td><input maxlength="45" size="25" name="domain"
                        value="<?php echo DOMAIN_CURRENT_SITE; ?>"/>
                </td>
            </tr>
            <tr valign="top">
                <th scope="row"><label for="fname">Path</label></th>
                <td>
                    <input maxlength="45" size="10" name="path"/>
                </td>
            </tr>
            <tr valign="top">
                <th scope="row"><label for="fname">Title</label></th>
                <td>
                    <input maxlength="45" size="25" name="title"/>
                </td>
            </tr>
            <tr valign="top">
                <th scope="row">
                    <label for="fname">User ID</label>
                </th>
                <td>
                    <input maxlength="45" size="3" name="user_id"/>
                </td>
            </tr>
            <tr valign="top">
                <td>
                <input type="submit" name="create_site"
                    value="Create Site" class="button-primary"/>
                <input type="submit" name="reset" value="Reset"
                    class="button-secondary"/>
                </td>
            </tr>
            </table>
            </form>
        </div>
        <?php
    } else {
             
        echo '<p>Multisite is not enabled</p>';
             
    }
             
}

There is also an easy method to update a site's status. This is useful if you want to dynamically archive a site or flag a site as spam. To do so, use the update_blog_status() function.

<?php update_blog_status( $blog_id, $pref, $value, $refresh ); ?>

The function accepts four parameters.

  • $site_id: The blog ID of the site to update
  • $pref: The status type to update
  • $value: The new value of the status
  • $refresh: Whether to refresh the site details cache

The first three parameters are required. The $pref parameter is the status to update, which accepts site_id, domain, path, registered, last_updated, public, archived, mature, spam, deleted, and lang_id. In this example, you update a site in your network to be archived.

<?php
update_blog_status( $blog_id, 'archived', 1 );
?>

Site Options

Site options in Multisite are stored identically to non‐Multisite options, but we recommend you use the dedicated family of functions to be explicit about what you are trying to do and why.

  • add_blog_option(): Creates a new option
  • update_blog_option(): Updates an option and creates it if it doesn't exist
  • get_blog_option(): Loads a site option that already exists
  • delete_blog_option(): Deletes a site option

The major difference between this set of functions and the standard option functions is you have to pass a blog ID parameter to each function. The function will then switch to the site specified, handle the option task, and then switch back to the current site.

<?php add_blog_option( $blog_id, $key, $value ); ?>

The $site_id value is the ID of the site you want to add an option to. The $key value is the name of the option to add, and $value is the value of the new option.

Loading site options is just as easy. Using the get_blog_option() function, you can load any site‐specific option required.

<?php
$site_id= 3;
echo '<p>Site ID: '.$site_id.'</p>';
echo '<p>Site Name: ' .get_blog_option( $blog_id, 'blogname' )
    .'</p>';
echo '<p>Site URL: ' .get_blog_option( $blog_id, 'siteurl' ) .'</p>';
?>

Network Options

Network options in Multisite are like Site Options but relate specifically to the network itself and are stored more like metadata than options.

  • add_network_option(): Creates a new option
  • update_network_option(): Updates an option and creates it if it doesn't exist
  • get_network_option(): Loads an option that already exists
  • delete_network_option(): Deletes an option

All of these will require you to either pass in a network ID as the first parameter or pass in null to use the current network. The function will then switch to the site specified, handle the option task, and then switch back to the current site.

<?php add_network_option( $network_id, $key, $value ); ?>

The $network_id value is the ID of the network you want to add an option to. The $key value is the name of the option to add, and $value is the value of the new option.

Loading network options is just as easy. Using the get_network_option() function, you can load any network‐specific option required.

<?php
$network_id= 3;
echo '<p>Network ID: '.$network_id.'</p>';
echo '<p>Network Name: ' .get_network_option( $network_id, 'sitename' )
    .'</p>';
echo '<p>Network URL: ' .get_network_option( $network_id, 'siteurl' ) .'</p>';
?>

Site Meta

Site meta in Multisite are stored globally and are a super useful way to add data to sites in a way that can be queried across multiple sites, whereas Network Options can be per site only, requiring site switching to aggregate them.

  • add_site_meta(): Creates new metadata
  • update_site_meta(): Updates metadata and creates it if it doesn't exist
  • get_site_meta(): Loads metadata that already exists
  • delete_site_meta(): Deletes metadata

These functions use the Metadata API inside WordPress to make querying for and caching these arbitrary values an absolute breeze.

<?php add_site_meta( $site_id, $key, $value ); ?>

The $site_id value is the ID of the site you want to add metadata to. The $key value is the name of the metadata, and $value is the value of the new data.

Loading site metadata is just as easy. Using the get_site_Meta() function, you can load any site‐specific metadata required. WordPress currently does not store any data in this table on its own. It exists simply as a way to extend sites at a global level.

Users and Roles

Users work slightly differently than in standard WordPress. If Allow New Registrations is enabled under Network Settings, visitors to your site can register new accounts. The major difference is that each site in your network will have its own different set of users based on who has access to those sites. Users can also have different roles on different sites throughout the network. Users are not automatically members of every site in your network.

Before executing any code that is site‐specific, you should verify that the user logged in is a member of that site. Multisite features multiple functions for working with users. To verify that a user is a member of the site, use the is_user_member_of_blog() function.

<?php is_user_member_of_blog( 0, $site_id ) ?>

The function accepts two parameters, $user_id and $site_id, which are both optional. If the parameters are not specified, it defaults to the currently logged‐in user and the current site you are on.

<?php
if ( is_user_member_of_blog() ) {
    //user is a member of this site
}
?>

You may specify a user ID if you want to verify that a user is a member of this site.

<?php
if ( is_user_member_of_blog( 42 ) ) {
    //user 42 is a member of this site
}
?>

You can also specify a blog ID if you want to verify that the user is a member of a specific site.

<?php
if ( is_user_member_of_blog( 42, 3 ) ) {
    //user 42 is a member of site ID 3
}
?>

Now that you understand how to check whether a user is a member of a site, let's look at how to add members to a site. In Multisite, you use the add_user_to_blog() function to add any user in WordPress to a specific site in your network.

<?php add_user_to_blog( $blog_id, $user_id, $role ); ?>

This function accepts three parameters.

  • $site_id: The ID of the site you want to add the user to
  • $user_id: The ID of the user to add
  • $role: The user role the user will have on the site

Look at the following working example:

<?php
$site_id = 18;
$user_id = 4;
$role = 'editor';
             
add_user_to_blog( $blog_id, $user_id, $role );
?>

Now build a real‐world example plugin. This plugin auto‐adds logged‐in users to any site they visit in your network. This is useful if you want users to become members on every site in your network without manually adding them to each one.

Start by using the init action hook to execute your custom function to add users to a site.

<?php
add_action( 'init', 'pdev_multisite_add_user_to_site' );
?>

Next create the pdev_multisite_add_user_to_site() function to add the users.

<?php
function pdev_multisite_add_user_to_site() {
             
    //verify user is logged in before proceeding
    if( !is_user_logged_in() ) {
        return false;
    }
             
    //verify user is not a member of this site
    if( !is_user_member_of_blog() ) {
             
        //add user to this site as a subscriber
        $site_id = get_current_blog_id();
        $user_id = get_current_user_id();
        add_user_to_blog( $site_id, $user_id, 'subscriber' );
             
    }
             
}
?>

The first step is to verify that the users are logged in and, if not, return false and exit the function. Next confirm if the users are already members of the site they are viewing. If the users are already members, there is no reason to add them again.

The final step is to add the users to the site using the add_user_to_blog() function. You'll pass in the blog ID, the current user ID, and the role the users are assigned on the site, in this case subscriber. That's it! For this plugin to automatically work across your entire network, you'll need to either upload it to the /mu‐plugins directory or activate the plugin on the Network Admin ➪ Plugins page. That forces the plugin to run across all sites in your network.

<?php
/*
Plugin Name: Multisite Auto-Add User to Site
Plugin URI: https://example.com/wordpress-plugins/my-plugin
Description: Plugin automatically adds the user to any site they visit
Version: 1.0
Author: Brad Williams
Author URI: https://wrox.com
License: GPLv2
*/
             
add_action( 'init', 'pdev_multisite_add_user_to_site' );
             
function pdev_multisite_add_user_to_site() {
             
    //verify user is logged in before proceeding
    if( !is_user_logged_in() ) {
        return false;
    }
             
    //load current blog ID and user data
    global $current_user, $blog_id;
    //verify user is not a member of this site
    if( !is_user_member_of_blog() ) {
             
        //add user to this site as a subscriber
        add_user_to_blog( $blog_id, $current_user->ID, 'subscriber' );
             
    }
             
}

As easily as you can add users, you can also remove users from a site using the remove_user_from_blog() function.

<?php remove_user_from_blog( $user_id, $blog_id, $reassign ); ?>

This function accepts three parameters.

  • $user_id: The user ID you want to remove
  • $site_id: The blog ID to remove the user from
  • $reassign: The user ID to reassign posts to

Look at the following working example:

<?php
$user_id = 4;
$site_id = 18;
$reassign = 1;
             
remove_user_from_blog( $user_id, $blog_id, $reassign );
?>

Another useful function when working with Multisite users is get_blogs_of_user(). This function retrieves site information for all sites the specified users are a member of.

<?php
$user_id = 1;
$user_sites = get_blogs_of_user( $user_id );
print_r( $user_sites );
?>

Running this code example will result in an object array being returned, as shown here:

Array
(
    [1] => stdClass Object
        (
            [userblog_id] => 1
            [blogname] => Main Site
            [domain] => example.com
            [path] => /
            [site_id] => 1
            [siteurl] => http://example.com
        )
             
    [2] => stdClass Object
        (
            [userblog_id] => 2
            [blogname] => Halloween Revenge
            [domain] => example.com
            [path] => /myers/
            [site_id] => 1
            [siteurl] => http://example.com/myers
        )
             
    [8] => stdClass Object
        (
            [userblog_id] => 8
            [blogname] => Freddy Lives
            [domain] => example.com
            [path] => /kruger/
            [site_id] => 1
            [siteurl] => http://example.com/kruger
        )
             
)

You can also do a foreach loop to display specific data from the array.

<?php
$user_id = 1;
$user_sites = get_blogs_of_user( $user_id );
             
foreach ( $user_sites as $user_site ) {
             
    echo '<p>' . $user_site->siteurl . '</p>';
             
}
?>

Super Admin

Multisite offers a pseudo‐user role: Super Administrator. Super admins have access to the administration areas of the entire WordPress installation, including one specific to the network. The network admin is where all network settings, themes, plugins, and so on, are managed. Super admins also have full control over every site in the network, whereas a regular admin can administer only their specific sites.

In Multisite, you can easily assign an existing user to the super admin role by using the grant_super_admin() function. This function accepts only one parameter, which is the user ID to which you want to grant super admin privileges.

<?php
$user_id = 4;
grant_super_admin( $user_id );
?>

As quickly as you can grant super admin privileges, you can just as easily revoke them using the revoke_super_admin() function. This function also accepts only one parameter, which is the user ID to revoke as super admin.

<?php
$user_id = 4;
revoke_super_admin( $user_id );
?>

Both of these functions are located in wp‐admin/includes/ms.php. This means these functions by default are not available on the public side of your site and can be used only on the admin side. For example, if you tried calling either of these functions with a shortcode, you would get a Call to Undefined Function PHP error.

To list all super admins in Multisite, use the get_super_admins() function. This function returns an array of all super admin usernames in your network.

<?php
$all_admins = get_super_admins();
print_r( $all_admins );
?>

This will return the following array of super admins:

Array
(
    [0] => admin
    [1] => brad
)

You can also easily check specific users' IDs to determine whether they are super admins in your network. To do so, use the is_super_admin() function.

<?php
$user_id = 1;
             
if ( is_super_admin( $user_id ) ) {
    echo 'User is Super admin';
}
?>

Checking the Site Owner

Every site in your Multisite network has a site owner. This owner is defined by the admin email address stored in the site options and is set when a new site is created in your network. If you allow open site registration, the user who created the site will be set as the site owner. If you created the site in the dashboard, you can set the owner's email at time of creation.

In some cases, you may want to retrieve a site owner and corresponding user data. The following is an example of how you can so:

<?php
$site_id = 3;
$admin_email = get_blog_option( $blog_id, 'admin_email' );
$user_info = get_user_by( 'email', $admin_email );
print_r( $user_info );
?>

First, use the get_blog_option() function to retrieve the admin_email value for blog ID 3. Next use the get_user_by() function to retrieve the user data based on the admin email. This function enables you to retrieve user data by either user ID, slug, email, or login. In this case, use the admin email to load the user data. The results are shown here:

WP_User Object
(
    [ID] => 3
    [user_login] => freddy
    [user_pass] => $P$B0VRNh0UbN/4YqMFB8fl3OZM2FGKfg1
    [user_nicename] => Freddy Krueger
    [user_email] => [email protected]
    [user_url] =>
    [user_registered] => 2019-10-31 19:00:00
    [user_activation_key] =>
    [user_status] => 0
    [display_name] => Freddy
    [spam] => 0
    [deleted] => 0
    [first_name] => Freddy
    [last_name] => Krueger
    [nickname] => fredster
    [description] =>
    [rich_editing] => true
    [comment_shortcuts] => false
    [admin_color] => fresh
    [use_ssl] => 0
    [aim] =>
    [yim] =>
    [jabber] =>
    [source_domain] => example.com
    [primary_blog] => 3
    [wp_3_capabilities] => Array
        (
            [administrator] => 1
        )
             
    [wp_3_user_level] => 10
    [user_firstname] => Freddy
    [user_lastname] => Krueger
    [user_description] =>
)

As you can see, a lot of useful user information is returned for the site admin account.

Network Stats

Multisite features a few functions to generate stats about your network. The get_user_count() function returns the total number of users registered in WordPress. The get_blog_count() function returns the total number of sites in your network. You can also use the get_sitestats() function to retrieve both values at once in an array.

<?php
$user_count = get_user_count();
echo '<p>Total users: ' .$user_count .'</p>';
             
$blog_count = get_blog_count();
echo '<p>Total sites: ' .$blog_count .'</p>';
             
$network_stats = get_sitestats();
print_r( $network_stats );
?>

DATABASE SCHEMA

WordPress Multisite features a different database schema from standard WordPress. When updating or enabling Multisite, WordPress creates the necessary tables in your database to support Multisite functionality.

Multisite‐Specific Tables

WordPress stores global Multisite settings in centralized tables. These tables are installed only when Multisite is activated and installed, excluding wp_users and wp_usermeta.

  • wp_blogs: Stores each site created in Multisite
  • wp_blogmeta: Stores various metadata at the global level for any sites wanting to store data here
  • wp_registration_log: Keeps a log of all users registered and activated in WordPress
  • wp_signups: Stores users and sites registered using the WordPress registration process
  • wp_site: Stores the primary site's address information
  • wp_sitemeta: Stores various metadata for every network
  • wp_users: Stores all users registered in WordPress
  • wp_usermeta: Stores all metadata for user accounts in WordPress

The rest of the tables created for Multisite are site‐specific tables.

Site‐Specific Tables

Each site in your network features site‐specific database tables. These tables hold the content and setting specific to that individual site. Remember, these tables are prefixed with the $table_prefix value defined in wp‐config.php, followed by $blog_id and then the table name.

  • wp_1_commentmeta
  • wp_1_comments
  • wp_1_options
  • wp_1_postmeta
  • wp_1_posts
  • wp_1_terms
  • wp_1_termmeta
  • wp_1_term_relationships
  • wp_1_term_taxonomy

As you can see, these tables can make your database quickly grow in size. That's why the only limitation to WordPress Multisite is the amount of server resources you have available to power your network of sites. If your network contains 1,000 sites, your database would have more than 9,000 tables. If you have plugins active that create their own database tables for each site, you would add those here as well.

Obviously, this wouldn't be ideal to host on a small shared hosting account. Shared web hosts literally share server resources for multiple websites and applications on single servers, and they are likely to have a low tolerance for customers who have thousands of database tables where there are usually only nine.

QUERY CLASSES

Multisite comes complete with query classes and matching single object classes to make querying for and interacting with these objects feel familiar. If you remember using WP_Query, covered in Chapter 5, you will feel right at home.

WP_Site_Query

This is the class you will use when you need to query the wp_blogs database table for site data. Even the get_blog_details() function from earlier in this chapter uses it internally.

This class is fully documented in a modern format in the official WordPress code documentation at developer.wordpress.org/reference/classes/wp_site_query.

WP_Network_Query

This is the class you will use when you need to query the wp_sites database table for network data. Functions like get_networks() use this query class very early due to how WordPress is loaded to ensure that sites are queried for correctly.

This class is fully documented in a modern format in the official WordPress code documentation at developer.wordpress.org/reference/classes/wp_network_query.

OBJECT CLASSES

Similar to query classes, Multisite comes with single object classes for sites and networks to make interacting with these objects easy. If you remember using WP_Post in Chapter 5, this will feel familiar.

WP_Site

This is the object class that is returned by WP_Site_Query and corresponds to a single row in the wp_blogs database table.

This class is fully documented in a modern format in the official WordPress code documentation at developer.wordpress.org/reference/classes/wp_site.

WP_Network

This is the object class that is returned by WP_Network_Query and corresponds to a single row in the wp_sites database table.

This class is fully documented in a modern format in the official WordPress code documentation at developer.wordpress.org/reference/classes/wp_network.

SUMMARY

WordPress Multisite features limitless possibilities. Enabling Multisite opens the door to creating an amazing network of sites. This also opens up new doors for your plugins with the additional Multisite features and functions.

When developing plugins for WordPress, you need to test your plugins in a Multisite setup to verify that they are compatible. Multisite is included in every WordPress download, and many users convert their standard site installation to Multisite to take advantage of the rapid site deployment features and network capabilities. It will always be important to make sure your plugins work with Multisite, even if it is not a feature you use yourself.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.61.142