I've got a custom post type that I want to allow a specific user role to add posts to, however they must not be able to read posts from others.

Here's my custom post type code:

register_post_type('product', array(
    'labels'                => create_post_type_labels('Product'),
    'public'                => TRUE,
    'query_var'             => TRUE,
    'rewrite'               => array('slug' => 'products', 'with_front' => FALSE),
    'capability_type'       => 'directory',
    'capabilities'          => array(
        'edit_post'         => "edit_directory",
        'read_post'         => "read_directory",
        'delete_post'       => "delete_directory",
        'edit_posts'        => "edit_directorys",
        'edit_others_posts'     => "edit_others_directorys",
        'publish_posts'       => "publish_directorys",
        'read_private_posts'    => "read_private_directorys",
        'delete_posts'        => "delete_directorys",
        'delete_private_posts'    => "delete_private_directorys",
        'delete_published_posts'  => "delete_published_directorys",
        'delete_others_posts'   => "delete_others_directorys",
        'edit_private_posts'    => "edit_private_directorys",
        'edit_published_posts'    => "edit_published_directorys"
    'supports'              => array('title', 'author'),
    'has_archive'           => TRUE

And here's a screenshot of the role permissions:

enter image description here

The problem I'm having is my directory contributor user can still see the posts from the other users.

Can anyone spot an error in the way I am doing things here?

  • First you're not "doing things here", but clicking together stuff with a plugin - this is not the default admin interface. Second, you're missing the mapping of meta caps IIRC.
    – kaiser
    Dec 4 '13 at 1:08
  • Semantics aside, I had used code to replicate the capabilities set in this plugin. However I didn't realise I needed to make the meta capabilities unless I wanted to do something against the default. After the mapping code from here (justintadlock.com/archives/2010/07/10/…) it now works as desired. Thanks Kaiser, want to add that as an answer? Dec 4 '13 at 13:48
  • Ben, I added you a more in depth explanation that works without some bogus filter callback - no idea how he got on that one. But for the future: Please make your questions more detailed, stuff all the needed info inside (plugin, anyone?) and show your efforts in code.
    – kaiser
    Dec 4 '13 at 15:31
  • Any progress? ..
    – kaiser
    Dec 12 '13 at 0:24
  • @kaiser Damn... completely forgot to update you, on it now :-) Dec 12 '13 at 10:57

The problem

The problem is, that you have a custom capability_type. This means, that this post type will be handled differently from default post types, that have a capability type of post: Users need to be granted access to this capabilities. Important thing here: This is a good decision.


What you're missing is mapping the capabilities to their corresponding meta capabilities. A short explanation straight from Codex:

edit_post, read_post, and delete_post - These three are meta capabilities, which are then generally mapped to corresponding primitive capabilities depending on the context, for example the post being edited/read/deleted and the user or role being checked. Thus these capabilities would generally not be granted directly to users or roles.

Then there're the other 4 primitive capbilities: edit_posts, edit_others_posts, publish_posts and read_private_posts. All those (7 in total) caps are assigned in the capabilities array and checked on various locations in core.

But then there're another 7 capabilities that are not checked against, but only mapped inside map_meta_cap(). When you look at the source of map_meta_cap(), then you'll see 17 cases for the switch in total. Don't be confused by that, as not everything is related to posts, but to plugins activation, users and other stuff.

Now the core function map_meta_caps() checks which type of cap is checked, then starts filling an array of capabilities. Then at the end the following is returned:

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

Now let's take a look at the core registration process of post types: map_meta_cap is NULL per default and if not explicitly set, it's FALSE.

And when we look at a user capability check with the has_cap() function, we can see that map_meta_cap() will be called to retrieve all needed capabilities and then it will be looped through and checked against the resulting capabilities from the mapped caps.

The rule

If you assign a capability_type, you better set map_meta_cap to TRUE.

  • 1
    Thank you @kaiser, I didn't realise I had to map the custom capabilities, after I had done this the user permissions behaved themselves. Dec 12 '13 at 10:59

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.