PluginProbe
Gutenberg / 23.6.2
Gutenberg v23.6.2
23.9.1 23.9.0 23.8.0 23.7.2 23.7.1 23.7.0 23.6.1 23.6.2 23.6.0 23.5.3 23.5.2 23.5.1 23.5.0 23.4.0 23.3.2 23.3.1 23.3.0 23.2.0 23.2.1 23.2.2 23.1.1 23.1.0 23.0.1 12.6.0 7.4.0 All 402 releases
gutenberg / lib / experimental / dashboard-widgets / class-wp-rest-widget-modules-controller.php

class-wp-rest-widget-modules-controller.php in Gutenberg 23.6.2, at lib/experimental/dashboard-widgets/class-wp-rest-widget-modules-controller.php

330 lines 10.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Widget Modules REST API: WP_REST_Widget_Modules_Controller class.
4 *
5 * @package gutenberg
6 */
7
8 if ( ! class_exists( 'WP_REST_Widget_Modules_Controller' ) ) {
9
10 /**
11 * Internal REST controller exposing the widget type registry.
12 *
13 * Reads from `WP_Widget_Type_Registry`. Read-only collection and item
14 * endpoints. Render and encode endpoints are intentionally absent:
15 * consumers import the render module on the client and render in JS,
16 * so there is no server-rendered HTML to expose.
17 *
18 * The endpoint lives at `/wp/v2/widget-modules` because the entity
19 * `(kind: 'root', name: 'widgetType')` and the path
20 * `/wp/v2/widget-types` are already taken by the legacy widgets API.
21 */
22 class WP_REST_Widget_Modules_Controller extends WP_REST_Controller {
23
24 /**
25 * Constructor.
26 */
27 public function __construct() {
28 $this->namespace = 'wp/v2';
29 $this->rest_base = 'widget-modules';
30 }
31
32 /**
33 * Registers the widget module routes.
34 */
35 public function register_routes() {
36 register_rest_route(
37 $this->namespace,
38 '/' . $this->rest_base,
39 array(
40 array(
41 'methods' => WP_REST_Server::READABLE,
42 'callback' => array( $this, 'get_items' ),
43 'permission_callback' => array( $this, 'get_items_permissions_check' ),
44 'args' => $this->get_collection_params(),
45 ),
46 'schema' => array( $this, 'get_public_item_schema' ),
47 )
48 );
49
50 register_rest_route(
51 $this->namespace,
52 '/' . $this->rest_base . '/(?P<id>[a-z0-9-]+\/[a-z0-9-]+)',
53 array(
54 'args' => array(
55 'id' => array(
56 'description' => __( 'Widget module name including namespace.', 'gutenberg' ),
57 'type' => 'string',
58 ),
59 ),
60 array(
61 'methods' => WP_REST_Server::READABLE,
62 'callback' => array( $this, 'get_item' ),
63 'permission_callback' => array( $this, 'get_item_permissions_check' ),
64 'args' => array(),
65 ),
66 'schema' => array( $this, 'get_public_item_schema' ),
67 )
68 );
69 }
70
71 /**
72 * Checks whether a given request has permission to read widget
73 * modules.
74 *
75 * @param WP_REST_Request $request Full details about the request.
76 * @return true|WP_Error True if the request has read access, WP_Error
77 * otherwise.
78 */
79 public function get_items_permissions_check( $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
80 return $this->check_read_permission();
81 }
82
83 /**
84 * Checks whether a given request has permission to read a single
85 * widget module.
86 *
87 * @param WP_REST_Request $request Full details about the request.
88 * @return true|WP_Error True if the request has read access, WP_Error
89 * otherwise.
90 */
91 public function get_item_permissions_check( $request ) {
92 $check = $this->check_read_permission();
93 if ( is_wp_error( $check ) ) {
94 return $check;
95 }
96
97 $widget_type = WP_Widget_Type_Registry::get_instance()->get_registered( $request['id'] );
98 if ( null === $widget_type ) {
99 return new WP_Error(
100 'rest_widget_module_invalid',
101 __( 'Invalid widget module name.', 'gutenberg' ),
102 array( 'status' => 404 )
103 );
104 }
105
106 return true;
107 }
108
109 /**
110 * Verifies the user has the basic read capability.
111 *
112 * Widget modules are not sensitive data; they describe what is
113 * available to render. Gating at the same level as the dashboard
114 * page menu (which requires `read`) keeps access consistent.
115 *
116 * @return true|WP_Error True if the request is allowed, WP_Error
117 * otherwise.
118 */
119 protected function check_read_permission() {
120 if ( ! current_user_can( 'read' ) ) {
121 return new WP_Error(
122 'rest_cannot_view_widget_modules',
123 __( 'Sorry, you are not allowed to view widget modules.', 'gutenberg' ),
124 array( 'status' => rest_authorization_required_code() )
125 );
126 }
127
128 return true;
129 }
130
131 /**
132 * Retrieves the list of all registered widget modules.
133 *
134 * @param WP_REST_Request $request Full details about the request.
135 * @return WP_REST_Response Response object on success.
136 */
137 public function get_items( $request ) {
138 $registered = WP_Widget_Type_Registry::get_instance()->get_all_registered();
139 $data = array();
140
141 foreach ( $registered as $widget_type ) {
142 $item = $this->prepare_item_for_response( $widget_type, $request );
143 $data[] = $this->prepare_response_for_collection( $item );
144 }
145
146 return rest_ensure_response( $data );
147 }
148
149 /**
150 * Retrieves a single widget module from the collection.
151 *
152 * @param WP_REST_Request $request Full details about the request.
153 * @return WP_REST_Response|WP_Error Response object on success, or
154 * WP_Error on failure.
155 */
156 public function get_item( $request ) {
157 $widget_type = WP_Widget_Type_Registry::get_instance()->get_registered( $request['id'] );
158 if ( null === $widget_type ) {
159 return new WP_Error(
160 'rest_widget_module_invalid',
161 __( 'Invalid widget module name.', 'gutenberg' ),
162 array( 'status' => 404 )
163 );
164 }
165
166 return rest_ensure_response( $this->prepare_item_for_response( $widget_type, $request ) );
167 }
168
169 /**
170 * Prepares a widget type object for serialization.
171 *
172 * @param WP_Widget_Type $item Widget type instance.
173 * @param WP_REST_Request $request Full details about the request.
174 * @return WP_REST_Response Response object containing the serialized
175 * widget module data.
176 */
177 public function prepare_item_for_response( $item, $request ) {
178 $widget_type = $item;
179 $fields = $this->get_fields_for_response( $request );
180 $data = array();
181
182 if ( rest_is_field_included( 'name', $fields ) ) {
183 $data['name'] = $widget_type->name;
184 }
185
186 if ( rest_is_field_included( 'render_module', $fields ) ) {
187 $data['render_module'] = $widget_type->render_module;
188 }
189
190 if ( rest_is_field_included( 'widget_module', $fields ) ) {
191 $data['widget_module'] = $widget_type->widget_module;
192 }
193
194 if ( rest_is_field_included( 'presentation', $fields ) ) {
195 $data['presentation'] = $widget_type->presentation;
196 }
197
198 if ( rest_is_field_included( 'category', $fields ) ) {
199 $data['category'] = $widget_type->category;
200 }
201
202 if ( rest_is_field_included( 'title', $fields ) ) {
203 $data['title'] = $widget_type->title;
204 }
205
206 if ( rest_is_field_included( 'description', $fields ) ) {
207 $data['description'] = $widget_type->description;
208 }
209
210 if ( rest_is_field_included( 'help', $fields ) ) {
211 $data['help'] = $widget_type->help;
212 }
213
214 if ( rest_is_field_included( 'keywords', $fields ) ) {
215 $data['keywords'] = $widget_type->keywords;
216 }
217
218 $context = ! empty( $request['context'] ) ? $request['context'] : 'view';
219 $data = $this->add_additional_fields_to_object( $data, $request );
220 $data = $this->filter_response_by_context( $data, $context );
221
222 return rest_ensure_response( $data );
223 }
224
225 /**
226 * Retrieves the widget module schema, conforming to JSON Schema.
227 *
228 * @return array Item schema data.
229 */
230 public function get_item_schema() {
231 if ( $this->schema ) {
232 return $this->add_additional_fields_schema( $this->schema );
233 }
234
235 $schema = array(
236 '$schema' => 'http://json-schema.org/draft-04/schema#',
237 'title' => 'widget-module',
238 'type' => 'object',
239 'properties' => array(
240 'name' => array(
241 'description' => __( 'Widget module name including namespace.', 'gutenberg' ),
242 'type' => 'string',
243 'context' => array( 'view', 'edit', 'embed' ),
244 'readonly' => true,
245 ),
246
247 'render_module' => array(
248 'description' => __( 'Script-module handle for the widget render entry point.', 'gutenberg' ),
249 'type' => array( 'string', 'null' ),
250 'context' => array( 'view', 'edit', 'embed' ),
251 'readonly' => true,
252 ),
253
254 'widget_module' => array(
255 'description' => __( 'Script-module handle for the widget metadata entry point.', 'gutenberg' ),
256 'type' => array( 'string', 'null' ),
257 'context' => array( 'view', 'edit', 'embed' ),
258 'readonly' => true,
259 ),
260
261 'presentation' => array(
262 'description' => __( 'Authoring intent about how the widget wants to render.', 'gutenberg' ),
263 'type' => array( 'string', 'null' ),
264 'enum' => array_merge( WP_Widget_Type::PRESENTATION_VALUES, array( null ) ),
265 'context' => array( 'view', 'edit', 'embed' ),
266 'readonly' => true,
267 ),
268
269 'category' => array(
270 'description' => __( 'Widget types are grouped into categories to help users browse and discover them.', 'gutenberg' ),
271 'type' => array( 'string', 'null' ),
272 'context' => array( 'view', 'edit', 'embed' ),
273 'readonly' => true,
274 ),
275
276 'title' => array(
277 'description' => __( 'Human-readable title that names the widget type. Translatable.', 'gutenberg' ),
278 'type' => array( 'string', 'null' ),
279 'context' => array( 'view', 'edit', 'embed' ),
280 'readonly' => true,
281 ),
282
283 'description' => array(
284 'description' => __( 'Human-readable description of what the widget type does. Translatable.', 'gutenberg' ),
285 'type' => array( 'string', 'null' ),
286 'context' => array( 'view', 'edit', 'embed' ),
287 'readonly' => true,
288 ),
289
290 'help' => array(
291 'description' => __( 'Contextual help note for the widget type: content plus optional links. Translatable.', 'gutenberg' ),
292 'type' => array( 'object', 'null' ),
293 'properties' => array(
294 'content' => array(
295 'description' => __( 'Help content; may carry minimal inline emphasis.', 'gutenberg' ),
296 'type' => 'string',
297 ),
298 'links' => array(
299 'description' => __( 'Optional links contextual to the help content.', 'gutenberg' ),
300 'type' => 'array',
301 'items' => array(
302 'type' => 'object',
303 'properties' => array(
304 'label' => array( 'type' => 'string' ),
305 'href' => array( 'type' => 'string' ),
306 ),
307 ),
308 ),
309 ),
310 'context' => array( 'view', 'edit', 'embed' ),
311 'readonly' => true,
312 ),
313
314 'keywords' => array(
315 'description' => __( 'Alternative terms used to match the widget type when searching, e.g. "calendar" for an events widget. Translatable.', 'gutenberg' ),
316 'type' => array( 'array', 'null' ),
317 'items' => array( 'type' => 'string' ),
318 'context' => array( 'view', 'edit', 'embed' ),
319 'readonly' => true,
320 ),
321 ),
322 );
323
324 $this->schema = $schema;
325
326 return $this->add_additional_fields_schema( $this->schema );
327 }
328 }
329 }
330