PluginProbe
Code Snippets / 3.6.6
Code Snippets v3.6.6
3.10.2 3.10.1 3.10.0 3.10.0-beta.2 3.10.0-beta.1 4.0.0-beta.1 3.9.6 trunk 2.10.0 2.10.1 2.12.0 2.12.1 2.13.0 2.13.1 2.13.2 2.13.3 2.14.0 2.14.1 2.14.2 2.14.3 2.14.4 2.14.5 2.14.6 3.0.0 3.0.1 All 64 releases
code-snippets / php / rest-api / class-snippets-rest-controller.php

class-snippets-rest-controller.php in Code Snippets 3.6.6, at php/rest-api/class-snippets-rest-controller.php

546 lines 15.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Code_Snippets\REST_API;
4
5 use Code_Snippets\Export;
6 use Code_Snippets\Snippet;
7 use WP_Error;
8 use WP_REST_Controller;
9 use WP_REST_Request;
10 use WP_REST_Response;
11 use WP_REST_Server;
12 use function Code_Snippets\activate_snippet;
13 use function Code_Snippets\code_snippets;
14 use function Code_Snippets\deactivate_snippet;
15 use function Code_Snippets\delete_snippet;
16 use function Code_Snippets\get_snippet;
17 use function Code_Snippets\get_snippets;
18 use function Code_Snippets\save_snippet;
19 use const Code_Snippets\REST_API_NAMESPACE;
20
21 /**
22 * Allows fetching snippet data through the WordPress REST API.
23 *
24 * @since 3.4.0
25 * @package Code_Snippets
26 */
27 final class Snippets_REST_Controller extends WP_REST_Controller {
28
29 /**
30 * Current API version.
31 */
32 const VERSION = 1;
33
34 /**
35 * The base of this controller's route.
36 */
37 const BASE_ROUTE = 'snippets';
38
39 /**
40 * The namespace of this controller's route.
41 *
42 * @var string
43 */
44 protected $namespace = REST_API_NAMESPACE . self::VERSION;
45
46 /**
47 * The base of this controller's route.
48 *
49 * @var string
50 */
51 protected $rest_base = self::BASE_ROUTE;
52
53 /**
54 * Retrieve this controller's REST API base path, including namespace.
55 *
56 * @return string
57 */
58 public static function get_base_route(): string {
59 return REST_API_NAMESPACE . self::VERSION . '/' . self::BASE_ROUTE;
60 }
61
62 /**
63 * Retrieve the full base route including the REST API prefix.
64 *
65 * @return string
66 */
67 public static function get_prefixed_base_route(): string {
68 return '/' . rtrim( rest_get_url_prefix(), '/\\' ) . '/' . self::get_base_route();
69 }
70
71 /**
72 * Register REST routes.
73 */
74 public function register_routes() {
75 $route = '/' . $this->rest_base;
76 $id_route = $route . '/(?P<id>[\d]+)';
77
78 $network_args = array_intersect_key(
79 $this->get_endpoint_args_for_item_schema(),
80 [ 'network' ]
81 );
82
83 register_rest_route(
84 $this->namespace,
85 $route,
86 [
87 [
88 'methods' => WP_REST_Server::READABLE,
89 'callback' => [ $this, 'get_items' ],
90 'permission_callback' => [ $this, 'get_items_permissions_check' ],
91 'args' => $network_args,
92 ],
93 [
94 'methods' => WP_REST_Server::CREATABLE,
95 'callback' => [ $this, 'create_item' ],
96 'permission_callback' => [ $this, 'create_item_permissions_check' ],
97 'args' => $this->get_endpoint_args_for_item_schema( true ),
98 ],
99 'schema' => [ $this, 'get_item_schema' ],
100 ]
101 );
102
103 register_rest_route(
104 $this->namespace,
105 $id_route,
106 [
107 [
108 'methods' => WP_REST_Server::READABLE,
109 'callback' => [ $this, 'get_item' ],
110 'permission_callback' => [ $this, 'get_item_permissions_check' ],
111 'args' => $network_args,
112 ],
113 [
114 'methods' => WP_REST_Server::EDITABLE,
115 'callback' => [ $this, 'update_item' ],
116 'permission_callback' => [ $this, 'update_item_permissions_check' ],
117 'args' => $this->get_endpoint_args_for_item_schema( false ),
118 ],
119 [
120 'methods' => WP_REST_Server::DELETABLE,
121 'callback' => [ $this, 'delete_item' ],
122 'permission_callback' => [ $this, 'delete_item_permissions_check' ],
123 'args' => $network_args,
124 ],
125 'schema' => [ $this, 'get_item_schema' ],
126 ]
127 );
128
129 register_rest_route(
130 $this->namespace,
131 $route . '/schema',
132 [
133 'methods' => WP_REST_Server::READABLE,
134 'callback' => [ $this, 'get_public_item_schema' ],
135 'permission_callback' => '__return_true',
136 ]
137 );
138
139 register_rest_route(
140 $this->namespace,
141 $id_route . '/activate',
142 [
143 'methods' => WP_REST_Server::EDITABLE,
144 'callback' => [ $this, 'activate_item' ],
145 'permission_callback' => [ $this, 'update_item_permissions_check' ],
146 'schema' => [ $this, 'get_item_schema' ],
147 'args' => $network_args,
148 ]
149 );
150
151 register_rest_route(
152 $this->namespace,
153 $id_route . '/deactivate',
154 [
155 'methods' => WP_REST_Server::EDITABLE,
156 'callback' => [ $this, 'deactivate_item' ],
157 'permission_callback' => [ $this, 'update_item_permissions_check' ],
158 'schema' => [ $this, 'get_item_schema' ],
159 'args' => $network_args,
160 ]
161 );
162
163 register_rest_route(
164 $this->namespace,
165 $id_route . '/export',
166 [
167 'methods' => WP_REST_Server::READABLE,
168 'callback' => [ $this, 'export_item' ],
169 'permission_callback' => [ $this, 'get_item_permissions_check' ],
170 'schema' => [ $this, 'get_item_schema' ],
171 'args' => $network_args,
172 ]
173 );
174
175 register_rest_route(
176 $this->namespace,
177 $id_route . '/export-code',
178 [
179 'methods' => WP_REST_Server::READABLE,
180 'callback' => [ $this, 'export_item_code' ],
181 'permission_callback' => [ $this, 'get_item_permissions_check' ],
182 'schema' => [ $this, 'get_item_schema' ],
183 'args' => $network_args,
184 ]
185 );
186 }
187
188 /**
189 * Retrieves a collection of snippets.
190 *
191 * @param WP_REST_Request $request Full details about the request.
192 *
193 * @return WP_REST_Response Response object on success.
194 */
195 public function get_items( $request ): WP_REST_Response {
196 $snippets = get_snippets();
197 $snippets_data = [];
198
199 foreach ( $snippets as $snippet ) {
200 $snippet_data = $this->prepare_item_for_response( $snippet, $request );
201 $snippets_data[] = $this->prepare_response_for_collection( $snippet_data );
202 }
203
204 return rest_ensure_response( $snippets_data );
205 }
206
207 /**
208 * Retrieves one item from the collection.
209 *
210 * @param WP_REST_Request $request Full details about the request.
211 *
212 * @return WP_REST_Response|WP_Error Response object on success.
213 */
214 public function get_item( $request ) {
215 $snippet_id = $request->get_param( 'id' );
216 $item = get_snippet( $snippet_id, $request->get_param( 'network' ) );
217
218 if ( ! $item->id && 0 !== $snippet_id && '0' !== $snippet_id ) {
219 return new WP_Error(
220 'rest_cannot_get',
221 __( 'The snippet could not be found.', 'code-snippets' ),
222 [ 'status' => 500 ]
223 );
224 }
225
226 $data = $this->prepare_item_for_response( $item, $request );
227 return rest_ensure_response( $data );
228 }
229
230 /**
231 * Create one item from the collection
232 *
233 * @param WP_REST_Request $request Full data about the request.
234 *
235 * @return WP_REST_Response|WP_Error
236 */
237 public function create_item( $request ) {
238 $snippet = $this->prepare_item_for_database( $request );
239 $result = save_snippet( $snippet );
240
241 return $result ?
242 $this->prepare_item_for_response( $result, $request ) :
243 new WP_Error(
244 'rest_cannot_create',
245 __( 'The snippet could not be created.', 'code-snippets' ),
246 [ 'status' => 500 ]
247 );
248 }
249
250 /**
251 * Update one item from the collection
252 *
253 * @param WP_REST_Request $request Full data about the request.
254 *
255 * @return WP_Error|WP_REST_Response
256 */
257 public function update_item( $request ) {
258 $snippet_id = absint( $request->get_param( 'id' ) );
259 $snippet = $snippet_id ? get_snippet( $snippet_id, $request->get_param( 'network' ) ) : null;
260
261 if ( ! $snippet_id || ! $snippet || ! $snippet->id ) {
262 return new WP_Error(
263 'rest_cannot_update',
264 __( 'Cannot update a snippet without a valid ID.', 'code-snippets' ),
265 [ 'status' => 400 ]
266 );
267 }
268
269 $item = $this->prepare_item_for_database( $request, $snippet );
270 $result = save_snippet( $item );
271
272 if ( $result ) {
273 $request->set_param( 'id', $result->id );
274 return $this->get_item( $request );
275 }
276
277 return new WP_Error(
278 'rest_cannot_update',
279 __( 'The snippet could not be updated.', 'code-snippets' ),
280 [ 'status' => 500 ]
281 );
282 }
283
284 /**
285 * Delete one item from the collection
286 *
287 * @param WP_REST_Request $request Full data about the request.
288 *
289 * @return WP_Error|WP_REST_Response
290 */
291 public function delete_item( $request ) {
292 $item = $this->prepare_item_for_database( $request );
293 $result = delete_snippet( $item->id, $item->network );
294
295 return $result ?
296 new WP_REST_Response( null, 204 ) :
297 new WP_Error(
298 'rest_cannot_delete',
299 __( 'The snippet could not be deleted.', 'code-snippets' ),
300 [ 'status' => 500 ]
301 );
302 }
303
304 /**
305 * Activate one item in the collection.
306 *
307 * @param WP_REST_Request $request Full data about the request.
308 *
309 * @return WP_Error|WP_REST_Response
310 */
311 public function activate_item( WP_REST_Request $request ) {
312 $item = $this->prepare_item_for_database( $request );
313 $result = activate_snippet( $item->id, $item->network );
314
315 return $result instanceof Snippet ?
316 rest_ensure_response( $result ) :
317 new WP_Error(
318 'rest_cannot_activate',
319 $result,
320 [ 'status' => 500 ]
321 );
322 }
323
324 /**
325 * Deactivate one item in the collection.
326 *
327 * @param WP_REST_Request $request Full data about the request.
328 *
329 * @return WP_Error|WP_REST_Response
330 */
331 public function deactivate_item( WP_REST_Request $request ) {
332 $item = $this->prepare_item_for_database( $request );
333 $result = deactivate_snippet( $item->id, $item->network );
334
335 return $result instanceof Snippet ?
336 rest_ensure_response( $result ) :
337 new WP_Error(
338 'rest_cannot_activate',
339 __( 'The snippet could not be deactivated.', 'code-snippets' ),
340 [ 'status' => 500 ]
341 );
342 }
343
344 /**
345 * Prepare an instance of the Export class from a request.
346 *
347 * @param WP_REST_Request $request Full data about the request.
348 *
349 * @return Export
350 */
351 protected function build_export( WP_REST_Request $request ): Export {
352 $item = $this->prepare_item_for_database( $request );
353 return new Export( [ $item->id ], $item->network );
354 }
355
356 /**
357 * Retrieve one item in the collection in JSON export format.
358 *
359 * @param WP_REST_Request $request Full data about the request.
360 *
361 * @return WP_Error|WP_REST_Response
362 */
363 public function export_item( WP_REST_Request $request ) {
364 $export = $this->build_export( $request );
365 $result = $export->create_export_object();
366 return rest_ensure_response( $result );
367 }
368
369 /**
370 * Retrieve one item in the collection in the code export format.
371 *
372 * @param WP_REST_Request $request Full data about the request.
373 *
374 * @return WP_Error|WP_REST_Response
375 */
376 public function export_item_code( WP_REST_Request $request ) {
377 $export = $this->build_export( $request );
378 $result = $export->export_snippets_code();
379
380 return rest_ensure_response( $result );
381 }
382
383 /**
384 * Prepares one item for create or update operation.
385 *
386 * @param WP_REST_Request $request Request object.
387 * @param Snippet|null $item Existing item to augment.
388 *
389 * @return Snippet The prepared item.
390 */
391 protected function prepare_item_for_database( $request, ?Snippet $item = null ): ?Snippet {
392 if ( ! $item instanceof Snippet ) {
393 $item = new Snippet();
394 }
395
396 foreach ( $item->get_allowed_fields() as $field ) {
397 if ( isset( $request[ $field ] ) ) {
398 $item->set_field( $field, $request[ $field ] );
399 }
400 }
401
402 return $item;
403 }
404
405 /**
406 * Prepare the item for the REST response.
407 *
408 * @param Snippet $item Snippet object.
409 * @param WP_REST_Request $request Request object.
410 *
411 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
412 */
413 public function prepare_item_for_response( $item, $request ) {
414 $schema = $this->get_item_schema();
415 $response = [];
416
417 foreach ( array_keys( $schema['properties'] ) as $property ) {
418 $response[ $property ] = $item->$property;
419 }
420
421 return rest_ensure_response( $response );
422 }
423
424 /**
425 * Check if a given request has access to get items.
426 *
427 * @param WP_REST_Request $request Full data about the request.
428 *
429 * @return boolean
430 */
431 public function get_items_permissions_check( $request ): bool {
432 return code_snippets()->current_user_can();
433 }
434
435 /**
436 * Check if a given request has access to get a specific item.
437 *
438 * @param WP_REST_Request $request Full data about the request.
439 *
440 * @return boolean
441 */
442 public function get_item_permissions_check( $request ): bool {
443 return $this->get_items_permissions_check( $request );
444 }
445
446 /**
447 * Check if a given request has access to create items.
448 *
449 * @param WP_REST_Request $request Full data about the request.
450 *
451 * @return boolean
452 */
453 public function create_item_permissions_check( $request ): bool {
454 return code_snippets()->current_user_can();
455 }
456
457 /**
458 * Check if a given request has access to update a specific item.
459 *
460 * @param WP_REST_Request $request Full data about the request.
461 *
462 * @return boolean
463 */
464 public function update_item_permissions_check( $request ): bool {
465 return $this->create_item_permissions_check( $request );
466 }
467
468 /**
469 * Check if a given request has access to delete a specific item.
470 *
471 * @param WP_REST_Request $request Full data about the request.
472 *
473 * @return boolean
474 */
475 public function delete_item_permissions_check( $request ): bool {
476 return $this->create_item_permissions_check( $request );
477 }
478
479 /**
480 * Get our sample schema for a post.
481 *
482 * @return array<string, mixed> The sample schema for a post
483 */
484 public function get_item_schema(): array {
485 if ( $this->schema ) {
486 return $this->schema;
487 }
488
489 $this->schema = [
490 '$schema' => 'http://json-schema.org/draft-04/schema#',
491 'title' => 'snippet',
492 'type' => 'object',
493 'properties' => [
494 'id' => [
495 'description' => esc_html__( 'Unique identifier for the snippet.', 'code-snippets' ),
496 'type' => 'integer',
497 'readonly' => true,
498 ],
499 'name' => [
500 'description' => esc_html__( 'Descriptive title for the snippet.', 'code-snippets' ),
501 'type' => 'string',
502 ],
503 'desc' => [
504 'description' => esc_html__( 'Descriptive text associated with snippet.', 'code-snippets' ),
505 'type' => 'string',
506 ],
507 'code' => [
508 'description' => esc_html__( 'Executable snippet code.', 'code-snippets' ),
509 'type' => 'string',
510 ],
511 'tags' => [
512 'description' => esc_html__( 'List of tag categories the snippet belongs to.', 'code-snippets' ),
513 'type' => 'array',
514 ],
515 'scope' => [
516 'description' => esc_html__( 'Context in which the snippet is executable.', 'code-snippets' ),
517 'type' => 'string',
518 ],
519 'active' => [
520 'description' => esc_html__( 'Snippet activation status.', 'code-snippets' ),
521 'type' => 'boolean',
522 ],
523 'priority' => [
524 'description' => esc_html__( 'Relative priority in which the snippet is executed.', 'code-snippets' ),
525 'type' => 'integer',
526 ],
527 'network' => [
528 'description' => esc_html__( 'Whether the snippet is network-wide instead of site-wide.', 'code-snippets' ),
529 'type' => 'boolean',
530 'default' => null,
531 ],
532 'shared_network' => [
533 'description' => esc_html__( 'If a network snippet, whether can be activated on discrete sites instead of network-wide.', 'code-snippets' ),
534 'type' => 'boolean',
535 ],
536 'modified' => [
537 'description' => esc_html__( 'Date and time when the snippet was last modified, in ISO format.', 'code-snippets' ),
538 'type' => 'string',
539 ],
540 ],
541 ];
542
543 return $this->schema;
544 }
545 }
546