[wp-trac] [WordPress Trac] #38276: "Is thing public" API
WordPress Trac
noreply at wordpress.org
Tue Oct 11 21:06:06 UTC 2016
#38276: "Is thing public" API
-----------------------------+------------------------------
Reporter: jdgrimes | Owner:
Type: feature request | Status: new
Priority: normal | Milestone: Awaiting Review
Component: Role/Capability | Version: trunk
Severity: normal | Resolution:
Keywords: | Focuses:
-----------------------------+------------------------------
Comment (by jdgrimes):
== How `WP_Query` handles visibility
There are three primary elements that play a role:
- Post passwords — These don't actually hide the post itself, but they do
obscure its contents.
- Post statuses — These can be public, private, protected, or excluded
from searches.
- The `perm` query arg — This can be `'readable'` or `'editable'`, or
empty (which it is by default, and which basically indicates "all posts").
It is important to keep in mind that `WP_Query` functions to both query
posts, and also retrieve individual posts. For this reason, some of the
logic that relates to post visibility is duplicated, to optimize based on
which of these use-cases a query instance is for. Our main concern here is
how to relate these checks to an individual post, but in `WP_Query` the
logic may not all be expressed in relation to just a single post. That's
not a problem, it is just something to keep in mind so that we make sure
we don't miss any relevant logic.
=== Post Passwords
The simplest and most straightforward logic revolves around posts with
passwords. So we'll tackle that first. There are two references to post
passwords. The first is in `WP_Query::parse_search()`, which causes
password-protected posts to be excluded when the query is a search query
(i.e., `s` query arg is being used to search for words in post content),
and the user is not logged in. Again, the post ''content'' is what is
hidden by the password, so that is being excluded from searching. Posts
with passwords aren't otherwise excluded from queries. The only other
place that post passwords are referenced is in `WP_Query::get_posts()`,
where the `post_password` and `has_password` query args are handled. The
latter is apparently unrestricted, while the former will cause the `perm`
arg to be set to `'readable'`. More on that below.
Basically, post passwords, though they do relate to the visibility of post
contents, do not relate to the visibility of posts themselves. This means
that we can ignore them in relation to an `is_thing_accessible()` API. Or
better yet, we can think about the potential for an
`is_object_attribute_accessible()` feature of the API. I think post
passwords and other methods of restricting both post contents and other
object attributes demonstrates that the potential usefulness of such an
API exists.
=== Post Statuses
The log for post statuses is split for `$this->is_single` and
non-`$this->is_single` queries, as mentioned above.
First, we'll consider the logic that is applied when the query `is_single
|| is_page`. What happens is that the object for the post's status is
retrieved, and if `$post_status_obj->public` is true, no further action is
taken. However, if the post status is not public, other logic kicks in.
This other logic can be circumvented by the logic for the `post_status`
query arg, whose handling we'll consider below, with the logic that is
applied when a query that is not for a single post. But assuming that the
`post_status` query arg hasn't been passed, it pretty much breaks down to
this:
- `public` status — Anyone can view. Note that if it isn't public and the
user isn't logged in, they can't view the post. It's that simple.
- `protected` status — The user must be logged in, and they must have the
`edit_post` capability for that post.
- `private` status — The user must be logged in, and they must have the
`read_post` capability for that post.
Don't forget though that there was a caveat in there: this logic is only
applied if the `post_status` query arg hasn't been passed. That's because
further up there is some complex logic relating to the `post_status` query
arg which is applied both when querying for a single post and when
querying for multiple posts. In that case, the query has specifically
asked for a particular post status, and so it will get it, regardless of
user permissions, unless the `perm` query arg is also set (which it isn't
by default). We'll look more at the `perm` query arg below. But basically,
the `post_status` arg is designed to allow a query to override the default
permissions that would normally apply to posts. So it really isn't
important for us to consider it too deeply in regard to post accessibility
checks.
There is one more piece of logic that involves posts statuses, which is
only applied when the `post_status` query arg isn't used and when
`$this->is_singular` is `false`. This logic basically duplicates that
which is applied when `is_single || is_page`, discussed above. All public
statuses are included by default, private statuses are only shown to users
who can `read_private_posts` or when the current user is the post author
(essentially what it means to have the `read_post` capability for a
private post). The main difference is that protected statuses are only
shown in the admin, and only if the post type specifically requests it.
Again, all of this only applies to generic queries, not checking the
ability of a user to access a particular post. So it isn't particularly
important in the development of an `is_thing_accessible()` API. One
takeaway though is that here `WP_Query` basically reproduces the logic it
expects the capabilities API to apply to posts, just in query form. This
reminds us again that a filter on `map_meta_cap`, or changes to
`map_meta_cap()` itself, won't automatically be reflected in `WP_Query`.
=== The `perm` Query Arg
As you might have gleaned from the above discussion, the `perm` query arg
is only applied when the `post_status` query arg is set. The `post_status`
query arg's only job is to override default handling for post permissions,
and the `perm` arg dictates how aggressive it is in this. As noted above,
this logic isn't particularly important to us, but I'm addressing it here
for clarity. It can take three values:
- empty — This is the default, and it indicates that a the post statuses
in `post_status` should be included ''without any permission checks at
all''. Only post statuses that have specifically requested to be excluded
from searches may be left out.
- `editable` — This will cause only posts that the current user would have
the capabilities to ''edit'' to be included.
- `readable` — This will cause only posts that the current user would have
the capabilities to ''read'' to be included.
So once again `WP_Query` basically replicates the logic that it expects
the capabilities API to apply to posts, just in query form.
Again, I've only addressed this for completeness, it isn't our main focus
in this ticket.
=== Discussion
Three main takeaways:
- We should probably consider an `is_thing_attribute_accessible()` API,
cf. password-protected posts.
- Post statuses are the only visibility restricting thing that `WP_Query`
takes into account for posts themselves (as opposed to password-protected
post ''content'').
- Post statuses are either totally public, or they are restricted, which
always falls back to the capabilities API.
The implications of the latter two points is that currently WordPress is
designed to only restrict post visibility based on post statuses. While it
is possible for plugins to imagine all sorts of other things that might
dictate post visibility, the only method that WordPress provides is a
binary choice of an always-public status or an always-restricted (though
possibly to varying degrees) status. Plugins can hook into the Query API,
and/or into the Capabilities API, in order to provide handling for other
situations. However, both of these APIs only allow the plugin to implement
restrictions, neither of them makes it possible for it to indicate when a
post is public.
The post status API ''does'', but is simply not a fit for every time that
a plugin wants to restrict post visibility. That is why an "is thing
public" API is needed: it will not just wrap the post status API, it will
also allow plugins to more confidently tinker with post visibility through
custom means. More confidently, because they will be able to hook into the
`is_thing_accessible()` API to provide that public vs restricted
information for objects, so that it can actually be conveyed to other
plugins in this way. Which again, as we've shown above, neither the caps
nor query API do.
--
Ticket URL: <https://core.trac.wordpress.org/ticket/38276#comment:7>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform
More information about the wp-trac
mailing list