 Hey there, and welcome to Learn WordPress! In this tutorial you'll be learning how to restrict access to custom post types via the built-in capability system. You will learn how the different capabilities are assigned to a custom post type and how to manage them, as well as how to add custom post type capabilities to new or existing roles. Let's say you need to create a plugin that allows a site owner to add the following functionality to a WordPress site. The ability to define a custom post type called Story. The ability to create users with a writer role who can only create, edit and delete unpublished stories. The writer role should also be able to publish their own stories, but not edit or delete stories once published. Any site administrator should be able to create, edit and delete any stories, regardless of whether they are published or not, or who they were created by. To create the Story custom post type, you might have some code in your plugin that looks like this. Here, you're using the registerPostType function to register the Story custom post type, and passing in some specific arguments to turn certain things on and off. When a custom post type is registered in this way, it inherits the capabilities of the post post type. To see these capabilities in your dashboard during development, you might register a submenu page that displays the capabilities of the Story custom post type, which is only available to admins. Inside the callback function for this page, you could access the custom post type object from the wpPostTypesGlobalArray variable. You could then print out the capability type, mapMetaCAP, and cap properties of the Story post type. With the plugin active, when logged in as an administrator user, this is what you might see when you visit the page in your dashboard. As you can see, the capability type inherits from post. MapMetaCAP is set to true, and the cap property contains an array of capabilities that are inherited from the default WordPress capabilities for a post. To understand how these capabilities are mapped to the custom post type, you need to understand how these three properties are set up. To do this, take a look at the registerPostType function, which currently exists at line 1679 of the wpInclude slash post.php file. On line 1694, the wpPostType object is created, and the args array is passed to the class constructor. If you open the file for the wpPostType class, you'll see the following code at around line 541. Here, if the capabilities argument is empty, and the MapMetaCAP argument is null, and the capability type argument is set to either post or page, then the MapMetaCAP argument is set to true. If you scroll back up to line 480, you can see that the default value for capability type is post. So because your custom post type doesn't pass any of these arguments, then MapMetaCAP is set to true. Later on, the cap property is set to the result of the getPostType capabilities function. This function accepts the args array as an argument and returns an array of capabilities. So because the capability type argument is set to post and the MapMetaCAP argument is set to true, then the cap property on the story custom post type will be set to an array of capabilities that are mapped to the default WordPress capabilities for a post. You can control these capabilities by passing relevant values to the three arguments that are used above, as described in the developer handbook entry for register post type. Possible arguments are capability type, capabilities, and MapMetaCAP. The first step in setting custom capabilities for your custom post type is to set the capability type argument to something other than post. In this case, you could set it to story. If you now take a look at the cap property on your custom post type, you will see that the capabilities have changed. However, it's not using the correct pluralization of story, so you need to explicitly set the plural version of the capability type by passing in an array of values, the first being the singular name and the second being the pluralization. So we would change this to an array, a story, and stories. Now if you take a look at the cap property on your custom post type, you will see that the capabilities have changed again. Notice that this list does not include all the same capabilities when they were inherited from post. This is because by setting a capability type, the first conditional check in the WP post type class you looked at earlier returns false. And so the MapMetaCAP argument is now set to false, which means the additional capabilities are not mapped for the custom post type. Now would be a good time to talk about the different types of capabilities. There are three types of capabilities available to custom post types. Metacapabilities are capabilities that are mapped to primitive capabilities. The three metacapabilities are editPost, readPost, and deletePost. As an example, the editPost metacapability is mapped to primitive capabilities like editPosts and editOthersPosts. Because the metacapabilities are automatically mapped to certain primitive capabilities, it's generally recommended not to grant the metacapabilities directly to users or roles, and rather, to grant any of the primitive capabilities. There are two types of primitive capabilities. Those that are automatically mapped to a metacapability when you register a custom post type with a specific capability type, and those that are not. Understand the differences, take a look at the getPostType capabilities function. And the WP includes post.php file. This is the same function you saw earlier in the WP post type class that builds up the capabilities object for the cap property on the custom post type object. Here you can see that the default capabilities are always set, which includes the three metacapabilities and five primitive capabilities. Then an additional six primitive capabilities are added. These are known as the primitive capabilities used within the mapMetacap function. Below that, the createPost capability is automatically mapped to edit posts for a total of 15 possible capabilities. You might be wondering what the mapMetacap function is and what it does. The mapMetacap function is used whenever the current userCAN function is called to check if a user role has a specific capability. If you dive into the code underneath the current userCAN function, you'll see it eventually calls the hasCAP function of the WP user class, which in turn calls the mapMetacap function. MapMetacap maps a capability to the primitive capabilities required of the given user to satisfy the capability being checked, based on the context of the check. The mapMetacap function does not check whether the user has the required capabilities. It just returns what the required capabilities are. To help explain this, let's look at an example. Let's say you want to use current userCAN to check if the current user can edit a specific post by checking against the editPost meta capability. Based on the context of the post, the result for this check can depend on a few factors. Is the user the author of the post? And is the post already published? When checking against the editPost capability, the mapMetacap function will check all these factors and return the correct set of primitive capabilities that the user must have to allow editing of the post. So if the post is written by someone else and it's published, it would turn an array containing the editOthersPosts and editPublishPost capabilities. In this case, the user would not only need the editPost capability, but also the editOthersPosts and editPublishPost capabilities in order to edit this post. If you look back at the different list of capabilities that you might need for your writer, you'll notice that some exist as meta capabilities, some as the automatically mapped primitive capabilities, and some exist as the primitive capabilities mapped inside MapMetacap. As discussed earlier, you should not grant the meta capabilities directly to users or roles and rather to add any of the primitive capabilities. Therefore, you're going to want to add the following automatically mapped primitive capabilities. Edit Stories, Delete Stories, Publish Stories, and Read Private Stories. But you're also going to want to add the following primitive capabilities mapped inside MapMetacap. The Read capability, the Delete Private Stories capability, and the Edit Private Stories capability. There are two ways you can do this. You can add additional primitive capabilities you need by using the capabilities arguments of the registered post type function. So under your capability type, you could add the specific capabilities you need. If you now take a look at the cap property of the custom post type object, you'll see that the capabilities object now includes all the capabilities you added. You can now add these capabilities to your writer role. The other option is to set the MapMetacap argument to true. This will automatically also map the meta capabilities to the primitive capabilities used inside MapMetacap. With this option, all the possible capabilities will be mapped and you can then add the specific ones you need to your user roles. But you can take out the capabilities and include MapMetacap. And if we inspect the list of capabilities, they are all there. Ultimately, it's up to you which option you choose. The capabilities argument is a bit more flexible and provides more fine grain control, but using the MapMetacap argument requires less work on your part. It's also worth noting that either option will not automatically add the capabilities to any role. You still need to define that yourself. One other thing changed when you set the custom capability type argument. Your admin user can no longer access stories in the dashboard. This is because the administrator user role has not been given the capabilities to access stories. Because your admin user role should be able to access everything, now would be a good time to add all the capabilities to the admin role. This is another reason to consider using the MapMetacap argument, as it will automatically map all the capabilities which you can assign to the admin role. As you learned in the developing WordPress user roles and capabilities tutorial, the right place to do this is inside a plugin activation hook. First, deactivate your plugin so that you can activate it to trigger the activation hook. Inside plugins, we can deactivate. Then create the register activation hook in your plugin and use it to assign the required capabilities to the administrator user. Because there are so many capabilities to add, it's a good idea to store them in an array and then loop through them to add them to the role. If you ever need to add or remove capabilities, you can just update the array. You do not need to add the read capability, as this is already added to the administrator role. At the same time, you can add the deactivation hook to remove the capabilities from the administrator role if the plugin is ever deactivated. Now you can create the writer role and only apply the capabilities that the writer needs. Inside the activation hook callback function, you can create the writer role using the add role function and add the relevant capabilities. As a reminder, the writer can only create, edit and delete unpublished stories and writers should also be able to publish their own stories but not edit or delete stories once published. Note that you can do this inside an existing activation hook callback function or you can create a new one. At the same time, it's a good idea to update the deactivation callback function or create a new one to remove the role when the plugin is deactivated. Now activate the plugin and check that the admin can create, edit, publish and edit a published story. So if we activate, admins can now access stories and create new stories. Then create a new user and assign the writer role to the user. Login is the writer and check that the writer can create, edit and publish a story but cannot delete a published story. They also should not be able to edit or delete anyone else's stories. So let's create the writer user, give them a simple password and assign the writer role and then login as the writer. The writer can access stories but cannot edit the admin story. They can create a new story. They can save and edit their stories but once published, writers cannot edit their own stories. Your specific requirements will determine how you set your capabilities for your custom post types. Perhaps you don't need the administrator role to access stories. Then you could just have manually set the additional capabilities you need and not use the map metacap argument. Either way, whatever capabilities you configure will only be applied once you add them to a role. Generally your process for setting up custom post type capabilities will be to set the correct capability type or types. Then use either the capabilities argument or the map metacap argument to set up any additional capabilities you require and then to either update an existing role or create a new custom role and assign the relevant capabilities needed. Always remember to set up roles and or capabilities inside an activation hook callback function and remove them inside a deactivation hook callback function. You could read up on how capabilities can be mapped on custom post types in the parameter detail information section of the register post type documentation in the WordPress developer handbook as well as the function reference entry on the get post type capabilities function. Happy coding.