2005-07-23 12:16:03 -04:00
< ? php
2008-09-16 17:51:36 -04:00
/**
2015-09-22 09:06:25 -04:00
* Core User Role & Capabilities API
2008-09-16 17:51:36 -04:00
*
* @ package WordPress
2015-09-22 09:46:25 -04:00
* @ subpackage Users
2015-11-20 02:24:30 -05:00
*/
/**
* 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' .
*
2015-09-22 09:06:25 -04:00
* @ since 2.0 . 0
2015-11-20 02:24:30 -05:00
*
2016-01-15 07:20:27 -05:00
* @ global array $post_type_meta_caps Used to get post type meta capabilities .
*
2015-11-20 02:24:30 -05:00
* @ param string $cap Capability name .
* @ param int $user_id User ID .
* @ param int $object_id Optional . ID of the specific object to check against if `$cap` is a " meta " cap .
* " Meta " capabilities , e . g . 'edit_post' , 'edit_user' , etc . , are capabilities used
* by map_meta_cap () to map to other " primitive " capabilities , e . g . 'edit_posts' ,
* 'edit_others_posts' , etc . The parameter is accessed via func_get_args () .
* @ return array Actual capabilities for meta capability .
2008-09-16 17:51:36 -04:00
*/
2015-11-20 02:24:30 -05:00
function map_meta_cap ( $cap , $user_id ) {
$args = array_slice ( func_get_args (), 2 );
$caps = array ();
switch ( $cap ) {
case 'remove_user' :
2016-12-12 16:42:42 -05:00
// In multisite the user must be a super admin to remove themselves.
if ( isset ( $args [ 0 ] ) && $user_id == $args [ 0 ] && ! is_super_admin ( $user_id ) ) {
$caps [] = 'do_not_allow' ;
} else {
$caps [] = 'remove_users' ;
}
2015-11-20 02:24:30 -05:00
break ;
case 'promote_user' :
case 'add_users' :
$caps [] = 'promote_users' ;
break ;
case 'edit_user' :
case 'edit_users' :
// Allow user to edit itself
if ( 'edit_user' == $cap && isset ( $args [ 0 ] ) && $user_id == $args [ 0 ] )
break ;
// In multisite the user must have manage_network_users caps. If editing a super admin, the user must be a super admin.
if ( is_multisite () && ( ( ! is_super_admin ( $user_id ) && 'edit_user' === $cap && is_super_admin ( $args [ 0 ] ) ) || ! user_can ( $user_id , 'manage_network_users' ) ) ) {
$caps [] = 'do_not_allow' ;
} else {
$caps [] = 'edit_users' ; // edit_user maps to edit_users.
}
break ;
case 'delete_post' :
case 'delete_page' :
$post = get_post ( $args [ 0 ] );
if ( ! $post ) {
$caps [] = 'do_not_allow' ;
break ;
}
if ( 'revision' == $post -> post_type ) {
$post = get_post ( $post -> post_parent );
if ( ! $post ) {
$caps [] = 'do_not_allow' ;
break ;
}
}
2016-08-26 14:23:31 -04:00
if ( ( get_option ( 'page_for_posts' ) == $post -> ID ) || ( get_option ( 'page_on_front' ) == $post -> ID ) ) {
$caps [] = 'manage_options' ;
break ;
}
2015-11-20 02:24:30 -05:00
$post_type = get_post_type_object ( $post -> post_type );
if ( ! $post_type ) {
/* translators: 1: post type, 2: capability name */
_doing_it_wrong ( __FUNCTION__ , sprintf ( __ ( 'The post type %1$s is not registered, so it may not be reliable to check the capability "%2$s" against a post of that type.' ), $post -> post_type , $cap ), '4.4.0' );
$caps [] = 'edit_others_posts' ;
break ;
}
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 ;
}
// If the post author is set and the user is the author...
if ( $post -> post_author && $user_id == $post -> post_author ) {
2015-11-28 21:27:18 -05:00
// If the post is published or scheduled...
if ( in_array ( $post -> post_status , array ( 'publish' , 'future' ), true ) ) {
2015-11-20 02:24:30 -05:00
$caps [] = $post_type -> cap -> delete_published_posts ;
} elseif ( 'trash' == $post -> post_status ) {
2015-11-28 21:27:18 -05:00
$status = get_post_meta ( $post -> ID , '_wp_trash_meta_status' , true );
if ( in_array ( $status , array ( 'publish' , 'future' ), true ) ) {
2015-11-20 02:24:30 -05:00
$caps [] = $post_type -> cap -> delete_published_posts ;
2015-11-28 21:27:18 -05:00
} else {
$caps [] = $post_type -> cap -> delete_posts ;
2015-11-20 02:24:30 -05:00
}
} 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 ;
2015-11-28 21:27:18 -05:00
// The post is published or scheduled, extra cap required.
if ( in_array ( $post -> post_status , array ( 'publish' , 'future' ), true ) ) {
2015-11-20 02:24:30 -05:00
$caps [] = $post_type -> cap -> delete_published_posts ;
} elseif ( 'private' == $post -> post_status ) {
$caps [] = $post_type -> cap -> delete_private_posts ;
}
}
2018-05-15 16:59:25 -04:00
/*
* Setting the privacy policy page requires `manage_privacy_options` ,
* so deleting it should require that too .
*/
if ( ( int ) get_option ( 'wp_page_for_privacy_policy' ) === $post -> ID ) {
$caps = array_merge ( $caps , map_meta_cap ( 'manage_privacy_options' , $user_id ) );
}
2015-11-20 02:24:30 -05:00
break ;
// edit_post breaks down to edit_posts, edit_published_posts, or
// edit_others_posts
case 'edit_post' :
case 'edit_page' :
$post = get_post ( $args [ 0 ] );
if ( ! $post ) {
$caps [] = 'do_not_allow' ;
break ;
}
if ( 'revision' == $post -> post_type ) {
$post = get_post ( $post -> post_parent );
if ( ! $post ) {
$caps [] = 'do_not_allow' ;
break ;
}
}
$post_type = get_post_type_object ( $post -> post_type );
if ( ! $post_type ) {
/* translators: 1: post type, 2: capability name */
_doing_it_wrong ( __FUNCTION__ , sprintf ( __ ( 'The post type %1$s is not registered, so it may not be reliable to check the capability "%2$s" against a post of that type.' ), $post -> post_type , $cap ), '4.4.0' );
$caps [] = 'edit_others_posts' ;
break ;
}
if ( ! $post_type -> map_meta_cap ) {
$caps [] = $post_type -> cap -> $cap ;
// Prior to 3.1 we would re-call map_meta_cap here.
if ( 'edit_post' == $cap )
$cap = $post_type -> cap -> $cap ;
break ;
}
// If the post author is set and the user is the author...
if ( $post -> post_author && $user_id == $post -> post_author ) {
2015-11-28 21:27:18 -05:00
// If the post is published or scheduled...
if ( in_array ( $post -> post_status , array ( 'publish' , 'future' ), true ) ) {
2015-11-20 02:24:30 -05:00
$caps [] = $post_type -> cap -> edit_published_posts ;
} elseif ( 'trash' == $post -> post_status ) {
2015-11-28 21:27:18 -05:00
$status = get_post_meta ( $post -> ID , '_wp_trash_meta_status' , true );
if ( in_array ( $status , array ( 'publish' , 'future' ), true ) ) {
2015-11-20 02:24:30 -05:00
$caps [] = $post_type -> cap -> edit_published_posts ;
2015-11-28 21:27:18 -05:00
} else {
$caps [] = $post_type -> cap -> edit_posts ;
2015-11-20 02:24:30 -05:00
}
} else {
// If the post is draft...
$caps [] = $post_type -> cap -> edit_posts ;
}
} else {
// The user is trying to edit someone else's post.
$caps [] = $post_type -> cap -> edit_others_posts ;
2015-11-28 21:27:18 -05:00
// The post is published or scheduled, extra cap required.
if ( in_array ( $post -> post_status , array ( 'publish' , 'future' ), true ) ) {
2015-11-20 02:24:30 -05:00
$caps [] = $post_type -> cap -> edit_published_posts ;
} elseif ( 'private' == $post -> post_status ) {
$caps [] = $post_type -> cap -> edit_private_posts ;
}
}
2018-05-15 16:59:25 -04:00
/*
* Setting the privacy policy page requires `manage_privacy_options` ,
* so editing it should require that too .
*/
if ( ( int ) get_option ( 'wp_page_for_privacy_policy' ) === $post -> ID ) {
$caps = array_merge ( $caps , map_meta_cap ( 'manage_privacy_options' , $user_id ) );
}
2015-11-20 02:24:30 -05:00
break ;
case 'read_post' :
case 'read_page' :
$post = get_post ( $args [ 0 ] );
if ( ! $post ) {
$caps [] = 'do_not_allow' ;
break ;
}
if ( 'revision' == $post -> post_type ) {
$post = get_post ( $post -> post_parent );
if ( ! $post ) {
$caps [] = 'do_not_allow' ;
break ;
}
}
$post_type = get_post_type_object ( $post -> post_type );
if ( ! $post_type ) {
/* translators: 1: post type, 2: capability name */
_doing_it_wrong ( __FUNCTION__ , sprintf ( __ ( 'The post type %1$s is not registered, so it may not be reliable to check the capability "%2$s" against a post of that type.' ), $post -> post_type , $cap ), '4.4.0' );
$caps [] = 'edit_others_posts' ;
break ;
}
if ( ! $post_type -> map_meta_cap ) {
$caps [] = $post_type -> cap -> $cap ;
// Prior to 3.1 we would re-call map_meta_cap here.
if ( 'read_post' == $cap )
$cap = $post_type -> cap -> $cap ;
break ;
}
$status_obj = get_post_status_object ( $post -> post_status );
if ( $status_obj -> public ) {
$caps [] = $post_type -> cap -> read ;
break ;
}
if ( $post -> post_author && $user_id == $post -> post_author ) {
$caps [] = $post_type -> cap -> read ;
} elseif ( $status_obj -> private ) {
$caps [] = $post_type -> cap -> read_private_posts ;
} else {
$caps = map_meta_cap ( 'edit_post' , $user_id , $post -> ID );
}
break ;
case 'publish_post' :
$post = get_post ( $args [ 0 ] );
if ( ! $post ) {
$caps [] = 'do_not_allow' ;
break ;
}
$post_type = get_post_type_object ( $post -> post_type );
if ( ! $post_type ) {
/* translators: 1: post type, 2: capability name */
_doing_it_wrong ( __FUNCTION__ , sprintf ( __ ( 'The post type %1$s is not registered, so it may not be reliable to check the capability "%2$s" against a post of that type.' ), $post -> post_type , $cap ), '4.4.0' );
$caps [] = 'edit_others_posts' ;
break ;
}
$caps [] = $post_type -> cap -> publish_posts ;
break ;
case 'edit_post_meta' :
case 'delete_post_meta' :
case 'add_post_meta' :
Roles/Capabilities: Add meta-caps for comment, term, and user meta.
Additionally, use these meta-caps in the REST API endpoints.
Previously, register_meta()'s auth_callback had no effect for non-post meta. This introduces `{add,edit,delete}_{comment,term,user}_meta` meta-caps to match the existing post meta capabilities. These are currently only used in the REST API.
Props tharsheblows, boonebgorges.
Fixes #38303, fixes #38412.
Built from https://develop.svn.wordpress.org/trunk@39179
git-svn-id: http://core.svn.wordpress.org/trunk@39119 1a063a9b-81f0-0310-95a4-ce76da25c4cd
2016-11-08 22:42:30 -05:00
case 'edit_comment_meta' :
case 'delete_comment_meta' :
case 'add_comment_meta' :
case 'edit_term_meta' :
case 'delete_term_meta' :
case 'add_term_meta' :
case 'edit_user_meta' :
case 'delete_user_meta' :
case 'add_user_meta' :
list ( $_ , $object_type , $_ ) = explode ( '_' , $cap );
$object_id = ( int ) $args [ 0 ];
2018-07-19 14:49:25 -04:00
$object_subtype = get_object_subtype ( $object_type , $object_id );
Roles/Capabilities: Add meta-caps for comment, term, and user meta.
Additionally, use these meta-caps in the REST API endpoints.
Previously, register_meta()'s auth_callback had no effect for non-post meta. This introduces `{add,edit,delete}_{comment,term,user}_meta` meta-caps to match the existing post meta capabilities. These are currently only used in the REST API.
Props tharsheblows, boonebgorges.
Fixes #38303, fixes #38412.
Built from https://develop.svn.wordpress.org/trunk@39179
git-svn-id: http://core.svn.wordpress.org/trunk@39119 1a063a9b-81f0-0310-95a4-ce76da25c4cd
2016-11-08 22:42:30 -05:00
2018-07-19 14:49:25 -04:00
if ( empty ( $object_subtype ) ) {
2015-11-20 02:24:30 -05:00
$caps [] = 'do_not_allow' ;
break ;
}
Roles/Capabilities: Add meta-caps for comment, term, and user meta.
Additionally, use these meta-caps in the REST API endpoints.
Previously, register_meta()'s auth_callback had no effect for non-post meta. This introduces `{add,edit,delete}_{comment,term,user}_meta` meta-caps to match the existing post meta capabilities. These are currently only used in the REST API.
Props tharsheblows, boonebgorges.
Fixes #38303, fixes #38412.
Built from https://develop.svn.wordpress.org/trunk@39179
git-svn-id: http://core.svn.wordpress.org/trunk@39119 1a063a9b-81f0-0310-95a4-ce76da25c4cd
2016-11-08 22:42:30 -05:00
$caps = map_meta_cap ( " edit_ { $object_type } " , $user_id , $object_id );
$meta_key = isset ( $args [ 1 ] ) ? $args [ 1 ] : false ;
2018-07-19 14:49:25 -04:00
if ( $meta_key ) {
$allowed = ! is_protected_meta ( $meta_key , $object_type );
if ( ! empty ( $object_subtype ) && has_filter ( " auth_ { $object_type } _meta_ { $meta_key } _for_ { $object_subtype } " ) ) {
/**
* Filters whether the user is allowed to edit a specific meta key of a specific object type and subtype .
*
* The dynamic portions of the hook name , `$object_type` , `$meta_key` ,
* and `$object_subtype` , refer to the metadata object type ( comment , post , term or user ),
* the meta key value , and the object subtype respectively .
*
* @ since 4.9 . 8
*
* @ param bool $allowed Whether the user can add the object meta . Default false .
* @ param string $meta_key The meta key .
* @ param int $object_id Object ID .
* @ param int $user_id User ID .
* @ param string $cap Capability name .
* @ param string [] $caps Array of the user ' s capabilities .
*/
$allowed = apply_filters ( " auth_ { $object_type } _meta_ { $meta_key } _for_ { $object_subtype } " , $allowed , $meta_key , $object_id , $user_id , $cap , $caps );
} else {
/**
* Filters whether the user is allowed to edit a specific meta key of a specific object type .
*
* Return true to have the mapped meta caps from `edit_{$object_type}` apply .
*
* The dynamic portion of the hook name , `$object_type` refers to the object type being filtered .
* The dynamic portion of the hook name , `$meta_key` , refers to the meta key passed to map_meta_cap () .
*
* @ since 3.3 . 0 As `auth_post_meta_{$meta_key}` .
* @ since 4.6 . 0
*
* @ param bool $allowed Whether the user can add the object meta . Default false .
* @ param string $meta_key The meta key .
* @ param int $object_id Object ID .
* @ param int $user_id User ID .
* @ param string $cap Capability name .
* @ param string [] $caps Array of the user ' s capabilities .
*/
$allowed = apply_filters ( " auth_ { $object_type } _meta_ { $meta_key } " , $allowed , $meta_key , $object_id , $user_id , $cap , $caps );
}
if ( ! empty ( $object_subtype ) ) {
/**
* Filters whether the user is allowed to edit meta for specific object types / subtypes .
*
* Return true to have the mapped meta caps from `edit_{$object_type}` apply .
*
* The dynamic portion of the hook name , `$object_type` refers to the object type being filtered .
* The dynamic portion of the hook name , `$object_subtype` refers to the object subtype being filtered .
* The dynamic portion of the hook name , `$meta_key` , refers to the meta key passed to map_meta_cap () .
*
* @ since 4.6 . 0 As `auth_post_{$post_type}_meta_{$meta_key}` .
* @ since 4.7 . 0
* @ deprecated 4.9 . 8 Use `auth_{$object_type}_meta_{$meta_key}_for_{$object_subtype}`
*
* @ param bool $allowed Whether the user can add the object meta . Default false .
* @ param string $meta_key The meta key .
* @ param int $object_id Object ID .
* @ param int $user_id User ID .
* @ param string $cap Capability name .
* @ param string [] $caps Array of the user ' s capabilities .
*/
$allowed = apply_filters_deprecated ( " auth_ { $object_type } _ { $object_subtype } _meta_ { $meta_key } " , array ( $allowed , $meta_key , $object_id , $user_id , $cap , $caps ), '4.9.8' , " auth_ { $object_type } _meta_ { $meta_key } _for_ { $object_subtype } " );
}
Roles/Capabilities: Add meta-caps for comment, term, and user meta.
Additionally, use these meta-caps in the REST API endpoints.
Previously, register_meta()'s auth_callback had no effect for non-post meta. This introduces `{add,edit,delete}_{comment,term,user}_meta` meta-caps to match the existing post meta capabilities. These are currently only used in the REST API.
Props tharsheblows, boonebgorges.
Fixes #38303, fixes #38412.
Built from https://develop.svn.wordpress.org/trunk@39179
git-svn-id: http://core.svn.wordpress.org/trunk@39119 1a063a9b-81f0-0310-95a4-ce76da25c4cd
2016-11-08 22:42:30 -05:00
if ( ! $allowed ) {
2015-11-20 02:24:30 -05:00
$caps [] = $cap ;
Roles/Capabilities: Add meta-caps for comment, term, and user meta.
Additionally, use these meta-caps in the REST API endpoints.
Previously, register_meta()'s auth_callback had no effect for non-post meta. This introduces `{add,edit,delete}_{comment,term,user}_meta` meta-caps to match the existing post meta capabilities. These are currently only used in the REST API.
Props tharsheblows, boonebgorges.
Fixes #38303, fixes #38412.
Built from https://develop.svn.wordpress.org/trunk@39179
git-svn-id: http://core.svn.wordpress.org/trunk@39119 1a063a9b-81f0-0310-95a4-ce76da25c4cd
2016-11-08 22:42:30 -05:00
}
2015-11-20 02:24:30 -05:00
}
break ;
case 'edit_comment' :
$comment = get_comment ( $args [ 0 ] );
if ( ! $comment ) {
$caps [] = 'do_not_allow' ;
break ;
}
$post = get_post ( $comment -> comment_post_ID );
2008-09-16 17:51:36 -04:00
2015-11-20 02:24:30 -05:00
/*
* If the post doesn ' t exist , we have an orphaned comment .
* Fall back to the edit_posts capability , instead .
*/
if ( $post ) {
$caps = map_meta_cap ( 'edit_post' , $user_id , $post -> ID );
} else {
$caps = map_meta_cap ( 'edit_posts' , $user_id );
}
break ;
case 'unfiltered_upload' :
if ( defined ( 'ALLOW_UNFILTERED_UPLOADS' ) && ALLOW_UNFILTERED_UPLOADS && ( ! is_multisite () || is_super_admin ( $user_id ) ) )
$caps [] = $cap ;
else
$caps [] = 'do_not_allow' ;
break ;
2016-11-08 20:12:30 -05:00
case 'edit_css' :
2015-11-20 02:24:30 -05:00
case 'unfiltered_html' :
// Disallow unfiltered_html for all users, even admins and super admins.
if ( defined ( 'DISALLOW_UNFILTERED_HTML' ) && DISALLOW_UNFILTERED_HTML )
$caps [] = 'do_not_allow' ;
elseif ( is_multisite () && ! is_super_admin ( $user_id ) )
$caps [] = 'do_not_allow' ;
else
2016-10-30 08:47:31 -04:00
$caps [] = 'unfiltered_html' ;
2016-10-19 14:15:31 -04:00
break ;
2015-11-20 02:24:30 -05:00
case 'edit_files' :
case 'edit_plugins' :
case 'edit_themes' :
// Disallow the file editors.
if ( defined ( 'DISALLOW_FILE_EDIT' ) && DISALLOW_FILE_EDIT )
$caps [] = 'do_not_allow' ;
2017-05-11 15:24:41 -04:00
elseif ( ! wp_is_file_mod_allowed ( 'capability_edit_themes' ) )
2015-11-20 02:24:30 -05:00
$caps [] = 'do_not_allow' ;
elseif ( is_multisite () && ! is_super_admin ( $user_id ) )
$caps [] = 'do_not_allow' ;
else
$caps [] = $cap ;
break ;
case 'update_plugins' :
case 'delete_plugins' :
case 'install_plugins' :
case 'upload_plugins' :
case 'update_themes' :
case 'delete_themes' :
case 'install_themes' :
case 'upload_themes' :
case 'update_core' :
// Disallow anything that creates, deletes, or updates core, plugin, or theme files.
// Files in uploads are excepted.
2017-05-11 15:24:41 -04:00
if ( ! wp_is_file_mod_allowed ( 'capability_update_core' ) ) {
2015-11-20 02:24:30 -05:00
$caps [] = 'do_not_allow' ;
} elseif ( is_multisite () && ! is_super_admin ( $user_id ) ) {
$caps [] = 'do_not_allow' ;
} elseif ( 'upload_themes' === $cap ) {
$caps [] = 'install_themes' ;
} elseif ( 'upload_plugins' === $cap ) {
$caps [] = 'install_plugins' ;
} else {
$caps [] = $cap ;
}
break ;
2017-08-18 14:31:44 -04:00
case 'install_languages' :
case 'update_languages' :
2018-01-24 17:59:38 -05:00
if ( ! wp_is_file_mod_allowed ( 'can_install_language_pack' ) ) {
2017-08-18 14:31:44 -04:00
$caps [] = 'do_not_allow' ;
} elseif ( is_multisite () && ! is_super_admin ( $user_id ) ) {
$caps [] = 'do_not_allow' ;
} else {
$caps [] = 'install_languages' ;
}
break ;
2015-11-20 02:24:30 -05:00
case 'activate_plugins' :
2017-08-22 10:02:44 -04:00
case 'deactivate_plugins' :
case 'activate_plugin' :
case 'deactivate_plugin' :
$caps [] = 'activate_plugins' ;
2015-11-20 02:24:30 -05:00
if ( is_multisite () ) {
// update_, install_, and delete_ are handled above with is_super_admin().
$menu_perms = get_site_option ( 'menu_items' , array () );
if ( empty ( $menu_perms [ 'plugins' ] ) )
$caps [] = 'manage_network_plugins' ;
}
break ;
case 'delete_user' :
case 'delete_users' :
// If multisite only super admins can delete users.
if ( is_multisite () && ! is_super_admin ( $user_id ) )
$caps [] = 'do_not_allow' ;
else
$caps [] = 'delete_users' ; // delete_user maps to delete_users.
break ;
case 'create_users' :
if ( ! is_multisite () )
$caps [] = $cap ;
elseif ( is_super_admin ( $user_id ) || get_site_option ( 'add_new_users' ) )
$caps [] = $cap ;
else
$caps [] = 'do_not_allow' ;
break ;
case 'manage_links' :
if ( get_option ( 'link_manager_enabled' ) )
$caps [] = $cap ;
else
$caps [] = 'do_not_allow' ;
break ;
case 'customize' :
$caps [] = 'edit_theme_options' ;
break ;
case 'delete_site' :
2016-12-04 17:07:39 -05:00
if ( is_multisite () ) {
$caps [] = 'manage_options' ;
} else {
$caps [] = 'do_not_allow' ;
}
2015-11-20 02:24:30 -05:00
break ;
Taxonomy: Introduce more fine grained capabilities for managing taxonomy terms.
This introduces the singular `edit_term`, `delete_term`, and `assign_term` meta capabilities for terms, and switches the base capability name for tags from `manage_categories` to `manage_post_tags` and the corresponding `edit_post_tags`, `delete_post_tags`, and `assign_post_tags`.
All of these capabilities ultimately map to `manage_categories` so by default there is no change in the behaviour of the capabilities for categories, tags, or custom taxonomies. The `map_meta_cap` filter and the `capabilities` argument when registering a taxonomy now allow for control over editing, deleting, and assigning individual terms, as well as a separation of capabilities for tags from those of categories.
Fixes #35614
Props johnjamesjacoby for feedback
Built from https://develop.svn.wordpress.org/trunk@38698
git-svn-id: http://core.svn.wordpress.org/trunk@38641 1a063a9b-81f0-0310-95a4-ce76da25c4cd
2016-09-30 18:40:28 -04:00
case 'edit_term' :
case 'delete_term' :
case 'assign_term' :
2016-12-03 00:08:41 -05:00
$term_id = ( int ) $args [ 0 ];
Taxonomy: Introduce more fine grained capabilities for managing taxonomy terms.
This introduces the singular `edit_term`, `delete_term`, and `assign_term` meta capabilities for terms, and switches the base capability name for tags from `manage_categories` to `manage_post_tags` and the corresponding `edit_post_tags`, `delete_post_tags`, and `assign_post_tags`.
All of these capabilities ultimately map to `manage_categories` so by default there is no change in the behaviour of the capabilities for categories, tags, or custom taxonomies. The `map_meta_cap` filter and the `capabilities` argument when registering a taxonomy now allow for control over editing, deleting, and assigning individual terms, as well as a separation of capabilities for tags from those of categories.
Fixes #35614
Props johnjamesjacoby for feedback
Built from https://develop.svn.wordpress.org/trunk@38698
git-svn-id: http://core.svn.wordpress.org/trunk@38641 1a063a9b-81f0-0310-95a4-ce76da25c4cd
2016-09-30 18:40:28 -04:00
$term = get_term ( $term_id );
if ( ! $term || is_wp_error ( $term ) ) {
$caps [] = 'do_not_allow' ;
break ;
}
$tax = get_taxonomy ( $term -> taxonomy );
if ( ! $tax ) {
$caps [] = 'do_not_allow' ;
break ;
}
if ( 'delete_term' === $cap && ( $term -> term_id == get_option ( 'default_' . $term -> taxonomy ) ) ) {
$caps [] = 'do_not_allow' ;
break ;
}
$taxo_cap = $cap . 's' ;
$caps = map_meta_cap ( $tax -> cap -> $taxo_cap , $user_id , $term_id );
break ;
case 'manage_post_tags' :
case 'edit_categories' :
case 'edit_post_tags' :
case 'delete_categories' :
case 'delete_post_tags' :
$caps [] = 'manage_categories' ;
break ;
case 'assign_categories' :
case 'assign_post_tags' :
$caps [] = 'edit_posts' ;
break ;
2016-09-30 18:06:28 -04:00
case 'create_sites' :
case 'delete_sites' :
case 'manage_network' :
case 'manage_sites' :
case 'manage_network_users' :
case 'manage_network_plugins' :
case 'manage_network_themes' :
case 'manage_network_options' :
2017-04-10 17:11:43 -04:00
case 'upgrade_network' :
2016-09-30 18:06:28 -04:00
$caps [] = $cap ;
break ;
2017-04-07 08:53:45 -04:00
case 'setup_network' :
if ( is_multisite () ) {
$caps [] = 'manage_network_options' ;
} else {
$caps [] = 'manage_options' ;
}
break ;
2018-05-01 23:49:26 -04:00
case 'export_others_personal_data' :
case 'erase_others_personal_data' :
2018-05-03 15:35:26 -04:00
case 'manage_privacy_options' :
2018-05-01 23:49:26 -04:00
$caps [] = is_multisite () ? 'manage_network' : 'manage_options' ;
break ;
2015-11-20 02:24:30 -05:00
default :
// Handle meta capabilities for custom post types.
2016-01-15 07:20:27 -05:00
global $post_type_meta_caps ;
2015-11-20 02:24:30 -05:00
if ( isset ( $post_type_meta_caps [ $cap ] ) ) {
$args = array_merge ( array ( $post_type_meta_caps [ $cap ], $user_id ), $args );
return call_user_func_array ( 'map_meta_cap' , $args );
}
2018-10-23 02:53:38 -04:00
// Block capabilities map to their post equivalent.
$block_caps = array (
'edit_blocks' ,
'edit_others_blocks' ,
'publish_blocks' ,
'read_private_blocks' ,
'delete_blocks' ,
'delete_private_blocks' ,
'delete_published_blocks' ,
'delete_others_blocks' ,
'edit_private_blocks' ,
'edit_published_blocks' ,
);
if ( in_array ( $cap , $block_caps , true ) ) {
$cap = str_replace ( '_blocks' , '_posts' , $cap );
}
2015-11-20 02:24:30 -05:00
// If no meta caps match, return the original cap.
$caps [] = $cap ;
}
/**
2016-05-22 14:50:28 -04:00
* Filters a user ' s capabilities depending on specific context and / or privilege .
2015-11-20 02:24:30 -05:00
*
* @ since 2.8 . 0
*
* @ param array $caps Returns the user ' s actual capabilities .
* @ param string $cap Capability name .
* @ param int $user_id The user ID .
* @ param array $args Adds the context to the cap . Typically the object ID .
*/
return apply_filters ( 'map_meta_cap' , $caps , $cap , $user_id , $args );
}
/**
* Whether the current user has a specific capability .
*
* While checking against particular roles in place of a capability is supported
* in part , this practice is discouraged as it may produce unreliable results .
*
2016-02-06 20:27:26 -05:00
* Note : Will always return true if the current user is a super admin , unless specifically denied .
*
2015-11-20 02:24:30 -05:00
* @ since 2.0 . 0
*
* @ see WP_User :: has_cap ()
* @ see map_meta_cap ()
*
* @ param string $capability Capability name .
* @ param int $object_id Optional . ID of the specific object to check against if `$capability` is a " meta " cap .
* " Meta " capabilities , e . g . 'edit_post' , 'edit_user' , etc . , are capabilities used
* by map_meta_cap () to map to other " primitive " capabilities , e . g . 'edit_posts' ,
* 'edit_others_posts' , etc . Accessed via func_get_args () and passed to WP_User :: has_cap (),
* then map_meta_cap () .
* @ return bool Whether the current user has the given capability . If `$capability` is a meta cap and `$object_id` is
* passed , whether the current user has the given meta capability for the given object .
*/
function current_user_can ( $capability ) {
$current_user = wp_get_current_user ();
if ( empty ( $current_user ) )
return false ;
$args = array_slice ( func_get_args (), 1 );
$args = array_merge ( array ( $capability ), $args );
return call_user_func_array ( array ( $current_user , 'has_cap' ), $args );
}
/**
2017-08-29 12:43:47 -04:00
* Whether the current user has a specific capability for a given site .
2015-11-20 02:24:30 -05:00
*
* @ since 3.0 . 0
*
2016-01-27 22:35:27 -05:00
* @ param int $blog_id Site ID .
2017-08-29 12:43:47 -04:00
* @ param string $capability Capability name .
* @ return bool Whether the user has the given capability .
2015-11-20 02:24:30 -05:00
*/
function current_user_can_for_blog ( $blog_id , $capability ) {
$switched = is_multisite () ? switch_to_blog ( $blog_id ) : false ;
$current_user = wp_get_current_user ();
if ( empty ( $current_user ) ) {
if ( $switched ) {
restore_current_blog ();
}
return false ;
}
$args = array_slice ( func_get_args (), 2 );
$args = array_merge ( array ( $capability ), $args );
$can = call_user_func_array ( array ( $current_user , 'has_cap' ), $args );
if ( $switched ) {
restore_current_blog ();
}
return $can ;
}
/**
2017-08-29 12:43:47 -04:00
* Whether the author of the supplied post has a specific capability .
2015-11-20 02:24:30 -05:00
*
* @ since 2.9 . 0
*
2017-08-29 12:43:47 -04:00
* @ param int | WP_Post $post Post ID or post object .
* @ param string $capability Capability name .
* @ return bool Whether the post author has the given capability .
2015-11-20 02:24:30 -05:00
*/
function author_can ( $post , $capability ) {
if ( ! $post = get_post ( $post ) )
return false ;
$author = get_userdata ( $post -> post_author );
if ( ! $author )
return false ;
$args = array_slice ( func_get_args (), 2 );
$args = array_merge ( array ( $capability ), $args );
return call_user_func_array ( array ( $author , 'has_cap' ), $args );
}
/**
2017-08-29 12:43:47 -04:00
* Whether a particular user has a specific capability .
2015-11-20 02:24:30 -05:00
*
* @ since 3.1 . 0
*
2017-08-29 12:43:47 -04:00
* @ param int | WP_User $user User ID or object .
* @ param string $capability Capability name .
* @ return bool Whether the user has the given capability .
2015-11-20 02:24:30 -05:00
*/
function user_can ( $user , $capability ) {
if ( ! is_object ( $user ) )
$user = get_userdata ( $user );
if ( ! $user || ! $user -> exists () )
return false ;
$args = array_slice ( func_get_args (), 2 );
$args = array_merge ( array ( $capability ), $args );
return call_user_func_array ( array ( $user , 'has_cap' ), $args );
}
/**
* Retrieves the global WP_Roles instance and instantiates it if necessary .
*
* @ since 4.3 . 0
*
* @ global WP_Roles $wp_roles WP_Roles global instance .
*
* @ return WP_Roles WP_Roles global instance if not already instantiated .
*/
function wp_roles () {
global $wp_roles ;
if ( ! isset ( $wp_roles ) ) {
$wp_roles = new WP_Roles ();
}
return $wp_roles ;
}
/**
* Retrieve role object .
*
* @ since 2.0 . 0
*
* @ param string $role Role name .
* @ return WP_Role | null WP_Role object if found , null if the role does not exist .
*/
function get_role ( $role ) {
return wp_roles () -> get_role ( $role );
}
/**
* Add role , if it does not exist .
*
* @ since 2.0 . 0
*
* @ param string $role Role name .
* @ param string $display_name Display name for role .
* @ param array $capabilities List of capabilities , e . g . array ( 'edit_posts' => true , 'delete_posts' => false );
* @ return WP_Role | null WP_Role object if role is added , null if already exists .
*/
function add_role ( $role , $display_name , $capabilities = array () ) {
if ( empty ( $role ) ) {
return ;
}
return wp_roles () -> add_role ( $role , $display_name , $capabilities );
}
/**
* Remove role , if it exists .
*
* @ since 2.0 . 0
*
* @ param string $role Role name .
*/
function remove_role ( $role ) {
wp_roles () -> remove_role ( $role );
}
/**
* Retrieve a list of super admins .
*
* @ since 3.0 . 0
*
* @ global array $super_admins
*
* @ return array List of super admin logins
*/
function get_super_admins () {
global $super_admins ;
if ( isset ( $super_admins ) )
return $super_admins ;
else
return get_site_option ( 'site_admins' , array ( 'admin' ) );
}
/**
* Determine if user is a site admin .
*
* @ since 3.0 . 0
*
* @ param int $user_id ( Optional ) The ID of a user . Defaults to the current user .
* @ return bool True if the user is a site admin .
*/
function is_super_admin ( $user_id = false ) {
if ( ! $user_id || $user_id == get_current_user_id () )
$user = wp_get_current_user ();
else
$user = get_userdata ( $user_id );
2015-09-22 09:06:25 -04:00
2015-11-20 02:24:30 -05:00
if ( ! $user || ! $user -> exists () )
return false ;
2015-09-22 09:06:25 -04:00
2015-11-20 02:24:30 -05:00
if ( is_multisite () ) {
$super_admins = get_super_admins ();
if ( is_array ( $super_admins ) && in_array ( $user -> user_login , $super_admins ) )
return true ;
} else {
if ( $user -> has_cap ( 'delete_users' ) )
return true ;
}
2015-09-22 09:06:25 -04:00
2015-11-20 02:24:30 -05:00
return false ;
}
2016-04-13 23:35:27 -04:00
/**
* Grants Super Admin privileges .
*
* @ since 3.0 . 0
*
* @ global array $super_admins
*
* @ param int $user_id ID of the user to be granted Super Admin privileges .
* @ return bool True on success , false on failure . This can fail when the user is
* already a super admin or when the `$super_admins` global is defined .
*/
function grant_super_admin ( $user_id ) {
// If global super_admins override is defined, there is nothing to do here.
if ( isset ( $GLOBALS [ 'super_admins' ] ) || ! is_multisite () ) {
return false ;
}
/**
* Fires before the user is granted Super Admin privileges .
*
* @ since 3.0 . 0
*
* @ param int $user_id ID of the user that is about to be granted Super Admin privileges .
*/
do_action ( 'grant_super_admin' , $user_id );
// Directly fetch site_admins instead of using get_super_admins()
$super_admins = get_site_option ( 'site_admins' , array ( 'admin' ) );
$user = get_userdata ( $user_id );
if ( $user && ! in_array ( $user -> user_login , $super_admins ) ) {
$super_admins [] = $user -> user_login ;
update_site_option ( 'site_admins' , $super_admins );
/**
* Fires after the user is granted Super Admin privileges .
*
* @ since 3.0 . 0
*
* @ param int $user_id ID of the user that was granted Super Admin privileges .
*/
do_action ( 'granted_super_admin' , $user_id );
return true ;
}
return false ;
}
/**
* Revokes Super Admin privileges .
*
* @ since 3.0 . 0
*
* @ global array $super_admins
*
* @ param int $user_id ID of the user Super Admin privileges to be revoked from .
* @ return bool True on success , false on failure . This can fail when the user ' s email
* is the network admin email or when the `$super_admins` global is defined .
*/
function revoke_super_admin ( $user_id ) {
// If global super_admins override is defined, there is nothing to do here.
if ( isset ( $GLOBALS [ 'super_admins' ] ) || ! is_multisite () ) {
return false ;
}
/**
* Fires before the user ' s Super Admin privileges are revoked .
*
* @ since 3.0 . 0
*
* @ param int $user_id ID of the user Super Admin privileges are being revoked from .
*/
do_action ( 'revoke_super_admin' , $user_id );
// Directly fetch site_admins instead of using get_super_admins()
$super_admins = get_site_option ( 'site_admins' , array ( 'admin' ) );
$user = get_userdata ( $user_id );
if ( $user && 0 !== strcasecmp ( $user -> user_email , get_site_option ( 'admin_email' ) ) ) {
if ( false !== ( $key = array_search ( $user -> user_login , $super_admins ) ) ) {
unset ( $super_admins [ $key ] );
update_site_option ( 'site_admins' , $super_admins );
/**
* Fires after the user ' s Super Admin privileges are revoked .
*
* @ since 3.0 . 0
*
* @ param int $user_id ID of the user Super Admin privileges were revoked from .
*/
do_action ( 'revoked_super_admin' , $user_id );
return true ;
}
}
return false ;
}
2017-08-18 14:31:44 -04:00
/**
* Filters the user capabilities to grant the 'install_languages' capability as necessary .
*
* A user must have at least one out of the 'update_core' , 'install_plugins' , and
* 'install_themes' capabilities to qualify for 'install_languages' .
*
* @ since 4.9 . 0
*
* @ param array $allcaps An array of all the user ' s capabilities .
* @ return array Filtered array of the user ' s capabilities .
*/
function wp_maybe_grant_install_languages_cap ( $allcaps ) {
if ( ! empty ( $allcaps [ 'update_core' ] ) || ! empty ( $allcaps [ 'install_plugins' ] ) || ! empty ( $allcaps [ 'install_themes' ] ) ) {
$allcaps [ 'install_languages' ] = true ;
}
return $allcaps ;
}