Ever wondered how to make your WordPress plugin a bit smarter, adapting its behavior depending on whether it’s active across your whole network or just on a single site? You’ve come to the right place. The core idea is to sniff out the context it’s running in and then apply your logic accordingly. It’s not as complex as it sounds, and we’ll break it down into practical steps.
Before we dive into the code, let’s make sure we’re on the same page about what “network-activated” and “site-activated” actually mean in the wonderful world of WordPress Multisite.
What is Network Activation?
When you network activate a plugin, it’s enabled for every site within your WordPress Multisite installation. Think of it like a global switch. Once flipped, that plugin’s code runs for all blogs, subdomains, or subdirectories under your main installation. This is great for plugins that provide core functionality, security enhancements, or administrative tools that you want consistently available everywhere.
What is Site Activation?
Site activation, on the other hand, means the plugin is enabled for a specific site within your Multisite network. If you have ten sites, you could activate the plugin on just one, or two, or even all ten individually. This is ideal for plugins that add features relevant to only certain types of sites, or if you want site administrators to have control over installing or removing specific functionality without bothering the network admin.
Why Does This Matter for Your Plugin?
The distinction is crucial because the available APIs, the database tables you might access, and even the “current” site can change depending on whether your plugin is network-activated or activated on a single site. For example, if your plugin stores options, where those options are stored (network-wide options table vs. site-specific options table) will differ. Knowing the context allows you to write more robust and flexible code.
If you’re interested in understanding how to build a plugin that behaves differently based on network-activated versus site-activated contexts, you might also find it helpful to explore the intricacies of server management and migration. A related article that delves into this topic is about migrating from one CyberPanel server to another, which can provide insights into managing different environments and configurations. You can read more about it here: Migrating to Another CyberPanel Server.
Detecting the Activation Context
The first and most important step is figuring out where your plugin is activated. WordPress provides some neat global functions and constants that make this surprisingly easy.
The is_multisite() Function
This is your most basic check. is_multisite() will return true if your WordPress installation is a Multisite, and false otherwise. This doesn’t tell you how your plugin is activated, but it’s a necessary first step to even consider network vs. site specifics. If is_multisite() is false, your plugin is, by definition, site-activated (as there’s only one site).
“`php
if ( is_multisite() ) {
// We are in a Multisite environment. Now, let’s dig deeper.
} else {
// This is a single site installation. The plugin is site-activated.
}
“`
Checking for Network-Active Status: is_plugin_active_for_network()
This is the golden ticket. The function is_plugin_active_for_network() checks if a given plugin is active for the entire network. It takes one argument: the plugin’s base file path (e.g., 'your-plugin/your-plugin.php').
“`php
/**
- Assumes ‘your-plugin/your-plugin.php’ is the main plugin file.
- You should replace this with your actual plugin path.
*/
$plugin_file = plugin_basename( __FILE__ );
if ( is_multisite() && is_plugin_active_for_network( $plugin_file ) ) {
// The plugin is network-activated.
} else {
// The plugin is site-activated (either because it’s a single site,
// or it’s a Multisite but activated only on the current site).
}
“`
Important Note: The plugin_basename( __FILE__ ) is crucial here. It gives you the path relative to the plugins directory, which is what is_plugin_active_for_network() expects. Always use this in your main plugin file.
Combining Checks for Clarity
For a complete and clear picture, you’ll often combine these checks. Here’s a common pattern:
“`php
$is_multisite_env = is_multisite();
$plugin_base = plugin_basename( __FILE__ );
$is_network_active = false;
if ( $is_multisite_env ) {
$is_network_active = is_plugin_active_for_network( $plugin_base );
}
if ( $is_network_active ) {
// Perform actions specific to network activation.
// E.g., network-wide settings, global database tables.
} elseif ( $is_multisite_env && ! $is_network_active ) {
// Perform actions specific to site activation within a Multisite environment.
// E.g., site-specific settings, individual site database tables.
} else {
// Perform actions for a single site, non-Multisite environment.
// This is essentially the same as the previous block if the plugin is designed only for single sites,
// but useful for distinguishing if you have separate logic.
}
“`
This structure makes it very explicit what context your code is operating in, allowing you to branch your plugin’s behavior intentionally.
Adapting Plugin Behavior Based on Context
Once you’ve identified the activation context, you can start making your plugin smart. This is where the real power comes in.
Handling Settings and Options
This is a very common scenario. Where do you store your plugin’s settings? Network-wide or site-specific?
Network-Wide Settings
If your plugin is network-activated and you want settings that apply to all sites, you should use network-specific options functions.
get_site_option( $option_name, $default ): Retrieves a network option.update_site_option( $option_name, $value ): Updates a network option.add_site_option( $option_name, $value ): Adds a network option (if it doesn’t exist).delete_site_option( $option_name ): Deletes a network option.
These functions store data in the wp_sitemeta table (or whatever your network’s prefix is followed by _sitemeta).
“`php
if ( $is_network_active ) {
// Getting a network-wide setting
$network_setting = get_site_option( ‘my_plugin_network_setting’, ‘default_value’ );
// …
}
“`
Site-Specific Settings
When your plugin is site-activated (or even network-activated, but you want certain settings to be configurable per-site), you use the standard WordPress option functions.
get_option( $option_name, $default ): Retrieves a site option.update_option( $option_name, $value ): Updates a site option.add_option( $option_name, $value ): Adds a site option.delete_option( $option_name ): Deletes a site option.
These functions store data in the wp_options table for the current site.
“`php
if ( ! $is_network_active ) { // Assuming we’re in a site-activated context (or single site env)
// Getting a site-specific setting
$site_setting = get_option( ‘my_plugin_site_setting’, ‘another_default_value’ );
// …
}
“`
Hybrid Approach: Network Defaults, Site Overrides
Sometimes, you want network admins to set a default for everyone, but allow individual site admins to override it. This requires a little more logic.
“`php
$plugin_setting = null;
if ( $is_network_active ) {
// First, check if there’s a site-specific override for the current site.
// Only applies if the plugin is network-active but we’re on a specific site front-end/admin.
$site_override_setting = get_option( ‘my_plugin_site_specific_override’, null );
if ( ! is_null( $site_override_setting ) ) {
$plugin_setting = $site_override_setting;
} else {
// If no site-specific override, use the network-wide setting.
$plugin_setting = get_site_option( ‘my_plugin_network_default’, ‘network_default_value’ );
}
} else {
// If not network-active (meaning site-activated or single site), just use site-specific setting.
$plugin_setting = get_option( ‘my_plugin_site_default’, ‘site_default_value’ );
}
// Now $plugin_setting holds the correct value based on context and overrides.
“`
Database Table Management
If your plugin creates custom database tables, their creation and management will also vary.
Network-Wide Tables
For data that needs to be shared across all sites or managed centrally, you’ll create a table in the main network database. You’ll interact with it via the $wpdb global object. Make sure to use global $wpdb; first.
“`php
// In your network activation hook
if ( $is_network_active ) {
global $wpdb;
$table_name = $wpdb->base_prefix . ‘my_plugin_network_data’; // Uses the main network table prefix
// … code to create table, e.g., using dbDelta() …
}
“`
Site-Specific Tables
For data specific to each blog, you’ll create tables prefixed with each site’s ID.
“`php
// In your site activation hook (or within logic that checks if not network active)
if ( ! $is_network_active ) {
global $wpdb;
$table_name = $wpdb->prefix . ‘my_plugin_site_data’; // Uses the current site’s table prefix
// … code to create table, e.g., using dbDelta() …
}
“`
Crucial Point for Site-Specific Tables in Multisite Environments: If your plugin is network-activated, but you still want it to manage site-specific tables for each blog, you need to be careful. You can’t just run CREATE TABLE on network activation and expect it to automatically create tables for all existing and future sites.
Instead, you’d hook into wp_insert_site (for new sites) and activate_blog (if you need to run logic when a sub-site is reactivated, though wp_insert_site covers new site creation). Within these hooks, you switch to the relevant site before creating tables:
“`php
// Example for creating site-specific tables when a new blog is created (if plugin is network active)
function my_plugin_create_blog_tables( $blog_id ) {
if ( is_multisite() && is_plugin_active_for_network( plugin_basename( __FILE__ ) ) ) {
switch_to_blog( $blog_id );
global $wpdb;
$table_name = $wpdb->prefix . ‘my_plugin_site_data’;
// … code to create table for $blog_id …
restore_current_blog(); // Always restore!
}
}
add_action( ‘wp_insert_site’, ‘my_plugin_create_blog_tables’ );
“`
And you’d need similar logic for your plugin’s main activation hook to create tables for existing sites if network-activated on an existing Multisite. You’d loop through all existing blogs.
Managing Activation and Deactivation Hooks
The activation and deactivation process also differs significantly. WordPress provides specialized hooks for network-wide plugins.
Network Activation Hooks
When your plugin is network-activated, register_activation_hook() and register_deactivation_hook() only run on the main blog (blog ID 1). This is important to remember.
If you need to perform actions per blog when your plugin is network-activated (e.g., create per-site options or tables for existing sites), you’ll need a different approach.
register_activation_hook() for Network
“`php
/**
- This hook runs only once on the main site (blog ID 1) when the plugin is network-activated.
*/
register_activation_hook( __FILE__, ‘my_plugin_network_activate’ );
function my_plugin_network_activate() {
if ( is_multisite() && is_plugin_active_for_network( plugin_basename( __FILE__ ) ) ) {
// Perform network-wide setup (e.g., create network options, network tables)
add_site_option( ‘my_plugin_network_version’, ‘1.0’ );
// If you need to initialize something for ALL existing sites when network-activated:
global $wpdb;
$blog_ids = $wpdb->get_col( “SELECT blog_id FROM $wpdb->blogs” );
foreach ( $blog_ids as $blog_id ) {
switch_to_blog( $blog_id );
// E.g., add default site option, create site table, etc.
add_option( ‘my_plugin_site_initial_setting’, ‘default_for_new_site’ );
restore_current_blog();
}
} else {
// If it’s a single site or site-activated on a Multisite, this is the same as usual activation.
add_option( ‘my_plugin_version’, ‘1.0’ );
}
}
“`
register_deactivation_hook() for Network
Similarly, the deactivation hook runs only on the main blog.
“`php
register_deactivation_hook( __FILE__, ‘my_plugin_network_deactivate’ );
function my_plugin_network_deactivate() {
if ( is_multisite() && is_plugin_active_for_network( plugin_basename( __FILE__ ) ) ) {
// Perform network-wide cleanup
delete_site_option( ‘my_plugin_network_version’ );
// If you need to clean up something for ALL existing sites when network-deactivated:
global $wpdb;
$blog_ids = $wpdb->get_col( “SELECT blog_id FROM $wpdb->blogs” );
foreach ( $blog_ids as $blog_id ) {
switch_to_blog( $blog_id );
// E.g., delete site options, drop site tables
delete_option( ‘my_plugin_site_initial_setting’ );
restore_current_blog();
}
} else {
// Single site or site-activated cleanup
delete_option( ‘my_plugin_version’ );
}
}
“`
Site Activation Hooks in a Multisite Environment (for network-active plugins)
What if you’re network-activated, but still need to run specific code when an individual site is activated (or created)? You’ll need other hooks.
wpmu_new_blog Hook (New Site Creation)
This hook fires when a new site is added to the Multisite network.
“`php
add_action( ‘wpmu_new_blog’, ‘my_plugin_new_blog_added’, 10, 6 );
function my_plugin_new_blog_added( $blog_id, $user_id, $domain, $path, $site_id, $meta ) {
if ( is_multisite() && is_plugin_active_for_network( plugin_basename( __FILE__ ) ) ) {
switch_to_blog( $blog_id );
// Perform site-specific setup for the new blog
add_option( ‘my_plugin_new_site_default’, ‘value_for_new_blog’ );
restore_current_blog();
}
}
“`
activate_blog Hook (Existing Site Activation)
This hook fires when an existing blog is activated (e.g., un-archived).
“`php
add_action( ‘activate_blog’, ‘my_plugin_blog_activated’, 10, 1 );
function my_plugin_blog_activated( $blog_id ) {
if ( is_multisite() && is_plugin_active_for_network( plugin_basename( __FILE__ ) ) ) {
switch_to_blog( $blog_id );
// Perform site-specific setup/re-setup for the activated blog
// This might be similar to your new blog hook, or handle existing data.
update_option( ‘my_plugin_blog_status’, ‘active’ );
restore_current_blog();
}
}
“`
deactivate_blog Hook (Existing Site Deactivation)
Fires when an existing blog is deactivated or archived.
“`php
add_action( ‘deactivate_blog’, ‘my_plugin_blog_deactivated’, 10, 1 );
function my_plugin_blog_deactivated( $blog_id ) {
if ( is_multisite() && is_plugin_active_for_network( plugin_basename( __FILE__ ) ) ) {
switch_to_blog( $blog_id );
// Perform site-specific cleanup for the deactivated blog
delete_option( ‘my_plugin_blog_status’ );
restore_current_blog();
}
}
“`
When developing a plugin that needs to function differently based on whether it is network-activated or site-activated, it’s essential to understand the underlying architecture of WordPress multisite. A related article that delves into this topic is available at this link, where you can find insights on best practices and strategies for managing plugin behavior in various contexts. This knowledge can significantly enhance your ability to create a more flexible and user-friendly plugin tailored to the specific needs of your WordPress installation.
Considerations for User Roles and Permissions
User roles also get a bit more nuanced in Multisite, especially regarding network vs. site admin capabilities.
Network Administrator Capabilities
Network administrators have the manage_network capability and effectively control everything across the entire network. Plugins active for the network should often provide their main configuration interface in the Network Admin dashboard.
You’d typically check for is_super_admin() to ensure only the network admin can access certain global settings.
“`php
if ( is_super_admin() ) {
// Show network-wide settings page
add_action( ‘network_admin_menu’, ‘my_plugin_add_network_admin_page’ );
}
“`
Site Administrator Capabilities
A site administrator on a sub-site does not automatically have the same capabilities as a network administrator. For example, they cannot activate or deactivate plugins if the plugin is in the mu-plugins directory or if the network admin has disabled plugin activation for site admins.
When your plugin is site-activated, its settings and functionality should reside within the regular site admin dashboard, accessible via standard capability checks (e.g., manage_options, edit_posts).
“`php
// For a plugin active on a single site (or site-activated), typically check standard capabilities
if ( current_user_can( ‘manage_options’ ) ) {
// Show site-specific settings page
add_action( ‘admin_menu’, ‘my_plugin_add_site_admin_page’ );
}
“`
Disabling Plugin for Sub-sites
As a network administrator, you can prevent specific plugins from being activated on sub-sites. You can also implement logic within your plugin to disable its functionality on certain sub-sites, even if it’s network-activated. This would involve storing an excluded_blog_ids option in wp_sitemeta and then checking get_current_blog_id() against that list.
Best Practices and Debugging Tips
Making your plugin adaptive can add complexity, so following some best practices is key.
Keep the plugin_basename(__FILE__) Handy
Always determine your plugin’s base file dynamically to avoid hardcoding issues, especially if you rename your plugin’s directory. Store it in a constant or a class property.
“`php
define( ‘MY_PLUGIN_BASE’, plugin_basename( __FILE__ ) );
// Then use MY_PLUGIN_BASE with is_plugin_active_for_network(), etc.
“`
Use Transients and Caching Wisely
Network-wide operations, especially looping through all blogs, can be resource-intensive. Consider caching the results of such operations using transients or other caching mechanisms.
Log Everything During Development
Debugging Multisite issues can be tricky. Use error_log() extensively to log the current blog ID, the context (network vs. site), and important variable values.
“`php
error_log( ‘My Plugin: Running in blog ID ‘ . get_current_blog_id() . ‘ with network active status: ‘ . ( $is_network_active ? ‘Yes’ : ‘No’ ) );
“`
Test Thoroughly in Both Contexts
Don’t assume. Always test your plugin’s installation, activation, deactivation, and all functionalities:
- On a single-site WordPress installation.
- On a Multisite network, when network-activated.
- On a Multisite network, when site-activated.
- On different sites within a Multisite network.
- With different user roles (Network Admin, Site Admin, Editor, Subscriber).
Building a plugin that gracefully handles network and site activation isn’t just about adding some if statements. It’s about designing your plugin to be aware of its environment, making it more resilient and user-friendly for anyone running a WordPress Multisite. By carefully considering where and how your plugin is active, you can create a truly adaptive and powerful tool.