Make WordPress Core

Ticket #43316: 43316.10.diff

File 43316.10.diff, 55.9 KB (added by azaozz, 7 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                                'schema' => array( $this, 'get_public_item_schema' ),
     91                        )
     92                );
     93
     94                register_rest_route(
     95                        $this->rest_namespace, '/' . $this->parent_base . '/(?P<parent>[\d]+)/' . $this->rest_base . '/(?P<id>[\d]+)', array(
     96                                'args'   => array(
     97                                        'parent' => array(
     98                                                'description' => __( 'The ID for the parent of the object.' ),
     99                                                'type'        => 'integer',
     100                                        ),
     101                                        'id'     => array(
     102                                                'description' => __( 'The ID for the object.' ),
     103                                                'type'        => 'integer',
     104                                        ),
     105                                ),
     106                                array(
     107                                        'methods'             => WP_REST_Server::READABLE,
     108                                        'callback'            => array( $this, 'get_item' ),
     109                                        'permission_callback' => array( $this->revision_controller, 'get_item_permissions_check' ),
     110                                        'args'                => array(
     111                                                'context' => $this->get_context_param( array( 'default' => 'view' ) ),
     112                                        ),
     113                                ),
     114                                array(
     115                                        'methods'             => WP_REST_Server::EDITABLE,
     116                                        'callback'            => array( $this, 'update_item' ),
     117                                        'permission_callback' => array( $this->parent_controller, 'update_item_permissions_check' ),
     118                                        'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
     119                                ),
     120                                array(
     121                                        'methods'             => WP_REST_Server::DELETABLE,
     122                                        'callback'            => array( $this, 'delete_item' ),
     123                                        'permission_callback' => array( $this->revision_controller, 'delete_item_permissions_check' ),
     124                                        'args'                => array(
     125                                                'force' => array(
     126                                                        'type'        => 'boolean',
     127                                                        'default'     => false,
     128                                                        'description' => __( 'Required to be true, as autosaves do not support trashing.' ),
     129                                                ),
     130                                        ),
     131                                ),
     132                                'schema' => array( $this, 'get_public_item_schema' ),
     133                        )
     134                );
     135
     136        }
     137
     138        /**
     139         * Get the parent post, if the ID is valid.
     140         *
     141         * @since 4.7.2
     142         *
     143         * @param int $id Supplied ID.
     144         * @return WP_Post|WP_Error Post object if ID is valid, WP_Error otherwise.
     145         */
     146        protected function get_parent( $parent ) {
     147                return $this->revision_controller->get_parent( $parent );
     148        }
     149
     150        /**
     151         * Create a revision when the autosave content is significantly different.
     152         *
     153         * If the autosave content is significantly different from the current post or the previous autosave,
     154         * create a revision from the old data.
     155         *
     156         * @since 5.0.0
     157         *
     158         * @param WP_Post $post          The current post or the previous autosave revision.
     159         * @param array   $autosave_data The autosave data.
     160         */
     161        protected function create_revision_for_autosave( $post, $autosave_data ) {
     162
     163                if ( ! wp_revisions_enabled( $post ) ) {
     164                        return;
     165                }
     166
     167                $post_data = get_object_vars( $post );
     168               
     169                if ( $post_data['post_type'] === 'revision' ) {
     170                        // If the old post is a revision, need to merge it with the actual post.
     171                        $parent_post = $this->get_parent( $post_data['post_parent'] );
     172                        foreach ( array_keys( _wp_post_revision_fields( $parent_post ) ) as $field ) {
     173                                if ( isset( $post_data[ $field ] ) ) {
     174                                        $parent_post->$field = $post_data[ $field ];
     175                                }
     176                        }
     177                        $post_data = get_object_vars( $parent_post );
     178                }
     179               
     180                $old_length = strlen( $post_data['post_content'] );
     181
     182                if ( $old_length > 1000 ) {
     183                        $difference = 250;
     184                } elseif ( $old_length > 500 ) {
     185                        $difference = (int) $old_length * 0.2;
     186                } elseif ( $old_length > 50 ) {
     187                        $difference = (int) $old_length * 0.3;
     188                } else {
     189                        $difference = (int) $old_length * 0.5;
     190                }
     191
     192                $size_diff = strlen( $autosave_data['content'] ) - $old_length;
     193                $create_revision   = absint( $size_diff ) > $difference;
     194                $revision_id = 0;
     195
     196                /**
     197                 * Filter whether a revision is created when an autosave is made via the REST API.
     198                 *
     199                 * @since 5.0.0
     200                 *
     201                 * @param bool    $create_revision Create a revision?
     202                 * @param WP_Post $post            The current post data.
     203                 * @param int     $size_diff       The calculated autosave difference.
     204                 */
     205                if ( apply_filters( 'wp_create_revision_for_autosave', $create_revision, $post_data, $size_diff ) ) {
     206                        _wp_put_post_revision( $post_data );
     207                }
     208        }
     209
     210        /**
     211         * Updates, deletes or creates an autosave revision.
     212         *
     213         * @since 5.0.0
     214         *
     215         * @param WP_REST_Request $request Full details about the request.
     216         * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
     217         */
     218        public function update_item( $request ) {
     219
     220                if ( ! defined( 'DOING_AUTOSAVE' ) ) {
     221                        define( 'DOING_AUTOSAVE', true );
     222                }
     223
     224                $post = get_post( $request->get_param( 'id' ) );
     225
     226                if ( is_wp_error( $post ) ) {
     227                        return $post;
     228                }
     229
     230                $prepared_post     = $this->parent_controller->prepare_item_for_database( $request );
     231                $prepared_post->ID = $post->ID;
     232                $user_id           = get_current_user_id();
     233
     234                if ( ( 'draft' === $post->post_status || 'auto-draft' === $post->post_status ) && $post->post_author == $user_id ) {
     235                        // Optionaly create a revision if the autosave data is significantly different.
     236                        // This protects the user from errors when editing, accidental deletes, etc.
     237                        $this->create_revision_for_autosave( $post, (array) $prepared_post );
     238                        // Draft posts for the same author: autosaving updates the post and does not create a revision.
     239                        // Convert the post object to an array and add slashes, wp_update_post expects escaped array.
     240                        $autosave_id = wp_update_post( wp_slash( (array) $prepared_post ), true );
     241                } else {
     242                        // Non-draft posts: create or update the post autosave.
     243                        $autosave_id = $this->create_post_autosave( (array) $prepared_post );
     244                }
     245
     246                if ( is_wp_error( $autosave_id ) ) {
     247                        return $autosave_id;
     248                }
     249
     250                $autosave = get_post( $autosave_id );
     251                $request->set_param( 'context', 'edit' );
     252
     253                $response = $this->prepare_item_for_response( $autosave, $request );
     254                $response = rest_ensure_response( $response );
     255
     256                return $response;
     257        }
     258
     259        /**
     260         * Get the autosave, if the ID is valid.
     261         *
     262         * @since 5.0.0
     263         *
     264         * @param WP_REST_Request $request Full data about the request.
     265         * @return WP_Post|WP_Error Revision post object if ID is valid, WP_Error otherwise.
     266         */
     267        public function get_item( $request ) {
     268                $parent_id = (int) $request->get_param( 'parent' );
     269
     270                if ( $parent_id <= 0 ) {
     271                        return new WP_Error( 'rest_post_invalid_id', __( 'Invalid parent post ID.' ), array( 'status' => 404 ) );
     272                }
     273
     274                $autosave = wp_get_post_autosave( $parent_id );
     275
     276                if ( ! $autosave ) {
     277                        return new WP_Error( 'rest_post_no_autosave', __( 'There is no autosave revision for this post.' ), array( 'status' => 404 ) );
     278                }
     279
     280                $response = $this->prepare_item_for_response( $autosave, $request );
     281                return $response;
     282        }
     283
     284        /**
     285         * Gets a collection of autosaves using wp_get_post_autosave.
     286         *
     287         * Contains the user's autosave, for empty if it doesn't exist.
     288         *
     289         * @since 5.0.0
     290         *
     291         * @param WP_REST_Request $request Full data about the request.
     292         * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
     293         */
     294        public function get_items( $request ) {
     295                $parent = $this->get_parent( $request->get_param( 'parent' ) );
     296                if ( is_wp_error( $parent ) ) {
     297                        return $parent;
     298                }
     299
     300                $response  = array();
     301                $parent_id = $parent->ID;
     302                $revisions = wp_get_post_revisions( $parent_id, array( 'check_enabled' => false ) );
     303
     304                foreach ( $revisions as $revision ) {
     305                        if ( false !== strpos( $revision->post_name, "{$parent_id}-autosave" ) ) {
     306                                $data       = $this->prepare_item_for_response( $revision, $request );
     307                                $response[] = $this->prepare_response_for_collection( $data );
     308                        }
     309                }
     310
     311                return rest_ensure_response( $response );
     312        }
     313
     314
     315        /**
     316         * Retrieves the autosave's schema, conforming to JSON Schema.
     317         *
     318         * @since 5.0.0
     319         *
     320         * @return array Item schema data.
     321         */
     322        public function get_item_schema() {
     323                return $this->revision_controller->get_item_schema();
     324        }
     325
     326        /**
     327         * Creates autosave for the specified post.
     328         *
     329         * From wp-admin/post.php.
     330         *
     331         * @since 5.0.0
     332         *
     333         * @param mixed $post_data Associative array containing the post data.
     334         * @return mixed The autosave revision ID or WP_Error.
     335         */
     336        public function create_post_autosave( $post_data ) {
     337
     338                $post_id = (int) $post_data['ID'];
     339                $post    = get_post( $post_id );
     340
     341                if ( is_wp_error( $post ) ) {
     342                        return $post;
     343                }
     344
     345                $user_id = get_current_user_id();
     346
     347                // Store one autosave per author. If there is already an autosave, overwrite it.
     348                $old_autosave = wp_get_post_autosave( $post_id, $user_id );
     349
     350                if ( $old_autosave ) {
     351                        $new_autosave                = _wp_post_revision_data( $post_data, true );
     352                        $new_autosave['ID']          = $old_autosave->ID;
     353                        $new_autosave['post_author'] = $user_id;
     354
     355                        // If the new autosave has the same content as the post, delete the autosave.
     356                        $autosave_is_different = false;
     357
     358                        foreach ( array_intersect( array_keys( $new_autosave ), array_keys( _wp_post_revision_fields( $post ) ) ) as $field ) {
     359                                if ( normalize_whitespace( $new_autosave[ $field ] ) != normalize_whitespace( $post->$field ) ) {
     360                                        $autosave_is_different = true;
     361                                        break;
     362                                }
     363                        }
     364
     365                        if ( ! $autosave_is_different ) {
     366                                wp_delete_post_revision( $old_autosave->ID );
     367                                return new WP_Error( 'rest_autosave_no_changes', __( 'There is nothing to save. The autosave and the post content are the same.' ) );
     368                        }
     369
     370                        /**
     371                         * This filter is documented in wp-admin/post.php.
     372                         */
     373                        do_action( 'wp_creating_autosave', $new_autosave );
     374
     375                        // Optionally create a post revision if the new autosave data is significantly different.
     376                        $this->create_revision_for_autosave( $old_autosave, $new_autosave );
     377
     378                        // wp_update_post expects escaped array.
     379                        return wp_update_post( wp_slash( $new_autosave ) );
     380                }
     381
     382                // Create the new autosave as a special post revision.
     383                return _wp_put_post_revision( $post_data, true );
     384        }
     385
     386        /**
     387         * Prepares the revision for the REST response.
     388         *
     389         * @since 5.0.0
     390         *
     391         * @param WP_Post         $post    Post revision object.
     392         * @param WP_REST_Request $request Request object.
     393         *
     394         * @return WP_REST_Response Response object.
     395         */
     396        public function prepare_item_for_response( $post, $request ) {
     397
     398                $response = $this->revision_controller->prepare_item_for_response( $post, $request );
     399
     400                /**
     401                 * Filters a revision returned from the API.
     402                 *
     403                 * Allows modification of the revision right before it is returned.
     404                 *
     405                 * @since 5.0.0
     406                 *
     407                 * @param WP_REST_Response $response The response object.
     408                 * @param WP_Post          $post     The original revision object.
     409                 * @param WP_REST_Request  $request  Request used to generate the response.
     410                 */
     411                return apply_filters( 'rest_prepare_autosave', $response, $post, $request );
     412        }
     413}
  • 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                wp_set_current_user( self::$editor_id );
     224                $request = new WP_REST_Request( 'DELETE', '/wp/v2/posts/' . self::$post_id . '/autosaves/' . self::$autosave_post_id );
     225                $request->set_param( 'force', true );
     226                $response = rest_get_server()->dispatch( $request );
     227                $this->assertEquals( 200, $response->get_status() );
     228                $this->assertNull( get_post( self::$autosave_post_id ) );
     229        }
     230
     231        public function test_delete_item_no_trash() {
     232                wp_set_current_user( self::$editor_id );
     233
     234                $request  = new WP_REST_Request( 'DELETE', '/wp/v2/posts/' . self::$post_id . '/autosaves/' . self::$autosave_post_id );
     235                $response = rest_get_server()->dispatch( $request );
     236                $this->assertErrorResponse( 'rest_trash_not_supported', $response, 501 );
     237
     238                $request->set_param( 'force', 'false' );
     239                $response = rest_get_server()->dispatch( $request );
     240                $this->assertErrorResponse( 'rest_trash_not_supported', $response, 501 );
     241
     242                // Ensure the revision still exists
     243                $this->assertNotNull( get_post( self::$autosave_post_id ) );
     244        }
     245
     246        public function test_delete_item_no_permission() {
     247                wp_set_current_user( self::$contributor_id );
     248                $request  = new WP_REST_Request( 'DELETE', '/wp/v2/posts/' . self::$post_id . '/autosaves/' . self::$autosave_post_id );
     249                $response = rest_get_server()->dispatch( $request );
     250                $this->assertErrorResponse( 'rest_cannot_read', $response, 403 );
     251        }
     252
     253        public function test_prepare_item() {
     254                wp_set_current_user( self::$editor_id );
     255                $request  = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/autosaves/' . self::$autosave_post_id );
     256                $response = rest_get_server()->dispatch( $request );
     257                $this->assertEquals( 200, $response->get_status() );
     258                $this->check_get_autosave_response( $response, $this->post_autosave );
     259        }
     260
     261        public function test_get_item_schema() {
     262                $request    = new WP_REST_Request( 'OPTIONS', '/wp/v2/posts/' . self::$post_id . '/autosaves' );
     263                $response   = rest_get_server()->dispatch( $request );
     264                $data       = $response->get_data();
     265                $properties = $data['schema']['properties'];
     266                $this->assertEquals( 12, count( $properties ) );
     267                $this->assertArrayHasKey( 'author', $properties );
     268                $this->assertArrayHasKey( 'content', $properties );
     269                $this->assertArrayHasKey( 'date', $properties );
     270                $this->assertArrayHasKey( 'date_gmt', $properties );
     271                $this->assertArrayHasKey( 'excerpt', $properties );
     272                $this->assertArrayHasKey( 'guid', $properties );
     273                $this->assertArrayHasKey( 'id', $properties );
     274                $this->assertArrayHasKey( 'modified', $properties );
     275                $this->assertArrayHasKey( 'modified_gmt', $properties );
     276                $this->assertArrayHasKey( 'parent', $properties );
     277                $this->assertArrayHasKey( 'slug', $properties );
     278                $this->assertArrayHasKey( 'title', $properties );
     279        }
     280
     281        public function test_create_item() {
     282                wp_set_current_user( self::$editor_id );
     283
     284                $request  = new WP_REST_Request( 'POST', '/wp/v2/posts/' . self::$post_id . '/autosaves/' . self::$post_id );
     285                $request->add_header( 'content-type', 'application/x-www-form-urlencoded' );
     286                $params = $this->set_post_data();
     287                $request->set_body_params( $params );
     288                $response = rest_get_server()->dispatch( $request );
     289
     290                $this->check_create_autosave_response( $response );
     291        }
     292
     293        public function test_update_item() {
     294                wp_set_current_user( self::$editor_id );
     295                $request  = new WP_REST_Request( 'POST', '/wp/v2/posts/' . self::$post_id . '/autosaves/' . self::$post_id );
     296                $request->add_header( 'content-type', 'application/x-www-form-urlencoded' );
     297
     298                $params = $this->set_post_data(
     299                        array(
     300                                'author' => self::$contributor_id,
     301                        )
     302                );
     303
     304                $request->set_body_params( $params );
     305                $response = rest_get_server()->dispatch( $request );
     306
     307                $this->check_create_autosave_response( $response );
     308        }
     309
     310        public function test_update_item_nopriv() {
     311                wp_set_current_user( self::$contributor_id );
     312
     313                $request  = new WP_REST_Request( 'POST', '/wp/v2/posts/' . self::$post_id . '/autosaves/' . self::$post_id );
     314                $request->add_header( 'content-type', 'application/x-www-form-urlencoded' );
     315
     316                $params = $this->set_post_data(
     317                        array(
     318                                'author' => self::$editor_id,
     319                        )
     320                );
     321
     322                $request->set_body_params( $params );
     323                $response = rest_get_server()->dispatch( $request );
     324
     325                $this->assertErrorResponse( 'rest_cannot_edit', $response, 403 );
     326        }
     327
     328        public function test_rest_autosave_published_post() {
     329                wp_set_current_user( self::$editor_id );
     330
     331                $request  = new WP_REST_Request( 'POST', '/wp/v2/posts/' . self::$post_id . '/autosaves/' . self::$post_id );
     332                $request->add_header( 'content-type', 'application/json' );
     333
     334                $current_post = get_post( self::$post_id );
     335
     336                $autosave_data = $this->set_post_data(
     337                        array(
     338                                'id' => self::$post_id,
     339                                'content' => 'Updated post \ content',
     340                                'excerpt' => $current_post->post_excerpt,
     341                                'title'   => $current_post->post_title,
     342                        )
     343                );
     344
     345                $request->set_body( wp_json_encode( $autosave_data ) );
     346                $response = rest_get_server()->dispatch( $request );
     347                $new_data = $response->get_data();
     348
     349                $this->assertEquals( $current_post->ID, $new_data['parent'] );
     350                $this->assertEquals( $current_post->post_title, $new_data['title']['raw'] );
     351                $this->assertEquals( $current_post->post_excerpt, $new_data['excerpt']['raw'] );
     352                // Updated post_content
     353                $this->assertNotEquals( $current_post->post_content, $new_data['content']['raw'] );
     354
     355                $autosave_post = wp_get_post_autosave( self::$post_id );
     356                $this->assertEquals( $autosave_data['title'], $autosave_post->post_title );
     357                $this->assertEquals( $autosave_data['content'], $autosave_post->post_content );
     358                $this->assertEquals( $autosave_data['excerpt'], $autosave_post->post_excerpt );
     359        }
     360
     361        public function test_rest_autosave_draft_post_same_author() {
     362                wp_set_current_user( self::$editor_id );
     363
     364                $post_data = array(
     365                        'post_content' => 'Test post content',
     366                        'post_title'   => 'Test post title',
     367                        'post_excerpt' => 'Test post excerpt',
     368                );
     369                $post_id = wp_insert_post( $post_data );
     370
     371                $autosave_data = array(
     372                        'id' => $post_id,
     373                        'content' => 'Updated post \ content',
     374                        'title'   => 'Updated post title',
     375                );
     376
     377                $request  = new WP_REST_Request( 'POST', '/wp/v2/posts/' . self::$post_id . '/autosaves/' . self::$post_id );
     378                $request->add_header( 'content-type', 'application/json' );
     379                $request->set_body( wp_json_encode( $autosave_data ) );
     380
     381                $response = rest_get_server()->dispatch( $request );
     382                $new_data = $response->get_data();
     383                $post = get_post( $post_id );
     384
     385                $this->assertEquals( $post_id, $new_data['id'] );
     386                // The draft post should be updated.
     387                $this->assertEquals( $autosave_data['content'], $new_data['content']['raw'] );
     388                $this->assertEquals( $autosave_data['title'], $new_data['title']['raw'] );
     389                $this->assertEquals( $autosave_data['content'], $post->post_content );
     390                $this->assertEquals( $autosave_data['title'], $post->post_title );
     391
     392                // Not updated.
     393                $this->assertEquals( $post_data['post_excerpt'], $post->post_excerpt );
     394
     395                wp_delete_post( $post_id );
     396        }
     397
     398        public function test_rest_autosave_draft_post_different_author() {
     399                wp_set_current_user( self::$editor_id );
     400
     401                $post_data = array(
     402                        'post_content' => 'Test post content',
     403                        'post_title'   => 'Test post title',
     404                        'post_excerpt' => 'Test post excerpt',
     405                        'post_author'  => self::$editor_id + 1,
     406                );
     407                $post_id = wp_insert_post( $post_data );
     408
     409                $autosave_data = array(
     410                        'id' => $post_id,
     411                        'content' => 'Updated post content',
     412                        'excerpt' => $post_data['post_excerpt'],
     413                        'title'   => $post_data['post_title'],
     414                );
     415
     416                $request  = new WP_REST_Request( 'POST', '/wp/v2/posts/' . self::$post_id . '/autosaves/' . self::$post_id );
     417                $request->add_header( 'content-type', 'application/json' );
     418                $request->set_body( wp_json_encode( $autosave_data ) );
     419
     420                $response = rest_get_server()->dispatch( $request );
     421                $new_data = $response->get_data();
     422                $current_post = get_post( $post_id );
     423
     424                $this->assertEquals( $current_post->ID, $new_data['parent'] );
     425
     426                // The draft post shouldn't change.
     427                $this->assertEquals( $current_post->post_title, $post_data['post_title'] );
     428                $this->assertEquals( $current_post->post_content, $post_data['post_content'] );
     429                $this->assertEquals( $current_post->post_excerpt, $post_data['post_excerpt'] );
     430
     431                $autosave_post = wp_get_post_autosave( $post_id );
     432
     433                // No changes
     434                $this->assertEquals( $current_post->post_title, $autosave_post->post_title );
     435                $this->assertEquals( $current_post->post_excerpt, $autosave_post->post_excerpt );
     436
     437                // Has changes
     438                $this->assertEquals( $autosave_data['content'], $autosave_post->post_content );
     439
     440                wp_delete_post( $post_id );
     441        }
     442
     443        public function test_get_additional_field_registration() {
     444                $schema = array(
     445                        'type'        => 'integer',
     446                        'description' => 'Some integer of mine',
     447                        'enum'        => array( 1, 2, 3, 4 ),
     448                        'context'     => array( 'view', 'edit' ),
     449                );
     450
     451                register_rest_field(
     452                        'post-revision', 'my_custom_int', array(
     453                                'schema'          => $schema,
     454                                'get_callback'    => array( $this, 'additional_field_get_callback' ),
     455                                'update_callback' => array( $this, 'additional_field_update_callback' ),
     456                        )
     457                );
     458
     459                $request = new WP_REST_Request( 'OPTIONS', '/wp/v2/posts/' . self::$post_id . '/autosaves' );
     460
     461                $response = rest_get_server()->dispatch( $request );
     462                $data     = $response->get_data();
     463
     464                $this->assertArrayHasKey( 'my_custom_int', $data['schema']['properties'] );
     465                $this->assertEquals( $schema, $data['schema']['properties']['my_custom_int'] );
     466
     467                wp_set_current_user( 1 );
     468
     469                $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/autosaves/' . self::$autosave_post_id );
     470
     471                $response = rest_get_server()->dispatch( $request );
     472                $this->assertArrayHasKey( 'my_custom_int', $response->data );
     473
     474                global $wp_rest_additional_fields;
     475                $wp_rest_additional_fields = array();
     476        }
     477
     478        public function additional_field_get_callback( $object ) {
     479                return get_post_meta( $object['id'], 'my_custom_int', true );
     480        }
     481
     482        public function additional_field_update_callback( $value, $post ) {
     483                update_post_meta( $post->ID, 'my_custom_int', $value );
     484        }
     485
     486        protected function check_get_autosave_response( $response, $autosave ) {
     487                if ( $response instanceof WP_REST_Response ) {
     488                        $links    = $response->get_links();
     489                        $response = $response->get_data();
     490                } else {
     491                        $this->assertArrayHasKey( '_links', $response );
     492                        $links = $response['_links'];
     493                }
     494
     495                $this->assertEquals( $autosave->post_author, $response['author'] );
     496
     497                $rendered_content = apply_filters( 'the_content', $autosave->post_content );
     498                $this->assertEquals( $rendered_content, $response['content']['rendered'] );
     499
     500                $this->assertEquals( mysql_to_rfc3339( $autosave->post_date ), $response['date'] );
     501                $this->assertEquals( mysql_to_rfc3339( $autosave->post_date_gmt ), $response['date_gmt'] );
     502
     503                $rendered_guid = apply_filters( 'get_the_guid', $autosave->guid, $autosave->ID );
     504                $this->assertEquals( $rendered_guid, $response['guid']['rendered'] );
     505
     506                $this->assertEquals( $autosave->ID, $response['id'] );
     507                $this->assertEquals( mysql_to_rfc3339( $autosave->post_modified ), $response['modified'] );
     508                $this->assertEquals( mysql_to_rfc3339( $autosave->post_modified_gmt ), $response['modified_gmt'] );
     509                $this->assertEquals( $autosave->post_name, $response['slug'] );
     510
     511                $rendered_title = get_the_title( $autosave->ID );
     512                $this->assertEquals( $rendered_title, $response['title']['rendered'] );
     513
     514                $parent            = get_post( $autosave->post_parent );
     515                $parent_controller = new WP_REST_Posts_Controller( $parent->post_type );
     516                $parent_object     = get_post_type_object( $parent->post_type );
     517                $parent_base       = ! empty( $parent_object->rest_base ) ? $parent_object->rest_base : $parent_object->name;
     518                $this->assertEquals( rest_url( '/wp/v2/' . $parent_base . '/' . $autosave->post_parent ), $links['parent'][0]['href'] );
     519        }
     520
     521        public function test_get_item_sets_up_postdata() {
     522                wp_set_current_user( self::$editor_id );
     523                $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/autosaves/' . self::$autosave_post_id );
     524                rest_get_server()->dispatch( $request );
     525
     526                $post           = get_post();
     527                $parent_post_id = wp_is_post_revision( $post->ID );
     528
     529                $this->assertEquals( $post->ID, self::$autosave_post_id );
     530                $this->assertEquals( $parent_post_id, self::$post_id );
     531        }
     532
     533}
  • 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

     
    783783                }
    784784            ]
    785785        },
     786        "/wp/v2/posts/(?P<parent>[\\d]+)/autosaves": {
     787            "namespace": "wp/v2",
     788            "methods": [
     789                "GET"
     790            ],
     791            "endpoints": [
     792                {
     793                    "methods": [
     794                        "GET"
     795                    ],
     796                    "args": {
     797                        "parent": {
     798                            "required": false,
     799                            "description": "The ID for the parent of the object.",
     800                            "type": "integer"
     801                        },
     802                        "context": {
     803                            "required": false,
     804                            "default": "view",
     805                            "enum": [
     806                                "view",
     807                                "embed",
     808                                "edit"
     809                            ],
     810                            "description": "Scope under which the request is made; determines fields present in response.",
     811                            "type": "string"
     812                        }
     813                    }
     814                }
     815            ]
     816        },
     817        "/wp/v2/posts/(?P<parent>[\\d]+)/autosaves/(?P<id>[\\d]+)": {
     818            "namespace": "wp/v2",
     819            "methods": [
     820                "GET",
     821                "POST",
     822                "PUT",
     823                "PATCH",
     824                "DELETE"
     825            ],
     826            "endpoints": [
     827                {
     828                    "methods": [
     829                        "GET"
     830                    ],
     831                    "args": {
     832                        "parent": {
     833                            "required": false,
     834                            "description": "The ID for the parent of the object.",
     835                            "type": "integer"
     836                        },
     837                        "id": {
     838                            "required": false,
     839                            "description": "The ID for the object.",
     840                            "type": "integer"
     841                        },
     842                        "context": {
     843                            "required": false,
     844                            "default": "view",
     845                            "enum": [
     846                                "view",
     847                                "embed",
     848                                "edit"
     849                            ],
     850                            "description": "Scope under which the request is made; determines fields present in response.",
     851                            "type": "string"
     852                        }
     853                    }
     854                },
     855                {
     856                    "methods": [
     857                        "POST",
     858                        "PUT",
     859                        "PATCH"
     860                    ],
     861                    "args": {
     862                        "parent": {
     863                            "required": false,
     864                            "description": "The ID for the parent of the object.",
     865                            "type": "integer"
     866                        },
     867                        "id": {
     868                            "required": false,
     869                            "description": "Unique identifier for the object.",
     870                            "type": "integer"
     871                        },
     872                        "author": {
     873                            "required": false,
     874                            "description": "The ID for the author of the object.",
     875                            "type": "integer"
     876                        },
     877                        "date": {
     878                            "required": false,
     879                            "description": "The date the object was published, in the site's timezone.",
     880                            "type": "string"
     881                        },
     882                        "date_gmt": {
     883                            "required": false,
     884                            "description": "The date the object was published, as GMT.",
     885                            "type": "string"
     886                        },
     887                        "modified": {
     888                            "required": false,
     889                            "description": "The date the object was last modified, in the site's timezone.",
     890                            "type": "string"
     891                        },
     892                        "modified_gmt": {
     893                            "required": false,
     894                            "description": "The date the object was last modified, as GMT.",
     895                            "type": "string"
     896                        },
     897                        "slug": {
     898                            "required": false,
     899                            "description": "An alphanumeric identifier for the object unique to its type.",
     900                            "type": "string"
     901                        },
     902                        "title": {
     903                            "required": false,
     904                            "description": "The title for the object.",
     905                            "type": "object"
     906                        },
     907                        "content": {
     908                            "required": false,
     909                            "description": "The content for the object.",
     910                            "type": "object"
     911                        },
     912                        "excerpt": {
     913                            "required": false,
     914                            "description": "The excerpt for the object.",
     915                            "type": "object"
     916                        }
     917                    }
     918                },
     919                {
     920                    "methods": [
     921                        "DELETE"
     922                    ],
     923                    "args": {
     924                        "parent": {
     925                            "required": false,
     926                            "description": "The ID for the parent of the object.",
     927                            "type": "integer"
     928                        },
     929                        "id": {
     930                            "required": false,
     931                            "description": "The ID for the object.",
     932                            "type": "integer"
     933                        },
     934                        "force": {
     935                            "required": false,
     936                            "default": false,
     937                            "description": "Required to be true, as autosaves do not support trashing.",
     938                            "type": "boolean"
     939                        }
     940                    }
     941                }
     942            ]
     943        },
    786944        "/wp/v2/pages": {
    787945            "namespace": "wp/v2",
    788946            "methods": [
     
    13211479                }
    13221480            ]
    13231481        },
     1482        "/wp/v2/pages/(?P<parent>[\\d]+)/autosaves": {
     1483            "namespace": "wp/v2",
     1484            "methods": [
     1485                "GET"
     1486            ],
     1487            "endpoints": [
     1488                {
     1489                    "methods": [
     1490                        "GET"
     1491                    ],
     1492                    "args": {
     1493                        "parent": {
     1494                            "required": false,
     1495                            "description": "The ID for the parent of the object.",
     1496                            "type": "integer"
     1497                        },
     1498                        "context": {
     1499                            "required": false,
     1500                            "default": "view",
     1501                            "enum": [
     1502                                "view",
     1503                                "embed",
     1504                                "edit"
     1505                            ],
     1506                            "description": "Scope under which the request is made; determines fields present in response.",
     1507                            "type": "string"
     1508                        }
     1509                    }
     1510                }
     1511            ]
     1512        },
     1513        "/wp/v2/pages/(?P<parent>[\\d]+)/autosaves/(?P<id>[\\d]+)": {
     1514            "namespace": "wp/v2",
     1515            "methods": [
     1516                "GET",
     1517                "POST",
     1518                "PUT",
     1519                "PATCH",
     1520                "DELETE"
     1521            ],
     1522            "endpoints": [
     1523                {
     1524                    "methods": [
     1525                        "GET"
     1526                    ],
     1527                    "args": {
     1528                        "parent": {
     1529                            "required": false,
     1530                            "description": "The ID for the parent of the object.",
     1531                            "type": "integer"
     1532                        },
     1533                        "id": {
     1534                            "required": false,
     1535                            "description": "The ID for the object.",
     1536                            "type": "integer"
     1537                        },
     1538                        "context": {
     1539                            "required": false,
     1540                            "default": "view",
     1541                            "enum": [
     1542                                "view",
     1543                                "embed",
     1544                                "edit"
     1545                            ],
     1546                            "description": "Scope under which the request is made; determines fields present in response.",
     1547                            "type": "string"
     1548                        }
     1549                    }
     1550                },
     1551                {
     1552                    "methods": [
     1553                        "POST",
     1554                        "PUT",
     1555                        "PATCH"
     1556                    ],
     1557                    "args": {
     1558                        "parent": {
     1559                            "required": false,
     1560                            "description": "The ID for the parent of the object.",
     1561                            "type": "integer"
     1562                        },
     1563                        "id": {
     1564                            "required": false,
     1565                            "description": "Unique identifier for the object.",
     1566                            "type": "integer"
     1567                        },
     1568                        "author": {
     1569                            "required": false,
     1570                            "description": "The ID for the author of the object.",
     1571                            "type": "integer"
     1572                        },
     1573                        "date": {
     1574                            "required": false,
     1575                            "description": "The date the object was published, in the site's timezone.",
     1576                            "type": "string"
     1577                        },
     1578                        "date_gmt": {
     1579                            "required": false,
     1580                            "description": "The date the object was published, as GMT.",
     1581                            "type": "string"
     1582                        },
     1583                        "modified": {
     1584                            "required": false,
     1585                            "description": "The date the object was last modified, in the site's timezone.",
     1586                            "type": "string"
     1587                        },
     1588                        "modified_gmt": {
     1589                            "required": false,
     1590                            "description": "The date the object was last modified, as GMT.",
     1591                            "type": "string"
     1592                        },
     1593                        "slug": {
     1594                            "required": false,
     1595                            "description": "An alphanumeric identifier for the object unique to its type.",
     1596                            "type": "string"
     1597                        },
     1598                        "title": {
     1599                            "required": false,
     1600                            "description": "The title for the object.",
     1601                            "type": "object"
     1602                        },
     1603                        "content": {
     1604                            "required": false,
     1605                            "description": "The content for the object.",
     1606                            "type": "object"
     1607                        },
     1608                        "excerpt": {
     1609                            "required": false,
     1610                            "description": "The excerpt for the object.",
     1611                            "type": "object"
     1612                        }
     1613                    }
     1614                },
     1615                {
     1616                    "methods": [
     1617                        "DELETE"
     1618                    ],
     1619                    "args": {
     1620                        "parent": {
     1621                            "required": false,
     1622                            "description": "The ID for the parent of the object.",
     1623                            "type": "integer"
     1624                        },
     1625                        "id": {
     1626                            "required": false,
     1627                            "description": "The ID for the object.",
     1628                            "type": "integer"
     1629                        },
     1630                        "force": {
     1631                            "required": false,
     1632                            "default": false,
     1633                            "description": "Required to be true, as autosaves do not support trashing.",
     1634                            "type": "boolean"
     1635                        }
     1636                    }
     1637                }
     1638            ]
     1639        },
    13241640        "/wp/v2/media": {
    13251641            "namespace": "wp/v2",
    13261642            "methods": [
     
    36703986            "rendered": "http://example.org/?p=4"
    36713987        },
    36723988        "title": {
     3989            "rendered": ""
     3990        },
     3991        "content": {
     3992            "rendered": "<p>Autosave post content.</p>\n"
     3993        },
     3994        "excerpt": {
     3995            "rendered": ""
     3996        },
     3997        "_links": {
     3998            "parent": [
     3999                {
     4000                    "href": "http://example.org/index.php?rest_route=/wp/v2/posts/3"
     4001                }
     4002            ]
     4003        }
     4004    },
     4005    {
     4006        "author": 2,
     4007        "date": "2017-02-14T00:00:00",
     4008        "date_gmt": "2017-02-14T00:00:00",
     4009        "id": 4,
     4010        "modified": "2017-02-14T00:00:00",
     4011        "modified_gmt": "2017-02-14T00:00:00",
     4012        "parent": 3,
     4013        "slug": "3-revision-v1",
     4014        "guid": {
     4015            "rendered": "http://example.org/?p=4"
     4016        },
     4017        "title": {
    36734018            "rendered": "REST API Client Fixture: Post"
    36744019        },
    36754020        "content": {
     
    37114056    }
    37124057};
    37134058
     4059mockedApiResponse.postAutosaves = [
     4060    {
     4061        "author": 2,
     4062        "date": "2017-02-14T00:00:00",
     4063        "date_gmt": "2017-02-14T00:00:00",
     4064        "id": 5,
     4065        "modified": "2017-02-14T00:00:00",
     4066        "modified_gmt": "2017-02-14T00:00:00",
     4067        "parent": 3,
     4068        "slug": "3-autosave-v1",
     4069        "guid": {
     4070            "rendered": "http://example.org/?p=5"
     4071        },
     4072        "title": {
     4073            "rendered": ""
     4074        },
     4075        "content": {
     4076            "rendered": "<p>Autosave post content.</p>\n"
     4077        },
     4078        "excerpt": {
     4079            "rendered": ""
     4080        },
     4081        "_links": {
     4082            "parent": [
     4083                {
     4084                    "href": "http://example.org/index.php?rest_route=/wp/v2/posts/3"
     4085                }
     4086            ]
     4087        }
     4088    }
     4089];
     4090
     4091mockedApiResponse.autosave = {
     4092    "author": 2,
     4093    "date": "2017-02-14T00:00:00",
     4094    "date_gmt": "2017-02-14T00:00:00",
     4095    "id": 5,
     4096    "modified": "2017-02-14T00:00:00",
     4097    "modified_gmt": "2017-02-14T00:00:00",
     4098    "parent": 3,
     4099    "slug": "3-autosave-v1",
     4100    "guid": {
     4101        "rendered": "http://example.org/?p=5"
     4102    },
     4103    "title": {
     4104        "rendered": ""
     4105    },
     4106    "content": {
     4107        "rendered": "<p>Autosave post content.</p>\n"
     4108    },
     4109    "excerpt": {
     4110        "rendered": ""
     4111    }
     4112};
     4113
    37144114mockedApiResponse.PagesCollection = [
    37154115    {
    37164116        "id": 5,
     
    38394239            "rendered": "http://example.org/?p=6"
    38404240        },
    38414241        "title": {
     4242            "rendered": ""
     4243        },
     4244        "content": {
     4245            "rendered": "<p>Autosave page content.</p>\n"
     4246        },
     4247        "excerpt": {
     4248            "rendered": ""
     4249        },
     4250        "_links": {
     4251            "parent": [
     4252                {
     4253                    "href": "http://example.org/index.php?rest_route=/wp/v2/pages/5"
     4254                }
     4255            ]
     4256        }
     4257    },
     4258    {
     4259        "author": 2,
     4260        "date": "2017-02-14T00:00:00",
     4261        "date_gmt": "2017-02-14T00:00:00",
     4262        "id": 7,
     4263        "modified": "2017-02-14T00:00:00",
     4264        "modified_gmt": "2017-02-14T00:00:00",
     4265        "parent": 6,
     4266        "slug": "6-revision-v1",
     4267        "guid": {
     4268            "rendered": "http://example.org/?p=7"
     4269        },
     4270        "title": {
    38424271            "rendered": "REST API Client Fixture: Page"
    38434272        },
    38444273        "content": {
     
    38504279        "_links": {
    38514280            "parent": [
    38524281                {
    3853                     "href": "http://example.org/index.php?rest_route=/wp/v2/pages/5"
     4282                    "href": "http://example.org/index.php?rest_route=/wp/v2/pages/6"
    38544283                }
    38554284            ]
    38564285        }
     
    38804309    }
    38814310};
    38824311
     4312mockedApiResponse.pageAutosaves = [
     4313    {
     4314        "author": 2,
     4315        "date": "2017-02-14T00:00:00",
     4316        "date_gmt": "2017-02-14T00:00:00",
     4317        "id": 8,
     4318        "modified": "2017-02-14T00:00:00",
     4319        "modified_gmt": "2017-02-14T00:00:00",
     4320        "parent": 6,
     4321        "slug": "6-autosave-v1",
     4322        "guid": {
     4323            "rendered": "http://example.org/?p=8"
     4324        },
     4325        "title": {
     4326            "rendered": ""
     4327        },
     4328        "content": {
     4329            "rendered": "<p>Autosave page content.</p>\n"
     4330        },
     4331        "excerpt": {
     4332            "rendered": ""
     4333        },
     4334        "_links": {
     4335            "parent": [
     4336                {
     4337                    "href": "http://example.org/index.php?rest_route=/wp/v2/pages/6"
     4338                }
     4339            ]
     4340        }
     4341    }
     4342];
     4343
     4344mockedApiResponse.pageAutosave = {
     4345    "author": 2,
     4346    "date": "2017-02-14T00:00:00",
     4347    "date_gmt": "2017-02-14T00:00:00",
     4348    "id": 8,
     4349    "modified": "2017-02-14T00:00:00",
     4350    "modified_gmt": "2017-02-14T00:00:00",
     4351    "parent": 6,
     4352    "slug": "6-autosave-v1",
     4353    "guid": {
     4354        "rendered": "http://example.org/?p=8"
     4355    },
     4356    "title": {
     4357        "rendered": ""
     4358    },
     4359    "content": {
     4360        "rendered": "<p>Autosave page content.</p>\n"
     4361    },
     4362    "excerpt": {
     4363        "rendered": ""
     4364    }
     4365};
     4366
    38834367mockedApiResponse.MediaCollection = [
    38844368    {
    38854369        "id": 7,