WordPress 3.4 and multisite initialization

 

We’ve been working on StatComm plugin, trying to make it multisite. This is a common requested feature and we decided go for it, since it is an unique opportunity to learn more about some WordPress aspects that we would never see otherwise. These experiences will be very useful for plugin programmers and we’ll try to publish it shortly in the future, but right now we want to share something we found that could affect several multisite plugins.

Activation & deactivation behavior on network-wide plugins.

Some complex multisite plugins relies on initialization procedures when are network-wide activated. In our case, we need to create a table for every site on the multisite network so we came across facing the same situation. Until version WP 3.3.2 there is a common technique to detect network-wide activation and such situation trigger a initialization procedure on every site on the network. This is a possible example showing how to deal in those cases:

registeractivationhook( FILE, 'myactivationmethod' );
function myactivationmethod() {
global $wpdb;

if (is_multisite()) {
    // check if it is a network activation and call activation for every blog in network
    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);
            per_blog_activation(); //initialization for every blog
        }
        switch_to_blog($old_blog);
        return;
    }
}

This is a slight variation you can find with more detail on Shiba Site . An analog code is also used on activation and perhaps on uninstall for cleaning purposes.

The problem: On WordPress 3.4 there is a change where the variable networkwide is not provided in network-wide activation (specifically there were changes on wp-admin\includes\class-wp-plugins-list-table.php file)

Consequence: Plugins using this technique can’t trigger its activation and deactivation procedures. This change could affect the uninstall procedure in most cases.

Workaround:Pay attention this workaround is in draft stages and should be improved:

if (is_multisite()) {
    $network=isset($_SERVER['SCRIPT_NAME'])?$_SERVER['SCRIPT_NAME']:"";
    $activate=isset($_GET['action'])?$_GET['action']:"";
    $isNetwork=($network=='/wp-admin/network/plugins.php')?true:false;
    $isActivation=($activate=='deactivate')?false:true;

    if ($isNetwork and $isActivation){
        $old_blog = $wpdb->blogid;
        $blogids = $wpdb->get_col($wpdb->prepare("SELECT blog_id FROM $wpdb->blogs"));
        foreach ($blogids as $blog_id) {
            switch_to_blog($blog_id);
            per_blog_activation();
        }
        switch_to_blog($old_blog);
        return;
    }
}
per_blog_activation();

The idea is to detect when the incoming link is from the network-wide activation and also checking if is a current activation.

We appreciate any comment about improving this code. We’ll try to contact WP developers to find out more about this.

Thank you and enjoy the day.

Comments

  1. Good find, thanks. I’ll test it out with one of my plugins.

  2. Here’s the Trac changeset where $_GET[ 'networkwide' ] was removed: http://core.trac.wordpress.org/browser/trunk/wp-admin/includes/class-wp-plugins-list-table.php?rev=20525.

    It’s related to http://core.trac.wordpress.org/ticket/20468 and http://core.trac.wordpress.org/ticket/20104.

    I’ve filed a new bug report at http://core.trac.wordpress.org/ticket/20995 since I think this should be deprecated instead of immediately removed without warning.

  3. Excellent, we hadn’t found where the problem started. Thank you.

  4. It looks like the callback for register_activation_hook() accepts a boolean parameter that indicates whether or not it’s a network-wide activation. That’s probably the best way to detect it. See the Trac ticket for details.

  5. And where i should put that code? Thank you.

  6. admin says:

    The proposed solution is a replacement for the above code. The problem applies only on multisite plugins which needs some initialization tasks.

  7. This works perfect for me:

    register_activation_hook( __FILE__,'my_activation_function');

    function my_activation_function( $network_wide ) {
    if ( $network_wide ) {
    $blog_list = get_blog_list( 0, 'all' );
    foreach ($blog_list as $blog) {
    switch_to_blog($blog['blog_id']);
    activate_it();
    }
    switch_to_blog($wpdb->blogid);
    } else {
    activate_it();
    }

    }

    function activate_it() {
    // do your activation stuff here :-)
    }

  8. admin says:

    Thanks for your comments. That is the way we should go, unless the plugin does not relies on activation hooks following recommendation on http://wpdevel.wordpress.com/2010/10/27/plugin-activation-hooks-no-longer-fire-for-updates/

  9. $_GET[ 'networkside' ] has been re-enabled in WP 3.4.1, but everyone should update their code to update the boolean parameter of the network activation callback instead.

  10. Very helpful findings..

Speak Your Mind

*