From 244a20948092d21aaae12b31828f79f57b166315 Mon Sep 17 00:00:00 2001 From: Anthony Burchell Date: Sun, 11 Sep 2022 22:33:29 +0000 Subject: [PATCH] Autosave/REST API: Block autosaving from overwriting changes when locked from editing. Previously when a user was locked from editing a post in the block editor, autosave functionality was allowed to overwrite changes made by the editor that has taken control. This patch honors the lock status keeping autosave from conflicitng with other content editors. Props jhart35, adamsilverstein, sathyapulse, chanthaboune, primetimejas, joemcgill, kadamwhite. Fixes #55659. git-svn-id: https://develop.svn.wordpress.org/trunk@54130 602fd350-edb4-49c9-b593-d223f7449a82 --- .../class-wp-rest-autosaves-controller.php | 10 ++- .../rest-api/rest-autosaves-controller.php | 67 +++++++++++++++++++ 2 files changed, 76 insertions(+), 1 deletion(-) 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 ); + } }