3

I have been trying to add new wordpress roles and capabilities, in a multisite installation, using the code below. The issue is, it only applies to the 'main' site of the multisite, and does not propogate to the subsites. I haven't really found anything in the documentation that covers this.

function civicrm_wp_set_capabilities() {
  global $wp_roles;
  if (!isset($wp_roles)) {
    $wp_roles = new WP_Roles();
  }

  //Minimum capabilities (Civicrm permissions) arrays
  $min_capabilities =  array(
    'access_civimail_subscribe_unsubscribe_pages' => 1,
    'access_all_custom_data' => 1,
    'access_uploaded_files' => 1,
    'make_online_contributions' => 1,
    'profile_create' => 1,
    'profile_edit' => 1,
    'profile_view' => 1,
    'register_for_events' => 1,
    'view_event_info' => 1,
    'sign_civicrm_petition' => 1,
    'view_public_civimail_content' => 1,
  );

  // Assign the Minimum capabilities (Civicrm permissions) to all WP roles
  foreach ( $wp_roles->role_names as $role => $name ) {
    $roleObj = $wp_roles->get_role($role);
    foreach ($min_capabilities as $capability_name => $capability_value) {
      $roleObj->add_cap($capability_name);
    }
  }

  //Add the 'anonymous_user' role with minimum capabilities.
  if (!in_array('anonymous_user' , $wp_roles->roles)) {
    add_role(
      'anonymous_user',
      'Anonymous User',
      $min_capabilities
    );
  }
}
  • To whoever voted down the question, a comment explaining why it was down-voted so it can be improved would be appreciated. – Anthony F. Camilleri Jun 9 '13 at 9:50
  • I didn't downvote it so far, but normally showing what you already tried is a prerequisite for asking questions on this site as "do this job for me"-questions are off-topic. – kaiser Jun 10 '13 at 0:50
  • 1
    OK, thanks. I've edited the question appropriately. – Anthony F. Camilleri Jun 15 '13 at 13:28
  • When does this civicrm_wp_set_capabilities function run? – brasofilo Jul 5 '13 at 22:11
  • Heh Anthony F. Camilleri, you might be interested to know that we are trying to launch a CiviCRM site here at Stack Exchange. It would be great if you could visit area51.stackexchange.com/proposals/77367/civicrm and become a committer to help our effort along. – Joe Murray Nov 13 '14 at 20:15
1

This is my experience.

I had to add role for each site in Wordpress, I developed an add page in dashboard so the site administrator can add the self-defined role/capabilities.

But I found the $wp_roles->add_role, add_cap only works the subsite. So I did some fix,

I've made a plugin, the super-admin(not site administrator but the "admin") can "Network Activate" in dashboard

xxx/wp-admin/network/plugins.php

the self-defined role/capabilities was saved in role.ini file, the plugin can create two table named wp_s_role and wp_s_cap, The pre-self-defined role/capabilities will insert into the table.

Then the $wp_roles->add_role and add_cap can across the network and insert the role/capabilities to all subsites.

But the subsite administrator must add the self-defined role/capabilities and make it works in all subsites, so I create a trigger for

    register_activation_hook(__FILE__, array($s_role, 'install'));
    register_deactivation_hook(__FILE__, array($s_role, 'uninstall'));

Then the uninstall function works, all the two tables data will be saved into the role.ini file again, after that, the install function will works again. and all self-defined role/capabilities was added into all subsites.

As you see, I made a trigger in order to add the self-defined role into all subsites, but the effectivity is very low, the plugin restart will take more than 5 seconds. So I've improve the methods, This time, when the add_role function is done, I copied the subsite role/capabilities into the others subsites.

Some steps:

  • In subsites, when the administrator add the role, I use $blog_id and $table_prefix to get a subsite table wp_2_options contents. (We assumed the blog_id is 2 and the table_prefix is wp).

  • I select the option_name=wp_2_user_roles

    result($wpdb->get_row("select `option_value` from `wp_2_options` where `option_name`='wp_2_user_roles'", ARRAY_A);), 
    

