• Write a Plugin for WordPress Multi-Site
    by ShibaShake on

    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($wpdb->prepare("SELECT COUNT(*) FROM wp_posts"));

    Instead, use the posts table name contained within the $wpdb global object -

    $post_count = $wpdb->get_var($wpdb->prepare("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.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    
    // 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.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    
    register_activation_hook( __FILE__, 'shiba_activate' );
    function shiba_activate() {
    	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 (isset($_GET['networkwide']) && ($_GET['networkwide'] == 1)) {
    	                $old_blog = $wpdb->blogid;
    			// Get all blog ids
    			$blogids = $wpdb->get_col($wpdb->prepare("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.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    
    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.

    register_deactivation_hook( __FILE__, 'shiba_deactivate' );
    function shiba_deactivate() {
    	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 (isset($_GET['networkwide']) && ($_GET['networkwide'] == 1)) {
    			$old_blog = $wpdb->blogid;
    			// Get all blog ids
    			$blogids = $wpdb->get_col($wpdb->prepare("SELECT blog_id FROM $wpdb->blogs"));
    			foreach ($blogids as $blog_id) {
    				switch_to_blog($blog_id);
    				_shiba_deactivate();
    			}
    			switch_to_blog($old_blog);
    			return;
    		}	
    	} 
    	_shiba_deactivate();		
    }

    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. Thanks!

    Related Articles

    26 Comments
    1. [...] it easy for plugin authors to make their plugins WordPress multi-site compatible. It is based on the excellent code Shibashake made available for doing this. I took that articles’s 3 separate (but almost identicalr) functions for activating, [...]

      6:26 pm on February 16th, 2012 Reply
    2. [...] WordPress MU Plugins [...]

      8:03 pm on January 16th, 2012 Reply
    3. 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 ;)

      4:45 am on September 5th, 2011 Reply
    4. [...] = new nggRewrite();   }         // THX to Shiba for the code     // See: http://shibashake.com/wordpress-theme/write-a-plugin-for-wordpress-multi-site     function multisite_new_blog($blog_id, $user_id, $domain, $path, $site_id, $meta ) {     [...]

      8:21 am on August 6th, 2011 Reply
    5. [...] Write a Plugin for WordPress Multi-Site [...]

      1:12 pm on July 27th, 2011 Reply
    6. 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?

      10:55 am on July 7th, 2011 Reply
      • Once you have switched to a blog, you can use the $wpdb global to access the blog database.

        2:50 pm on July 10th, 2011 Reply
    7. 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.

      12:35 am on July 2nd, 2011 Reply
    8. Jesse

      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?

      2:06 pm on June 24th, 2011 Reply
      • 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.

        3:50 pm on June 24th, 2011 Reply
    9. Is there a global settings database that can be accessed from all sites? I need the network admin to be able to override settings.

      5:32 am on June 15th, 2011 Reply
      • 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.

        1:12 pm on June 16th, 2011 Reply
    10. ray

      Awesome article! Thanks so much

      12:54 pm on June 2nd, 2011 Reply
    11. [...] 24. Write a Plugin for WordPress Multi-Site [...]

      3:28 pm on May 12th, 2011 Reply
    12. You are my hero!
      It is amazing post! Thank you so much for this information – it really save my time.

      2:54 am on April 14th, 2011 Reply
    13. Thank you very much Shiba.

      You’ve saved me lots of digging around.

      8:36 am on April 2nd, 2011 Reply
    14. [...] posted here: Write a Plugin for WordPress Multi-Site Posted in WordPress Tags: development, plugin, plugins, WordPress « Most Incredible Free [...]

      9:39 am on March 2nd, 2011 Reply
    15. [...] for database access and the $wpdb->prefix property. It’s all explained in the Codex and in this blog post.The above blog post also has some useful examples for dealing with network activation and [...]

      7:24 am on February 22nd, 2011 Reply
    16. 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 ;)

      1:45 am on January 27th, 2011 Reply
    17. Thanks a lot for this post, it help a lot !

      2:10 am on January 17th, 2011 Reply
    18. Excellent writeup.
      Nice Work.:)

      12:26 pm on October 22nd, 2010 Reply
    19. larry

      VERY NICE!!!!!!!
      thanks!

      6:53 am on August 17th, 2010 Reply
    20. arifeen

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

      8:44 am on August 5th, 2010 Reply
      • My guess is that you got a WP_Error somewhere and are trying to use it as some other object.

        8:05 am on August 6th, 2010 Reply
    21. 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!

      1:51 pm on July 26th, 2010 Reply

    Leave a Reply

    Your email address will not be published.

    You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="" highlight="">

    search button search button
    rss