PluginProbe
Code Snippets / 3.5.0
Code Snippets v3.5.0
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.5.0, at php/rest-api/class-snippets-rest-controller.php

544 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 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 return $result ?
273 $this->prepare_item_for_response( $result, $request ) :
274 new WP_Error(
275 'rest_cannot_update',
276 __( 'The snippet could not be updated.', 'code-snippets' ),
277 [ 'status' => 500 ]
278 );
279 }
280
281 /**
282 * Delete one item from the collection
283 *
284 * @param WP_REST_Request $request Full data about the request.
285 *
286 * @return WP_Error|WP_REST_Response
287 */
288 public function delete_item( $request ) {
289 $item = $this->prepare_item_for_database( $request );
290 $result = delete_snippet( $item->id, $item->network );
291
292 return $result ?
293 new WP_REST_Response( null, 204 ) :
294 new WP_Error(
295 'rest_cannot_delete',
296 __( 'The snippet could not be deleted.', 'code-snippets' ),
297 [ 'status' => 500 ]
298 );
299 }
300
301 /**
302 * Activate one item in the collection.
303 *
304 * @param WP_REST_Request $request Full data about the request.
305 *
306 * @return WP_Error|WP_REST_Response
307 */
308 public function activate_item( WP_REST_Request $request ) {
309 $item = $this->prepare_item_for_database( $request );
310 $result = activate_snippet( $item->id, $item->network );
311
312 return $result instanceof Snippet ?
313 rest_ensure_response( $result ) :
314 new WP_Error(
315 'rest_cannot_activate',
316 $result,
317 [ 'status' => 500 ]
318 );
319 }
320
321 /**
322 * Deactivate one item in the collection.
323 *
324 * @param WP_REST_Request $request Full data about the request.
325 *
326 * @return WP_Error|WP_REST_Response
327 */
328 public function deactivate_item( WP_REST_Request $request ) {
329 $item = $this->prepare_item_for_database( $request );
330 $result = deactivate_snippet( $item->id, $item->network );
331
332 return $result instanceof Snippet ?
333 rest_ensure_response( $result ) :
334 new WP_Error(
335 'rest_cannot_activate',
336 __( 'The snippet could not be deactivated.', 'code-snippets' ),
337 [ 'status' => 500 ]
338 );
339 }
340
341 /**
342 * Prepare an instance of the Export class from a request.
343 *
344 * @param WP_REST_Request $request Full data about the request.
345 *
346 * @return Export
347 */
348 protected function build_export( WP_REST_Request $request ): Export {
349 $item = $this->prepare_item_for_database( $request );
350
351 $ids = [ $item->id ];
352 $table_name = code_snippets()->db->get_table_name( $item->network );
353
354 return new Export( $ids, $table_name );
355 }
356
357 /**
358 * Retrieve one item in the collection in JSON export format.
359 *
360 * @param WP_REST_Request $request Full data about the request.
361 *
362 * @return WP_Error|WP_REST_Response
363 */
364 public function export_item( WP_REST_Request $request ) {
365 $export = $this->build_export( $request );
366 $result = $export->create_export_object();
367 return rest_ensure_response( $result );
368 }
369
370 /**
371 * Retrieve one item in the collection in the code export format.
372 *
373 * @param WP_REST_Request $request Full data about the request.
374 *
375 * @return WP_Error|WP_REST_Response
376 */
377 public function export_item_code( WP_REST_Request $request ) {
378 $export = $this->build_export( $request );
379 $result = $export->export_snippets_code();
380
381 return rest_ensure_response( $result );
382 }
383
384 /**
385 * Prepares one item for create or update operation.
386 *
387 * @param WP_REST_Request $request Request object.
388 * @param Snippet|null $item Existing item to augment.
389 *
390 * @return Snippet The prepared item.
391 */
392 protected function prepare_item_for_database( $request, Snippet $item = null ) {
393 if ( ! $item instanceof Snippet ) {
394 $item = new Snippet();
395 }
396
397 foreach ( $item->get_allowed_fields() as $field ) {
398 if ( isset( $request[ $field ] ) ) {
399 $item->set_field( $field, $request[ $field ] );
400 }
401 }
402
403 if ( ! empty( $request['encoded'] ) ) {
404 $item->code = html_entity_decode( $item->code );
405 }
406
407 return $item;
408 }
409
410 /**
411 * Prepare the item for the REST response.
412 *
413 * @param Snippet $item Snippet object.
414 * @param WP_REST_Request $request Request object.
415 *
416 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
417 */
418 public function prepare_item_for_response( $item, $request ) {
419 return rest_ensure_response( $item->get_fields() );
420 }
421
422 /**
423 * Check if a given request has access to get items.
424 *
425 * @param WP_REST_Request $request Full data about the request.
426 *
427 * @return boolean
428 */
429 public function get_items_permissions_check( $request ): bool {
430 return code_snippets()->current_user_can();
431 }
432
433 /**
434 * Check if a given request has access to get a specific item.
435 *
436 * @param WP_REST_Request $request Full data about the request.
437 *
438 * @return boolean
439 */
440 public function get_item_permissions_check( $request ): bool {
441 return $this->get_items_permissions_check( $request );
442 }
443
444 /**
445 * Check if a given request has access to create items.
446 *
447 * @param WP_REST_Request $request Full data about the request.
448 *
449 * @return boolean
450 */
451 public function create_item_permissions_check( $request ): bool {
452 return code_snippets()->current_user_can();
453 }
454
455 /**
456 * Check if a given request has access to update a specific item.
457 *
458 * @param WP_REST_Request $request Full data about the request.
459 *
460 * @return boolean
461 */
462 public function update_item_permissions_check( $request ): bool {
463 return $this->create_item_permissions_check( $request );
464 }
465
466 /**
467 * Check if a given request has access to delete a specific item.
468 *
469 * @param WP_REST_Request $request Full data about the request.
470 *
471 * @return boolean
472 */
473 public function delete_item_permissions_check( $request ): bool {
474 return $this->create_item_permissions_check( $request );
475 }
476
477 /**
478 * Get our sample schema for a post.
479 *
480 * @return array<string, mixed> The sample schema for a post
481 */
482 public function get_item_schema(): array {
483 if ( $this->schema ) {
484 return $this->schema;
485 }
486
487 $this->schema = [
488 '$schema' => 'http://json-schema.org/draft-04/schema#',
489 'title' => 'snippet',
490 'type' => 'object',
491 'properties' => [
492 'id' => [
493 'description' => esc_html__( 'Unique identifier for the snippet.', 'code-snippets' ),
494 'type' => 'integer',
495 'readonly' => true,
496 ],
497 'name' => [
498 'description' => esc_html__( 'Descriptive title for the snippet.', 'code-snippets' ),
499 'type' => 'string',
500 ],
501 'desc' => [
502 'description' => esc_html__( 'Descriptive text associated with snippet.', 'code-snippets' ),
503 'type' => 'string',
504 ],
505 'code' => [
506 'description' => esc_html__( 'Executable snippet code.', 'code-snippets' ),
507 'type' => 'string',
508 ],
509 'tags' => [
510 'description' => esc_html__( 'List of tag categories the snippet belongs to.', 'code-snippets' ),
511 'type' => 'array',
512 ],
513 'scope' => [
514 'description' => esc_html__( 'Context in which the snippet is executable.', 'code-snippets' ),
515 'type' => 'string',
516 ],
517 'active' => [
518 'description' => esc_html__( 'Snippet activation status.', 'code-snippets' ),
519 'type' => 'boolean',
520 ],
521 'priority' => [
522 'description' => esc_html__( 'Relative priority in which the snippet is executed.', 'code-snippets' ),
523 'type' => 'integer',
524 ],
525 'network' => [
526 'description' => esc_html__( 'Whether the snippet is network-wide instead of site-wide.', 'code-snippets' ),
527 'type' => 'boolean',
528 'default' => null,
529 ],
530 'shared_network' => [
531 'description' => esc_html__( 'If a network snippet, whether can be activated on discrete sites instead of network-wide.', 'code-snippets' ),
532 'type' => 'boolean',
533 ],
534 'modified' => [
535 'description' => esc_html__( 'Date and time when the snippet was last modified, in ISO format.', 'code-snippets' ),
536 'type' => 'string',
537 ],
538 ],
539 ];
540
541 return $this->schema;
542 }
543 }
544