Custom user role that can only edit specific (non-custom-type) page and all child pages [duplicate]
IT Nursery
May 8, 2022
0
My site has several static pages and several custom post types. I’m trying to create a custom user role called students and give students access to ONLY certain custom post types and certain specific static pages.
I understand how to do this with custom post types by using add_cap() and populating the ‘capability_type’ and ‘map_meta_cap’ fields passed to register_post_type for that custom post type.
However, I don’t understand how to do this for generic pages (which are not custom, but have been populated with different content). Specifically, I would like to take a parent page called internal-resources and give the student users editing capability for that specific page. There are some child pages of the internal-resources page they should be able to edit too. Finally, I would like them to be able to create new child pages under internal-resources. However, other static pages like Research and People they should NOT be able to edit. This shouldn’t be too difficult, right?
Thanks for the help!!
1 Answer 1
There’s no way in WordPress to assign the capability for editing (or any action) a specific post to a role.
However, you can filter capabilities checks and change them on the fly using the map_meta_cap.
When handling post permissions, WordPress ultimately deals in just 4 capabilties:
edit_post
read_post
delete_post
publish_post
Then whenever an action is performed on a post it maps these capabilities to the ‘primitive’ capabilities. These are the capabilities you’ll be more familiar with:
publish_posts
edit_posts
edit_others_posts
edit_private_posts
edit_published_posts
read
read_private_posts
delete_posts
delete_private_posts
delete_published_posts
delete_others_posts
There is also create_posts, but as far as I can tell this is only used in some REST endpoints and for controlling whether some UI appears. When saving a post create_posts is mapped to edit_posts.
What map_meta_cap() does is that when someone tries to edit a post it determines which primitive capability is required.
So if a user tries to edit a post, map_meta_cap() checks if they are the author of that post. If they are then the edit_post meta capability will be mapped to edit_posts. If they are not the author it will be mapped to edit_others_posts. WordPress will then check if the user has the mapped capability and respond accordingly.
So if you want to change permissions on a per-page basis, you need to filter map_meta_cap to change the way it assigns the meta capabilities.
In your example, you want to let users edit_page for the Internal Resources page (and others), but not edit any other pages. This is a little bit tricky because to do this they need access to the Pages menu in the Dashboard. So you’ll need to grant your student role the edit_pages and publish_pages capabilities, and then use the filter to revoke those capabilities on a page-by-page basis:
function wpse_293259_map_meta_cap( $required_caps, $cap, $user_id, $args ) {
if ( in_array( $cap, ['edit_post', 'publish_post'] ) ) {
$page_id = $args[0]; // The ID of the post being edited.
$student_pages = [1,2,3]; // The IDs of the pages students are allowed to edit.
/**
* If the page being edited is not one students can edit, check if the user
* is a student. If they are, set the required capabilities to 'do_not_allow'
* to prevent them editing.
*/
if ( ! in_array( $page_id, $student_pages ) ) {
$user = new WP_User( $user_id );
if ( in_array( 'students', $user->roles ) ) {
$required_caps = ['do_not_allow'];
}
}
}
return $required_caps;
}
add_filter( 'map_meta_cap', 'wpse_293259_map_meta_cap', 10, 4 );
This will prevent publishing or editing of pages that aren’t in the $student_pages.
I have not been able to figure out a good way to allow users to publish pages but only if they’re a child of a particular page. Every mix of editing and publishing capabilities I’ve tried has resulted in weird behaviour. I don’t think child pages are a good way to manage permissions because they are a thing that can be changed on the page editor. This means that you would be changing permissions between publishing a post and being redirected back to edit it.
You might be best off using the technique I described to allow editing of the Internal Resources page, but then break the sub-pages out into a separate post type with its own permissions.