diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-autosaves-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-autosaves-controller.php index 0a2cf5d49b..f99a201974 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-autosaves-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-autosaves-controller.php @@ -220,7 +220,15 @@ class WP_REST_Autosaves_Controller extends WP_REST_Revisions_Controller { $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 ) { + // 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 ); diff --git a/tests/phpunit/tests/rest-api/rest-autosaves-controller.php b/tests/phpunit/tests/rest-api/rest-autosaves-controller.php index 0128c0dcec..2a7eacb8b9 100644 --- a/tests/phpunit/tests/rest-api/rest-autosaves-controller.php +++ b/tests/phpunit/tests/rest-api/rest-autosaves-controller.php @@ -607,4 +607,71 @@ class WP_Test_REST_Autosaves_Controller extends WP_Test_REST_Post_Type_Controlle $response = rest_get_server()->dispatch( $request ); $this->assertNotEquals( 'garbage', get_post( self::$draft_page_id )->comment_status ); } + + /** + * Test ensuring that autosave from the original author doesn't overwrite changes after it has been taken over by a 2nd author. + * + * @ticket 55659 + */ + public function test_rest_autosave_draft_post_locked_to_different_author() { + + // Create a post by the editor. + $post_data = array( + 'post_content' => 'Test post content', + 'post_title' => 'Test post title', + 'post_excerpt' => 'Test post excerpt', + 'post_author' => self::$editor_id, + 'post_status' => 'draft', + ); + $post_id = wp_insert_post( $post_data ); + + // Set the post lock to the contributor, simulating a takeover of the post. + wp_set_current_user( self::$contributor_id ); + wp_set_post_lock( $post_id ); + + // Update the post with new data from the contributor. + $updated_post_data = array( + 'ID' => $post_id, + 'post_content' => 'New post content from the contributor', + 'post_title' => 'New post title', + ); + wp_update_post( $updated_post_data ); + + // Set the current user to the editor and initiate an autosave with some new data. + wp_set_current_user( self::$editor_id ); + $autosave_data = array( + 'id' => $post_id, + 'content' => 'Updated post content', + 'excerpt' => 'A new excerpt to test', + 'title' => $post_data['post_title'], + ); + + // Initiate an autosave via the REST API as Gutenberg does. + $request = new WP_REST_Request( 'POST', '/wp/v2/posts/' . self::$post_id . '/autosaves' ); + $request->add_header( 'content-type', 'application/json' ); + $request->set_body( wp_json_encode( $autosave_data ) ); + + $response = rest_get_server()->dispatch( $request ); + $new_data = $response->get_data(); + + // The current version of our test post. + $current_post = get_post( $post_id ); + + // The new data from the autosave should have its parent ID set to the original post ID. + $this->assertSame( $post_id, $new_data['parent'] ); + + // The post title and content should still be the updated versions from the contributor. + $this->assertSame( $current_post->post_title, $updated_post_data['post_title'] ); + $this->assertSame( $current_post->post_content, $updated_post_data['post_content'] ); + + // The excerpt should have stayed the same. + $this->assertSame( $current_post->post_excerpt, $post_data['post_excerpt'] ); + + $autosave_post = wp_get_post_autosave( $post_id ); + + // Has changes. + $this->assertSame( $autosave_data['content'], $autosave_post->post_content ); + + wp_delete_post( $post_id ); + } }