Repository navigation
Revisions: render the author for users who cannot edit users - #82306
davidsword wants to merge 2 commits into
Conversation
The revisions inspector renders each revision's author with AuthorView, which resolves the user through getEntityRecord( 'root', 'user', id ). The root/user entity is fixed to context=edit, and WP_REST_Users_Controller::get_item_permissions_check() refuses that context unless the caller can edit_user the target. Any user without edit_users therefore receives a 403, and the row renders with an empty name and the fallback avatar icon. Viewers still see their own row, because the controller short-circuits when the caller is the target. Rather than widen who may read a user, supply the author alongside the revision. Gutenberg_REST_Revisions_Controller now exposes author_name and author_avatar_urls next to the numeric author, mirroring WP_REST_Comments_Controller, and AuthorView prefers them and skips the fetch when they are present. That also removes one request per distinct revision author. Drops a comment in AuthorView claiming the record is already in the store from the field's getElements function. getElements populates the 'view' context bucket through getEntityRecords, while AuthorView reads the default bucket through getEntityRecord, so the two never share a cache entry and the fetch always happened. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message. To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
|
Thanks for the issue + PR!!
That's the usual method. It can be a pain, but it keeps compat features separate. When Gutenberg drops support for older versions of WordPress, the older folders can be mostly deleted with the current compat files still in effect. The next folder would be In terms of reviewing I'd lean on @ntsekouras or @ellatrix as they've recently been looking at revisions. |
The author fields were added to Gutenberg_REST_Revisions_Controller in lib/compat/wordpress-7.0/. They have not shipped in any release, so they belong in a version-suffixed subclass under the compat directory for the release they target, following Gutenberg_REST_Attachments_Controller_7_1. Restores the 7.0 controller and adds Gutenberg_REST_Revisions_Controller_7_2 in lib/compat/wordpress-7.2/, which extends it. Rather than duplicating the whole of prepare_item_for_response(), the subclass calls the parent and adds author_name and author_avatar_urls to the response, so the 7.0 nested _fields handling stays in one place. lib/compat/wordpress-7.2/rest-api.php points revisions_rest_controller_class at the new class at priority 11, superseding the 7.0 override while leaving a controller a site set itself alone. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Thanks @ramonjd! Moved it to |
ntsekouras
left a comment
There was a problem hiding this comment.
Thanks @davidsword! I've left some comments, but the fix works well for me as an editor. Can you rebase?
| */ | ||
| export interface BasePost extends CommonPost { | ||
| author_name?: string; | ||
| author_avatar_urls?: Record< number, string >; |
There was a problem hiding this comment.
I guess we can match the type as above:
Partial< User< 'view' >[ 'avatar_urls' ] >
| * @see Gutenberg_REST_Revisions_Controller | ||
| * @see WP_REST_Comments_Controller | ||
| */ | ||
| class Gutenberg_REST_Revisions_Controller_7_2 extends Gutenberg_REST_Revisions_Controller { |
There was a problem hiding this comment.
Curious why we need a subclass here.. Wouldn't register_rest_field on {post_type}-revision do the same with less code?
| // WP_REST_Users_Controller only allows that context for users who can | ||
| // `edit_user` the target, so everyone else receives a 403 and no name. | ||
| const authorId = item?.author; | ||
| const inlineAuthorName = item?.author_name; |
There was a problem hiding this comment.
While the changes aren't big here, I'm wondering if it's better to have a new revisionAuthor field. Wdyt @oandregal?
|
👋 - While reviewing this PR I noticed that the author field would remain blank for users that cannot edit users (e.g. editor roles). When I started fixing the above as a separate complementary PR, I remembered about I opened a PR for that here: #83498. Can you test that? You'll get the props if that supersedes this one. |
|
Thank you for the thorough investigation and fix, @davidsword! The author rendering for users without |
What?
Closes #82305
Renders a revision's author for users who cannot
edit_users, by supplying the author's display name and avatars with the revision instead of resolving them through a separate user request.Why?
AuthorViewresolves a revision's author withgetEntityRecord( 'root', 'user', authorId ). Theroot/userentity is fixed tocontext: 'edit', andWP_REST_Users_Controller::get_item_permissions_check()refuses that context unless the caller canedit_userthe target, which maps to the administrator-onlyedit_userscapability.Every other role therefore gets a
403 rest_forbidden_contextand the row renders as an empty name next to the fallback person icon. Viewers still see their own row, because the controller short-circuits when the caller is the target. Captured as an editor-role user on one page load:The obvious fix is to pass
{ context: 'view' }at the call site, and it does work for the common case. I did not take that route, because a second gate in the same permission check (count_user_posts()) still refuses a user who has never published, even inviewcontext. Any approach that resolves the author through/wp/v2/users/<id>hits that gate,_embedincluded, sincecontext=embedonly skips the first one. That leaves a blank name for a revision authored by someone with no published posts.How?
Supplies the author with the revision, mirroring
WP_REST_Comments_Controller, which has exposedauthor_nameandauthor_avatar_urlsdirectly for exactly this reason since 4.7. The server decides what to expose, the client never asks about a user, and the permission question does not arise.Gutenberg_REST_Revisions_Controller_7_2(new, inlib/compat/wordpress-7.2/) extendsGutenberg_REST_Revisions_Controllerand exposesauthor_nameandauthor_avatar_urlsalongside the existing numericauthor, plus aget_item_schema()override declaring them. It calls the parent'sprepare_item_for_response()and adds the two fields to the response rather than duplicating the method, so the 7.0 nested_fieldshandling stays in one place. The avatar block follows the comments controller, including theshow_avatarsguard and therest_get_avatar_sizes()loop.lib/compat/wordpress-7.2/rest-api.phppointsrevisions_rest_controller_classat the new class at priority 11, superseding the 7.0 override while leaving a controller a site set itself alone.buildRevisionsPageQueryrequests the two new fields.AuthorViewprefers them and skips the fetch when they are present, falling back to the existing fetch and_embeddedpaths otherwise, so it behaves unchanged wherever the fields are absent.This also removes one REST request per distinct revision author. In the repro below, the revisions sidebar goes from 3 user requests to 0.
Two incidental fixes:
getElementsalready populated the store. Records are keyed by the query's context:getElementsusesgetEntityRecordswithcontext: 'view'and writes the"view"bucket, whileAuthorViewreads the"default"bucket. They never share an entry, so the request always happened.getElementsalso does not run in the revisions inspector at all.author_nameandauthor_avatar_urlstoBasePost.Testing Instructions
alice) and an editor (bob).alice, then edit and save it twice more asalice.bob.bob, open the post, enter the revisions view, and open the settings sidebar.trunk,alice's two revisions show an empty name and a generic person icon.aliceto confirm no regression for administrators.context: 'view'.Verified against WordPress 7.2-alpha with an editor-role viewer, four revisions by three authors, one of whom has no published posts:
/wp/v2/usersrequests["", "Bob Editor", "", ""]403alice,403carol,200self["Carol NoPosts", "Bob Editor", "Alice Admin", "Alice Admin"]Also checked:
view/edit/embedcontexts,_fieldsstill honoured when the new fields are not requested,title.rawsub-field filtering unaffected,show_avatars = 0omitsauthor_avatar_urlsfrom both the response and the schema, andHEADstill returns an empty body.Testing Instructions for Keyboard
No UI or interaction changes. The revision list is reachable and operable exactly as before; only the rendered author text and avatar differ.
Screenshots or screencast
Before

After
