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 ); |