Skip to content
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

Add show show_in_rest param to register_sidebar #1578

Closed
wants to merge 16 commits into from

Conversation

spacedmonkey
Copy link
Member

Trac ticket: https://core.trac.wordpress.org/ticket/53915


This Pull Request is for code review only. Please keep all other discussion in the Trac ticket. Do not merge this Pull Request. See GitHub Pull Requests for Code Review in the Core Handbook for more details.

@TimothyBJacobs
Copy link
Member

Won't we need a change in the widgets controller to check if a widget is part of a public sidebar?

@spacedmonkey
Copy link
Member Author

@TimothyBJacobs Updated the PR with your feedback.

One question. Should we / can we change raw instance values to only display in edit context.

'raw' => array(
'description' => __( 'Unencoded instance settings, if supported.' ),
'type' => 'object',
'context' => array( 'view', 'edit', 'embed' ),
),

I am wonder about sensitive data leaking?

@TimothyBJacobs
Copy link
Member

Thanks. Looks like the test failure may be legit.

We would need to change the whole instance value to be only be exposed in an edit context, since the encoded form is just encoded, not hidden.

I think sensitive data leakage is definitely something we should be concerned about. While a theme author may opt-in to having their sidebar available, they might not know that a widget's setting contains an API key for instance.

However, I think there is definitely value in having the instance values available, not just the rendered widget, to allow for rendering the widget yourself.

I think for now we could omit the instance entirely, but it may make sense in the future to allow for show_instance_in_rest to be overloaded to a configuration array similarly to register_meta to allow for a widget author to selectively expose certain fields.

@spacedmonkey
Copy link
Member Author

Seems like the issue here was running retrieve_widgets twice, once in permission check and once in *_items function. I added a workaround for this, with some state is seems to work. I would love some feedback on managing state like this better. Thoughts @TimothyBJacobs

@TimothyBJacobs
Copy link
Member

Pinging @adamziel as he's been looking into retrieve_widgets issues.

@spacedmonkey
Copy link
Member Author

We would need to change the whole instance value to be only be exposed in an edit context, since the encoded form is just encoded, not hidden.

So should only instance be changed to 'context' => array( 'edit' ), or instance, instance.encoded, instance.hash and instance.raw be changed?

'instance' => array(
'description' => __( 'Instance settings of the widget, if supported.' ),
'type' => 'object',
'context' => array( 'view', 'edit', 'embed' ),
'default' => null,
'properties' => array(
'encoded' => array(
'description' => __( 'Base64 encoded representation of the instance settings.' ),
'type' => 'string',
'context' => array( 'view', 'edit', 'embed' ),
),
'hash' => array(
'description' => __( 'Cryptographic hash of the instance settings.' ),
'type' => 'string',
'context' => array( 'view', 'edit', 'embed' ),
),
'raw' => array(
'description' => __( 'Unencoded instance settings, if supported.' ),
'type' => 'object',
'context' => array( 'view', 'edit', 'embed' ),
),
),
),

@adamziel
Copy link
Contributor

adamziel commented Aug 30, 2021

I added a workaround for this, with some state is seems to work. I would love some feedback on managing state like this better.

@spacedmonkey I think the mental model of retrieve_widgets is broken right now. Even the name is confusing: That function doesn't just retrieve stuff, it actually updates the database. It's like "switching themes breaks the stored data, so we need to fix the data before each usage". I would like to make it more of "we never break the data in the first place" but that's a larger project. For now I just proposed a more accurate name and it has commit label so you might want to update this PR once that change is merged.

Since your issue seems to come from running retrieve_widgets twice and we've seen similar problems earlier, I wonder if this fix might help you.

Other than that, I don't think that we have a good way around multiple function calls other than what you proposed. Well, maybe aside of a hook that would run when a related REST route is matched – that way we could have only a single call that would cover both the permissions check and the actual handler.

@spacedmonkey
Copy link
Member Author

@adamziel Do you think that #1525 blocks this PR? Would it effect anything in your PR? Did you review this PR?

@adamziel
Copy link
Contributor

adamziel commented Sep 2, 2021

l Do you think that #1525 blocks this PR?

@spacedmonkey I wouldn't say it blocks, no. Maybe it would allow you to get rid of this check:

		if ( $this->widgets_retrieved ) {
			return;
		}

But I'm not 100% certain about that since I don't have a good grasp of what the problem was.

Would it effect anything in your PR?

I don't this PR would affect #1525 much – a rebase, if necessary, should be pretty easy. Feel free to move forward with this one once you have approvals.

Did you review this PR?

I read the code and it looks good, but I did not test much. I'm happy to help with testing once you confirm the code is where you want it to be, sync_registered_widgets or not.

Also, one more question – should this PR be moved to the Gutenberg repo so that 5.7+plugin may benefit from these changes? That's what we did with WordPress/gutenberg#34230, it may or may not make sense here as well

@spacedmonkey
Copy link
Member Author

