<!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>[44126] trunk: REST API: Introduce Autosaves controller and endpoint.</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/44126">44126</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/44126","name":"Review Commit"}}</script></dd>
<dt style="float: left; width: 6em; font-weight: bold">Author</dt> <dd>desrosj</dd>
<dt style="float: left; width: 6em; font-weight: bold">Date</dt> <dd>2018-12-13 22:41:47 +0000 (Thu, 13 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: Introduce Autosaves controller and endpoint.

- Adds `WP_REST_Autosaves_Controller` which extends `WP_REST_Revisions_Controller`.
- Autosaves endpoint is registered for all post types except attachment because even post types without revisions enabled are expected to autosave.
- Because setting the `DOING_AUTOSAVE` constant pollutes the test suite, autosaves tests are run last. We may want to improve upon this later. 

Also, use a truly impossibly high number in User Controller tests. The number `100`, (or `7777` in `trunk`), could be valid in certain test run configurations. The `REST_TESTS_IMPOSSIBLY_HIGH_NUMBER` constant is impossibly high for this very reason.

Finally, Skip Autosaves controller test for multisite. There's a PHP 5.2 edge case where paths calculated differently, possibly caused by differing version of PHPUnit.

Props adamsilverstein, aduth, azaozz, danielbachhuber, rmccue, danielbachhuber.

Merges <a href="https://core.trac.wordpress.org/changeset/43767">[43767]</a>, <a href="https://core.trac.wordpress.org/changeset/43768">[43768]</a>, <a href="https://core.trac.wordpress.org/changeset/43769">[43769]</a> to trunk.

See <a href="https://core.trac.wordpress.org/ticket/45132">#45132</a>, <a href="https://core.trac.wordpress.org/ticket/45131">#45131</a>.
Fixes <a href="https://core.trac.wordpress.org/ticket/45128">#45128</a>, <a href="https://core.trac.wordpress.org/ticket/43316">#43316</a>.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkphpunitxmldist">trunk/phpunit.xml.dist</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="#trunktestsphpunitmultisitexml">trunk/tests/phpunit/multisite.xml</a></li>
<li><a href="#trunktestsphpunittestsrestapirestschemasetupphp">trunk/tests/phpunit/tests/rest-api/rest-schema-setup.php</a></li>
<li><a href="#trunktestsphpunittestsrestapirestuserscontrollerphp">trunk/tests/phpunit/tests/rest-api/rest-users-controller.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="#trunksrcwpincludesrestapiendpointsclasswprestautosavescontrollerphp">trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-autosaves-controller.php</a></li>
<li><a href="#trunktestsphpunittestsrestapirestautosavescontrollerphp">trunk/tests/phpunit/tests/rest-api/rest-autosaves-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-13 21:11:22 UTC (rev 44125)
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+++ trunk 2018-12-13 22:41:47 UTC (rev 44126)
</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
</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
</del><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-43769
</ins><span class="cx" style="display: block; padding: 0 10px"> /trunk:18512
</span><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of property
</span><a id="trunkphpunitxmldist"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/phpunit.xml.dist</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/phpunit.xml.dist    2018-12-13 21:11:22 UTC (rev 44125)
+++ trunk/phpunit.xml.dist      2018-12-13 22:41:47 UTC (rev 44126)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -6,7 +6,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">         >
</span><span class="cx" style="display: block; padding: 0 10px">     <testsuites>
</span><span class="cx" style="display: block; padding: 0 10px">         <!-- Default test suite to run all tests -->
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        <testsuite>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        <testsuite name="default">
</ins><span class="cx" style="display: block; padding: 0 10px">             <directory suffix=".php">tests/phpunit/tests</directory>
</span><span class="cx" style="display: block; padding: 0 10px">             <exclude>tests/phpunit/tests/actions/closures.php</exclude>
</span><span class="cx" style="display: block; padding: 0 10px">             <exclude>tests/phpunit/tests/image/editor.php</exclude>
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -13,6 +13,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">             <exclude>tests/phpunit/tests/image/editorGd.php</exclude>
</span><span class="cx" style="display: block; padding: 0 10px">             <exclude>tests/phpunit/tests/image/editorImagick.php</exclude>
</span><span class="cx" style="display: block; padding: 0 10px">             <exclude>tests/phpunit/tests/oembed/headers.php</exclude>
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+            <exclude>tests/phpunit/tests/rest-api/rest-autosaves-controller.php</exclude>
</ins><span class="cx" style="display: block; padding: 0 10px">             <file phpVersion="5.3.0">tests/phpunit/tests/actions/closures.php</file>
</span><span class="cx" style="display: block; padding: 0 10px">             <file phpVersion="5.3.0">tests/phpunit/tests/image/editor.php</file>
</span><span class="cx" style="display: block; padding: 0 10px">             <file phpVersion="5.3.0">tests/phpunit/tests/image/editorGd.php</file>
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -19,6 +20,10 @@
</span><span class="cx" style="display: block; padding: 0 10px">             <file phpVersion="5.3.0">tests/phpunit/tests/image/editorImagick.php</file>
</span><span class="cx" style="display: block; padding: 0 10px">             <file phpVersion="5.3.0">tests/phpunit/tests/oembed/headers.php</file>
</span><span class="cx" style="display: block; padding: 0 10px">         </testsuite>
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        <!-- Sets the DOING_AUTOSAVE constant, so needs to be run last -->
+        <testsuite name="restapi-autosave">
+            <file>tests/phpunit/tests/rest-api/rest-autosaves-controller.php</file>
+        </testsuite>
</ins><span class="cx" style="display: block; padding: 0 10px">     </testsuites>
</span><span class="cx" style="display: block; padding: 0 10px">     <groups>
</span><span class="cx" style="display: block; padding: 0 10px">         <exclude>
</span></span></pre></div>
<a id="trunksrcwpincludesrestapiendpointsclasswprestautosavescontrollerphpfromrev43768branches50srcwpincludesrestapiendpointsclasswprestautosavescontrollerphp"></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-autosaves-controller.php (from rev 43768, branches/5.0/src/wp-includes/rest-api/endpoints/class-wp-rest-autosaves-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-autosaves-controller.php                           (rev 0)
+++ trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-autosaves-controller.php     2018-12-13 22:41:47 UTC (rev 44126)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,392 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * REST API: WP_REST_Autosaves_Controller class.
+ *
+ * @package WordPress
+ * @subpackage REST_API
+ * @since 5.0.0
+ */
+
+/**
+ * Core class used to access autosaves via the REST API.
+ *
+ * @since 5.0.0
+ *
+ * @see WP_REST_Controller
+ */
+class WP_REST_Autosaves_Controller extends WP_REST_Revisions_Controller {
+
+       /**
+        * Parent post type.
+        *
+        * @since 5.0.0
+        * @var string
+        */
+       private $parent_post_type;
+
+       /**
+        * Parent post controller.
+        *
+        * @since 5.0.0
+        * @var WP_REST_Controller
+        */
+       private $parent_controller;
+
+       /**
+        * Revision controller.
+        *
+        * @since 5.0.0
+        * @var WP_REST_Controller
+        */
+       private $revisions_controller;
+
+       /**
+        * The base of the parent controller's route.
+        *
+        * @since 5.0.0
+        * @var string
+        */
+       private $parent_base;
+
+       /**
+        * Constructor.
+        *
+        * @since 5.0.0
+        *
+        * @param string $parent_post_type Post type of the parent.
+        */
+       public function __construct( $parent_post_type ) {
+               $this->parent_post_type = $parent_post_type;
+               $post_type_object       = get_post_type_object( $parent_post_type );
+
+               // Ensure that post type-specific controller logic is available.
+               $parent_controller_class = ! empty( $post_type_object->rest_controller_class ) ? $post_type_object->rest_controller_class : 'WP_REST_Posts_Controller';
+
+               $this->parent_controller    = new $parent_controller_class( $post_type_object->name );
+               $this->revisions_controller = new WP_REST_Revisions_Controller( $parent_post_type );
+               $this->rest_namespace       = 'wp/v2';
+               $this->rest_base            = 'autosaves';
+               $this->parent_base          = ! empty( $post_type_object->rest_base ) ? $post_type_object->rest_base : $post_type_object->name;
+       }
+
+       /**
+        * Registers routes for autosaves.
+        *
+        * @since 5.0.0
+        *
+        * @see register_rest_route()
+        */
+       public function register_routes() {
+               register_rest_route(
+                       $this->rest_namespace,
+                       '/' . $this->parent_base . '/(?P<parent>[\d]+)/' . $this->rest_base,
+                       array(
+                               'args'   => array(
+                                       'parent' => array(
+                                               'description' => __( 'The ID for the parent of the object.' ),
+                                               'type'        => 'integer',
+                                       ),
+                               ),
+                               array(
+                                       'methods'             => WP_REST_Server::READABLE,
+                                       'callback'            => array( $this, 'get_items' ),
+                                       'permission_callback' => array( $this->revisions_controller, 'get_items_permissions_check' ),
+                                       'args'                => $this->get_collection_params(),
+                               ),
+                               array(
+                                       'methods'             => WP_REST_Server::CREATABLE,
+                                       'callback'            => array( $this, 'create_item' ),
+                                       'permission_callback' => array( $this, 'create_item_permissions_check' ),
+                                       'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ),
+                               ),
+                               'schema' => array( $this, 'get_public_item_schema' ),
+                       )
+               );
+
+               register_rest_route(
+                       $this->rest_namespace,
+                       '/' . $this->parent_base . '/(?P<parent>[\d]+)/' . $this->rest_base . '/(?P<id>[\d]+)',
+                       array(
+                               'args'   => array(
+                                       'parent' => array(
+                                               'description' => __( 'The ID for the parent of the object.' ),
+                                               'type'        => 'integer',
+                                       ),
+                                       'id'     => array(
+                                               'description' => __( 'The ID for the object.' ),
+                                               'type'        => 'integer',
+                                       ),
+                               ),
+                               array(
+                                       'methods'             => WP_REST_Server::READABLE,
+                                       'callback'            => array( $this, 'get_item' ),
+                                       'permission_callback' => array( $this->revisions_controller, 'get_item_permissions_check' ),
+                                       'args'                => array(
+                                               'context' => $this->get_context_param( array( 'default' => 'view' ) ),
+                                       ),
+                               ),
+                               'schema' => array( $this, 'get_public_item_schema' ),
+                       )
+               );
+
+       }
+
+       /**
+        * Get the parent post.
+        *
+        * @since 5.0.0
+        *
+        * @param int $parent_id Supplied ID.
+        * @return WP_Post|WP_Error Post object if ID is valid, WP_Error otherwise.
+        */
+       protected function get_parent( $parent_id ) {
+               return $this->revisions_controller->get_parent( $parent_id );
+       }
+
+       /**
+        * Checks if a given request has access to create an autosave revision.
+        *
+        * Autosave revisions inherit permissions from the parent post,
+        * check if the current user has permission to edit the post.
+        *
+        * @since 5.0.0
+        *
+        * @param WP_REST_Request $request Full details about the request.
+        * @return true|WP_Error True if the request has access to create the item, WP_Error object otherwise.
+        */
+       public function create_item_permissions_check( $request ) {
+               $id = $request->get_param( 'id' );
+               if ( empty( $id ) ) {
+                       return new WP_Error( 'rest_post_invalid_id', __( 'Invalid item ID.' ), array( 'status' => 404 ) );
+               }
+
+               return $this->parent_controller->update_item_permissions_check( $request );
+       }
+
+       /**
+        * Creates, updates or deletes an autosave revision.
+        *
+        * @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 create_item( $request ) {
+
+               if ( ! defined( 'DOING_AUTOSAVE' ) ) {
+                       define( 'DOING_AUTOSAVE', true );
+               }
+
+               $post = get_post( $request->get_param( 'id' ) );
+
+               if ( is_wp_error( $post ) ) {
+                       return $post;
+               }
+
+               $prepared_post     = $this->parent_controller->prepare_item_for_database( $request );
+               $prepared_post->ID = $post->ID;
+               $user_id           = get_current_user_id();
+
+               if ( ( 'draft' === $post->post_status || 'auto-draft' === $post->post_status ) && $post->post_author == $user_id ) {
+                       // Draft posts for the same author: autosaving updates the post and does not create a revision.
+                       // Convert the post object to an array and add slashes, wp_update_post expects escaped array.
+                       $autosave_id = wp_update_post( wp_slash( (array) $prepared_post ), true );
+               } else {
+                       // Non-draft posts: create or update the post autosave.
+                       $autosave_id = $this->create_post_autosave( (array) $prepared_post );
+               }
+
+               if ( is_wp_error( $autosave_id ) ) {
+                       return $autosave_id;
+               }
+
+               $autosave = get_post( $autosave_id );
+               $request->set_param( 'context', 'edit' );
+
+               $response = $this->prepare_item_for_response( $autosave, $request );
+               $response = rest_ensure_response( $response );
+
+               return $response;
+       }
+
+       /**
+        * Get the autosave, if the ID is valid.
+        *
+        * @since 5.0.0
+        *
+        * @param WP_REST_Request $request Full data about the request.
+        * @return WP_Post|WP_Error Revision post object if ID is valid, WP_Error otherwise.
+        */
+       public function get_item( $request ) {
+               $parent_id = (int) $request->get_param( 'parent' );
+
+               if ( $parent_id <= 0 ) {
+                       return new WP_Error( 'rest_post_invalid_id', __( 'Invalid parent post ID.' ), array( 'status' => 404 ) );
+               }
+
+               $autosave = wp_get_post_autosave( $parent_id );
+
+               if ( ! $autosave ) {
+                       return new WP_Error( 'rest_post_no_autosave', __( 'There is no autosave revision for this post.' ), array( 'status' => 404 ) );
+               }
+
+               $response = $this->prepare_item_for_response( $autosave, $request );
+               return $response;
+       }
+
+       /**
+        * Gets a collection of autosaves using wp_get_post_autosave.
+        *
+        * Contains the user's autosave, for empty if it doesn't exist.
+        *
+        * @since 5.0.0
+        *
+        * @param WP_REST_Request $request Full data about the request.
+        * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
+        */
+       public function get_items( $request ) {
+               $parent = $this->get_parent( $request->get_param( 'parent' ) );
+               if ( is_wp_error( $parent ) ) {
+                       return $parent;
+               }
+
+               $response  = array();
+               $parent_id = $parent->ID;
+               $revisions = wp_get_post_revisions( $parent_id, array( 'check_enabled' => false ) );
+
+               foreach ( $revisions as $revision ) {
+                       if ( false !== strpos( $revision->post_name, "{$parent_id}-autosave" ) ) {
+                               $data       = $this->prepare_item_for_response( $revision, $request );
+                               $response[] = $this->prepare_response_for_collection( $data );
+                       }
+               }
+
+               return rest_ensure_response( $response );
+       }
+
+
+       /**
+        * Retrieves the autosave's schema, conforming to JSON Schema.
+        *
+        * @since 5.0.0
+        *
+        * @return array Item schema data.
+        */
+       public function get_item_schema() {
+               $schema = $this->revisions_controller->get_item_schema();
+
+               $schema['properties']['preview_link'] = array(
+                       'description' => __( 'Preview link for the post.' ),
+                       'type'        => 'string',
+                       'format'      => 'uri',
+                       'context'     => array( 'edit' ),
+                       'readonly'    => true,
+               );
+
+               return $schema;
+       }
+
+       /**
+        * Creates autosave for the specified post.
+        *
+        * From wp-admin/post.php.
+        *
+        * @since 5.0.0
+        *
+        * @param mixed $post_data Associative array containing the post data.
+        * @return mixed The autosave revision ID or WP_Error.
+        */
+       public function create_post_autosave( $post_data ) {
+
+               $post_id = (int) $post_data['ID'];
+               $post    = get_post( $post_id );
+
+               if ( is_wp_error( $post ) ) {
+                       return $post;
+               }
+
+               $user_id = get_current_user_id();
+
+               // Store one autosave per author. If there is already an autosave, overwrite it.
+               $old_autosave = wp_get_post_autosave( $post_id, $user_id );
+
+               if ( $old_autosave ) {
+                       $new_autosave                = _wp_post_revision_data( $post_data, true );
+                       $new_autosave['ID']          = $old_autosave->ID;
+                       $new_autosave['post_author'] = $user_id;
+
+                       // If the new autosave has the same content as the post, delete the autosave.
+                       $autosave_is_different = false;
+
+                       foreach ( array_intersect( array_keys( $new_autosave ), array_keys( _wp_post_revision_fields( $post ) ) ) as $field ) {
+                               if ( normalize_whitespace( $new_autosave[ $field ] ) != normalize_whitespace( $post->$field ) ) {
+                                       $autosave_is_different = true;
+                                       break;
+                               }
+                       }
+
+                       if ( ! $autosave_is_different ) {
+                               wp_delete_post_revision( $old_autosave->ID );
+                               return new WP_Error( 'rest_autosave_no_changes', __( 'There is nothing to save. The autosave and the post content are the same.' ), array( 'status' => 400 ) );
+                       }
+
+                       /**
+                        * This filter is documented in wp-admin/post.php.
+                        */
+                       do_action( 'wp_creating_autosave', $new_autosave );
+
+                       // wp_update_post expects escaped array.
+                       return wp_update_post( wp_slash( $new_autosave ) );
+               }
+
+               // Create the new autosave as a special post revision.
+               return _wp_put_post_revision( $post_data, true );
+       }
+
+       /**
+        * Prepares the revision for the REST response.
+        *
+        * @since 5.0.0
+        *
+        * @param WP_Post         $post    Post revision object.
+        * @param WP_REST_Request $request Request object.
+        *
+        * @return WP_REST_Response Response object.
+        */
+       public function prepare_item_for_response( $post, $request ) {
+
+               $response = $this->revisions_controller->prepare_item_for_response( $post, $request );
+
+               $fields = $this->get_fields_for_response( $request );
+
+               if ( in_array( 'preview_link', $fields, true ) ) {
+                       $parent_id          = wp_is_post_autosave( $post );
+                       $preview_post_id    = false === $parent_id ? $post->ID : $parent_id;
+                       $preview_query_args = array();
+
+                       if ( false !== $parent_id ) {
+                               $preview_query_args['preview_id']    = $parent_id;
+                               $preview_query_args['preview_nonce'] = wp_create_nonce( 'post_preview_' . $parent_id );
+                       }
+
+                       $response->data['preview_link'] = get_preview_post_link( $preview_post_id, $preview_query_args );
+               }
+
+               $context        = ! empty( $request['context'] ) ? $request['context'] : 'view';
+               $response->data = $this->add_additional_fields_to_object( $response->data, $request );
+               $response->data = $this->filter_response_by_context( $response->data, $context );
+
+               /**
+                * Filters a revision returned from the API.
+                *
+                * Allows modification of the revision right before it is returned.
+                *
+                * @since 5.0.0
+                *
+                * @param WP_REST_Response $response The response object.
+                * @param WP_Post          $post     The original revision object.
+                * @param WP_REST_Request  $request  Request used to generate the response.
+                */
+               return apply_filters( 'rest_prepare_autosave', $response, $post, $request );
+       }
+}
</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-13 21:11:22 UTC (rev 44125)
+++ trunk/src/wp-includes/rest-api.php  2018-12-13 22:41:47 UTC (rev 44126)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -193,6 +193,11 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        $revisions_controller = new WP_REST_Revisions_Controller( $post_type->name );
</span><span class="cx" style="display: block; padding: 0 10px">                        $revisions_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">+
+               if ( 'attachment' !== $post_type->name ) {
+                       $autosaves_controller = new WP_REST_Autosaves_Controller( $post_type->name );
+                       $autosaves_controller->register_routes();
+               }
</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">        // Post types.
</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-13 21:11:22 UTC (rev 44125)
+++ trunk/src/wp-settings.php   2018-12-13 22:41:47 UTC (rev 44126)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -230,6 +230,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-post-types-controller.php' );
</span><span class="cx" style="display: block; padding: 0 10px"> require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-post-statuses-controller.php' );
</span><span class="cx" style="display: block; padding: 0 10px"> require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-revisions-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-autosaves-controller.php' );
</ins><span class="cx" style="display: block; padding: 0 10px"> require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-taxonomies-controller.php' );
</span><span class="cx" style="display: block; padding: 0 10px"> require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-terms-controller.php' );
</span><span class="cx" style="display: block; padding: 0 10px"> require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-users-controller.php' );
</span></span></pre></div>
<a id="trunktestsphpunitmultisitexml"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/phpunit/multisite.xml</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/multisite.xml 2018-12-13 21:11:22 UTC (rev 44125)
+++ trunk/tests/phpunit/multisite.xml   2018-12-13 22:41:47 UTC (rev 44126)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -9,12 +9,16 @@
</span><span class="cx" style="display: block; padding: 0 10px">     </php>
</span><span class="cx" style="display: block; padding: 0 10px">     <testsuites>
</span><span class="cx" style="display: block; padding: 0 10px">         <!-- Default test suite to run all tests -->
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        <testsuite>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        <testsuite name="default">
</ins><span class="cx" style="display: block; padding: 0 10px">             <directory suffix=".php">tests</directory>
</span><span class="cx" style="display: block; padding: 0 10px">             <exclude>tests/phpunit/tests/actions/closures.php</exclude>
</span><span class="cx" style="display: block; padding: 0 10px">             <exclude>tests/phpunit/tests/image/editor.php</exclude>
</span><span class="cx" style="display: block; padding: 0 10px">             <exclude>tests/phpunit/tests/image/editorGd.php</exclude>
</span><span class="cx" style="display: block; padding: 0 10px">             <exclude>tests/phpunit/tests/image/editorImagick.php</exclude>
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+            <!-- DOING_AUTOSAVE constant pollutes test suite -->
+            <!-- and there is a weird 5.2 edge case: https://core.trac.wordpress.org/ticket/45132 -->
+            <exclude>tests/rest-api/rest-autosaves-controller.php</exclude>
+            <exclude>tests/phpunit/tests/rest-api/rest-autosaves-controller.php</exclude>
</ins><span class="cx" style="display: block; padding: 0 10px">             <file phpVersion="5.3.0">tests/phpunit/tests/actions/closures.php</file>
</span><span class="cx" style="display: block; padding: 0 10px">             <file phpVersion="5.3.0">tests/phpunit/tests/image/editor.php</file>
</span><span class="cx" style="display: block; padding: 0 10px">             <file phpVersion="5.3.0">tests/phpunit/tests/image/editorGd.php</file>
</span></span></pre></div>
<a id="trunktestsphpunittestsrestapirestautosavescontrollerphpfromrev43768branches50testsphpunittestsrestapirestautosavescontrollerphp"></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-autosaves-controller.php (from rev 43768, branches/5.0/tests/phpunit/tests/rest-api/rest-autosaves-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-autosaves-controller.php                          (rev 0)
+++ trunk/tests/phpunit/tests/rest-api/rest-autosaves-controller.php    2018-12-13 22:41:47 UTC (rev 44126)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,520 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * Unit tests covering WP_REST_Autosaves_Controller functionality.
+ *
+ * @package WordPress
+ * @subpackage REST API
+ */
+
+/**
+ * @group restapi-autosave
+ * @group restapi
+ */
+class WP_Test_REST_Autosaves_Controller extends WP_Test_REST_Post_Type_Controller_Testcase {
+       protected static $post_id;
+       protected static $page_id;
+
+       protected static $autosave_post_id;
+       protected static $autosave_page_id;
+
+       protected static $editor_id;
+       protected static $contributor_id;
+
+       protected function set_post_data( $args = array() ) {
+               $defaults = array(
+                       'title'   => 'Post Title',
+                       'content' => 'Post content',
+                       'excerpt' => 'Post excerpt',
+                       'name'    => 'test',
+                       'author'  => get_current_user_id(),
+               );
+
+               return wp_parse_args( $args, $defaults );
+       }
+
+       protected function check_create_autosave_response( $response ) {
+               $this->assertNotInstanceOf( 'WP_Error', $response );
+               $response = rest_ensure_response( $response );
+               $data     = $response->get_data();
+
+               $this->assertArrayHasKey( 'content', $data );
+               $this->assertArrayHasKey( 'excerpt', $data );
+               $this->assertArrayHasKey( 'title', $data );
+       }
+
+       public static function wpSetUpBeforeClass( $factory ) {
+               self::$post_id = $factory->post->create();
+               self::$page_id = $factory->post->create( array( 'post_type' => 'page' ) );
+
+               self::$editor_id      = $factory->user->create(
+                       array(
+                               'role' => 'editor',
+                       )
+               );
+               self::$contributor_id = $factory->user->create(
+                       array(
+                               'role' => 'contributor',
+                       )
+               );
+
+               wp_set_current_user( self::$editor_id );
+
+               // Create an autosave.
+               self::$autosave_post_id = wp_create_post_autosave(
+                       array(
+                               'post_content' => 'This content is better.',
+                               'post_ID'      => self::$post_id,
+                               'post_type'    => 'post',
+                       )
+               );
+
+               self::$autosave_page_id = wp_create_post_autosave(
+                       array(
+                               'post_content' => 'This content is better.',
+                               'post_ID'      => self::$page_id,
+                               'post_type'    => 'post',
+                       )
+               );
+
+       }
+
+       public static function wpTearDownAfterClass() {
+               // Also deletes revisions.
+               wp_delete_post( self::$post_id, true );
+               wp_delete_post( self::$page_id, true );
+
+               self::delete_user( self::$editor_id );
+               self::delete_user( self::$contributor_id );
+       }
+
+       public function setUp() {
+               parent::setUp();
+               wp_set_current_user( self::$editor_id );
+
+               $this->post_autosave = wp_get_post_autosave( self::$post_id );
+       }
+
+       public function test_register_routes() {
+               $routes = rest_get_server()->get_routes();
+               $this->assertArrayHasKey( '/wp/v2/posts/(?P<parent>[\d]+)/autosaves', $routes );
+               $this->assertArrayHasKey( '/wp/v2/posts/(?P<parent>[\d]+)/autosaves/(?P<id>[\d]+)', $routes );
+               $this->assertArrayHasKey( '/wp/v2/pages/(?P<parent>[\d]+)/autosaves', $routes );
+               $this->assertArrayHasKey( '/wp/v2/pages/(?P<parent>[\d]+)/autosaves/(?P<id>[\d]+)', $routes );
+       }
+
+       public function test_context_param() {
+
+               // Collection.
+               $request  = new WP_REST_Request( 'OPTIONS', '/wp/v2/posts/' . self::$post_id . '/autosaves' );
+               $response = rest_get_server()->dispatch( $request );
+               $data     = $response->get_data();
+               $this->assertEquals( 'view', $data['endpoints'][0]['args']['context']['default'] );
+               $this->assertEqualSets( array( 'view', 'edit', 'embed' ), $data['endpoints'][0]['args']['context']['enum'] );
+
+               // Single.
+               $request  = new WP_REST_Request( 'OPTIONS', '/wp/v2/posts/' . self::$post_id . '/autosaves/' . self::$autosave_post_id );
+               $response = rest_get_server()->dispatch( $request );
+               $data     = $response->get_data();
+               $this->assertEquals( 'view', $data['endpoints'][0]['args']['context']['default'] );
+               $this->assertEqualSets( array( 'view', 'edit', 'embed' ), $data['endpoints'][0]['args']['context']['enum'] );
+       }
+
+       public function test_get_items() {
+               wp_set_current_user( self::$editor_id );
+               $request  = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/autosaves' );
+               $response = rest_get_server()->dispatch( $request );
+               $data     = $response->get_data();
+               $this->assertEquals( 200, $response->get_status() );
+               $this->assertCount( 1, $data );
+
+               $this->assertEquals( self::$autosave_post_id, $data[0]['id'] );
+
+               $this->check_get_autosave_response( $data[0], $this->post_autosave );
+       }
+
+       public function test_get_items_no_permission() {
+               wp_set_current_user( 0 );
+               $request  = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/autosaves' );
+               $response = rest_get_server()->dispatch( $request );
+               $this->assertErrorResponse( 'rest_cannot_read', $response, 401 );
+               wp_set_current_user( self::$contributor_id );
+               $response = rest_get_server()->dispatch( $request );
+               $this->assertErrorResponse( 'rest_cannot_read', $response, 403 );
+       }
+
+       public function test_get_items_missing_parent() {
+               wp_set_current_user( self::$editor_id );
+               $request  = new WP_REST_Request( 'GET', '/wp/v2/posts/' . REST_TESTS_IMPOSSIBLY_HIGH_NUMBER . '/autosaves' );
+               $response = rest_get_server()->dispatch( $request );
+               $this->assertErrorResponse( 'rest_post_invalid_parent', $response, 404 );
+       }
+
+       public function test_get_items_invalid_parent_post_type() {
+               wp_set_current_user( self::$editor_id );
+               $request  = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$page_id . '/autosaves' );
+               $response = rest_get_server()->dispatch( $request );
+               $this->assertErrorResponse( 'rest_post_invalid_parent', $response, 404 );
+       }
+
+       public function test_get_item() {
+               wp_set_current_user( self::$editor_id );
+               $request  = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/autosaves/' . self::$autosave_post_id );
+               $response = rest_get_server()->dispatch( $request );
+               $this->assertEquals( 200, $response->get_status() );
+               $data = $response->get_data();
+
+               $this->check_get_autosave_response( $response, $this->post_autosave );
+               $fields = array(
+                       'author',
+                       'date',
+                       'date_gmt',
+                       'modified',
+                       'modified_gmt',
+                       'guid',
+                       'id',
+                       'parent',
+                       'slug',
+                       'title',
+                       'excerpt',
+                       'content',
+               );
+               $this->assertEqualSets( $fields, array_keys( $data ) );
+               $this->assertSame( self::$editor_id, $data['author'] );
+       }
+
+       public function test_get_item_embed_context() {
+               wp_set_current_user( self::$editor_id );
+               $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/autosaves/' . self::$autosave_post_id );
+               $request->set_param( 'context', 'embed' );
+               $response = rest_get_server()->dispatch( $request );
+               $fields   = array(
+                       'author',
+                       'date',
+                       'id',
+                       'parent',
+                       'slug',
+                       'title',
+                       'excerpt',
+               );
+               $data     = $response->get_data();
+               $this->assertEqualSets( $fields, array_keys( $data ) );
+       }
+
+       public function test_get_item_no_permission() {
+               $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/autosaves/' . self::$autosave_post_id );
+               wp_set_current_user( self::$contributor_id );
+               $response = rest_get_server()->dispatch( $request );
+               $this->assertErrorResponse( 'rest_cannot_read', $response, 403 );
+       }
+
+       public function test_get_item_missing_parent() {
+               wp_set_current_user( self::$editor_id );
+               $request  = new WP_REST_Request( 'GET', '/wp/v2/posts/' . REST_TESTS_IMPOSSIBLY_HIGH_NUMBER . '/autosaves/' . self::$autosave_post_id );
+               $response = rest_get_server()->dispatch( $request );
+               $this->assertErrorResponse( 'rest_post_invalid_parent', $response, 404 );
+
+       }
+
+       public function test_get_item_invalid_parent_post_type() {
+               wp_set_current_user( self::$editor_id );
+               $request  = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$page_id . '/autosaves' );
+               $response = rest_get_server()->dispatch( $request );
+               $this->assertErrorResponse( 'rest_post_invalid_parent', $response, 404 );
+       }
+
+       public function test_delete_item() {
+               // Doesn't exist.
+       }
+
+       public function test_prepare_item() {
+               wp_set_current_user( self::$editor_id );
+               $request  = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/autosaves/' . self::$autosave_post_id );
+               $response = rest_get_server()->dispatch( $request );
+               $this->assertEquals( 200, $response->get_status() );
+               $this->check_get_autosave_response( $response, $this->post_autosave );
+       }
+
+       public function test_get_item_schema() {
+               $request    = new WP_REST_Request( 'OPTIONS', '/wp/v2/posts/' . self::$post_id . '/autosaves' );
+               $response   = rest_get_server()->dispatch( $request );
+               $data       = $response->get_data();
+               $properties = $data['schema']['properties'];
+               $this->assertEquals( 13, count( $properties ) );
+               $this->assertArrayHasKey( 'author', $properties );
+               $this->assertArrayHasKey( 'content', $properties );
+               $this->assertArrayHasKey( 'date', $properties );
+               $this->assertArrayHasKey( 'date_gmt', $properties );
+               $this->assertArrayHasKey( 'excerpt', $properties );
+               $this->assertArrayHasKey( 'guid', $properties );
+               $this->assertArrayHasKey( 'id', $properties );
+               $this->assertArrayHasKey( 'modified', $properties );
+               $this->assertArrayHasKey( 'modified_gmt', $properties );
+               $this->assertArrayHasKey( 'parent', $properties );
+               $this->assertArrayHasKey( 'slug', $properties );
+               $this->assertArrayHasKey( 'title', $properties );
+               $this->assertArrayHasKey( 'preview_link', $properties );
+       }
+
+       public function test_create_item() {
+               wp_set_current_user( self::$editor_id );
+
+               $request = new WP_REST_Request( 'POST', '/wp/v2/posts/' . self::$post_id . '/autosaves' );
+               $request->add_header( 'content-type', 'application/x-www-form-urlencoded' );
+
+               $params = $this->set_post_data(
+                       array(
+                               'id' => self::$post_id,
+                       )
+               );
+               $request->set_body_params( $params );
+               $response = rest_get_server()->dispatch( $request );
+
+               $this->check_create_autosave_response( $response );
+       }
+
+       public function test_update_item() {
+               wp_set_current_user( self::$editor_id );
+               $request = new WP_REST_Request( 'POST', '/wp/v2/posts/' . self::$post_id . '/autosaves' );
+               $request->add_header( 'content-type', 'application/x-www-form-urlencoded' );
+
+               $params = $this->set_post_data(
+                       array(
+                               'id'     => self::$post_id,
+                               'author' => self::$contributor_id,
+                       )
+               );
+
+               $request->set_body_params( $params );
+               $response = rest_get_server()->dispatch( $request );
+
+               $this->check_create_autosave_response( $response );
+       }
+
+       public function test_update_item_nopriv() {
+               wp_set_current_user( self::$contributor_id );
+
+               $request = new WP_REST_Request( 'POST', '/wp/v2/posts/' . self::$post_id . '/autosaves' );
+               $request->add_header( 'content-type', 'application/x-www-form-urlencoded' );
+
+               $params = $this->set_post_data(
+                       array(
+                               'id'     => self::$post_id,
+                               'author' => self::$editor_id,
+                       )
+               );
+
+               $request->set_body_params( $params );
+               $response = rest_get_server()->dispatch( $request );
+
+               $this->assertErrorResponse( 'rest_cannot_edit', $response, 403 );
+       }
+
+       public function test_rest_autosave_published_post() {
+               wp_set_current_user( self::$editor_id );
+
+               $request = new WP_REST_Request( 'POST', '/wp/v2/posts/' . self::$post_id . '/autosaves' );
+               $request->add_header( 'content-type', 'application/json' );
+
+               $current_post = get_post( self::$post_id );
+
+               $autosave_data = $this->set_post_data(
+                       array(
+                               'id'      => self::$post_id,
+                               'content' => 'Updated post \ content',
+                               'excerpt' => $current_post->post_excerpt,
+                               'title'   => $current_post->post_title,
+                       )
+               );
+
+               $request->set_body( wp_json_encode( $autosave_data ) );
+               $response = rest_get_server()->dispatch( $request );
+               $new_data = $response->get_data();
+
+               $this->assertEquals( $current_post->ID, $new_data['parent'] );
+               $this->assertEquals( $current_post->post_title, $new_data['title']['raw'] );
+               $this->assertEquals( $current_post->post_excerpt, $new_data['excerpt']['raw'] );
+
+               // Updated post_content.
+               $this->assertNotEquals( $current_post->post_content, $new_data['content']['raw'] );
+
+               $autosave_post = wp_get_post_autosave( self::$post_id );
+               $this->assertEquals( $autosave_data['title'], $autosave_post->post_title );
+               $this->assertEquals( $autosave_data['content'], $autosave_post->post_content );
+               $this->assertEquals( $autosave_data['excerpt'], $autosave_post->post_excerpt );
+       }
+
+       public function test_rest_autosave_draft_post_same_author() {
+               wp_set_current_user( self::$editor_id );
+
+               $post_data = array(
+                       'post_content' => 'Test post content',
+                       'post_title'   => 'Test post title',
+                       'post_excerpt' => 'Test post excerpt',
+               );
+               $post_id   = wp_insert_post( $post_data );
+
+               $autosave_data = array(
+                       'id'      => $post_id,
+                       'content' => 'Updated post \ content',
+                       'title'   => 'Updated post title',
+               );
+
+               $request = new WP_REST_Request( 'POST', '/wp/v2/posts/' . self::$post_id . '/autosaves' );
+               $request->add_header( 'content-type', 'application/json' );
+               $request->set_body( wp_json_encode( $autosave_data ) );
+
+               $response = rest_get_server()->dispatch( $request );
+               $new_data = $response->get_data();
+               $post     = get_post( $post_id );
+
+               $this->assertEquals( $post_id, $new_data['id'] );
+               // The draft post should be updated.
+               $this->assertEquals( $autosave_data['content'], $new_data['content']['raw'] );
+               $this->assertEquals( $autosave_data['title'], $new_data['title']['raw'] );
+               $this->assertEquals( $autosave_data['content'], $post->post_content );
+               $this->assertEquals( $autosave_data['title'], $post->post_title );
+
+               // Not updated.
+               $this->assertEquals( $post_data['post_excerpt'], $post->post_excerpt );
+
+               wp_delete_post( $post_id );
+       }
+
+       public function test_rest_autosave_draft_post_different_author() {
+               wp_set_current_user( self::$editor_id );
+
+               $post_data = array(
+                       'post_content' => 'Test post content',
+                       'post_title'   => 'Test post title',
+                       'post_excerpt' => 'Test post excerpt',
+                       'post_author'  => self::$editor_id + 1,
+               );
+               $post_id   = wp_insert_post( $post_data );
+
+               $autosave_data = array(
+                       'id'      => $post_id,
+                       'content' => 'Updated post content',
+                       'excerpt' => $post_data['post_excerpt'],
+                       'title'   => $post_data['post_title'],
+               );
+
+               $request = new WP_REST_Request( 'POST', '/wp/v2/posts/' . self::$post_id . '/autosaves' );
+               $request->add_header( 'content-type', 'application/json' );
+               $request->set_body( wp_json_encode( $autosave_data ) );
+
+               $response     = rest_get_server()->dispatch( $request );
+               $new_data     = $response->get_data();
+               $current_post = get_post( $post_id );
+
+               $this->assertEquals( $current_post->ID, $new_data['parent'] );
+
+               // The draft post shouldn't change.
+               $this->assertEquals( $current_post->post_title, $post_data['post_title'] );
+               $this->assertEquals( $current_post->post_content, $post_data['post_content'] );
+               $this->assertEquals( $current_post->post_excerpt, $post_data['post_excerpt'] );
+
+               $autosave_post = wp_get_post_autosave( $post_id );
+
+               // No changes.
+               $this->assertEquals( $current_post->post_title, $autosave_post->post_title );
+               $this->assertEquals( $current_post->post_excerpt, $autosave_post->post_excerpt );
+
+               // Has changes.
+               $this->assertEquals( $autosave_data['content'], $autosave_post->post_content );
+
+               wp_delete_post( $post_id );
+       }
+
+       public function test_get_additional_field_registration() {
+               $schema = array(
+                       'type'        => 'integer',
+                       'description' => 'Some integer of mine',
+                       'enum'        => array( 1, 2, 3, 4 ),
+                       'context'     => array( 'view', 'edit' ),
+               );
+
+               register_rest_field(
+                       'post-revision',
+                       'my_custom_int',
+                       array(
+                               'schema'          => $schema,
+                               'get_callback'    => array( $this, 'additional_field_get_callback' ),
+                               'update_callback' => array( $this, 'additional_field_update_callback' ),
+                       )
+               );
+
+               $request = new WP_REST_Request( 'OPTIONS', '/wp/v2/posts/' . self::$post_id . '/autosaves' );
+
+               $response = rest_get_server()->dispatch( $request );
+               $data     = $response->get_data();
+
+               $this->assertArrayHasKey( 'my_custom_int', $data['schema']['properties'] );
+               $this->assertEquals( $schema, $data['schema']['properties']['my_custom_int'] );
+
+               wp_set_current_user( 1 );
+
+               $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/autosaves/' . self::$autosave_post_id );
+
+               $response = rest_get_server()->dispatch( $request );
+               $this->assertArrayHasKey( 'my_custom_int', $response->data );
+
+               global $wp_rest_additional_fields;
+               $wp_rest_additional_fields = array();
+       }
+
+       public function additional_field_get_callback( $object ) {
+               return get_post_meta( $object['id'], 'my_custom_int', true );
+       }
+
+       public function additional_field_update_callback( $value, $post ) {
+               update_post_meta( $post->ID, 'my_custom_int', $value );
+       }
+
+       protected function check_get_autosave_response( $response, $autosave ) {
+               if ( $response instanceof WP_REST_Response ) {
+                       $links    = $response->get_links();
+                       $response = $response->get_data();
+               } else {
+                       $this->assertArrayHasKey( '_links', $response );
+                       $links = $response['_links'];
+               }
+
+               $this->assertEquals( $autosave->post_author, $response['author'] );
+
+               $rendered_content = apply_filters( 'the_content', $autosave->post_content );
+               $this->assertEquals( $rendered_content, $response['content']['rendered'] );
+
+               $this->assertEquals( mysql_to_rfc3339( $autosave->post_date ), $response['date'] ); //@codingStandardsIgnoreLine
+               $this->assertEquals( mysql_to_rfc3339( $autosave->post_date_gmt ), $response['date_gmt'] ); //@codingStandardsIgnoreLine
+
+               $rendered_guid = apply_filters( 'get_the_guid', $autosave->guid, $autosave->ID );
+               $this->assertEquals( $rendered_guid, $response['guid']['rendered'] );
+
+               $this->assertEquals( $autosave->ID, $response['id'] );
+               $this->assertEquals( mysql_to_rfc3339( $autosave->post_modified ), $response['modified'] ); //@codingStandardsIgnoreLine
+               $this->assertEquals( mysql_to_rfc3339( $autosave->post_modified_gmt ), $response['modified_gmt'] ); //@codingStandardsIgnoreLine
+               $this->assertEquals( $autosave->post_name, $response['slug'] );
+
+               $rendered_title = get_the_title( $autosave->ID );
+               $this->assertEquals( $rendered_title, $response['title']['rendered'] );
+
+               $parent            = get_post( $autosave->post_parent );
+               $parent_controller = new WP_REST_Posts_Controller( $parent->post_type );
+               $parent_object     = get_post_type_object( $parent->post_type );
+               $parent_base       = ! empty( $parent_object->rest_base ) ? $parent_object->rest_base : $parent_object->name;
+               $this->assertEquals( rest_url( '/wp/v2/' . $parent_base . '/' . $autosave->post_parent ), $links['parent'][0]['href'] );
+       }
+
+       public function test_get_item_sets_up_postdata() {
+               wp_set_current_user( self::$editor_id );
+               $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/autosaves/' . self::$autosave_post_id );
+               rest_get_server()->dispatch( $request );
+
+               $post           = get_post();
+               $parent_post_id = wp_is_post_revision( $post->ID );
+
+               $this->assertEquals( $post->ID, self::$autosave_post_id );
+               $this->assertEquals( $parent_post_id, self::$post_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-13 21:11:22 UTC (rev 44125)
+++ trunk/tests/phpunit/tests/rest-api/rest-schema-setup.php    2018-12-13 22:41:47 UTC (rev 44126)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -89,10 +89,14 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        '/wp/v2/posts/(?P<id>[\\d]+)',
</span><span class="cx" style="display: block; padding: 0 10px">                        '/wp/v2/posts/(?P<parent>[\\d]+)/revisions',
</span><span class="cx" style="display: block; padding: 0 10px">                        '/wp/v2/posts/(?P<parent>[\\d]+)/revisions/(?P<id>[\\d]+)',
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        '/wp/v2/posts/(?P<parent>[\\d]+)/autosaves',
+                       '/wp/v2/posts/(?P<parent>[\\d]+)/autosaves/(?P<id>[\\d]+)',
</ins><span class="cx" style="display: block; padding: 0 10px">                         '/wp/v2/pages',
</span><span class="cx" style="display: block; padding: 0 10px">                        '/wp/v2/pages/(?P<id>[\\d]+)',
</span><span class="cx" style="display: block; padding: 0 10px">                        '/wp/v2/pages/(?P<parent>[\\d]+)/revisions',
</span><span class="cx" style="display: block; padding: 0 10px">                        '/wp/v2/pages/(?P<parent>[\\d]+)/revisions/(?P<id>[\\d]+)',
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        '/wp/v2/pages/(?P<parent>[\\d]+)/autosaves',
+                       '/wp/v2/pages/(?P<parent>[\\d]+)/autosaves/(?P<id>[\\d]+)',
</ins><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><span class="cx" style="display: block; padding: 0 10px">                        '/wp/v2/types',
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -161,6 +165,15 @@
</span><span class="cx" style="display: block; padding: 0 10px">                $post_revisions   = array_values( wp_get_post_revisions( $post_id ) );
</span><span class="cx" style="display: block; padding: 0 10px">                $post_revision_id = $post_revisions[ count( $post_revisions ) - 1 ]->ID;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                // Create an autosave.
+               wp_create_post_autosave(
+                       array(
+                               'post_ID'      => $post_id,
+                               'post_content' => 'Autosave post content.',
+                               'post_type'    => 'post',
+                       )
+               );
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 $page_id = $this->factory->post->create(
</span><span class="cx" style="display: block; padding: 0 10px">                        array(
</span><span class="cx" style="display: block; padding: 0 10px">                                'post_type'     => 'page',
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -182,6 +195,15 @@
</span><span class="cx" style="display: block; padding: 0 10px">                $page_revisions   = array_values( wp_get_post_revisions( $page_id ) );
</span><span class="cx" style="display: block; padding: 0 10px">                $page_revision_id = $page_revisions[ count( $page_revisions ) - 1 ]->ID;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                // Create an autosave.
+               wp_create_post_autosave(
+                       array(
+                               'post_ID'      => $page_id,
+                               'post_content' => 'Autosave page content.',
+                               'post_type'    => 'page',
+                       )
+               );
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 $tag_id = $this->factory->tag->create(
</span><span class="cx" style="display: block; padding: 0 10px">                        array(
</span><span class="cx" style="display: block; padding: 0 10px">                                'name'        => 'REST API Client Fixture: Tag',
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -276,6 +298,14 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                'name'  => 'revision',
</span><span class="cx" style="display: block; padding: 0 10px">                        ),
</span><span class="cx" style="display: block; padding: 0 10px">                        array(
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                'route' => '/wp/v2/posts/' . $post_id . '/autosaves',
+                               'name'  => 'postAutosaves',
+                       ),
+                       array(
+                               'route' => '/wp/v2/posts/' . $post_id . '/autosaves/' . $post_revision_id,
+                               'name'  => 'autosave',
+                       ),
+                       array(
</ins><span class="cx" style="display: block; padding: 0 10px">                                 'route' => '/wp/v2/pages',
</span><span class="cx" style="display: block; padding: 0 10px">                                'name'  => 'PagesCollection',
</span><span class="cx" style="display: block; padding: 0 10px">                        ),
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -292,6 +322,14 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                'name'  => 'pageRevision',
</span><span class="cx" style="display: block; padding: 0 10px">                        ),
</span><span class="cx" style="display: block; padding: 0 10px">                        array(
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                'route' => '/wp/v2/pages/' . $page_id . '/autosaves',
+                               'name'  => 'pageAutosaves',
+                       ),
+                       array(
+                               'route' => '/wp/v2/pages/' . $page_id . '/autosaves/' . $page_revision_id,
+                               'name'  => 'pageAutosave',
+                       ),
+                       array(
</ins><span class="cx" style="display: block; padding: 0 10px">                                 'route' => '/wp/v2/media',
</span><span class="cx" style="display: block; padding: 0 10px">                                'name'  => 'MediaCollection',
</span><span class="cx" style="display: block; padding: 0 10px">                        ),
</span></span></pre></div>
<a id="trunktestsphpunittestsrestapirestuserscontrollerphp"></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-users-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-users-controller.php      2018-12-13 21:11:22 UTC (rev 44125)
+++ trunk/tests/phpunit/tests/rest-api/rest-users-controller.php        2018-12-13 22:41:47 UTC (rev 44126)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -896,7 +896,8 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        public function test_get_user_invalid_id() {
</span><span class="cx" style="display: block; padding: 0 10px">                wp_set_current_user( self::$user );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $request  = new WP_REST_Request( 'GET', '/wp/v2/users/7777' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+               $request  = new WP_REST_Request( 'GET', '/wp/v2/users/' . REST_TESTS_IMPOSSIBLY_HIGH_NUMBER );
</ins><span class="cx" style="display: block; padding: 0 10px">                 $response = rest_get_server()->dispatch( $request );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                $this->assertErrorResponse( 'rest_user_invalid_id', $response, 404 );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -2265,7 +2266,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                $this->allow_user_to_manage_multisite();
</span><span class="cx" style="display: block; padding: 0 10px">                wp_set_current_user( self::$user );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $request          = new WP_REST_Request( 'DELETE', '/wp/v2/users/7777' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $request          = new WP_REST_Request( 'DELETE', '/wp/v2/users/' . REST_TESTS_IMPOSSIBLY_HIGH_NUMBER );
</ins><span class="cx" style="display: block; padding: 0 10px">                 $request['force'] = true;
</span><span class="cx" style="display: block; padding: 0 10px">                $request->set_param( 'reassign', false );
</span><span class="cx" style="display: block; padding: 0 10px">                $response = rest_get_server()->dispatch( $request );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -2317,7 +2318,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                $request          = new WP_REST_Request( 'DELETE', sprintf( '/wp/v2/users/%d', $user_id ) );
</span><span class="cx" style="display: block; padding: 0 10px">                $request['force'] = true;
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $request->set_param( 'reassign', 7777 );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $request->set_param( 'reassign', REST_TESTS_IMPOSSIBLY_HIGH_NUMBER );
</ins><span class="cx" style="display: block; padding: 0 10px">                 $response = rest_get_server()->dispatch( $request );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                // Not implemented in multisite.
</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-13 21:11:22 UTC (rev 44125)
+++ trunk/tests/qunit/fixtures/wp-api-generated.js      2018-12-13 22:41:47 UTC (rev 44126)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -850,6 +850,200 @@
</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/posts/(?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"
+                        },
+                        "excerpt": {
+                            "required": false,
+                            "description": "The excerpt for the object.",
+                            "type": "object"
+                        }
+                    }
+                }
+            ]
+        },
+        "/wp/v2/posts/(?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/pages": {
</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">@@ -1456,6 +1650,200 @@
</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/pages/(?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"
+                        },
+                        "excerpt": {
+                            "required": false,
+                            "description": "The excerpt for the object.",
+                            "type": "object"
+                        }
+                    }
+                }
+            ]
+        },
+        "/wp/v2/pages/(?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/media": {
</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">@@ -3850,7 +4238,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">             ],
</span><span class="cx" style="display: block; padding: 0 10px">             "version-history": [
</span><span class="cx" style="display: block; padding: 0 10px">                 {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                    "count": 1,
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                    "count": 2,
</ins><span class="cx" style="display: block; padding: 0 10px">                     "href": "http://example.org/index.php?rest_route=/wp/v2/posts/4/revisions"
</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">@@ -3942,6 +4330,35 @@
</span><span class="cx" style="display: block; padding: 0 10px">             "rendered": "http://example.org/?p=5"
</span><span class="cx" style="display: block; padding: 0 10px">         },
</span><span class="cx" style="display: block; padding: 0 10px">         "title": {
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+            "rendered": ""
+        },
+        "content": {
+            "rendered": "<p>Autosave post content.</p>\n"
+        },
+        "excerpt": {
+            "rendered": ""
+        },
+        "_links": {
+            "parent": [
+                {
+                    "href": "http://example.org/index.php?rest_route=/wp/v2/posts/4"
+                }
+            ]
+        }
+    },
+    {
+        "author": 359,
+        "date": "2017-02-14T00:00:00",
+        "date_gmt": "2017-02-14T00:00:00",
+        "id": 36734,
+        "modified": "2017-02-14T00:00:00",
+        "modified_gmt": "2017-02-14T00:00:00",
+        "parent": 36733,
+        "slug": "36733-revision-v1",
+        "guid": {
+            "rendered": "http://example.org/?p=36734"
+        },
+        "title": {
</ins><span class="cx" style="display: block; padding: 0 10px">             "rendered": "REST API Client Fixture: Post"
</span><span class="cx" style="display: block; padding: 0 10px">         },
</span><span class="cx" style="display: block; padding: 0 10px">         "content": {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -3953,7 +4370,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/4"
</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/36733"
</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">@@ -3983,6 +4400,61 @@
</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">+mockedApiResponse.postAutosaves = [
+    {
+        "author": 359,
+        "date": "2017-02-14T00:00:00",
+        "date_gmt": "2017-02-14T00:00:00",
+        "id": 36735,
+        "modified": "2017-02-14T00:00:00",
+        "modified_gmt": "2017-02-14T00:00:00",
+        "parent": 36733,
+        "slug": "36733-autosave-v1",
+        "guid": {
+            "rendered": "http://example.org/?p=36735"
+        },
+        "title": {
+            "rendered": ""
+        },
+        "content": {
+            "rendered": "<p>Autosave post content.</p>\n"
+        },
+        "excerpt": {
+            "rendered": ""
+        },
+        "_links": {
+            "parent": [
+                {
+                    "href": "http://example.org/index.php?rest_route=/wp/v2/posts/36733"
+                }
+            ]
+        }
+    }
+];
+
+mockedApiResponse.autosave = {
+    "author": 359,
+    "date": "2017-02-14T00:00:00",
+    "date_gmt": "2017-02-14T00:00:00",
+    "id": 36735,
+    "modified": "2017-02-14T00:00:00",
+    "modified_gmt": "2017-02-14T00:00:00",
+    "parent": 36733,
+    "slug": "36733-autosave-v1",
+    "guid": {
+        "rendered": "http://example.org/?p=36735"
+    },
+    "title": {
+        "rendered": ""
+    },
+    "content": {
+        "rendered": "<p>Autosave post content.</p>\n"
+    },
+    "excerpt": {
+        "rendered": ""
+    }
+};
+
</ins><span class="cx" style="display: block; padding: 0 10px"> mockedApiResponse.PagesCollection = [
</span><span class="cx" style="display: block; padding: 0 10px">     {
</span><span class="cx" style="display: block; padding: 0 10px">         "id": 6,
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -4042,7 +4514,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">             ],
</span><span class="cx" style="display: block; padding: 0 10px">             "version-history": [
</span><span class="cx" style="display: block; padding: 0 10px">                 {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                    "count": 1,
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                    "count": 2,
</ins><span class="cx" style="display: block; padding: 0 10px">                     "href": "http://example.org/index.php?rest_route=/wp/v2/pages/6/revisions"
</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">@@ -4118,6 +4590,35 @@
</span><span class="cx" style="display: block; padding: 0 10px">             "rendered": "http://example.org/?p=7"
</span><span class="cx" style="display: block; padding: 0 10px">         },
</span><span class="cx" style="display: block; padding: 0 10px">         "title": {
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+            "rendered": ""
+        },
+        "content": {
+            "rendered": "<p>Autosave page content.</p>\n"
+        },
+        "excerpt": {
+            "rendered": ""
+        },
+        "_links": {
+            "parent": [
+                {
+                    "href": "http://example.org/index.php?rest_route=/wp/v2/pages/6"
+                }
+            ]
+        }
+    },
+    {
+        "author": 359,
+        "date": "2017-02-14T00:00:00",
+        "date_gmt": "2017-02-14T00:00:00",
+        "id": 36737,
+        "modified": "2017-02-14T00:00:00",
+        "modified_gmt": "2017-02-14T00:00:00",
+        "parent": 36736,
+        "slug": "36736-revision-v1",
+        "guid": {
+            "rendered": "http://example.org/?p=36737"
+        },
+        "title": {
</ins><span class="cx" style="display: block; padding: 0 10px">             "rendered": "REST API Client Fixture: Page"
</span><span class="cx" style="display: block; padding: 0 10px">         },
</span><span class="cx" style="display: block; padding: 0 10px">         "content": {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -4129,7 +4630,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/6"
</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/36736"
</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">@@ -4159,6 +4660,61 @@
</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">+mockedApiResponse.pageAutosaves = [
+    {
+        "author": 359,
+        "date": "2017-02-14T00:00:00",
+        "date_gmt": "2017-02-14T00:00:00",
+        "id": 36738,
+        "modified": "2017-02-14T00:00:00",
+        "modified_gmt": "2017-02-14T00:00:00",
+        "parent": 36736,
+        "slug": "36736-autosave-v1",
+        "guid": {
+            "rendered": "http://example.org/?p=36738"
+        },
+        "title": {
+            "rendered": ""
+        },
+        "content": {
+            "rendered": "<p>Autosave page content.</p>\n"
+        },
+        "excerpt": {
+            "rendered": ""
+        },
+        "_links": {
+            "parent": [
+                {
+                    "href": "http://example.org/index.php?rest_route=/wp/v2/pages/36736"
+                }
+            ]
+        }
+    }
+];
+
+mockedApiResponse.pageAutosave = {
+    "author": 359,
+    "date": "2017-02-14T00:00:00",
+    "date_gmt": "2017-02-14T00:00:00",
+    "id": 36738,
+    "modified": "2017-02-14T00:00:00",
+    "modified_gmt": "2017-02-14T00:00:00",
+    "parent": 36736,
+    "slug": "36736-autosave-v1",
+    "guid": {
+        "rendered": "http://example.org/?p=36738"
+    },
+    "title": {
+        "rendered": ""
+    },
+    "content": {
+        "rendered": "<p>Autosave page content.</p>\n"
+    },
+    "excerpt": {
+        "rendered": ""
+    }
+};
+
</ins><span class="cx" style="display: block; padding: 0 10px"> mockedApiResponse.MediaCollection = [
</span><span class="cx" style="display: block; padding: 0 10px">     {
</span><span class="cx" style="display: block; padding: 0 10px">         "id": 8,
</span></span></pre>
</div>
</div>

</body>
</html>