wp/wp-includes/rest-api/endpoints/class-wp-rest-autosaves-controller.php
changeset 16 a86126ab1dd4
parent 9 177826044cd9
child 18 be944660c56a
equal deleted inserted replaced
15:3d4e9c994f10 16:a86126ab1dd4
    10 /**
    10 /**
    11  * Core class used to access autosaves via the REST API.
    11  * Core class used to access autosaves via the REST API.
    12  *
    12  *
    13  * @since 5.0.0
    13  * @since 5.0.0
    14  *
    14  *
       
    15  * @see WP_REST_Revisions_Controller
    15  * @see WP_REST_Controller
    16  * @see WP_REST_Controller
    16  */
    17  */
    17 class WP_REST_Autosaves_Controller extends WP_REST_Revisions_Controller {
    18 class WP_REST_Autosaves_Controller extends WP_REST_Revisions_Controller {
    18 
    19 
    19 	/**
    20 	/**
    56 	 * @param string $parent_post_type Post type of the parent.
    57 	 * @param string $parent_post_type Post type of the parent.
    57 	 */
    58 	 */
    58 	public function __construct( $parent_post_type ) {
    59 	public function __construct( $parent_post_type ) {
    59 		$this->parent_post_type = $parent_post_type;
    60 		$this->parent_post_type = $parent_post_type;
    60 		$post_type_object       = get_post_type_object( $parent_post_type );
    61 		$post_type_object       = get_post_type_object( $parent_post_type );
    61 
    62 		$parent_controller      = $post_type_object->get_rest_controller();
    62 		// Ensure that post type-specific controller logic is available.
    63 
    63 		$parent_controller_class = ! empty( $post_type_object->rest_controller_class ) ? $post_type_object->rest_controller_class : 'WP_REST_Posts_Controller';
    64 		if ( ! $parent_controller ) {
    64 
    65 			$parent_controller = new WP_REST_Posts_Controller( $parent_post_type );
    65 		$this->parent_controller    = new $parent_controller_class( $post_type_object->name );
    66 		}
       
    67 
       
    68 		$this->parent_controller    = $parent_controller;
    66 		$this->revisions_controller = new WP_REST_Revisions_Controller( $parent_post_type );
    69 		$this->revisions_controller = new WP_REST_Revisions_Controller( $parent_post_type );
    67 		$this->rest_namespace       = 'wp/v2';
    70 		$this->rest_namespace       = 'wp/v2';
    68 		$this->rest_base            = 'autosaves';
    71 		$this->rest_base            = 'autosaves';
    69 		$this->parent_base          = ! empty( $post_type_object->rest_base ) ? $post_type_object->rest_base : $post_type_object->name;
    72 		$this->parent_base          = ! empty( $post_type_object->rest_base ) ? $post_type_object->rest_base : $post_type_object->name;
    70 	}
    73 	}
    71 
    74 
    72 	/**
    75 	/**
    73 	 * Registers routes for autosaves.
    76 	 * Registers the routes for autosaves.
    74 	 *
    77 	 *
    75 	 * @since 5.0.0
    78 	 * @since 5.0.0
    76 	 *
    79 	 *
    77 	 * @see register_rest_route()
    80 	 * @see register_rest_route()
    78 	 */
    81 	 */
   146 	/**
   149 	/**
   147 	 * Checks if a given request has access to get autosaves.
   150 	 * Checks if a given request has access to get autosaves.
   148 	 *
   151 	 *
   149 	 * @since 5.0.0
   152 	 * @since 5.0.0
   150 	 *
   153 	 *
   151 	 * @param WP_REST_Request $request Full data about the request.
   154 	 * @param WP_REST_Request $request Full details about the request.
   152 	 * @return true|WP_Error True if the request has read access, WP_Error object otherwise.
   155 	 * @return true|WP_Error True if the request has read access, WP_Error object otherwise.
   153 	 */
   156 	 */
   154 	public function get_items_permissions_check( $request ) {
   157 	public function get_items_permissions_check( $request ) {
   155 		$parent = $this->get_parent( $request['id'] );
   158 		$parent = $this->get_parent( $request['id'] );
   156 		if ( is_wp_error( $parent ) ) {
   159 		if ( is_wp_error( $parent ) ) {
   157 			return $parent;
   160 			return $parent;
   158 		}
   161 		}
   159 
   162 
   160 		$parent_post_type_obj = get_post_type_object( $parent->post_type );
   163 		if ( ! current_user_can( 'edit_post', $parent->ID ) ) {
   161 		if ( ! current_user_can( $parent_post_type_obj->cap->edit_post, $parent->ID ) ) {
   164 			return new WP_Error(
   162 			return new WP_Error( 'rest_cannot_read', __( 'Sorry, you are not allowed to view autosaves of this post.' ), array( 'status' => rest_authorization_required_code() ) );
   165 				'rest_cannot_read',
       
   166 				__( 'Sorry, you are not allowed to view autosaves of this post.' ),
       
   167 				array( 'status' => rest_authorization_required_code() )
       
   168 			);
   163 		}
   169 		}
   164 
   170 
   165 		return true;
   171 		return true;
   166 	}
   172 	}
   167 
   173 
   176 	 * @param WP_REST_Request $request Full details about the request.
   182 	 * @param WP_REST_Request $request Full details about the request.
   177 	 * @return true|WP_Error True if the request has access to create the item, WP_Error object otherwise.
   183 	 * @return true|WP_Error True if the request has access to create the item, WP_Error object otherwise.
   178 	 */
   184 	 */
   179 	public function create_item_permissions_check( $request ) {
   185 	public function create_item_permissions_check( $request ) {
   180 		$id = $request->get_param( 'id' );
   186 		$id = $request->get_param( 'id' );
       
   187 
   181 		if ( empty( $id ) ) {
   188 		if ( empty( $id ) ) {
   182 			return new WP_Error( 'rest_post_invalid_id', __( 'Invalid item ID.' ), array( 'status' => 404 ) );
   189 			return new WP_Error(
       
   190 				'rest_post_invalid_id',
       
   191 				__( 'Invalid item ID.' ),
       
   192 				array( 'status' => 404 )
       
   193 			);
   183 		}
   194 		}
   184 
   195 
   185 		return $this->parent_controller->update_item_permissions_check( $request );
   196 		return $this->parent_controller->update_item_permissions_check( $request );
   186 	}
   197 	}
   187 
   198 
   209 		$prepared_post->ID = $post->ID;
   220 		$prepared_post->ID = $post->ID;
   210 		$user_id           = get_current_user_id();
   221 		$user_id           = get_current_user_id();
   211 
   222 
   212 		if ( ( 'draft' === $post->post_status || 'auto-draft' === $post->post_status ) && $post->post_author == $user_id ) {
   223 		if ( ( 'draft' === $post->post_status || 'auto-draft' === $post->post_status ) && $post->post_author == $user_id ) {
   213 			// Draft posts for the same author: autosaving updates the post and does not create a revision.
   224 			// Draft posts for the same author: autosaving updates the post and does not create a revision.
   214 			// Convert the post object to an array and add slashes, wp_update_post expects escaped array.
   225 			// Convert the post object to an array and add slashes, wp_update_post() expects escaped array.
   215 			$autosave_id = wp_update_post( wp_slash( (array) $prepared_post ), true );
   226 			$autosave_id = wp_update_post( wp_slash( (array) $prepared_post ), true );
   216 		} else {
   227 		} else {
   217 			// Non-draft posts: create or update the post autosave.
   228 			// Non-draft posts: create or update the post autosave.
   218 			$autosave_id = $this->create_post_autosave( (array) $prepared_post );
   229 			$autosave_id = $this->create_post_autosave( (array) $prepared_post );
   219 		}
   230 		}
   234 	/**
   245 	/**
   235 	 * Get the autosave, if the ID is valid.
   246 	 * Get the autosave, if the ID is valid.
   236 	 *
   247 	 *
   237 	 * @since 5.0.0
   248 	 * @since 5.0.0
   238 	 *
   249 	 *
   239 	 * @param WP_REST_Request $request Full data about the request.
   250 	 * @param WP_REST_Request $request Full details about the request.
   240 	 * @return WP_Post|WP_Error Revision post object if ID is valid, WP_Error otherwise.
   251 	 * @return WP_Post|WP_Error Revision post object if ID is valid, WP_Error otherwise.
   241 	 */
   252 	 */
   242 	public function get_item( $request ) {
   253 	public function get_item( $request ) {
   243 		$parent_id = (int) $request->get_param( 'parent' );
   254 		$parent_id = (int) $request->get_param( 'parent' );
   244 
   255 
   245 		if ( $parent_id <= 0 ) {
   256 		if ( $parent_id <= 0 ) {
   246 			return new WP_Error( 'rest_post_invalid_id', __( 'Invalid parent post ID.' ), array( 'status' => 404 ) );
   257 			return new WP_Error(
       
   258 				'rest_post_invalid_id',
       
   259 				__( 'Invalid post parent ID.' ),
       
   260 				array( 'status' => 404 )
       
   261 			);
   247 		}
   262 		}
   248 
   263 
   249 		$autosave = wp_get_post_autosave( $parent_id );
   264 		$autosave = wp_get_post_autosave( $parent_id );
   250 
   265 
   251 		if ( ! $autosave ) {
   266 		if ( ! $autosave ) {
   252 			return new WP_Error( 'rest_post_no_autosave', __( 'There is no autosave revision for this post.' ), array( 'status' => 404 ) );
   267 			return new WP_Error(
       
   268 				'rest_post_no_autosave',
       
   269 				__( 'There is no autosave revision for this post.' ),
       
   270 				array( 'status' => 404 )
       
   271 			);
   253 		}
   272 		}
   254 
   273 
   255 		$response = $this->prepare_item_for_response( $autosave, $request );
   274 		$response = $this->prepare_item_for_response( $autosave, $request );
   256 		return $response;
   275 		return $response;
   257 	}
   276 	}
   261 	 *
   280 	 *
   262 	 * Contains the user's autosave, for empty if it doesn't exist.
   281 	 * Contains the user's autosave, for empty if it doesn't exist.
   263 	 *
   282 	 *
   264 	 * @since 5.0.0
   283 	 * @since 5.0.0
   265 	 *
   284 	 *
   266 	 * @param WP_REST_Request $request Full data about the request.
   285 	 * @param WP_REST_Request $request Full details about the request.
   267 	 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
   286 	 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
   268 	 */
   287 	 */
   269 	public function get_items( $request ) {
   288 	public function get_items( $request ) {
   270 		$parent = $this->get_parent( $request['id'] );
   289 		$parent = $this->get_parent( $request['id'] );
   271 		if ( is_wp_error( $parent ) ) {
   290 		if ( is_wp_error( $parent ) ) {
   293 	 * @since 5.0.0
   312 	 * @since 5.0.0
   294 	 *
   313 	 *
   295 	 * @return array Item schema data.
   314 	 * @return array Item schema data.
   296 	 */
   315 	 */
   297 	public function get_item_schema() {
   316 	public function get_item_schema() {
       
   317 		if ( $this->schema ) {
       
   318 			return $this->add_additional_fields_schema( $this->schema );
       
   319 		}
       
   320 
   298 		$schema = $this->revisions_controller->get_item_schema();
   321 		$schema = $this->revisions_controller->get_item_schema();
   299 
   322 
   300 		$schema['properties']['preview_link'] = array(
   323 		$schema['properties']['preview_link'] = array(
   301 			'description' => __( 'Preview link for the post.' ),
   324 			'description' => __( 'Preview link for the post.' ),
   302 			'type'        => 'string',
   325 			'type'        => 'string',
   303 			'format'      => 'uri',
   326 			'format'      => 'uri',
   304 			'context'     => array( 'edit' ),
   327 			'context'     => array( 'edit' ),
   305 			'readonly'    => true,
   328 			'readonly'    => true,
   306 		);
   329 		);
   307 
   330 
   308 		return $schema;
   331 		$this->schema = $schema;
       
   332 
       
   333 		return $this->add_additional_fields_schema( $this->schema );
   309 	}
   334 	}
   310 
   335 
   311 	/**
   336 	/**
   312 	 * Creates autosave for the specified post.
   337 	 * Creates autosave for the specified post.
   313 	 *
   338 	 *
   314 	 * From wp-admin/post.php.
   339 	 * From wp-admin/post.php.
   315 	 *
   340 	 *
   316 	 * @since 5.0.0
   341 	 * @since 5.0.0
   317 	 *
   342 	 *
   318 	 * @param mixed $post_data Associative array containing the post data.
   343 	 * @param array $post_data Associative array containing the post data.
   319 	 * @return mixed The autosave revision ID or WP_Error.
   344 	 * @return mixed The autosave revision ID or WP_Error.
   320 	 */
   345 	 */
   321 	public function create_post_autosave( $post_data ) {
   346 	public function create_post_autosave( $post_data ) {
   322 
   347 
   323 		$post_id = (int) $post_data['ID'];
   348 		$post_id = (int) $post_data['ID'];
   339 
   364 
   340 			// If the new autosave has the same content as the post, delete the autosave.
   365 			// If the new autosave has the same content as the post, delete the autosave.
   341 			$autosave_is_different = false;
   366 			$autosave_is_different = false;
   342 
   367 
   343 			foreach ( array_intersect( array_keys( $new_autosave ), array_keys( _wp_post_revision_fields( $post ) ) ) as $field ) {
   368 			foreach ( array_intersect( array_keys( $new_autosave ), array_keys( _wp_post_revision_fields( $post ) ) ) as $field ) {
   344 				if ( normalize_whitespace( $new_autosave[ $field ] ) != normalize_whitespace( $post->$field ) ) {
   369 				if ( normalize_whitespace( $new_autosave[ $field ] ) !== normalize_whitespace( $post->$field ) ) {
   345 					$autosave_is_different = true;
   370 					$autosave_is_different = true;
   346 					break;
   371 					break;
   347 				}
   372 				}
   348 			}
   373 			}
   349 
   374 
   350 			if ( ! $autosave_is_different ) {
   375 			if ( ! $autosave_is_different ) {
   351 				wp_delete_post_revision( $old_autosave->ID );
   376 				wp_delete_post_revision( $old_autosave->ID );
   352 				return new WP_Error( 'rest_autosave_no_changes', __( 'There is nothing to save. The autosave and the post content are the same.' ), array( 'status' => 400 ) );
   377 				return new WP_Error(
       
   378 					'rest_autosave_no_changes',
       
   379 					__( 'There is nothing to save. The autosave and the post content are the same.' ),
       
   380 					array( 'status' => 400 )
       
   381 				);
   353 			}
   382 			}
   354 
   383 
   355 			/**
   384 			/** This filter is documented in wp-admin/post.php */
   356 			 * This filter is documented in wp-admin/post.php.
       
   357 			 */
       
   358 			do_action( 'wp_creating_autosave', $new_autosave );
   385 			do_action( 'wp_creating_autosave', $new_autosave );
   359 
   386 
   360 			// wp_update_post expects escaped array.
   387 			// wp_update_post() expects escaped array.
   361 			return wp_update_post( wp_slash( $new_autosave ) );
   388 			return wp_update_post( wp_slash( $new_autosave ) );
   362 		}
   389 		}
   363 
   390 
   364 		// Create the new autosave as a special post revision.
   391 		// Create the new autosave as a special post revision.
   365 		return _wp_put_post_revision( $post_data, true );
   392 		return _wp_put_post_revision( $post_data, true );
   370 	 *
   397 	 *
   371 	 * @since 5.0.0
   398 	 * @since 5.0.0
   372 	 *
   399 	 *
   373 	 * @param WP_Post         $post    Post revision object.
   400 	 * @param WP_Post         $post    Post revision object.
   374 	 * @param WP_REST_Request $request Request object.
   401 	 * @param WP_REST_Request $request Request object.
   375 	 *
       
   376 	 * @return WP_REST_Response Response object.
   402 	 * @return WP_REST_Response Response object.
   377 	 */
   403 	 */
   378 	public function prepare_item_for_response( $post, $request ) {
   404 	public function prepare_item_for_response( $post, $request ) {
   379 
   405 
   380 		$response = $this->revisions_controller->prepare_item_for_response( $post, $request );
   406 		$response = $this->revisions_controller->prepare_item_for_response( $post, $request );