mirror of
https://github.com/gosticks/wordpress-develop.git
synced 2026-07-01 15:50:09 +00:00
Query: Check each post-type's capabilities when querying multiple post-types.
When querying multiple post types, check the `read_private_posts` capability for each post type when determining which post statuses to return. This ensures private posts appear in search results and archives for users permitted to read them. Props leogermani, hellofromTonya, jeffpaul, peterwilsoncc. Fixes #48556. git-svn-id: https://develop.svn.wordpress.org/trunk@51276 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
@@ -2420,26 +2420,28 @@ class WP_Query {
|
||||
$where .= $wpdb->prepare( " AND {$wpdb->posts}.ping_status = %s ", $q['ping_status'] );
|
||||
}
|
||||
|
||||
$skip_post_status = false;
|
||||
if ( 'any' === $post_type ) {
|
||||
$in_search_post_types = get_post_types( array( 'exclude_from_search' => false ) );
|
||||
if ( empty( $in_search_post_types ) ) {
|
||||
$where .= ' AND 1=0 ';
|
||||
$post_type_where = ' AND 1=0 ';
|
||||
$skip_post_status = true;
|
||||
} else {
|
||||
$where .= " AND {$wpdb->posts}.post_type IN ('" . implode( "', '", array_map( 'esc_sql', $in_search_post_types ) ) . "')";
|
||||
$post_type_where = " AND {$wpdb->posts}.post_type IN ('" . implode( "', '", array_map( 'esc_sql', $in_search_post_types ) ) . "')";
|
||||
}
|
||||
} elseif ( ! empty( $post_type ) && is_array( $post_type ) ) {
|
||||
$where .= " AND {$wpdb->posts}.post_type IN ('" . implode( "', '", esc_sql( $post_type ) ) . "')";
|
||||
$post_type_where = " AND {$wpdb->posts}.post_type IN ('" . implode( "', '", esc_sql( $post_type ) ) . "')";
|
||||
} elseif ( ! empty( $post_type ) ) {
|
||||
$where .= $wpdb->prepare( " AND {$wpdb->posts}.post_type = %s", $post_type );
|
||||
$post_type_where = $wpdb->prepare( " AND {$wpdb->posts}.post_type = %s", $post_type );
|
||||
$post_type_object = get_post_type_object( $post_type );
|
||||
} elseif ( $this->is_attachment ) {
|
||||
$where .= " AND {$wpdb->posts}.post_type = 'attachment'";
|
||||
$post_type_where = " AND {$wpdb->posts}.post_type = 'attachment'";
|
||||
$post_type_object = get_post_type_object( 'attachment' );
|
||||
} elseif ( $this->is_page ) {
|
||||
$where .= " AND {$wpdb->posts}.post_type = 'page'";
|
||||
$post_type_where = " AND {$wpdb->posts}.post_type = 'page'";
|
||||
$post_type_object = get_post_type_object( 'page' );
|
||||
} else {
|
||||
$where .= " AND {$wpdb->posts}.post_type = 'post'";
|
||||
$post_type_where = " AND {$wpdb->posts}.post_type = 'post'";
|
||||
$post_type_object = get_post_type_object( 'post' );
|
||||
}
|
||||
|
||||
@@ -2457,7 +2459,12 @@ class WP_Query {
|
||||
$user_id = get_current_user_id();
|
||||
|
||||
$q_status = array();
|
||||
if ( ! empty( $q['post_status'] ) ) {
|
||||
if ( $skip_post_status ) {
|
||||
$where .= $post_type_where;
|
||||
} elseif ( ! empty( $q['post_status'] ) ) {
|
||||
|
||||
$where .= $post_type_where;
|
||||
|
||||
$statuswheres = array();
|
||||
$q_status = $q['post_status'];
|
||||
if ( ! is_array( $q_status ) ) {
|
||||
@@ -2517,39 +2524,69 @@ class WP_Query {
|
||||
$where .= " AND ($where_status)";
|
||||
}
|
||||
} elseif ( ! $this->is_singular ) {
|
||||
$where .= " AND ({$wpdb->posts}.post_status = 'publish'";
|
||||
|
||||
// Add public states.
|
||||
$public_states = get_post_stati( array( 'public' => true ) );
|
||||
foreach ( (array) $public_states as $state ) {
|
||||
if ( 'publish' === $state ) { // Publish is hard-coded above.
|
||||
continue;
|
||||
}
|
||||
$where .= " OR {$wpdb->posts}.post_status = '$state'";
|
||||
if ( 'any' === $post_type ) {
|
||||
$queried_post_types = get_post_types( array( 'exclude_from_search' => false ) );
|
||||
} elseif ( is_array( $post_type ) ) {
|
||||
$queried_post_types = $post_type;
|
||||
} elseif ( ! empty( $post_type ) ) {
|
||||
$queried_post_types = array( $post_type );
|
||||
} else {
|
||||
$queried_post_types = array( 'post' );
|
||||
}
|
||||
|
||||
if ( $this->is_admin ) {
|
||||
// Add protected states that should show in the admin all list.
|
||||
$admin_all_states = get_post_stati(
|
||||
array(
|
||||
'protected' => true,
|
||||
'show_in_admin_all_list' => true,
|
||||
)
|
||||
);
|
||||
foreach ( (array) $admin_all_states as $state ) {
|
||||
$where .= " OR {$wpdb->posts}.post_status = '$state'";
|
||||
}
|
||||
}
|
||||
if ( ! empty( $queried_post_types ) ) {
|
||||
|
||||
if ( is_user_logged_in() ) {
|
||||
// Add private states that are limited to viewing by the author of a post or someone who has caps to read private states.
|
||||
$private_states = get_post_stati( array( 'private' => true ) );
|
||||
foreach ( (array) $private_states as $state ) {
|
||||
$where .= current_user_can( $read_private_cap ) ? " OR {$wpdb->posts}.post_status = '$state'" : " OR {$wpdb->posts}.post_author = $user_id AND {$wpdb->posts}.post_status = '$state'";
|
||||
}
|
||||
}
|
||||
$status_type_clauses = array();
|
||||
|
||||
$where .= ')';
|
||||
foreach ( $queried_post_types as $queried_post_type ) {
|
||||
|
||||
$queried_post_type_object = get_post_type_object( $queried_post_type );
|
||||
|
||||
$type_where = '(' . $wpdb->prepare( "{$wpdb->posts}.post_type = %s AND (", $queried_post_type );
|
||||
|
||||
// Public statuses.
|
||||
$public_statuses = get_post_stati( array( 'public' => true ) );
|
||||
$status_clauses = array();
|
||||
foreach ( (array) $public_statuses as $public_status ) {
|
||||
$status_clauses[] = "{$wpdb->posts}.post_status = '$public_status'";
|
||||
}
|
||||
$type_where .= implode( ' OR ', $status_clauses );
|
||||
|
||||
// Add protected states that should show in the admin all list.
|
||||
if ( $this->is_admin ) {
|
||||
$admin_all_statuses = get_post_stati(
|
||||
array(
|
||||
'protected' => true,
|
||||
'show_in_admin_all_list' => true,
|
||||
)
|
||||
);
|
||||
foreach ( (array) $admin_all_statuses as $admin_all_status ) {
|
||||
$type_where .= " OR {$wpdb->posts}.post_status = '$admin_all_status'";
|
||||
}
|
||||
}
|
||||
|
||||
// Add private states that are visible to current user.
|
||||
if ( is_user_logged_in() && $queried_post_type_object instanceof WP_Post_Type ) {
|
||||
$read_private_cap = $queried_post_type_object->cap->read_private_posts;
|
||||
$private_statuses = get_post_stati( array( 'private' => true ) );
|
||||
foreach ( (array) $private_statuses as $private_status ) {
|
||||
$type_where .= current_user_can( $read_private_cap ) ? " \nOR {$wpdb->posts}.post_status = '$private_status'" : " \nOR ({$wpdb->posts}.post_author = $user_id AND {$wpdb->posts}.post_status = '$private_status')";
|
||||
}
|
||||
}
|
||||
|
||||
$type_where .= '))';
|
||||
|
||||
$status_type_clauses[] = $type_where;
|
||||
}
|
||||
|
||||
if ( ! empty( $status_type_clauses ) ) {
|
||||
$where .= ' AND (' . implode( ' OR ', $status_type_clauses ) . ')';
|
||||
}
|
||||
} else {
|
||||
$where .= ' AND 1=0 ';
|
||||
}
|
||||
} else {
|
||||
$where .= $post_type_where;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -98,6 +98,25 @@ class Tests_Query_InvalidQueries extends WP_UnitTestCase {
|
||||
$this->assertCount( 0, $posts );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test WP Query with an invalid post type in a mutiple post type query.
|
||||
*
|
||||
* @ticket 48556
|
||||
*/
|
||||
public function test_unregistered_post_type_wp_query_multiple_post_types() {
|
||||
global $wpdb;
|
||||
|
||||
$query = new WP_Query(
|
||||
array(
|
||||
'post_type' => array( 'unregistered_cpt', 'page' ),
|
||||
)
|
||||
);
|
||||
$posts = $query->get_posts();
|
||||
|
||||
$this->assertContains( "{$wpdb->posts}.post_type = 'unregistered_cpt'", self::$last_posts_request );
|
||||
$this->assertCount( 1, $posts, 'the valid `page` post type should still return one post' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test WP Query with an invalid post type specified in the URL.
|
||||
*
|
||||
|
||||
@@ -6,22 +6,21 @@
|
||||
class Tests_Query_PostStatus extends WP_UnitTestCase {
|
||||
public static $editor_user_id;
|
||||
public static $author_user_id;
|
||||
public static $editor_private_post;
|
||||
public static $author_private_post;
|
||||
public static $editor_privatefoo_post;
|
||||
public static $author_privatefoo_post;
|
||||
public static $subscriber_user_id;
|
||||
public static $post_ids;
|
||||
|
||||
public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
|
||||
self::$editor_user_id = $factory->user->create( array( 'role' => 'editor' ) );
|
||||
self::$author_user_id = $factory->user->create( array( 'role' => 'author' ) );
|
||||
self::$editor_user_id = $factory->user->create( array( 'role' => 'editor' ) );
|
||||
self::$author_user_id = $factory->user->create( array( 'role' => 'author' ) );
|
||||
self::$subscriber_user_id = $factory->user->create( array( 'role' => 'subscriber' ) );
|
||||
|
||||
self::$editor_private_post = $factory->post->create(
|
||||
self::$post_ids['editor_private_post'] = $factory->post->create(
|
||||
array(
|
||||
'post_author' => self::$editor_user_id,
|
||||
'post_status' => 'private',
|
||||
)
|
||||
);
|
||||
self::$author_private_post = $factory->post->create(
|
||||
self::$post_ids['author_private_post'] = $factory->post->create(
|
||||
array(
|
||||
'post_author' => self::$author_user_id,
|
||||
'post_status' => 'private',
|
||||
@@ -30,19 +29,83 @@ class Tests_Query_PostStatus extends WP_UnitTestCase {
|
||||
|
||||
// Custom status with private=true.
|
||||
register_post_status( 'privatefoo', array( 'private' => true ) );
|
||||
self::$editor_privatefoo_post = $factory->post->create(
|
||||
self::$post_ids['editor_privatefoo_post'] = $factory->post->create(
|
||||
array(
|
||||
'post_author' => self::$editor_user_id,
|
||||
'post_status' => 'privatefoo',
|
||||
)
|
||||
);
|
||||
self::$author_privatefoo_post = $factory->post->create(
|
||||
self::$post_ids['author_privatefoo_post'] = $factory->post->create(
|
||||
array(
|
||||
'post_author' => self::$author_user_id,
|
||||
'post_status' => 'privatefoo',
|
||||
)
|
||||
);
|
||||
_unregister_post_status( 'privatefoo' );
|
||||
|
||||
self::register_custom_post_objects();
|
||||
|
||||
self::$post_ids['wptests_pt1_p1'] = $factory->post->create(
|
||||
array(
|
||||
'post_type' => 'wptests_pt1',
|
||||
'post_status' => 'private',
|
||||
'post_author' => self::$editor_user_id,
|
||||
)
|
||||
);
|
||||
|
||||
self::$post_ids['wptests_pt1_p2'] = $factory->post->create(
|
||||
array(
|
||||
'post_type' => 'wptests_pt1',
|
||||
'post_status' => 'publish',
|
||||
'post_author' => self::$editor_user_id,
|
||||
)
|
||||
);
|
||||
|
||||
self::$post_ids['wptests_pt2_p1'] = $factory->post->create(
|
||||
array(
|
||||
'post_type' => 'wptests_pt2',
|
||||
'post_status' => 'private',
|
||||
'post_author' => self::$editor_user_id,
|
||||
)
|
||||
);
|
||||
|
||||
self::$post_ids['wptests_pt2_p2'] = $factory->post->create(
|
||||
array(
|
||||
'post_type' => 'wptests_pt2',
|
||||
'post_status' => 'publish',
|
||||
'post_author' => self::$editor_user_id,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
self::register_custom_post_objects();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register custom post types and statuses used in multiple tests.
|
||||
*
|
||||
* CPTs and CPSs are reset between each test run so need to be registered
|
||||
* in both the wpSetUpBeforeClass() and setUp() methods.
|
||||
*/
|
||||
public static function register_custom_post_objects() {
|
||||
register_post_type(
|
||||
'wptests_pt1',
|
||||
array(
|
||||
'exclude_from_search' => false,
|
||||
'capabilities' => array(
|
||||
'read_private_posts' => 'read_private_pt1s',
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
register_post_type(
|
||||
'wptests_pt2',
|
||||
array(
|
||||
'exclude_from_search' => false,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function test_any_should_not_include_statuses_where_exclude_from_search_is_true() {
|
||||
@@ -78,8 +141,8 @@ class Tests_Query_PostStatus extends WP_UnitTestCase {
|
||||
);
|
||||
|
||||
$expected = array(
|
||||
self::$editor_private_post,
|
||||
self::$author_private_post,
|
||||
self::$post_ids['editor_private_post'],
|
||||
self::$post_ids['author_private_post'],
|
||||
);
|
||||
|
||||
$this->assertSameSets( $expected, wp_list_pluck( $q->posts, 'ID' ) );
|
||||
@@ -109,7 +172,7 @@ class Tests_Query_PostStatus extends WP_UnitTestCase {
|
||||
);
|
||||
|
||||
$expected = array(
|
||||
self::$author_private_post,
|
||||
self::$post_ids['author_private_post'],
|
||||
);
|
||||
|
||||
$this->assertSameSets( $expected, wp_list_pluck( $q->posts, 'ID' ) );
|
||||
@@ -126,8 +189,8 @@ class Tests_Query_PostStatus extends WP_UnitTestCase {
|
||||
);
|
||||
|
||||
$expected = array(
|
||||
self::$author_private_post,
|
||||
self::$editor_private_post,
|
||||
self::$post_ids['author_private_post'],
|
||||
self::$post_ids['editor_private_post'],
|
||||
);
|
||||
|
||||
$this->assertSameSets( $expected, wp_list_pluck( $q->posts, 'ID' ) );
|
||||
@@ -144,7 +207,7 @@ class Tests_Query_PostStatus extends WP_UnitTestCase {
|
||||
);
|
||||
|
||||
$expected = array(
|
||||
self::$author_private_post,
|
||||
self::$post_ids['author_private_post'],
|
||||
);
|
||||
|
||||
$this->assertSameSets( $expected, wp_list_pluck( $q->posts, 'ID' ) );
|
||||
@@ -161,8 +224,8 @@ class Tests_Query_PostStatus extends WP_UnitTestCase {
|
||||
);
|
||||
|
||||
$expected = array(
|
||||
self::$author_private_post,
|
||||
self::$editor_private_post,
|
||||
self::$post_ids['author_private_post'],
|
||||
self::$post_ids['editor_private_post'],
|
||||
);
|
||||
|
||||
$this->assertSameSets( $expected, wp_list_pluck( $q->posts, 'ID' ) );
|
||||
@@ -225,8 +288,8 @@ class Tests_Query_PostStatus extends WP_UnitTestCase {
|
||||
)
|
||||
);
|
||||
|
||||
$this->assertContains( self::$author_privatefoo_post, wp_list_pluck( $q->posts, 'ID' ) );
|
||||
$this->assertContains( self::$editor_privatefoo_post, wp_list_pluck( $q->posts, 'ID' ) );
|
||||
$this->assertContains( self::$post_ids['author_privatefoo_post'], wp_list_pluck( $q->posts, 'ID' ) );
|
||||
$this->assertContains( self::$post_ids['editor_privatefoo_post'], wp_list_pluck( $q->posts, 'ID' ) );
|
||||
}
|
||||
|
||||
public function test_private_statuses_should_not_be_included_when_current_user_cannot_read_private_posts() {
|
||||
@@ -236,16 +299,16 @@ class Tests_Query_PostStatus extends WP_UnitTestCase {
|
||||
|
||||
$q = new WP_Query(
|
||||
array(
|
||||
'posts_per_page' => 2, // Or the query will short-circuit.
|
||||
'posts_per_page' => -1, // Or the query will short-circuit.
|
||||
)
|
||||
);
|
||||
|
||||
$expected = array(
|
||||
self::$author_privatefoo_post,
|
||||
self::$post_ids['author_privatefoo_post'],
|
||||
);
|
||||
|
||||
$this->assertContains( self::$author_privatefoo_post, wp_list_pluck( $q->posts, 'ID' ) );
|
||||
$this->assertNotContains( self::$editor_privatefoo_post, wp_list_pluck( $q->posts, 'ID' ) );
|
||||
$this->assertContains( self::$post_ids['author_privatefoo_post'], wp_list_pluck( $q->posts, 'ID' ) );
|
||||
$this->assertNotContains( self::$post_ids['editor_privatefoo_post'], wp_list_pluck( $q->posts, 'ID' ) );
|
||||
}
|
||||
|
||||
public function test_single_post_with_nonpublic_status_should_not_be_shown_to_logged_out_users() {
|
||||
@@ -457,4 +520,64 @@ class Tests_Query_PostStatus extends WP_UnitTestCase {
|
||||
|
||||
$this->assertContains( $p1, wp_list_pluck( $q->posts, 'ID' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @ticket 48556
|
||||
* @ticket 13509
|
||||
*/
|
||||
public function test_non_singular_queries_using_post_type_any_should_respect_post_type_read_private_posts_cap() {
|
||||
$post_ids = self::$post_ids;
|
||||
|
||||
wp_set_current_user( 0 );
|
||||
|
||||
$q = new WP_Query(
|
||||
array(
|
||||
'post_type' => 'any',
|
||||
)
|
||||
);
|
||||
|
||||
$this->assertSameSets( array( $post_ids['wptests_pt1_p2'], $post_ids['wptests_pt2_p2'] ), wp_list_pluck( $q->posts, 'ID' ) );
|
||||
|
||||
wp_set_current_user( self::$subscriber_user_id );
|
||||
$GLOBALS['current_user']->add_cap( 'read_private_pt1s' );
|
||||
|
||||
$q = new WP_Query(
|
||||
array(
|
||||
'post_type' => 'any',
|
||||
)
|
||||
);
|
||||
|
||||
$this->assertSameSets( array( $post_ids['wptests_pt1_p1'], $post_ids['wptests_pt1_p2'], $post_ids['wptests_pt2_p2'] ), wp_list_pluck( $q->posts, 'ID' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @ticket 48556
|
||||
* @ticket 13509
|
||||
*/
|
||||
public function test_non_singular_queries_using_multiple_post_type_should_respect_post_type_read_private_posts_cap() {
|
||||
wp_set_current_user( 0 );
|
||||
|
||||
$post_ids = self::$post_ids;
|
||||
|
||||
$q = new WP_Query(
|
||||
array(
|
||||
'post_type' => 'any',
|
||||
'posts_per_page' => -1,
|
||||
)
|
||||
);
|
||||
|
||||
$this->assertSameSets( array( $post_ids['wptests_pt1_p2'], $post_ids['wptests_pt2_p2'] ), wp_list_pluck( $q->posts, 'ID' ) );
|
||||
|
||||
wp_set_current_user( self::$subscriber_user_id );
|
||||
$GLOBALS['current_user']->add_cap( 'read_private_pt1s' );
|
||||
|
||||
$q = new WP_Query(
|
||||
array(
|
||||
'post_type' => array( 'wptests_pt1', 'wptests_pt2' ),
|
||||
'posts_per_page' => -1,
|
||||
)
|
||||
);
|
||||
|
||||
$this->assertSameSets( array( $post_ids['wptests_pt1_p1'], $post_ids['wptests_pt1_p2'], $post_ids['wptests_pt2_p2'] ), wp_list_pluck( $q->posts, 'ID' ) );
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user