Restricting Access to Custom Post Types Using Roles in WordPress

Custom post types extend the capabilities of WordPress in terms of what types of content can be published and managed, but these days at 3.7 we find ourselves working on projects that need more granular permissions related to custom post types.

The most common situation I’ve run into is a particular user (or group of users) needs the ability to manage specific custom post types but shouldn’t have the ability to alter the rest of the site. For example, you may have someone in an organization that needs to manage job listings (a custom post type) but shouldn’t be allowed to edit posts or pages.

For this example, I’ll base the situation off our project management plugin Panorama. Many of our customers need users to manage projects, but don’t want them to have access to any other types of content. There are some good tutorials out there, but many of them are a few years dated and I found a slightly updated approach was necessary to make this work.

What We’re Aiming For

In the case of Panorama, we wanted our “projects” custom post type to be managed by Editors, Administrators and a new role of “Project Managers.” Project Managers have all the capabilities of an editor but only for Projects, they won’t have access to any other types of content or settings (with the exception of adding media.)

The Approach

There are a few things we have to setup in order for this to work.

  1. We need to create our new role
  2. We need to turn on meta capabilities mapping on our custom post type
  3. We need to create new meta capabilities
  4. We need to assign said capabilities to our new role, editors and administrators

Creating a New Role

The first thing we have to do is create a new role, so we have more control than the typical WordPress subscriber,contributor,author,etc… roles give us. Because this roles are stored in the database we only need to run this once, typically on plugin or theme activation. The following code assumes your adding this to a plugin, but you could include it in your functions.php as well.


function psp_add_project_management_role() {
 add_role('psp_project_manager',
            'Project Manager',
            array(
                'read' => true,
                'edit_posts' => false,
                'delete_posts' => false,
                'publish_posts' => false,
                'upload_files' => true,
            )
        );
   }
   register_activation_hook( __FILE__, 'psp_add_project_management_role' );

This function allows us to add a role with a unique identifier of psp_project_manager, give it a title of “Project Manager” and give it some basic permissions passed in via an array. Since we only want our project managers to edit projects, we remove the ability to edit, delete and publish posts.

Meta Capabilities Mapping

The next step is to assign custom capabilities for our custom post type. This is done through two additional arguments passed into the register_post_type() function.


add_action( 'init', 'psp_register_cpt_projects');
function psp_register_cpt_projects() {
     		$args = array(
			'label'               => __( 'psp_projects', 'psp_projects' ),
			'description'         => __( 'Projects', 'psp_projects' ),
			'labels'              => $labels,
			'supports'            => array( 'title', 'comments', 'revisions', ),
			'hierarchical'        => false,
			'public'              => true,
			'show_ui'             => true,
			'rewrite'             => $rewrite,
                        'capability_type'     => array('psp_project','psp_projects'),
                        'map_meta_cap'        => true,
		);
		register_post_type( 'psp_projects', $args );
}

The code above is a simplified example of registering a custom post type. The arguments you should pay attention to are ‘capability_type’ and ‘map_meta_cap.’ If you’re not familiar with the register_post_type function check out the Codex.

capability_type

This argument allows us to pass in the read, edit and delete capabilities. Typically you’d see ‘post’ or ‘page’ showing up in this list, but we’re looking to create our own specific capabilities so we’ll just create our own. You’ll notice we have two capabilities (the name is arbitrary but unique), one being the plural of the other which allows us to create plural capabilities.

map_meta_cap

This overrides the default meta capabilities handling so we can use our own. It’s important to note that doing this removes the ability for administrators or editors to edit this custom post type until we specifically grant them permission.

The last thing we need to do is actually create the psp_project/psp_projects capability types and assign them to the relevant roles.

Creating Custom Capabilities

WordPress has a default set of capabilities that we can add or remove from any role. We’ve used them earlier when we created our “Project Manager” role and they follow a simple convention. edit_(identifier), read_(identifier), publish_(identifier), etc… For example, the default post capabilities look like this:

  • edit_post
  • read_post
  • delete_post

When we registered our custom post type we specified two new identifiers, “psp_project” and “psp_projects.” Now we can add these capabilities to any role using the WordPress capability naming convention by substituting “post” for “psp_projects.” For example, “edit_post” would become “edit_psp_project.”

To simplify this process, let’s use a function that loops through a list of roles and assigns them all capabilities required manage (add,edit,publish,delete) our custom post type.


    add_action('admin_init','psp_add_role_caps',999);
    function psp_add_role_caps() {

		// Add the roles you'd like to administer the custom post types
		$roles = array('psp_project_manager','editor','administrator');
		
		// Loop through each role and assign capabilities
		foreach($roles as $the_role) { 

		     $role = get_role($the_role);
			
	             $role->add_cap( 'read' );
	             $role->add_cap( 'read_psp_project');
	             $role->add_cap( 'read_private_psp_projects' );
	             $role->add_cap( 'edit_psp_project' );
	             $role->add_cap( 'edit_psp_projects' );
	             $role->add_cap( 'edit_others_psp_projects' );
	             $role->add_cap( 'edit_published_psp_projects' );
	             $role->add_cap( 'publish_psp_projects' );
	             $role->add_cap( 'delete_others_psp_projects' );
	             $role->add_cap( 'delete_private_psp_projects' );
	             $role->add_cap( 'delete_published_psp_projects' );
		
		}

This code is fairly straight forward. We have a loop that grabs the WP_Role object of the current role, then adds our new _psp_project/_psp_projects capabilities to said role using the WordPress add_cap() function. See the get_role() function on the codex to get a better understanding of what capabilities are available.

The Result

After all this, editor and administrative users won’t see much difference. They will be able to add, edit, delete and publish projects like they typically would. Users assigned to our new “Project Manager” role will only see the media and projects tab.

Screen Shot 2014-08-01 at 12.47.00 PM

Stay tuned to a follow up post where we’ll cover assigning specific posts, pages, or custom post types to a user.

Need Help With Your Project?

Reach out if you need help with your WordPress development, digital marketing, or web design project. We’ve helped over 200 companies get more out of their web presence and would love to help you overcome your big challenges.

Submit your website link and get a detailed report on how you can improve page performance, SEO optimization, mobile usability, and more.