then I foreach the blogs and get the subsite table. So I insert the select result into each subsite table(wp_n_options) and the main table(wp_options), muhaha, am I smart enough? : )

The below function is how to remove the self-defined roles in all subsites. I think it's would helpful with you.

public function reset_subsite_role_cap()
{
    //select * from `wp_options` where `option_name` = 'wp_user_roles';
    //select * from `wp_options` where `option_name` = 'wp_backup_user_roles';
    //select * from `wp_3_options` where `option_name` = 'wp_3_user_roles';
    //select * from `wp_4_options` where `option_name` = 'wp_4_user_roles';
    //select * from `wp_5_options` where `option_name` = 'wp_5_user_roles';
    global $wpdb;
    $sql = "select * from ".$wpdb->blogs;
    $multisite_info = $wpdb->get_results($sql, ARRAY_A);
    $site_result = array();
    if (!empty($multisite_info) && is_array($multisite_info)) {
        foreach ($multisite_info as $k => $v) {
            $site_result[$v['blog_id']] = trim($v['path'], '/');
        }
    }

    global $table_prefix;
    $tp_arr = explode('_', $table_prefix);
    $table_arr = array();
    foreach ($site_result as $blog_id => $site_name) {
        if ($blog_id !== 1) {
            $table_arr[$tp_arr[0].'_'.$blog_id.'_options'] = $tp_arr[0].'_'.$blog_id.'_user_roles';
        } else {
            $table_arr[$tp_arr[0].'_options'] = $tp_arr[0].'_user_roles';
        }
    }

    // get the backup user roles.
    $backup_roles_result = $wpdb->get_row("select `option_value` from `".$tp_arr[0]."_options` where `option_name`='wp_backup_user_roles'", ARRAY_A);

    // clean the others role cap
    if (isset($table_arr) && is_array($table_arr)) {
        foreach ($table_arr as $table_role_cap_name => $table_role_cap_filed) {
            $wpdb->update(
                $table_role_cap_name,
                array('option_value' => $backup_roles_result['option_value']),
                array('option_name' => $table_role_cap_filed)
            );
        }
    }
    return true;
}

For the $site_result, I have another function to fetch all exact site info.

/**
 * Get the all site info.
 *
 * @param integer $id BlogID.
 *
 * @return array array('blog_id' => 'path', '1' => 'printsolv', '2' => 'govsolv')
 */
public function s_get_multisite_info($id = null)
{
    global $wpdb;
    $where = '1=1';
    if (isset($id)) {
        $sql = "select * from ".$wpdb->blogs." where `blog_id`="."'".$id."'";
    } else {
        $sql = "select * from ".$wpdb->blogs." where 1=1 ";
    }
    $multisite_info = $wpdb->get_results($sql, ARRAY_A);
    $result = array();
    if (!empty($multisite_info) && is_array($multisite_info)) {
        // clean the path, > 1 means not http://site_name/theme.php but http://site_name/path_name/theme.php
        if (isset($id)) {
            $site_info = $wpdb->get_row("select * from ".$wpdb->site." where `id`="."'".$multisite_info[0]['site_id']."'", ARRAY_A);
        } else {
            $site_info = $wpdb->get_row("select * from ".$wpdb->site, ARRAY_A);
        }

        $site_path_status = false; // path= /
        if (isset($site_info['path']) && strlen(trim($site_info['path'], '/')) > 1 ) {
            // site have path./xxx/
            $site_path_status = true;
        }

        foreach ($multisite_info as $k => $v) {
            if (isset($site_info['domain']) && $site_info['domain'] == $v['domain']) {
                if ($site_path_status == true) {
                    $result[$v['blog_id']] = trim(substr($v['path'], strlen(trim($site_info['path'], '/')) + 1), '/');
                } else {
                    $result[$v['blog_id']] = trim($v['path'], '/');
                }
            }
        }
    }
    return $result;
}

