Ticket #43316: 43316.14.diff
File 43316.14.diff, 50.3 KB (added by , 6 years ago) |
---|
-
src/wp-includes/rest-api.php
192 192 if ( post_type_supports( $post_type->name, 'revisions' ) ) { 193 193 $revisions_controller = new WP_REST_Revisions_Controller( $post_type->name ); 194 194 $revisions_controller->register_routes(); 195 $controller = new WP_REST_Autosaves_Controller( $post_type->name ); 196 $controller->register_routes(); 195 197 } 196 198 } 197 199 -
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 */ 17 class 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
230 230 require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-post-types-controller.php' ); 231 231 require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-post-statuses-controller.php' ); 232 232 require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-revisions-controller.php' ); 233 require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-autosaves-controller.php' ); 233 234 require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-taxonomies-controller.php' ); 234 235 require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-terms-controller.php' ); 235 236 require( 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 */ 13 class 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
89 89 '/wp/v2/posts/(?P<id>[\\d]+)', 90 90 '/wp/v2/posts/(?P<parent>[\\d]+)/revisions', 91 91 '/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]+)', 92 94 '/wp/v2/pages', 93 95 '/wp/v2/pages/(?P<id>[\\d]+)', 94 96 '/wp/v2/pages/(?P<parent>[\\d]+)/revisions', 95 97 '/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]+)', 96 100 '/wp/v2/media', 97 101 '/wp/v2/media/(?P<id>[\\d]+)', 98 102 '/wp/v2/types', … … 159 163 $post_revisions = array_values( wp_get_post_revisions( $post_id ) ); 160 164 $post_revision_id = $post_revisions[ count( $post_revisions ) - 1 ]->ID; 161 165 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 162 175 $page_id = $this->factory->post->create( 163 176 array( 164 177 'post_type' => 'page', … … 180 193 $page_revisions = array_values( wp_get_post_revisions( $page_id ) ); 181 194 $page_revision_id = $page_revisions[ count( $page_revisions ) - 1 ]->ID; 182 195 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 183 205 $tag_id = $this->factory->tag->create( 184 206 array( 185 207 'name' => 'REST API Client Fixture: Tag', … … 272 294 'name' => 'revision', 273 295 ), 274 296 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( 275 305 'route' => '/wp/v2/pages', 276 306 'name' => 'PagesCollection', 277 307 ), … … 288 318 'name' => 'pageRevision', 289 319 ), 290 320 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( 291 329 'route' => '/wp/v2/media', 292 330 'name' => 'MediaCollection', 293 331 ), -
tests/qunit/fixtures/wp-api-generated.js
787 787 } 788 788 ] 789 789 }, 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 }, 790 920 "/wp/v2/pages": { 791 921 "namespace": "wp/v2", 792 922 "methods": [ … … 1329 1459 } 1330 1460 ] 1331 1461 }, 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 }, 1332 1592 "/wp/v2/media": { 1333 1593 "namespace": "wp/v2", 1334 1594 "methods": [ … … 3678 3938 "rendered": "http://example.org/?p=4" 3679 3939 }, 3680 3940 "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": { 3681 3970 "rendered": "REST API Client Fixture: Post" 3682 3971 }, 3683 3972 "content": { … … 3719 4008 } 3720 4009 }; 3721 4010 4011 mockedApiResponse.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 4043 mockedApiResponse.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 3722 4066 mockedApiResponse.PagesCollection = [ 3723 4067 { 3724 4068 "id": 5, … … 3847 4191 "rendered": "http://example.org/?p=6" 3848 4192 }, 3849 4193 "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": { 3850 4223 "rendered": "REST API Client Fixture: Page" 3851 4224 }, 3852 4225 "content": { … … 3858 4231 "_links": { 3859 4232 "parent": [ 3860 4233 { 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" 3862 4235 } 3863 4236 ] 3864 4237 } … … 3888 4261 } 3889 4262 }; 3890 4263 4264 mockedApiResponse.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 4296 mockedApiResponse.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 3891 4319 mockedApiResponse.MediaCollection = [ 3892 4320 { 3893 4321 "id": 7,