Building Web Apps with WordPress (2014)

Chapter 13. Building WordPress Multisite Networks

With the release of WordPress version 3.0 came WordPress Multisite. WordPress Multisite was known as WordPress Multiuser or WPMU prior to v3.0 because it was a separate open source project. Since WordPress and WPMU share most of the same code, it made sense to roll it all into one project. Multisite gives WordPress administrators the ability to create their own network with multiple sites. All of the sites on a Multisite network share the same database and the same source files. When Multisite is set up, new tables are created in the database for each new website created on the network.

Why Multisite?

If you are running more than one install of WordPress, you should consider using Multisite. Some of the benefits include:

§  Logging into one network and making any changes you need to any of your WordPress sites with one administrator account.

§  Making updates to WordPress and installed plugins and/or themes one time in one place instead of multiple websites.

§  Managing all of the users on your network in one location.

§  Easily deploying a new website with a few clicks.

§  If you are using a theme framework for all of the sites on your network, you could make updates to all of your themes at the same time utilizing an available hook in your theme.

Setting Up a Multisite Network

Although setting up Multisite is not as easy as enabling it in a WordPress setting, it is fairly straightforward. The first thing you should do if you are not setting up a brand-new WordPress install is to make a backup of your database and file directory.

Open your wp-config.php file in the root of your WordPress directory and add the following piece of code right under the line that says /* That’s all, stop editing! Happy blogging. */:

define( 'WP_ALLOW_MULTISITE', true );

Refresh your WordPress admin dashboard and go to Tools → Network Setup. Here you should see a few form fields asking you for the following information:

Subdomain or Subdirectory

How do you want to build the subsites on your Multisite network? If you want subdomains like sub.domain.com, then choose subdomain. If you want domain.com/sub, then choose subdirectory. You could always use a domain mapping plugin to map any domain you want to either a subdomain or subdirectory.

Network Title

The name of your Multisite network.

Admin Email Address

The email of the network administrator, most likely your email address.

Once you have filled out the required information, click the Install button.

You should now see two text area boxes. The first text area box is going to contain code that you will need to copy and paste into your wp-config.php file right under the line of code you just previously added under the line that says /* That’s all, stop editing! Happy blogging. */:

define( 'MULTISITE', true );

define( 'SUBDOMAIN_INSTALL', false );

define( 'DOMAIN_CURRENT_SITE', 'whatever.com' );

define( 'PATH_CURRENT_SITE', '/' );

define( 'SITE_ID_CURRENT_SITE', 1 );

define( 'BLOG_ID_CURRENT_SITE', 1 );

In this example, we chose to use subdirectories, which is why we are defining SUBDOMAIN_INSTALL to false. If we chose subdomains, SUBDOMAIN_INSTALL would be set to true.

The second text area box contains code that you will need to copy and paste into your .htaccess file, which should also be in the root directory of your WordPress install:

RewriteEngine On

RewriteBase /

RewriteRule ^index\.php$ - [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]

Once you have copied and pasted the code from each text area box into the appropriate files and saved them on your web server, you can refresh your browser.

You should now be prompted to log in again. Log in with your administrator account username and password. Boom! You should now be running WordPress Multisite as a Super Administrator. A Super Administrator has full access to all of the sites on your network, while regular administrators only have full access to the sites they are administrators of.

If you decided to use subdomains instead of subdirectories, there are a couple extra steps you should take to save you time in the long run. You should set up a subdomain wildcard record on your domain registrar DNS settings page. Remember when first installing WordPress, you set the A (Host) record to the IP address of your hosting account? Well, in the same place you did that, you should be able to add a host name of “*” instead of “@” and point it to the same IP address as the “@” record. This acts as a catchall for any subdomains on your main domain. In short, whatever.whatever.com or somethingelse.whatever.com will both be mapped to the same IP address as the main domain.

Depending on your hosting account, you may also need to set up a wildcard subdomain entry to point to the same directory that your main domain is pointing to. So register a new subdomain like *.whatever.com and point it to the same folder of whatever.com.

So why are we doing all of this? We set up a wildcard for subdomains so when we create new sites on the Multisite network they will automatically work. If we didn’t set up a wildcard for subdomains, we would have to manually add each subdomain we create to the domain registrar and to the host. If we set up the subdomain wildcard once, it will automatically work for any subdomain sites we create on our Multisite network.

