How to Allow Administrators to Edit Users in a WordPress Network

Jason Conroy of _FindingSimple emailed me today to ask if I knew how to allow site admins to edit the profiles of other users.

I hadn’t even realised administrators couldn’t edit users!

That’s because it relates only to WordPress networks. In a WordPress 3.x Network, the Super Admin role is the only role allowed to edit users.

Diagnosing the Problem

As a solution for Jason, my first thought was to simply assign the edit_users capability to the admin role.

I used Justin Tadlock’s Members plugin to check that the admin roles no longer had the edit_users & related user capabilities. However, despite not being able to edit users, admins do have the edit_users capability. In my opinion, that’s a bug in 3.1 or at very least a quirk. If a user has the edit_users capability, they should be able to edit users.

The only hope then was to enter the dark depths of the map_meta_cap function found in capabilities.php.

Around line 816 of that file, within map_meta_cap I found this bit of code:

	case 'edit_users':
		// If multisite these caps are allowed only for super admins.
		if ( is_multisite() && !is_super_admin( $user_id ) )
			$caps[] = 'do_not_allow';
		else
			$caps[] = 'edit_users'; // Explicit due to primitive fall through
		break;

And there it is. The master override that is preventing any role other than Super Admin from editing other users.

Solution

Fortunately, as is the wonder of WP, the $caps array is passed through a filter before being returned:

	return apply_filters('map_meta_cap', $caps, $cap, $user_id, $args);

So the trick then is to use the map_meta_cap filter to turn that do_not_allow capability in the $caps array into either edit_users, delete_users or create_users.

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', 10, 4 );

This restores the edit_users, delete_users and create_users capabilities to their former glory.

If a user has the admin role, or has been manually assigned any of these capabilities, they will then be able to edit users.

— Update 21 May 2012 —

A few important updates to get the code to work with WordPress 3.3 and to prevent admin users being able to edit super admin accounts and accounts of users not on their site.

Thanks to everyone in the comments, especially for those comments made by Andrew Cafourek, Peter Edwards & Morty Dot.

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');

/**
 * Checks that both the editing user and the user being edited are
 * members of the blog and prevents the super admin being edited.
 */
function mc_edit_permission_check() {
	global $current_user, $profileuser;

	$screen = get_current_screen();

	get_currentuserinfo();

	if( ! is_super_admin( $current_user->ID ) && in_array( $screen->base, array( 'user-edit', 'user-edit-network' ) ) ) { // editing a user profile
		if ( is_super_admin( $profileuser->ID ) ) { // trying to edit a superadmin while less than a superadmin
			wp_die( __( 'You do not have permission to edit this user.' ) );
		} elseif ( ! ( is_user_member_of_blog( $profileuser->ID, get_current_blog_id() ) && is_user_member_of_blog( $current_user->ID, get_current_blog_id() ) )) { // editing user and edited user aren't members of the same blog
			wp_die( __( 'You do not have permission to edit this user.' ) );
		}
	}

}
add_filter( 'admin_head', 'mc_edit_permission_check', 1, 4 );

— Update 28 June 2012 —

Updated the mc_edit_permission_check() to prevent a super admin form being deleted. Thanks again to Peter Edwards in the comments.

— Update 29 September 2012 —

Updated the mc_edit_permission_check() to allow super admins to edit users again. Thanks to Scott Fennell in the comments.

Posted in Blogdex, WordPress | Tagged , , , , , | 59 Comments

The Web Without Facebook

I live in Vietnam where the government block Facebook. I knew Facebook was rife, but seeing content disappear all over the web has still surprised me. It’s amazing just how much of the web is now pulling data from Facebook servers.

To give you a taste, here are screenshots of TechCrunch, Mashable, Time and other prominent websites.

A side note for the Australian Government, who has been debating an internet filter, it takes about 20 seconds to get around the block here.

Posted in Hacker Tales, Vietnam | Tagged , | Comments Off on The Web Without Facebook

Word Spectrum in Mac OS X

One of my favourite features in Mac OS X is Dictionary. And my favourite part of Dictionary is the thesaurus.

Apple uses the Oxford American Writer’s Thesaurus, which comes with a nifty tool called Word Spectrum. Word Spectrum lists synonyms between semantic opposites. The best part – the spectrum is ordered according to each word’s shade of meaning.

Prior to buying my first Mac, if something looked good it was “beautiful”. If something looked bad, it was “ugly”. I rarely communicated the scale of how good or bad it looked. I’ve started to do that much more in the last few years, thanks primarily to the Oxford Writer’s Thesaurus & Mac OS X.

Dictionary’s definitions also often include a context for a word, which helps choose the most appropriate synonym.

Combined with Spotlight, you can get high quality definitions and synonyms in seconds. Next time you’re looking for a better word, punch in CMD + Space, type the word & hit enter. You’ll not be disappointed.

Word Spectrum Mac OS X Dictionary

Posted in Aside, Meta, Navel Gazing | Tagged , , , | 2 Comments

Instagram Prints & Photobook

Thanks to Instagram, I’ve got all these faux-pro photos to show off on tumblr and its elk, but I have nowhere physical to flaunt them.

A quick Google brought me to a post by Kin Lane & another by Allan Hoffman about making an Instagram photo book… I want. With the Instagram Realtime API announcement, this is now a real possibility.

To build or wait for someone else to build, that is the question.

UPDATE: Checkout the super awesome instaprint.me.

Posted in Ideas | Tagged , , , , , , , , , | 3 Comments

Add your Latest Tweet to DotNetNuke, Including Retweets

To add your latest tweet to a DotNetNuke website, simply add the following HTML to your skin templates.

<div id="twitter">
 <ul id="twitter_update_list"></ul>
 — <a id="twitter_link" href="http://twitter.com/USERNAME">@USERNAME</a>
 </div>
 <script src="http://twitter.com/javascripts/blogger.js" type="text/javascript"></script>
 <script src="http://api.twitter.com/1/statuses/user_timeline.json?callback=twitterCallback2&screen_name=USERNAME&count=1&include_rts=true" type="text/javascript"></script>
</div>

Just replace USERNAME with your twitter username.

Technical Details

This approach uses the Twitter User Timeline API to pull in the latest tweet as a JSON object. A function in the blogger.js script is then used to parse the JSON and insert it into the twitter_update_list element.

Add some CSS magic to the twitter div and you have a very easy, client side solution for pulling, parsing and then publishing your latest tweet.

Include Retweets

Notice the include_rts=true parameter being sent to the user_timeline.json script?

That tells twitter to include retweets. Without it, you’ll end up with an empty value when your latest tweet is a retweet.

Further Reading

Special thanks to Isabella Snow and Blogger Buster for their articles. They refer to the old Twitter API but still provide a great starting point.

For more information about what else you can get from the Twitter API, see the full Twitter User Timeline API documentation.

Posted in Blogdex | Tagged , , | Comments Off on Add your Latest Tweet to DotNetNuke, Including Retweets

Floodaid is the Future

Floodaid.com.au is not a charity, nor is it a business. We’re not a non-for-profit or an organisation. We’re merely an international group of like-minded people following a vision and combining skills to create a new type of social network with big plans for the future.

– FloodAid.com.au

A new instance of my favourite up-and-coming structure for production. Someone needs to stick a name on this phenomenon. Something more catchy & concise than real-time, distributed networks.

Posted in Aside, Meta, Navel Gazing | Tagged , , | Comments Off on Floodaid is the Future