Shiba

Adventures in WordPress

  • — Home —
  • Dog
  • Art
  • Contact
  • WordPress Articles
    • WP Plugins
    • WP Programming
    • WP Admin Panels
    • WP Theme Design
    • WP How-To
    • WP Theme Images
You are here: Home / WordPress Programming / Write a Plugin for WordPress Multi-Site

Write a Plugin for WordPress Multi-Site

Tweet

by ShibaShake 45 Comments

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

  • Multi-site administration functions.
  • Multi-site WordPress API.
  • WordPress database object.

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.

Related Articles

Move an Existing Blog into WordPress Multi-Site

How I moved my WordPress blog from a single-site to a multi-site setup.

Add Term or Taxonomy Meta Data

How to add metadata to custom taxonomy objects such as tags and categories. In particular we examine add_metadata, update_metadata, delete_metadata, and get_metadata.

How to Add Admin Columns in WordPress

Many of the key administration areas within WordPress displays a table containing a list of objects and some of their attributes. We describe how to modify these administration tables.

Comments

  1. Andrew says

    April 3, 2020 at 9:58 am

    Hi,

    action ‘wpmu_new_blog’ has been deprecated. Use โ€˜wp_insert_siteโ€™ instead.

    Reply
  2. An Quach says

    December 18, 2017 at 7:42 pm

    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?

    Reply
  3. Jacco says

    December 17, 2016 at 8:17 am

    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???

    Reply
  4. Nadeem says

    May 7, 2015 at 2:43 am

    Nice article. its very helpful for plugins to compatible for multisites … thanks alot

    Reply
  5. Paal Joachim says

    February 4, 2015 at 7:40 pm

    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!

    Reply
  6. jacco says

    June 2, 2014 at 3:31 pm

    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?

    Reply
    • ShibaShake says

      June 2, 2014 at 5:45 pm

      It comes from WP core. I.e., the WP core code calls this function and passes in the proper networkwide value.

      Reply
  7. Jerome Gentolia says

    May 1, 2014 at 2:28 pm

    Can you do a multi-theme for multisites?

    Reply
  8. Eberton says

    April 18, 2013 at 4:02 pm

    Hello, anyone can help me with this plugin:
    http://wordpress.org/extend/plugins/simple-ajax-shoutbox/

    She work fine in a one wordpress, but broken with multisite.
    I have try using the article, but no sucess…. :/

    Reply
  9. Franky says

    April 10, 2013 at 3:07 pm

    Hi,

    this statement:

    $blogids = $wpdb->get_col($wpdb->prepare(“SELECT blog_id FROM $wpdb->blogs”));

    causes WP warnings (prepare statements need 2 arguments) since WP 3.5, and since the prepare statement doesn’t use correct backticks for column names, you should use this:

    $blogids = $wpdb->get_col(“SELECT blog_id FROM “.$wpdb->blogs);

    Franky

    Reply
    • ShibaShake says

      April 10, 2013 at 9:39 pm

      Updated! Thanks.

      Reply
  10. Diego says

    December 9, 2012 at 8:45 am

    Hi all,

    I am following this post instructions, to create a plugin. My plugin uses a php script, and the $wpdb object as described in this post, to delete all posts of the current site, and create a new post.

    It works with the main site, but when run from other sub-sites dashboards, it deletes and creates post in main site!! It seems the $wpdb always refers to main site.

    Just to debug, I tried putting this in my Php script:

    global $wpdb
    echo $wpdb->blogid;

    and it always returns 1!! Although this script is triggered from a subsite dashboard button. But the script is located under “plugings/” folder.

    So, my question is, how the wpdb object instantiation knows which subsite it must gater its data from?…

    Any answer will be appreciated, this is making me crazy!…

    Reply
  11. Lorna says

    November 8, 2012 at 1:07 am

    Argh, I can’t find any Twitter / Facebook share button to bookmark this post! I’m kind of desperately need to make my plugins work in a multi-site environment, so hopefully you make this a living post so that I can follow all updates about your MU findings.

    Reply
  12. Devtard says

    September 20, 2012 at 6:39 am

    Thanks for the line with the collation check. ๐Ÿ™‚

    Reply
  13. Ian Dunn says

    June 16, 2012 at 2:59 pm

    It looks like there are changes to network-wide activation in WP 3.4 that break your activation/deactivation functions.

    Reply
    • ShibaShake says

      July 25, 2012 at 3:44 pm

      That is very useful. Thanks for letting me know!

      Reply
  14. Nando says

    June 14, 2012 at 9:46 am

    Great info, specially on the activation/deactivation process.

    Reply
  15. Laura says

    May 29, 2012 at 6:37 am

    Thanks Shiba, your tutorial has given me a lot of insight on what I need to do now to make my plugin multisite capable. Great job as usual!

    Reply
  16. TheCrowned says

    September 5, 2011 at 4:45 am

    You cannot imagine how valuable your information has been to me.

    Just a brief correction to notice that the uninstall procedure is slightly different from the deactivation one. I’m using something like the following:


    //If working on a multisite blog
    if ( function_exists( 'is_multisite' ) AND is_multisite() ) {

    //Get all blog ids; foreach them and call the uninstall procedure on each of them
    $blog_ids = $wpdb->get_col($wpdb->prepare("SELECT blog_id FROM ".$wpdb->blogs));

    //Get all blog ids; foreach them and call the install procedure on each of them if the plugin table is found
    foreach ( $blog_ids as $blog_id ) {
    switch_to_blog( $blog_id );

    if( $wpdb->query( 'SHOW TABLES FROM '.$wpdb->dbname.' LIKE "'.$wpdb->prefix.'post_pay_counter"' ) )
    post_pay_counter_uninstall_procedure();

    }

    //Go back to the main blog and return - so that if not multisite or not network activation, run the procedure once
    restore_current_blog();
    return;
    }
    post_pay_counter_uninstall_procedure();

    Sorry for the horrible identation ๐Ÿ˜‰

    Reply
  17. Saurabh Shukla says

    July 7, 2011 at 10:55 am

    Hi,

    I was trying to write a function that tries to find blog posts by slug(post_name) in a blog in the network. Any help how I can access the database exclusively for a particular blog after I’ve switched to it?

    Reply
    • ShibaShake says

      July 10, 2011 at 2:50 pm

      Once you have switched to a blog, you can use the $wpdb global to access the blog database.

      Reply
  18. Ian Dunn says

    July 2, 2011 at 12:35 am

    Instead of capturing the original blog ID and then switching back to it after you’re done looping through the blogs, you can just call restore_current_blog() at the end.

    Reply
  19. Jesse says

    June 24, 2011 at 2:06 pm

    Thanks a ton for posting this code. I have one question about the use of $this, as in $this->_shiba_activate(). It is throwing an error for me: “Using $this when not in object context”.

    Am I missing something? Is $this needed?

    Reply
    • ShibaShake says

      June 24, 2011 at 3:50 pm

      I usually encapsulate all of my functions within a PHP class. This ensures that my function names do not clash with native WP function names or the function names of other plugins. I use $this to call functions from within the same class.

      I try to remove all of that in my tutorials so that it is easier to read, but sometimes I miss a few. Thanks for pointing it out. I will remove the ones on this article.

      Reply
  20. Daniel Chatfield says

    June 15, 2011 at 5:32 am

    Is there a global settings database that can be accessed from all sites? I need the network admin to be able to override settings.

    Reply
    • ShibaShake says

      June 16, 2011 at 1:12 pm

      I don’t know of a global options table, but perhaps you can use one of the existing global multisite tables to store the necessary data.

      Reply
  21. ray says

    June 2, 2011 at 12:54 pm

    Awesome article! Thanks so much

    Reply
  22. Ivan says

    April 14, 2011 at 2:54 am

    You are my hero!
    It is amazing post! Thank you so much for this information – it really save my time.

    Reply
  23. Michel Carroll says

    April 2, 2011 at 8:36 am

    Thank you very much Shiba.

    You’ve saved me lots of digging around.

    Reply
    • ShibaShake says

      April 2, 2011 at 12:14 pm

      Glad it helped. ๐Ÿ˜€

      Reply
  24. isogashii says

    January 27, 2011 at 1:45 am

    this is just cool article, I’ve never worked on WordPress Multisite, the name just keeps me away. Now, is the time to tackle it,
    Thank you, keep up the great job ๐Ÿ˜‰

    Reply
  25. Thomas says

    January 17, 2011 at 2:10 am

    Thanks a lot for this post, it help a lot !

    Reply
  26. XYDAC says

    October 22, 2010 at 12:26 pm

    Excellent writeup.
    Nice Work.:)

    Reply
  27. larry says

    August 17, 2010 at 6:53 am

    VERY NICE!!!!!!!
    thanks!

    Reply
  28. arifeen says

    August 5, 2010 at 8:44 am

    thanks for this amazing post, i tried writing a plugin for my wordpress 3.0 network as MU is not available anymore, but it gave me the “Cannot redeclare” error, any ideas???

    Reply
    • ShibaShake says

      August 6, 2010 at 8:05 am

      My guess is that you got a WP_Error somewhere and are trying to use it as some other object.

      Reply
  29. Jeff says

    July 26, 2010 at 1:51 pm

    Thanks SO much for this amazing set of code snippets. I’ve spent an entire day looking for this information. Nobody has written a comprehensive guide on writing plugins that are Multi-site aware.

    You’ve saved me hours of playing around!

    Reply