Managing a Multisite Network

In your WordPress Admin Menu Bar, go to My Sites → Network Admin to administer your new Multisite network. You can also go to whatever.com/wp-admin/network/ to get to the network dashboard. The network admin area looks very similar to any other site admin area on the network. To help keep track of where you are, just look for the /network/ at the end of the address bar in your browser because it can be easy to get confused.

Dashboard

Your network dashboard is set up very similarly to the default dashboard you are used to seeing on a standard WordPress install except the Right Now widget displays links to quickly add new network sites or users. It also has two text boxes to search for specific users or sites. Just like the regular WordPress dashboard, this network dashboard can be completely customized using plugins or custom code.

Sites

Under Sites, you will manage all of the sites on your network. You can add any number of sites you like and even give access to your network users to create their own sites.

Adding a new site is pretty straightforward; click the Add New button at the top of the sites page or click Add New Link on the Sites submenu to get to the Add New Site page, where you will see the following fields:

Site Address

Depending on how you set up your network, the address you enter will either be a subdomain or a subdirectory.

Site Title

The title or name of your new site.

Admin Email

The email address of the administrator of your new site. This does not have to be your email address; it could be a client or a user that you are setting up a new WordPress site for.

Click the Add Site button, and voila, instant WordPress website. That sure saves a lot of time setting up a brand new install of WordPress.

Users

All of the sites you create on your network will pull from the same users pool. Technically, all users are stored in the wp_users table, and they each have user metadata tying them to one or more sites on your network. From the network users page, you can manage all of the users on any of your sites, set any user to be a Super Admin where the user would have rights to manage the entire WordPress Multisite network, and see what sites each user is a member of.

You can click the Add New button at the top of the Users page or the Add New Link on the Users submenu to get to the Add New User page, where you will see the following fields:

Username

The username of the new user you are creating. Remember, all lowercase and no spaces or special characters.

Email

The email address of the new user you are adding.

Once you click the Add User button, the user you added should receive an email with her username and password, which she can use to log in to the default top-level website with a default role of whatever you set a new user’s role to be. Adding users this way may not be ideal, as you will have to take another step to add them to specific subsites on your network. Depending on your situation, it might be easier for you to add new users to subsites directly from within that subsite, where you can add a new user the same way you would on a typical install of WordPress. If you try to add a user to a site and that username already exists on the network, you will receive a message indicating that the username already exists. At that point, you can add the user to the site by adding her username to the Add Existing User section and choosing a role and whether to send a confirmation email to that user.

Themes

These are all of the themes available in the /wp-content/themes/ directory. You can control all of the themes any of the sites on your network can use. You must network activate a theme first before any site on your network can use it. If you don’t network activate a theme, it won’t even show up as an option to activate on a site’s Appearance → Themes page.

Plugins

These are all of the plugins available in the /wp-content/plugins/directory. You can network activate plugins so that they will automatically run on all sites on your network, including new sites. Network-activated plugins will not show up on the individual site’s plugins page. In fact, unless you specifically enable the plugins menu on the network settings page (see next section), individual site administrators won’t even see the plugins page. If you allow each site administrator to manage their own plugins, he will only be able to activate plugins that are already installed; he will not be able to install his own plugins. This is good because you want to know what plugins are available to all of the sites on your network. You don’t want a site administrator to be able to install any plugin he wants or a custom plugin that could potentially have a negative effect on other sites on your network.

To add a new plugin at the network level, you would add it the same way you would on a normal install of WordPress.

Settings

These settings are unlike the typical WordPress settings you can update for a standard WordPress site; these are network-wide settings. If you click the Settings link on the left admin navigation menu, you should see the following settings:

Operational Settings

§  Network Name

§  Network Admin Email

Registration Settings

§  Allow new registrations

§  Registration notification

§  Add New Users

§  Banned Names

§  Limited Email Registrations

§  Banned Email Domains

New Site Settings

§  Welcome Email

§  Welcome User Email

§  First Post

§  First Page

§  First Comment Author

§  First Comment URL

Upload Settings

§  Site upload space

§  Upload file types

§  Max upload file size

Menu Settings

§  Enable administration menus

Updates

