Opened 10 years ago
Last modified 6 years ago
#28568 reopened defect (bug)
Can't get private posts/pages via WP_Query when not logged in as administrator
Reported by: | mklusak | Owned by: | |
---|---|---|---|
Milestone: | Awaiting Review | Priority: | normal |
Severity: | normal | Version: | 3.9 |
Component: | Query | Keywords: | |
Focuses: | Cc: |
Description
I want to use WP_Query to display content of one private page on some place in a template.
// ... theme template sidebar.php, for example <?php $the_query = new WP_Query( array( 'page_id' => 25, 'post_status' => 'private' )); // page #25 is set to "private" if ($the_query->have_posts()) { // ... nope, no posts for you today while ($the_query->have_posts()) { $the_query->the_post(); the_content(); } wp_reset_postdata(); } ?>
But it only works when I am logged in as administrator.
When I am not logged in, and print_r() the $the_query, there is "request" index filled with:
SELECT wp_posts.* FROM wp_posts WHERE 1=1 AND wp_posts.ID = 25 AND wp_posts.post_type = 'page' AND ((wp_posts.post_status = 'private')) ORDER BY wp_posts.post_date DESC
Querying database with it directly in PHPMyAdmin, it returns one row as expected (page ID 25 row). But template loop does not anything. "Posts" index in $the_query is empty.
I have expected WP_Query works like "just give me anything I want (defined by the arguments)" ... and I want page with ID 25.
Change History (9)
#2
follow-up:
↓ 3
@
10 years ago
- Keywords reporter-feedback removed
Well, I have my reasons. For example I don't want that page URL to be indexable, but I want its content displayed somewhere...
This is "magic" behaviour for me. I want exact post/page... using the most proper WP function... and I don't get it. Even when I know it is private and I am declaring it in arguments array.
If this is a feature, then it should be mentioned in Codex: "You can't use post_status => private on front-end, it works only for logged in users".
#3
in reply to:
↑ 2
@
10 years ago
Replying to mklusak:
If this is a feature, then it should be mentioned in Codex: "You can't use post_status => private on front-end, it works only for logged in users".
It is a feature, see the code in WP_Query
: tags/3.9.1/src/wp-includes/query.php#L3307.
And it's actually mentioned in WP_Query docs: ('private' – not visible to users who are not logged in).
#4
@
10 years ago
- Keywords close removed
- Milestone Awaiting Review deleted
- Resolution set to invalid
- Status changed from new to closed
#5
@
10 years ago
Well, thank you for your responses. It's obvious that the Codex line is a little bit misleading ... because of hours of my time spent by googling any solution and composing Trac ticket. I have read it as "post_status => private - you can get programmatically posts, which URL access is restricted for not logged in users (--> not visible to users who are not logged in)" ... but as I see now, I was wrong. Well, now I am smarter.
#6
@
6 years ago
I came across this thread because I had the same issue.
I noticed that you can't get a private post by id if not logged, but it works if you remove 'page_id' argument; specifying only 'post_status' = 'private' without 'page_id', you'll get all posts including private ones.
Feature or (security) bug?
#7
@
6 years ago
- Resolution invalid deleted
- Status changed from closed to reopened
For everyone who stumble over this bug there is a solution to bypass:
<?php $posts = []; $query = new \WP_Query(); $fetchPosts = function ( array $postResults, \WP_Query $currentQuery ) use ( &$posts, $query ): array { if ( $currentQuery === $query ) { $posts = $postResults; } return $postResults; }; add_filter( 'posts_results', $fetchPosts, 10, 2 ); $query->query( ['post_status' => 'any'] ); remove_filter( 'posts_results', $fetchPosts, 10 );
@ocean90 Maybe you want to rethink your decision. I know of no reason why WP_Query (which is the only API to fetch posts from database) should depend on global state whether a user is logged in or not. That makes no sense. These security «protections» inside WP_Query::get_posts() should at least only act if it's the main query or should be bypassed by the suppress_filters parameter.
#8
@
6 years ago
I agree with @dnaber-de and @mklusak:
There's an inconsistency between get_post()
and get_posts()
which is confusing:
$private_id = 123; $item1 = get_post( $private_id ); $item2 = get_posts( [ 'p' => $private_id, 'post_type' => 'page' ] ); // $item1 is a WP_Post object of the private post. The private-state is not checked here. // $item2 is an empty array, because I am not logged in.
There should be a new option for get_posts like ignore_private => true
.
The post status
private
should be understood as as non-public-stuff. Why is the page private if it's public?