How to enable a site administrator to edit users in a WordPress network/ multisite setup?

I have a few sites in a network. I don’t want the site administrators to manage things like network plugins, but I would like them to be able to edit profiles of all network users, not use those on their site.

At the top of the /wp-admin/network/users.php file is this:

Read More
if ( ! current_user_can( 'manage_network_users' ) )
    wp_die( __( 'You do not have permission to access this page.' ) );

How can I grant site administrators priviledges to manage_network_users without promoting them to Super Admin?

Here’s what the code is looking like:

/**
 * Retrieve a list of super admins.
 *
 * @since 3.0.0
 *
 * @uses $super_admins Super admins global variable, if set.
 *
 * @return array List of super admin logins
 */
function get_super_admins() {
    global $super_admins;

    if ( isset($super_admins) )
        return $super_admins;
    else
        return get_site_option( 'site_admins', array('admin') );
}
/**
 * Determine if user is a site admin.
 *
 * @since 3.0.0
 *
 * @param int $user_id (Optional) The ID of a user. Defaults to the current user.
 * @return bool True if the user is a site admin.
 */
function is_super_admin( $user_id = false ) {
    if ( $user_id )
        $user = new WP_User( $user_id );
    else
        $user = wp_get_current_user();

    if ( empty( $user->id ) )
        return false;

    if ( is_multisite() ) {
        $super_admins = get_super_admins();
        if ( is_array( $super_admins ) && in_array( $user->user_login, $super_admins ) )
            return true;
    } else {
        if ( $user->has_cap('delete_users') )
            return true;
    }

    return false;
}

    /**
     * Whether user has capability or role name.
     *
     * This is useful for looking up whether the user has a specific role
     * assigned to the user. The second optional parameter can also be used to
     * check for capabilities against a specfic post.
     *
     * @since 2.0.0
     * @access public
     *
     * @param string|int $cap Capability or role name to search.
     * @param int $post_id Optional. Post ID to check capability against specific post.
     * @return bool True, if user has capability; false, if user does not have capability.
     */
    function has_cap( $cap ) {
#       fb($cap);
        if ( is_numeric( $cap ) ) {
            _deprecated_argument( __FUNCTION__, '2.0', __('Usage of user levels by plugins and themes is deprecated. Use roles and capabilities instead.') );
            $cap = $this->translate_level_to_cap( $cap );
        }

        $args = array_slice( func_get_args(), 1 );
        $args = array_merge( array( $cap, $this->ID ), $args );
        $caps = call_user_func_array( 'map_meta_cap', $args );

        // Multisite super admin has all caps by definition, Unless specifically denied.
        if ( is_multisite() && is_super_admin( $this->ID ) ) {
            if ( in_array('do_not_allow', $caps) )
                return false;
            return true;
        }

        // Must have ALL requested caps
        $capabilities = apply_filters( 'user_has_cap', $this->allcaps, $caps, $args );
        $capabilities['exist'] = true; // Everyone is allowed to exist
        foreach ( (array) $caps as $cap ) {
            //echo "Checking cap $cap
"; if ( empty( $capabilities[$cap] ) || !$capabilities[$cap] ) return false; } return true; }

Code from users.php in wp-admin showing a second capability check for editing network users:

// Only allow super admins on multisite to edit every user.
if ( is_multisite() && ! current_user_can( 'manage_network_users' ) && $user_id != $current_user->ID && ! apply_filters( 'enable_edit_any_user_configuration', true ) )
    wp_die( __( 'You do not have permission to edit this user.' ) );

Related posts

Leave a Reply

3 comments

  1. Unfortunately I don’t have enough points to add a comment or vote up @Michael‘s answer above, but I wanted to point out to another post which gives a nicer solution to the problem of stopping non-admin users from editing admin/super-admin accounts.

    The solution in the post link given by Michael works fine, I just applied it to a WP multi-site v4.1. However, it calls die() function when a non-admin user tries to edit an admin account. So instead I used this solution (which is initially for a single site installation of WP) which hides all admin user accounts from the list of user when it is accessed by a non-admin user.

    The final solution, a combination of the above two, gives this…

    function mc_admin_users_caps( $caps, $cap, $user_id, $args ){
    
        foreach( $caps as $key => $capability ){
    
            if( $capability != 'do_not_allow' )
                continue;
    
            switch( $cap ) {
                case 'edit_user':
                case 'edit_users':
                    $caps[$key] = 'edit_users';
                    break;
                case 'delete_user':
                case 'delete_users':
                    $caps[$key] = 'delete_users';
                    break;
                case 'create_users':
                    $caps[$key] = $cap;
                    break;
            }
        }
    
        return $caps;
    }
    add_filter( 'map_meta_cap', 'mc_admin_users_caps', 1, 4 );
    remove_all_filters( 'enable_edit_any_user_configuration' );
    add_filter( 'enable_edit_any_user_configuration', '__return_true');
    
    /*
     * hide admin from user list
     */
    add_action('pre_user_query','isa_pre_user_query');
    function isa_pre_user_query($user_search) {
      $user = wp_get_current_user();
      if ($user->ID!=1) { // Is not administrator, remove administrator
        global $wpdb;
        $user_search->query_where = str_replace('WHERE 1=1',
          "WHERE 1=1 AND {$wpdb->users}.ID<>1",$user_search->query_where);
      }
    }
    
  2. The code above only hides the super admin with ID = 1. Here’s a a code to hide all super users:

    /*
     * hide super admin from user list
     */
    add_action('pre_user_query','isa_pre_user_query');
    function isa_pre_user_query($user_search) {
      $user = wp_get_current_user();
      if ( !is_super_admin($user->ID) ) { // Is not administrator, remove administrator
        global $wpdb;
            $admins = get_network_option(1, 'site_admins', [1]);
            $admin_user = implode("','", $admins);
        $user_search->query_where = str_replace('WHERE 1=1', "WHERE 1=1 AND {$wpdb->users}.user_login NOT IN ('{$admin_user}')", $user_search->query_where);
      }
    }