[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