diff --git a/src/wp-admin/edit-form-advanced.php b/src/wp-admin/edit-form-advanced.php
index 2cb9dd3e1c487..9bfd4e54b9dda 100644
--- a/src/wp-admin/edit-form-advanced.php
+++ b/src/wp-admin/edit-form-advanced.php
@@ -549,7 +549,7 @@
}
if ( $post_type_object->public
- && ! ( 'pending' === get_post_status( $post ) && ! current_user_can( $post_type_object->cap->publish_posts ) )
+ && ! ( 'pending' === get_post_status( $post ) && ! current_user_can( 'publish_post', $post ) )
) {
$has_sample_permalink = $sample_permalink_html && 'auto-draft' !== $post->post_status;
?>
diff --git a/src/wp-admin/includes/meta-boxes.php b/src/wp-admin/includes/meta-boxes.php
index 5d003d3210c0d..89529dd82d495 100644
--- a/src/wp-admin/includes/meta-boxes.php
+++ b/src/wp-admin/includes/meta-boxes.php
@@ -33,7 +33,7 @@ function post_submit_meta_box( $post, $args = array() ) {
$post_id = (int) $post->ID;
$post_type = $post->post_type;
$post_type_object = get_post_type_object( $post_type );
- $can_publish = current_user_can( $post_type_object->cap->publish_posts );
+ $can_publish = current_user_can( 'publish_post', $post );
?>
@@ -1559,7 +1559,7 @@ function register_and_do_post_meta_boxes( $post ) {
}
}
- if ( ! ( 'pending' === get_post_status( $post ) && ! current_user_can( $post_type_object->cap->publish_posts ) ) ) {
+ if ( ! ( 'pending' === get_post_status( $post ) && ! current_user_can( 'publish_post', $post ) ) ) {
add_meta_box( 'slugdiv', __( 'Slug' ), 'post_slug_meta_box', null, 'normal', 'core', array( '__back_compat_meta_box' => true ) );
}
diff --git a/src/wp-admin/includes/post.php b/src/wp-admin/includes/post.php
index fbf11b3dedf2b..d7e06dad8d60f 100644
--- a/src/wp-admin/includes/post.php
+++ b/src/wp-admin/includes/post.php
@@ -436,7 +436,7 @@ function edit_post( $post_data = null ) {
wp_set_post_lock( $post_ID );
- if ( current_user_can( $ptype->cap->edit_others_posts ) && current_user_can( $ptype->cap->publish_posts ) ) {
+ if ( current_user_can( $ptype->cap->edit_others_posts ) && current_user_can( 'publish_post', $post_ID ) ) {
if ( ! empty( $post_data['sticky'] ) ) {
stick_post( $post_ID );
} else {
diff --git a/src/wp-includes/capabilities.php b/src/wp-includes/capabilities.php
index 881075f1e2287..54d274ddda035 100644
--- a/src/wp-includes/capabilities.php
+++ b/src/wp-includes/capabilities.php
@@ -65,219 +65,6 @@ function map_meta_cap( $cap, $user_id, ...$args ) {
$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 ) {
- $caps[] = 'do_not_allow';
- break;
- }
-
- if ( ( get_option( 'page_for_posts' ) == $post->ID ) || ( get_option( 'page_on_front' ) == $post->ID ) ) {
- $caps[] = 'manage_options';
- 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 ( '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 ) {
- // If the post is published or scheduled...
- if ( in_array( $post->post_status, array( 'publish', 'future' ), true ) ) {
- $caps[] = $post_type->cap->delete_published_posts;
- } elseif ( 'trash' === $post->post_status ) {
- $status = get_post_meta( $post->ID, '_wp_trash_meta_status', true );
- if ( in_array( $status, array( 'publish', 'future' ), true ) ) {
- $caps[] = $post_type->cap->delete_published_posts;
- } else {
- $caps[] = $post_type->cap->delete_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 or scheduled, extra cap required.
- if ( in_array( $post->post_status, array( 'publish', 'future' ), true ) ) {
- $caps[] = $post_type->cap->delete_published_posts;
- } elseif ( 'private' === $post->post_status ) {
- $caps[] = $post_type->cap->delete_private_posts;
- }
- }
-
- /*
- * 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 ) );
- }
-
- 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 ) {
- // If the post is published or scheduled...
- if ( in_array( $post->post_status, array( 'publish', 'future' ), true ) ) {
- $caps[] = $post_type->cap->edit_published_posts;
- } elseif ( 'trash' === $post->post_status ) {
- $status = get_post_meta( $post->ID, '_wp_trash_meta_status', true );
- if ( in_array( $status, array( 'publish', 'future' ), true ) ) {
- $caps[] = $post_type->cap->edit_published_posts;
- } else {
- $caps[] = $post_type->cap->edit_posts;
- }
- } 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;
- // The post is published or scheduled, extra cap required.
- if ( in_array( $post->post_status, array( 'publish', 'future' ), true ) ) {
- $caps[] = $post_type->cap->edit_published_posts;
- } elseif ( 'private' === $post->post_status ) {
- $caps[] = $post_type->cap->edit_private_posts;
- }
- }
-
- /*
- * 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 ) );
- }
-
- 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 ) {
- /* translators: 1: Post status, 2: Capability name. */
- _doing_it_wrong( __FUNCTION__, sprintf( __( 'The post status %1$s is not registered, so it may not be reliable to check the capability "%2$s" against a post with that status.' ), $post->post_status, $cap ), '5.4.0' );
- $caps[] = 'edit_others_posts';
- break;
- }
-
- 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':
@@ -592,6 +379,157 @@ function map_meta_cap( $cap, $user_id, ...$args ) {
case 'erase_others_personal_data':
case 'manage_privacy_options':
$caps[] = is_multisite() ? 'manage_network' : 'manage_options';
+ break;
+ case 'delete_post':
+ case 'delete_page':
+ case 'edit_post':
+ case 'edit_page':
+ case 'publish_post':
+ case 'read_post':
+ case 'read_page':
+ $post = get_post( $args[0] );
+ if ( ! $post ) {
+ $caps[] = 'do_not_allow';
+ break;
+ }
+
+ if ( 'publish_post' !== $cap && 'revision' === $post->post_type ) {
+ if ( in_array( $cap, array( 'delete_post', 'delete_page' ), true ) ) {
+ $caps[] = 'do_not_allow';
+ break;
+ } else {
+ $post = get_post( $post->post_parent );
+ if ( ! $post ) {
+ $caps[] = 'do_not_allow';
+ break;
+ }
+ }
+ }
+
+ if ( in_array( $cap, array( 'delete_post', 'delete_page' ), true ) ) {
+ if ( ( get_option( 'page_for_posts' ) == $post->ID ) || ( get_option( 'page_on_front' ) == $post->ID ) ) {
+ $caps[] = 'manage_options';
+ 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 ) {
+ $post_cap = str_replace( '_page', '_post', $cap );
+ $caps[] = $post_type->cap->$post_cap;
+ // Prior to 3.1 we would re-call map_meta_cap here.
+ if ( $post_cap === $cap ) {
+ $cap = $post_type->cap->$post_cap;
+ }
+ break;
+ }
+
+ if ( in_array( $cap, array( 'read_post', 'read_page' ), true ) ) {
+ $status_obj = get_post_status_object( $post->post_status );
+ if ( ! $status_obj ) {
+ /* translators: 1: Post status, 2: Capability name. */
+ _doing_it_wrong( __FUNCTION__, sprintf( __( 'The post status %1$s is not registered, so it may not be reliable to check the capability "%2$s" against a post with that status.' ), $post->post_status, $cap ), '5.4.0' );
+ $caps[] = 'edit_others_posts';
+ break;
+ }
+
+ if ( $status_obj->public ) {
+ $caps[] = $post_type->cap->read;
+ break;
+ }
+ }
+
+ switch ( $cap ) {
+ case 'delete_post':
+ case 'delete_page':
+ // If the post author is set and the user is the author...
+ if ( $post->post_author && $user_id == $post->post_author ) {
+ // If the post is published or scheduled...
+ if ( in_array( $post->post_status, array( 'publish', 'future' ), true ) ) {
+ $caps[] = $post_type->cap->delete_published_posts;
+ } elseif ( 'trash' === $post->post_status ) {
+ $status = get_post_meta( $post->ID, '_wp_trash_meta_status', true );
+ if ( in_array( $status, array( 'publish', 'future' ), true ) ) {
+ $caps[] = $post_type->cap->delete_published_posts;
+ } else {
+ $caps[] = $post_type->cap->delete_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 or scheduled, extra cap required.
+ if ( in_array( $post->post_status, array( 'publish', 'future' ), true ) ) {
+ $caps[] = $post_type->cap->delete_published_posts;
+ } elseif ( 'private' === $post->post_status ) {
+ $caps[] = $post_type->cap->delete_private_posts;
+ }
+ }
+ break;
+ case 'edit_post':
+ case 'edit_page':
+ // If the post author is set and the user is the author...
+ if ( $post->post_author && $user_id == $post->post_author ) {
+ // If the post is published or scheduled...
+ if ( in_array( $post->post_status, array( 'publish', 'future' ), true ) ) {
+ $caps[] = $post_type->cap->edit_published_posts;
+ } elseif ( 'trash' === $post->post_status ) {
+ $status = get_post_meta( $post->ID, '_wp_trash_meta_status', true );
+ if ( in_array( $status, array( 'publish', 'future' ), true ) ) {
+ $caps[] = $post_type->cap->edit_published_posts;
+ } else {
+ $caps[] = $post_type->cap->edit_posts;
+ }
+ } 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;
+ // The post is published or scheduled, extra cap required.
+ if ( in_array( $post->post_status, array( 'publish', 'future' ), true ) ) {
+ $caps[] = $post_type->cap->edit_published_posts;
+ } elseif ( 'private' === $post->post_status ) {
+ $caps[] = $post_type->cap->edit_private_posts;
+ }
+ }
+ break;
+ case 'publish_post':
+ $caps[] = $post_type->cap->publish_posts;
+ break;
+ case 'read_post':
+ case 'read_page':
+ 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;
+ }
+
+ /*
+ * Setting the privacy policy page requires `manage_privacy_options`,
+ * so editing it should require that too.
+ */
+ if ( in_array( $cap, array( 'delete_post', 'delete_page', 'edit_post', 'edit_page' ), true ) ) {
+ if ( (int) get_option( 'wp_page_for_privacy_policy' ) === $post->ID ) {
+ $caps = array_merge( $caps, map_meta_cap( 'manage_privacy_options', $user_id ) );
+ }
+ }
+
break;
default:
// Handle meta capabilities for custom post types.
diff --git a/src/wp-includes/class-wp-customize-nav-menus.php b/src/wp-includes/class-wp-customize-nav-menus.php
index 145ded9d7c1f5..123ff889b8773 100644
--- a/src/wp-includes/class-wp-customize-nav-menus.php
+++ b/src/wp-includes/class-wp-customize-nav-menus.php
@@ -1361,7 +1361,7 @@ public function sanitize_nav_menus_created_posts( $value ) {
if ( ! $post_type_obj ) {
continue;
}
- if ( ! current_user_can( $post_type_obj->cap->publish_posts ) || ! current_user_can( 'edit_post', $post_id ) ) {
+ if ( ! current_user_can( 'publish_post', $post_id ) || ! current_user_can( 'edit_post', $post_id ) ) {
continue;
}
$post_ids[] = $post->ID;
diff --git a/src/wp-includes/post.php b/src/wp-includes/post.php
index 9f360e3391654..e3e4197ca1ec9 100644
--- a/src/wp-includes/post.php
+++ b/src/wp-includes/post.php
@@ -1581,6 +1581,7 @@ function get_post_type_capabilities( $args ) {
'edit_post' => 'edit_' . $singular_base,
'read_post' => 'read_' . $singular_base,
'delete_post' => 'delete_' . $singular_base,
+ 'publish_post' => 'publish_' . $singular_base,
// Primitive capabilities used outside of map_meta_cap():
'edit_posts' => 'edit_' . $plural_base,
'edit_others_posts' => 'edit_others_' . $plural_base,
@@ -1631,7 +1632,7 @@ function _post_type_meta_capabilities( $capabilities = null ) {
global $post_type_meta_caps;
foreach ( $capabilities as $core => $custom ) {
- if ( in_array( $core, array( 'read_post', 'delete_post', 'edit_post' ), true ) ) {
+ if ( in_array( $core, array( 'read_post', 'delete_post', 'edit_post', 'publish_post' ), true ) ) {
$post_type_meta_caps[ $custom ] = $core;
}
}
diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php
index 30cb8f69fa1b6..f81bbcf54ced3 100644
--- a/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php
+++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php
@@ -728,7 +728,7 @@ public function update_item_permissions_check( $request ) {
);
}
- if ( ! empty( $request['sticky'] ) && ! current_user_can( $post_type->cap->edit_others_posts ) && ! current_user_can( $post_type->cap->publish_posts ) ) {
+ if ( ! empty( $request['sticky'] ) && ! current_user_can( $post_type->cap->edit_others_posts ) && ! current_user_can( 'publish_post', $post ) ) {
return new WP_Error(
'rest_cannot_assign_sticky',
__( 'Sorry, you are not allowed to make posts sticky.' ),
@@ -2028,7 +2028,7 @@ protected function get_available_actions( $post, $request ) {
$post_type = get_post_type_object( $post->post_type );
- if ( 'attachment' !== $this->post_type && current_user_can( $post_type->cap->publish_posts ) ) {
+ if ( 'attachment' !== $this->post_type && current_user_can( 'publish_post', $post ) ) {
$rels[] = 'https://api.w.org/action-publish';
}
@@ -2037,7 +2037,7 @@ protected function get_available_actions( $post, $request ) {
}
if ( 'post' === $post_type->name ) {
- if ( current_user_can( $post_type->cap->edit_others_posts ) && current_user_can( $post_type->cap->publish_posts ) ) {
+ if ( current_user_can( $post_type->cap->edit_others_posts ) && current_user_can( 'publish_post', $post ) ) {
$rels[] = 'https://api.w.org/action-sticky';
}
}