WordPress.org

Make WordPress Core

Ticket #43316: 43316.14.diff

File 43316.14.diff, 50.3 KB (added by azaozz, 4 years ago)
  • src/wp-includes/rest-api.php

     
    192192                if ( post_type_supports( $post_type->name, 'revisions' ) ) {
    193193                        $revisions_controller = new WP_REST_Revisions_Controller( $post_type->name );
    194194                        $revisions_controller->register_routes();
     195                        $controller = new WP_REST_Autosaves_Controller( $post_type->name );
     196                        $controller->register_routes();
    195197                }
    196198        }
    197199
  • src/wp-includes/rest-api/endpoints/class-wp-rest-autosaves-controller.php

     
     1<?php
     2/**
     3 * REST API: WP_REST_Autosaves_Controller class.
     4 *
     5 * @package WordPress
     6 * @subpackage REST_API
     7 * @since 5.0.0
     8 */
     9
     10/**
     11 * Core class used to access autosaves via the REST API.
     12 *
     13 * @since 5.0.0
     14 *
     15 * @see WP_REST_Controller
     16 */
     17class WP_REST_Autosaves_Controller extends WP_REST_Revisions_Controller {
     18
     19        /**
     20         * Parent post type.
     21         *
     22         * @since 5.0.0
     23         * @var string
     24         */
     25        private $parent_post_type;
     26
     27        /**
     28         * Parent controller.
     29         *
     30         * @since 5.0.0
     31         * @var WP_REST_Controller
     32         */
     33        private $parent_controller;
     34
     35        /**
     36         * Parent controller.
     37         *
     38         * @since 5.0.0
     39         * @var WP_REST_Controller
     40         */
     41        private $revision_controller;
     42
     43        /**
     44         * The base of the parent controller's route.
     45         *
     46         * @since 5.0.0
     47         * @var string
     48         */
     49        private $parent_base;
     50
     51        /**
     52         * Constructor.
     53         *
     54         * @since 5.0.0
     55         *
     56         * @param string $parent_post_type Post type of the parent.
     57         */
     58        public function __construct( $parent_post_type ) {
     59                $this->parent_post_type    = $parent_post_type;
     60                $this->parent_controller   = new WP_REST_Posts_Controller( $parent_post_type );
     61                $this->revision_controller = new WP_REST_Revisions_Controller( $parent_post_type );
     62                $this->rest_namespace      = 'wp/v2';
     63                $this->rest_base           = 'autosaves';
     64                $post_type_object          = get_post_type_object( $parent_post_type );
     65                $this->parent_base         = ! empty( $post_type_object->rest_base ) ? $post_type_object->rest_base : $post_type_object->name;
     66        }
     67
     68        /**o
     69         * Registers routes for autosaves.
     70         *
     71         * @since 5.0.0
     72         *
     73         * @see register_rest_route()
     74         */
     75        public function register_routes() {
     76                register_rest_route(
     77                        $this->rest_namespace, '/' . $this->parent_base . '/(?P<parent>[\d]+)/' . $this->rest_base, array(
     78                                'args'   => array(
     79                                        'parent' => array(
     80                                                'description' => __( 'The ID for the parent of the object.' ),
     81                                                'type'        => 'integer',
     82                                        ),
     83                                ),
     84                                array(
     85                                        'methods'             => WP_REST_Server::READABLE,
     86                                        'callback'            => array( $this, 'get_items' ),
     87                                        'permission_callback' => array( $this->revision_controller, 'get_items_permissions_check' ),
     88                                        'args'                => $this->get_collection_params(),
     89                                ),
     90                                array(
     91                                        'methods'             => WP_REST_Server::CREATABLE,
     92                                        'callback'            => array( $this, 'create_item' ),
     93                                        'permission_callback' => array( $this, 'create_item_permissions_check' ),
     94                                        'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ),
     95                                ),
     96                                'schema' => array( $this, 'get_public_item_schema' ),
     97                        )
     98                );
     99
     100                register_rest_route(
     101                        $this->rest_namespace, '/' . $this->parent_base . '/(?P<parent>[\d]+)/' . $this->rest_base . '/(?P<id>[\d]+)', array(
     102                                'args'   => array(
     103                                        'parent' => array(
     104                                                'description' => __( 'The ID for the parent of the object.' ),
     105                                                'type'        => 'integer',
     106                                        ),
     107                                        'id'     => array(
     108                                                'description' => __( 'The ID for the object.' ),
     109                                                'type'        => 'integer',
     110                                        ),
     111                                ),
     112                                array(
     113                                        'methods'             => WP_REST_Server::READABLE,
     114                                        'callback'            => array( $this, 'get_item' ),
     115                                        'permission_callback' => array( $this->revision_controller, 'get_item_permissions_check' ),
     116                                        'args'                => array(
     117                                                'context' => $this->get_context_param( array( 'default' => 'view' ) ),
     118                                        ),
     119                                ),
     120                                'schema' => array( $this, 'get_public_item_schema' ),
     121                        )
     122                );
     123
     124        }
     125
     126        /**
     127         * Get the parent post.
     128         *
     129         * @since 5.0.0
     130         *
     131         * @param int $parent_id Supplied ID.
     132         * @return WP_Post|WP_Error Post object if ID is valid, WP_Error otherwise.
     133         */
     134        protected function get_parent( $parent_id ) {
     135                return $this->revision_controller->get_parent( $parent_id );
     136        }
     137
     138        /**
     139         * Checks if a given request has access to create an autosave revision.
     140         *
     141         * Autosave revisions inherit permissions from the parent post,
     142         * check if the current user has permission to edit the post.
     143         *
     144         * @since 5.0.0
     145         *
     146         * @param WP_REST_Request $request Full details about the request.
     147         * @return true|WP_Error True if the request has access to create the item, WP_Error object otherwise.
     148         */
     149        public function create_item_permissions_check( $request ) {
     150                if ( empty( $request->get_param( 'id' ) ) ) {
     151                        return new WP_Error( 'rest_post_invalid_id', __( 'Invalid item ID.' ), array( 'status' => 404 ) );
     152                }
     153
     154                return $this->parent_controller->update_item_permissions_check( $request );
     155        }
     156
     157        /**
     158         * Creates, updates or deletes an autosave revision.
     159         *
     160         * @since 5.0.0
     161         *
     162         * @param WP_REST_Request $request Full details about the request.
     163         * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
     164         */
     165        public function create_item( $request ) {
     166
     167                if ( ! defined( 'DOING_AUTOSAVE' ) ) {
     168                        define( 'DOING_AUTOSAVE', true );
     169                }
     170
     171                $post = get_post( $request->get_param( 'id' ) );
     172
     173                if ( is_wp_error( $post ) ) {
     174                        return $post;
     175                }
     176
     177                $prepared_post     = $this->parent_controller->prepare_item_for_database( $request );
     178                $prepared_post->ID = $post->ID;
     179                $user_id           = get_current_user_id();
     180
     181                if ( ( 'draft' === $post->post_status || 'auto-draft' === $post->post_status ) && $post->post_author == $user_id ) {
     182                        // Draft posts for the same author: autosaving updates the post and does not create a revision.
     183                        // Convert the post object to an array and add slashes, wp_update_post expects escaped array.
     184                        $autosave_id = wp_update_post( wp_slash( (array) $prepared_post ), true );
     185                } else {
     186                        // Non-draft posts: create or update the post autosave.
     187                        $autosave_id = $this->create_post_autosave( (array) $prepared_post );
     188                }
     189
     190                if ( is_wp_error( $autosave_id ) ) {
     191                        return $autosave_id;
     192                }
     193
     194                $autosave = get_post( $autosave_id );
     195                $request->set_param( 'context', 'edit' );
     196
     197                $response = $this->prepare_item_for_response( $autosave, $request );
     198                $response = rest_ensure_response( $response );
     199
     200                return $response;
     201        }
     202
     203        /**
     204         * Get the autosave, if the ID is valid.
     205         *
     206         * @since 5.0.0
     207         *
     208         * @param WP_REST_Request $request Full data about the request.
     209         * @return WP_Post|WP_Error Revision post object if ID is valid, WP_Error otherwise.
     210         */
     211        public function get_item( $request ) {
     212                $parent_id = (int) $request->get_param( 'parent' );
     213
     214                if ( $parent_id <= 0 ) {
     215                        return new WP_Error( 'rest_post_invalid_id', __( 'Invalid parent post ID.' ), array( 'status' => 404 ) );
     216                }
     217
     218                $autosave = wp_get_post_autosave( $parent_id );
     219
     220                if ( ! $autosave ) {
     221                        return new WP_Error( 'rest_post_no_autosave', __( 'There is no autosave revision for this post.' ), array( 'status' => 404 ) );
     222                }
     223
     224                $response = $this->prepare_item_for_response( $autosave, $request );
     225                return $response;
     226        }
     227
     228        /**
     229         * Gets a collection of autosaves using wp_get_post_autosave.
     230         *
     231         * Contains the user's autosave, for empty if it doesn't exist.
     232         *
     233         * @since 5.0.0
     234         *
     235         * @param WP_REST_Request $request Full data about the request.
     236         * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
     237         */
     238        public function get_items( $request ) {
     239                $parent = $this->get_parent( $request->get_param( 'parent' ) );
     240                if ( is_wp_error( $parent ) ) {
     241                        return $parent;
     242                }
     243
     244                $response  = array();
     245                $parent_id = $parent->ID;
     246                $revisions = wp_get_post_revisions( $parent_id, array( 'check_enabled' => false ) );
     247
     248                foreach ( $revisions as $revision ) {
     249                        if ( false !== strpos( $revision->post_name, "{$parent_id}-autosave" ) ) {
     250                                $data       = $this->prepare_item_for_response( $revision, $request );
     251                                $response[] = $this->prepare_response_for_collection( $data );
     252                        }
     253                }
     254
     255                return rest_ensure_response( $response );
     256        }
     257
     258
     259        /**
     260         * Retrieves the autosave's schema, conforming to JSON Schema.
     261         *
     262         * @since 5.0.0
     263         *
     264         * @return array Item schema data.
     265         */
     266        public function get_item_schema() {
     267                return $this->revision_controller->get_item_schema();
     268        }
     269
     270        /**
     271         * Creates autosave for the specified post.
     272         *
     273         * From wp-admin/post.php.
     274         *
     275         * @since 5.0.0
     276         *
     277         * @param mixed $post_data Associative array containing the post data.
     278         * @return mixed The autosave revision ID or WP_Error.
     279         */
     280        public function create_post_autosave( $post_data ) {
     281
     282                $post_id = (int) $post_data['ID'];
     283                $post    = get_post( $post_id );
     284
     285                if ( is_wp_error( $post ) ) {
     286                        return $post;
     287                }
     288
     289                $user_id = get_current_user_id();
     290
     291                // Store one autosave per author. If there is already an autosave, overwrite it.
     292                $old_autosave = wp_get_post_autosave( $post_id, $user_id );
     293
     294                if ( $old_autosave ) {
     295                        $new_autosave                = _wp_post_revision_data( $post_data, true );
     296                        $new_autosave['ID']          = $old_autosave->ID;
     297                        $new_autosave['post_author'] = $user_id;
     298
     299                        // If the new autosave has the same content as the post, delete the autosave.
     300                        $autosave_is_different = false;
     301
     302                        foreach ( array_intersect( array_keys( $new_autosave ), array_keys( _wp_post_revision_fields( $post ) ) ) as $field ) {
     303                                if ( normalize_whitespace( $new_autosave[ $field ] ) != normalize_whitespace( $post->$field ) ) {
     304                                        $autosave_is_different = true;
     305                                        break;
     306                                }
     307                        }
     308
     309                        if ( ! $autosave_is_different ) {
     310                                wp_delete_post_revision( $old_autosave->ID );
     311                                return new WP_Error( 'rest_autosave_no_changes', __( 'There is nothing to save. The autosave and the post content are the same.' ) );
     312                        }
     313
     314                        /**
     315                         * This filter is documented in wp-admin/post.php.
     316                         */
     317                        do_action( 'wp_creating_autosave', $new_autosave );
     318
     319                        // wp_update_post expects escaped array.
     320                        return wp_update_post( wp_slash( $new_autosave ) );
     321                }
     322
     323                // Create the new autosave as a special post revision.
     324                return _wp_put_post_revision( $post_data, true );
     325        }
     326
     327        /**
     328         * Prepares the revision for the REST response.
     329         *
     330         * @since 5.0.0
     331         *
     332         * @param WP_Post         $post    Post revision object.
     333         * @param WP_REST_Request $request Request object.
     334         *
     335         * @return WP_REST_Response Response object.
     336         */
     337        public function prepare_item_for_response( $post, $request ) {
     338
     339                $response = $this->revision_controller->prepare_item_for_response( $post, $request );
     340
     341                /**
     342                 * Filters a revision returned from the API.
     343                 *
     344                 * Allows modification of the revision right before it is returned.
     345                 *
     346                 * @since 5.0.0
     347                 *
     348                 * @param WP_REST_Response $response The response object.
     349                 * @param WP_Post          $post     The original revision object.
     350                 * @param WP_REST_Request  $request  Request used to generate the response.
     351                 */
     352                return apply_filters( 'rest_prepare_autosave', $response, $post, $request );
     353        }
     354}
  • src/wp-settings.php

    Property changes on: src/wp-includes/rest-api/endpoints/class-wp-rest-autosaves-controller.php
    ___________________________________________________________________
    Added: svn:eol-style
    ## -0,0 +1 ##
    +native
    \ No newline at end of property
     
    230230require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-post-types-controller.php' );
    231231require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-post-statuses-controller.php' );
    232232require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-revisions-controller.php' );
     233require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-autosaves-controller.php' );
    233234require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-taxonomies-controller.php' );
    234235require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-terms-controller.php' );
    235236require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-users-controller.php' );
  • tests/phpunit/tests/rest-api/rest-autosaves-controller.php

     
     1<?php
     2/**
     3 * Unit tests covering WP_REST_Autosaves_Controller functionality.
     4 *
     5 * @package WordPress
     6 * @subpackage REST API
     7 */
     8
     9/**
     10 * @group restapi-autosave
     11 * @group restapi
     12 */
     13class WP_Test_REST_Autosaves_Controller extends WP_Test_REST_Post_Type_Controller_Testcase {
     14        protected static $post_id;
     15        protected static $page_id;
     16
     17        protected static $autosave_post_id;
     18        protected static $autosave_page_id;
     19
     20        protected static $editor_id;
     21        protected static $contributor_id;
     22
     23        protected function set_post_data( $args = array() ) {
     24                $defaults = array(
     25                        'title'   => 'Post Title',
     26                        'content' => 'Post content',
     27                        'excerpt' => 'Post excerpt',
     28                        'name'    => 'test',
     29                        'author'  => get_current_user_id(),
     30                );
     31
     32                return wp_parse_args( $args, $defaults );
     33        }
     34
     35        protected function check_create_autosave_response( $response ) {
     36                $this->assertNotInstanceOf( 'WP_Error', $response );
     37                $response = rest_ensure_response( $response );
     38                $data = $response->get_data();
     39
     40                $this->assertArrayHasKey( 'content', $data );
     41                $this->assertArrayHasKey( 'excerpt', $data );
     42                $this->assertArrayHasKey( 'title', $data );
     43        }
     44
     45        public static function wpSetUpBeforeClass( $factory ) {
     46                self::$post_id = $factory->post->create();
     47                self::$page_id = $factory->post->create( array( 'post_type' => 'page' ) );
     48
     49                self::$editor_id      = $factory->user->create(
     50                        array(
     51                                'role' => 'editor',
     52                        )
     53                );
     54                self::$contributor_id = $factory->user->create(
     55                        array(
     56                                'role' => 'contributor',
     57                        )
     58                );
     59
     60                wp_set_current_user( self::$editor_id );
     61
     62                // Create an autosave.
     63                self::$autosave_post_id = wp_create_post_autosave(
     64                        array(
     65                                'post_content' => 'This content is better.',
     66                                'post_ID'      => self::$post_id,
     67                                'post_type'    => 'post',
     68                        )
     69                );
     70
     71                self::$autosave_page_id = wp_create_post_autosave(
     72                        array(
     73                                'post_content' => 'This content is better.',
     74                                'post_ID'      => self::$page_id,
     75                                'post_type'    => 'post',
     76                        )
     77                );
     78
     79        }
     80
     81        public static function wpTearDownAfterClass() {
     82                // Also deletes revisions.
     83                wp_delete_post( self::$post_id, true );
     84                wp_delete_post( self::$page_id, true );
     85
     86                self::delete_user( self::$editor_id );
     87                self::delete_user( self::$contributor_id );
     88        }
     89
     90        public function setUp() {
     91                parent::setUp();
     92                wp_set_current_user( self::$editor_id );
     93
     94                $this->post_autosave = wp_get_post_autosave( self::$post_id );
     95        }
     96
     97        public function test_register_routes() {
     98                $routes = rest_get_server()->get_routes();
     99                $this->assertArrayHasKey( '/wp/v2/posts/(?P<parent>[\d]+)/autosaves', $routes );
     100                $this->assertArrayHasKey( '/wp/v2/posts/(?P<parent>[\d]+)/autosaves/(?P<id>[\d]+)', $routes );
     101                $this->assertArrayHasKey( '/wp/v2/pages/(?P<parent>[\d]+)/autosaves', $routes );
     102                $this->assertArrayHasKey( '/wp/v2/pages/(?P<parent>[\d]+)/autosaves/(?P<id>[\d]+)', $routes );
     103        }
     104
     105        public function test_context_param() {
     106                // Collection
     107                $request  = new WP_REST_Request( 'OPTIONS', '/wp/v2/posts/' . self::$post_id . '/autosaves' );
     108                $response = rest_get_server()->dispatch( $request );
     109                $data     = $response->get_data();
     110                $this->assertEquals( 'view', $data['endpoints'][0]['args']['context']['default'] );
     111                $this->assertEqualSets( array( 'view', 'edit', 'embed' ), $data['endpoints'][0]['args']['context']['enum'] );
     112                // Single
     113                $request  = new WP_REST_Request( 'OPTIONS', '/wp/v2/posts/' . self::$post_id . '/autosaves/' . self::$autosave_post_id );
     114                $response = rest_get_server()->dispatch( $request );
     115                $data     = $response->get_data();
     116                $this->assertEquals( 'view', $data['endpoints'][0]['args']['context']['default'] );
     117                $this->assertEqualSets( array( 'view', 'edit', 'embed' ), $data['endpoints'][0]['args']['context']['enum'] );   }
     118
     119        public function test_get_items() {
     120                wp_set_current_user( self::$editor_id );
     121                $request  = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/autosaves' );
     122                $response = rest_get_server()->dispatch( $request );
     123                $data     = $response->get_data();
     124                $this->assertEquals( 200, $response->get_status() );
     125                $this->assertCount( 1, $data );
     126
     127                $this->assertEquals( self::$autosave_post_id, $data[0]['id'] );
     128
     129                $this->check_get_autosave_response( $data[0], $this->post_autosave );
     130        }
     131
     132        public function test_get_items_no_permission() {
     133                wp_set_current_user( 0 );
     134                $request  = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/autosaves' );
     135                $response = rest_get_server()->dispatch( $request );
     136                $this->assertErrorResponse( 'rest_cannot_read', $response, 401 );
     137                wp_set_current_user( self::$contributor_id );
     138                $response = rest_get_server()->dispatch( $request );
     139                $this->assertErrorResponse( 'rest_cannot_read', $response, 403 );
     140        }
     141
     142        public function test_get_items_missing_parent() {
     143                wp_set_current_user( self::$editor_id );
     144                $request  = new WP_REST_Request( 'GET', '/wp/v2/posts/' . REST_TESTS_IMPOSSIBLY_HIGH_NUMBER . '/autosaves' );
     145                $response = rest_get_server()->dispatch( $request );
     146                $this->assertErrorResponse( 'rest_post_invalid_parent', $response, 404 );
     147        }
     148
     149        public function test_get_items_invalid_parent_post_type() {
     150                wp_set_current_user( self::$editor_id );
     151                $request  = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$page_id . '/autosaves' );
     152                $response = rest_get_server()->dispatch( $request );
     153                $this->assertErrorResponse( 'rest_post_invalid_parent', $response, 404 );
     154        }
     155
     156        public function test_get_item() {
     157                wp_set_current_user( self::$editor_id );
     158                $request  = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/autosaves/' . self::$autosave_post_id );
     159                $response = rest_get_server()->dispatch( $request );
     160                $this->assertEquals( 200, $response->get_status() );
     161                $data     = $response->get_data();
     162
     163                $this->check_get_autosave_response( $response, $this->post_autosave );
     164                $fields = array(
     165                        'author',
     166                        'date',
     167                        'date_gmt',
     168                        'modified',
     169                        'modified_gmt',
     170                        'guid',
     171                        'id',
     172                        'parent',
     173                        'slug',
     174                        'title',
     175                        'excerpt',
     176                        'content',
     177                );
     178                $this->assertEqualSets( $fields, array_keys( $data ) );
     179                $this->assertSame( self::$editor_id, $data['author'] );
     180        }
     181
     182        public function test_get_item_embed_context() {
     183                wp_set_current_user( self::$editor_id );
     184                $request  = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/autosaves/' . self::$autosave_post_id );
     185                $request->set_param( 'context', 'embed' );
     186                $response = rest_get_server()->dispatch( $request );
     187                $fields   = array(
     188                        'author',
     189                        'date',
     190                        'id',
     191                        'parent',
     192                        'slug',
     193                        'title',
     194                        'excerpt',
     195                );
     196                $data     = $response->get_data();
     197                $this->assertEqualSets( $fields, array_keys( $data ) );
     198        }
     199
     200        public function test_get_item_no_permission() {
     201                $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/autosaves/' . self::$autosave_post_id );
     202                wp_set_current_user( self::$contributor_id );
     203                $response = rest_get_server()->dispatch( $request );
     204                $this->assertErrorResponse( 'rest_cannot_read', $response, 403 );
     205        }
     206
     207        public function test_get_item_missing_parent() {
     208                wp_set_current_user( self::$editor_id );
     209                $request  = new WP_REST_Request( 'GET', '/wp/v2/posts/' . REST_TESTS_IMPOSSIBLY_HIGH_NUMBER . '/autosaves/' . self::$autosave_post_id );
     210                $response = rest_get_server()->dispatch( $request );
     211                $this->assertErrorResponse( 'rest_post_invalid_parent', $response, 404 );
     212
     213        }
     214
     215        public function test_get_item_invalid_parent_post_type() {
     216                wp_set_current_user( self::$editor_id );
     217                $request  = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$page_id . '/autosaves' );
     218                $response = rest_get_server()->dispatch( $request );
     219                $this->assertErrorResponse( 'rest_post_invalid_parent', $response, 404 );
     220        }
     221
     222        public function test_delete_item() {
     223                // Doesn't exist.
     224        }
     225
     226        public function test_prepare_item() {
     227                wp_set_current_user( self::$editor_id );
     228                $request  = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/autosaves/' . self::$autosave_post_id );
     229                $response = rest_get_server()->dispatch( $request );
     230                $this->assertEquals( 200, $response->get_status() );
     231                $this->check_get_autosave_response( $response, $this->post_autosave );
     232        }
     233
     234        public function test_get_item_schema() {
     235                $request    = new WP_REST_Request( 'OPTIONS', '/wp/v2/posts/' . self::$post_id . '/autosaves' );
     236                $response   = rest_get_server()->dispatch( $request );
     237                $data       = $response->get_data();
     238                $properties = $data['schema']['properties'];
     239                $this->assertEquals( 12, count( $properties ) );
     240                $this->assertArrayHasKey( 'author', $properties );
     241                $this->assertArrayHasKey( 'content', $properties );
     242                $this->assertArrayHasKey( 'date', $properties );
     243                $this->assertArrayHasKey( 'date_gmt', $properties );
     244                $this->assertArrayHasKey( 'excerpt', $properties );
     245                $this->assertArrayHasKey( 'guid', $properties );
     246                $this->assertArrayHasKey( 'id', $properties );
     247                $this->assertArrayHasKey( 'modified', $properties );
     248                $this->assertArrayHasKey( 'modified_gmt', $properties );
     249                $this->assertArrayHasKey( 'parent', $properties );
     250                $this->assertArrayHasKey( 'slug', $properties );
     251                $this->assertArrayHasKey( 'title', $properties );
     252        }
     253
     254        public function test_create_item() {
     255                wp_set_current_user( self::$editor_id );
     256
     257                $request  = new WP_REST_Request( 'POST', '/wp/v2/posts/' . self::$post_id . '/autosaves' );
     258                $request->add_header( 'content-type', 'application/x-www-form-urlencoded' );
     259
     260                $params = $this->set_post_data(
     261                        array(
     262                                'id' => self::$post_id,
     263                        )
     264                );
     265                $request->set_body_params( $params );
     266                $response = rest_get_server()->dispatch( $request );
     267
     268                $this->check_create_autosave_response( $response );
     269        }
     270
     271        public function test_update_item() {
     272                wp_set_current_user( self::$editor_id );
     273                $request  = new WP_REST_Request( 'POST', '/wp/v2/posts/' . self::$post_id . '/autosaves' );
     274                $request->add_header( 'content-type', 'application/x-www-form-urlencoded' );
     275
     276                $params = $this->set_post_data(
     277                        array(
     278                                'id'     => self::$post_id,
     279                                'author' => self::$contributor_id,
     280                        )
     281                );
     282
     283                $request->set_body_params( $params );
     284                $response = rest_get_server()->dispatch( $request );
     285
     286                $this->check_create_autosave_response( $response );
     287        }
     288
     289        public function test_update_item_nopriv() {
     290                wp_set_current_user( self::$contributor_id );
     291
     292                $request = new WP_REST_Request( 'POST', '/wp/v2/posts/' . self::$post_id . '/autosaves' );
     293                $request->add_header( 'content-type', 'application/x-www-form-urlencoded' );
     294
     295                $params = $this->set_post_data(
     296                        array(
     297                                'id'     => self::$post_id,
     298                                'author' => self::$editor_id,
     299                        )
     300                );
     301
     302                $request->set_body_params( $params );
     303                $response = rest_get_server()->dispatch( $request );
     304
     305                $this->assertErrorResponse( 'rest_cannot_edit', $response, 403 );
     306        }
     307
     308        public function test_rest_autosave_published_post() {
     309                wp_set_current_user( self::$editor_id );
     310
     311                $request  = new WP_REST_Request( 'POST', '/wp/v2/posts/' . self::$post_id . '/autosaves' );
     312                $request->add_header( 'content-type', 'application/json' );
     313
     314                $current_post = get_post( self::$post_id );
     315
     316                $autosave_data = $this->set_post_data(
     317                        array(
     318                                'id'      => self::$post_id,
     319                                'content' => 'Updated post \ content',
     320                                'excerpt' => $current_post->post_excerpt,
     321                                'title'   => $current_post->post_title,
     322                        )
     323                );
     324
     325                $request->set_body( wp_json_encode( $autosave_data ) );
     326                $response = rest_get_server()->dispatch( $request );
     327                $new_data = $response->get_data();
     328
     329                $this->assertEquals( $current_post->ID, $new_data['parent'] );
     330                $this->assertEquals( $current_post->post_title, $new_data['title']['raw'] );
     331                $this->assertEquals( $current_post->post_excerpt, $new_data['excerpt']['raw'] );
     332                // Updated post_content
     333                $this->assertNotEquals( $current_post->post_content, $new_data['content']['raw'] );
     334
     335                $autosave_post = wp_get_post_autosave( self::$post_id );
     336                $this->assertEquals( $autosave_data['title'], $autosave_post->post_title );
     337                $this->assertEquals( $autosave_data['content'], $autosave_post->post_content );
     338                $this->assertEquals( $autosave_data['excerpt'], $autosave_post->post_excerpt );
     339        }
     340
     341        public function test_rest_autosave_draft_post_same_author() {
     342                wp_set_current_user( self::$editor_id );
     343
     344                $post_data = array(
     345                        'post_content' => 'Test post content',
     346                        'post_title'   => 'Test post title',
     347                        'post_excerpt' => 'Test post excerpt',
     348                );
     349                $post_id = wp_insert_post( $post_data );
     350
     351                $autosave_data = array(
     352                        'id' => $post_id,
     353                        'content' => 'Updated post \ content',
     354                        'title'   => 'Updated post title',
     355                );
     356
     357                $request  = new WP_REST_Request( 'POST', '/wp/v2/posts/' . self::$post_id . '/autosaves' );
     358                $request->add_header( 'content-type', 'application/json' );
     359                $request->set_body( wp_json_encode( $autosave_data ) );
     360
     361                $response = rest_get_server()->dispatch( $request );
     362                $new_data = $response->get_data();
     363                $post = get_post( $post_id );
     364
     365                $this->assertEquals( $post_id, $new_data['id'] );
     366                // The draft post should be updated.
     367                $this->assertEquals( $autosave_data['content'], $new_data['content']['raw'] );
     368                $this->assertEquals( $autosave_data['title'], $new_data['title']['raw'] );
     369                $this->assertEquals( $autosave_data['content'], $post->post_content );
     370                $this->assertEquals( $autosave_data['title'], $post->post_title );
     371
     372                // Not updated.
     373                $this->assertEquals( $post_data['post_excerpt'], $post->post_excerpt );
     374
     375                wp_delete_post( $post_id );
     376        }
     377
     378        public function test_rest_autosave_draft_post_different_author() {
     379                wp_set_current_user( self::$editor_id );
     380
     381                $post_data = array(
     382                        'post_content' => 'Test post content',
     383                        'post_title'   => 'Test post title',
     384                        'post_excerpt' => 'Test post excerpt',
     385                        'post_author'  => self::$editor_id + 1,
     386                );
     387                $post_id = wp_insert_post( $post_data );
     388
     389                $autosave_data = array(
     390                        'id' => $post_id,
     391                        'content' => 'Updated post content',
     392                        'excerpt' => $post_data['post_excerpt'],
     393                        'title'   => $post_data['post_title'],
     394                );
     395
     396                $request  = new WP_REST_Request( 'POST', '/wp/v2/posts/' . self::$post_id . '/autosaves' );
     397                $request->add_header( 'content-type', 'application/json' );
     398                $request->set_body( wp_json_encode( $autosave_data ) );
     399
     400                $response = rest_get_server()->dispatch( $request );
     401                $new_data = $response->get_data();
     402                $current_post = get_post( $post_id );
     403
     404                $this->assertEquals( $current_post->ID, $new_data['parent'] );
     405
     406                // The draft post shouldn't change.
     407                $this->assertEquals( $current_post->post_title, $post_data['post_title'] );
     408                $this->assertEquals( $current_post->post_content, $post_data['post_content'] );
     409                $this->assertEquals( $current_post->post_excerpt, $post_data['post_excerpt'] );
     410
     411                $autosave_post = wp_get_post_autosave( $post_id );
     412
     413                // No changes
     414                $this->assertEquals( $current_post->post_title, $autosave_post->post_title );
     415                $this->assertEquals( $current_post->post_excerpt, $autosave_post->post_excerpt );
     416
     417                // Has changes
     418                $this->assertEquals( $autosave_data['content'], $autosave_post->post_content );
     419
     420                wp_delete_post( $post_id );
     421        }
     422
     423        public function test_get_additional_field_registration() {
     424                $schema = array(
     425                        'type'        => 'integer',
     426                        'description' => 'Some integer of mine',
     427                        'enum'        => array( 1, 2, 3, 4 ),
     428                        'context'     => array( 'view', 'edit' ),
     429                );
     430
     431                register_rest_field(
     432                        'post-revision', 'my_custom_int', array(
     433                                'schema'          => $schema,
     434                                'get_callback'    => array( $this, 'additional_field_get_callback' ),
     435                                'update_callback' => array( $this, 'additional_field_update_callback' ),
     436                        )
     437                );
     438
     439                $request = new WP_REST_Request( 'OPTIONS', '/wp/v2/posts/' . self::$post_id . '/autosaves' );
     440
     441                $response = rest_get_server()->dispatch( $request );
     442                $data     = $response->get_data();
     443
     444                $this->assertArrayHasKey( 'my_custom_int', $data['schema']['properties'] );
     445                $this->assertEquals( $schema, $data['schema']['properties']['my_custom_int'] );
     446
     447                wp_set_current_user( 1 );
     448
     449                $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/autosaves/' . self::$autosave_post_id );
     450
     451                $response = rest_get_server()->dispatch( $request );
     452                $this->assertArrayHasKey( 'my_custom_int', $response->data );
     453
     454                global $wp_rest_additional_fields;
     455                $wp_rest_additional_fields = array();
     456        }
     457
     458        public function additional_field_get_callback( $object ) {
     459                return get_post_meta( $object['id'], 'my_custom_int', true );
     460        }
     461
     462        public function additional_field_update_callback( $value, $post ) {
     463                update_post_meta( $post->ID, 'my_custom_int', $value );
     464        }
     465
     466        protected function check_get_autosave_response( $response, $autosave ) {
     467                if ( $response instanceof WP_REST_Response ) {
     468                        $links    = $response->get_links();
     469                        $response = $response->get_data();
     470                } else {
     471                        $this->assertArrayHasKey( '_links', $response );
     472                        $links = $response['_links'];
     473                }
     474
     475                $this->assertEquals( $autosave->post_author, $response['author'] );
     476
     477                $rendered_content = apply_filters( 'the_content', $autosave->post_content );
     478                $this->assertEquals( $rendered_content, $response['content']['rendered'] );
     479
     480                $this->assertEquals( mysql_to_rfc3339( $autosave->post_date ), $response['date'] );
     481                $this->assertEquals( mysql_to_rfc3339( $autosave->post_date_gmt ), $response['date_gmt'] );
     482
     483                $rendered_guid = apply_filters( 'get_the_guid', $autosave->guid, $autosave->ID );
     484                $this->assertEquals( $rendered_guid, $response['guid']['rendered'] );
     485
     486                $this->assertEquals( $autosave->ID, $response['id'] );
     487                $this->assertEquals( mysql_to_rfc3339( $autosave->post_modified ), $response['modified'] );
     488                $this->assertEquals( mysql_to_rfc3339( $autosave->post_modified_gmt ), $response['modified_gmt'] );
     489                $this->assertEquals( $autosave->post_name, $response['slug'] );
     490
     491                $rendered_title = get_the_title( $autosave->ID );
     492                $this->assertEquals( $rendered_title, $response['title']['rendered'] );
     493
     494                $parent            = get_post( $autosave->post_parent );
     495                $parent_controller = new WP_REST_Posts_Controller( $parent->post_type );
     496                $parent_object     = get_post_type_object( $parent->post_type );
     497                $parent_base       = ! empty( $parent_object->rest_base ) ? $parent_object->rest_base : $parent_object->name;
     498                $this->assertEquals( rest_url( '/wp/v2/' . $parent_base . '/' . $autosave->post_parent ), $links['parent'][0]['href'] );
     499        }
     500
     501        public function test_get_item_sets_up_postdata() {
     502                wp_set_current_user( self::$editor_id );
     503                $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/autosaves/' . self::$autosave_post_id );
     504                rest_get_server()->dispatch( $request );
     505
     506                $post           = get_post();
     507                $parent_post_id = wp_is_post_revision( $post->ID );
     508
     509                $this->assertEquals( $post->ID, self::$autosave_post_id );
     510                $this->assertEquals( $parent_post_id, self::$post_id );
     511        }
     512
     513}
  • tests/phpunit/tests/rest-api/rest-schema-setup.php

     
    8989                        '/wp/v2/posts/(?P<id>[\\d]+)',
    9090                        '/wp/v2/posts/(?P<parent>[\\d]+)/revisions',
    9191                        '/wp/v2/posts/(?P<parent>[\\d]+)/revisions/(?P<id>[\\d]+)',
     92                        '/wp/v2/posts/(?P<parent>[\\d]+)/autosaves',
     93                        '/wp/v2/posts/(?P<parent>[\\d]+)/autosaves/(?P<id>[\\d]+)',
    9294                        '/wp/v2/pages',
    9395                        '/wp/v2/pages/(?P<id>[\\d]+)',
    9496                        '/wp/v2/pages/(?P<parent>[\\d]+)/revisions',
    9597                        '/wp/v2/pages/(?P<parent>[\\d]+)/revisions/(?P<id>[\\d]+)',
     98                        '/wp/v2/pages/(?P<parent>[\\d]+)/autosaves',
     99                        '/wp/v2/pages/(?P<parent>[\\d]+)/autosaves/(?P<id>[\\d]+)',
    96100                        '/wp/v2/media',
    97101                        '/wp/v2/media/(?P<id>[\\d]+)',
    98102                        '/wp/v2/types',
     
    159163                $post_revisions   = array_values( wp_get_post_revisions( $post_id ) );
    160164                $post_revision_id = $post_revisions[ count( $post_revisions ) - 1 ]->ID;
    161165
     166                // Create an autosave.
     167                wp_create_post_autosave(
     168                        array(
     169                                'post_ID'      => $post_id,
     170                                'post_content' => 'Autosave post content.',
     171                                'post_type'    => 'post',
     172                        )
     173                );
     174
    162175                $page_id = $this->factory->post->create(
    163176                        array(
    164177                                'post_type'     => 'page',
     
    180193                $page_revisions   = array_values( wp_get_post_revisions( $page_id ) );
    181194                $page_revision_id = $page_revisions[ count( $page_revisions ) - 1 ]->ID;
    182195
     196                // Create an autosave.
     197                wp_create_post_autosave(
     198                        array(
     199                                'post_ID'      => $page_id,
     200                                'post_content' => 'Autosave page content.',
     201                                'post_type'    => 'page',
     202                        )
     203                );
     204
    183205                $tag_id = $this->factory->tag->create(
    184206                        array(
    185207                                'name'        => 'REST API Client Fixture: Tag',
     
    272294                                'name'  => 'revision',
    273295                        ),
    274296                        array(
     297                                'route' => '/wp/v2/posts/' . $post_id . '/autosaves',
     298                                'name'  => 'postAutosaves',
     299                        ),
     300                        array(
     301                                'route' => '/wp/v2/posts/' . $post_id . '/autosaves/' . $post_revision_id,
     302                                'name'  => 'autosave',
     303                        ),
     304                        array(
    275305                                'route' => '/wp/v2/pages',
    276306                                'name'  => 'PagesCollection',
    277307                        ),
     
    288318                                'name'  => 'pageRevision',
    289319                        ),
    290320                        array(
     321                                'route' => '/wp/v2/pages/' . $page_id . '/autosaves',
     322                                'name'  => 'pageAutosaves',
     323                        ),
     324                        array(
     325                                'route' => '/wp/v2/pages/' . $page_id . '/autosaves/' . $page_revision_id,
     326                                'name'  => 'pageAutosave',
     327                        ),
     328                        array(
    291329                                'route' => '/wp/v2/media',
    292330                                'name'  => 'MediaCollection',
    293331                        ),
  • tests/qunit/fixtures/wp-api-generated.js

     
    787787                }
    788788            ]
    789789        },
     790        "/wp/v2/posts/(?P<parent>[\\d]+)/autosaves": {
     791            "namespace": "wp/v2",
     792            "methods": [
     793                "GET",
     794                "POST"
     795            ],
     796            "endpoints": [
     797                {
     798                    "methods": [
     799                        "GET"
     800                    ],
     801                    "args": {
     802                        "parent": {
     803                            "required": false,
     804                            "description": "The ID for the parent of the object.",
     805                            "type": "integer"
     806                        },
     807                        "context": {
     808                            "required": false,
     809                            "default": "view",
     810                            "enum": [
     811                                "view",
     812                                "embed",
     813                                "edit"
     814                            ],
     815                            "description": "Scope under which the request is made; determines fields present in response.",
     816                            "type": "string"
     817                        }
     818                    }
     819                },
     820                {
     821                    "methods": [
     822                        "POST"
     823                    ],
     824                    "args": {
     825                        "parent": {
     826                            "required": false,
     827                            "description": "The ID for the parent of the object.",
     828                            "type": "integer"
     829                        },
     830                        "author": {
     831                            "required": false,
     832                            "description": "The ID for the author of the object.",
     833                            "type": "integer"
     834                        },
     835                        "date": {
     836                            "required": false,
     837                            "description": "The date the object was published, in the site's timezone.",
     838                            "type": "string"
     839                        },
     840                        "date_gmt": {
     841                            "required": false,
     842                            "description": "The date the object was published, as GMT.",
     843                            "type": "string"
     844                        },
     845                        "id": {
     846                            "required": false,
     847                            "description": "Unique identifier for the object.",
     848                            "type": "integer"
     849                        },
     850                        "modified": {
     851                            "required": false,
     852                            "description": "The date the object was last modified, in the site's timezone.",
     853                            "type": "string"
     854                        },
     855                        "modified_gmt": {
     856                            "required": false,
     857                            "description": "The date the object was last modified, as GMT.",
     858                            "type": "string"
     859                        },
     860                        "slug": {
     861                            "required": false,
     862                            "description": "An alphanumeric identifier for the object unique to its type.",
     863                            "type": "string"
     864                        },
     865                        "title": {
     866                            "required": false,
     867                            "description": "The title for the object.",
     868                            "type": "object"
     869                        },
     870                        "content": {
     871                            "required": false,
     872                            "description": "The content for the object.",
     873                            "type": "object"
     874                        },
     875                        "excerpt": {
     876                            "required": false,
     877                            "description": "The excerpt for the object.",
     878                            "type": "object"
     879                        }
     880                    }
     881                }
     882            ]
     883        },
     884        "/wp/v2/posts/(?P<parent>[\\d]+)/autosaves/(?P<id>[\\d]+)": {
     885            "namespace": "wp/v2",
     886            "methods": [
     887                "GET"
     888            ],
     889            "endpoints": [
     890                {
     891                    "methods": [
     892                        "GET"
     893                    ],
     894                    "args": {
     895                        "parent": {
     896                            "required": false,
     897                            "description": "The ID for the parent of the object.",
     898                            "type": "integer"
     899                        },
     900                        "id": {
     901                            "required": false,
     902                            "description": "The ID for the object.",
     903                            "type": "integer"
     904                        },
     905                        "context": {
     906                            "required": false,
     907                            "default": "view",
     908                            "enum": [
     909                                "view",
     910                                "embed",
     911                                "edit"
     912                            ],
     913                            "description": "Scope under which the request is made; determines fields present in response.",
     914                            "type": "string"
     915                        }
     916                    }
     917                }
     918            ]
     919        },
    790920        "/wp/v2/pages": {
    791921            "namespace": "wp/v2",
    792922            "methods": [
     
    13291459                }
    13301460            ]
    13311461        },
     1462        "/wp/v2/pages/(?P<parent>[\\d]+)/autosaves": {
     1463            "namespace": "wp/v2",
     1464            "methods": [
     1465                "GET",
     1466                "POST"
     1467            ],
     1468            "endpoints": [
     1469                {
     1470                    "methods": [
     1471                        "GET"
     1472                    ],
     1473                    "args": {
     1474                        "parent": {
     1475                            "required": false,
     1476                            "description": "The ID for the parent of the object.",
     1477                            "type": "integer"
     1478                        },
     1479                        "context": {
     1480                            "required": false,
     1481                            "default": "view",
     1482                            "enum": [
     1483                                "view",
     1484                                "embed",
     1485                                "edit"
     1486                            ],
     1487                            "description": "Scope under which the request is made; determines fields present in response.",
     1488                            "type": "string"
     1489                        }
     1490                    }
     1491                },
     1492                {
     1493                    "methods": [
     1494                        "POST"
     1495                    ],
     1496                    "args": {
     1497                        "parent": {
     1498                            "required": false,
     1499                            "description": "The ID for the parent of the object.",
     1500                            "type": "integer"
     1501                        },
     1502                        "author": {
     1503                            "required": false,
     1504                            "description": "The ID for the author of the object.",
     1505                            "type": "integer"
     1506                        },
     1507                        "date": {
     1508                            "required": false,
     1509                            "description": "The date the object was published, in the site's timezone.",
     1510                            "type": "string"
     1511                        },
     1512                        "date_gmt": {
     1513                            "required": false,
     1514                            "description": "The date the object was published, as GMT.",
     1515                            "type": "string"
     1516                        },
     1517                        "id": {
     1518                            "required": false,
     1519                            "description": "Unique identifier for the object.",
     1520                            "type": "integer"
     1521                        },
     1522                        "modified": {
     1523                            "required": false,
     1524                            "description": "The date the object was last modified, in the site's timezone.",
     1525                            "type": "string"
     1526                        },
     1527                        "modified_gmt": {
     1528                            "required": false,
     1529                            "description": "The date the object was last modified, as GMT.",
     1530                            "type": "string"
     1531                        },
     1532                        "slug": {
     1533                            "required": false,
     1534                            "description": "An alphanumeric identifier for the object unique to its type.",
     1535                            "type": "string"
     1536                        },
     1537                        "title": {
     1538                            "required": false,
     1539                            "description": "The title for the object.",
     1540                            "type": "object"
     1541                        },
     1542                        "content": {
     1543                            "required": false,
     1544                            "description": "The content for the object.",
     1545                            "type": "object"
     1546                        },
     1547                        "excerpt": {
     1548                            "required": false,
     1549                            "description": "The excerpt for the object.",
     1550                            "type": "object"
     1551                        }
     1552                    }
     1553                }
     1554            ]
     1555        },
     1556        "/wp/v2/pages/(?P<parent>[\\d]+)/autosaves/(?P<id>[\\d]+)": {
     1557            "namespace": "wp/v2",
     1558            "methods": [
     1559                "GET"
     1560            ],
     1561            "endpoints": [
     1562                {
     1563                    "methods": [
     1564                        "GET"
     1565                    ],
     1566                    "args": {
     1567                        "parent": {
     1568                            "required": false,
     1569                            "description": "The ID for the parent of the object.",
     1570                            "type": "integer"
     1571                        },
     1572                        "id": {
     1573                            "required": false,
     1574                            "description": "The ID for the object.",
     1575                            "type": "integer"
     1576                        },
     1577                        "context": {
     1578                            "required": false,
     1579                            "default": "view",
     1580                            "enum": [
     1581                                "view",
     1582                                "embed",
     1583                                "edit"
     1584                            ],
     1585                            "description": "Scope under which the request is made; determines fields present in response.",
     1586                            "type": "string"
     1587                        }
     1588                    }
     1589                }
     1590            ]
     1591        },
    13321592        "/wp/v2/media": {
    13331593            "namespace": "wp/v2",
    13341594            "methods": [
     
    36783938            "rendered": "http://example.org/?p=4"
    36793939        },
    36803940        "title": {
     3941            "rendered": ""
     3942        },
     3943        "content": {
     3944            "rendered": "<p>Autosave post content.</p>\n"
     3945        },
     3946        "excerpt": {
     3947            "rendered": ""
     3948        },
     3949        "_links": {
     3950            "parent": [
     3951                {
     3952                    "href": "http://example.org/index.php?rest_route=/wp/v2/posts/3"
     3953                }
     3954            ]
     3955        }
     3956    },
     3957    {
     3958        "author": 2,
     3959        "date": "2017-02-14T00:00:00",
     3960        "date_gmt": "2017-02-14T00:00:00",
     3961        "id": 4,
     3962        "modified": "2017-02-14T00:00:00",
     3963        "modified_gmt": "2017-02-14T00:00:00",
     3964        "parent": 3,
     3965        "slug": "3-revision-v1",
     3966        "guid": {
     3967            "rendered": "http://example.org/?p=4"
     3968        },
     3969        "title": {
    36813970            "rendered": "REST API Client Fixture: Post"
    36823971        },
    36833972        "content": {
     
    37194008    }
    37204009};
    37214010
     4011mockedApiResponse.postAutosaves = [
     4012    {
     4013        "author": 2,
     4014        "date": "2017-02-14T00:00:00",
     4015        "date_gmt": "2017-02-14T00:00:00",
     4016        "id": 5,
     4017        "modified": "2017-02-14T00:00:00",
     4018        "modified_gmt": "2017-02-14T00:00:00",
     4019        "parent": 3,
     4020        "slug": "3-autosave-v1",
     4021        "guid": {
     4022            "rendered": "http://example.org/?p=5"
     4023        },
     4024        "title": {
     4025            "rendered": ""
     4026        },
     4027        "content": {
     4028            "rendered": "<p>Autosave post content.</p>\n"
     4029        },
     4030        "excerpt": {
     4031            "rendered": ""
     4032        },
     4033        "_links": {
     4034            "parent": [
     4035                {
     4036                    "href": "http://example.org/index.php?rest_route=/wp/v2/posts/3"
     4037                }
     4038            ]
     4039        }
     4040    }
     4041];
     4042
     4043mockedApiResponse.autosave = {
     4044    "author": 2,
     4045    "date": "2017-02-14T00:00:00",
     4046    "date_gmt": "2017-02-14T00:00:00",
     4047    "id": 5,
     4048    "modified": "2017-02-14T00:00:00",
     4049    "modified_gmt": "2017-02-14T00:00:00",
     4050    "parent": 3,
     4051    "slug": "3-autosave-v1",
     4052    "guid": {
     4053        "rendered": "http://example.org/?p=5"
     4054    },
     4055    "title": {
     4056        "rendered": ""
     4057    },
     4058    "content": {
     4059        "rendered": "<p>Autosave post content.</p>\n"
     4060    },
     4061    "excerpt": {
     4062        "rendered": ""
     4063    }
     4064};
     4065
    37224066mockedApiResponse.PagesCollection = [
    37234067    {
    37244068        "id": 5,
     
    38474191            "rendered": "http://example.org/?p=6"
    38484192        },
    38494193        "title": {
     4194            "rendered": ""
     4195        },
     4196        "content": {
     4197            "rendered": "<p>Autosave page content.</p>\n"
     4198        },
     4199        "excerpt": {
     4200            "rendered": ""
     4201        },
     4202        "_links": {
     4203            "parent": [
     4204                {
     4205                    "href": "http://example.org/index.php?rest_route=/wp/v2/pages/5"
     4206                }
     4207            ]
     4208        }
     4209    },
     4210    {
     4211        "author": 2,
     4212        "date": "2017-02-14T00:00:00",
     4213        "date_gmt": "2017-02-14T00:00:00",
     4214        "id": 7,
     4215        "modified": "2017-02-14T00:00:00",
     4216        "modified_gmt": "2017-02-14T00:00:00",
     4217        "parent": 6,
     4218        "slug": "6-revision-v1",
     4219        "guid": {
     4220            "rendered": "http://example.org/?p=7"
     4221        },
     4222        "title": {
    38504223            "rendered": "REST API Client Fixture: Page"
    38514224        },
    38524225        "content": {
     
    38584231        "_links": {
    38594232            "parent": [
    38604233                {
    3861                     "href": "http://example.org/index.php?rest_route=/wp/v2/pages/5"
     4234                    "href": "http://example.org/index.php?rest_route=/wp/v2/pages/6"
    38624235                }
    38634236            ]
    38644237        }
     
    38884261    }
    38894262};
    38904263
     4264mockedApiResponse.pageAutosaves = [
     4265    {
     4266        "author": 2,
     4267        "date": "2017-02-14T00:00:00",
     4268        "date_gmt": "2017-02-14T00:00:00",
     4269        "id": 8,
     4270        "modified": "2017-02-14T00:00:00",
     4271        "modified_gmt": "2017-02-14T00:00:00",
     4272        "parent": 6,
     4273        "slug": "6-autosave-v1",
     4274        "guid": {
     4275            "rendered": "http://example.org/?p=8"
     4276        },
     4277        "title": {
     4278            "rendered": ""
     4279        },
     4280        "content": {
     4281            "rendered": "<p>Autosave page content.</p>\n"
     4282        },
     4283        "excerpt": {
     4284            "rendered": ""
     4285        },
     4286        "_links": {
     4287            "parent": [
     4288                {
     4289                    "href": "http://example.org/index.php?rest_route=/wp/v2/pages/6"
     4290                }
     4291            ]
     4292        }
     4293    }
     4294];
     4295
     4296mockedApiResponse.pageAutosave = {
     4297    "author": 2,
     4298    "date": "2017-02-14T00:00:00",
     4299    "date_gmt": "2017-02-14T00:00:00",
     4300    "id": 8,
     4301    "modified": "2017-02-14T00:00:00",
     4302    "modified_gmt": "2017-02-14T00:00:00",
     4303    "parent": 6,
     4304    "slug": "6-autosave-v1",
     4305    "guid": {
     4306        "rendered": "http://example.org/?p=8"
     4307    },
     4308    "title": {
     4309        "rendered": ""
     4310    },
     4311    "content": {
     4312        "rendered": "<p>Autosave page content.</p>\n"
     4313    },
     4314    "excerpt": {
     4315        "rendered": ""
     4316    }
     4317};
     4318
    38914319mockedApiResponse.MediaCollection = [
    38924320    {
    38934321        "id": 7,