Just like in a standard WordPress installation, you can update core WordPress or any outdated plugins and/or themes all from this page. The beauty of a Multisite network is update everything once and they are updated across all of the sites on your network. This is way more efficient than running updates on multiple WordPress installs. Update WordPress, plugins, and themes…done and done!

Multisite Database Structure

All sites on a Multisite network share the same database. Enabling WordPress Multisite creates a few new tables in your existing database.

Network-Wide Tables

wp_blogs

The wp_blogs table stores information about each site created on the Multisite network. Table 13-1 shows the structure of the wp_blogs table.

Table 13-1. DB schema for wp_blogs table

Column

Type

Collation

Null

Default

Extra

blog_id

bigint(20)

 

No

None

AUTO_INCREMENT

site_id

bigint(20)

 

No

0

 

domain

varchar(200)

utf8_general_ci

No

   

path

varchar(100)

utf8_general_ci

No

   

registered

datetime

 

No

0000-00-00 00:00:00

 

last_updated

datetime

 

No

0000-00-00 00:00:00

 

public

tinyint(2)

 

No

1

 

archived

enum(01)

utf8_general_ci

No

0

 

mature

tinyint(2)

 

No

0

 

spam

tinyint(2)

 

No

0

 

deleted

tinyint(2)

 

No

0

 

lang_id

int(11)

 

No

0

 

wp_blog_versions

The wp_blog_versions table stores which database schema each site on the network is using. Table 13-2 shows the structure of the wp_blog_versions table.

Table 13-2. DB schema for wp_blog_versions table

Column

Type

Collation

Null

Default

Extra

blog_id

bigint(20)

 

No

0

 

db_version

varchar(20)

utf8_general_ci

No

   

last_updated

datetime

 

No

0000-00-00 00:00:00

 

wp_registration_log

The wp_registration_log table stores information about each user that registers on your network like user ID, email address, IP address, and blog ID. Table 13-3 shows the structure of the wp_registration_log table.

Table 13-3. DB schema for wp_registration_log table

Column

Type

Collation

Null

Default

Extra

ID

bigint(20)

 

No

None

AUTO_INCREMENT

email

varchar(255)

utf8_general_ci

No

   

IP

varchar(30)

utf8_general_ci

No

   

blog_id

bigint(20)

 

No

0

 

date_registered

datetime

 

No

0000-00-00 00:00:00

 

wp_signups

The wp_signups table also stores information about each user that registers on your network. Table 13-4 shows the structure of the wp_signups table.

Table 13-4. DB schema for the wp_signups table

Column

Type

Collation

Null

Default

Extra

domain

varchar(200)

utf8_general_ci

No

   

path

varchar(100)

utf8_general_ci

No

   

title

longtext

utf8_general_ci

No

None

 

user_login

varchar(60)

utf8_general_ci

No

   

user_email

varchar(100)

utf8_general_ci

No

   

registered

datetime

 

No

0000-00-00 00:00:00

 

activated

datetime

 

No

0000-00-00 00:00:00

 

active

tinyint(1)

 

No

0

 

activation_key

varchar(50)

utf8_general_ci

No

   

meta

longtext

utf8_general_ci

Yes

NULL

 

wp_site

The wp_site table stores basic information about your Multisite network like the ID, domain, and path. This table will usually only ever have one record in it. Table 13-5 shows the structure of the wp_site table.

Table 13-5. DB schema for wp_site table

Column

Type

Collation

Null

Default

Extra

id

bigint(20)

 

No

None

AUTO_INCREMENT

domain

varchar(200)

utf8_general_ci

No

   

path

varchar(100)

utf8_general_ci

No

   

wp_sitemeta

The wp_sitemeta table stores all of the network-wide options or settings. Table 13-6 shows the structure of the wp_sitemeta table.

Table 13-6. DB schema for wp_sitemeta table

Column

Type

Collation

Null

Default

Extra

meta_id

bigint(20)

 

No

None

AUTO_INCREMENT

site_id

bigint(20)

 

No

0

 

meta_key

varchar(255)

utf8_general_ci

Yes

NULL

 

meta_value

longtext

utf8_general_ci

Yes

NULL

 

Individual Site Tables

Every site added to your network is automatically given a blog_id when it is created. Each site also creates its own tables, adding its blog_id to each table name. Let’s say we are creating the first additional site on our network besides our main site. It would be given a blog_id of 2, and the following tables would be created in the database:

