63 |
63 |
64 if ( ! $parent_controller ) { |
64 if ( ! $parent_controller ) { |
65 $parent_controller = new WP_REST_Posts_Controller( $parent_post_type ); |
65 $parent_controller = new WP_REST_Posts_Controller( $parent_post_type ); |
66 } |
66 } |
67 |
67 |
68 $this->parent_controller = $parent_controller; |
68 $this->parent_controller = $parent_controller; |
69 $this->revisions_controller = new WP_REST_Revisions_Controller( $parent_post_type ); |
69 |
|
70 $revisions_controller = $post_type_object->get_revisions_rest_controller(); |
|
71 if ( ! $revisions_controller ) { |
|
72 $revisions_controller = new WP_REST_Revisions_Controller( $parent_post_type ); |
|
73 } |
|
74 $this->revisions_controller = $revisions_controller; |
70 $this->rest_base = 'autosaves'; |
75 $this->rest_base = 'autosaves'; |
|
76 $this->parent_base = ! empty( $post_type_object->rest_base ) ? $post_type_object->rest_base : $post_type_object->name; |
71 $this->namespace = ! empty( $post_type_object->rest_namespace ) ? $post_type_object->rest_namespace : 'wp/v2'; |
77 $this->namespace = ! empty( $post_type_object->rest_namespace ) ? $post_type_object->rest_namespace : 'wp/v2'; |
72 $this->parent_base = ! empty( $post_type_object->rest_base ) ? $post_type_object->rest_base : $post_type_object->name; |
|
73 } |
78 } |
74 |
79 |
75 /** |
80 /** |
76 * Registers the routes for autosaves. |
81 * Registers the routes for autosaves. |
77 * |
82 * |
204 * @param WP_REST_Request $request Full details about the request. |
208 * @param WP_REST_Request $request Full details about the request. |
205 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. |
209 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. |
206 */ |
210 */ |
207 public function create_item( $request ) { |
211 public function create_item( $request ) { |
208 |
212 |
209 if ( ! defined( 'DOING_AUTOSAVE' ) ) { |
213 if ( ! defined( 'WP_RUN_CORE_TESTS' ) && ! defined( 'DOING_AUTOSAVE' ) ) { |
210 define( 'DOING_AUTOSAVE', true ); |
214 define( 'DOING_AUTOSAVE', true ); |
211 } |
215 } |
212 |
216 |
213 $post = get_post( $request['id'] ); |
217 $post = $this->get_parent( $request['id'] ); |
214 |
218 |
215 if ( is_wp_error( $post ) ) { |
219 if ( is_wp_error( $post ) ) { |
216 return $post; |
220 return $post; |
217 } |
221 } |
218 |
222 |
219 $prepared_post = $this->parent_controller->prepare_item_for_database( $request ); |
223 $prepared_post = $this->parent_controller->prepare_item_for_database( $request ); |
220 $prepared_post->ID = $post->ID; |
224 $prepared_post->ID = $post->ID; |
221 $user_id = get_current_user_id(); |
225 $user_id = get_current_user_id(); |
222 |
226 |
223 if ( ( 'draft' === $post->post_status || 'auto-draft' === $post->post_status ) && $post->post_author == $user_id ) { |
227 // We need to check post lock to ensure the original author didn't leave their browser tab open. |
224 // Draft posts for the same author: autosaving updates the post and does not create a revision. |
228 if ( ! function_exists( 'wp_check_post_lock' ) ) { |
225 // Convert the post object to an array and add slashes, wp_update_post() expects escaped array. |
229 require_once ABSPATH . 'wp-admin/includes/post.php'; |
|
230 } |
|
231 |
|
232 $post_lock = wp_check_post_lock( $post->ID ); |
|
233 $is_draft = 'draft' === $post->post_status || 'auto-draft' === $post->post_status; |
|
234 |
|
235 if ( $is_draft && (int) $post->post_author === $user_id && ! $post_lock ) { |
|
236 /* |
|
237 * Draft posts for the same author: autosaving updates the post and does not create a revision. |
|
238 * Convert the post object to an array and add slashes, wp_update_post() expects escaped array. |
|
239 */ |
226 $autosave_id = wp_update_post( wp_slash( (array) $prepared_post ), true ); |
240 $autosave_id = wp_update_post( wp_slash( (array) $prepared_post ), true ); |
227 } else { |
241 } else { |
228 // Non-draft posts: create or update the post autosave. |
242 // Non-draft posts: create or update the post autosave. Pass the meta data. |
229 $autosave_id = $this->create_post_autosave( (array) $prepared_post ); |
243 $autosave_id = $this->create_post_autosave( (array) $prepared_post, (array) $request->get_param( 'meta' ) ); |
230 } |
244 } |
231 |
245 |
232 if ( is_wp_error( $autosave_id ) ) { |
246 if ( is_wp_error( $autosave_id ) ) { |
233 return $autosave_id; |
247 return $autosave_id; |
234 } |
248 } |
337 * Creates autosave for the specified post. |
351 * Creates autosave for the specified post. |
338 * |
352 * |
339 * From wp-admin/post.php. |
353 * From wp-admin/post.php. |
340 * |
354 * |
341 * @since 5.0.0 |
355 * @since 5.0.0 |
|
356 * @since 6.4.0 The `$meta` parameter was added. |
342 * |
357 * |
343 * @param array $post_data Associative array containing the post data. |
358 * @param array $post_data Associative array containing the post data. |
|
359 * @param array $meta Associative array containing the post meta data. |
344 * @return mixed The autosave revision ID or WP_Error. |
360 * @return mixed The autosave revision ID or WP_Error. |
345 */ |
361 */ |
346 public function create_post_autosave( $post_data ) { |
362 public function create_post_autosave( $post_data, array $meta = array() ) { |
347 |
363 |
348 $post_id = (int) $post_data['ID']; |
364 $post_id = (int) $post_data['ID']; |
349 $post = get_post( $post_id ); |
365 $post = get_post( $post_id ); |
350 |
366 |
351 if ( is_wp_error( $post ) ) { |
367 if ( is_wp_error( $post ) ) { |
352 return $post; |
368 return $post; |
353 } |
369 } |
354 |
370 |
355 $user_id = get_current_user_id(); |
371 // Only create an autosave when it is different from the saved post. |
356 |
372 $autosave_is_different = false; |
357 // Store one autosave per author. If there is already an autosave, overwrite it. |
373 $new_autosave = _wp_post_revision_data( $post_data, true ); |
358 $old_autosave = wp_get_post_autosave( $post_id, $user_id ); |
374 |
359 |
375 foreach ( array_intersect( array_keys( $new_autosave ), array_keys( _wp_post_revision_fields( $post ) ) ) as $field ) { |
360 if ( $old_autosave ) { |
376 if ( normalize_whitespace( $new_autosave[ $field ] ) !== normalize_whitespace( $post->$field ) ) { |
361 $new_autosave = _wp_post_revision_data( $post_data, true ); |
377 $autosave_is_different = true; |
362 $new_autosave['ID'] = $old_autosave->ID; |
378 break; |
363 $new_autosave['post_author'] = $user_id; |
379 } |
364 |
380 } |
365 // If the new autosave has the same content as the post, delete the autosave. |
381 |
366 $autosave_is_different = false; |
382 // Check if meta values have changed. |
367 |
383 if ( ! empty( $meta ) ) { |
368 foreach ( array_intersect( array_keys( $new_autosave ), array_keys( _wp_post_revision_fields( $post ) ) ) as $field ) { |
384 $revisioned_meta_keys = wp_post_revision_meta_keys( $post->post_type ); |
369 if ( normalize_whitespace( $new_autosave[ $field ] ) !== normalize_whitespace( $post->$field ) ) { |
385 foreach ( $revisioned_meta_keys as $meta_key ) { |
|
386 // get_metadata_raw is used to avoid retrieving the default value. |
|
387 $old_meta = get_metadata_raw( 'post', $post_id, $meta_key, true ); |
|
388 $new_meta = isset( $meta[ $meta_key ] ) ? $meta[ $meta_key ] : ''; |
|
389 |
|
390 if ( $new_meta !== $old_meta ) { |
370 $autosave_is_different = true; |
391 $autosave_is_different = true; |
371 break; |
392 break; |
372 } |
393 } |
373 } |
394 } |
374 |
395 } |
375 if ( ! $autosave_is_different ) { |
396 |
376 wp_delete_post_revision( $old_autosave->ID ); |
397 $user_id = get_current_user_id(); |
377 return new WP_Error( |
398 |
378 'rest_autosave_no_changes', |
399 // Store one autosave per author. If there is already an autosave, overwrite it. |
379 __( 'There is nothing to save. The autosave and the post content are the same.' ), |
400 $old_autosave = wp_get_post_autosave( $post_id, $user_id ); |
380 array( 'status' => 400 ) |
401 |
381 ); |
402 if ( ! $autosave_is_different && $old_autosave ) { |
382 } |
403 // Nothing to save, return the existing autosave. |
|
404 return $old_autosave->ID; |
|
405 } |
|
406 |
|
407 if ( $old_autosave ) { |
|
408 $new_autosave['ID'] = $old_autosave->ID; |
|
409 $new_autosave['post_author'] = $user_id; |
383 |
410 |
384 /** This filter is documented in wp-admin/post.php */ |
411 /** This filter is documented in wp-admin/post.php */ |
385 do_action( 'wp_creating_autosave', $new_autosave ); |
412 do_action( 'wp_creating_autosave', $new_autosave ); |
386 |
413 |
387 // wp_update_post() expects escaped array. |
414 // wp_update_post() expects escaped array. |
388 return wp_update_post( wp_slash( $new_autosave ) ); |
415 $revision_id = wp_update_post( wp_slash( $new_autosave ) ); |
389 } |
416 } else { |
390 |
417 // Create the new autosave as a special post revision. |
391 // Create the new autosave as a special post revision. |
418 $revision_id = _wp_put_post_revision( $post_data, true ); |
392 return _wp_put_post_revision( $post_data, true ); |
419 } |
|
420 |
|
421 if ( is_wp_error( $revision_id ) || 0 === $revision_id ) { |
|
422 return $revision_id; |
|
423 } |
|
424 |
|
425 // Attached any passed meta values that have revisions enabled. |
|
426 if ( ! empty( $meta ) ) { |
|
427 foreach ( $revisioned_meta_keys as $meta_key ) { |
|
428 if ( isset( $meta[ $meta_key ] ) ) { |
|
429 update_metadata( 'post', $revision_id, $meta_key, wp_slash( $meta[ $meta_key ] ) ); |
|
430 } |
|
431 } |
|
432 } |
|
433 |
|
434 return $revision_id; |
393 } |
435 } |
394 |
436 |
395 /** |
437 /** |
396 * Prepares the revision for the REST response. |
438 * Prepares the revision for the REST response. |
397 * |
439 * |
402 * @param WP_REST_Request $request Request object. |
444 * @param WP_REST_Request $request Request object. |
403 * @return WP_REST_Response Response object. |
445 * @return WP_REST_Response Response object. |
404 */ |
446 */ |
405 public function prepare_item_for_response( $item, $request ) { |
447 public function prepare_item_for_response( $item, $request ) { |
406 // Restores the more descriptive, specific name for use within this method. |
448 // Restores the more descriptive, specific name for use within this method. |
407 $post = $item; |
449 $post = $item; |
|
450 |
408 $response = $this->revisions_controller->prepare_item_for_response( $post, $request ); |
451 $response = $this->revisions_controller->prepare_item_for_response( $post, $request ); |
409 |
452 $fields = $this->get_fields_for_response( $request ); |
410 $fields = $this->get_fields_for_response( $request ); |
|
411 |
453 |
412 if ( in_array( 'preview_link', $fields, true ) ) { |
454 if ( in_array( 'preview_link', $fields, true ) ) { |
413 $parent_id = wp_is_post_autosave( $post ); |
455 $parent_id = wp_is_post_autosave( $post ); |
414 $preview_post_id = false === $parent_id ? $post->ID : $parent_id; |
456 $preview_post_id = false === $parent_id ? $post->ID : $parent_id; |
415 $preview_query_args = array(); |
457 $preview_query_args = array(); |