diff -r 7b1b88e27a20 -r 48c4eec2b7e6 wp/wp-includes/rest-api/endpoints/class-wp-rest-autosaves-controller.php --- a/wp/wp-includes/rest-api/endpoints/class-wp-rest-autosaves-controller.php Thu Sep 29 08:06:27 2022 +0200 +++ b/wp/wp-includes/rest-api/endpoints/class-wp-rest-autosaves-controller.php Fri Sep 05 18:40:08 2025 +0200 @@ -65,11 +65,16 @@ $parent_controller = new WP_REST_Posts_Controller( $parent_post_type ); } - $this->parent_controller = $parent_controller; - $this->revisions_controller = new WP_REST_Revisions_Controller( $parent_post_type ); + $this->parent_controller = $parent_controller; + + $revisions_controller = $post_type_object->get_revisions_rest_controller(); + if ( ! $revisions_controller ) { + $revisions_controller = new WP_REST_Revisions_Controller( $parent_post_type ); + } + $this->revisions_controller = $revisions_controller; $this->rest_base = 'autosaves'; + $this->parent_base = ! empty( $post_type_object->rest_base ) ? $post_type_object->rest_base : $post_type_object->name; $this->namespace = ! empty( $post_type_object->rest_namespace ) ? $post_type_object->rest_namespace : 'wp/v2'; - $this->parent_base = ! empty( $post_type_object->rest_base ) ? $post_type_object->rest_base : $post_type_object->name; } /** @@ -131,7 +136,6 @@ 'schema' => array( $this, 'get_public_item_schema' ), ) ); - } /** @@ -206,11 +210,11 @@ */ public function create_item( $request ) { - if ( ! defined( 'DOING_AUTOSAVE' ) ) { + if ( ! defined( 'WP_RUN_CORE_TESTS' ) && ! defined( 'DOING_AUTOSAVE' ) ) { define( 'DOING_AUTOSAVE', true ); } - $post = get_post( $request['id'] ); + $post = $this->get_parent( $request['id'] ); if ( is_wp_error( $post ) ) { return $post; @@ -220,13 +224,23 @@ $prepared_post->ID = $post->ID; $user_id = get_current_user_id(); - if ( ( 'draft' === $post->post_status || 'auto-draft' === $post->post_status ) && $post->post_author == $user_id ) { - // Draft posts for the same author: autosaving updates the post and does not create a revision. - // Convert the post object to an array and add slashes, wp_update_post() expects escaped array. + // We need to check post lock to ensure the original author didn't leave their browser tab open. + if ( ! function_exists( 'wp_check_post_lock' ) ) { + require_once ABSPATH . 'wp-admin/includes/post.php'; + } + + $post_lock = wp_check_post_lock( $post->ID ); + $is_draft = 'draft' === $post->post_status || 'auto-draft' === $post->post_status; + + if ( $is_draft && (int) $post->post_author === $user_id && ! $post_lock ) { + /* + * Draft posts for the same author: autosaving updates the post and does not create a revision. + * Convert the post object to an array and add slashes, wp_update_post() expects escaped array. + */ $autosave_id = wp_update_post( wp_slash( (array) $prepared_post ), true ); } else { - // Non-draft posts: create or update the post autosave. - $autosave_id = $this->create_post_autosave( (array) $prepared_post ); + // Non-draft posts: create or update the post autosave. Pass the meta data. + $autosave_id = $this->create_post_autosave( (array) $prepared_post, (array) $request->get_param( 'meta' ) ); } if ( is_wp_error( $autosave_id ) ) { @@ -296,7 +310,7 @@ $revisions = wp_get_post_revisions( $parent_id, array( 'check_enabled' => false ) ); foreach ( $revisions as $revision ) { - if ( false !== strpos( $revision->post_name, "{$parent_id}-autosave" ) ) { + if ( str_contains( $revision->post_name, "{$parent_id}-autosave" ) ) { $data = $this->prepare_item_for_response( $revision, $request ); $response[] = $this->prepare_response_for_collection( $data ); } @@ -339,11 +353,13 @@ * From wp-admin/post.php. * * @since 5.0.0 + * @since 6.4.0 The `$meta` parameter was added. * * @param array $post_data Associative array containing the post data. + * @param array $meta Associative array containing the post meta data. * @return mixed The autosave revision ID or WP_Error. */ - public function create_post_autosave( $post_data ) { + public function create_post_autosave( $post_data, array $meta = array() ) { $post_id = (int) $post_data['ID']; $post = get_post( $post_id ); @@ -352,44 +368,70 @@ return $post; } + // Only create an autosave when it is different from the saved post. + $autosave_is_different = false; + $new_autosave = _wp_post_revision_data( $post_data, true ); + + foreach ( array_intersect( array_keys( $new_autosave ), array_keys( _wp_post_revision_fields( $post ) ) ) as $field ) { + if ( normalize_whitespace( $new_autosave[ $field ] ) !== normalize_whitespace( $post->$field ) ) { + $autosave_is_different = true; + break; + } + } + + // Check if meta values have changed. + if ( ! empty( $meta ) ) { + $revisioned_meta_keys = wp_post_revision_meta_keys( $post->post_type ); + foreach ( $revisioned_meta_keys as $meta_key ) { + // get_metadata_raw is used to avoid retrieving the default value. + $old_meta = get_metadata_raw( 'post', $post_id, $meta_key, true ); + $new_meta = isset( $meta[ $meta_key ] ) ? $meta[ $meta_key ] : ''; + + if ( $new_meta !== $old_meta ) { + $autosave_is_different = true; + break; + } + } + } + $user_id = get_current_user_id(); // Store one autosave per author. If there is already an autosave, overwrite it. $old_autosave = wp_get_post_autosave( $post_id, $user_id ); + if ( ! $autosave_is_different && $old_autosave ) { + // Nothing to save, return the existing autosave. + return $old_autosave->ID; + } + if ( $old_autosave ) { - $new_autosave = _wp_post_revision_data( $post_data, true ); $new_autosave['ID'] = $old_autosave->ID; $new_autosave['post_author'] = $user_id; - // If the new autosave has the same content as the post, delete the autosave. - $autosave_is_different = false; - - foreach ( array_intersect( array_keys( $new_autosave ), array_keys( _wp_post_revision_fields( $post ) ) ) as $field ) { - if ( normalize_whitespace( $new_autosave[ $field ] ) !== normalize_whitespace( $post->$field ) ) { - $autosave_is_different = true; - break; - } - } - - if ( ! $autosave_is_different ) { - wp_delete_post_revision( $old_autosave->ID ); - 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 ) - ); - } - /** This filter is documented in wp-admin/post.php */ do_action( 'wp_creating_autosave', $new_autosave ); // wp_update_post() expects escaped array. - return wp_update_post( wp_slash( $new_autosave ) ); + $revision_id = wp_update_post( wp_slash( $new_autosave ) ); + } else { + // Create the new autosave as a special post revision. + $revision_id = _wp_put_post_revision( $post_data, true ); + } + + if ( is_wp_error( $revision_id ) || 0 === $revision_id ) { + return $revision_id; } - // Create the new autosave as a special post revision. - return _wp_put_post_revision( $post_data, true ); + // Attached any passed meta values that have revisions enabled. + if ( ! empty( $meta ) ) { + foreach ( $revisioned_meta_keys as $meta_key ) { + if ( isset( $meta[ $meta_key ] ) ) { + update_metadata( 'post', $revision_id, $meta_key, wp_slash( $meta[ $meta_key ] ) ); + } + } + } + + return $revision_id; } /** @@ -404,10 +446,10 @@ */ public function prepare_item_for_response( $item, $request ) { // Restores the more descriptive, specific name for use within this method. - $post = $item; + $post = $item; + $response = $this->revisions_controller->prepare_item_for_response( $post, $request ); - - $fields = $this->get_fields_for_response( $request ); + $fields = $this->get_fields_for_response( $request ); if ( in_array( 'preview_link', $fields, true ) ) { $parent_id = wp_is_post_autosave( $post );