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'; } }