-
Notifications
You must be signed in to change notification settings - Fork 4.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use targetSchema
of JSON Hyper Schema to communicate sticky action
#6529
Conversation
<PostStickyCheck postType="page" post={ { | ||
_links: { | ||
self: { | ||
href: 'https://w.org/wp-json/wp/v2/posts/5', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be /page/5
if postType="page"
? (Doesn't change test result, admittedly)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
}, | ||
'wp:action-sticky': { | ||
href: 'https://w.org/wp-json/wp/v2/posts/5', | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Once again the test works as-is because of what we're testing for, but all of the keys on the _links
object hold arrays of objects, instead of plain objects
(e.g. wp:action-sticky': [ { /* ... */ } ]
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lib/rest-api.php
Outdated
), | ||
), | ||
), | ||
), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have this issue all over the API, but this is a lot of data to be piling into the response just to indicate whether the UI can take some action. Do we really want to be repeating all of this:
for each post every time we're in a collection where we might possibly want to sticky something? Our _links
object is getting quite heavy.
I don't see a better option so I'm 👍 on the implementation, but I think we'll need to come back to that at some point.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we ever land generating a URL to access a schema, the targetSchema
section could be replaced with a $ref
for the property.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could also remove the description
property but it'd make the link less helpful for clients.
@@ -27,14 +23,8 @@ export function PostStickyCheck( { postType, children, user } ) { | |||
export default compose( [ | |||
withSelect( ( select ) => { | |||
return { | |||
post: select( 'core/editor' ).getCurrentPost(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be better to return only:
hasStickyAction: get( post, [ '_links', 'wp:action-sticky' ], false )`
from withSelect
to avoid unnecessary rerenders when the current post changes after unrelated modifications.
In general, it is recommended to pass down as props only what is really used inside the component.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch, fixed in 3e591c5
Because WordPress' capabilities system is filterable, we need to execute the capabilities before we know whether the current user can perform the action. Fortunately, `targetSchema` supports exactly this use-case.
18e815f
to
e2bf660
Compare
lib/rest-api.php
Outdated
// Only Posts can be sticky. | ||
if ( 'post' === $post->post_type ) { | ||
if ( current_user_can( $post_type->cap->edit_others_posts ) | ||
&& current_user_can( $post_type->cap->publish_posts ) ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is the capability for publishing posts required to make a post sticky?
More importantly though, this should use the meta capabilities for the specified post and rely on map_meta_cap()
instead of handling the logic manually:
if ( 'post' === $post->post_type ) {
if ( current_user_can( 'edit_post', $post->ID ) && current_user_can( 'publish_post', $post->ID ) {
// Add link.
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is the capability for publishing posts required to make a post sticky?
I'm using the check within edit_post()
as my point of reference: https://github.com/WordPress/WordPress/blob/3266b10d04ad43b9d983e0b55d4d6247be33e18a/wp-admin/includes/post.php#L401
More importantly though, this should use the meta capabilities for the specified post and rely on
map_meta_cap()
instead of handling the logic manually.
I'm not sure I follow. Can you explain why?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm using the check within
edit_post()
as my point of reference: https://github.com/WordPress/WordPress/blob/3266b10d04ad43b9d983e0b55d4d6247be33e18a/wp-admin/includes/post.php#L401
Okay, I didn't know this was previously being used for it. I'm not sure that check makes sense though even in this area.
Basically, any action you perform to a specific item (like a post) should go through a meta capability check (using a singular noun, like edit_post
or publish_post
). Logic to resolve these to their primitive required capabilities is in place in the map_meta_cap()
function, and developers rely on this functionality, for example to revoke that permission from a user (for example for one specific post).
Therefore I think the check in edit_post()
is not accurate either. For this very case, there's no appropriate capability yet, so I'd recommend to introduce stick_post
and unstick_post
meta capabilities and add a clause for them in map_meta_cap()
. We can then resolve it to the primitive capabilities we want to in there (if those are decided to be what currently happens in edit_post()
, that's alright I guess). The benefit is that we then don't have to memorize all over what to check for when making a post sticky or unsticky, we can simply use intuitive stick_post
/ unstick_post
, both in the current backend, the REST API, anywhere.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Therefore I think the check in
edit_post()
is not accurate either. For this very case, there's no appropriate capability yet, so I'd recommend to introducestick_post
andunstick_post
meta capabilities and add a clause for them inmap_meta_cap()
. We can then resolve it to the primitive capabilities we want to in there (if those are decided to be what currently happens inedit_post()
, that's alright I guess). The benefit is that we then don't have to memorize all over what to check for when making a post sticky or unsticky, we can simply use intuitivestick_post
/unstick_post
, both in the current backend, the REST API, anywhere.
Does what you describe need to be solved for in this pull request?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given that only the post
post type can be sticky, I don't think there's a need to add these special meta capabilities.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've been letting this tumble around for a bit, and I have a few concerns.
First off, I like the idea of providing information in the form of "this is an action the current user can perform, and here are the properties that action relates to". This is useful information to provide.
As has been mentioned, this solution seems quite verbose. I guess a major part of that is the description
being on the post object, instead of in the schema, where all the other descriptions are. It's also duplicating the description of the sticky
property, rather than documenting what action-sticky
is for.
On that note, the naming is quite obtuse, too, it took me way too long to understand how the data is supposed to be used. There's nothing to indicate that "the presence of wp:action-sticky
mean you should render the Sticky Post UI".
I think this would be better in a sibling property to _links
, _actions
, or something like that. _links
is entirely used as a list of shortcuts to related information about the current object, adding in "here is an action the current user can perform" is confusing.
lib/rest-api.php
Outdated
// Only Posts can be sticky. | ||
if ( 'post' === $post->post_type ) { | ||
if ( current_user_can( $post_type->cap->edit_others_posts ) | ||
&& current_user_can( $post_type->cap->publish_posts ) ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given that only the post
post type can be sticky, I don't think there's a need to add these special meta capabilities.
This would have the added benefit that such information could be omitted using |
Can you point me to the existing spec where this is documented? |
It isn't documented as far as I'm aware, this is just how As far as adhering to the JSON API spec goes, I'm fine with using something that exists within the spec, if that fits our requirements. If not, I'm 💯 in favour of doing something that works, rather than something that's shoe-horned into a vaguely related structure. |
I'm not particularly interested in spending a ton of time bikeshedding an alternative spec. Can you describe the alternative implementation you'd accept? cc @TimothyBJacobs for any thoughts he has. |
Sure, here's a rough idea of something I think would be extensible, would clearly show the difference between In the schema, {
"routes": {
"/wp/v2/posts/(?P<id>[\\d]+)": {
"endpoints": [
{
"methods": [ "POST", "PUT", "PATCH" ],
"args": {
// ...
},
"_actions": {
"wp:sticky": {
"title": "Sticky Post",
"description": "Whether the current user can sticky the post or not",
"targetSchema": {
// Something in here about being related to the "sticky" property.
}
}
}
},
],
},
},
} Then, in the post object, the {
"id": 1,
// ...
"_actions": {
"wp:sticky": true,
},
} Alternatively, {
"id": 1,
// ...
"_actions": [
"wp:sticky",
],
} |
Again, I'm strongly opposed to inventing a new spec when the existing spec is sufficient. In inventing a new spec, we'll need to go through multiple iterations of relearning past mistake/decision cycles. This doesn't seem like a judicious use of our time. To address response size concerns, what if we only included these action links if |
I agree with @danielbachhuber. I don't see why including them in a separate "magic" field makes much sense. As I mentioned earlier, we can definitely omit the We've talked in #core-restapi a few times about adding links to the actual schemas. We could revisit this and try to land it for 5.0. This would allow us to replace the entire property description with a I also think including the links only if the {
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "post",
"type": "object",
"properties": {
"date": {
"description": "The date the object was published, in the site's timezone.",
"type": "string",
"format": "date-time",
"context": [
"view",
"edit",
"embed"
]
}
},
"links": [
{
"rel": "https://api.w.org/action-sticky",
"href": "https://actualwebsite.com/wp-json/wp/v2/posts/{id}",
"targetSchema": {
"type": "object",
"properties": {
"sticky": {
"$ref": "#/properties/sticky"
}
}
}
}
]
} Our response could possibly then look something like. {
"_links": {
"self": [],
"wp:action-sticky": [
{
"href": "https://w.org/wp-json/wp/v2/posts/5"
}
]
}
} However, we are not currently using this pattern anywhere else and we are introducing concepts like URI Templates and JSON Pointers, which while I'd be in favor of the API supporting, are not things we have looked at before. |
Yah, that'd be fine. I don't think there's a need for them in other contexts, we can always add it to them if there is. That's not really the issue, though. The specific concern I have is that, when reading these new links, there's nothing to suggest what they mean, or how you use them. The same problem exists in @TimothyBJacobs' example: there's nothing to suggest what The problem we need to solve is that there are pieces of UI that should only be displayed when a certain set of conditions are met: we want to be calculating those conditions on the server, rather than leaving it up to the client. So, here's what I would like to see:
If there isn't a spec, then we can just make something that works, and iterate it on it later if needed. |
I disagree. If you understand what I also disagree that it is super complex. Its a fairly pattern of representing what the target resource looks like and is certainly no more complex than many other G7g patterns that are necessary to fulfill a complex requirement. |
To clarify my own position: I'm open to being convinced there is an implementation superior to that proposed on this pull request. However, at this point, I'm unclear as to what the alternative implementation is, and how it's functionally better. From my perspective, the
|
In case it wasn’t clear, I of course have no issue with some other mechanism being used to indicate what actions are available, but I very much do not want to see WordPress adopt another standard in a weird way that isn’t really applicable and break the purpose of it being standard without a very good reason. And I don’t think that |
Thank you for the discussion, @danielbachhuber and @TimothyBJacobs. cce57b8 is a step in a good direction. I've opened #6613 with some small tweaks, but I think we can go with this. |
We're not introducing meta caps at this point.
Thanks @pento. |
What is the best method to remove 'Sticky' option/toggle from Posts globally now? |
Description
Updates the
post-sticky
check to use presence ofwp:action-sticky
in_links
to determine whether the sticky UI should display. This system is calledtargetSchema
. It allows us to compute whether a user can perform an action server-side, and then communicate it to the client.See #6361 (comment)
How has this been tested?
For editors and above, the "Stick to the Front Page" UI should display:
For authors and below, it should not:
User Switching is a helpful plugin for quickly testing this.
Checklist: