If you read WordPress Codex “Roles and Capabilities” page or use plugin like User Role Editor to manage WordPress roles and user capabilities, you saw there this capabilities set, in alphabetical order:
delete_others_pages, delete_others_posts, delete_pages, delete_posts, delete_private_pages, delete_private_posts, delete_published_pages, delete_published_posts.
While these capabilities names are self-explained, it’s interesting, how and where WordPress checks them.
Going through WordPress core source code I discovered that these capabilities are not used directly. The only place where you can find capability name string like ‘delete_others_posts’ is wp-admin/includes/schema.php
file, where these capabilities set was defined for WordPress version 2.1.
669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 | /** * Create and modify WordPress roles for WordPress 2.1. * * @since 2.1.0 */ function populate_roles_210() { $roles = array('administrator', 'editor'); foreach ($roles as $role) { $role =& get_role($role); if ( empty($role) ) continue; $role->add_cap('edit_others_pages'); $role->add_cap('edit_published_pages'); $role->add_cap('publish_pages'); $role->add_cap('delete_pages'); $role->add_cap('delete_others_pages'); $role->add_cap('delete_published_pages'); $role->add_cap('delete_posts'); $role->add_cap('delete_others_posts'); $role->add_cap('delete_published_posts'); $role->add_cap('delete_private_posts'); $role->add_cap('edit_private_posts'); $role->add_cap('read_private_posts'); $role->add_cap('delete_private_pages'); $role->add_cap('edit_private_pages'); $role->add_cap('read_private_pages'); } $role =& get_role('administrator'); if ( ! empty($role) ) { $role->add_cap('delete_users'); $role->add_cap('create_users'); } $role =& get_role('author'); if ( ! empty($role) ) { $role->add_cap('delete_posts'); $role->add_cap('delete_published_posts'); } $role =& get_role('contributor'); if ( ! empty($role) ) { $role->add_cap('delete_posts'); } } |
Thus, according to the code above, starting from WordPress version 2.1 ‘administrator’ and ‘editor’ roles can delete all WordPress posts and pages, despite its status and author.Author can delete own posts only including published posts. Contributor can delete own unpublished posts only.
Does the fact, that these capabilities are not called by WordPress directly, mean, that they just added to WordPress capabilities list, but they are not in use yet? No, they are in use. WordPress uses them indirectly, via sophisticated mechanism of mapping so-called meta capabilities, like ‘edit_post’, ‘edit_page’ to the primitive capabilities, like deletion capabilities list mentioned above.
Is it interesting for you? Let’s see together, how does WordPress realize that.
When user are going to move a post or page to trash or delete it forever, wp-admin/edit.php
script is called. WordPress checks if user has necessary capabilities set for deletion of selected post of page this way:
For moving post or page to Trash:
77 78 79 80 81 | case 'trash': $trashed = 0; foreach( (array) $post_ids as $post_id ) { if ( !current_user_can($post_type_object->cap->delete_post, $post_id) ) wp_die( __('You are not allowed to move this item to the Trash.') ); |
In order to delete post or page permanently:
103 104 105 106 107 108 109 | case 'delete': $deleted = 0; foreach( (array) $post_ids as $post_id ) { $post_del = get_post($post_id); if ( !current_user_can($post_type_object->cap->delete_post, $post_id) ) wp_die( __('You are not allowed to delete this item.') ); |
As you can see, WordPress checks if user has ‘delete_post’ capability. Let’s look to the WordPress roles. No one role contains ‘delete_post’ capability. It simply doesn’t exist, even for ‘Administrator’ role. What’s the puzzle WordPress authors prepared for us? Let’s go further.
Looking at the call stack I see that WordPress calls map_meta_cap()
function from capabilities.php
file via user object method has_cap()
.
891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 | /** * Whether user has capability or role name. * * This is useful for looking up whether the user has a specific role * assigned to the user. The second optional parameter can also be used to * check for capabilities against a specific object, such as a post or user. * * @since 2.0.0 * @access public * * @param string|int $cap Capability or role name to search. * @return bool True, if user has capability; false, if user does not have capability. */ function has_cap( $cap ) { if ( is_numeric( $cap ) ) { _deprecated_argument( __FUNCTION__, '2.0', __('Usage of user levels by plugins and themes is deprecated. Use roles and capabilities instead.') ); $cap = $this->translate_level_to_cap( $cap ); } $args = array_slice( func_get_args(), 1 ); $args = array_merge( array( $cap, $this->ID ), $args ); $caps = call_user_func_array( 'map_meta_cap', $args ); |
What does the function map_meta_cap()
do? This is the definition of function map_meta_cap()
from capabilities.php
file:
964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 | /** * Map meta capabilities to primitive capabilities. * * This does not actually compare whether the user ID has the actual capability, * just what the capability or capabilities are. Meta capability list value can * be 'delete_user', 'edit_user', 'remove_user', 'promote_user', 'delete_post', * 'delete_page', 'edit_post', 'edit_page', 'read_post', or 'read_page'. * * @since 2.0.0 * * @param string $cap Capability name. * @param int $user_id User ID. * @return array Actual capabilities for meta capability. */ function map_meta_cap( $cap, $user_id ) { |
As for this post theme let’s look for ‘delete_post’ capability occurrence:
1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 | case 'delete_post': case 'delete_page': $post = get_post( $args[0] ); if ( 'revision' == $post->post_type ) { $post = get_post( $post->post_parent ); } $post_type = get_post_type_object( $post->post_type ); if ( ! $post_type->map_meta_cap ) { $caps[] = $post_type->cap->$cap; // Prior to 3.1 we would re-call map_meta_cap here. if ( 'delete_post' == $cap ) $cap = $post_type->cap->$cap; break; } $post_author_id = $post->post_author; // If no author set yet, default to current user for cap checks. if ( ! $post_author_id ) $post_author_id = $user_id; $post_author_data = $post_author_id == get_current_user_id() ? wp_get_current_user() : get_userdata( $post_author_id ); // If the user is the author... if ( is_object( $post_author_data ) && $user_id == $post_author_data->ID ) { // If the post is published... if ( 'publish' == $post->post_status ) { $caps[] = $post_type->cap->delete_published_posts; } elseif ( 'trash' == $post->post_status ) { if ('publish' == get_post_meta($post->ID, '_wp_trash_meta_status', true) ) $caps[] = $post_type->cap->delete_published_posts; } else { // If the post is draft... $caps[] = $post_type->cap->delete_posts; } } else { // The user is trying to edit someone else's post. $caps[] = $post_type->cap->delete_others_posts; // The post is published, extra cap required. if ( 'publish' == $post->post_status ) $caps[] = $post_type->cap->delete_published_posts; elseif ( 'private' == $post->post_status ) $caps[] = $post_type->cap->delete_private_posts; } break; |
Meta capabilities delete_post
and delete_page
are processed here together. Here WordPress checks if current user is the author of this post and according to post status expand one meta capability, like ‘delete_post’ to the whole set of primitive capabilities to check, like ‘delete_others_posts’, ‘delete_published_posts’. How those capabilities are defined, depends from the post type. For ‘post’ post type they are ‘delete_others_posts’, ‘delete_published_posts’, for ‘pages’ post type they are ‘delete_others_pages’, ‘delete_published_pages’, etc.
For advanced or curious PHP developers comment from the get_post_type_capabilities()
function definition (file wp-admin/post.php
) in relation to the linked register_post_type()
function from the same file could help to look on the subject from the wider base:
1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 | /** * Builds an object with all post type capabilities out of a post type object * * Post type capabilities use the 'capability_type' argument as a base, if the * capability is not set in the 'capabilities' argument array or if the * 'capabilities' argument is not supplied. * * The capability_type argument can optionally be registered as an array, with * the first value being singular and the second plural, e.g. array('story, 'stories') * Otherwise, an 's' will be added to the value for the plural form. After * registration, capability_type will always be a string of the singular value. * * By default, seven keys are accepted as part of the capabilities array: * * - edit_post, read_post, and delete_post are meta capabilities, which are then * generally mapped to corresponding primitive capabilities depending on the * context, which would be the post being edited/read/deleted and the user or * role being checked. Thus these capabilities would generally not be granted * directly to users or roles. * * - edit_posts - Controls whether objects of this post type can be edited. * - edit_others_posts - Controls whether objects of this type owned by other users * can be edited. If the post type does not support an author, then this will * behave like edit_posts. * - publish_posts - Controls publishing objects of this post type. * - read_private_posts - Controls whether private objects can be read. * * These four primitive capabilities are checked in core in various locations. * There are also seven other primitive capabilities which are not referenced * directly in core, except in map_meta_cap(), which takes the three aforementioned * meta capabilities and translates them into one or more primitive capabilities * that must then be checked against the user or role, depending on the context. * * - read - Controls whether objects of this post type can be read. * - delete_posts - Controls whether objects of this post type can be deleted. * - delete_private_posts - Controls whether private objects can be deleted. * - delete_published_posts - Controls whether published objects can be deleted. * - delete_others_posts - Controls whether objects owned by other users can be * can be deleted. If the post type does not support an author, then this will * behave like delete_posts. * - edit_private_posts - Controls whether private objects can be edited. * - edit_published_posts - Controls whether published objects can be edited. * * These additional capabilities are only used in map_meta_cap(). Thus, they are * only assigned by default if the post type is registered with the 'map_meta_cap' * argument set to true (default is false). * * @see map_meta_cap() * @since 3.0.0 * * @param object $args Post type registration arguments * @return object object with all the capabilities as member variables */ function get_post_type_capabilities( $args ) { |
Finally WordPress checks, if user has all primitive capabilities added by map_meta_cap() function and decides to permit or prohibit requested operation.
This post is built on the base of WordPress 3.5 Beta 1 core source code.
Tags: capability, WordPress