I have created a must-use plugin where I keep general functions. Like register_post_type().

Here I've created several Custom Post Types. With the help of several really good posts, I managed to get a grip of Capability for Custom Post Types (I think).

// Create Custom Post Types

add_action( 'init', 'create_post_type' );

    function create_post_type() {
    register_post_type( 'pms',
          'labels' => array(
            'name' => __( 'Personal Mission Statements' ),
            'singular_name' => __( 'Personal Mission Statement' )
          'public' => true,
          'has_archive' => true,
          'menu_position' => 2,
          'menu_icon' => 'dashicons-heart',
          'capability_type' => array('pms','pmss'),
          'map_meta_cap' => true,
          'supports'           => array( 'title', 'editor', 'author' )


// Create Custom Roles

//Add new roles

//Can't create new post and only edit their own
$capabilities_employee = array (
    'edit_others_pages' => true,
    'edit_others_posts' => true,
    'edit_pages' => true,
    'edit_posts' => true,
    'edit_private_posts' => true,
    'edit_published_posts' => true,
    'list_users' => true,
    'manage_categories' => true,
    'publish_posts' => true,
    'read' => true,
    'upload_files' => true,
    'manage_categories' => true,

//Create additional Roles

function add_roles () {
    add_role( 'employee', 'Employee', $capabilities_employee );

add_action( 'admin_init', 'add_roles');

//Specify Capabilities Custom Post Types

//Add capabilities to the other Post Types (need added).
//The remove a capabilities it is NOT enough to remove the line, you need to add remove_cap()

function add_theme_caps() {

    // gets the candidate role
    $employees = get_role( 'employee' );

    $employees->add_cap( 'read' );

    $employees->add_cap( 'edit_pms' ); 
    $employees->add_cap( 'read_pms' ); 
    $employees->add_cap( 'edit_pmss' );
    $employees->add_cap( 'publish_pmss' ); 
    $employees->add_cap( 'edit_published_pmss' );
add_action( 'admin_init', 'add_theme_caps');

PROBLEM Before editing a Custom Post 'pms' I check for current_user_can('edit_pms')

1) Default role 'editor' returns TRUE 2) Custom role 'employee' (above) returns FALSE

Is use $GLOBALS['wp_post_types'] to debug capability on the 'pms' Custom Post Type and get_userdata() to debug capability for the a user with the employee role. They both come out with the right capabilities.

QUESTION Any idea way (2) above returns FALSE?


https://codex.wordpress.org/Function_Reference/register_post_type http://justintadlock.com/archives/2010/07/10/meta-capabilities-for-custom-post-types

  • why do you think that $GLOBALS['wp_post_types'] is a good way to debug capabilities? Commented Sep 3, 2015 at 14:11
  • Since it shows the Capabilities added to the CPT? Added using 'capability_type' => array('pms','pmss'), 'map_meta_cap' => true in the register_post_type. As explained in codex.wordpress.org/Function_Reference/register_post_type. --- From your question, I guess I am doing something wrong. Please share your thoughts.
    – Coloumbo
    Commented Sep 3, 2015 at 15:24
  • I am not sure that you are doing anything wrong, it is just the first time I heard of anybody trying to debug things that way. It is just that capabilities can be overridden by the usage of filter therefor what you see in the DB do not have to be the value returned by the function (disclaimer, I am not familiar enough with the relevant code paths, so not going to guess what are the chances it might or might not work in default no plugin enviroment) Commented Sep 3, 2015 at 15:45
  • Thank you for taking the time and replying to my question. Any suggestion for how to find the reason for the above behavior and to solve it?
    – Coloumbo
    Commented Sep 3, 2015 at 16:08
  • frankly if I ran into such an "obviously should work but doesn't" problem I just debug the core. I don't see a reason it should not work so maybe you have something else going on as well. Commented Sep 3, 2015 at 17:42

1 Answer 1


The register_post_type() function takes a post type name as argument for map_meta_cap, where the default is post. Take a look at the internals of get_post_type_capabilities() for extended insights. That should help you to understand how it's meant to be:

function get_post_type_capabilities( $args ) {
    if ( ! is_array( $args->capability_type ) )
        $args->capability_type = array( $args->capability_type, $args->capability_type . 's' );

    // Singular base for meta capabilities, plural base for primitive capabilities.
    list( $singular_base, $plural_base ) = $args->capability_type;

    $default_capabilities = array(
        // Meta capabilities
        'edit_post'          => 'edit_'         . $singular_base,
        'read_post'          => 'read_'         . $singular_base,
        'delete_post'        => 'delete_'       . $singular_base,
        // Primitive capabilities used outside of map_meta_cap():
        'edit_posts'         => 'edit_'         . $plural_base,
        'edit_others_posts'  => 'edit_others_'  . $plural_base,
        'publish_posts'      => 'publish_'      . $plural_base,
        'read_private_posts' => 'read_private_' . $plural_base,

    // Primitive capabilities used within map_meta_cap():
    if ( $args->map_meta_cap ) {
        $default_capabilities_for_mapping = array(
            'read'                   => 'read',
            'delete_posts'           => 'delete_'           . $plural_base,
            'delete_private_posts'   => 'delete_private_'   . $plural_base,
            'delete_published_posts' => 'delete_published_' . $plural_base,
            'delete_others_posts'    => 'delete_others_'    . $plural_base,
            'edit_private_posts'     => 'edit_private_'     . $plural_base,
            'edit_published_posts'   => 'edit_published_'   . $plural_base,
        $default_capabilities = array_merge( $default_capabilities, $default_capabilities_for_mapping );

    $capabilities = array_merge( $default_capabilities, $args->capabilities );

    // Post creation capability simply maps to edit_posts by default:
    if ( ! isset( $capabilities['create_posts'] ) )
        $capabilities['create_posts'] = $capabilities['edit_posts'];

    // Remember meta capabilities for future reference.
    if ( $args->map_meta_cap )
        _post_type_meta_capabilities( $capabilities );

    return (object) $capabilities;

A better way to debug that is the following hook:

do_action( 'registered_post_type', $post_type, $args );

Use it like this (it runs exactly after registration):

add_action( 'registered_post_type', function( $cpt, $args )
    $cpt === 'your_cpt_name' && var_dump(
        $args->cap // result of get_post_type_capabilities()
}, 10, 2 );

Replace your_cpt_name with your actual name that you used as 1st arg during registration.

Also keep in mind that for most use cases, it's completely unnecessary to check against custom capabilities. Those are much harder to maintain that it should be. I prefer checking against the post type and a default capability instead.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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