38

I have a custom post type that I'd like to restrict access to certain roles however, I've already added content using the custom post type and now I have to make them restricted. The capability_type was 'post'

'capability_type' => 'post'

Which is fine as the content show up in the backend however, now as soon as I add any capabilities the content disappears from the backend?

I've tried customising the capability type to include plural definitions to construct my own but as soon as I remove or change capability types it's gone!

full code:

add_action( 'init', 'register_cpt_gallery' );

function register_cpt_gallery() {
$labels = array( 
    'name' => _x( 'Galleries', 'gallery' ),
    'singular_name' => _x( 'Gallery', 'gallery' ),
    'add_new' => _x( 'Add New', 'gallery' ),
    'add_new_item' => _x( 'Add New Gallery', 'gallery' ),
    'edit_item' => _x( 'Edit Gallery', 'gallery' ),
    'new_item' => _x( 'New Gallery', 'gallery' ),
    'view_item' => _x( 'View Gallery', 'gallery' ),
    'search_items' => _x( 'Search Galleries', 'gallery' ),
    'not_found' => _x( 'No galleries found', 'gallery' ),
    'not_found_in_trash' => _x( 'No galleries found in Trash', 'gallery' ),
    'parent_item_colon' => _x( 'Parent Gallery:', 'gallery' ),
    'menu_name' => _x( 'Galleries', 'gallery' ),
);

$args = array( 
    'labels' => $labels,
    'hierarchical' => true,
    'description' => 'Image galleries for teachers classes',
    'supports' => array( 'title', 'editor', 'author'),

    'public' => true,
    'show_ui' => true,
    'show_in_menu' => true,

    'menu_icon' => get_bloginfo('template_url') . '/images/imagegallery.png',
    'show_in_nav_menus' => true,
    'publicly_queryable' => true,
    'exclude_from_search' => false,
    'has_archive' => true,
    'query_var' => true,
    'can_export' => true,
    'rewrite' => true,
    'capability_type' => 'post',
    'capabilities' => array(
        'edit_post' => 'edit_gallery',
        'edit_posts' => 'edit_galleries',
        'edit_others_posts' => 'edit_other_galleries',
        'publish_posts' => 'publish_galleries',
        'read_post' => 'read_gallery',
        'read_private_posts' => 'read_private_galleries',
        'delete_post' => 'delete_gallery'
    )
);

register_post_type( 'gallery', $args );
}

I've also tested this with a completely new custom post type and regardless of the capability type I get the same issue eg even if I remove it and add my custom one:

'capability_type' => array('movie','movies');

4 Answers 4

56

After a quick chat with Magicroundabout who pointed out a useful resource from Justin Tadlock, it turns out that capabilities for custom post types don't actually exist unless you use add_cap to the role, for example for the following custom post type:

add_action( 'init', 'register_cpt_gallery' );

function register_cpt_gallery() {
$labels = array( 
    'name' => __( 'Galleries', 'gallery' ),
    'singular_name' => __( 'Gallery', 'gallery' ),
    'add_new' => __( 'Add New', 'gallery' ),
    'add_new_item' => __( 'Add New Gallery', 'gallery' ),
    'edit_item' => __( 'Edit Gallery', 'gallery' ),
    'new_item' => __( 'New Gallery', 'gallery' ),
    'view_item' => __( 'View Gallery', 'gallery' ),
    'search_items' => __( 'Search Galleries', 'gallery' ),
    'not_found' => __( 'No galleries found', 'gallery' ),
    'not_found_in_trash' => __( 'No galleries found in Trash', 'gallery' ),
    'parent_item_colon' => __( 'Parent Gallery:', 'gallery' ),
    'menu_name' => __( 'Galleries', 'gallery' ),
);

$args = array( 
    'labels' => $labels,
    'hierarchical' => true,
    'description' => 'Image galleries for teachers classes',
    'supports' => array( 'title', 'editor', 'author'),
    'public' => true,
    'show_ui' => true,
    'show_in_menu' => true,
    'menu_icon' => get_bloginfo('template_url') . '/images/imagegallery.png',
    'show_in_nav_menus' => true,
    'publicly_queryable' => true,
    'exclude_from_search' => false,
    'has_archive' => true,
    'query_var' => true,
    'can_export' => true,
    'rewrite' => true,
    'capabilities' => array(
        'edit_post' => 'edit_gallery',
        'edit_posts' => 'edit_galleries',
        'edit_others_posts' => 'edit_other_galleries',
        'publish_posts' => 'publish_galleries',
        'read_post' => 'read_gallery',
        'read_private_posts' => 'read_private_galleries',
        'delete_post' => 'delete_gallery'
    ),
    // as pointed out by iEmanuele, adding map_meta_cap will map the meta correctly 
    'map_meta_cap' => true
);

register_post_type( 'gallery', $args );
}

