Support » Fixing WordPress » Custom post type only works when role has edit_posts capability

  • Resolved richarduk

    (@richarduk)


    I have a custom post type and a role that deals with it. Unfortunately that role needs to have edit_posts capabilities before it can create (custom) posts.

    What have I done wrong?

    I’ve mapped the meta capabilities.

Viewing 15 replies - 1 through 15 (of 24 total)
  • I’m getting there.

    The error message inside the Admin Panel is ‘You are not allowed to edit this post.’

    Which is baloney, because I can, and I can publish it and then visit the custom post on the site. So removing edit_post capabilities works, it just throws up this stupid error message.

    However, I still can’t use taxonomies such as categories or custom taxonomies.

    For anyone bashing their brains out and looking for a reasonably neat way of adding custom posts – er – adding a combined role and custom post, whatever (given that I haven’t figured out how to display categories and custom taxonomies yet – they only show when I add the capability edit_posts) here’s a snippet of useful code:

    $AdaptRole4 = get_role('subscriber');
    $caps4 = $AdaptRole4->capabilities;
    add_role('reviewer', 'Reviewer', $caps4);  
    
    $AdaptRole4 = get_role('reviewer');
    
    	// $AdaptRole4 -> add_cap('delete_others_reviews');
    $AdaptRole4 -> add_cap('delete_review');
    $AdaptRole4 -> add_cap('delete_reviews');
    $AdaptRole4 -> add_cap('delete_private_reviews');
    $AdaptRole4 -> add_cap('delete_published_reviews');
    	// $AdaptRole4 -> add_cap('edit_others_reviews');
    $AdaptRole4 -> add_cap('edit_review');
    $AdaptRole4 -> add_cap('edit_reviews');
    $AdaptRole4 -> add_cap('edit_private_reviews');
    $AdaptRole4 -> add_cap('edit_published_reviews');
    $AdaptRole4 -> add_cap('publish_reviews');
    $AdaptRole4 -> add_cap('read_review');
    $AdaptRole4 -> add_cap('read_private_reviews'); 
    
    $AdaptRole4 -> remove_cap('edit_posts');
    $AdaptRole4 -> remove_cap('read');

    You’re better off going with a plugin to help you manage capabilities. Here’s a tutorial on how to use Members to restrict access to CPT.

    Thanks 🙂

    I don’t like plugins generally speaking.

    It didn’t help that I assigned meta capabilities to the role in the above code 🙂 Corrected that, but still getting nowhere.

    Trying from a different angle now – ‘You cannot modify this taxonomy’

    In the core that’s something to do with assigning terms:

    <?php if ( !current_user_can($tax->cap->assign_terms) ) : ?>
    					<p><em><?php _e('You cannot modify this Taxonomy.'); ?></em></p>

    If you’re going to do everything manually, best get familiar with all of the Roles and Capabilities.

    If you want your custom role to be able to manage your categories, assign it the manage_categories cap.

    🙂

    I tried that. I sill get ‘you cannot modify this taxonomy.’

    Create custom post type.

    /* ********************************** ADD CUSTOM POST ********/
    add_action( 'init', 'add_post_type' ); 
    
    function add_post_type() {
    	register_post_type('review',
    		array(
    			'labels' => array(
    	'name' => __( 'Reviews' ),
    	'singular_name' => __( 'Post Your Review' ),
    	'add_new' => __( 'Add A New Review' ),
    	'add_new_item' => __( 'Add A New Review' ),
    	'edit' => __( 'Edit Reviews' ),
    	'edit_item' => __( 'Edit Reviews' ),
    	'new_item' => __( 'New Review' ),
    	'view' => __( 'View This Review' ),
    	'view_item' => __( 'View This Review' ),
    	'search_items' => __( 'Search Reviews' ),
    	'not_found' => __( 'No Reviews Found' ),
    	'not_found_in_trash' => __( 'No Reviews Found In Trash' ),
    	'parent' => __( 'Parent Review' ),
    ),
    
    'show_ui' => true,
    'publicly_queryable' => true,
    'exclude_from_search' => false,
    'description' => __( 'Brilliant Reviews ' ),
    'menu_position' => 5,
    'menu_icon' => get_stylesheet_directory_uri() . '/images/custom-post-type.png',
    'hierarchical' => false,
    'query_var' => true, 					
    
    'capabilities' => array(  		
    
    	//   'delete_others_posts' => 'delete_others_reviews',
    	'delete_post' => 'delete_review',	//METACAPABILITY - DON'T ASSIGN TO A ROLE
    	'delete_posts' => 'delete_reviews',
    	'delete_private_posts' => 'delete_private_reviews',
    	'delete_published_posts' => 'delete_published_reviews',
    	// 'edit_others_posts' => 'edit_others_reviews',
    	'edit_post' => 'edit_review',  		  //METACAPABILITY - DON'T ASSIGN TO A ROLE
    	'edit_posts' => 'edit_reviews',
    	'edit_private_posts' => 'edit_private_posts',
    	 'edit_published_posts' => 'edit_published_reviews',
    	'publish_posts' => 'publish_reviews',
    	 'read_post' => 'read_review', 		//METACAPABILITY - DON'T ASSIGN TO A ROLE
    	'read_private_posts' => 'read_private_reviews',
    ),
    
    'supports' => array( 'title', 'editor', 'comments','trackbacks', 'revisions','author', 'excerpt',  'thumbnail','custom-fields', 'page-attributes' ), 
    
    '_builtin' => false,
    
    'rewrite' => array( 'slug' => 'blog', 'with_front' => false ), 
    
    'taxonomies' => array( 'post_tag', 'division', 'category',),                    
    
    'can_export' => true,
    /*
    Create a callback function e.g. to set up the HTML for various meta boxes on the Admin screen.
    */
    
    'permalink_epmask' => EP_PERMALINK,
    
    		)
    	);
    }

    Map Meta Capabilities

    /***************** MAP META CAPABILITIES **************** */
    
    add_filter( 'map_meta_cap', 'my_map_meta_cap', 10, 4 );
    
    function my_map_meta_cap( $caps, $cap, $user_id, $args ) {
    
    	// If editing, deleting, or reading a review, get the post and post type object.
    	if ( 'edit_review' == $cap || 'delete_review' == $cap || 'read_review' == $cap ) {
    		$post = get_post( $args[0] );
    		$post_type = get_post_type_object( $post->post_type );
    
    		// Set an empty array for the caps.
    		$caps = array();
    	}
    
    	//  If editing a review, assign the required capability.
    	if ( 'edit_review' == $cap ) {
    		if ( $user_id == $post->post_author )
    			$caps[] = $post_type->cap->edit_posts;
    		else
    			$caps[] = $post_type->cap->edit_others_posts;
    	}
    
    	// If deleting a review, assign the required capability.
    	elseif ( 'delete_review' == $cap ) {
    		if ( $user_id == $post->post_author )
    			$caps[] = $post_type->cap->delete_posts;
    		else
    			$caps[] = $post_type->cap->delete_others_posts;
    	}
    
    	// If reading a private review, assign the required capability.
    	elseif ( 'read_review' == $cap ) {
    
    		if ( 'private' != $post->post_status )
    			$caps[] = 'read';
    		elseif ( $user_id == $post->post_author )
    			$caps[] = 'read';
    		else
    			$caps[] = $post_type->cap->read_private_posts;
    	}
    
    // Return the capabilities required by the user.
    	return $caps;
    }

    Create Role

    /* ************* ADD ROLE ******************** */
    
    remove_role('reviewer'); // Reset role whilst figuring this out
    
    $AdaptRole4 = get_role('subscriber');
    $caps4 = $AdaptRole4->capabilities;
    add_role('reviewer', 'Reviewer', $caps4);  
    
    $AdaptRole4 = get_role('reviewer');
    
    	// $AdaptRole4 -> add_cap('delete_others_reviews');
    $AdaptRole4 -> add_cap('delete_reviews');
    $AdaptRole4 -> add_cap('delete_private_reviews');
    $AdaptRole4 -> add_cap('delete_published_reviews');
    	// $AdaptRole4 -> add_cap('edit_others_reviews');
    $AdaptRole4 -> add_cap('edit_reviews');
    $AdaptRole4 -> add_cap('edit_private_reviews');
    $AdaptRole4 -> add_cap('edit_published_reviews');
    $AdaptRole4 -> add_cap('publish_reviews');
    $AdaptRole4 -> add_cap('read_private_reviews'); 
    
    $AdaptRole4 -> remove_cap('edit_posts');
    $AdaptRole4 -> remove_cap('read');

    End result – the ability to post Reviews and nothing else is as expected BUT I get an error message telling me that I can’t edit my custom post type Review (which I can) whilst categories are grayed out and I’m unable to do anything with taxonomies, custom or otherwise.

    Here are the capabilities that the role Reviewer has as displayed on the test website page:

    level_0 1
    delete_reviews 1
    delete_private_reviews 1
    delete_published_reviews 1
    edit_reviews 1
    edit_private_reviews 1
    edit_published_reviews 1
    publish_reviews 1
    read_private_reviews 1

    I am in the process of launching a new multi-user website, attempting to use custom roles and custom post types. I can verify, after tearing hair out for several days, that this piece of CMS functionality is broken in all plugins, and inherently, in core (which the plugins are attempting to add functionality to).

    The problem is really multifaceted.

    It all really begins because there is a total lack of levels of access when it comes to taxonomies. First of all, “manage_categories” is the ONLY entry in the WP capabilities system which is a total misnomer in itself. It is Categories AND Tags, and the capability SHOULD BE titled “manage_taxonomies”. Secondly, there should be capabilities to manage only tags, ie “manage_taxonomies_tags” AND to manage categories “manage_taxonomies_categories”. And while we’re at it, there should be capabilities “edit_xxx” “delete_xxx” just the same as posts and pages.

    I believe that somewhere in the wordpress TAGS capabilities is where the solution to the problem of “You cannot modify this taxonomy.” SHOULD be located, instead, due to some illogical path, a bug exists which makes the solution exist in giving users “edit_posts” capabilities. The ability to modify taxonomy on a post (or custom post type in my case), lies in “edit_posts”? And yet, with the “manage_categories” capability, I can add/edit/delete tags outside of posts, using the admin?

    So what I’m saying is the capabilities need to be completely reviewed regarding handling of taxonomies, and total granularity needs to be implemented for provisioning these things.

    After using Drupal for so long, I can see that there are many places that WordPress is advanced, but it is half a decade behind in terms of CMS functionality.

    Role Scoper got me closer, but frankly, it is so complicated the way it functions, I refuse to use it.

    Not sure if this has any bearing on your post, but the ‘solution’ to my original post is to always make sure that any new role has edit_posts capability. No way round it at present.

    This automatically adds a menu item in the left sidebar i.e Add Post (as in, add standard post, not custom post for that role)

    You also need the Read capability to access the Admin screen, which if I remember rightly also adds the Dashboard menu item.

    Both the Add Post and Dashboard menu item then have to be removed with a bit of extra code, and the page that the Add Post menu would have gone to has to be redirected back to the Dashboard (so that users can’t guess the url to go to in order to be able to add standard posts).

    Yes, it’s currently not working as it should. Most people never seem to encounter that problem because (I guess) they create hybrid roles where the user also has Admin capabilities.

    Either that, or nobody else has bothered posting that they have that problem? 🙂

    Richard,

    Thank you for returning to comment. I am glad to see you essentially agree with me that simply, the way it works now, is pretty much broken. Its a good thing I have an understanding boss on this project because I had to explain to him this situation, and his question was the same as yours “well how come nobody else has this problem”. The only answer I can come up with is the same as you have.

    As you suggest, the “edit_post” capability adds the “Post” area to the admin. This is clearly not desired when using custom post types and limiting user capabilities.

    Do we need to submit a ticket or something to Trac? I’m kind of unfamiliar with the support of WordPress.

    Mainly, my suggestions would be:

    1) Rename the manage_categories capability to manage_taxonomies, or remove it in favor of manage_categories , manage_tags , and manage_custom_taxonomy_xxx with support for custom taxonomies.

    2) Remove the capability of edit_posts needed to alter tags or categories or custom taxonomies on any post type. Instead, the capability for this should lie in another more related capability, such as “edit_posts_tags” etc.

    Again the issue is that reliance on “posts” and “pages” need to be depreciated completely as WordPress 3.x ushers in custom content types. Posts and Pages are now simply content types with templates. Other custom content types are just as relevant a reference, even though theoretically they use the term “custom post type”, they are in fact, “custom content types” because they have their own functionality, template, style, taxonomies, and permissions/capabilities.

    A perfect example of why this is needed. Lets say I have a news site that includes reviews of movies, local news, us news, user-submitted news, and also has an ecommerce area.

    I don’t want any person who is working in one area of the site, for instance, movie reviews, to have any access to my posts, pages, ecommerce, or anything else. To do this, we need to make everything exclusive from everything else.

    trevorgreenfieldwp

    I don’t want any person who is working in one area of the site, for instance, movie reviews, to have any access to my posts, pages, ecommerce, or anything else. To do this, we need to make everything exclusive from everything else.

    This is possible, but does require the removal of menu items as described. I actually have a demo ‘review’ custom post type on my test site which is only accessible to ‘Reviewers’ + Admin. 🙂

    Temporarily, the Adminimize plugin seems to do a good job of hiding menu items, what do you think?

    It doesn’t address the necessary additional capabilities, and you can circumvent it by visiting urls directly such as wp-admin/add-post.php, needed but at least it hides areas we don’t want people to see.

    This is from my notes. Hope it helps you. It works 🙂

    // A glitch in WP means that  edit_posts is required before custom posts can work. However, adding   edit_posts  throws up some unwanted menu items.
    // Menus are either top level   menu  or  lower level  submenu. The code below shows how to remove menu items. I couldn't manage to remove submenus.
    // Removing menus or submenus doesn't prevent users pasting urls into the browser and accessing things.  The code  below also prevents that happenning.
    //Thanks to:  http://www.shinephp.com/how-to-block-wordpress-admin-menu-item/
    // Thanks to Grimbog http://wordpress.org/support/topic/how-to-get-the-current-logged-in-users-role?replies=10
    
    function get_user_role() {
    	global $current_user;
    	$user_roles = $current_user->roles;
    	$user_role = array_shift($user_roles);
    	return $user_role;
    }
    
    if (( is_user_logged_in()) && (get_user_role()=='reviewer')) // If the user has the role   Reviewer  then remove these menu items
    
    		{
    function removeMenu(){
    global $menu;	//    ****************** Find the numbers corresponding to the menu items to remove in   wp-admin/menu.php **********
    unset($menu[2]); // remove dashboard menu
    unset($menu[5]); // remove post menu
    unset($menu[25]); // remove comments menu
    unset($menu[70]); // remove profile menu
    unset($menu[75]); // remove tools menu
    				}
    
    add_action( 'admin_head' , 'removeMenu');				
    
    function menu_redirect() { // Redirect links. Thanks to   Vladimir Garagulya at  http://www.shinephp.com/how-to-block-wordpress-admin-menu-item/
    // Although menu items have been removed a user could still paste in the url for that menu item and go to that page. This redirects such urls back to the dashboard.
    // Before removing the menu items click on each one and copy the relevant urls. Take the last bit of each url and paste into either   $linksToAllow = array
    // or $linksToBlock = array    (below)
    // Again, before removing the menu items, check that the above code works when you click on each menu item.
    // edit.php    is not redirected for some reason.
    // If the user pastes    edit.php     onto the end of /wp-admin/ they can see a list of all the posts that have been published.
    // However, they can't do anything with that list of posts. And trying to add a new post redirects them back to the dashboard.
    // It's not ideal. Redirecting edit.php back to the dashboard would be better.
    
    $result = false;
    $linksToAllow = array('edit.php?post_type=review', 'post-new.php?post_type=review');  		// Links to allow
    foreach ($linksToAllow as $key=>$value) {
    $result = stripos($_SERVER['REQUEST_URI'], $value);
    if ($result!==false) {
    $result = 'blah';
    break;
    }
    }
    
    if ($result!==blah) {
    $linksToBlock = array('edit.php', 'post-new.php', 'tools.php', 'profile.php', 'edit-comments.php'); 		// Links to block (i.e. to redirect back to wp-admin/index.php)
    foreach ($linksToBlock as $key=>$value) {
    $result = stripos($_SERVER['REQUEST_URI'], $value);
    if ($result!==false) {
    $result = 'something';
    break;
    }
    }
    }
    
    if ($result==something) {
    wp_redirect(get_option('siteurl') . '/wp-admin/index.php');
    }
    
    }
    
    add_action('admin_menu', 'menu_redirect');
    }

    I agree with you both. @trevorgreenfieldwp I was recently trying to setup custom roles for certain users that would just be overwhelmed by all the menu items available to them in the Administration Panel. I used the “User Role Editor” plugin and was able to alter a lot of their capabilities, but the edit_posts capability covers too many individual things that I would like to be able to define separately. For instance, I have a user who only needs access to the Media Gallery – add, edit and delete. I don’t want them to be able to get into anything else besides the Pages editor. However, Delete capabilities in the Media Gallery are not available unless you use edit_posts (which in turn displays the Posts manager menu).

    I understand that there are plugins which will allow me to block out certain items from the Admin Menu, but it seems like this would be something very useful for many reason and less of a hack than simply blocking out menu items. Just my 2 cents worth.

    So I found this post while looking for a solution to my problem. I agree that just removing the menu is a bad idea. If I know that all you have done is remove the menu then I can change the url or use a tool like web scarab to remove your posts or add new ones to your site even though I dont have access to the menu item. Here is a link to a little walk through I made to solve my custom post_type, taxonomy, and role problem. I can also add a meta box to the post edit/add screen that will allow a user to add tags to the custom taxonomy. this can all be done without giving the user edit_post capabilities. http://www.jsterup.com/dev/wordpress/custom-post_type-without-edit_post-capability

    Thanks foomagoo

    I’m no php guru or security expert. I wonder what you think of my solution re. edit.php? Is that secure?

    <
    // If the user pastes edit.php onto the end of /wp-admin/ they can see a list of all the posts that have been published.
    // However, they can’t do anything with that list of posts. And trying to add a new post redirects them back to the dashboard.
    >

    If Javascript is turned off will the checkboxes still show in your solution?

    There are a couple of issues I see. Some plugins allow access to features if the user has edit_posts capabilities so having that ability would open those plugins up (Akismet, Stream Video Player, WP Super Edit are a few). Also you would be able to add/edit/delete categories by going to the right url.
    Yes my solution would require the user to have javascript but the rest of my plugin requires it also so I went the easy route. 🙂

    I did finally figure out how to do this properly though. I was very close a few times but couldn’t quite get it. I went looking for a hook to enable the checkboxes and figured it out along the way. Here is what you need to do. In your custom taxonomy put these 2 arguments.

    'capability_type' => 'couponbook',
    'capabilities' => array('assign_terms'=>'edit_coupons','manage_terms' => 'manage_couponbooks','edit_terms' => 'manage_couponbooks','delete_terms' => 'manage_couponbooks'),

    These arguments are specific to my post_type and taxonomy. My post type is coupon and has a capability_type argument of coupon. In my custom role I assign the capabilities edit_coupons and manage_couponbooks. Then the user can add/edit/delete couponbooks (my custom taxonomy) without the edit_posts capability. I have updated my tutorial at http://www.jsterup.com/dev/wordpress/custom-post_type-without-edit_post-capability. It gives a full code example.

Viewing 15 replies - 1 through 15 (of 24 total)
  • The topic ‘Custom post type only works when role has edit_posts capability’ is closed to new replies.