Also, one more question – should this PR be moved to the Gutenberg repo so that 5.7+plugin may benefit from these changes? That's what we did with WordPress/gutenberg#34230, it may or may not make sense here as well

Yeah, it can be backported.

@spacedmonkey
Copy link
Member Author

I wouldn't say it blocks, no. Maybe it would allow you to get rid of this check:

So we should wait until your work is merged before this goes in?

@adamziel
Copy link
Contributor

adamziel commented Sep 3, 2021

If it takes a few days then it would be convenient – potentially one check less in your PR. If it takes more, it's probably not worth it – we could re-evaluate that check at some later time.

Maybe just rebasing this PR on top of #1525 would do the trick? I think the consensus is there and we were just adjusting code standards. Also, if #1525 does not solve the "if retrieve_widgets already called" situation, feel free to just proceed without looking back.

@adamziel
Copy link
Contributor

adamziel commented Sep 3, 2021

Yeah, it can be backported.

Why not Gutenberg-first? These "double" changes have been challenging lately, see the discussion in WordPress/gutenberg#33810 and WordPress/gutenberg#34536.

The sidebars endpoint is getting removed from the next Gutenberg version, so no problem there. It's all about having a consistent widgets endpoint now.

@spacedmonkey
Copy link
Member Author

Why not Gutenberg-first? These "double" changes have been challenging lately

This change doesn't make sense to backport. It has little use in the gutenberg plugin and it an opt in feature for devs. It is most useful for headless applications of WordPress.

@adamziel
Copy link
Contributor

adamziel commented Sep 6, 2021

This change doesn't make sense to backport. It has little use in the gutenberg plugin and it an opt in feature for devs. It is most useful for headless applications of WordPress.

I see, thank you for explaining! I am only concerned about merging changes later on. If we keep updating the same endpoints both in core and in the Gutenberg repo, we may end up with conflicting changes. On the other hand, I think I discussed that with @gziolo in WordPress/gutenberg#33810 and the conclusion was that in practice this rarely becomes a problem so maybe we're good here?

@spacedmonkey
Copy link
Member Author

this rarely becomes a problem so maybe we're good here?

If there was any value in adding this to the gutenberg plugin, I would have done it both places. I will do that future PRs.

@spacedmonkey
Copy link
Member Author

Unit tests are passing!

@@ -240,6 +240,46 @@ public function test_get_items_no_permission() {
$this->assertErrorResponse( 'rest_cannot_manage_widgets', $response, 401 );
}

public function test_get_items_no_permission_public() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confused because the test name says "no permission public", but then it sets up a sidebar with two widgets and the endpoint returns both. I'd add a few test cases where the API returns a limited list of widgets or a 403 error.

/**
* @ticket 41683
*/
public function test_get_items_no_permission_public() {
Copy link
Contributor

@adamziel adamziel Sep 30, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few additional test cases would go a long way, see my other comment

* @param array $sidebar Sidebar.
* @return bool Whether the side can be read.
*/
public function check_read_permission( $sidebar ) {
Copy link
Contributor

@adamziel adamziel Oct 26, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about this below?

Suggested change
public function check_read_permission( $sidebar ) {
public function can_show_sidebar_in_rest( $sidebar ) {

check_read_permission is called quite similarly to get_item_permissions_check and do_permissions_check, I got a bit confused after returning to this PR.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This naming follows existing core methods. See

public function check_read_permission( $post ) {
// By default the read_post capability is mapped to edit_posts.
if ( ! current_user_can( 'read_post', $post->ID ) ) {
return false;
}
return parent::check_read_permission( $post );
}

foreach ( wp_get_sidebars_widgets() as $id => $widgets ) {
$sidebar = $this->get_sidebar( $id );

if ( ! $sidebar ) {
continue;
}

if ( is_wp_error( $permissions_check ) && ! $this->check_read_permission( $sidebar ) ) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

show_in_rest is only checked for users without edit_theme_options capability. Is this intended? Intuitively, I would expect that setting an option named show_in_rest to false will remove my sidebar from any and all REST API responses.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this is namely wrong. show_in_rest only means show publically in rest. It will always be in the rest api.

If we remove this, then it the widget editor will not work.


foreach ( wp_get_sidebars_widgets() as $sidebar_id => $widget_ids ) {
if ( isset( $request['sidebar'] ) && $sidebar_id !== $request['sidebar'] ) {
continue;
}

if ( is_wp_error( $permissions_check ) && ! $this->check_read_sidebar_permission( $sidebar_id ) ) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have the same question as above – is it intended that show_in_rest is not effective for users with the right permissions?


$prepared = array();
$prepared = array();
$permissions_check = $this->permissions_check( $request );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
$permissions_check = $this->permissions_check( $request );
$user_has_permissions = $this->permissions_check( $request );

@adamziel
Copy link
Contributor

PHPUnit failures seem to be just flaky CI. I think this PR is pretty close 👍 I left some last notes and proposed a few tests.

Copy link
Contributor

@adamziel adamziel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 👍

@TimothyBJacobs
Copy link
Member

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants