diff --git a/src/wp-includes/abilities/class-wp-abilities-content.php b/src/wp-includes/abilities/class-wp-abilities-content.php index 28e03177dd62f..6be7e065e0988 100644 --- a/src/wp-includes/abilities/class-wp-abilities-content.php +++ b/src/wp-includes/abilities/class-wp-abilities-content.php @@ -1336,10 +1336,10 @@ private function get_content_query_output_schema(): array { * Returns a user when the current user may see them as an author. * * The current user may see themselves, the users they can edit, every user when they can - * list users, and otherwise the authors of posts they can read in a post type exposed to - * abilities: this ability returns those posts with their author, so the author is already - * in view. Which post types the REST API exposes plays no part. A user the current user - * may not see is reported like a missing one. + * list users, and otherwise the authors of posts they can read in a publicly viewable post + * type exposed to abilities: this ability returns those posts with their author, so the + * author is already in view. Which post types the REST API exposes plays no part. A user + * the current user may not see is reported like a missing one. * * The `author` field and the `author` slug filter share this rule, so the filter never * confirms a slug the field withholds. The post count is kept per author for the query, @@ -1361,7 +1361,7 @@ private function get_visible_author( int $user_id ): ?WP_User { } if ( ! isset( $this->visible_authors[ $user_id ] ) ) { - $post_types = get_post_types( array( 'show_in_abilities' => true ), 'names' ); + $post_types = array_values( array_filter( get_post_types( array( 'show_in_abilities' => true ), 'names' ), 'is_post_type_viewable' ) ); $this->visible_authors[ $user_id ] = (int) count_user_posts( $user_id, $post_types ) > 0; } diff --git a/tests/phpunit/tests/abilities-api/wpRegisterCoreContentAbility.php b/tests/phpunit/tests/abilities-api/wpRegisterCoreContentAbility.php index a625215ddb823..8e8772eb7776b 100644 --- a/tests/phpunit/tests/abilities-api/wpRegisterCoreContentAbility.php +++ b/tests/phpunit/tests/abilities-api/wpRegisterCoreContentAbility.php @@ -2583,7 +2583,7 @@ static function ( $count, $user_id ) use ( &$looked_up ) { } /** - * An author is visible once they have a published post in a post type exposed to + * An author is visible once they have a published post in a viewable post type exposed to * abilities, whether or not that post type is exposed to the REST API, and not for a * published post in a post type exposed to the REST API only. * @@ -2603,10 +2603,11 @@ public function test_author_visibility_follows_the_post_types_exposed_to_abiliti register_post_type( 'abilities_cpt', array( - 'public' => false, - 'show_in_rest' => false, - 'show_in_abilities' => true, - 'supports' => array( 'title', 'author' ), + 'public' => false, + 'publicly_queryable' => true, + 'show_in_rest' => false, + 'show_in_abilities' => true, + 'supports' => array( 'title', 'author' ), ) ); @@ -2658,7 +2659,7 @@ public function test_author_visibility_follows_the_post_types_exposed_to_abiliti $post_ids['abilities_cpt'] => 'abilities_cpt-author', ), $slugs, - 'Only the author with a published post in a post type exposed to abilities should be named.' + 'Only the author with a published post in a viewable post type exposed to abilities should be named.' ); $filter = static function ( string $slug ) use ( $ability, $post_ids ) { @@ -2678,6 +2679,47 @@ public function test_author_visibility_follows_the_post_types_exposed_to_abiliti $this->assertSame( array( $post_ids['abilities_cpt'] ), wp_list_pluck( $filter( 'abilities_cpt-author' )['items'], 'id' ), 'The slug of an author published in a post type exposed to abilities should match their post.' ); } + /** + * Published posts in a non-viewable post type must not make an author's slug visible. + * + * @since 7.2.0 + */ + public function test_author_slug_is_not_exposed_by_posts_in_a_non_viewable_post_type(): void { + register_post_type( + 'hidden_author_cpt', + array( + 'public' => true, + 'publicly_queryable' => false, + 'show_in_abilities' => true, + 'supports' => array( 'title', 'author' ), + ) + ); + $author = self::factory()->user->create_and_get( array( 'role' => 'author' ) ); + self::factory()->post->create( + array( + 'post_type' => 'hidden_author_cpt', + 'post_author' => $author->ID, + 'post_status' => 'publish', + ) + ); + + $this->login_as( 'subscriber' ); + $this->register_ability(); + $ability = wp_get_ability( 'core/content-query' ); + + foreach ( array( $author->user_nicename, 'no-such-user' ) as $slug ) { + $result = $ability->execute( + array( + 'where' => array( + 'type' => 'hidden_author_cpt', + 'author' => array( 'slug' => $slug ), + ), + ) + ); + $this->assertSame( self::EMPTY_RESULT, $result, 'A hidden author slug should return zero totals, just like an unknown slug.' ); + } + } + /** * Requested objects that do not apply to the post type are left out, and the post keeps its * ID.