Hey there, and welcome to LearnWordPress.
In this tutorial, you'll belearning how to restrict access
to custom post types via thebuilt in capability system. You
will learn how the differentcapabilities are assigned to a
Custom Post Type, and how tomanage them, as well as how to
add Custom Post Typecapabilities to new or existing
roles.
Let's say you need to create aplugin that allows a site owner
to add the followingfunctionality to a WordPress
site. The ability to define aCustom Post Type called Story.
The ability to create users witha writer role who could only
create, edit and deleteunpublished stories. The writer
role should also be able topublish their own stories, but
not edit or delete stories. Oncepublished. Any site
administrators should be able tocreate, edit and delete any
stories, regardless of whetherthey are published or not, or
who they were created by.
To create the story Custom PostType, you might have some code
in your plugin that looks likethis. Here you're using the
register_post_type function toregister the story Custom Post
Type, and passing in somespecific arguments to turn
certain things on and off.
When a Custom Post Type isregistered in this way, it
inherits the capabilities of thePost post type. To see these
capabilities in your dashboardduring development, you might
register a sub menu page thatdisplays the capabilities of the
story Custom Post Type, which isonly available to admins. Inside
the callback function for thispage, you could access the
Custom Post Type object from thewp_post_types global array
variable. You could then printout the capability_type,
map_meta_cap and cap propertiesof the story post type. With the
plugin active when logged in asan administrator user, this is
what you might see when youvisit the page in your
dashboard. As you can see, thecapability type inherits from
post map_meta_cap is set to trueand the cap property contains an
array of capabilities that areinherited from the default
WordPress capabilities for apost.
To understand how thesecapabilities are mapped to the
Custom Post Type, you need tounderstand how these three
properties are set up. To dothis, take a look at the
register_post_type function,which currently exists at line
1679 of the wp-includes/post.phpfile. On line 1694 the
WP_Post_Type object is createdand the args array is passed to
the class constructor.
If you open the file for theWP_Post_Type class, you'll see
the following code at aroundline 541. Here if the
capabilities argument is empty,and the map_meta_cap argument is
null and the capability_typeargument is set to either post
or page, then the map_meta_capargument is set to true. If you
scroll back up to line 480, youcan see that the default value
for capability_type is post. Sobecause your Custom Post Type
doesn't pass any of thesearguments, then map_meta_cap is
set to true. Later on the capproperty is set to the result of
the get_post_type_capabilitiesfunction. This function accepts
the args array as an argumentand returns an array of
capabilities. So because thecapability_type argument is set
to post and the map_meta_capargument is set to true, then
the cap property on the storyCustom Post Type will be set to
an array of capabilities thatare mapped to the default
WordPress capabilities for apost.
You can control thesecapabilities by passing relevant
values to the three argumentsthat are used above as described
in the developer handbook entryfor register_post_type. Possible
arguments are capability_type,capabilities, and map_meta_cap.
The first step in setting customcapabilities for your Custom
Post Type is to set thecapability_type argument to
something other than post. Inthis case, you could set it to
story. If you now take a look atthe cap property on your Custom
Post Type, you will see that thecapabilities have changed.
However, it's not using thecorrect pluralization of story.
So you need to explicitly setthe plural version of the
capability type by passing in anarray of values, the first being
the singular name and the secondbeing the pluralization.
So we will change this to anarray, story and stories.
Now if you take a look at thecap property on your Custom Post
Type, you will see that thecapabilities have changed again.
Notice that this list does notinclude all the same
capabilities when they wereinherited from post. This is
because by setting a capabilitytype the first conditional check
in the WP_Post_Type class youlooked at earlier returns false.
And so the map_meta_cap argumentis now set to false, which means
the additional capabilities arenot mapped for the Custom Post
Type.
Now it'd be a good time to talkabout the different types of
capabilities. There are threetypes of capabilities available
to custom post types. Metacapabilities are capabilities
that are mapped to primitivecapabilities. The three meta
capabilities are edit_post,read_post, and delete_post. As
an example, the edit_post metacapability is mapped to
primitive capabilities likeedit_posts and
edit_others_posts. Because themeta capabilities are
automatically mapped to certainprimitive capabilities, it's
generally recommended not togrant the meta capabilities
directly to users or roles, andrather to grant any of the
primitive capabilities.
There are two types of primitivecapabilities; those that are
automatically mapped to a metacapability when you register a
Custom Post Type with a specificcapability_type, and those that
are not. To understand thedifferences take a look at the
get_post_type_capabilitiesfunction in the
wp-includes/post.php file. Thisis the same function you saw
earlier in the WP_Post_Typeclass that builds up the
capabilities object for the catproperty on the Custom Post Type
object. Here you can see thatthe default capabilities are
always set, which includes thethree meta capabilities and five
primitive capabilities, then anadditional six primitive
capabilities are added. Theseare known as the primitive
capabilities used within themap_meta_cap function. Below
that the create_post capabilityis automatically mapped to edit
posts for a total of 15 possiblecapabilities.
You might be wondering what themap_meta_cap function is and
what it does. The map_meta_capfunction is used whenever the
current_user_can function iscalled to check if a user role
has a specific capability. Ifyou dive into the code
underneath the current_user_canfunction, you'll see it
eventually calls the has_capfunction of the WP_User class,
which in turn calls themap_meta_cap function.
map_meta_cap maps a capabilityto the primitive capabilities
required of a given user tosatisfy the capability being
checked, based on the context ofthe check. The map_meta_cap
function does not check whetherthe user has the required
capabilities, it just returnswhat the required capabilities
are.
To help explain this, let's lookat an example. Let's say you
want to use current_user_can tocheck if the current user can
edit a specific post by checkingagainst the edit_post meta
capability. Based on the contextof the post, the result for this
check can depend on a fewfactors; is the user the author
of the post, and is the postalready published. When checking
against the edit_postcapability, the map_meta_cap
function will check all thesefactors and return the correct
set of primitive capabilitiesthat the user must have to allow
editing of the post. So if thepost is written by someone else,
and it's published, it returnsan array containing the
edit_others_posts andedit_published_posts
capabilities. In this case, theuser would not only need the
edit_post capability, but alsothe edit_others_posts and
edit_published_postscapabilities in order to edit
this post.
If you look back at thedifferent lists of capabilities
that you might need for yourwriter, you'll notice that some
exist as meta capabilities, someis the automatically mapped
primitive capabilities, and someexist as the primitive
capabilities mapped insidemap_meta_cap. As discussed
earlier, you should not grantthe meta capabilities directly
to users or roles, and rather toadd any of the primitive
capabilities. Therefore, you'regoing to want to add the
following automatically mappedprimitive capabilities;
edit_stories, delete_stories,publish_stories, and
read_private_stories. But you'realso going to want to add the
following primitive capabilitiesmapped inside map_meta_cap; the
read capability, thedelete_private_stories
capability and theedit_private_stories capability.
There are two ways you can dothis. You can add additional
primitive capabilities you needby using the capabilities
argument of theregister_post_type function. So
under your capability type, youcould add the specific
capabilities you need. If younow take a look at the cap
property of the Custom Post Typeobject, you'll see that the
capabilities object now includesall the capabilities you added.
You can now add thesecapabilities to your writer
role.
The other option is to set themap_meta_cap argument to true.
This will automatically also mapthe meta capabilities to the
primitive capabilities usedinside map_meta_cap. With this
option, all the possiblecapabilities will be mapped. And
you can then add the specificones you need to your user
roles.
So you can take out thecapabilities include
map_meta_cap, and if we inspectthe list of capabilities, they
are all there.
Ultimately, it's up to you whichoption you choose. The
capabilities argument is a bitmore flexible, and provides more
fine grained control. But usingthe map_meta_cap argument
requires less work on your part.It's also worth noting that
either option will notautomatically add the
capabilities to any role, youstill need to define that yourself.
One other thing change when youset the custom capability type
argument, your admin user can nolonger access stories in the
dashboard. This is because theadministrator user role has not
been given the capabilities toaccess stories, because your
admin user role should be ableto access everything. Now it'd
be a good time to add all thecapabilities to the admin role.
This is another reason toconsider using the map_meta_cap
argument as it willautomatically map all the
capabilities, which you canassign to the admin role.
As you learned in the developingWordPress user roles and
capabilities tutorial, the rightplace to do this is inside a
plugin activation hook. First,deactivate your plugin so that
you can activate it to triggerthe activation hook.
Inside plugins we can deactivate.
Then create theregister_activation_hook in your
plugin and use it to assign therequired capabilities to the
administrator user. Becausethere are so many capabilities
to add it's a good idea to storethem in an array, and then loop
through them to add them to therole. If you ever need to add or
remove capabilities, you canjust update the array. You do
not need to add the readcapability as this is already
added to the administrator role.At the same time, you can add
the deactivation hook to removethe capabilities from the
administrator role, if theplugin is ever deactivated. Now
you can create the writer roleand only apply the capabilities
that the writer needs.
Inside the activation hookcallback function, you can
create the writer role using theadd_role function and add the
relevant capabilities.
As a reminder, the writer canonly create, edit and delete and
publish stories. And writersshould also be able to publish
their own stories, but not editor delete stories once
published. Note that you can dothis inside an existing
activation hook callbackfunction, or you can create a
new one. At the same time, it'sa good idea to update the
deactivation callback functionor create a new one to remove
the role when the plugin isdeactivated. Now activate the
plugin and check that the admincan create, edit, publish and
edit a published story.
So if we activate, admins cannow access stories and create
new stories.
Then create a new user andassign the writer role to the user.
Log in as the writer and checkthat the writer can create, edit
and publish the story, butcannot delete a published story.
They also should not be able toedit or delete anyone else's stories.
So let's create the writer usergive them a simple password and
assign the writer role. And thenlogin as the writer
writer can access stories butcannot edit the admin story.
They can create a new story.
They can save and edit theirstories. But once published,
writers cannot edit their own stories.
Your specific requirements willdetermine how you set your
capabilities for your custompost types. Perhaps you don't
need the administrator role toaccess stories, then you could
just have manually set theadditional capabilities you need
and not use the magnetic appargument. Either way, whatever
capabilities you configure willonly be applied once you add
them to a role.
Generally your process forsetting up Custom Post Type
capabilities will be to set thecorrect capability type or
types. Then use either thecapabilities argument or the map
meta cap argument to set up anyadditional capabilities require
and then to either update anexisting role or create a new
custom role and assign therelevant capabilities needed.
Always remember to set up rolesand or capabilities inside an
activation hook callbackfunction and remove them inside
a deactivation hook callback function.
You can read up on howcapabilities can be mapped on
custom post types in theparameter detail information
section of theregister_post_type documentation
in the WordPress developerhandbook, as well as the
function reference entry on theget_post_type_capabilities function.
Happy coding!