Skip to content

Commit 3c4d1d8

Browse files
committed
REST API: Make the templates controller follow core REST patterns.
The templates controller now respects the `_fields` parameter and filters the response accordingly. The schema has been updated to include all the fields returned. The `content.block_version` field has been added. The controller now returns WP_Error objects for improved error handling. Add new unit tests. Props TimothyBlynJacobs, hellofromtonya, zieladam. Fixes #54422. git-svn-id: https://develop.svn.wordpress.org/trunk@52186 602fd350-edb4-49c9-b593-d223f7449a82
1 parent 076d207 commit 3c4d1d8

File tree

4 files changed

+937
-251
lines changed

4 files changed

+937
-251
lines changed

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

Lines changed: 187 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -70,15 +70,18 @@ public function register_routes() {
7070
$this->namespace,
7171
'/' . $this->rest_base . '/(?P<id>[\/\w-]+)',
7272
array(
73+
'args' => array(
74+
'id' => array(
75+
'description' => __( 'The id of a template' ),
76+
'type' => 'string',
77+
),
78+
),
7379
array(
7480
'methods' => WP_REST_Server::READABLE,
7581
'callback' => array( $this, 'get_item' ),
7682
'permission_callback' => array( $this, 'get_item_permissions_check' ),
7783
'args' => array(
78-
'id' => array(
79-
'description' => __( 'The id of a template' ),
80-
'type' => 'string',
81-
),
84+
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
8285
),
8386
),
8487
array(
@@ -231,7 +234,12 @@ public function update_item( $request ) {
231234

232235
if ( isset( $request['source'] ) && 'theme' === $request['source'] ) {
233236
wp_delete_post( $template->wp_id, true );
234-
return $this->prepare_item_for_response( get_block_file_template( $request['id'], $this->post_type ), $request );
237+
$request->set_param( 'context', 'edit' );
238+
239+
$template = get_block_template( $request['id'], $this->post_type );
240+
$response = $this->prepare_item_for_response( $template, $request );
241+
242+
return rest_ensure_response( $response );
235243
}
236244

237245
$changes = $this->prepare_item_for_database( $request );
@@ -241,7 +249,13 @@ public function update_item( $request ) {
241249
} else {
242250
$result = wp_insert_post( wp_slash( (array) $changes ), true );
243251
}
252+
244253
if ( is_wp_error( $result ) ) {
254+
if ( 'db_update_error' === $result->get_error_code() ) {
255+
$result->add_data( array( 'status' => 500 ) );
256+
} else {
257+
$result->add_data( array( 'status' => 400 ) );
258+
}
245259
return $result;
246260
}
247261

@@ -251,10 +265,11 @@ public function update_item( $request ) {
251265
return $fields_update;
252266
}
253267

254-
return $this->prepare_item_for_response(
255-
get_block_template( $request['id'], $this->post_type ),
256-
$request
257-
);
268+
$request->set_param( 'context', 'edit' );
269+
270+
$response = $this->prepare_item_for_response( $template, $request );
271+
272+
return rest_ensure_response( $response );
258273
}
259274

260275
/**
@@ -278,15 +293,21 @@ public function create_item_permissions_check( $request ) {
278293
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
279294
*/
280295
public function create_item( $request ) {
281-
$changes = $this->prepare_item_for_database( $request );
282-
$changes->post_name = $request['slug'];
283-
$result = wp_insert_post( wp_slash( (array) $changes ), true );
284-
if ( is_wp_error( $result ) ) {
285-
return $result;
296+
$prepared_post = $this->prepare_item_for_database( $request );
297+
$prepared_post->post_name = $request['slug'];
298+
$post_id = wp_insert_post( wp_slash( (array) $prepared_post ), true );
299+
if ( is_wp_error( $post_id ) ) {
300+
if ( 'db_insert_error' === $post_id->get_error_code() ) {
301+
$post_id->add_data( array( 'status' => 500 ) );
302+
} else {
303+
$post_id->add_data( array( 'status' => 400 ) );
304+
}
305+
306+
return $post_id;
286307
}
287-
$posts = get_block_templates( array( 'wp_id' => $result ), $this->post_type );
308+
$posts = get_block_templates( array( 'wp_id' => $post_id ), $this->post_type );
288309
if ( ! count( $posts ) ) {
289-
return new WP_Error( 'rest_template_insert_error', __( 'No templates exist with that id.' ) );
310+
return new WP_Error( 'rest_template_insert_error', __( 'No templates exist with that id.' ), array( 'status' => 400 ) );
290311
}
291312
$id = $posts[0]->id;
292313
$template = get_block_template( $id, $this->post_type );
@@ -295,10 +316,13 @@ public function create_item( $request ) {
295316
return $fields_update;
296317
}
297318

298-
return $this->prepare_item_for_response(
299-
get_block_template( $id, $this->post_type ),
300-
$request
301-
);
319+
$response = $this->prepare_item_for_response( $template, $request );
320+
$response = rest_ensure_response( $response );
321+
322+
$response->set_status( 201 );
323+
$response->header( 'Location', rest_url( sprintf( '%s/%s/%s', $this->namespace, $this->rest_base, $template->id ) ) );
324+
325+
return $response;
302326
}
303327

304328
/**
@@ -333,33 +357,45 @@ public function delete_item( $request ) {
333357
$id = $template->wp_id;
334358
$force = (bool) $request['force'];
335359

360+
$request->set_param( 'context', 'edit' );
361+
336362
// If we're forcing, then delete permanently.
337363
if ( $force ) {
338364
$previous = $this->prepare_item_for_response( $template, $request );
339-
wp_delete_post( $id, true );
365+
$result = wp_delete_post( $id, true );
340366
$response = new WP_REST_Response();
341367
$response->set_data(
342368
array(
343369
'deleted' => true,
344370
'previous' => $previous->get_data(),
345371
)
346372
);
373+
} else {
374+
// Otherwise, only trash if we haven't already.
375+
if ( 'trash' === $template->status ) {
376+
return new WP_Error(
377+
'rest_template_already_trashed',
378+
__( 'The template has already been deleted.' ),
379+
array( 'status' => 410 )
380+
);
381+
}
347382

348-
return $response;
383+
// (Note that internally this falls through to `wp_delete_post()`
384+
// if the Trash is disabled.)
385+
$result = wp_trash_post( $id );
386+
$template->status = 'trash';
387+
$response = $this->prepare_item_for_response( $template, $request );
349388
}
350389

351-
// Otherwise, only trash if we haven't already.
352-
if ( 'trash' === $template->status ) {
390+
if ( ! $result ) {
353391
return new WP_Error(
354-
'rest_template_already_trashed',
355-
__( 'The template has already been deleted.' ),
356-
array( 'status' => 410 )
392+
'rest_cannot_delete',
393+
__( 'The template cannot be deleted.' ),
394+
array( 'status' => 500 )
357395
);
358396
}
359397

360-
wp_trash_post( $id );
361-
$template->status = 'trash';
362-
return $this->prepare_item_for_response( $template, $request );
398+
return $response;
363399
}
364400

365401
/**
@@ -392,12 +428,20 @@ protected function prepare_item_for_database( $request ) {
392428
$changes->post_status = 'publish';
393429
}
394430
if ( isset( $request['content'] ) ) {
395-
$changes->post_content = $request['content'];
431+
if ( is_string( $request['content'] ) ) {
432+
$changes->post_content = $request['content'];
433+
} elseif ( isset( $request['content']['raw'] ) ) {
434+
$changes->post_content = $request['content']['raw'];
435+
}
396436
} elseif ( null !== $template && 'custom' !== $template->source ) {
397437
$changes->post_content = $template->content;
398438
}
399439
if ( isset( $request['title'] ) ) {
400-
$changes->post_title = $request['title'];
440+
if ( is_string( $request['title'] ) ) {
441+
$changes->post_title = $request['title'];
442+
} elseif ( ! empty( $request['title']['raw'] ) ) {
443+
$changes->post_title = $request['title']['raw'];
444+
}
401445
} elseif ( null !== $template && 'custom' !== $template->source ) {
402446
$changes->post_title = $template->title;
403447
}
@@ -433,31 +477,88 @@ protected function prepare_item_for_database( $request ) {
433477
public function prepare_item_for_response( $item, $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
434478
// Restores the more descriptive, specific name for use within this method.
435479
$template = $item;
436-
$result = array(
437-
'id' => $template->id,
438-
'theme' => $template->theme,
439-
'content' => array( 'raw' => $template->content ),
440-
'slug' => $template->slug,
441-
'source' => $template->source,
442-
'type' => $template->type,
443-
'description' => $template->description,
444-
'title' => array(
445-
'raw' => $template->title,
446-
'rendered' => $template->title,
447-
),
448-
'status' => $template->status,
449-
'wp_id' => $template->wp_id,
450-
'has_theme_file' => $template->has_theme_file,
451-
);
452480

453-
if ( 'wp_template_part' === $template->type ) {
454-
$result['area'] = $template->area;
481+
$fields = $this->get_fields_for_response( $request );
482+
483+
// Base fields for every template.
484+
$data = array();
485+
486+
if ( rest_is_field_included( 'id', $fields ) ) {
487+
$data['id'] = $template->id;
488+
}
489+
490+
if ( rest_is_field_included( 'theme', $fields ) ) {
491+
$data['theme'] = $template->theme;
492+
}
493+
494+
if ( rest_is_field_included( 'content', $fields ) ) {
495+
$data['content'] = array();
496+
}
497+
if ( rest_is_field_included( 'content.raw', $fields ) ) {
498+
$data['content']['raw'] = $template->content;
499+
}
500+
501+
if ( rest_is_field_included( 'content.block_version', $fields ) ) {
502+
$data['content']['block_version'] = block_version( $template->content );
503+
}
504+
505+
if ( rest_is_field_included( 'slug', $fields ) ) {
506+
$data['slug'] = $template->slug;
507+
}
508+
509+
if ( rest_is_field_included( 'source', $fields ) ) {
510+
$data['source'] = $template->source;
455511
}
456512

457-
$result = $this->add_additional_fields_to_object( $result, $request );
513+
if ( rest_is_field_included( 'type', $fields ) ) {
514+
$data['type'] = $template->type;
515+
}
516+
517+
if ( rest_is_field_included( 'description', $fields ) ) {
518+
$data['description'] = $template->description;
519+
}
520+
521+
if ( rest_is_field_included( 'title', $fields ) ) {
522+
$data['title'] = array();
523+
}
524+
525+
if ( rest_is_field_included( 'title.raw', $fields ) ) {
526+
$data['title']['raw'] = $template->title;
527+
}
528+
529+
if ( rest_is_field_included( 'title.rendered', $fields ) ) {
530+
if ( $template->wp_id ) {
531+
/** This filter is documented in wp-includes/post-template.php */
532+
$data['title']['rendered'] = apply_filters( 'the_title', $template->title, $template->wp_id );
533+
} else {
534+
$data['title']['rendered'] = $template->title;
535+
}
536+
}
537+
538+
if ( rest_is_field_included( 'status', $fields ) ) {
539+
$data['status'] = $template->status;
540+
}
541+
542+
if ( rest_is_field_included( 'wp_id', $fields ) ) {
543+
$data['wp_id'] = (int) $template->wp_id;
544+
}
458545

459-
$response = rest_ensure_response( $result );
460-
$links = $this->prepare_links( $template->id );
546+
if ( rest_is_field_included( 'has_theme_file', $fields ) ) {
547+
$data['has_theme_file'] = (bool) $template->has_theme_file;
548+
}
549+
550+
if ( rest_is_field_included( 'area', $fields ) && 'wp_template_part' === $template->type ) {
551+
$data['area'] = $template->area;
552+
}
553+
554+
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
555+
$data = $this->add_additional_fields_to_object( $data, $request );
556+
$data = $this->filter_response_by_context( $data, $context );
557+
558+
// Wrap the data in a response object.
559+
$response = rest_ensure_response( $data );
560+
561+
$links = $this->prepare_links( $template->id );
461562
$response->add_links( $links );
462563
if ( ! empty( $links['self']['href'] ) ) {
463564
$actions = $this->get_available_actions();
@@ -530,7 +631,7 @@ protected function get_available_actions() {
530631
*/
531632
public function get_collection_params() {
532633
return array(
533-
'context' => $this->get_context_param(),
634+
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
534635
'wp_id' => array(
535636
'description' => __( 'Limit to the specified post id.' ),
536637
'type' => 'integer',
@@ -583,6 +684,11 @@ public function get_item_schema() {
583684
'type' => 'string',
584685
'context' => array( 'embed', 'view', 'edit' ),
585686
),
687+
'type' => array(
688+
'description' => __( 'Type of template.' ),
689+
'type' => 'string',
690+
'context' => array( 'embed', 'view', 'edit' ),
691+
),
586692
'source' => array(
587693
'description' => __( 'Source of template' ),
588694
'type' => 'string',
@@ -594,12 +700,38 @@ public function get_item_schema() {
594700
'type' => array( 'object', 'string' ),
595701
'default' => '',
596702
'context' => array( 'embed', 'view', 'edit' ),
703+
'properties' => array(
704+
'raw' => array(
705+
'description' => __( 'Content for the template, as it exists in the database.' ),
706+
'type' => 'string',
707+
'context' => array( 'view', 'edit' ),
708+
),
709+
'block_version' => array(
710+
'description' => __( 'Version of the content block format used by the template.' ),
711+
'type' => 'integer',
712+
'context' => array( 'edit' ),
713+
'readonly' => true,
714+
),
715+
),
597716
),
598717
'title' => array(
599718
'description' => __( 'Title of template.' ),
600719
'type' => array( 'object', 'string' ),
601720
'default' => '',
602721
'context' => array( 'embed', 'view', 'edit' ),
722+
'properties' => array(
723+
'raw' => array(
724+
'description' => __( 'Title for the template, as it exists in the database.' ),
725+
'type' => 'string',
726+
'context' => array( 'view', 'edit', 'embed' ),
727+
),
728+
'rendered' => array(
729+
'description' => __( 'HTML title for the template, transformed for display.' ),
730+
'type' => 'string',
731+
'context' => array( 'view', 'edit', 'embed' ),
732+
'readonly' => true,
733+
),
734+
),
603735
),
604736
'description' => array(
605737
'description' => __( 'Description of template.' ),
@@ -610,6 +742,7 @@ public function get_item_schema() {
610742
'status' => array(
611743
'description' => __( 'Status of template.' ),
612744
'type' => 'string',
745+
'enum' => array_keys( get_post_stati( array( 'internal' => false ) ) ),
613746
'default' => 'publish',
614747
'context' => array( 'embed', 'view', 'edit' ),
615748
),

0 commit comments

Comments
 (0)