Leave a Reply to Franky Cancel reply

Your email address will not be published.

Recent Posts

  • Screen-shot of mobile responsive Poll Daddy object, where text floats properly to the right of radio buttons.How to Make Poll Daddy Objects Mobile Responsive
  • Screen-shot of blog post with no page border (flowing design).Genesis Skins 1.5
  • Screen-shot of the media manager Create-Gallery screen, while doing a post search.Shiba Media Library 3.7
  • Screenshot of the Edit Gallery screen after we hit the Create Gallery button.How to Expand the WordPress Media Manager Interface
  • Blonde girl looking through and holding a circular picture frame.Shiba Gallery 4.3
  • Close-up of beautiful blonde holding a square picture frame.Google Authorship - Good or Bad for Search Traffic?
  • Shiba Widgets 2.0
  • Media Temple screenshot of editing my sub-domain DNS entry.Using CDN Cnames with w3tc and MultiSite
  • Shiba Skins WordPress ThemeShiba Skins WordPress Theme
  • How to add the Media Manager Menu to the Theme Preview InterfaceHow to Add the Media Manager Menu to the Theme Preview Interface

Recent Comments

  • WordPress Search Widget – How to Style It (56)
    • Nelson
      - Tanks master - Fine
    • TelFiRE
      - How do you style the "X" that shows up when you start typing?
  • Update custom inputs with the proper data using Javascript.Expand the WordPress Quick Edit Menu (58)
    • Mike G
      - This is exactly what is happening to me. It is updating the value in the database and in the column, but I have to refresh ...
    • PhoenixJP
      - Thanks for this tutorial. Does someone knows if this still work with wordpress 5.03 and 5.1.
    • Francine Carrel
      - This is a very long time away from your original comment, but did you ever work it out? I am stuck on the exact same thing ...
  • Custom meta-box with a set of radio-buttons.Add a Metabox to Your Custom Post Type Screen (27)
    • mike
      - Hi Shiba am a newbie to wordpress, I just installed a plugin with a custom post type, but has no option for discussion and ...
  • Write a Plugin for WordPress Multi-Site (45)
    • Andrew
      - Hi,action 'wpmu_new_blog' has been deprecated. Use โ€˜wp_insert_siteโ€™ instead.
  • Populate our Edit Gallery menu using a gallery shortcode.How to Add the WordPress 3.5 Media Manager Interface – Part 2 (29)
    • Janine
      - Still relevant in 2019.
  • WordPress Excerpt – How to Style It (36)
    • James
      - Great post. I really need some help. I have set border lines around my excerpts on my blog page/post page. The problem is ...
  • Add Custom Taxonomy Tags to Your WordPress Permalinks (123)
    • Darshan Saroya
      - Update permalinks. Go to settings > permalink and just save it.

Copyright © 2021 · Genesis Skins by ShibaShake · Terms of Service · Privacy Policy ·