* @since 3.3.0 As `auth_post_meta_{$meta_key}`.
* @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 Renamed from `auth_post_{$post_type}_meta_{$meta_key}` to
* `auth_{$object_type}_{$object_subtype}_meta_{$meta_key}`.
* @deprecated 4.9.8 Use {@see 'auth_{$object_type}_meta_{$meta_key}_for_{$object_subtype}'} instead.
* @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 ),
"auth_{$object_type}_meta_{$meta_key}_for_{$object_subtype}"
if ( ! isset( $args[0] ) ) {
/* translators: %s: Capability name. */
$message = __( 'When checking for the %s capability, you must always check it against a specific comment.' );
sprintf( $message, '<code>' . $cap . '</code>' ),
$caps[] = 'do_not_allow';
$comment = get_comment( $args[0] );
$caps[] = 'do_not_allow';
$post = get_post( $comment->comment_post_ID );
* If the post doesn't exist, we have an orphaned comment.
* Fall back to the edit_posts capability, instead.
$caps = map_meta_cap( 'edit_post', $user_id, $post->ID );
$caps = map_meta_cap( 'edit_posts', $user_id );
case 'unfiltered_upload':
if ( defined( 'ALLOW_UNFILTERED_UPLOADS' ) && ALLOW_UNFILTERED_UPLOADS && ( ! is_multisite() || is_super_admin( $user_id ) ) ) {
$caps[] = 'do_not_allow';
// 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';
$caps[] = 'unfiltered_html';
// Disallow the file editors.
if ( defined( 'DISALLOW_FILE_EDIT' ) && DISALLOW_FILE_EDIT ) {
$caps[] = 'do_not_allow';
} elseif ( ! wp_is_file_mod_allowed( 'capability_edit_themes' ) ) {
$caps[] = 'do_not_allow';
} elseif ( is_multisite() && ! is_super_admin( $user_id ) ) {
$caps[] = 'do_not_allow';
* Disallow anything that creates, deletes, or updates core, plugin, or theme files.
* Files in uploads are excepted.
if ( ! wp_is_file_mod_allowed( 'capability_update_core' ) ) {
$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';
case 'install_languages':
if ( ! wp_is_file_mod_allowed( 'can_install_language_pack' ) ) {
$caps[] = 'do_not_allow';
} elseif ( is_multisite() && ! is_super_admin( $user_id ) ) {
$caps[] = 'do_not_allow';
$caps[] = 'install_languages';
case 'deactivate_plugins':
case 'deactivate_plugin':
$caps[] = 'activate_plugins';
// 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';
$caps[] = 'resume_plugins';
$caps[] = 'resume_themes';
// If multisite only super admins can delete users.
if ( is_multisite() && ! is_super_admin( $user_id ) ) {
$caps[] = 'do_not_allow';
$caps[] = 'delete_users'; // delete_user maps to delete_users.
if ( ! is_multisite() ) {
} elseif ( is_super_admin( $user_id ) || get_site_option( 'add_new_users' ) ) {
$caps[] = 'do_not_allow';
if ( get_option( 'link_manager_enabled' ) ) {
$caps[] = 'do_not_allow';
$caps[] = 'edit_theme_options';
$caps[] = 'manage_options';
$caps[] = 'do_not_allow';
if ( ! isset( $args[0] ) ) {
/* translators: %s: Capability name. */
$message = __( 'When checking for the %s capability, you must always check it against a specific term.' );
sprintf( $message, '<code>' . $cap . '</code>' ),
$caps[] = 'do_not_allow';
$term_id = (int) $args[0];
$term = get_term( $term_id );
if ( ! $term || is_wp_error( $term ) ) {
$caps[] = 'do_not_allow';
$tax = get_taxonomy( $term->taxonomy );
$caps[] = 'do_not_allow';
if ( 'delete_term' === $cap
&& ( (int) get_option( 'default_' . $term->taxonomy ) === $term->term_id
|| (int) get_option( 'default_term_' . $term->taxonomy ) === $term->term_id )
$caps[] = 'do_not_allow';
$caps = map_meta_cap( $tax->cap->$taxo_cap, $user_id, $term_id );
case 'delete_categories':
$caps[] = 'manage_categories';
case 'assign_categories':
case 'manage_network_users':
case 'manage_network_plugins':
case 'manage_network_themes':
case 'manage_network_options':
$caps[] = 'manage_network_options';
$caps[] = 'manage_options';
if ( is_multisite() && ! is_super_admin( $user_id ) ) {
$caps[] = 'do_not_allow';
if ( is_multisite() && ! is_super_admin( $user_id ) ) {
$caps[] = 'do_not_allow';
$caps[] = 'manage_options';
case 'export_others_personal_data':
case 'erase_others_personal_data':
case 'manage_privacy_options':
$caps[] = is_multisite() ? 'manage_network' : 'manage_options';
case 'create_app_password':
case 'list_app_passwords':
case 'read_app_password':
case 'edit_app_password':
case 'delete_app_passwords':
case 'delete_app_password':
$caps = map_meta_cap( 'edit_user', $user_id, $args[0] );
case 'edit_block_binding':
$block_editor_context = $args[0];
if ( isset( $block_editor_context->post ) ) {
$object_id = $block_editor_context->post->ID;
* If the post ID is null, check if the context is the site editor.
* Fall back to the edit_theme_options in that case.
if ( ! isset( $object_id ) ) {
if ( ! isset( $block_editor_context->name ) || 'core/edit-site' !== $block_editor_context->name ) {
$caps[] = 'do_not_allow';
$caps = map_meta_cap( 'edit_theme_options', $user_id );
$object_subtype = get_object_subtype( 'post', (int) $object_id );
if ( empty( $object_subtype ) ) {
$caps[] = 'do_not_allow';
$post_type_object = get_post_type_object( $object_subtype );
// Initialize empty array if it doesn't exist.
if ( ! isset( $post_type_object->capabilities ) ) {
$post_type_object->capabilities = array();
$post_type_capabilities = get_post_type_capabilities( $post_type_object );
$caps = map_meta_cap( $post_type_capabilities->edit_post, $user_id, $object_id );
// Handle meta capabilities for custom post types.
global $post_type_meta_caps;
if ( isset( $post_type_meta_caps[ $cap ] ) ) {
return map_meta_cap( $post_type_meta_caps[ $cap ], $user_id, ...$args );
// Block capabilities map to their post equivalent.
'delete_published_blocks',
if ( in_array( $cap, $block_caps, true ) ) {
$cap = str_replace( '_blocks', '_posts', $cap );
// If no meta caps match, return the original cap.
* Filters the primitive capabilities required of the given user to satisfy the
* capability being checked.
* @param string[] $caps Primitive capabilities required of the user.
* @param string $cap Capability being checked.
* @param int $user_id The user ID.
* @param array $args Adds context to the capability check, typically
* starting with an object ID.
return apply_filters( 'map_meta_cap', $caps, $cap, $user_id, $args );
* Returns whether the current user has the specified capability.
* This function also accepts an ID of an object to check against if the capability is a meta capability. Meta
* capabilities such as `edit_post` and `edit_user` are capabilities used by the `map_meta_cap()` function to
* map to primitive capabilities that a user or role has, such as `edit_posts` and `edit_others_posts`.
* current_user_can( 'edit_posts' );
* current_user_can( 'edit_post', $post->ID );
* current_user_can( 'edit_post_meta', $post->ID, $meta_key );
* While checking against particular roles in place of a capability is supported
* in part, this practice is discouraged as it may produce unreliable results.
* Note: Will always return true if the current user is a super admin, unless specifically denied.
* @since 5.3.0 Formalized the existing and already documented `...$args` parameter
* by adding it to the function signature.
* @since 5.8.0 Converted to wrapper for the user_can() function.
* @see WP_User::has_cap()
* @param string $capability Capability name.
* @param mixed ...$args Optional further parameters, typically starting with an object ID.
* @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, ...$args ) {
return user_can( wp_get_current_user(), $capability, ...$args );
* Returns whether the current user has the specified capability for a given site.
* This function also accepts an ID of an object to check against if the capability is a meta capability. Meta
* capabilities such as `edit_post` and `edit_user` are capabilities used by the `map_meta_cap()` function to
* map to primitive capabilities that a user or role has, such as `edit_posts` and `edit_others_posts`.
* This function replaces the current_user_can_for_blog() function.
* current_user_can_for_site( $site_id, 'edit_posts' );
* current_user_can_for_site( $site_id, 'edit_post', $post->ID );
* current_user_can_for_site( $site_id, 'edit_post_meta', $post->ID, $meta_key );
* @param int $site_id Site ID.
* @param string $capability Capability name.
* @param mixed ...$args Optional further parameters, typically starting with an object ID.
* @return bool Whether the user has the given capability.
function current_user_can_for_site( $site_id, $capability, ...$args ) {
$switched = is_multisite() ? switch_to_blog( $site_id ) : false;
$can = current_user_can( $capability, ...$args );
* Returns whether the author of the supplied post has the specified capability.
* This function also accepts an ID of an object to check against if the capability is a meta capability. Meta
* capabilities such as `edit_post` and `edit_user` are capabilities used by the `map_meta_cap()` function to
* map to primitive capabilities that a user or role has, such as `edit_posts` and `edit_others_posts`.
* author_can( $post, 'edit_posts' );
* author_can( $post, 'edit_post', $post->ID );
* author_can( $post, 'edit_post_meta', $post->ID, $meta_key );
* @since 5.3.0 Formalized the existing and already documented `...$args` parameter
* by adding it to the function signature.
* @param int|WP_Post $post Post ID or post object.
* @param string $capability Capability name.
* @param mixed ...$args Optional further parameters, typically starting with an object ID.
* @return bool Whether the post author has the given capability.
function author_can( $post, $capability, ...$args ) {
$post = get_post( $post );
$author = get_userdata( $post->post_author );
return $author->has_cap( $capability, ...$args );
* Returns whether a particular user has the specified capability.
* This function also accepts an ID of an object to check against if the capability is a meta capability. Meta
* capabilities such as `edit_post` and `edit_user` are capabilities used by the `map_meta_cap()` function to
* map to primitive capabilities that a user or role has, such as `edit_posts` and `edit_others_posts`.
* user_can( $user->ID, 'edit_posts' );
* user_can( $user->ID, 'edit_post', $post->ID );
* user_can( $user->ID, 'edit_post_meta', $post->ID, $meta_key );
* @since 5.3.0 Formalized the existing and already documented `...$args` parameter
* by adding it to the function signature.