map_meta_capfilter-hookWP 2.8.0

Allows changing the primitive capabilities required when checking a user's permissions.

The hook fires at the end of map_meta_cap(), which determines whether the current user can perform a specified action; see current_user_can(). The hook can perform an additional permission check and allow or deny the current action.

To pass the check, the hook must return an array of capabilities, all of which the user must have. See the $caps parameter.

Usage

add_filter( 'map_meta_cap', 'wp_kama_map_meta_cap_filter', 10, 4 );

/**
 * Function for `map_meta_cap` filter-hook.
 * 
 * @param string[] $caps    Primitive capabilities required of the user.
 * @param string   $cap     Capability being checked.
 * @param int      $user_id The user ID.
 * @param array    $args    Adds context to the capability check, typically starting with an object ID.
 *
 * @return string[]
 */
function wp_kama_map_meta_cap_filter( $caps, $cap, $user_id, $args ){

	// filter...
	return $caps;
}
$caps(array)

An array of capabilities the user must have to pass the check. The user must have every capability in this array.

For example, suppose we need to check whether a user can edit someone else's published post:

The hook returns:
Array (
	[0] => edit_others_schools
	[1] => edit_published_schools
)

The user has these primitive capabilities:
Array (
	[0] => edit_others_schools
	[1] => read
	[2] => edit_posts
)

In this case, the user cannot edit the post being checked
because they do not have the edit_published_schools capability.
$cap(string)
The capability currently being checked, such as edit_post (a meta capability) or edit_posts (a primitive capability).
$user_id(int)
ID of the user whose capability is being checked.
$args(array)

Additional arguments passed to the capability-checking function. For example, this contains all parameters of current_user_can() except the first one.

Usually this array has one element: the ID of the object for which the capability is checked. For current_user_can( 'edit_post', 123 ), for example, this parameter is array( [0] => 123 ).

Examples

#1 Example of using the map_meta_cap filter

This example shows how to grant a user additional access to an action. Here, a subscriber is allowed to edit their post from the front end.

It assumes that the post can be edited on the front end and that the capability is checked in the standard way: current_user_can( 'edit_post', $post_id );

// Allow a subscriber to edit a post if they are its author and
// no more than X months have passed since it was published
! is_admin() && add_filter( 'map_meta_cap', 'allow_subscribers_edit_self_posts', 10, 4 );
function allow_subscribers_edit_self_posts( $caps, $cap, $user_id, $args ){
	// print_r( $caps );    // [0] => edit_published_posts
	// print_r( $cap );     // edit_post
	// print_r( $user_id ); // 80
	// print_r( $args );    // [0] => 913

	if( $cap === 'edit_post' && $caps[0] === 'edit_published_posts' ){
		$post = get_post( $args[0] );
		// Allow editing if the user is the post author and no more
		// than two months have passed since the post was published
		if(
			$post->post_author && $user_id == $post->post_author &&
			get_userdata($user_id)->roles[0] === 'subscriber' &&
			( time() < ( MONTH_IN_SECONDS * 2 + strtotime( $post->post_date_gmt ) ) )
		){
			$caps[0] = 'read';
		}
	}

	return $caps;
}

#2 Allow particular users to edit other users' Posts

Suppose authors publish projects (Posts) and store the ID of a tracker (a custom role) in a metadata field. That tracker should then be able to edit their projects.

add_filter( 'map_meta_cap', 'allow_tracker_edit_posts', 10, 4 );

/**
 * Allows Trackers to edit their assigned users' posts.
 *
 * @param array  $caps
 * @param string $cap
 * @param int    $user_id
 * @param array  $args
 *
 * @return array
 */
function allow_tracker_edit_posts( $caps, $cap, $user_id, $args ) {
	if (
		// Handle only the required meta capability
		$cap === 'edit_post'
		// The current user ID must match the value stored in the post metadata
		&& (int) get_post( $args[0] )->project_tracker === $user_id
		// The current user must have the role for which the capability is being added
		&& get_userdata( $user_id )->has_cap('project_tracker')
	) {
		$caps[0] = 'edit_posts'; // Add the primitive capability
	}

	return $caps;
}

#3 Unconditionally deny something to absolutely everyone

To strictly deny a capability through this hook, return an array containing 'do_not_allow'.

If an empty capability array or an array containing nonexistent capabilities is returned instead, a super administrator on a Multisite network, for example, can still have any capability—even one that does not exist.

Therefore, when nobody, including a super administrator, should have the specified capability, add the 'do_not_allow' string to the returned capabilities array.

For example, remove permission to use the WordPress Customizer from everyone, including super administrators:

add_filter( 'map_meta_cap', 'remove_customize_capability', 10, 2 );
function remove_customize_capability( $caps, $cap ) {
	return ( $cap === 'customize' ) ? [ 'do_not_allow' ] : $caps;
}

Changelog

Since 2.8.0 Introduced.

Where the hook is called

map_meta_cap()
map_meta_cap
wp-includes/capabilities.php 879
return apply_filters( 'map_meta_cap', $caps, $cap, $user_id, $args );

Where the hook is used in WordPress

wp-includes/class-wp-customize-manager.php 2966
add_filter( 'map_meta_cap', array( $this, 'grant_edit_post_capability_for_changeset' ), 10, 4 );
wp-includes/class-wp-customize-manager.php 2973
remove_filter( 'map_meta_cap', array( $this, 'grant_edit_post_capability_for_changeset' ), 10 );