§  wp_$blog_id_options

§  wp_$blog_id_posts

§  wp_$blog_id_postmeta

§  wp_$blog_id_comments

§  wp_$blog_id_commentsmeta

§  wp_$blog_id_links

§  wp_$blog_id_term_taxonomy

§  wp_$blog_id_terms

§  wp_$blog_id_term_relationships

As you can see, these are the same exact tables included in a standard install of WordPress, except they have a blog_id in the name. For every new site you create on your Multisite network, these tables will be duplicated with that new site’s blog_id.

Shared Site Tables

All users on your Multisite network share the same wp_users and wp_usermeta tables.

Users are associated with various sites on the network by a few user meta keys in the wp_usermeta table. If we added a new user to our second site on the network, these meta keys would be created:

§  primary_blog

§  wp_2_capabilities

§  wp_2_user_level

A user can only have one primary_blog but can be tied to multiple sites with the capabilities and user_level meta keys. In a default install of WordPress and on the top-level site on a Multisite network, these meta keys are stored as wp_capabilities and wp_user_level. When you add users to sites on the network, new meta key records are created for each user for the blog_id you are adding them to with whatever role you added them as.

Multisite Plugins

If a regular WordPress plugin is built correctly, then it should work the way it was intended on one or more sites on your network. Developers can also build WordPress plugins specifically for Multisite. The following are a few of the more popular Multisite plugins and what they are built to accomplish.

WordPress MU Domain Mapping

This plugin allows you to map your blog or site on the Multisite network to an external domain. This does require manual installation, and complete instructions can be found with the plugin.

Blog Copier

This plugin is very useful for anyone who needs to duplicate the content from one site for another site on the network. However, it only allows copying from one subsite to another subsite and does not allow you to copy the top-level site.

More Privacy Options

Once installed, this plugin adds additional levels of privacy to the Reading settings. These new levels are Network Users Only, Blog Members Only, and Admins Only, which makes your site visible only to whichever of these groups you choose.

Multisite Global Search

This allows you to search across the multiple sites on your network and receive results from all those sites. This plugin also comes with a built-in widget that can be used to display the search bar in the sidebar. Both the widget and the results page come with a customizable stylesheet. The plugin uses shortcodes, enabling you to insert the search in any templates you choose.

Multisite Robots.txt Manager

This plugin allows you to create custom robots.txt files for each website on the network and then quickly publish those files to the network or a website. This plugin will also instantly add sitemap URLs to all the robots.txt files. It will also automatically detect 404 or old robots.txt files and allows for easy correction once identified.

Basic Multisite Functionality

When you activate WordPress Multisite, you can utilize Multisite-specific functionality that was sitting there dormant in WordPress core just waiting to be used.

$blog_id

After reviewing the tables Multisite creates, we know that each site has a unique blog_id. You can use this ID to tell WordPress what site you want to retrieve data from or push data to.

The global variable $blog_id will automatically be set to the site you are on unless changed with the switch_to_blog() function. This variable will be useful when writing custom Multisite functionality:

<?php

function wds_show_blog_id(){

    global $blog_id;

        echo 'current site id: ' . $blog_id;

}

add_action( 'init', 'wds_show_blog_id' );

?>

If you are on the top-level site or original site on your network, you should see 1. If you are on the second site you created on your network, you should see 2.

is_multisite()

This function checks to see if WordPress Multisite is enabled. You should only run Multisite-specific functionality if you are running Multisite. If you are not running Multisite and try to use a Multisite function, you may get an error. Always do a check to see if Multisite is enabled before executing any Multisite-specific code:

<?php

function wds_run_multisite_functions(){

        if ( is_multisite() )

                echo 'Run whatever WordPress Multisite functionality you want!';

}

add_action( 'init' , 'wds_run_multisite_functions' );

?>

get_current_blog_id()

This function returns the blog_id that your are currently on. The function itself is literally two lines of code:

<?php

// core function get_current_blog_id

function get_current_blog_id() {

        global $blog_id;

        return absint($blog_id);

}

?>

get_current_blog_id() is located in wp-includes/load.php.

switch_to_blog( $new_blog )

This function switches the current blog to any blog you specify. This function is useful if you need to pull posts or other information from other sites on your network. You can switch back afterward using restore_current_blog(). Autoloaded options and plugins are not switched with this function. This function accepts one parameter, $new_blog, which is a required integer of the ID of the site to which you want to switch.

If we wanted to switch the current site we are on, we could run the following code in any plugin function or theme file:

<?php

echo 'current site id: ' . get_current_blog_id() . '<br>';

switch_to_blog(2);

echo 'new current site id: ' . get_current_blog_id();

?>

The code should output something like:

current site id: 1

new current site id: 2

switch_to_blog() is located in wp-includes/ms-blogs.php.

restore_current_blog()

With this function we can restore the current site, after calling the switch_to_blog() function. This function doesn’t accept any parameters.

If we wanted to restore a switched site, we could run the following code:

<?php

echo 'current site id: ' . get_current_blog_id() . '<br>';

switch_to_blog(2);

echo 'new current site id: ' . get_current_blog_id() . '<br>';

restore_current_blog();

echo 'original site id: ' . get_current_blog_id();

?>

The code should output something like:

current site id: 1

new current site id: 2

original site id: 1

restore_current_blog() is located in wp-includes/ms-blogs.php.

get_blog_details( $fields = null, $get_all = true )

This function gets all of the available details of a site/blog and accepts two parameters:

§  $fields—The ID or name of a specific blog, or an array of blog IDs or blog names. Defaults to the current blog ID.

§  $getall—Default is set to true to return all available data in the object.

This function returns an object of the following variables:

§  blog_id—The ID of the blog being queried.

§  site_id—The ID of the site this blog ID is attached to.

§  domain—The domain used to access the blog.

§  path—The path used to access the site.

§  registered—Timestamp of when the blog was registered.

§  last_updated—Timestamp of when the blog was last updated.

§  public—1 or 0 indicating whether the blog is public or not.

§  archived—1 or 0 indicating whether the blog is achieved or not.

§  mature—1 or 0 indicating whether the blog has adult content or not.

§  spam—1 or 0 indicating whether the blog has been marked as spam or not.

§  deleted—1 or 0 indicating whether the blog has been deleted or not.

§  lang_id—ID of the language the blog is written in.

§  blogname—The name of the blog.

§  siteurl—The URL of the site the blog belongs to.

§  post_content—The number of posts in the blog.

If we wanted to display the entire object returned by this function, we would run the following code:

<?php

$details = get_blog_details( 1 );

echo '<pre>';

print_r($details);

echo '</pre>';

echo 'Site URL:' . $details->siteurl;

echo 'Post Count:' . $details->post_count;

?>

The code should return a similar object and string:

stdClass Object

(

    [blog_id] => 1

    [site_id] => 1

    [domain] => schoolpress.me

    [path] => /

    [registered] => 2013-03-01 00:23:26

    [last_updated] => 2013-04-01 14:18:59

    [public] => 1

    [archived] => 0

    [mature] => 0

    [spam] => 0

    [deleted] => 0

    [lang_id] => 0

    [blogname] => School Press

    [siteurl] => http://schoolpress.me

    [post_count] => 10

)

This site URL is http://schoolpress.me and has 10 posts.

get_blog_details() is located in wp-includes/ms-blogs.php.

update_blog_details( $blog_id, $details = array() )

This function updates the details for a blog and accepts two parameters:

§  $blog_id—A required integer of the ID of the blog you want to update.

§  $details—A required array of any of the fields from the blog’s table as keys with any values you want to update.

If we wanted to mark a particular site as deleted, we could run the following code:

<?php

update_blog_details( 2, array( 'deleted' => '0' ) );

?>

update_blog_details() is located in wp-includes/ms-blogs.php.

get_blog_status( $id, $pref )

This function is similar to the get_blog_details() function, except instead of returning an object of all of the fields in the wp_blogs table, it returns the value of one specific field:

§  $id—A required integer of the ID of the site you want to return a wp_blogs field from.

§  $pref—A required string of the field name from the wp_blogs table.

If we wanted to show when the current site was registered, we could run the following code:

<?php

echo get_blog_status( get_current_blog_id(), 'registered' );

?>

get_blog_status() is located in wp-includes/ms-blogs.php.

update_blog_status( $blog_id, $pref, $value )

This function is similar to the update_blog_details() function, except instead of updating an array of fields in the wp_blogs table, it updates one specific field:

§  $blog_id—A required integer of the ID of the site you want to update a wp_blogs field for.

§  $pref—A required string of the field name from the wp_blogs table you want to update.

§  $value—A required string of the field value you want to update.

If we wanted to mark the current site as deleted, we could run the following code:

<?php

update_blog_status( get_current_blog_id(), 'deleted', '1' );

?>

update_blog_status() is located in wp-includes/ms-blogs.php.

get_blog_option( $id, $option, $default = false )

This function saves you the hassle of using switch_to_blog() and then using the regular WordPress get_option() function or writing a custom SQL query if you wanted to grab an option from a specific site. This function will return an option value from any site on your network by passing in the following parameters:

§  $id—A required integer of the ID of the site you want to get an option from. You can pass in null if you want to get an option from the current site.

§  $option—A required string of the option name you want to get.

§  $default—Optional string to return if the function does not find a matching option.

If we wanted to get the admin_email of a particular site, we could run the following code:

<?php

echo 'The admin email for site id 2 is ' . get_blog_option( 2, 'admin_email' );

?>

get_blog_option() is located in wp-includes/ms-blogs.php.

update_blog_option( $id, $option, $value )

This function updates any option for a particular site and accepts three parameters:

§  $id—A required integer of the ID of the site you want to update an option on.

§  $option—A required string of the option name you want to update.

§  $value—A required string of the option value you want to update.

If we wanted to update the admin_email of a particular site, we could run the following code:

<?php

update_blog_option( 2, 'admin_email', 'brian@webdevstudios.com' );

?>

update_blog_option() is located in wp-includes/ms-blogs.php.

delete_blog_option( $id, $option )

This function deletes any option from a particular site and accepts two parameters:

§  $id—A required integer of the ID of the site you want to delete an option on.

§  $option—A required string of the option name you want to delete.

If we wanted to delete a custom site option from a particular site, we could run the following code:

<?php

delete_blog_option( 2, 'wds_custom_option' );

?>

delete_blog_option() is located in wp-includes/ms-blogs.php.

get_blog_post( $blog_id, $post_id )

This function gets a post from any site on the network and accepts two parameters:

§  $blog_id—A required integer of the blog ID of the site you want to get a post from.

§  $post_id—A required integer of the post ID of the post that you want to get.

If we wanted to get the post title of the third post from the second site on our network, we could run the following code:

<?php

$post = get_blog_post( 2, 3 );

echo $post->post_title;

?>

get_blog_post() is located in wp-includes/ms-functions.php.

add_user_to_blog( $blog_id, $user_id, $role )

This function adds a user to any site on the network with a specified user role and accepts three parameters:

§  $blog_id—A required integer of the blog ID of the site you want to add the user to.

§  $user_id—A required integer of the user ID of the user that you want to add to the site.

§  $role—A required string of the role you want the user to have.

This function will return true if a user was added successfully; and if not, it will return a WP_Error.

If we wanted to add a specific user to the second site on our network with a role of Administrator, we could run the following code:

<?php

add_user_to_blog( 2, 5, 'administrator' );

?>

add_user_to_blog() is located in wp-includes/ms-functions.php.

create_empty_blog( $domain, $path, $weblog_title, $site_id = 1 )

This function creates a new site on the network after making sure it doesn’t already exist. The UI for adding new sites in the network admin uses this function. This function accepts four parameters:

§  $domain—A required string of the domain of the new blog.

§  $path—A required string of the path of the new blog.

§  $weblog_title—A required string of the title or name of the new blog.

§  $site_id—An optional integer of the site ID associated with the new blog. The default is 1.

If we wanted to add a new site to our network we could run the following code:

<?php

create_empty_blog( 'someteacher.schoolpress.me', '/', 'Mr. Some Teacher' );

?>

create_empty_blog() is located in wp-includes/ms-functions.php.

Functions We Didn’t Mention

We didn’t cover all of the Multisite functions available, but we did cover most of the important ones. Well I guess that depends on what you are trying to accomplish. To find all of the available Multisite functions, look in the code! You can find WordPress Multisite functions in the following files:

§  wp-admin/includes/ms.php

§  wp-includes/ms-blogs.php

§  wp-includes/ms-functions.php