I have just finished the function and it works perfectly. You can use s_copy_site_role_cap() to call. The function can copy the role into others subsite roles after you add_role. Because the plugin might works in main site or subsite, there will have two ways in network(subdomain, subpath) , so i made another function to get the right current blog_name , so I can get the lastest roles contents from the blog_name information.

/**
 * Copy the subsite role/caps to all subsites and main site..
 *
 * @param string $subsite_name SubSite Name.
 *
 * @return boolean.
 */
public function s_copy_site_role_cap()
{
    global $wpdb;
    // Get all site info 
    $multisite_info = $this->s_get_multisite_info();

    global $table_prefix;
    $tp_arr = explode('_', $table_prefix);
    // Get all site wp_x_options table. and table filed.
    $table_arr = array();
    foreach ($multisite_info as $blog_id => $site_name) {
        if ($blog_id !== 1) {
            $table_arr[$tp_arr[0].'_'.$blog_id.'_options'] = $tp_arr[0].'_'.$blog_id.'_user_roles';
        } else {
            $table_arr[$tp_arr[0].'_options'] = $tp_arr[0].'_user_roles';
        }
    }
    // select the blog id by blog name.
    $subsite_name = $this->s_get_dashboard_site_name();
    $subsite_id = array_search($subsite_name, $multisite_info);
    if ($subsite_id === false) {
        return false;
    }
    $current_site_table_pre = $tp_arr[0].'_'.$subsite_id;
    // get the current subsite roles.
    $subsite_roles = $wpdb->get_row("select `option_value` from `".$current_site_table_pre."_options` where `option_name`='".$current_site_table_pre."_user_roles'", ARRAY_A);

    if (isset($table_arr) && is_array($table_arr)) {
        foreach ($table_arr as $table_role_cap_name => $table_role_cap_filed) {
            if ($table_role_cap_name != $current_site_table_pre.'_options') {
                $wpdb->update(
                    $table_role_cap_name,
                    array('option_value' => $subsite_roles['option_value']),
                    array('option_name' => $table_role_cap_filed)
                );
            }
        }
    }
    return true;
}

/**
 * Get dashboard site name.
 *
 * @return string. SiteName
 */
public function s_get_dashboard_site_name()
{
    $dashboard_url = admin_url();

    //$dashboard_url = "http://xxx/wp-admin/";
    //$dashboard_url = "http://subsite_name.xxx/wp-admin/";
    //$dashboard_url = "http://xxx/subsite_name/wp-admin/";

    // get main site name.
    $site_name = get_current_site()->domain;
    $parse_url = parse_url($dashboard_url);
    if (!is_subdomain_install()) {
        // base path  http://xxx/wp-admin/
        // subsite path http://xxx/subsite_name/wp-admin/
        if ($parse_url['path'] == "/wp-admin/") {
            return $site_name;
        } else {
            $tmp_url = explode("/", trim($parse_url['path'], "/"));
            return $tmp_url[0];
        }
    } else {
        // base domain  http://xxx/wp-admin/
        // subsite domain  http://subsite_name.xxx/wp-admin/
        if ($parse_url['host'] == $site_name) {
            return $site_name;
        } else {
            $tmp_url = explode(".", $parse_url['host']);
            return $tmp_url[0];
        }
    }

}

Thanks.

  • 1
    This answer is incomplete and it doesn't give a solution as it is. – cybmeta Aug 20 '14 at 8:24
  • I fixed the answer, The steps is one solution for it. – rainysia Aug 20 '14 at 8:45
  • In the function to get the all site information, the "$site_result", I have another function to fetch the exact site information, add it in the post – rainysia Aug 20 '14 at 9:41
0

Just add it as a mu-plugin:

wp-content/mu-plugins/roles.php

/**
* Plugin name: Multisite user roles
*/

add_role(
    'example_role',
    __('Example Role'),
    [
        'read',
        'edit_post',
        'publish_post',
        'delete_post'
    ]
);

mu-plugins are loaded by default on all sites on the network.

If you want a more robust solution, you can use my WordPress Register Roles project.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.