# Making your plugin routines multisite-compatible

If you've been getting your way around with WordPress, you have probably heard of that thing called [Multisite](https://codex.wordpress.org/Create_A_Network). Multiple web sites in one WordPress installation, that is. You may also call it a network of sites. If you haven't actually used it, that's another issue - maybe you have not (yet) come across a project where Multisite would have been the right fit. (In any case, I would encourage you to try it out on your dev environment then.) This post is not about Multisite though. It's about how you can make your regular plugin that you would like to write or might have written years ago compatible with Multisite. Because even if your plugin does not do anything related to Multisite in any way, there are some things to take care of, in particular you need to take care of your plugin's activation / deactivation / uninstallation routines (if you have something like it in your plugin). Otherwise you are locking out some users from using your plugin, and you certainly don't want that, I'm sure. Now that you have read this, please don't run away, it's not something you need to spend days for - it might only take a few minutes, and if you don't have any of these routines, there actually *is* nothing else to do to make the plugin compatible (at least not for the scope of this tutorial). But now, let's get started!

## Setup your installation routine

 First of all, note that "installation" here is synonymous with "activation". As you might already know, when you want to install your plugin in WordPress, you use the activation routine for that. You probably run your plugin's installation/activation process inside a function which you have passed to `register_activation_hook()`. Let's imagine your plugin needs a custom database table which needs to be setup when the plugin is activated for the first time. The code for it could look something like this: https://gist.github.com/e571f481f90a468e21f01037bf8ffe80 This works perfectly fine on a regular site, but you are likely to run into problems on a Multisite. That is because on a Multisite it is possible to network activate a plugin, meaning it will be activated on all sites in the network at once. So how do you as a plugin developer know when a plugin is being activated for an entire network? Luckily, there is parameter `$network_wide` which is automatically passed to your callback function (in the above case to `myplugin_install()` as the first (and only) parameter. This variable is a boolean and will be true if the plugin is being activated for the entire Multisite. What you need to do in your plugin now is check whether the parameter is true, and if so, not only run the above installation routine once, but for all sites in your network instead. To prepare the installation routine for that, let's outsource the relevant parts that will need to be executed for every site into a separate function. In the below example, that function is called `myplugin_install_single_site()`. (Note that I left out the `myplugin_register_table()` function below.) https://gist.github.com/495f1d530e493deb28528f338e90c2fa As you can see, all that has changed is that almost everything from the `myplugin_install()` was moved to the new `myplugin_install_single_site()` function. The only exception is the `myplugin_register_table()` call because that is something that only needs to happen once. Be aware that from now on, the `myplugin_install_single_site()` will no longer be included in the following code snippets as that will not change. Okay, we'll get to the next code snippet straight away. In the following snippet, we make use of the `$network_wide` parameter to check whether the plugin is being activated as usual or for the entire Multisite. If it should be installed for the entire network, we will query all sites in the network, iterate through them (using the function `switch_to_blog()`) and call our `myplugin_install_single_site()` for each site. If it's just a regular activation, we simply call `myplugin_install_single_site` once. https://gist.github.com/fcbb5dd33c2a3dc416a7d991cc8ac5bd In the SQL query above you might be confused about reading the terms "Blog" and "Site" there. These are the old naming conventions for Multisite: If I had written this tutorial years ago, I wouldn't have told you about "Networks of Sites", but about "Sites of Blogs". Yes, the old naming conventions probably don't make any sense to you as well. That's why they were changed. However, due to WordPress being committed to backwards-compatibility, the old terminology is still widely used in Core (in particular the database tables use the old conventions). To sum this up, in the SQL query `$wpdb->blogs` is the database table for sites, `blog_id` is the ID of a site, and `site_id` is the ID of a network. Furthermore, the functions `switch_to_blog()` and `restore_current_blog()` are actually related to sites as well. I know, it's really confusing, and I wish it would be different. Another thing you should definitely keep in mind is that you should not remove the `WHERE site_id = $wpdb->siteid` bit. While a regular Multisite is only one network of sites, WordPress allows to have multiple networks (I won't address this here, but be aware it can exist). Therefore this check is required - otherwise the plugin would be installed on all sites in all networks which is not what "Network Activate" means. That's basically it for the installation. A minor, but possibly useful hint. In the upcoming WordPress version 4.6 it will be a lot easier to get the site IDs you need in the above code snippet. WordPress will introduce a new function `get_sites()` (yay! This time it uses the current naming conventions) which you can use instead. To be backwards-compatible, this should only happen in a conditional statement though (unless you don't care for users below the latest version). See the snippet below for an example that uses `get_sites()` plus another new function called `get_current_network_id()`, but remains backwards-compatible. https://gist.github.com/10f4e95ee264ccf4bb03d3307204ad34 Now you're really prepared, even for something that will only be available in an upcoming version of WordPress (at this point, WordPress 4.6 is a little more than a month away)! ## Handling deactivation

 The good thing about the above tutorial is that it applies to the deactivation routine of a plugin as well. It's a lot less common to have deactivation routine in a plugin anyway since you shouldn't remove anything from your plugin on deactivation. You might still have one for things like [flushing rewrite rules](https://codex.wordpress.org/Function_Reference/flush_rewrite_rules). In a case like that, it works just like the above. You create a function `myplugin_deactivate_single_site()` and another function `myplugin_deactivate()`. The latter will be passed as a callback to `register_deactivation_hook()` (as the second parameter, just as in the activation hook function). Your function `myplugin_deactivate` will get passed a `$network_wide` parameter to determine whether the plugin is being deactivated network wide or not. Then you can act on it as in the above tutorial. ## Handling uninstallation

 Uninstallation works a little differently. It is not as critical as not removing your plugin's data won't break anything. Still, you should probably do it right as otherwise your plugin's data will remain on the WordPress site as garbage (don't get me wrong, your plugin is not garbage, just its data when it's not being used anymore :) ). For uninstallation, you still need to have a function `myplugin_uninstall_single_site()` and another function `myplugin_uninstall()` and pass the latter to `register_uninstall_hook()`, similar like you have for installation/activation and deactivation. The difference here is that there is no `$network_wide` parameter. Which makes sense because your plugin is being uninstalled when its files are completely deleted from the server. That means that in your code in `myplugin_uninstall()`, you need to check for this another way. A good way to currently do this is simply to check whether the current site is a multisite or not. If it is, you uninstall the plugin for all sites (this time for all sites in all networks, since the plugin is completely removed), otherwise you only uninstall it for the single site. The following snippet provides an example. https://gist.github.com/d397a5bcfb48c54bd929e9329fd5923d Note that in your `myplugin_uninstall_single_site()` function you should check whether the plugin is installed at all before trying to uninstall it, for example by using an option like `myplugin_installed` (which was also used in the first installation code snippets). And that's it - all your plugin's important routines are now compatible with Multisite, and even [Multinetwork](https://wordpress.org/plugins/wp-multi-network/) (that's the thing when there are multiple networks in one WordPress setup). If you're a plugin developer, I strongly encourage you to follow these (or similar) steps if you haven't yet ensured your plugin's installation/deactivation/uninstallation routines are compatible with Multisite. Users will be grateful for it, and your plugin will be a good example (feel free to browse through GitHub to see how many plugins are `_doing_it_wrong()`). And if you see other plugins that use incompatible routines, you might wanna submit a pull-request to them, or point them to this post. :)