The additional capabilities should be added to a role for the permissions to actually work in the backend, including the 'administrator' - for example:

function add_theme_caps() {
    // gets the administrator role
    $admins = get_role( 'administrator' );

    $admins->add_cap( 'edit_gallery' ); 
    $admins->add_cap( 'edit_galleries' ); 
    $admins->add_cap( 'edit_other_galleries' ); 
    $admins->add_cap( 'publish_galleries' ); 
    $admins->add_cap( 'read_gallery' ); 
    $admins->add_cap( 'read_private_galleries' ); 
    $admins->add_cap( 'delete_gallery' ); 
}
add_action( 'admin_init', 'add_theme_caps');

I hope this is useful to others.

Additionally, the _x() translation function expects the second argument to be string $context, which is a short description, and the third to be string $domain. When not providing a description, use the __() translation function instead which has the string $domain as the second argument.

2
  • 15
    add_theme_caps() should be called one time only, not everytime an admin page is loaded. It would be better to use switch_theme as hook for theme activation or register_activation_hook on plugin activation.
    – d79
    Commented Apr 13, 2015 at 10:42
  • Nice! I like to use wp cli to add the capabilities if it's a completely custom/unique site since it's an action that only needs to happen once. Commented Dec 1, 2017 at 1:56
12

Add:

map_meta_cap => true

to your $args array. Look here, for more. Hope it helps!

2
  • 2
    This is what I thought also but not totally the case.
    – erichmond
    Commented Jul 30, 2013 at 19:47
  • This worked for me
    – Shikyo
    Commented Mar 27, 2018 at 14:20
1

IMHO you never map your own capabilities. Make sure to use map meta cap plugin to do so. http://codex.wordpress.org/Function_Reference/map_meta_cap

I spent days trying to map my custom caps manually with code. Just install that plugin, map your caps and deactivate once working. If creating custom roles you WILL need Members plugin.

The way I test to MAKE sure my role has those capabilites (sometimes you swear you do but don't actually) make a debugging page with:

    if( !function_exists( 'current_user_has_role' ) ){
        function current_user_has_role( $role ){
            $current_user = new WP_User( wp_get_current_user()->ID );
            $user_roles = $current_user->roles;
            $is_or_not = in_array( $role, $user_roles );
            return $is_or_not;
        }
    }

This will show you which capabilities you do in fact have.

0

For Custom Post Types, I DON'T suggest using hook:

add_action( 'registered_post_type', 'your_func', 10, 2 );

instead I suggest using:

add_filter( 'register_post_type_args', 'your_func', 10, 2 );
function your_func( $args, $name ) 
{
   if ( $name == "your_custom_post_name" ) 
   ...
}
2
  • 1
    the suggestion is a good one, but it does not answer the question.
    – Aurovrata
    Commented Nov 11, 2019 at 8:06
  • 1
    I don't even understand why it's a good suggestion. That action is called from post.php at the bottom of register_post_type, and passes the type and object back to any code that wants to handle it. There doesn't seem to be anything wrong with this. Though nothing in this Q&A referred to this action anyway. ;)
    – TonyG
    Commented Oct 13, 2021 at 23:58

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.