Skip to content

Commit 827dd20

Browse files
REST API: Allow sidebars and their widgets to be public.
By default, only users with the `edit_theme_options` capability can access the sidebars and widgets REST API endpoints. In this commit, A new `show_in_rest` parameter is added to the `register_sidebar` function. When enabled, all users will be able to access that sidebar and any widgets belonging to that sidebar. This commit reduces the `context` for a widget's `instance` information to only `edit`. This is to ensure that internal widget data is not inadvertently exposed to the public. A future ticket may expose additional APIs to allow widget authors to indicate that their instance data can be safely exposed. REST API consumers intending to access this `instance` information should take care to explicitly set the `context` parameter to `edit`. Props spacedmonkey, zieladam. Fixes #53915. git-svn-id: https://develop.svn.wordpress.org/trunk@52016 602fd350-edb4-49c9-b593-d223f7449a82
1 parent 4a67a97 commit 827dd20

File tree

6 files changed

+445
-107
lines changed

6 files changed

+445
-107
lines changed

src/wp-includes/rest-api/endpoints/class-wp-rest-sidebars-controller.php

Lines changed: 63 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,14 @@
1818
*/
1919
class WP_REST_Sidebars_Controller extends WP_REST_Controller {
2020

21+
/**
22+
* Tracks whether {@see retrieve_widgets()} has been called in the current request.
23+
*
24+
* @since 5.9.0
25+
* @var bool
26+
*/
27+
protected $widgets_retrieved = false;
28+
2129
/**
2230
* Sidebars controller constructor.
2331
*
@@ -86,6 +94,19 @@ public function register_routes() {
8694
* @return true|WP_Error True if the request has read access, WP_Error object otherwise.
8795
*/
8896
public function get_items_permissions_check( $request ) {
97+
$this->retrieve_widgets();
98+
foreach ( wp_get_sidebars_widgets() as $id => $widgets ) {
99+
$sidebar = $this->get_sidebar( $id );
100+
101+
if ( ! $sidebar ) {
102+
continue;
103+
}
104+
105+
if ( $this->check_read_permission( $sidebar ) ) {
106+
return true;
107+
}
108+
}
109+
89110
return $this->do_permissions_check();
90111
}
91112

@@ -95,19 +116,25 @@ public function get_items_permissions_check( $request ) {
95116
* @since 5.8.0
96117
*
97118
* @param WP_REST_Request $request Full details about the request.
98-
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
119+
* @return WP_REST_Response Response object on success.
99120
*/
100121
public function get_items( $request ) {
101-
retrieve_widgets();
122+
$this->retrieve_widgets();
123+
124+
$data = array();
125+
$permissions_check = $this->do_permissions_check();
102126

103-
$data = array();
104127
foreach ( wp_get_sidebars_widgets() as $id => $widgets ) {
105128
$sidebar = $this->get_sidebar( $id );
106129

107130
if ( ! $sidebar ) {
108131
continue;
109132
}
110133

134+
if ( is_wp_error( $permissions_check ) && ! $this->check_read_permission( $sidebar ) ) {
135+
continue;
136+
}
137+
111138
$data[] = $this->prepare_response_for_collection(
112139
$this->prepare_item_for_response( $sidebar, $request )
113140
);
@@ -125,9 +152,28 @@ public function get_items( $request ) {
125152
* @return true|WP_Error True if the request has read access, WP_Error object otherwise.
126153
*/
127154
public function get_item_permissions_check( $request ) {
155+
$this->retrieve_widgets();
156+
157+
$sidebar = $this->get_sidebar( $request['id'] );
158+
if ( $sidebar && $this->check_read_permission( $sidebar ) ) {
159+
return true;
160+
}
161+
128162
return $this->do_permissions_check();
129163
}
130164

165+
/**
166+
* Checks if a sidebar can be read publicly.
167+
*
168+
* @since 5.9.0
169+
*
170+
* @param array $sidebar The registered sidebar configuration.
171+
* @return bool Whether the side can be read.
172+
*/
173+
protected function check_read_permission( $sidebar ) {
174+
return ! empty( $sidebar['show_in_rest'] );
175+
}
176+
131177
/**
132178
* Retrieves one sidebar from the collection.
133179
*
@@ -137,10 +183,9 @@ public function get_item_permissions_check( $request ) {
137183
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
138184
*/
139185
public function get_item( $request ) {
140-
retrieve_widgets();
186+
$this->retrieve_widgets();
141187

142188
$sidebar = $this->get_sidebar( $request['id'] );
143-
144189
if ( ! $sidebar ) {
145190
return new WP_Error( 'rest_sidebar_not_found', __( 'No sidebar exists with that id.' ), array( 'status' => 404 ) );
146191
}
@@ -234,28 +279,25 @@ protected function do_permissions_check() {
234279
*
235280
* @since 5.8.0
236281
*
237-
* @global array $wp_registered_sidebars The registered sidebars.
238-
*
239282
* @param string|int $id ID of the sidebar.
240283
* @return array|null The discovered sidebar, or null if it is not registered.
241284
*/
242285
protected function get_sidebar( $id ) {
243-
global $wp_registered_sidebars;
244-
245-
foreach ( (array) $wp_registered_sidebars as $sidebar ) {
246-
if ( $sidebar['id'] === $id ) {
247-
return $sidebar;
248-
}
249-
}
286+
return wp_get_sidebar( $id );
287+
}
250288

251-
if ( 'wp_inactive_widgets' === $id ) {
252-
return array(
253-
'id' => 'wp_inactive_widgets',
254-
'name' => __( 'Inactive widgets' ),
255-
);
289+
/**
290+
* Looks for "lost" widgets once per request.
291+
*
292+
* @since 5.9.0
293+
*
294+
* @see retrieve_widgets()
295+
*/
296+
protected function retrieve_widgets() {
297+
if ( ! $this->widgets_retrieved ) {
298+
retrieve_widgets();
299+
$this->widgets_retrieved = true;
256300
}
257-
258-
return null;
259301
}
260302

261303
/**

src/wp-includes/rest-api/endpoints/class-wp-rest-widgets-controller.php

Lines changed: 70 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,14 @@
1616
*/
1717
class WP_REST_Widgets_Controller extends WP_REST_Controller {
1818

19+
/**
20+
* Tracks whether {@see retrieve_widgets()} has been called in the current request.
21+
*
22+
* @since 5.9.0
23+
* @var bool
24+
*/
25+
protected $widgets_retrieved = false;
26+
1927
/**
2028
* Widgets controller constructor.
2129
*
@@ -97,6 +105,17 @@ public function register_routes() {
97105
* @return true|WP_Error True if the request has read access, WP_Error object otherwise.
98106
*/
99107
public function get_items_permissions_check( $request ) {
108+
$this->retrieve_widgets();
109+
if ( isset( $request['sidebar'] ) && $this->check_read_sidebar_permission( $request['sidebar'] ) ) {
110+
return true;
111+
}
112+
113+
foreach ( wp_get_sidebars_widgets() as $sidebar_id => $widget_ids ) {
114+
if ( $this->check_read_sidebar_permission( $sidebar_id ) ) {
115+
return true;
116+
}
117+
}
118+
100119
return $this->permissions_check( $request );
101120
}
102121

@@ -109,15 +128,20 @@ public function get_items_permissions_check( $request ) {
109128
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
110129
*/
111130
public function get_items( $request ) {
112-
retrieve_widgets();
131+
$this->retrieve_widgets();
113132

114-
$prepared = array();
133+
$prepared = array();
134+
$permissions_check = $this->permissions_check( $request );
115135

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

141+
if ( is_wp_error( $permissions_check ) && ! $this->check_read_sidebar_permission( $sidebar_id ) ) {
142+
continue;
143+
}
144+
121145
foreach ( $widget_ids as $widget_id ) {
122146
$response = $this->prepare_item_for_response( compact( 'sidebar_id', 'widget_id' ), $request );
123147

@@ -139,9 +163,32 @@ public function get_items( $request ) {
139163
* @return true|WP_Error True if the request has read access, WP_Error object otherwise.
140164
*/
141165
public function get_item_permissions_check( $request ) {
166+
$this->retrieve_widgets();
167+
168+
$widget_id = $request['id'];
169+
$sidebar_id = wp_find_widgets_sidebar( $widget_id );
170+
171+
if ( $sidebar_id && $this->check_read_sidebar_permission( $sidebar_id ) ) {
172+
return true;
173+
}
174+
142175
return $this->permissions_check( $request );
143176
}
144177

178+
/**
179+
* Checks if a sidebar can be read publicly.
180+
*
181+
* @since 5.9.0
182+
*
183+
* @param string $sidebar_id The sidebar id.
184+
* @return bool Whether the sidebar can be read.
185+
*/
186+
protected function check_read_sidebar_permission( $sidebar_id ) {
187+
$sidebar = wp_get_sidebar( $sidebar_id );
188+
189+
return ! empty( $sidebar['show_in_rest'] );
190+
}
191+
145192
/**
146193
* Gets an individual widget.
147194
*
@@ -151,7 +198,7 @@ public function get_item_permissions_check( $request ) {
151198
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
152199
*/
153200
public function get_item( $request ) {
154-
retrieve_widgets();
201+
$this->retrieve_widgets();
155202

156203
$widget_id = $request['id'];
157204
$sidebar_id = wp_find_widgets_sidebar( $widget_id );
@@ -247,8 +294,7 @@ public function update_item( $request ) {
247294
* See https://core.trac.wordpress.org/ticket/53657.
248295
*/
249296
wp_get_sidebars_widgets();
250-
251-
retrieve_widgets();
297+
$this->retrieve_widgets();
252298

253299
$widget_id = $request['id'];
254300
$sidebar_id = wp_find_widgets_sidebar( $widget_id );
@@ -323,8 +369,7 @@ public function delete_item( $request ) {
323369
* See https://core.trac.wordpress.org/ticket/53657.
324370
*/
325371
wp_get_sidebars_widgets();
326-
327-
retrieve_widgets();
372+
$this->retrieve_widgets();
328373

329374
$widget_id = $request['id'];
330375
$sidebar_id = wp_find_widgets_sidebar( $widget_id );
@@ -439,6 +484,20 @@ protected function permissions_check( $request ) {
439484
return true;
440485
}
441486

487+
/**
488+
* Looks for "lost" widgets once per request.
489+
*
490+
* @since 5.9.0
491+
*
492+
* @see retrieve_widgets()
493+
*/
494+
protected function retrieve_widgets() {
495+
if ( ! $this->widgets_retrieved ) {
496+
retrieve_widgets();
497+
$this->widgets_retrieved = true;
498+
}
499+
}
500+
442501
/**
443502
* Saves the widget in the request object.
444503
*
@@ -767,23 +826,23 @@ public function get_item_schema() {
767826
'instance' => array(
768827
'description' => __( 'Instance settings of the widget, if supported.' ),
769828
'type' => 'object',
770-
'context' => array( 'view', 'edit', 'embed' ),
829+
'context' => array( 'edit' ),
771830
'default' => null,
772831
'properties' => array(
773832
'encoded' => array(
774833
'description' => __( 'Base64 encoded representation of the instance settings.' ),
775834
'type' => 'string',
776-
'context' => array( 'view', 'edit', 'embed' ),
835+
'context' => array( 'edit' ),
777836
),
778837
'hash' => array(
779838
'description' => __( 'Cryptographic hash of the instance settings.' ),
780839
'type' => 'string',
781-
'context' => array( 'view', 'edit', 'embed' ),
840+
'context' => array( 'edit' ),
782841
),
783842
'raw' => array(
784843
'description' => __( 'Unencoded instance settings, if supported.' ),
785844
'type' => 'object',
786-
'context' => array( 'view', 'edit', 'embed' ),
845+
'context' => array( 'edit' ),
787846
),
788847
),
789848
),

src/wp-includes/widgets.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ function register_sidebars( $number = 1, $args = array() ) {
220220
*
221221
* @since 2.2.0
222222
* @since 5.6.0 Added the `before_sidebar` and `after_sidebar` arguments.
223+
* @since 5.9.0 Added the `show_in_rest` argument.
223224
*
224225
* @global array $wp_registered_sidebars Registered sidebars.
225226
*
@@ -250,6 +251,8 @@ function register_sidebars( $number = 1, $args = array() ) {
250251
* @type string $after_sidebar HTML content to append to the sidebar when displayed.
251252
* Outputs before the {@see 'dynamic_sidebar_after'} action.
252253
* Default empty string.
254+
* @type bool $show_in_rest Whether to show this sidebar publicly in the REST API.
255+
* Defaults to only showing the sidebar to administrator users.
253256
* }
254257
* @return string Sidebar ID added to $wp_registered_sidebars global.
255258
*/
@@ -272,6 +275,7 @@ function register_sidebar( $args = array() ) {
272275
'after_title' => "</h2>\n",
273276
'before_sidebar' => '',
274277
'after_sidebar' => '',
278+
'show_in_rest' => false,
275279
);
276280

277281
/**
@@ -1035,6 +1039,35 @@ function wp_get_sidebars_widgets( $deprecated = true ) {
10351039
return apply_filters( 'sidebars_widgets', $sidebars_widgets );
10361040
}
10371041

1042+
/**
1043+
* Retrieves the registered sidebar with the given id.
1044+
*
1045+
* @since 5.9.0
1046+
*
1047+
* @global array $wp_registered_sidebars The registered sidebars.
1048+
*
1049+
* @param string $id The sidebar id.
1050+
* @return array|null The discovered sidebar, or null if it is not registered.
1051+
*/
1052+
function wp_get_sidebar( $id ) {
1053+
global $wp_registered_sidebars;
1054+
1055+
foreach ( (array) $wp_registered_sidebars as $sidebar ) {
1056+
if ( $sidebar['id'] === $id ) {
1057+
return $sidebar;
1058+
}
1059+
}
1060+
1061+
if ( 'wp_inactive_widgets' === $id ) {
1062+
return array(
1063+
'id' => 'wp_inactive_widgets',
1064+
'name' => __( 'Inactive widgets' ),
1065+
);
1066+
}
1067+
1068+
return null;
1069+
}
1070+
10381071
/**
10391072
* Set the sidebar widget option to update sidebars.
10401073
*

0 commit comments

Comments
 (0)