WordPress multisite functionality is now integrated into WordPress 3.0. Currently, we must go through a non-trivial process to turn on multisite capability. However, there are already plugins that try to automate this process in one-click. As this process becomes easier and more streamlined, the multisite capability will likely see wider adoption.
Here, we consider how we can make our plugins compatible with WordPress multisite.
WordPress Multi-Site vs WordPress Single-Site
As its name suggests, WordPress multisite allows us to control multiple blogs from a single WordPress dashboard. WordPress.com for example, uses this multisite capability, so it is a great place to visit if you want to quickly test out the multisite interface and functions.
From a plugins perspective, the most important difference between WordPress multisite and WordPress single is when we write data – both writing to the database and writing to local file(s).
In the single WordPress configuration, we usually have an entire database and an entire WordPress installation area dedicated to a single blog. Plugins can be assured that there is only one posts table (wp_posts), one comments table (wp_comments), one links table (wp_links) etc.
Similarly, each plugin has their own file space that need only be dedicated to a single blog. Some plugins may cache files within this space.
In a multisite WordPress configuration, our database will contain tables for all of our blogs. Therefore, we may have multiple posts tables (wp_posts, wp2_posts, wp3_posts) multiple comments tables, multiple links tables, etc.
Furthermore, each blog no longer has its own installation file space. Instead, all blogs share a common plugin file-space.
Some plugins may need to create local pages/files that are specific to a blog (e.g., a caching plugin). In a multisite configuration, it is necessary for these plugins to create separate file areas for each blog and keep track of each of these areas. This results in more record keeping by the plugin application.
WordPress Multisite Plugin – Database Table Names
When writing a WordPress multisite plugin, it is absolutely crucial that you DO NOT make any database calls to hard-coded table names. Instead, always extract the names from the $wpdb global object.
For example, if you need to execute an SQL command on the posts table, then DO NOT use wp_posts as the table name –
$post_count = $wpdb->get_var("SELECT COUNT(*) FROM wp_posts");
Instead, use the posts table name contained within the $wpdb global object –
$post_count = $wpdb->get_var("SELECT COUNT(*) FROM $wpdb->posts");
The $wpdb global object always points to the correct database table names in both the single and multisite WordPress configurations. By tying our plugin to the $wpdb object, we can be assured that our plugin is accessing the right tables for the current blog.
The list of tables pointed to by the $wpdb global object include –
- comments
- commentmeta
- links
- options
- postmeta
- posts
- terms
- term_relationships
- term_taxonomy
Global multisite tables include –
- usermeta
- users
- blogs
- blog_versions
- registration_log
- signups
- site
- sitecategories
WordPress Multisite Plugin – Creating a Table
Sometimes, it is necessary to create our own tables. In this case, we must ensure that we obtain the proper table prefix ($wpdb->prefix). In the example below, we create a term metadata table for our plugin. This metadata table can then be used to store metadata for WordPress taxonomy objects such as tags, categories, or our own custom terms.
// Create a term metadata table where $type = metadata type global $wpdb; $table_name = $wpdb->prefix . $type . 'meta'; if ($wpdb->get_var( "SHOW TABLES LIKE '{$table_name}'") != $table_name) { shiba_create_metadata_table($table_name, $type); } function shiba_create_metadata_table($table_name, $type) { global $wpdb; if (!empty ($wpdb->charset)) $charset_collate = "DEFAULT CHARACTER SET {$wpdb->charset}"; if (!empty ($wpdb->collate)) $charset_collate .= " COLLATE {$wpdb->collate}"; $sql = "CREATE TABLE IF NOT EXISTS {$table_name} ( meta_id bigint(20) NOT NULL AUTO_INCREMENT, {$type}_id bigint(20) NOT NULL default 0, meta_key varchar(255) DEFAULT NULL, meta_value longtext DEFAULT NULL, UNIQUE KEY meta_id (meta_id) ) {$charset_collate};"; require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); dbDelta($sql); }
Line 2 – Set our database table name by pre-pending it with the right table prefix.
Line 3 – Check to see if table already exists. Only create a new table if one does not already exists.
Line 5 – Create our taxonomy metadata table.
WordPress Multisite Plugin – Activation
When a plugin gets activated, we may run certain initialization functions by tying them to the register_activation_hook function. For example, we may create our plugin table during the activation process.
In a single blog configuration, we run our activation function, create a single table, and we are all done. However, in a multisite configuration, we will need to create tables for each blog within our network. Therefore, we need to iterate over each blog and run our plugin activation function for each and every one of them.
register_activation_hook( __FILE__, 'shiba_activate' ); function shiba_activate($networkwide) { global $wpdb; if (function_exists('is_multisite') && is_multisite()) { // check if it is a network activation - if so, run the activation function for each blog id if ($networkwide) { $old_blog = $wpdb->blogid; // Get all blog ids $blogids = $wpdb->get_col("SELECT blog_id FROM $wpdb->blogs"); foreach ($blogids as $blog_id) { switch_to_blog($blog_id); _shiba_activate(); } switch_to_blog($old_blog); return; } } _shiba_activate(); } function _shiba_activate() { // Add initial plugin options here $current_theme = get_current_theme(); add_option('shiba_bg_theme', $current_theme); // Create term metadata table if necessary global $wpdb; $type = 'shiba_term'; $table_name = $wpdb->prefix . $type . 'meta'; if ($wpdb->get_var( "SHOW TABLES LIKE '{$table_name}'") != $table_name) { shiba_create_metadata_table($table_name, $type); } }
Line 5 – Check if multisite capability is on.
Line 7 – Check if plugin has been activated for the entire blog network.
Lines 9-13 – Iterate over each blog within our network, and run our activation function for each blog.
Line 17 – If multisite capability is not on, or if plugin is only activated for a single blog, then just run our activation function once.
WordPress Multisite Plugin – Adding a New Blog
We may also want to run our plugin activation function whenever a new blog is created.
add_action( 'wpmu_new_blog', 'new_blog', 10, 6); function new_blog($blog_id, $user_id, $domain, $path, $site_id, $meta ) { global $wpdb; if (is_plugin_active_for_network('shiba-custom-background/shiba-custom-background.php')) { $old_blog = $wpdb->blogid; switch_to_blog($blog_id); _shiba_activate(); switch_to_blog($old_blog); } }
WordPress Multisite Plugin – Deactivation, and Uninstall
In the same way that we want to initialize each blog when our plugin is network activated, we will also want to clean up every blog when our plugin is network deactivated. We can combine both activate and deactivate functions as follows.
register_activation_hook( __FILE__, 'shiba_activate' ); register_deactivation_hook( __FILE__, 'shiba_deactivate' ); function my_network_propagate($pfunction, $networkwide) { global $wpdb; if (function_exists('is_multisite') && is_multisite()) { // check if it is a network activation - if so, run the activation function // for each blog id if ($networkwide) { $old_blog = $wpdb->blogid; // Get all blog ids $blogids = $wpdb->get_col("SELECT blog_id FROM {$wpdb->blogs}"); foreach ($blogids as $blog_id) { switch_to_blog($blog_id); call_user_func($pfunction, $networkwide); } switch_to_blog($old_blog); return; } } call_user_func($pfunction, $networkwide); } function shiba_activate($networkwide) { my_network_propagate('_my_activate', $networkwide); } function shiba_deactivate($networkwide) { my_network_propagate('_my_deactivate', $networkwide); }
Finally, we just need to repeat our deactivation function when a blog is deleted, and make sure that our uninstall.php file executes clean up operations for all the blogs in our WordPress multisite network. This process is very similar to what we have done above so we will not repeat it again here.
Relevant WordPress Multi-Site Files
A Better Way
I am just starting to play around with WordPress multisite so if you know of a better way to initialize and clean-up plugins for a multisite configuration please let me know.
Many thanks to Ian Dunn for letting us know about the network wide activation updates in WP 3.4.
Andrew says
Hi,
action ‘wpmu_new_blog’ has been deprecated. Use ‘wp_insert_site’ instead.
An Quach says
I always use single site because it’s easy for me to managed. When I need another worpdress on subdomain or subfolder, I just install a new single site for it.
Is it better if I use multi-site wordpress?
Jacco says
I got a tricky question for you… When I write my plugins and use
Include_once(“settings.php”)
or I use
include_once( __FILE__, “settings.php”)
(Wish I was at my computer so I can get the right syntax but hopefully you understand what I’m doing there)
If I do the former and go into my individual sites and activate the plugin, whenever I then open up the multisite dashboard all I see is an error about the user not being found. I have to return to the site dashboard and deactivate the plugin then go back to the network dashboard and activate it from there using netowk activate…only for it to tell me it can’t find the settings file… Thus I change to the latter format for specifying the file’s location and then thhe network activate option works perfectly… But when I open any of my sote’s dashboards it tells me it van’t find the user and thus my entire dashboard is history…
So I go back to my main dashboard, deactivate the plugin, go back to my site dashboard and activate it there…only to find it can’t find the settings.php file…
So what’s the deal here? Hiw do I include a settings file that is in the root of my plugin’s folder in such a way that I can enable the plugin either from my individual site(s) or via network activate without the one destroying the dashboard of the other???
Nadeem says
Nice article. its very helpful for plugins to compatible for multisites … thanks alot
Paal Joachim says
Hey
It seems you can have a lot to offer when the WordPress core project on improving multisite is moving forward at make.wordpress.org/core
Could you perhaps update your article?
Have a great day!
jacco says
Hi there
I am more than slightly confused by this line of code:
function shiba_activate($networkwide)
I can see you registering the function and I can see you using the $networkwide variable but I can’t see $networkwide actually ever being set… Googling around doesn’t tell me anything about the function receiving any parameters when the activation hook runs… so when and where and how does the $networkwide variable get it’s value from?
ShibaShake says
It comes from WP core. I.e., the WP core code calls this function and passes in the proper networkwide value.