<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[44150] trunk: REST API: Add endpoints for blocks.</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { white-space: pre-line; overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta" style="font-size: 105%">
<dt style="float: left; width: 6em; font-weight: bold">Revision</dt> <dd><a style="font-weight: bold" href="https://core.trac.wordpress.org/changeset/44150">44150</a><script type="application/ld+json">{"@context":"http://schema.org","@type":"EmailMessage","description":"Review this Commit","action":{"@type":"ViewAction","url":"https://core.trac.wordpress.org/changeset/44150","name":"Review Commit"}}</script></dd>
<dt style="float: left; width: 6em; font-weight: bold">Author</dt> <dd>jeremyfelt</dd>
<dt style="float: left; width: 6em; font-weight: bold">Date</dt> <dd>2018-12-14 02:34:28 +0000 (Fri, 14 Dec 2018)</dd>
</dl>

<pre style='padding-left: 1em; margin: 2em 0; border-left: 2px solid #ccc; line-height: 1.25; font-size: 105%; font-family: sans-serif'>REST API: Add endpoints for blocks.

`WP_REST_Block_Renderer_Controller` allows rendering of server-side rendered blocks, whilst `WP_REST_Blocks_Controller` allows retrieving of reusable blocks.

Merges <a href="https://core.trac.wordpress.org/changeset/43805">[43805]</a> and <a href="https://core.trac.wordpress.org/changeset/43806">[43806]</a> from the 5.0 branch to trunk.

Props desrosj, danielbachhuber, pento, Presskopp, swissspidy.
See <a href="https://core.trac.wordpress.org/ticket/45065">#45065</a>, <a href="https://core.trac.wordpress.org/ticket/45098">#45098</a>.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunksrcwpincludespostphp">trunk/src/wp-includes/post.php</a></li>
<li><a href="#trunksrcwpincludesrestapiphp">trunk/src/wp-includes/rest-api.php</a></li>
<li><a href="#trunksrcwpsettingsphp">trunk/src/wp-settings.php</a></li>
<li><a href="#trunktestsphpunittestsrestapirestschemasetupphp">trunk/tests/phpunit/tests/rest-api/rest-schema-setup.php</a></li>
<li><a href="#trunktestsqunitfixtureswpapigeneratedjs">trunk/tests/qunit/fixtures/wp-api-generated.js</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunksrcwpincludesrestapiendpointsclasswprestblockrenderercontrollerphp">trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-block-renderer-controller.php</a></li>
<li><a href="#trunksrcwpincludesrestapiendpointsclasswprestblockscontrollerphp">trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-blocks-controller.php</a></li>
<li><a href="#trunktestsphpunittestsrestapirestblockrenderercontrollerphp">trunk/tests/phpunit/tests/rest-api/rest-block-renderer-controller.php</a></li>
<li><a href="#trunktestsphpunittestsrestapirestblockscontrollerphp">trunk/tests/phpunit/tests/rest-api/rest-blocks-controller.php</a></li>
</ul>

<h3>Property Changed</h3>
<ul>
<li><a href="#trunk">trunk/</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<span class="cx" style="display: block; padding: 0 10px">Index: trunk
</span><span class="cx" style="display: block; padding: 0 10px">===================================================================
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">--- trunk        2018-12-14 02:32:33 UTC (rev 44149)
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+++ trunk 2018-12-14 02:34:28 UTC (rev 44150)
</ins><a id="trunk"></a>
<div class="propset"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Property changes: trunk</h4>
<pre class="diff"><span>
</span></pre></div>
<a id="svnmergeinfo"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: svn:mergeinfo</h4></div>
<span class="cx" style="display: block; padding: 0 10px"> /branches/3.3:20543
</span><span class="cx" style="display: block; padding: 0 10px"> /branches/3.4:21757
</span><span class="cx" style="display: block; padding: 0 10px"> /branches/4.9:43557,43622
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-/branches/5.0:43681-43682,43684-43688,43719-43720,43723,43726-43727,43729-43731,43734-43744,43751-43754,43758,43761-43765,43767-43770,43772,43774-43781,43783,43790-43800,43802,43804,43808,43821,43842,43860,43892,43904,43909,43926-43929,43956,43961-43963
</del><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of property
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+/branches/5.0:43681-43682,43684-43688,43719-43720,43723,43726-43727,43729-43731,43734-43744,43751-43754,43758,43761-43765,43767-43770,43772,43774-43781,43783,43790-43800,43802,43804-43806,43808,43821,43842,43860,43892,43904,43909,43926-43929,43956,43961-43963
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of property
</span><a id="trunksrcwpincludespostphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/post.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/post.php    2018-12-14 02:32:33 UTC (rev 44149)
+++ trunk/src/wp-includes/post.php      2018-12-14 02:34:28 UTC (rev 44150)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -255,18 +255,21 @@
</span><span class="cx" style="display: block; padding: 0 10px">        register_post_type(
</span><span class="cx" style="display: block; padding: 0 10px">                'wp_block',
</span><span class="cx" style="display: block; padding: 0 10px">                array(
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        'labels'          => array(
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 'labels'                => array(
</ins><span class="cx" style="display: block; padding: 0 10px">                                 'name'          => __( 'Blocks' ),
</span><span class="cx" style="display: block; padding: 0 10px">                                'singular_name' => __( 'Block' ),
</span><span class="cx" style="display: block; padding: 0 10px">                                'search_items'  => __( 'Search Blocks' ),
</span><span class="cx" style="display: block; padding: 0 10px">                        ),
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        'public'          => false,
-                       '_builtin'        => true, /* internal use only. don't use this when registering your own post type. */
-                       'show_ui'         => true,
-                       'show_in_menu'    => false,
-                       'rewrite'         => false,
-                       'capability_type' => 'block',
-                       'capabilities'    => array(
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 'public'                => false,
+                       '_builtin'              => true, /* internal use only. don't use this when registering your own post type. */
+                       'show_ui'               => true,
+                       'show_in_menu'          => false,
+                       'rewrite'               => false,
+                       'show_in_rest'          => true,
+                       'rest_base'             => 'blocks',
+                       'rest_controller_class' => 'WP_REST_Blocks_Controller',
+                       'capability_type'       => 'block',
+                       'capabilities'          => array(
</ins><span class="cx" style="display: block; padding: 0 10px">                                 // You need to be able to edit posts, in order to read blocks in their raw form.
</span><span class="cx" style="display: block; padding: 0 10px">                                'read'                   => 'edit_posts',
</span><span class="cx" style="display: block; padding: 0 10px">                                // You need to be able to publish posts, in order to create blocks.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -276,8 +279,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                'edit_others_posts'      => 'edit_others_posts',
</span><span class="cx" style="display: block; padding: 0 10px">                                'delete_others_posts'    => 'delete_others_posts',
</span><span class="cx" style="display: block; padding: 0 10px">                        ),
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        'map_meta_cap'    => true,
-                       'supports'        => array(
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 'map_meta_cap'          => true,
+                       'supports'              => array(
</ins><span class="cx" style="display: block; padding: 0 10px">                                 'title',
</span><span class="cx" style="display: block; padding: 0 10px">                                'editor',
</span><span class="cx" style="display: block; padding: 0 10px">                        ),
</span></span></pre></div>
<a id="trunksrcwpincludesrestapiendpointsclasswprestblockrenderercontrollerphpfromrev43805branches50srcwpincludesrestapiendpointsclasswprestblockrenderercontrollerphp"></a>
<div class="copfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Copied: trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-block-renderer-controller.php (from rev 43805, branches/5.0/src/wp-includes/rest-api/endpoints/class-wp-rest-block-renderer-controller.php)</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-block-renderer-controller.php                              (rev 0)
+++ trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-block-renderer-controller.php        2018-12-14 02:34:28 UTC (rev 44150)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,177 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * Block Renderer REST API: WP_REST_Block_Renderer_Controller class
+ *
+ * @package WordPress
+ * @subpackage REST_API
+ * @since 5.0.0
+ */
+
+/**
+ * Controller which provides REST endpoint for rendering a block.
+ *
+ * @since 5.0.0
+ *
+ * @see WP_REST_Controller
+ */
+class WP_REST_Block_Renderer_Controller extends WP_REST_Controller {
+
+       /**
+        * Constructs the controller.
+        *
+        * @since 5.0.0
+        */
+       public function __construct() {
+               $this->namespace = 'wp/v2';
+               $this->rest_base = 'block-renderer';
+       }
+
+       /**
+        * Registers the necessary REST API routes, one for each dynamic block.
+        *
+        * @since 5.0.0
+        */
+       public function register_routes() {
+               $block_types = WP_Block_Type_Registry::get_instance()->get_all_registered();
+
+               foreach ( $block_types as $block_type ) {
+                       if ( ! $block_type->is_dynamic() ) {
+                               continue;
+                       }
+
+                       register_rest_route(
+                               $this->namespace,
+                               '/' . $this->rest_base . '/(?P<name>' . $block_type->name . ')',
+                               array(
+                                       'args'   => array(
+                                               'name' => array(
+                                                       'description' => __( 'Unique registered name for the block.' ),
+                                                       'type'        => 'string',
+                                               ),
+                                       ),
+                                       array(
+                                               'methods'             => WP_REST_Server::READABLE,
+                                               'callback'            => array( $this, 'get_item' ),
+                                               'permission_callback' => array( $this, 'get_item_permissions_check' ),
+                                               'args'                => array(
+                                                       'context'    => $this->get_context_param( array( 'default' => 'view' ) ),
+                                                       'attributes' => array(
+                                                               /* translators: %s is the name of the block */
+                                                               'description'          => sprintf( __( 'Attributes for %s block' ), $block_type->name ),
+                                                               'type'                 => 'object',
+                                                               'additionalProperties' => false,
+                                                               'properties'           => $block_type->get_attributes(),
+                                                       ),
+                                                       'post_id'    => array(
+                                                               'description' => __( 'ID of the post context.' ),
+                                                               'type'        => 'integer',
+                                                       ),
+                                               ),
+                                       ),
+                                       'schema' => array( $this, 'get_public_item_schema' ),
+                               )
+                       );
+               }
+       }
+
+       /**
+        * Checks if a given request has access to read blocks.
+        *
+        * @since 5.0.0
+        *
+        * @param WP_REST_Request $request Request.
+        * @return true|WP_Error True if the request has read access, WP_Error object otherwise.
+        */
+       public function get_item_permissions_check( $request ) {
+               global $post;
+
+               $post_id = isset( $request['post_id'] ) ? intval( $request['post_id'] ) : 0;
+
+               if ( 0 < $post_id ) {
+                       $post = get_post( $post_id );
+
+                       if ( ! $post || ! current_user_can( 'edit_post', $post->ID ) ) {
+                               return new WP_Error(
+                                       'block_cannot_read',
+                                       __( 'Sorry, you are not allowed to read blocks of this post.' ),
+                                       array(
+                                               'status' => rest_authorization_required_code(),
+                                       )
+                               );
+                       }
+               } else {
+                       if ( ! current_user_can( 'edit_posts' ) ) {
+                               return new WP_Error(
+                                       'block_cannot_read',
+                                       __( 'Sorry, you are not allowed to read blocks as this user.' ),
+                                       array(
+                                               'status' => rest_authorization_required_code(),
+                                       )
+                               );
+                       }
+               }
+
+               return true;
+       }
+
+       /**
+        * Returns block output from block's registered render_callback.
+        *
+        * @since 5.0.0
+        *
+        * @param WP_REST_Request $request Full details about the request.
+        * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
+        */
+       public function get_item( $request ) {
+               global $post;
+
+               $post_id = isset( $request['post_id'] ) ? intval( $request['post_id'] ) : 0;
+
+               if ( 0 < $post_id ) {
+                       $post = get_post( $post_id );
+
+                       // Set up postdata since this will be needed if post_id was set.
+                       setup_postdata( $post );
+               }
+               $registry = WP_Block_Type_Registry::get_instance();
+               $block    = $registry->get_registered( $request['name'] );
+
+               if ( null === $block ) {
+                       return new WP_Error(
+                               'block_invalid',
+                               __( 'Invalid block.' ),
+                               array(
+                                       'status' => 404,
+                               )
+                       );
+               }
+
+               $data = array(
+                       'rendered' => $block->render( $request->get_param( 'attributes' ) ),
+               );
+               return rest_ensure_response( $data );
+       }
+
+       /**
+        * Retrieves block's output schema, conforming to JSON Schema.
+        *
+        * @since 5.0.0
+        *
+        * @return array Item schema data.
+        */
+       public function get_item_schema() {
+               return array(
+                       '$schema'    => 'http://json-schema.org/schema#',
+                       'title'      => 'rendered-block',
+                       'type'       => 'object',
+                       'properties' => array(
+                               'rendered' => array(
+                                       'description' => __( 'The rendered block.' ),
+                                       'type'        => 'string',
+                                       'required'    => true,
+                                       'context'     => array( 'edit' ),
+                               ),
+                       ),
+               );
+       }
+}
</ins></span></pre></div>
<a id="trunksrcwpincludesrestapiendpointsclasswprestblockscontrollerphpfromrev43805branches50srcwpincludesrestapiendpointsclasswprestblockscontrollerphp"></a>
<div class="copfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Copied: trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-blocks-controller.php (from rev 43805, branches/5.0/src/wp-includes/rest-api/endpoints/class-wp-rest-blocks-controller.php)</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-blocks-controller.php                              (rev 0)
+++ trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-blocks-controller.php        2018-12-14 02:34:28 UTC (rev 44150)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,39 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * Reusable blocks REST API: WP_REST_Blocks_Controller class
+ *
+ * @package WordPress
+ * @subpackage REST_API
+ * @since 5.0.0
+ */
+
+/**
+ * Controller which provides a REST endpoint for the editor to read, create,
+ * edit and delete reusable blocks. Blocks are stored as posts with the wp_block
+ * post type.
+ *
+ * @since 5.0.0
+ *
+ * @see WP_REST_Posts_Controller
+ * @see WP_REST_Controller
+ */
+class WP_REST_Blocks_Controller extends WP_REST_Posts_Controller {
+
+       /**
+        * Checks if a block can be read.
+        *
+        * @since 5.0.0
+        *
+        * @param object $post Post object that backs the block.
+        * @return bool Whether the block can be read.
+        */
+       public function check_read_permission( $post ) {
+               // Ensure that the user is logged in and has the read_blocks capability.
+               $post_type = get_post_type_object( $post->post_type );
+               if ( ! current_user_can( $post_type->cap->read_post, $post->ID ) ) {
+                       return false;
+               }
+
+               return parent::check_read_permission( $post );
+       }
+}
</ins></span></pre></div>
<a id="trunksrcwpincludesrestapiphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/rest-api.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/rest-api.php        2018-12-14 02:32:33 UTC (rev 44149)
+++ trunk/src/wp-includes/rest-api.php  2018-12-14 02:34:28 UTC (rev 44150)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -249,6 +249,10 @@
</span><span class="cx" style="display: block; padding: 0 10px">        $controller = new WP_REST_Search_Controller( $search_handlers );
</span><span class="cx" style="display: block; padding: 0 10px">        $controller->register_routes();
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        // Block Renderer.
+       $controller = new WP_REST_Block_Renderer_Controller;
+       $controller->register_routes();
+
</ins><span class="cx" style="display: block; padding: 0 10px">         // Settings.
</span><span class="cx" style="display: block; padding: 0 10px">        $controller = new WP_REST_Settings_Controller;
</span><span class="cx" style="display: block; padding: 0 10px">        $controller->register_routes();
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -256,6 +260,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">        // Themes.
</span><span class="cx" style="display: block; padding: 0 10px">        $controller = new WP_REST_Themes_Controller;
</span><span class="cx" style="display: block; padding: 0 10px">        $controller->register_routes();
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> /**
</span></span></pre></div>
<a id="trunksrcwpsettingsphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-settings.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-settings.php 2018-12-14 02:32:33 UTC (rev 44149)
+++ trunk/src/wp-settings.php   2018-12-14 02:34:28 UTC (rev 44150)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -236,6 +236,8 @@
</span><span class="cx" style="display: block; padding: 0 10px"> require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-users-controller.php' );
</span><span class="cx" style="display: block; padding: 0 10px"> require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-comments-controller.php' );
</span><span class="cx" style="display: block; padding: 0 10px"> require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-search-controller.php' );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-blocks-controller.php' );
+require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-block-renderer-controller.php' );
</ins><span class="cx" style="display: block; padding: 0 10px"> require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-settings-controller.php' );
</span><span class="cx" style="display: block; padding: 0 10px"> require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-themes-controller.php' );
</span><span class="cx" style="display: block; padding: 0 10px"> require( ABSPATH . WPINC . '/rest-api/fields/class-wp-rest-meta-fields.php' );
</span></span></pre></div>
<a id="trunktestsphpunittestsrestapirestblockrenderercontrollerphpfromrev43805branches50testsphpunittestsrestapirestblockrenderercontrollerphp"></a>
<div class="copfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Copied: trunk/tests/phpunit/tests/rest-api/rest-block-renderer-controller.php (from rev 43805, branches/5.0/tests/phpunit/tests/rest-api/rest-block-renderer-controller.php)</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/rest-api/rest-block-renderer-controller.php                             (rev 0)
+++ trunk/tests/phpunit/tests/rest-api/rest-block-renderer-controller.php       2018-12-14 02:34:28 UTC (rev 44150)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,529 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * WP_REST_Block_Renderer_Controller tests.
+ *
+ * @package WordPress
+ * @subpackage REST_API
+ * @since 5.0.0
+ */
+
+/**
+ * Tests for WP_REST_Block_Renderer_Controller.
+ *
+ * @since 5.0.0
+ *
+ * @covers WP_REST_Block_Renderer_Controller
+ *
+ * @group restapi-blocks
+ * @group restapi
+ */
+class REST_Block_Renderer_Controller_Test extends WP_Test_REST_Controller_Testcase {
+
+       /**
+        * The REST API route for the block renderer.
+        *
+        * @since 5.0.0
+        *
+        * @var string
+        */
+       protected static $rest_api_route = '/wp/v2/block-renderer/';
+
+       /**
+        * Test block's name.
+        *
+        * @since 5.0.0
+        *
+        * @var string
+        */
+       protected static $block_name = 'core/test-block';
+
+       /**
+        * Test post context block's name.
+        *
+        * @since 5.0.0
+        *
+        * @var string
+        */
+       protected static $context_block_name = 'core/context-test-block';
+
+       /**
+        * Test API user's ID.
+        *
+        * @since 5.0.0
+        *
+        * @var int
+        */
+       protected static $user_id;
+
+       /**
+        * Test post ID.
+        *
+        * @since 5.0.0
+        *
+        * @var int
+        */
+       protected static $post_id;
+
+       /**
+        * Author test user ID.
+        *
+        * @since 5.0.0
+        *
+        * @var int
+        */
+       protected static $author_id;
+
+       /**
+        * Create test data before the tests run.
+        *
+        * @since 5.0.0
+        *
+        * @param WP_UnitTest_Factory $factory Helper that lets us create fake data.
+        */
+       public static function wpSetUpBeforeClass( $factory ) {
+               self::$user_id = $factory->user->create(
+                       array(
+                               'role' => 'editor',
+                       )
+               );
+
+               self::$author_id = $factory->user->create(
+                       array(
+                               'role' => 'author',
+                       )
+               );
+
+               self::$post_id = $factory->post->create(
+                       array(
+                               'post_title' => 'Test Post',
+                       )
+               );
+       }
+
+       /**
+        * Delete test data after our tests run.
+        *
+        * @since 5.0.0
+        */
+       public static function wpTearDownAfterClass() {
+               self::delete_user( self::$user_id );
+       }
+
+       /**
+        * Set up each test method.
+        *
+        * @since 5.0.0
+        */
+       public function setUp() {
+               $this->register_test_block();
+               $this->register_post_context_test_block();
+               parent::setUp();
+       }
+
+       /**
+        * Tear down each test method.
+        *
+        * @since 5.0.0
+        */
+       public function tearDown() {
+               WP_Block_Type_Registry::get_instance()->unregister( self::$block_name );
+               WP_Block_Type_Registry::get_instance()->unregister( self::$context_block_name );
+               parent::tearDown();
+       }
+
+       /**
+        * Register test block.
+        *
+        * @since 5.0.0
+        */
+       public function register_test_block() {
+               register_block_type(
+                       self::$block_name,
+                       array(
+                               'attributes'      => array(
+                                       'some_string' => array(
+                                               'type'    => 'string',
+                                               'default' => 'some_default',
+                                       ),
+                                       'some_int'    => array(
+                                               'type' => 'integer',
+                                       ),
+                                       'some_array'  => array(
+                                               'type'  => 'array',
+                                               'items' => array(
+                                                       'type' => 'integer',
+                                               ),
+                                       ),
+                               ),
+                               'render_callback' => array( $this, 'render_test_block' ),
+                       )
+               );
+       }
+
+       /**
+        * Register test block with post_id as attribute for post context test.
+        *
+        * @since 5.0.0
+        */
+       public function register_post_context_test_block() {
+               register_block_type(
+                       self::$context_block_name,
+                       array(
+                               'attributes'      => array(),
+                               'render_callback' => array( $this, 'render_post_context_test_block' ),
+                       )
+               );
+       }
+
+       /**
+        * Test render callback.
+        *
+        * @since 5.0.0
+        *
+        * @param array $attributes Props.
+        * @return string Rendered attributes, which is here just JSON.
+        */
+       public function render_test_block( $attributes ) {
+               return wp_json_encode( $attributes );
+       }
+
+       /**
+        * Test render callback for testing post context.
+        *
+        * @since 5.0.0
+        *
+        * @return string
+        */
+       public function render_post_context_test_block() {
+               return get_the_title();
+       }
+
+       /**
+        * Check that the route was registered properly.
+        *
+        * @ticket 45098
+        *
+        * @covers WP_REST_Block_Renderer_Controller::register_routes()
+        */
+       public function test_register_routes() {
+               $dynamic_block_names = get_dynamic_block_names();
+               $this->assertContains( self::$block_name, $dynamic_block_names );
+
+               $routes = rest_get_server()->get_routes();
+               foreach ( $dynamic_block_names as $dynamic_block_name ) {
+                       $this->assertArrayHasKey( self::$rest_api_route . "(?P<name>$dynamic_block_name)", $routes );
+               }
+       }
+
+       /**
+        * Test getting item without permissions.
+        *
+        * @ticket 45098
+        *
+        * @covers WP_REST_Block_Renderer_Controller::get_item()
+        */
+       public function test_get_item_without_permissions() {
+               wp_set_current_user( 0 );
+
+               $request = new WP_REST_Request( 'GET', self::$rest_api_route . self::$block_name );
+               $request->set_param( 'context', 'edit' );
+
+               $response = rest_get_server()->dispatch( $request );
+
+               $this->assertErrorResponse( 'block_cannot_read', $response, rest_authorization_required_code() );
+       }
+
+       /**
+        * Test getting item without 'edit' context.
+        *
+        * @ticket 45098
+        */
+       public function test_get_item_with_invalid_context() {
+               wp_set_current_user( self::$user_id );
+
+               $request  = new WP_REST_Request( 'GET', self::$rest_api_route . self::$block_name );
+               $response = rest_get_server()->dispatch( $request );
+
+               $this->assertErrorResponse( 'rest_invalid_param', $response, 400 );
+       }
+
+       /**
+        * Test getting item with invalid block name.
+        *
+        * @ticket 45098
+        *
+        * @covers WP_REST_Block_Renderer_Controller::get_item()
+        */
+       public function test_get_item_invalid_block_name() {
+               wp_set_current_user( self::$user_id );
+               $request = new WP_REST_Request( 'GET', self::$rest_api_route . 'core/123' );
+
+               $request->set_param( 'context', 'edit' );
+               $response = rest_get_server()->dispatch( $request );
+
+               $this->assertErrorResponse( 'rest_no_route', $response, 404 );
+       }
+
+       /**
+        * Check getting item with an invalid param provided.
+        *
+        * @ticket 45098
+        *
+        * @covers WP_REST_Block_Renderer_Controller::get_item()
+        */
+       public function test_get_item_invalid_attribute() {
+               wp_set_current_user( self::$user_id );
+               $request = new WP_REST_Request( 'GET', self::$rest_api_route . self::$block_name );
+               $request->set_param( 'context', 'edit' );
+               $request->set_param(
+                       'attributes',
+                       array(
+                               'some_string' => array( 'no!' ),
+                       )
+               );
+               $response = rest_get_server()->dispatch( $request );
+               $this->assertEquals( 400, $response->get_status() );
+       }
+
+       /**
+        * Check getting item with an invalid param provided.
+        *
+        * @ticket 45098
+        *
+        * @covers WP_REST_Block_Renderer_Controller::get_item()
+        */
+       public function test_get_item_unrecognized_attribute() {
+               wp_set_current_user( self::$user_id );
+               $request = new WP_REST_Request( 'GET', self::$rest_api_route . self::$block_name );
+               $request->set_param( 'context', 'edit' );
+               $request->set_param(
+                       'attributes',
+                       array(
+                               'unrecognized' => 'yes',
+                       )
+               );
+               $response = rest_get_server()->dispatch( $request );
+               $this->assertEquals( 400, $response->get_status() );
+       }
+
+       /**
+        * Check getting item with default attributes provided.
+        *
+        * @ticket 45098
+        *
+        * @covers WP_REST_Block_Renderer_Controller::get_item()
+        */
+       public function test_get_item_default_attributes() {
+               wp_set_current_user( self::$user_id );
+
+               $block_type = WP_Block_Type_Registry::get_instance()->get_registered( self::$block_name );
+               $defaults   = array();
+               foreach ( $block_type->attributes as $key => $attribute ) {
+                       $defaults[ $key ] = isset( $attribute['default'] ) ? $attribute['default'] : null;
+               }
+
+               $request = new WP_REST_Request( 'GET', self::$rest_api_route . self::$block_name );
+               $request->set_param( 'context', 'edit' );
+               $request->set_param( 'attributes', array() );
+               $response = rest_get_server()->dispatch( $request );
+               $this->assertEquals( 200, $response->get_status() );
+               $data = $response->get_data();
+
+               $this->assertEquals( $defaults, json_decode( $data['rendered'], true ) );
+               $this->assertEquals(
+                       json_decode( $block_type->render( $defaults ) ),
+                       json_decode( $data['rendered'] )
+               );
+       }
+
+       /**
+        * Check getting item with attributes provided.
+        *
+        * @ticket 45098
+        *
+        * @covers WP_REST_Block_Renderer_Controller::get_item()
+        */
+       public function test_get_item() {
+               wp_set_current_user( self::$user_id );
+
+               $block_type = WP_Block_Type_Registry::get_instance()->get_registered( self::$block_name );
+               $attributes = array(
+                       'some_int'    => '123',
+                       'some_string' => 'foo',
+                       'some_array'  => array( 1, '2', 3 ),
+               );
+
+               $expected_attributes               = $attributes;
+               $expected_attributes['some_int']   = (int) $expected_attributes['some_int'];
+               $expected_attributes['some_array'] = array_map( 'intval', $expected_attributes['some_array'] );
+
+               $request = new WP_REST_Request( 'GET', self::$rest_api_route . self::$block_name );
+               $request->set_param( 'context', 'edit' );
+               $request->set_param( 'attributes', $attributes );
+               $response = rest_get_server()->dispatch( $request );
+               $this->assertEquals( 200, $response->get_status() );
+               $data = $response->get_data();
+
+               $this->assertEquals( $expected_attributes, json_decode( $data['rendered'], true ) );
+               $this->assertEquals(
+                       json_decode( $block_type->render( $attributes ), true ),
+                       json_decode( $data['rendered'], true )
+               );
+       }
+
+
+
+       /**
+        * Check success response for getting item with layout attribute provided.
+        *
+        * @ticket 45098
+        */
+       public function test_get_item_with_layout() {
+               wp_set_current_user( self::$user_id );
+
+               $attributes = array(
+                       'layout' => 'foo',
+               );
+
+               $request = new WP_REST_Request( 'GET', self::$rest_api_route . self::$block_name );
+               $request->set_param( 'context', 'edit' );
+               $request->set_param( 'attributes', $attributes );
+               $response = rest_get_server()->dispatch( $request );
+               $this->assertEquals( 200, $response->get_status() );
+       }
+
+       /**
+        * Test getting item with post context.
+        *
+        * @ticket 45098
+        */
+       public function test_get_item_with_post_context() {
+               wp_set_current_user( self::$user_id );
+
+               $expected_title = 'Test Post';
+               $request        = new WP_REST_Request( 'GET', self::$rest_api_route . self::$context_block_name );
+               $request->set_param( 'context', 'edit' );
+
+               // Test without post ID.
+               $response = rest_get_server()->dispatch( $request );
+
+               $this->assertEquals( 200, $response->get_status() );
+               $data = $response->get_data();
+
+               $this->assertTrue( empty( $data['rendered'] ) );
+
+               // Now test with post ID.
+               $request->set_param( 'post_id', self::$post_id );
+               $response = rest_get_server()->dispatch( $request );
+
+               $this->assertEquals( 200, $response->get_status() );
+               $data = $response->get_data();
+
+               $this->assertEquals( $expected_title, $data['rendered'] );
+       }
+
+       /**
+        * Test getting item with invalid post ID.
+        *
+        * @ticket 45098
+        */
+       public function test_get_item_without_permissions_invalid_post() {
+               wp_set_current_user( self::$user_id );
+
+               $request = new WP_REST_Request( 'GET', self::$rest_api_route . self::$context_block_name );
+               $request->set_param( 'context', 'edit' );
+
+               // Test with invalid post ID.
+               $request->set_param( 'post_id', PHP_INT_MAX );
+               $response = rest_get_server()->dispatch( $request );
+
+               $this->assertErrorResponse( 'block_cannot_read', $response, 403 );
+       }
+
+       /**
+        * Test getting item without permissions to edit context post.
+        *
+        * @ticket 45098
+        */
+       public function test_get_item_without_permissions_cannot_edit_post() {
+               wp_set_current_user( self::$author_id );
+
+               $request = new WP_REST_Request( 'GET', self::$rest_api_route . self::$context_block_name );
+               $request->set_param( 'context', 'edit' );
+
+               // Test with private post ID.
+               $request->set_param( 'post_id', self::$post_id );
+               $response = rest_get_server()->dispatch( $request );
+
+               $this->assertErrorResponse( 'block_cannot_read', $response, 403 );
+       }
+
+       /**
+        * Get item schema.
+        *
+        * @ticket 45098
+        *
+        * @covers WP_REST_Block_Renderer_Controller::get_item_schema()
+        */
+       public function test_get_item_schema() {
+               $request  = new WP_REST_Request( 'OPTIONS', self::$rest_api_route . self::$block_name );
+               $response = rest_get_server()->dispatch( $request );
+               $data     = $response->get_data();
+
+               $this->assertEqualSets( array( 'GET' ), $data['endpoints'][0]['methods'] );
+               $this->assertEqualSets(
+                       array( 'name', 'context', 'attributes', 'post_id' ),
+                       array_keys( $data['endpoints'][0]['args'] )
+               );
+               $this->assertEquals( 'object', $data['endpoints'][0]['args']['attributes']['type'] );
+
+               $this->assertArrayHasKey( 'schema', $data );
+               $this->assertEquals( 'rendered-block', $data['schema']['title'] );
+               $this->assertEquals( 'object', $data['schema']['type'] );
+               $this->arrayHasKey( 'rendered', $data['schema']['properties'] );
+               $this->arrayHasKey( 'string', $data['schema']['properties']['rendered']['type'] );
+               $this->assertEquals( array( 'edit' ), $data['schema']['properties']['rendered']['context'] );
+       }
+
+       /**
+        * The update_item() method does not exist for block rendering.
+        */
+       public function test_update_item() {
+               $this->markTestSkipped( 'Controller does not implement update_item().' );
+       }
+
+       /**
+        * The create_item() method does not exist for block rendering.
+        */
+       public function test_create_item() {
+               $this->markTestSkipped( 'Controller does not implement create_item().' );
+       }
+
+       /**
+        * The delete_item() method does not exist for block rendering.
+        */
+       public function test_delete_item() {
+               $this->markTestSkipped( 'Controller does not implement delete_item().' );
+       }
+
+       /**
+        * The get_items() method does not exist for block rendering.
+        */
+       public function test_get_items() {
+               $this->markTestSkipped( 'Controller does not implement get_items().' );
+       }
+
+       /**
+        * The context_param() method does not exist for block rendering.
+        */
+       public function test_context_param() {
+               $this->markTestSkipped( 'Controller does not implement context_param().' );
+       }
+
+       /**
+        * The prepare_item() method does not exist for block rendering.
+        */
+       public function test_prepare_item() {
+               $this->markTestSkipped( 'Controller does not implement prepare_item().' );
+       }
+}
</ins></span></pre></div>
<a id="trunktestsphpunittestsrestapirestblockscontrollerphpfromrev43805branches50testsphpunittestsrestapirestblockscontrollerphp"></a>
<div class="copfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Copied: trunk/tests/phpunit/tests/rest-api/rest-blocks-controller.php (from rev 43805, branches/5.0/tests/phpunit/tests/rest-api/rest-blocks-controller.php)</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/rest-api/rest-blocks-controller.php                             (rev 0)
+++ trunk/tests/phpunit/tests/rest-api/rest-blocks-controller.php       2018-12-14 02:34:28 UTC (rev 44150)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,204 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * WP_REST_Blocks_Controller tests
+ *
+ * @package WordPress
+ * @subpackage REST_API
+ * @since 5.0.0
+ */
+
+/**
+ * Tests for WP_REST_Blocks_Controller.
+ *
+ * @since 5.0.0
+ *
+ * @see WP_Test_REST_Controller_Testcase
+ *
+ * @group restapi-blocks
+ * @group restapi
+ */
+class REST_Blocks_Controller_Test extends WP_UnitTestCase {
+
+       /**
+        * Our fake block's post ID.
+        *
+        * @since 5.0.0
+        *
+        * @var int
+        */
+       protected static $post_id;
+
+       /**
+        * Our fake user's ID.
+        *
+        * @since 5.0.0
+        *
+        * @var int
+        */
+       protected static $user_id;
+
+       /**
+        * Create fake data before our tests run.
+        *
+        * @since 5.0.0
+        *
+        * @param WP_UnitTest_Factory $factory Helper that lets us create fake data.
+        */
+       public static function wpSetUpBeforeClass( $factory ) {
+               self::$post_id = wp_insert_post(
+                       array(
+                               'post_type'    => 'wp_block',
+                               'post_status'  => 'publish',
+                               'post_title'   => 'My cool block',
+                               'post_content' => '<!-- wp:core/paragraph --><p>Hello!</p><!-- /wp:core/paragraph -->',
+                       )
+               );
+
+               self::$user_id = $factory->user->create(
+                       array(
+                               'role' => 'editor',
+                       )
+               );
+       }
+
+       /**
+        * Delete our fake data after our tests run.
+        *
+        * @since 5.0.0
+        */
+       public static function wpTearDownAfterClass() {
+               wp_delete_post( self::$post_id );
+
+               self::delete_user( self::$user_id );
+       }
+
+       /**
+        * Test cases for test_capabilities().
+        *
+        * @since 5.0.0
+        */
+       public function data_capabilities() {
+               return array(
+                       array( 'create', 'editor', 201 ),
+                       array( 'create', 'author', 201 ),
+                       array( 'create', 'contributor', 403 ),
+                       array( 'create', null, 401 ),
+
+                       array( 'read', 'editor', 200 ),
+                       array( 'read', 'author', 200 ),
+                       array( 'read', 'contributor', 200 ),
+                       array( 'read', null, 401 ),
+
+                       array( 'update_delete_own', 'editor', 200 ),
+                       array( 'update_delete_own', 'author', 200 ),
+                       array( 'update_delete_own', 'contributor', 403 ),
+
+                       array( 'update_delete_others', 'editor', 200 ),
+                       array( 'update_delete_others', 'author', 403 ),
+                       array( 'update_delete_others', 'contributor', 403 ),
+                       array( 'update_delete_others', null, 401 ),
+               );
+       }
+
+       /**
+        * Exhaustively check that each role either can or cannot create, edit,
+        * update, and delete reusable blocks.
+        *
+        * @ticket 45098
+        *
+        * @dataProvider data_capabilities
+        *
+        * @param string $action          Action to perform in the test.
+        * @param string $role            User role to test.
+        * @param int    $expected_status Expected HTTP response status.
+        */
+       public function test_capabilities( $action, $role, $expected_status ) {
+               if ( $role ) {
+                       $user_id = $this->factory->user->create( array( 'role' => $role ) );
+                       wp_set_current_user( $user_id );
+               } else {
+                       wp_set_current_user( 0 );
+               }
+
+               switch ( $action ) {
+                       case 'create':
+                               $request = new WP_REST_Request( 'POST', '/wp/v2/blocks' );
+                               $request->set_body_params(
+                                       array(
+                                               'title'   => 'Test',
+                                               'content' => '<!-- wp:core/paragraph --><p>Test</p><!-- /wp:core/paragraph -->',
+                                       )
+                               );
+
+                               $response = rest_get_server()->dispatch( $request );
+                               $this->assertEquals( $expected_status, $response->get_status() );
+
+                               break;
+
+                       case 'read':
+                               $request = new WP_REST_Request( 'GET', '/wp/v2/blocks/' . self::$post_id );
+
+                               $response = rest_get_server()->dispatch( $request );
+                               $this->assertEquals( $expected_status, $response->get_status() );
+
+                               break;
+
+                       case 'update_delete_own':
+                               $post_id = wp_insert_post(
+                                       array(
+                                               'post_type'    => 'wp_block',
+                                               'post_status'  => 'publish',
+                                               'post_title'   => 'My cool block',
+                                               'post_content' => '<!-- wp:core/paragraph --><p>Hello!</p><!-- /wp:core/paragraph -->',
+                                               'post_author'  => $user_id,
+                                       )
+                               );
+
+                               $request = new WP_REST_Request( 'PUT', '/wp/v2/blocks/' . $post_id );
+                               $request->set_body_params(
+                                       array(
+                                               'title'   => 'Test',
+                                               'content' => '<!-- wp:core/paragraph --><p>Test</p><!-- /wp:core/paragraph -->',
+                                       )
+                               );
+
+                               $response = rest_get_server()->dispatch( $request );
+                               $this->assertEquals( $expected_status, $response->get_status() );
+
+                               $request = new WP_REST_Request( 'DELETE', '/wp/v2/blocks/' . $post_id );
+
+                               $response = rest_get_server()->dispatch( $request );
+                               $this->assertEquals( $expected_status, $response->get_status() );
+
+                               wp_delete_post( $post_id );
+
+                               break;
+
+                       case 'update_delete_others':
+                               $request = new WP_REST_Request( 'PUT', '/wp/v2/blocks/' . self::$post_id );
+                               $request->set_body_params(
+                                       array(
+                                               'title'   => 'Test',
+                                               'content' => '<!-- wp:core/paragraph --><p>Test</p><!-- /wp:core/paragraph -->',
+                                       )
+                               );
+
+                               $response = rest_get_server()->dispatch( $request );
+                               $this->assertEquals( $expected_status, $response->get_status() );
+
+                               $request = new WP_REST_Request( 'DELETE', '/wp/v2/blocks/' . self::$post_id );
+
+                               $response = rest_get_server()->dispatch( $request );
+                               $this->assertEquals( $expected_status, $response->get_status() );
+
+                               break;
+
+                       default:
+                               $this->fail( "'$action' is not a valid action." );
+               }
+
+               if ( isset( $user_id ) ) {
+                       self::delete_user( $user_id );
+               }
+       }
+}
</ins></span></pre></div>
<a id="trunktestsphpunittestsrestapirestschemasetupphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/phpunit/tests/rest-api/rest-schema-setup.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/rest-api/rest-schema-setup.php  2018-12-14 02:32:33 UTC (rev 44149)
+++ trunk/tests/phpunit/tests/rest-api/rest-schema-setup.php    2018-12-14 02:34:28 UTC (rev 44150)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -99,6 +99,10 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        '/wp/v2/pages/(?P<parent>[\\d]+)/autosaves/(?P<id>[\\d]+)',
</span><span class="cx" style="display: block; padding: 0 10px">                        '/wp/v2/media',
</span><span class="cx" style="display: block; padding: 0 10px">                        '/wp/v2/media/(?P<id>[\\d]+)',
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        '/wp/v2/blocks',
+                       '/wp/v2/blocks/(?P<id>[\d]+)',
+                       '/wp/v2/blocks/(?P<parent>[\d]+)/autosaves',
+                       '/wp/v2/blocks/(?P<parent>[\d]+)/autosaves/(?P<id>[\d]+)',
</ins><span class="cx" style="display: block; padding: 0 10px">                         '/wp/v2/types',
</span><span class="cx" style="display: block; padding: 0 10px">                        '/wp/v2/types/(?P<type>[\\w-]+)',
</span><span class="cx" style="display: block; padding: 0 10px">                        '/wp/v2/statuses',
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -115,6 +119,12 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        '/wp/v2/comments',
</span><span class="cx" style="display: block; padding: 0 10px">                        '/wp/v2/comments/(?P<id>[\\d]+)',
</span><span class="cx" style="display: block; padding: 0 10px">                        '/wp/v2/search',
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        '/wp/v2/block-renderer/(?P<name>core/block)',
+                       '/wp/v2/block-renderer/(?P<name>core/latest-comments)',
+                       '/wp/v2/block-renderer/(?P<name>core/archives)',
+                       '/wp/v2/block-renderer/(?P<name>core/categories)',
+                       '/wp/v2/block-renderer/(?P<name>core/latest-posts)',
+                       '/wp/v2/block-renderer/(?P<name>core/shortcode)',
</ins><span class="cx" style="display: block; padding: 0 10px">                         '/wp/v2/settings',
</span><span class="cx" style="display: block; padding: 0 10px">                        '/wp/v2/themes',
</span><span class="cx" style="display: block; padding: 0 10px">                );
</span></span></pre></div>
<a id="trunktestsqunitfixtureswpapigeneratedjs"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/qunit/fixtures/wp-api-generated.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/qunit/fixtures/wp-api-generated.js    2018-12-14 02:32:33 UTC (rev 44149)
+++ trunk/tests/qunit/fixtures/wp-api-generated.js      2018-12-14 02:34:28 UTC (rev 44150)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -2271,6 +2271,508 @@
</span><span class="cx" style="display: block; padding: 0 10px">                 }
</span><span class="cx" style="display: block; padding: 0 10px">             ]
</span><span class="cx" style="display: block; padding: 0 10px">         },
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        "/wp/v2/blocks": {
+            "namespace": "wp/v2",
+            "methods": [
+                "GET",
+                "POST"
+            ],
+            "endpoints": [
+                {
+                    "methods": [
+                        "GET"
+                    ],
+                    "args": {
+                        "context": {
+                            "required": false,
+                            "default": "view",
+                            "enum": [
+                                "view",
+                                "embed",
+                                "edit"
+                            ],
+                            "description": "Scope under which the request is made; determines fields present in response.",
+                            "type": "string"
+                        },
+                        "page": {
+                            "required": false,
+                            "default": 1,
+                            "description": "Current page of the collection.",
+                            "type": "integer"
+                        },
+                        "per_page": {
+                            "required": false,
+                            "default": 10,
+                            "description": "Maximum number of items to be returned in result set.",
+                            "type": "integer"
+                        },
+                        "search": {
+                            "required": false,
+                            "description": "Limit results to those matching a string.",
+                            "type": "string"
+                        },
+                        "after": {
+                            "required": false,
+                            "description": "Limit response to posts published after a given ISO8601 compliant date.",
+                            "type": "string"
+                        },
+                        "before": {
+                            "required": false,
+                            "description": "Limit response to posts published before a given ISO8601 compliant date.",
+                            "type": "string"
+                        },
+                        "exclude": {
+                            "required": false,
+                            "default": [],
+                            "description": "Ensure result set excludes specific IDs.",
+                            "type": "array",
+                            "items": {
+                                "type": "integer"
+                            }
+                        },
+                        "include": {
+                            "required": false,
+                            "default": [],
+                            "description": "Limit result set to specific IDs.",
+                            "type": "array",
+                            "items": {
+                                "type": "integer"
+                            }
+                        },
+                        "offset": {
+                            "required": false,
+                            "description": "Offset the result set by a specific number of items.",
+                            "type": "integer"
+                        },
+                        "order": {
+                            "required": false,
+                            "default": "desc",
+                            "enum": [
+                                "asc",
+                                "desc"
+                            ],
+                            "description": "Order sort attribute ascending or descending.",
+                            "type": "string"
+                        },
+                        "orderby": {
+                            "required": false,
+                            "default": "date",
+                            "enum": [
+                                "author",
+                                "date",
+                                "id",
+                                "include",
+                                "modified",
+                                "parent",
+                                "relevance",
+                                "slug",
+                                "include_slugs",
+                                "title"
+                            ],
+                            "description": "Sort collection by object attribute.",
+                            "type": "string"
+                        },
+                        "slug": {
+                            "required": false,
+                            "description": "Limit result set to posts with one or more specific slugs.",
+                            "type": "array",
+                            "items": {
+                                "type": "string"
+                            }
+                        },
+                        "status": {
+                            "required": false,
+                            "default": "publish",
+                            "description": "Limit result set to posts assigned one or more statuses.",
+                            "type": "array",
+                            "items": {
+                                "enum": [
+                                    "publish",
+                                    "future",
+                                    "draft",
+                                    "pending",
+                                    "private",
+                                    "trash",
+                                    "auto-draft",
+                                    "inherit",
+                                    "request-pending",
+                                    "request-confirmed",
+                                    "request-failed",
+                                    "request-completed",
+                                    "any"
+                                ],
+                                "type": "string"
+                            }
+                        }
+                    }
+                },
+                {
+                    "methods": [
+                        "POST"
+                    ],
+                    "args": {
+                        "date": {
+                            "required": false,
+                            "description": "The date the object was published, in the site's timezone.",
+                            "type": "string"
+                        },
+                        "date_gmt": {
+                            "required": false,
+                            "description": "The date the object was published, as GMT.",
+                            "type": "string"
+                        },
+                        "slug": {
+                            "required": false,
+                            "description": "An alphanumeric identifier for the object unique to its type.",
+                            "type": "string"
+                        },
+                        "status": {
+                            "required": false,
+                            "enum": [
+                                "publish",
+                                "future",
+                                "draft",
+                                "pending",
+                                "private"
+                            ],
+                            "description": "A named status for the object.",
+                            "type": "string"
+                        },
+                        "password": {
+                            "required": false,
+                            "description": "A password to protect access to the content and excerpt.",
+                            "type": "string"
+                        },
+                        "title": {
+                            "required": false,
+                            "description": "The title for the object.",
+                            "type": "object"
+                        },
+                        "content": {
+                            "required": false,
+                            "description": "The content for the object.",
+                            "type": "object"
+                        },
+                        "template": {
+                            "required": false,
+                            "description": "The theme file to use to display the object.",
+                            "type": "string"
+                        }
+                    }
+                }
+            ],
+            "_links": {
+                "self": "http://example.org/index.php?rest_route=/wp/v2/blocks"
+            }
+        },
+        "/wp/v2/blocks/(?P<id>[\\d]+)": {
+            "namespace": "wp/v2",
+            "methods": [
+                "GET",
+                "POST",
+                "PUT",
+                "PATCH",
+                "DELETE"
+            ],
+            "endpoints": [
+                {
+                    "methods": [
+                        "GET"
+                    ],
+                    "args": {
+                        "id": {
+                            "required": false,
+                            "description": "Unique identifier for the object.",
+                            "type": "integer"
+                        },
+                        "context": {
+                            "required": false,
+                            "default": "view",
+                            "enum": [
+                                "view",
+                                "embed",
+                                "edit"
+                            ],
+                            "description": "Scope under which the request is made; determines fields present in response.",
+                            "type": "string"
+                        },
+                        "password": {
+                            "required": false,
+                            "description": "The password for the post if it is password protected.",
+                            "type": "string"
+                        }
+                    }
+                },
+                {
+                    "methods": [
+                        "POST",
+                        "PUT",
+                        "PATCH"
+                    ],
+                    "args": {
+                        "id": {
+                            "required": false,
+                            "description": "Unique identifier for the object.",
+                            "type": "integer"
+                        },
+                        "date": {
+                            "required": false,
+                            "description": "The date the object was published, in the site's timezone.",
+                            "type": "string"
+                        },
+                        "date_gmt": {
+                            "required": false,
+                            "description": "The date the object was published, as GMT.",
+                            "type": "string"
+                        },
+                        "slug": {
+                            "required": false,
+                            "description": "An alphanumeric identifier for the object unique to its type.",
+                            "type": "string"
+                        },
+                        "status": {
+                            "required": false,
+                            "enum": [
+                                "publish",
+                                "future",
+                                "draft",
+                                "pending",
+                                "private"
+                            ],
+                            "description": "A named status for the object.",
+                            "type": "string"
+                        },
+                        "password": {
+                            "required": false,
+                            "description": "A password to protect access to the content and excerpt.",
+                            "type": "string"
+                        },
+                        "title": {
+                            "required": false,
+                            "description": "The title for the object.",
+                            "type": "object"
+                        },
+                        "content": {
+                            "required": false,
+                            "description": "The content for the object.",
+                            "type": "object"
+                        },
+                        "template": {
+                            "required": false,
+                            "description": "The theme file to use to display the object.",
+                            "type": "string"
+                        }
+                    }
+                },
+                {
+                    "methods": [
+                        "DELETE"
+                    ],
+                    "args": {
+                        "id": {
+                            "required": false,
+                            "description": "Unique identifier for the object.",
+                            "type": "integer"
+                        },
+                        "force": {
+                            "required": false,
+                            "default": false,
+                            "description": "Whether to bypass trash and force deletion.",
+                            "type": "boolean"
+                        }
+                    }
+                }
+            ]
+        },
+        "/wp/v2/blocks/(?P<parent>[\\d]+)/autosaves": {
+            "namespace": "wp/v2",
+            "methods": [
+                "GET",
+                "POST"
+            ],
+            "endpoints": [
+                {
+                    "methods": [
+                        "GET"
+                    ],
+                    "args": {
+                        "parent": {
+                            "required": false,
+                            "description": "The ID for the parent of the object.",
+                            "type": "integer"
+                        },
+                        "context": {
+                            "required": false,
+                            "default": "view",
+                            "enum": [
+                                "view",
+                                "embed",
+                                "edit"
+                            ],
+                            "description": "Scope under which the request is made; determines fields present in response.",
+                            "type": "string"
+                        },
+                        "page": {
+                            "required": false,
+                            "default": 1,
+                            "description": "Current page of the collection.",
+                            "type": "integer"
+                        },
+                        "per_page": {
+                            "required": false,
+                            "description": "Maximum number of items to be returned in result set.",
+                            "type": "integer"
+                        },
+                        "search": {
+                            "required": false,
+                            "description": "Limit results to those matching a string.",
+                            "type": "string"
+                        },
+                        "exclude": {
+                            "required": false,
+                            "default": [],
+                            "description": "Ensure result set excludes specific IDs.",
+                            "type": "array",
+                            "items": {
+                                "type": "integer"
+                            }
+                        },
+                        "include": {
+                            "required": false,
+                            "default": [],
+                            "description": "Limit result set to specific IDs.",
+                            "type": "array",
+                            "items": {
+                                "type": "integer"
+                            }
+                        },
+                        "offset": {
+                            "required": false,
+                            "description": "Offset the result set by a specific number of items.",
+                            "type": "integer"
+                        },
+                        "order": {
+                            "required": false,
+                            "default": "desc",
+                            "enum": [
+                                "asc",
+                                "desc"
+                            ],
+                            "description": "Order sort attribute ascending or descending.",
+                            "type": "string"
+                        },
+                        "orderby": {
+                            "required": false,
+                            "default": "date",
+                            "enum": [
+                                "date",
+                                "id",
+                                "include",
+                                "relevance",
+                                "slug",
+                                "include_slugs",
+                                "title"
+                            ],
+                            "description": "Sort collection by object attribute.",
+                            "type": "string"
+                        }
+                    }
+                },
+                {
+                    "methods": [
+                        "POST"
+                    ],
+                    "args": {
+                        "parent": {
+                            "required": false,
+                            "description": "The ID for the parent of the object.",
+                            "type": "integer"
+                        },
+                        "author": {
+                            "required": false,
+                            "description": "The ID for the author of the object.",
+                            "type": "integer"
+                        },
+                        "date": {
+                            "required": false,
+                            "description": "The date the object was published, in the site's timezone.",
+                            "type": "string"
+                        },
+                        "date_gmt": {
+                            "required": false,
+                            "description": "The date the object was published, as GMT.",
+                            "type": "string"
+                        },
+                        "id": {
+                            "required": false,
+                            "description": "Unique identifier for the object.",
+                            "type": "integer"
+                        },
+                        "modified": {
+                            "required": false,
+                            "description": "The date the object was last modified, in the site's timezone.",
+                            "type": "string"
+                        },
+                        "modified_gmt": {
+                            "required": false,
+                            "description": "The date the object was last modified, as GMT.",
+                            "type": "string"
+                        },
+                        "slug": {
+                            "required": false,
+                            "description": "An alphanumeric identifier for the object unique to its type.",
+                            "type": "string"
+                        },
+                        "title": {
+                            "required": false,
+                            "description": "The title for the object.",
+                            "type": "object"
+                        },
+                        "content": {
+                            "required": false,
+                            "description": "The content for the object.",
+                            "type": "object"
+                        }
+                    }
+                }
+            ]
+        },
+        "/wp/v2/blocks/(?P<parent>[\\d]+)/autosaves/(?P<id>[\\d]+)": {
+            "namespace": "wp/v2",
+            "methods": [
+                "GET"
+            ],
+            "endpoints": [
+                {
+                    "methods": [
+                        "GET"
+                    ],
+                    "args": {
+                        "parent": {
+                            "required": false,
+                            "description": "The ID for the parent of the object.",
+                            "type": "integer"
+                        },
+                        "id": {
+                            "required": false,
+                            "description": "The ID for the object.",
+                            "type": "integer"
+                        },
+                        "context": {
+                            "required": false,
+                            "default": "view",
+                            "enum": [
+                                "view",
+                                "embed",
+                                "edit"
+                            ],
+                            "description": "Scope under which the request is made; determines fields present in response.",
+                            "type": "string"
+                        }
+                    }
+                }
+            ]
+        },
</ins><span class="cx" style="display: block; padding: 0 10px">         "/wp/v2/types": {
</span><span class="cx" style="display: block; padding: 0 10px">             "namespace": "wp/v2",
</span><span class="cx" style="display: block; padding: 0 10px">             "methods": [
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -3865,6 +4367,240 @@
</span><span class="cx" style="display: block; padding: 0 10px">                 "self": "http://example.org/index.php?rest_route=/wp/v2/search"
</span><span class="cx" style="display: block; padding: 0 10px">             }
</span><span class="cx" style="display: block; padding: 0 10px">         },
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        "/wp/v2/block-renderer/(?P<name>core/block)": {
+            "namespace": "wp/v2",
+            "methods": [
+                "GET"
+            ],
+            "endpoints": [
+                {
+                    "methods": [
+                        "GET"
+                    ],
+                    "args": {
+                        "name": {
+                            "required": false,
+                            "description": "Unique registered name for the block.",
+                            "type": "string"
+                        },
+                        "context": {
+                            "required": false,
+                            "default": "view",
+                            "enum": [
+                                "edit"
+                            ],
+                            "description": "Scope under which the request is made; determines fields present in response.",
+                            "type": "string"
+                        },
+                        "attributes": {
+                            "required": false,
+                            "description": "Attributes for core/block block",
+                            "type": "object"
+                        },
+                        "post_id": {
+                            "required": false,
+                            "description": "ID of the post context.",
+                            "type": "integer"
+                        }
+                    }
+                }
+            ]
+        },
+        "/wp/v2/block-renderer/(?P<name>core/latest-comments)": {
+            "namespace": "wp/v2",
+            "methods": [
+                "GET"
+            ],
+            "endpoints": [
+                {
+                    "methods": [
+                        "GET"
+                    ],
+                    "args": {
+                        "name": {
+                            "required": false,
+                            "description": "Unique registered name for the block.",
+                            "type": "string"
+                        },
+                        "context": {
+                            "required": false,
+                            "default": "view",
+                            "enum": [
+                                "edit"
+                            ],
+                            "description": "Scope under which the request is made; determines fields present in response.",
+                            "type": "string"
+                        },
+                        "attributes": {
+                            "required": false,
+                            "description": "Attributes for core/latest-comments block",
+                            "type": "object"
+                        },
+                        "post_id": {
+                            "required": false,
+                            "description": "ID of the post context.",
+                            "type": "integer"
+                        }
+                    }
+                }
+            ]
+        },
+        "/wp/v2/block-renderer/(?P<name>core/archives)": {
+            "namespace": "wp/v2",
+            "methods": [
+                "GET"
+            ],
+            "endpoints": [
+                {
+                    "methods": [
+                        "GET"
+                    ],
+                    "args": {
+                        "name": {
+                            "required": false,
+                            "description": "Unique registered name for the block.",
+                            "type": "string"
+                        },
+                        "context": {
+                            "required": false,
+                            "default": "view",
+                            "enum": [
+                                "edit"
+                            ],
+                            "description": "Scope under which the request is made; determines fields present in response.",
+                            "type": "string"
+                        },
+                        "attributes": {
+                            "required": false,
+                            "description": "Attributes for core/archives block",
+                            "type": "object"
+                        },
+                        "post_id": {
+                            "required": false,
+                            "description": "ID of the post context.",
+                            "type": "integer"
+                        }
+                    }
+                }
+            ]
+        },
+        "/wp/v2/block-renderer/(?P<name>core/categories)": {
+            "namespace": "wp/v2",
+            "methods": [
+                "GET"
+            ],
+            "endpoints": [
+                {
+                    "methods": [
+                        "GET"
+                    ],
+                    "args": {
+                        "name": {
+                            "required": false,
+                            "description": "Unique registered name for the block.",
+                            "type": "string"
+                        },
+                        "context": {
+                            "required": false,
+                            "default": "view",
+                            "enum": [
+                                "edit"
+                            ],
+                            "description": "Scope under which the request is made; determines fields present in response.",
+                            "type": "string"
+                        },
+                        "attributes": {
+                            "required": false,
+                            "description": "Attributes for core/categories block",
+                            "type": "object"
+                        },
+                        "post_id": {
+                            "required": false,
+                            "description": "ID of the post context.",
+                            "type": "integer"
+                        }
+                    }
+                }
+            ]
+        },
+        "/wp/v2/block-renderer/(?P<name>core/latest-posts)": {
+            "namespace": "wp/v2",
+            "methods": [
+                "GET"
+            ],
+            "endpoints": [
+                {
+                    "methods": [
+                        "GET"
+                    ],
+                    "args": {
+                        "name": {
+                            "required": false,
+                            "description": "Unique registered name for the block.",
+                            "type": "string"
+                        },
+                        "context": {
+                            "required": false,
+                            "default": "view",
+                            "enum": [
+                                "edit"
+                            ],
+                            "description": "Scope under which the request is made; determines fields present in response.",
+                            "type": "string"
+                        },
+                        "attributes": {
+                            "required": false,
+                            "description": "Attributes for core/latest-posts block",
+                            "type": "object"
+                        },
+                        "post_id": {
+                            "required": false,
+                            "description": "ID of the post context.",
+                            "type": "integer"
+                        }
+                    }
+                }
+            ]
+        },
+        "/wp/v2/block-renderer/(?P<name>core/shortcode)": {
+            "namespace": "wp/v2",
+            "methods": [
+                "GET"
+            ],
+            "endpoints": [
+                {
+                    "methods": [
+                        "GET"
+                    ],
+                    "args": {
+                        "name": {
+                            "required": false,
+                            "description": "Unique registered name for the block.",
+                            "type": "string"
+                        },
+                        "context": {
+                            "required": false,
+                            "default": "view",
+                            "enum": [
+                                "edit"
+                            ],
+                            "description": "Scope under which the request is made; determines fields present in response.",
+                            "type": "string"
+                        },
+                        "attributes": {
+                            "required": false,
+                            "description": "Attributes for core/shortcode block",
+                            "type": "object"
+                        },
+                        "post_id": {
+                            "required": false,
+                            "description": "ID of the post context.",
+                            "type": "integer"
+                        }
+                    }
+                }
+            ]
+        },
</ins><span class="cx" style="display: block; padding: 0 10px">         "/wp/v2/settings": {
</span><span class="cx" style="display: block; padding: 0 10px">             "namespace": "wp/v2",
</span><span class="cx" style="display: block; padding: 0 10px">             "methods": [
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -4347,16 +5083,16 @@
</span><span class="cx" style="display: block; padding: 0 10px">         }
</span><span class="cx" style="display: block; padding: 0 10px">     },
</span><span class="cx" style="display: block; padding: 0 10px">     {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        "author": 359,
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        "author": 375,
</ins><span class="cx" style="display: block; padding: 0 10px">         "date": "2017-02-14T00:00:00",
</span><span class="cx" style="display: block; padding: 0 10px">         "date_gmt": "2017-02-14T00:00:00",
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        "id": 3153,
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        "id": 36744,
</ins><span class="cx" style="display: block; padding: 0 10px">         "modified": "2017-02-14T00:00:00",
</span><span class="cx" style="display: block; padding: 0 10px">         "modified_gmt": "2017-02-14T00:00:00",
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        "parent": 3152,
-        "slug": "3152-revision-v1",
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        "parent": 36743,
+        "slug": "36743-revision-v1",
</ins><span class="cx" style="display: block; padding: 0 10px">         "guid": {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-            "rendered": "http://example.org/?p=3153"
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+            "rendered": "http://example.org/?p=36744"
</ins><span class="cx" style="display: block; padding: 0 10px">         },
</span><span class="cx" style="display: block; padding: 0 10px">         "title": {
</span><span class="cx" style="display: block; padding: 0 10px">             "rendered": "REST API Client Fixture: Post"
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -4370,7 +5106,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">         "_links": {
</span><span class="cx" style="display: block; padding: 0 10px">             "parent": [
</span><span class="cx" style="display: block; padding: 0 10px">                 {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                    "href": "http://example.org/index.php?rest_route=/wp/v2/posts/3152"
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                    "href": "http://example.org/index.php?rest_route=/wp/v2/posts/36743"
</ins><span class="cx" style="display: block; padding: 0 10px">                 }
</span><span class="cx" style="display: block; padding: 0 10px">             ]
</span><span class="cx" style="display: block; padding: 0 10px">         }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -4402,16 +5138,16 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> mockedApiResponse.postAutosaves = [
</span><span class="cx" style="display: block; padding: 0 10px">     {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        "author": 359,
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        "author": 375,
</ins><span class="cx" style="display: block; padding: 0 10px">         "date": "2017-02-14T00:00:00",
</span><span class="cx" style="display: block; padding: 0 10px">         "date_gmt": "2017-02-14T00:00:00",
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        "id": 3154,
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        "id": 36745,
</ins><span class="cx" style="display: block; padding: 0 10px">         "modified": "2017-02-14T00:00:00",
</span><span class="cx" style="display: block; padding: 0 10px">         "modified_gmt": "2017-02-14T00:00:00",
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        "parent": 3152,
-        "slug": "3152-autosave-v1",
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        "parent": 36743,
+        "slug": "36743-autosave-v1",
</ins><span class="cx" style="display: block; padding: 0 10px">         "guid": {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-            "rendered": "http://example.org/?p=3154"
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+            "rendered": "http://example.org/?p=36745"
</ins><span class="cx" style="display: block; padding: 0 10px">         },
</span><span class="cx" style="display: block; padding: 0 10px">         "title": {
</span><span class="cx" style="display: block; padding: 0 10px">             "rendered": ""
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -4425,7 +5161,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">         "_links": {
</span><span class="cx" style="display: block; padding: 0 10px">             "parent": [
</span><span class="cx" style="display: block; padding: 0 10px">                 {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                    "href": "http://example.org/index.php?rest_route=/wp/v2/posts/3152"
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                    "href": "http://example.org/index.php?rest_route=/wp/v2/posts/36743"
</ins><span class="cx" style="display: block; padding: 0 10px">                 }
</span><span class="cx" style="display: block; padding: 0 10px">             ]
</span><span class="cx" style="display: block; padding: 0 10px">         }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -4433,16 +5169,16 @@
</span><span class="cx" style="display: block; padding: 0 10px"> ];
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> mockedApiResponse.autosave = {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-    "author": 359,
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+    "author": 375,
</ins><span class="cx" style="display: block; padding: 0 10px">     "date": "2017-02-14T00:00:00",
</span><span class="cx" style="display: block; padding: 0 10px">     "date_gmt": "2017-02-14T00:00:00",
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-    "id": 3154,
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+    "id": 36745,
</ins><span class="cx" style="display: block; padding: 0 10px">     "modified": "2017-02-14T00:00:00",
</span><span class="cx" style="display: block; padding: 0 10px">     "modified_gmt": "2017-02-14T00:00:00",
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-    "parent": 3152,
-    "slug": "3152-autosave-v1",
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+    "parent": 36743,
+    "slug": "36743-autosave-v1",
</ins><span class="cx" style="display: block; padding: 0 10px">     "guid": {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        "rendered": "http://example.org/?p=3154"
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        "rendered": "http://example.org/?p=36745"
</ins><span class="cx" style="display: block; padding: 0 10px">     },
</span><span class="cx" style="display: block; padding: 0 10px">     "title": {
</span><span class="cx" style="display: block; padding: 0 10px">         "rendered": ""
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -4607,16 +5343,16 @@
</span><span class="cx" style="display: block; padding: 0 10px">         }
</span><span class="cx" style="display: block; padding: 0 10px">     },
</span><span class="cx" style="display: block; padding: 0 10px">     {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        "author": 359,
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        "author": 375,
</ins><span class="cx" style="display: block; padding: 0 10px">         "date": "2017-02-14T00:00:00",
</span><span class="cx" style="display: block; padding: 0 10px">         "date_gmt": "2017-02-14T00:00:00",
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        "id": 3156,
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        "id": 36747,
</ins><span class="cx" style="display: block; padding: 0 10px">         "modified": "2017-02-14T00:00:00",
</span><span class="cx" style="display: block; padding: 0 10px">         "modified_gmt": "2017-02-14T00:00:00",
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        "parent": 3155,
-        "slug": "3155-revision-v1",
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        "parent": 36746,
+        "slug": "36746-revision-v1",
</ins><span class="cx" style="display: block; padding: 0 10px">         "guid": {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-            "rendered": "http://example.org/?p=3156"
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+            "rendered": "http://example.org/?p=36747"
</ins><span class="cx" style="display: block; padding: 0 10px">         },
</span><span class="cx" style="display: block; padding: 0 10px">         "title": {
</span><span class="cx" style="display: block; padding: 0 10px">             "rendered": "REST API Client Fixture: Page"
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -4630,7 +5366,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">         "_links": {
</span><span class="cx" style="display: block; padding: 0 10px">             "parent": [
</span><span class="cx" style="display: block; padding: 0 10px">                 {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                    "href": "http://example.org/index.php?rest_route=/wp/v2/pages/3155"
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                    "href": "http://example.org/index.php?rest_route=/wp/v2/pages/36746"
</ins><span class="cx" style="display: block; padding: 0 10px">                 }
</span><span class="cx" style="display: block; padding: 0 10px">             ]
</span><span class="cx" style="display: block; padding: 0 10px">         }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -4662,16 +5398,16 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> mockedApiResponse.pageAutosaves = [
</span><span class="cx" style="display: block; padding: 0 10px">     {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        "author": 359,
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        "author": 375,
</ins><span class="cx" style="display: block; padding: 0 10px">         "date": "2017-02-14T00:00:00",
</span><span class="cx" style="display: block; padding: 0 10px">         "date_gmt": "2017-02-14T00:00:00",
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        "id": 3157,
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        "id": 36748,
</ins><span class="cx" style="display: block; padding: 0 10px">         "modified": "2017-02-14T00:00:00",
</span><span class="cx" style="display: block; padding: 0 10px">         "modified_gmt": "2017-02-14T00:00:00",
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        "parent": 3155,
-        "slug": "3155-autosave-v1",
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        "parent": 36746,
+        "slug": "36746-autosave-v1",
</ins><span class="cx" style="display: block; padding: 0 10px">         "guid": {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-            "rendered": "http://example.org/?p=3157"
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+            "rendered": "http://example.org/?p=36748"
</ins><span class="cx" style="display: block; padding: 0 10px">         },
</span><span class="cx" style="display: block; padding: 0 10px">         "title": {
</span><span class="cx" style="display: block; padding: 0 10px">             "rendered": ""
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -4685,7 +5421,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">         "_links": {
</span><span class="cx" style="display: block; padding: 0 10px">             "parent": [
</span><span class="cx" style="display: block; padding: 0 10px">                 {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                    "href": "http://example.org/index.php?rest_route=/wp/v2/pages/3155"
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                    "href": "http://example.org/index.php?rest_route=/wp/v2/pages/36746"
</ins><span class="cx" style="display: block; padding: 0 10px">                 }
</span><span class="cx" style="display: block; padding: 0 10px">             ]
</span><span class="cx" style="display: block; padding: 0 10px">         }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -4693,16 +5429,16 @@
</span><span class="cx" style="display: block; padding: 0 10px"> ];
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> mockedApiResponse.pageAutosave = {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-    "author": 359,
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+    "author": 375,
</ins><span class="cx" style="display: block; padding: 0 10px">     "date": "2017-02-14T00:00:00",
</span><span class="cx" style="display: block; padding: 0 10px">     "date_gmt": "2017-02-14T00:00:00",
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-    "id": 3157,
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+    "id": 36748,
</ins><span class="cx" style="display: block; padding: 0 10px">     "modified": "2017-02-14T00:00:00",
</span><span class="cx" style="display: block; padding: 0 10px">     "modified_gmt": "2017-02-14T00:00:00",
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-    "parent": 3155,
-    "slug": "3155-autosave-v1",
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+    "parent": 36746,
+    "slug": "36746-autosave-v1",
</ins><span class="cx" style="display: block; padding: 0 10px">     "guid": {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        "rendered": "http://example.org/?p=3157"
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        "rendered": "http://example.org/?p=36748"
</ins><span class="cx" style="display: block; padding: 0 10px">     },
</span><span class="cx" style="display: block; padding: 0 10px">     "title": {
</span><span class="cx" style="display: block; padding: 0 10px">         "rendered": ""
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -4898,6 +5634,33 @@
</span><span class="cx" style="display: block; padding: 0 10px">                 }
</span><span class="cx" style="display: block; padding: 0 10px">             ]
</span><span class="cx" style="display: block; padding: 0 10px">         }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+    },
+    "wp_block": {
+        "description": "",
+        "hierarchical": false,
+        "name": "Blocks",
+        "slug": "wp_block",
+        "taxonomies": [],
+        "rest_base": "blocks",
+        "_links": {
+            "collection": [
+                {
+                    "href": "http://example.org/index.php?rest_route=/wp/v2/types"
+                }
+            ],
+            "wp:items": [
+                {
+                    "href": "http://example.org/index.php?rest_route=/wp/v2/blocks"
+                }
+            ],
+            "curies": [
+                {
+                    "name": "wp",
+                    "href": "https://api.w.org/{rel}",
+                    "templated": true
+                }
+            ]
+        }
</ins><span class="cx" style="display: block; padding: 0 10px">     }
</span><span class="cx" style="display: block; padding: 0 10px"> };
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span></span></pre>
</div>
</div>

</body>
</html>