Skip to content

Commit

Permalink
Modify autosave logic so that backend and frontend perform the same c…
Browse files Browse the repository at this point in the history
…hecks

This modifies the checks that are carried out to determine whether a post
is autosaveable. Previously, the frontend carried out a comparison
between the current autosave and the proposed autosave. If there were
differences, the post would be saved. The backend, however, carried out
checks between the latest post content and the proposed autosave.

Because the frontend and backend checks were different failures could
occur on the backend in certain conditions (when the post is different
to the latest autosave, but the same as the current post).

This commit ensures the frontend makes the same checks as the backend.

The proposed autosave is checked first against post content. If different,
the proposed autosave is next checked against the most recent autosave.
  • Loading branch information
talldan committed Sep 25, 2018
1 parent 7fe4c03 commit 9e1a31b
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 38 deletions.
40 changes: 29 additions & 11 deletions lib/class-wp-rest-autosaves-controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,26 @@ public function get_item_schema() {
return $schema;
}

/**
* Checks whether the post data has the same values for common fields.
*
* @since 5.0.0
*
* @param array $post_data An associative array of post data.
* @param object $comparison_post A post object to compare against.
*
* @return boolean Do the posts have the same value for common fields.
*/
public function is_post_equal( $post_data, $comparison_post ) {
foreach ( array_intersect( array_keys( $post_data ), array_keys( _wp_post_revision_fields( $comparison_post ) ) ) as $field ) {
if ( normalize_whitespace( $post_data[ $field ] ) != normalize_whitespace( $comparison_post->$field ) ) {
return false;
}
}

return true;
}

/**
* Creates autosave for the specified post.
*
Expand Down Expand Up @@ -311,21 +331,19 @@ public function create_post_autosave( $post_data ) {
$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 ) {
// If the new autosave has the same content as the post, delete the autosave and return an error.
if ( $this->is_post_equal( $new_autosave, $post ) ) {
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.', 'gutenberg' ), array( 'status' => 400 ) );
}

// If there's an autosave newer than the last save of the post, also check against that and don't save if they're equal.
$old_autosave_modified_date = mysql2date( 'U', $old_autosave->post_modified_gmt, false );
$post_modified_date = mysql2date( 'U', $post->post_modified_gmt, false );
if ( $old_autosave_modified_date > $post_modified_date && $this->is_post_equal( $new_autosave, $old_autosave ) ) {
return new WP_Error( 'rest_autosave_no_changes', __( 'There is nothing to save. The autosave has the same content as the most recent autosave.', 'gutenberg' ), array( 'status' => 400 ) );
}

/**
* This filter is documented in wp-admin/post.php.
*/
Expand Down
18 changes: 16 additions & 2 deletions packages/editor/src/store/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -398,9 +398,23 @@ export function isEditedPostAutosaveable( state ) {
return true;
}

// If the title, excerpt or content has changed, the post is autosaveable.
const comparisonFields = [ 'title', 'excerpt', 'content' ];

// If the title, excerpt or content has not changed from the last save,
// the post is not autosaveable.
const savedPost = getCurrentPost( state );
const isModifiedFromLastSave = comparisonFields.some( ( field ) => (
savedPost[ field ] !== getEditedPostAttribute( state, field )
) );

if ( ! isModifiedFromLastSave ) {
return false;
}

// If the title, excerpt or content has not changed from the most recent autosave,
// the post is not autosaveable.
const autosave = getAutosave( state );
return [ 'title', 'excerpt', 'content' ].some( ( field ) => (
return comparisonFields.some( ( field ) => (
autosave[ field ] !== getEditedPostAttribute( state, field )
) );
}
Expand Down
54 changes: 29 additions & 25 deletions packages/editor/src/store/test/selectors.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* External dependencies
*/
import { filter, without } from 'lodash';
import { filter } from 'lodash';

/**
* WordPress dependencies
Expand Down Expand Up @@ -1031,14 +1031,16 @@ describe( 'selectors', () => {
expect( isEditedPostAutosaveable( state ) ).toBe( true );
} );

it( 'should return false if none of title, excerpt, or content have changed', () => {
it( 'should return false if none of title, excerpt, or content have changed from the currentPost or autosave', () => {
const state = {
editor: {
present: {
blocksByClientId: {},
blockOrder: {},
edits: {
title: 'foo',
content: 'foo',
excerpt: 'foo',
},
},
},
Expand All @@ -1058,33 +1060,35 @@ describe( 'selectors', () => {
expect( isEditedPostAutosaveable( state ) ).toBe( false );
} );

it( 'should return true if title, excerpt, or content have changed', () => {
it( 'should return true if title, excerpt, or content have changed from the currentPost or autosave', () => {
for ( const variantField of [ 'title', 'excerpt', 'content' ] ) {
for ( const constantField of without( [ 'title', 'excerpt', 'content' ], variantField ) ) {
const state = {
editor: {
present: {
blocksByClientId: {},
blockOrder: {},
edits: {
content: 'foo',
},
const state = {
editor: {
present: {
blocksByClientId: {},
blockOrder: {},
edits: {
title: 'foo',
content: 'foo',
excerpt: 'foo',
[ variantField ]: 'bar',
},
},
currentPost: {
title: 'foo',
content: 'foo',
excerpt: 'foo',
},
saving: {},
autosave: {
[ constantField ]: 'foo',
[ variantField ]: 'bar',
},
};
},
currentPost: {
title: 'foo',
content: 'foo',
excerpt: 'foo',
},
saving: {},
autosave: {
title: 'foo',
content: 'foo',
excerpt: 'foo',
},
};

expect( isEditedPostAutosaveable( state ) ).toBe( true );
}
expect( isEditedPostAutosaveable( state ) ).toBe( true );
}
} );
} );
Expand Down

0 comments on commit 9e1a31b

Please sign in to comment.