From b8bf5efda6d93a3587237f744cc0bd37f0d6ec36 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Mon, 17 Jul 2023 15:18:33 -0600 Subject: [PATCH 001/101] =?UTF-8?q?Add=20revisioning=20of=20post=20meta,?= =?UTF-8?q?=20including=20=E2=80=98footnotes=E2=80=99=20by=20default?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/wp-admin/includes/post.php | 62 ++++++++++++- src/wp-includes/default-filters.php | 3 + src/wp-includes/revision.php | 139 ++++++++++++++++++++++++++++ 3 files changed, 202 insertions(+), 2 deletions(-) diff --git a/src/wp-admin/includes/post.php b/src/wp-admin/includes/post.php index ed73b8ff5fd14..4bc92d1e4cc7c 100644 --- a/src/wp-admin/includes/post.php +++ b/src/wp-admin/includes/post.php @@ -1961,7 +1961,7 @@ function wp_create_post_autosave( $post_data ) { * @param array $new_autosave Post array - the autosave that is about to be saved. */ do_action( 'wp_creating_autosave', $new_autosave ); - + wp_autosave_post_revisioned_meta_fields( $new_autosave ); return wp_update_post( $new_autosave ); } @@ -1969,7 +1969,65 @@ function wp_create_post_autosave( $post_data ) { $post_data = wp_unslash( $post_data ); // Otherwise create the new autosave as a special post revision. - return _wp_put_post_revision( $post_data, true ); + $revision = _wp_put_post_revision( $post_data, true ); + + // Update the revisioned meta data as well. + wp_autosave_post_revisioned_meta_fields( $revision ); + return $revision; +} + +/** + * Autosave the revisioned meta fields. + * + * Iterates thru the revisioned meta fields and checks each to see if they are set, + * and have a changed value. If so, the meta value is saved and attached to the autosave. + * + * @since 6.4.0 + * + * @param Post object $new_autosave The new post being autosaved. + */ +function wp_autosave_post_revisioned_meta_fields( $new_autosave ) { + /* + * The post data arrives as either $_POST['data']['wp_autosave'] or the $_POST + * itself. This sets $posted_data to the correct variable. + * + * Ignoring sanitization to avoid altering meta. Ignoring the nonce check because + * this is hooked on inner core hooks where a valid nonce was already checked. + * + * @phpcs:disable WordPress.Security + */ + $posted_data = isset( $_POST['data']['wp_autosave'] ) ? $_POST['data']['wp_autosave'] : $_POST; + // phpcs:enable + + /* + * Go thru the revisioned meta keys and save them as part of the autosave, if + * the meta key is part of the posted data, the meta value is not blank and + * the the meta value has changes from the last autosaved value. + */ + foreach ( wp_post_revision_meta_keys() as $meta_key ) { + + if ( + isset( $posted_data[ $meta_key ] ) && + get_post_meta( $new_autosave['ID'], $meta_key, true ) !== wp_unslash( $posted_data[ $meta_key ] ) + ) { + /* + * Use the underlying delete_metadata() and add_metadata() functions + * vs delete_post_meta() and add_post_meta() to make sure we're working + * with the actual revision meta. + */ + delete_metadata( 'post', $new_autosave['ID'], $meta_key ); + + /* + * One last check to ensure meta value not empty(). + */ + if ( ! empty( $posted_data[ $meta_key ] ) ) { + /* + * Add the revisions meta data to the autosave. + */ + add_metadata( 'post', $new_autosave['ID'], $meta_key, $posted_data[ $meta_key ] ); + } + } + } } /** diff --git a/src/wp-includes/default-filters.php b/src/wp-includes/default-filters.php index bf39f29b4dd93..f0afed91e9939 100644 --- a/src/wp-includes/default-filters.php +++ b/src/wp-includes/default-filters.php @@ -719,4 +719,7 @@ // CPT wp_block custom postmeta field. add_action( 'init', 'wp_create_initial_post_meta' ); +// Including revisioned meta when considering whether a post revision has changed. +add_filter( 'wp_save_post_revision_post_has_changed', 'wp_check_revisioned_meta_fields_have_changed' ), 10, 3 ); + unset( $filter, $action ); diff --git a/src/wp-includes/revision.php b/src/wp-includes/revision.php index e8bbcdbce4e52..8b1a47d67446f 100644 --- a/src/wp-includes/revision.php +++ b/src/wp-includes/revision.php @@ -365,11 +365,34 @@ function _wp_put_post_revision( $post = null, $autosave = false ) { * @param int $revision_id Post revision ID. */ do_action( '_wp_put_post_revision', $revision_id ); + + // Save any revisioned meta fields. + wp_save_revisioned_meta_fields( $revision_id ); } return $revision_id; } + +/** + * Save the revisioned meta fields. + * + * @param int $revision_id The ID of the revision to save the meta to. + * + * @since 6.4.0 + */ +function wp_save_revisioned_meta_fields( $revision_id ) { + $revision = get_post( $revision_id ); + $post_id = $revision->post_parent; + + // Save revisioned meta fields. + foreach ( wp_post_revision_meta_keys() as $meta_key ) { + if ( metadata_exists( 'post', $post_id, $meta_key ) ) { + _wp_copy_post_meta( $post_id, $revision_id, $meta_key ); + } + } +} + /** * Gets a post revision. * @@ -450,6 +473,9 @@ function wp_restore_post_revision( $revision, $fields = null ) { // Update last edit user. update_post_meta( $post_id, '_edit_last', get_current_user_id() ); + // Restore any revisioned meta fields. + wp_restore_post_revision_meta( $post_id, $revision['ID'] ); + /** * Fires after a post revision has been restored. * @@ -463,6 +489,83 @@ function wp_restore_post_revision( $revision, $fields = null ) { return $post_id; } +/** + * Restore the revisioned meta values for a post. + * + * @param int $post_id The ID of the post to restore the meta to. + * @param int $revision_id The ID of the revision to restore the meta from. + * + * @since 6.4.0 + */ +function wp_restore_post_revision_meta( $post_id, $revision_id ) { + + // Restore revisioned meta fields. + foreach ( (array) wp_post_revision_meta_keys() as $meta_key ) { + + // Clear any existing meta. + delete_post_meta( $post_id, $meta_key ); + + _wp_copy_post_meta( $revision_id, $post_id, $meta_key ); + } +} + +/** + * Copy post meta for the given key from one post to another. + * + * @param int $source_post_id Post ID to copy meta value(s) from. + * @param int $target_post_id Post ID to copy meta value(s) to. + * @param string $meta_key Meta key to copy. + * + * @since 6.4.0 + */ +function _wp_copy_post_meta( $source_post_id, $target_post_id, $meta_key ) { + + foreach ( get_post_meta( $source_post_id, $meta_key ) as $meta_value ) { + /** + * We use add_metadata() function vs add_post_meta() here + * to allow for a revision post target OR regular post. + */ + add_metadata( 'post', $target_post_id, $meta_key, wp_slash( $meta_value ) ); + } +} + +/** + * Determine which post meta fields should be revisioned. + * + * @since 6.4.0 + * + * @return array An array of meta keys to be revisioned. + */ +function wp_post_revision_meta_keys() { + /** + * Filter the list of post meta keys to be revisioned. + * + * @since 6.4.0 + * + * @param array $keys An array of default meta fields to be revisioned. + */ + return apply_filters( 'wp_post_revision_meta_keys', array( 'footnotes' ) ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals +} + +/** + * Check whether revisioned post meta fields have changed. + * + * @param bool $post_has_changed Whether the post has changed. + * @param WP_Post $last_revision The last revision post object. + * @param WP_Post $post The post object. + * + * @since 6.4.0 + */ +function wp_check_revisioned_meta_fields_have_changed( $post_has_changed, WP_Post $last_revision, WP_Post $post ) { + foreach ( wp_post_revision_meta_keys() as $meta_key ) { + if ( get_post_meta( $post->ID, $meta_key ) !== get_post_meta( $last_revision->ID, $meta_key ) ) { + $post_has_changed = true; + break; + } + } + return $post_has_changed; +} + /** * Deletes a revision. * @@ -728,6 +831,7 @@ function _set_preview( $post ) { add_filter( 'get_the_terms', '_wp_preview_terms_filter', 10, 3 ); add_filter( 'get_post_metadata', '_wp_preview_post_thumbnail_filter', 10, 3 ); + add_filter( 'get_post_metadata', '_wp_preview_meta_filter', 10, 4 ); return $post; } @@ -948,3 +1052,38 @@ function _wp_upgrade_revisions_of_post( $post, $revisions ) { return true; } + +/** + * Filters preview post meta retrieval to get values from the autosave. + * + * Filters revisioned meta keys only. + * + * @since 6.4.0 + * + * @param mixed $value Meta value to filter. + * @param int $object_id Object ID. + * @param string $meta_key Meta key to filter a value for. + * @param bool $single Whether to return a single value. Default false. + * @return mixed Original meta value if the meta key isn't revisioned, the object doesn't exist, + * the post type is a revision or the post ID doesn't match the object ID. + * Otherwise, the revisioned meta value is returned for the preview. + */ +function _wp_preview_meta_filter( $value, $object_id, $meta_key, $single ) { + + $post = get_post(); + if ( + empty( $post ) || + $post->ID !== $object_id || + ! in_array( $meta_key, wp_post_revision_meta_keys(), true ) || + 'revision' === $post->post_type + ) { + return $value; + } + + $preview = wp_get_post_autosave( $post->ID ); + if ( ! is_object( $preview ) ) { + return $value; + } + + return get_post_meta( $preview->ID, $meta_key, $single ); +} From 6667610cc0a5f526aab66a96a7b6b79450f4447c Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Mon, 17 Jul 2023 16:42:09 -0600 Subject: [PATCH 002/101] correct typo in `src/wp-includes/default-filters.php` --- src/wp-includes/default-filters.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/default-filters.php b/src/wp-includes/default-filters.php index f0afed91e9939..6b7a1cdc841af 100644 --- a/src/wp-includes/default-filters.php +++ b/src/wp-includes/default-filters.php @@ -720,6 +720,6 @@ add_action( 'init', 'wp_create_initial_post_meta' ); // Including revisioned meta when considering whether a post revision has changed. -add_filter( 'wp_save_post_revision_post_has_changed', 'wp_check_revisioned_meta_fields_have_changed' ), 10, 3 ); +add_filter( 'wp_save_post_revision_post_has_changed', 'wp_check_revisioned_meta_fields_have_changed', 10, 3 ); unset( $filter, $action ); From 15e9e4ee32de23c4e4549aa9480d1f4bddb173c4 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Mon, 17 Jul 2023 16:45:18 -0600 Subject: [PATCH 003/101] add tests for meta revisioning --- tests/phpunit/tests/post/metaRevisions.php | 536 +++++++++++++++++++++ 1 file changed, 536 insertions(+) create mode 100644 tests/phpunit/tests/post/metaRevisions.php diff --git a/tests/phpunit/tests/post/metaRevisions.php b/tests/phpunit/tests/post/metaRevisions.php new file mode 100644 index 0000000000000..9c04c9d7cce5f --- /dev/null +++ b/tests/phpunit/tests/post/metaRevisions.php @@ -0,0 +1,536 @@ +revisioned_keys = array( 'meta_revision_test' ); + } + + /** + * Callback function to add the revisioned keys. + * + * @param array $keys The array of revisioned keys. + * + * @return array + */ + public function add_revisioned_keys( $keys ) { + $keys[] = 'meta_revision_test'; + $keys[] = 'meta_multiples_test'; + return $keys; + } + + /** + * Test the revisions system for storage of meta values with slashes. + * + * @param string $passed The passed data for testing. + * + * @param string $expected The expected value after storing & retrieving. + * + * @group revision + * @group slashed + * @dataProvider slashed_data_provider + */ + public function test_revisions_stores_meta_values_with_slashes( $passed, $expected ) { + // Set up a new post. + $post_id = $this->factory->post->create(); + $original_post_id = $post_id; + + // And update to store an initial revision. + wp_update_post( + array( + 'post_content' => 'some initial content', + 'ID' => $post_id, + ) + ); + add_filter( 'wp_post_revision_meta_keys', array( $this, 'add_revisioned_keys' ) ); + + // Store a custom meta value, which is not revisioned by default. + update_post_meta( $post_id, 'meta_revision_test', wp_slash( $passed ) ); + $this->assertEquals( $expected, get_post_meta( $post_id, 'meta_revision_test', true ) ); + + // Update the post, storing a revision. + wp_update_post( + array( + 'post_content' => 'some more content', + 'ID' => $post_id, + ) + ); + + // Overwrite. + update_post_meta( $post_id, 'meta_revision_test', 'original' ); + // Update the post, storing a revision. + wp_update_post( + array( + 'post_content' => 'some more content again', + 'ID' => $post_id, + ) + ); + + // Restore the previous revision. + $revisions = (array) wp_get_post_revisions( $post_id ); + + // Go back two to load the previous revision. + array_shift( $revisions ); + $last_revision = array_shift( $revisions ); + + // Restore! + wp_restore_post_revision( $last_revision->ID ); + + $this->assertEquals( $expected, get_post_meta( $post_id, 'meta_revision_test', true ) ); + remove_filter( 'wp_post_revision_meta_keys', array( $this, 'add_revisioned_keys' ) ); + } + + /** + * Provide data for the slashed data tests. + */ + public function slashed_data_provider() { + return array( + array( + 'some\text', + 'some\text', + ), + array( + 'test some\ \\extra \\\slashed \\\\text ', + 'test some\ \\extra \\\slashed \\\\text ', + ), + array( + "This \'is\' an example \n of a \"quoted\" string", + "This \'is\' an example \n of a \"quoted\" string", + ), + array( + 'some unslashed text just to test! % & * ( ) #', + 'some unslashed text just to test! % & * ( ) #', + ), + ); + } + + /** + * Test the revisions system for storage of meta values. + * + * @group revision + */ + public function test_revisions_stores_meta_values() { + /* + * Set Up. + */ + + // Set up a new post. + $post_id = $this->factory->post->create(); + $original_post_id = $post_id; + + // And update to store an initial revision. + wp_update_post( + array( + 'post_content' => 'some initial content', + 'ID' => $post_id, + ) + ); + + // One revision so far. + $revisions = wp_get_post_revisions( $post_id ); + $this->assertCount( 1, $revisions ); + + /* + * First set up a meta value. + */ + + // Store a custom meta value, which is not revisioned by default. + update_post_meta( $post_id, 'meta_revision_test', 'original' ); + + // Update the post, storing a revision. + wp_update_post( + array( + 'post_content' => 'some more content', + 'ID' => $post_id, + ) + ); + + $revisions = wp_get_post_revisions( $post_id ); + $this->assertCount( 2, $revisions ); + + // Next, store some updated meta values for the same key. + update_post_meta( $post_id, 'meta_revision_test', 'update1' ); + + // Save the post, changing content to force a revision. + wp_update_post( + array( + 'post_content' => 'some updated content', + 'ID' => $post_id, + ) + ); + + $revisions = wp_get_post_revisions( $post_id ); + $this->assertCount( 3, $revisions ); + + /* + * Now restore the original revision. + */ + + // Restore the previous revision. + $revisions = (array) wp_get_post_revisions( $post_id ); + + // Go back two to load the previous revision. + array_shift( $revisions ); + $last_revision = array_shift( $revisions ); + + // Restore! + wp_restore_post_revision( $last_revision->ID ); + + wp_update_post( array( 'ID' => $post_id ) ); + $revisions = wp_get_post_revisions( $post_id ); + $this->assertCount( 4, $revisions ); + + /* + * Check the meta values to verify they are NOT revisioned - they are not revisioned by default. + */ + + // Custom post meta should NOT be restored, orignal value should not be restored, value still 'update1'. + $this->assertEquals( 'update1', get_post_meta( $post_id, 'meta_revision_test', true ) ); + + update_post_meta( $post_id, 'meta_revision_test', 'update2' ); + + /* + * Test the revisioning of custom meta when enabled by the wp_post_revision_meta_keys filter. + */ + + // Add the custom field to be revised via the wp_post_revision_meta_keys filter. + add_filter( 'wp_post_revision_meta_keys', array( $this, 'add_revisioned_keys' ) ); + + // Save the post, changing content to force a revision. + wp_update_post( + array( + 'post_content' => 'more updated content', + 'ID' => $post_id, + ) + ); + + $revisions = array_values( wp_get_post_revisions( $post_id ) ); + $this->assertCount( 5, $revisions ); + $this->assertEquals( 'update2', get_post_meta( $revisions[0]->ID, 'meta_revision_test', true ) ); + + // Store custom meta values, which should now be revisioned. + update_post_meta( $post_id, 'meta_revision_test', 'update3' ); + + /* + * Save the post again, custom meta should now be revisioned. + * + * Note that a revision is saved even though there is no change + * in post content, because the revisioned post_meta has changed. + */ + wp_update_post( + array( + 'ID' => $post_id, + ) + ); + + // This revision contains the existing post meta ('update3'). + $revisions = wp_get_post_revisions( $post_id ); + $this->assertCount( 6, $revisions ); + + // Verify that previous post meta is set. + $this->assertEquals( 'update3', get_post_meta( $post_id, 'meta_revision_test', true ) ); + + // Restore the previous revision. + $revisions = wp_get_post_revisions( $post_id ); + + // Go back two to load the previous revision. + array_shift( $revisions ); + $last_revision = array_shift( $revisions ); + wp_restore_post_revision( $last_revision->ID ); + + /* + * Verify that previous post meta is restored. + */ + $this->assertEquals( 'update2', get_post_meta( $post_id, 'meta_revision_test', true ) ); + + // Try storing a blank meta. + update_post_meta( $post_id, 'meta_revision_test', '' ); + wp_update_post( + array( + 'ID' => $post_id, + ) + ); + + update_post_meta( $post_id, 'meta_revision_test', 'update 4' ); + wp_update_post( + array( + 'ID' => $post_id, + ) + ); + + // Restore the previous revision. + $revisions = wp_get_post_revisions( $post_id ); + array_shift( $revisions ); + $last_revision = array_shift( $revisions ); + wp_restore_post_revision( $last_revision->ID ); + + /* + * Verify that previous blank post meta is restored. + */ + $this->assertEquals( '', get_post_meta( $post_id, 'meta_revision_test', true ) ); + + /* + * Test not tracking a key - remove the key from the revisioned meta. + */ + remove_all_filters( 'wp_post_revision_meta_keys' ); + + // Meta should no longer be revisioned. + update_post_meta( $post_id, 'meta_revision_test', 'update 5' ); + wp_update_post( + array( + 'ID' => $post_id, + 'post_content' => 'changed content', + ) + ); + update_post_meta( $post_id, 'meta_revision_test', 'update 6' ); + wp_update_post( + array( + 'ID' => $post_id, + 'post_content' => 'go updated content', + ) + ); + + // Restore the previous revision. + $revisions = wp_get_post_revisions( $post_id ); + array_shift( $revisions ); + $last_revision = array_shift( $revisions ); + wp_restore_post_revision( $last_revision->ID ); + + /* + * Verify that previous post meta is NOT restored. + */ + $this->assertEquals( 'update 6', get_post_meta( $post_id, 'meta_revision_test', true ) ); + + // Add the custom field to be revised via the wp_post_revision_meta_keys filter. + add_filter( 'wp_post_revision_meta_keys', array( $this, 'add_revisioned_keys' ) ); + + /* + * Test the revisioning of multiple meta keys. + */ + + // Add three values for meta. + update_post_meta( $post_id, 'meta_revision_test', 'update 7' ); + add_post_meta( $post_id, 'meta_revision_test', 'update 7 number 2' ); + add_post_meta( $post_id, 'meta_revision_test', 'update 7 number 3' ); + wp_update_post( array( 'ID' => $post_id ) ); + + // Update all three values. + update_post_meta( $post_id, 'meta_revision_test', 'update 8', 'update 7' ); + update_post_meta( $post_id, 'meta_revision_test', 'update 8 number 2', 'update 7 number 2' ); + update_post_meta( $post_id, 'meta_revision_test', 'update 8 number 3', 'update 7 number 3' ); + + // Restore the previous revision. + $revisions = wp_get_post_revisions( $post_id ); + $last_revision = array_shift( $revisions ); + wp_restore_post_revision( $last_revision->ID ); + + /* + * Verify that multiple metas stored correctly. + */ + $this->assertEquals( array( 'update 7', 'update 7 number 2', 'update 7 number 3' ), get_post_meta( $post_id, 'meta_revision_test' ) ); + + /* + * Test the revisioning of a multidimensional array. + */ + $test_array = array( + 'a' => array( + '1', + '2', + '3', + ), + 'b' => 'ok', + 'c' => array( + 'multi' => array( + 'a', + 'b', + 'c', + ), + 'not' => 'ok', + ), + ); + + // Clear any old value. + delete_post_meta( $post_id, 'meta_revision_test' ); + + // Set the test meta to the array. + update_post_meta( $post_id, 'meta_revision_test', $test_array ); + + // Update to save. + wp_update_post( array( 'ID' => $post_id ) ); + + // Set the test meta blank. + update_post_meta( $post_id, 'meta_revision_test', '' ); + + // Restore the previous revision. + $revisions = wp_get_post_revisions( $post_id ); + $last_revision = array_shift( $revisions ); + wp_restore_post_revision( $last_revision->ID ); + + /* + * Verify multidimensional array stored correctly. + */ + $stored_array = get_post_meta( $post_id, 'meta_revision_test' ); + $this->assertEquals( $test_array, $stored_array[0] ); + /* + + * Test multiple revisions on the same key. + */ + + // Set the test meta to the array. + add_post_meta( $post_id, 'meta_multiples_test', 'test1' ); + add_post_meta( $post_id, 'meta_multiples_test', 'test2' ); + add_post_meta( $post_id, 'meta_multiples_test', 'test3' ); + + // Update to save. + wp_update_post( array( 'ID' => $post_id ) ); + + $stored_array = get_post_meta( $post_id, 'meta_multiples_test' ); + $expect = array( 'test1', 'test2', 'test3' ); + + $this->assertEquals( $expect, $stored_array ); + + // Restore the previous revision. + $revisions = wp_get_post_revisions( $post_id ); + $last_revision = array_shift( $revisions ); + wp_restore_post_revision( $last_revision->ID ); + + $stored_array = get_post_meta( $post_id, 'meta_multiples_test' ); + $expect = array( 'test1', 'test2', 'test3' ); + + $this->assertEquals( $expect, $stored_array ); + + // Cleanup! + wp_delete_post( $original_post_id ); + } + + /** + * Verify that only existing meta is revisioned. + */ + public function only_existing_meta_is_revisioned() { + $this->revisioned_keys[] = 'foo'; + $this->revisioned_keys[] = 'bar'; + + add_filter( 'wp_post_revision_meta_keys', array( $this, 'add_revisioned_keys' ) ); + + // Set up a new post. + $post_id = $this->factory->post->create( + array( + 'post_content' => 'initial content', + ) + ); + + // Revision v1. + wp_update_post( + array( + 'ID' => $post_id, + 'post_content' => 'updated content v1', + ) + ); + + $this->assertPostNotHasMetaKey( $post_id, 'foo' ); + $this->assertPostNotHasMetaKey( $post_id, 'bar' ); + + $revisions = wp_get_post_revisions( $post_id ); + $revision = array_shift( $revisions ); + $this->assertEmpty( get_metadata( 'post', $revision->ID ) ); + + // Revision v2. + wp_update_post( + array( + 'ID' => $post_id, + 'post_content' => 'updated content v2', + 'meta_input' => array( + 'foo' => 'foo v2', + ), + ) + ); + + $this->assertPostHasMetaKey( $post_id, 'foo' ); + $this->assertPostNotHasMetaKey( $post_id, 'bar' ); + $this->assertPostNotHasMetaKey( $post_id, 'meta_revision_test' ); + + $revisions = wp_get_post_revisions( $post_id ); + $revision = array_shift( $revisions ); + $this->assertPostHasMetaKey( $revision->ID, 'foo' ); + $this->assertPostNotHasMetaKey( $revision->ID, 'bar' ); + $this->assertPostNotHasMetaKey( $revision->ID, 'meta_revision_test' ); + } + + /** + * Verify that blank strings are revisioned correctly. + */ + public function blank_meta_is_revisioned() { + $this->revisioned_keys[] = 'foo'; + + add_filter( 'wp_post_revision_meta_keys', array( $this, 'add_revisioned_keys' ) ); + + // Set up a new post. + $post_id = $this->factory->post->create( + array( + 'post_content' => 'initial content', + 'meta_input' => array( + 'foo' => 'foo', + ), + ) + ); + + // Set the test meta to an empty string. + update_post_meta( $post_id, 'foo', '' ); + + // Update to save. + wp_update_post( array( 'ID' => $post_id ) ); + + $stored_array = get_post_meta( $post_id, 'meta_multiples_test' ); + $expect = array( 'test1', 'test2', 'test3' ); + + $this->assertEquals( $expect, $stored_array ); + + // Restore the previous revision. + $revisions = wp_get_post_revisions( $post_id ); + $last_revision = array_shift( $revisions ); + wp_restore_post_revision( $last_revision->ID ); + $stored_data = get_post_meta( $post_id, 'foo' ); + $this->assertEquals( '', $stored_data[0] ); + } + + /** + * Assert the a post has a meta key. + * + * @param int $post_id The ID of the post to check. + * @param string $meta_key The meta key to check for. + */ + protected function assertPostHasMetaKey( $post_id, $meta_key ) { + $this->assertEquals( $expect, $stored_array ); + } + + /** + * Assert that post does not have a meta key. + * + * @param int $post_id The ID of the post to check. + * @param string $meta_key The meta key to check for. + */ + protected function assertPostNotHasMetaKey( $post_id, $meta_key ) { + $this->assertArrayNotHasKey( $meta_key, get_metadata( 'post', $post_id ) ); + } +} From b89232357006253e4905fe6b71777038269abc27 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Mon, 17 Jul 2023 17:58:33 -0600 Subject: [PATCH 004/101] Try: revisioning in the rest endpoint directly, after meta is saved --- .../endpoints/class-wp-rest-posts-controller.php | 12 ++++++++++++ src/wp-includes/revision.php | 8 ++------ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php index 4ec972347fb8f..eb39e242ebe55 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php @@ -743,6 +743,12 @@ public function create_item( $request ) { } } + // Add meta to revision. + $revision = wp_get_post_revision( $post_id ); + if ( $revision ) { + wp_save_revisioned_meta_fields( $revision->post_id, $post_id ); + } + $post = get_post( $post_id ); $fields_update = $this->update_additional_fields_for_object( $post, $request ); @@ -936,6 +942,12 @@ public function update_item( $request ) { return $fields_update; } + // Add meta to revision. + $revision_id = wp_get_post_revision( $post_id ); + if ( $revision_id ) { + wp_save_revisioned_meta_fields( $revision_id, $post_id ); + } + $request->set_param( 'context', 'edit' ); // Filter is fired in WP_REST_Attachments_Controller subclass. diff --git a/src/wp-includes/revision.php b/src/wp-includes/revision.php index 8b1a47d67446f..8b1b75342ece0 100644 --- a/src/wp-includes/revision.php +++ b/src/wp-includes/revision.php @@ -365,9 +365,6 @@ function _wp_put_post_revision( $post = null, $autosave = false ) { * @param int $revision_id Post revision ID. */ do_action( '_wp_put_post_revision', $revision_id ); - - // Save any revisioned meta fields. - wp_save_revisioned_meta_fields( $revision_id ); } return $revision_id; @@ -378,12 +375,11 @@ function _wp_put_post_revision( $post = null, $autosave = false ) { * Save the revisioned meta fields. * * @param int $revision_id The ID of the revision to save the meta to. + * @param int $post_id The ID of the post the revision is associated with. * * @since 6.4.0 */ -function wp_save_revisioned_meta_fields( $revision_id ) { - $revision = get_post( $revision_id ); - $post_id = $revision->post_parent; +function wp_save_revisioned_meta_fields( $revision_id, $post_id) { // Save revisioned meta fields. foreach ( wp_post_revision_meta_keys() as $meta_key ) { From 6235af5c503f55d43501e9207c5b275784ec583a Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 18 Jul 2023 08:52:01 -0600 Subject: [PATCH 005/101] Add _wp_save_post_revision_meta helper that copies revisioned meta to last stored revision --- src/wp-includes/revision.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/wp-includes/revision.php b/src/wp-includes/revision.php index 8b1b75342ece0..24909d537bb14 100644 --- a/src/wp-includes/revision.php +++ b/src/wp-includes/revision.php @@ -389,6 +389,25 @@ function wp_save_revisioned_meta_fields( $revision_id, $post_id) { } } +/** + * Copy any revisioned meta fields from the post to the most recently saved revision. + * + * @since 6.4.0 + * + */ +function _wp_save_post_revision_meta( $post_id ) { + // Exit early if no revisions are enabled. + if ( 0 >= wp_revisions_to_keep( get_post( $post_id ) ) ) { + return; + } + + // Add meta to revision. + $revision = wp_get_post_revision( $post_id ); + if ( $revision ) { + wp_save_revisioned_meta_fields( $revision->post_id, $post_id ); + } +} + /** * Gets a post revision. * From 3a877493bf99ead2067aa2b72694f24194408671 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 18 Jul 2023 08:56:41 -0600 Subject: [PATCH 006/101] remove direct REST API integration, move save to wp_after_insert_post --- src/wp-includes/post.php | 4 ++++ .../endpoints/class-wp-rest-posts-controller.php | 12 ------------ 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/wp-includes/post.php b/src/wp-includes/post.php index 22a37683eaf96..15ca0ab97f4e7 100644 --- a/src/wp-includes/post.php +++ b/src/wp-includes/post.php @@ -5458,6 +5458,10 @@ function wp_after_insert_post( $post, $update, $post_before ) { $post_id = $post->ID; + if ( $update ) { + _wp_save_post_revision_meta( $post_id ); + } + /** * Fires once a post, its terms and meta data has been saved. * diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php index eb39e242ebe55..4ec972347fb8f 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php @@ -743,12 +743,6 @@ public function create_item( $request ) { } } - // Add meta to revision. - $revision = wp_get_post_revision( $post_id ); - if ( $revision ) { - wp_save_revisioned_meta_fields( $revision->post_id, $post_id ); - } - $post = get_post( $post_id ); $fields_update = $this->update_additional_fields_for_object( $post, $request ); @@ -942,12 +936,6 @@ public function update_item( $request ) { return $fields_update; } - // Add meta to revision. - $revision_id = wp_get_post_revision( $post_id ); - if ( $revision_id ) { - wp_save_revisioned_meta_fields( $revision_id, $post_id ); - } - $request->set_param( 'context', 'edit' ); // Filter is fired in WP_REST_Attachments_Controller subclass. From 05d0f862326b78cc2cdc418b7cb2bfebac7360f4 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 18 Jul 2023 10:53:47 -0600 Subject: [PATCH 007/101] Clean up revisions save --- src/wp-includes/revision.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/wp-includes/revision.php b/src/wp-includes/revision.php index 24909d537bb14..66a121416dfc5 100644 --- a/src/wp-includes/revision.php +++ b/src/wp-includes/revision.php @@ -374,12 +374,12 @@ function _wp_put_post_revision( $post = null, $autosave = false ) { /** * Save the revisioned meta fields. * - * @param int $revision_id The ID of the revision to save the meta to. * @param int $post_id The ID of the post the revision is associated with. + * @param int $revision_id The ID of the revision to save the meta to. * * @since 6.4.0 */ -function wp_save_revisioned_meta_fields( $revision_id, $post_id) { +function wp_save_revisioned_meta_fields( $post_id, $revision_id ) { // Save revisioned meta fields. foreach ( wp_post_revision_meta_keys() as $meta_key ) { @@ -396,15 +396,15 @@ function wp_save_revisioned_meta_fields( $revision_id, $post_id) { * */ function _wp_save_post_revision_meta( $post_id ) { - // Exit early if no revisions are enabled. - if ( 0 >= wp_revisions_to_keep( get_post( $post_id ) ) ) { + // Exit early if no revisions are stored. + if ( 0 === wp_revisions_to_keep( get_post( $post_id ) ) ) { return; } // Add meta to revision. - $revision = wp_get_post_revision( $post_id ); - if ( $revision ) { - wp_save_revisioned_meta_fields( $revision->post_id, $post_id ); + $revisions = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) ); + if ( $revisions ) { + wp_save_revisioned_meta_fields( $post_id, $revisions[0]->post_id ); } } From 19e1f8a808565f97f89f121d3fb9c7bc7f12e4a3 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 18 Jul 2023 10:57:44 -0600 Subject: [PATCH 008/101] phpcbf --- src/wp-includes/revision.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/revision.php b/src/wp-includes/revision.php index 66a121416dfc5..d234e2682c964 100644 --- a/src/wp-includes/revision.php +++ b/src/wp-includes/revision.php @@ -404,7 +404,7 @@ function _wp_save_post_revision_meta( $post_id ) { // Add meta to revision. $revisions = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) ); if ( $revisions ) { - wp_save_revisioned_meta_fields( $post_id, $revisions[0]->post_id ); + wp_save_revisioned_meta_fields( $post_id, $revisions[0]->post_id ); } } From 0b86da90e3ae4562d1cd4ec2dc12c7a97dee9702 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 18 Jul 2023 16:10:20 -0600 Subject: [PATCH 009/101] Ensure `$revisions[0]->post_id ` is set before using --- src/wp-includes/revision.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/revision.php b/src/wp-includes/revision.php index d234e2682c964..f1ceead68de21 100644 --- a/src/wp-includes/revision.php +++ b/src/wp-includes/revision.php @@ -403,7 +403,7 @@ function _wp_save_post_revision_meta( $post_id ) { // Add meta to revision. $revisions = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) ); - if ( $revisions ) { + if ( $revisions && isset ( $revisions[0]->post_id ) ) { wp_save_revisioned_meta_fields( $post_id, $revisions[0]->post_id ); } } From 52dd94340b0822be8c1bc2ead3ee46b1b344b0d6 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 18 Jul 2023 17:07:09 -0600 Subject: [PATCH 010/101] Test work --- src/wp-includes/revision.php | 5 +++-- tests/phpunit/tests/post/metaRevisions.php | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/wp-includes/revision.php b/src/wp-includes/revision.php index f1ceead68de21..e37e674935559 100644 --- a/src/wp-includes/revision.php +++ b/src/wp-includes/revision.php @@ -403,8 +403,9 @@ function _wp_save_post_revision_meta( $post_id ) { // Add meta to revision. $revisions = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) ); - if ( $revisions && isset ( $revisions[0]->post_id ) ) { - wp_save_revisioned_meta_fields( $post_id, $revisions[0]->post_id ); + if ( $revisions ) { + $revision = array_shift( $revisions ); + wp_save_revisioned_meta_fields( $post_id, $revision->ID ); } } diff --git a/tests/phpunit/tests/post/metaRevisions.php b/tests/phpunit/tests/post/metaRevisions.php index 9c04c9d7cce5f..134c84f7d8a41 100644 --- a/tests/phpunit/tests/post/metaRevisions.php +++ b/tests/phpunit/tests/post/metaRevisions.php @@ -7,6 +7,7 @@ * @group post * @group revision * @group meta + * @group meta-revisions */ class MetaRevisionTests extends WP_UnitTestCase { @@ -246,7 +247,7 @@ public function test_revisions_stores_meta_values() { // This revision contains the existing post meta ('update3'). $revisions = wp_get_post_revisions( $post_id ); - $this->assertCount( 6, $revisions ); + $this->assertCount( 5, $revisions ); // Verify that previous post meta is set. $this->assertEquals( 'update3', get_post_meta( $post_id, 'meta_revision_test', true ) ); From ef14c11171c66442637e324512d02516e372302f Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Wed, 19 Jul 2023 08:27:53 -0600 Subject: [PATCH 011/101] Expected revision count is 6 --- tests/phpunit/tests/post/metaRevisions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/phpunit/tests/post/metaRevisions.php b/tests/phpunit/tests/post/metaRevisions.php index 134c84f7d8a41..08e3e022e32da 100644 --- a/tests/phpunit/tests/post/metaRevisions.php +++ b/tests/phpunit/tests/post/metaRevisions.php @@ -247,7 +247,7 @@ public function test_revisions_stores_meta_values() { // This revision contains the existing post meta ('update3'). $revisions = wp_get_post_revisions( $post_id ); - $this->assertCount( 5, $revisions ); + $this->assertCount( 6, $revisions ); // Verify that previous post meta is set. $this->assertEquals( 'update3', get_post_meta( $post_id, 'meta_revision_test', true ) ); From da8abe6fa5617c6237b7da2af91ed9b42d10ebcc Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 25 Jul 2023 08:37:40 -0600 Subject: [PATCH 012/101] Add REST API tests for revisioning of post meta. --- .../tests/rest-api/rest-post-meta-fields.php | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/tests/phpunit/tests/rest-api/rest-post-meta-fields.php b/tests/phpunit/tests/rest-api/rest-post-meta-fields.php index 2142e805820a6..d713d05382c44 100644 --- a/tests/phpunit/tests/rest-api/rest-post-meta-fields.php +++ b/tests/phpunit/tests/rest-api/rest-post-meta-fields.php @@ -3112,4 +3112,93 @@ public function error_delete_query( $query ) { } return $query; } + + /** + * Test that post meta is revisioned when saving to the REST API as part of a post update. + * + * @ticket 20564 + */ + public function test_revisioned_post_meta() { + $this->grant_write_permission(); + + register_post_meta( + 'post', + 'foo', + array( + 'single' => true, + 'show_in_rest' => true, + ) + ); + + $post_id = self::$post_id; + + // Create a revision. + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d/revisions', $post_id ) ); + $request->set_body_params( + array( + 'title' => 'Initial revision', + 'meta' => array( + 'foo' => 'initial', + ), + ) + ); + $response = rest_get_server()->dispatch( $request ); + $this->assertSame( 201, $response->get_status() ); + $revision_id = $response->get_data()['id']; + + // Update the post. + $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', $post_id ) ); + $request->set_body_params( + array( + 'title' => 'Revision 1', + 'meta' => array( + 'foo' => 'bar', + ), + ) + ); + $response = rest_get_server()->dispatch( $request ); + $this->assertSame( 200, $response->get_status() ); + + // Check that the revision has the old value. + $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d/revisions/%d', $post_id, $revision_id ) ); + $response = rest_get_server()->dispatch( $request ); + $this->assertSame( 200, $response->get_status() ); + $this->assertSame( 'bar', $response->get_data()['meta']['foo'] ); + + // Create two more revisions with different meta values for the foo key. + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d/revisions', $post_id ) ); + $request->set_body_params( + array( + 'title' => 'Revision 2', + 'meta' => array( + 'foo' => 'baz', + ), + ) + ); + $response = rest_get_server()->dispatch( $request ); + $this->assertSame( 201, $response->get_status() ); + $revision_id_2 = $response->get_data()['id']; + + // One more revision! + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d/revisions', $post_id ) ); + $request->set_body_params( + array( + 'title' => 'Revision 3', + 'meta' => array( + 'foo' => 'qux', + ), + ) + ); + + // Get the 3rd oldest revision. + $revisions = wp_get_post_revisions( $post_id ); + $revision_id_3 = $revisions[1]->ID; + + // Restore it by id. + wp_restore_post_revision( $revision_id_3 ); + + // Check that the revision's 'foo' post meta has the proper value. + $this->assertSame( 'bar', get_post_meta( $post_id, 'foo', true ) ); + $this->assertSame( 'Revision 1', get_post( $post_id )->post_title ); + } } From 6c9bece3dd3a02c6d1e8ad753f68b71c77ded483 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 25 Jul 2023 09:06:50 -0600 Subject: [PATCH 013/101] Remove footnote specific revisioning of meta --- src/wp-includes/blocks/footnotes.php | 100 --------------------------- 1 file changed, 100 deletions(-) diff --git a/src/wp-includes/blocks/footnotes.php b/src/wp-includes/blocks/footnotes.php index 9815033820406..3a6e89f0c0d88 100644 --- a/src/wp-includes/blocks/footnotes.php +++ b/src/wp-includes/blocks/footnotes.php @@ -83,106 +83,6 @@ function register_block_core_footnotes() { } add_action( 'init', 'register_block_core_footnotes' ); -/** - * Saves the footnotes meta value to the revision. - * - * @since 6.3.0 - * - * @param int $revision_id The revision ID. - */ -function wp_save_footnotes_meta( $revision_id ) { - $post_id = wp_is_post_revision( $revision_id ); - - if ( $post_id ) { - $footnotes = get_post_meta( $post_id, 'footnotes', true ); - - if ( $footnotes ) { - // Can't use update_post_meta() because it doesn't allow revisions. - update_metadata( 'post', $revision_id, 'footnotes', $footnotes ); - } - } -} -add_action( 'wp_after_insert_post', 'wp_save_footnotes_meta' ); - -/** - * Keeps track of the revision ID for "rest_after_insert_{$post_type}". - * - * @since 6.3.0 - * - * @global int $wp_temporary_footnote_revision_id The footnote revision ID. - * - * @param int $revision_id The revision ID. - */ -function wp_keep_footnotes_revision_id( $revision_id ) { - global $wp_temporary_footnote_revision_id; - $wp_temporary_footnote_revision_id = $revision_id; -} -add_action( '_wp_put_post_revision', 'wp_keep_footnotes_revision_id' ); - -/** - * This is a specific fix for the REST API. The REST API doesn't update - * the post and post meta in one go (through `meta_input`). While it - * does fix the `wp_after_insert_post` hook to be called correctly after - * updating meta, it does NOT fix hooks such as post_updated and - * save_post, which are normally also fired after post meta is updated - * in `wp_insert_post()`. Unfortunately, `wp_save_post_revision` is - * added to the `post_updated` action, which means the meta is not - * available at the time, so we have to add it afterwards through the - * `"rest_after_insert_{$post_type}"` action. - * - * @since 6.3.0 - * - * @global int $wp_temporary_footnote_revision_id The footnote revision ID. - * - * @param WP_Post $post The post object. - */ -function wp_add_footnotes_revisions_to_post_meta( $post ) { - global $wp_temporary_footnote_revision_id; - - if ( $wp_temporary_footnote_revision_id ) { - $revision = get_post( $wp_temporary_footnote_revision_id ); - - if ( ! $revision ) { - return; - } - - $post_id = $revision->post_parent; - - // Just making sure we're updating the right revision. - if ( $post->ID === $post_id ) { - $footnotes = get_post_meta( $post_id, 'footnotes', true ); - - if ( $footnotes ) { - // Can't use update_post_meta() because it doesn't allow revisions. - update_metadata( 'post', $wp_temporary_footnote_revision_id, 'footnotes', $footnotes ); - } - } - } -} - -foreach ( array( 'post', 'page' ) as $post_type ) { - add_action( "rest_after_insert_{$post_type}", 'wp_add_footnotes_revisions_to_post_meta' ); -} - -/** - * Restores the footnotes meta value from the revision. - * - * @since 6.3.0 - * - * @param int $post_id The post ID. - * @param int $revision_id The revision ID. - */ -function wp_restore_footnotes_from_revision( $post_id, $revision_id ) { - $footnotes = get_post_meta( $revision_id, 'footnotes', true ); - - if ( $footnotes ) { - update_post_meta( $post_id, 'footnotes', $footnotes ); - } else { - delete_post_meta( $post_id, 'footnotes' ); - } -} -add_action( 'wp_restore_post_revision', 'wp_restore_footnotes_from_revision', 10, 2 ); - /** * Adds the footnotes field to the revision. * From 2e7496420bc0cc5a6b29278dc5a0a4b6ae7f3666 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 25 Jul 2023 12:14:11 -0600 Subject: [PATCH 014/101] phpcbf --- src/wp-includes/revision.php | 2 +- tests/phpunit/tests/rest-api/rest-post-meta-fields.php | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/wp-includes/revision.php b/src/wp-includes/revision.php index e37e674935559..59783976a3259 100644 --- a/src/wp-includes/revision.php +++ b/src/wp-includes/revision.php @@ -560,7 +560,7 @@ function wp_post_revision_meta_keys() { * * @param array $keys An array of default meta fields to be revisioned. */ - return apply_filters( 'wp_post_revision_meta_keys', array( 'footnotes' ) ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals + return apply_filters( 'wp_post_revision_meta_keys', array( 'footnotes' ) ); } /** diff --git a/tests/phpunit/tests/rest-api/rest-post-meta-fields.php b/tests/phpunit/tests/rest-api/rest-post-meta-fields.php index d713d05382c44..af2b95a761cb2 100644 --- a/tests/phpunit/tests/rest-api/rest-post-meta-fields.php +++ b/tests/phpunit/tests/rest-api/rest-post-meta-fields.php @@ -3137,7 +3137,7 @@ public function test_revisioned_post_meta() { $request->set_body_params( array( 'title' => 'Initial revision', - 'meta' => array( + 'meta' => array( 'foo' => 'initial', ), ) @@ -3151,7 +3151,7 @@ public function test_revisioned_post_meta() { $request->set_body_params( array( 'title' => 'Revision 1', - 'meta' => array( + 'meta' => array( 'foo' => 'bar', ), ) @@ -3160,7 +3160,7 @@ public function test_revisioned_post_meta() { $this->assertSame( 200, $response->get_status() ); // Check that the revision has the old value. - $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d/revisions/%d', $post_id, $revision_id ) ); + $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d/revisions/%d', $post_id, $revision_id ) ); $response = rest_get_server()->dispatch( $request ); $this->assertSame( 200, $response->get_status() ); $this->assertSame( 'bar', $response->get_data()['meta']['foo'] ); @@ -3191,7 +3191,7 @@ public function test_revisioned_post_meta() { ); // Get the 3rd oldest revision. - $revisions = wp_get_post_revisions( $post_id ); + $revisions = wp_get_post_revisions( $post_id ); $revision_id_3 = $revisions[1]->ID; // Restore it by id. From 268385fcd150229c311998025a4bbec889e18722 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 25 Jul 2023 12:32:18 -0600 Subject: [PATCH 015/101] Fix test setup --- tests/phpunit/tests/post/metaRevisions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/phpunit/tests/post/metaRevisions.php b/tests/phpunit/tests/post/metaRevisions.php index 08e3e022e32da..b4467997438c2 100644 --- a/tests/phpunit/tests/post/metaRevisions.php +++ b/tests/phpunit/tests/post/metaRevisions.php @@ -21,7 +21,7 @@ class MetaRevisionTests extends WP_UnitTestCase { /** * {@inheritDoc} */ - protected function setUp() { + public function setUp() { parent::setUp(); // Reset for current test. From 4fd4302a5ab1a2ce3fcef8a6fe33d12328c8b793 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 25 Jul 2023 16:02:40 -0600 Subject: [PATCH 016/101] Save revisioned meta in `_wp_put_post_revision` --- src/wp-includes/post.php | 4 ---- src/wp-includes/revision.php | 23 +++-------------------- 2 files changed, 3 insertions(+), 24 deletions(-) diff --git a/src/wp-includes/post.php b/src/wp-includes/post.php index 15ca0ab97f4e7..22a37683eaf96 100644 --- a/src/wp-includes/post.php +++ b/src/wp-includes/post.php @@ -5458,10 +5458,6 @@ function wp_after_insert_post( $post, $update, $post_before ) { $post_id = $post->ID; - if ( $update ) { - _wp_save_post_revision_meta( $post_id ); - } - /** * Fires once a post, its terms and meta data has been saved. * diff --git a/src/wp-includes/revision.php b/src/wp-includes/revision.php index 59783976a3259..227889d79bc78 100644 --- a/src/wp-includes/revision.php +++ b/src/wp-includes/revision.php @@ -356,6 +356,9 @@ function _wp_put_post_revision( $post = null, $autosave = false ) { return $revision_id; } + // Save any revisioned post meta. + wp_save_revisioned_meta_fields( $post['post_parent'], $revision_id ); + if ( $revision_id ) { /** * Fires once a revision has been saved. @@ -389,26 +392,6 @@ function wp_save_revisioned_meta_fields( $post_id, $revision_id ) { } } -/** - * Copy any revisioned meta fields from the post to the most recently saved revision. - * - * @since 6.4.0 - * - */ -function _wp_save_post_revision_meta( $post_id ) { - // Exit early if no revisions are stored. - if ( 0 === wp_revisions_to_keep( get_post( $post_id ) ) ) { - return; - } - - // Add meta to revision. - $revisions = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) ); - if ( $revisions ) { - $revision = array_shift( $revisions ); - wp_save_revisioned_meta_fields( $post_id, $revision->ID ); - } -} - /** * Gets a post revision. * From 3722d1e945df2be95092b07aac31f1108ce8c50d Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 25 Jul 2023 16:30:53 -0600 Subject: [PATCH 017/101] Enable setting revisioning in register_meta --- src/wp-includes/blocks/footnotes.php | 7 ++++--- src/wp-includes/meta.php | 8 ++++++++ src/wp-includes/revision.php | 13 ++++++++++++- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/wp-includes/blocks/footnotes.php b/src/wp-includes/blocks/footnotes.php index 3a6e89f0c0d88..bb76396ff13f5 100644 --- a/src/wp-includes/blocks/footnotes.php +++ b/src/wp-includes/blocks/footnotes.php @@ -68,9 +68,10 @@ function register_block_core_footnotes() { $post_type, 'footnotes', array( - 'show_in_rest' => true, - 'single' => true, - 'type' => 'string', + 'show_in_rest' => true, + 'single' => true, + 'type' => 'string', + 'revisions_enabled' => true, ) ); } diff --git a/src/wp-includes/meta.php b/src/wp-includes/meta.php index 0b9f08544e498..e81f8dc88ab85 100644 --- a/src/wp-includes/meta.php +++ b/src/wp-includes/meta.php @@ -1367,6 +1367,7 @@ function sanitize_meta( $meta_key, $meta_value, $object_type, $object_subtype = * @since 4.9.8 The `$object_subtype` argument was added to the arguments array. * @since 5.3.0 Valid meta types expanded to include "array" and "object". * @since 5.5.0 The `$default` argument was added to the arguments array. + * @since 6.4.0 The `$revisions_enabled` argument was added to the arguments array. * * @param string $object_type Type of object metadata is for. Accepts 'post', 'comment', 'term', 'user', * or any other object type with an associated meta table. @@ -1392,6 +1393,7 @@ function sanitize_meta( $meta_key, $meta_value, $object_type, $object_subtype = * support for custom fields for registered meta to be accessible via REST. * When registering complex meta values this argument may optionally be an * array with 'schema' or 'prepare_callback' keys instead of a boolean. + * @type bool $revisions_enabled Whether to enable revisions support for this meta_key. * } * @param string|array $deprecated Deprecated. Use `$args` instead. * @return bool True if the meta key was successfully registered in the global array, false if not. @@ -1414,6 +1416,7 @@ function register_meta( $object_type, $meta_key, $args, $deprecated = null ) { 'sanitize_callback' => null, 'auth_callback' => null, 'show_in_rest' => false, + 'revisions_enabled' => false, ); // There used to be individual args for sanitize and auth callbacks. @@ -1514,6 +1517,11 @@ function register_meta( $object_type, $meta_key, $args, $deprecated = null ) { return true; } + // Store the revisioned meta fields. + if ( isset( $args['revisions_enabled'] ) && $args['revisions_enabled'] ) { + $wp_meta_keys['wp_revisions_enabled'][ $meta_key ] = $args['revisions_enabled']; + } + return false; } diff --git a/src/wp-includes/revision.php b/src/wp-includes/revision.php index 227889d79bc78..bc33a8dc90d1d 100644 --- a/src/wp-includes/revision.php +++ b/src/wp-includes/revision.php @@ -536,6 +536,17 @@ function _wp_copy_post_meta( $source_post_id, $target_post_id, $meta_key ) { * @return array An array of meta keys to be revisioned. */ function wp_post_revision_meta_keys() { + global $wp_meta_keys; + + $revisioned_keys = array(); + if ( ! empty ( $wp_meta_keys['wp_revisions_enabled'] ) ) { + foreach( $wp_meta_keys['wp_revisions_enabled'] as $meta_key => $revisions_enabled ) { + if ( $revisions_enabled ) { + $revisioned_keys[] = $meta_key; + } + } + } + /** * Filter the list of post meta keys to be revisioned. * @@ -543,7 +554,7 @@ function wp_post_revision_meta_keys() { * * @param array $keys An array of default meta fields to be revisioned. */ - return apply_filters( 'wp_post_revision_meta_keys', array( 'footnotes' ) ); + return apply_filters( 'wp_post_revision_meta_keys', $revisioned_keys ); } /** From b26a6959ba907af4fc0d2e86c5f3343e9f6ef9cc Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 25 Jul 2023 22:41:45 -0600 Subject: [PATCH 018/101] phpcs --- src/wp-includes/revision.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wp-includes/revision.php b/src/wp-includes/revision.php index bc33a8dc90d1d..8d39bdc50ac3b 100644 --- a/src/wp-includes/revision.php +++ b/src/wp-includes/revision.php @@ -539,8 +539,8 @@ function wp_post_revision_meta_keys() { global $wp_meta_keys; $revisioned_keys = array(); - if ( ! empty ( $wp_meta_keys['wp_revisions_enabled'] ) ) { - foreach( $wp_meta_keys['wp_revisions_enabled'] as $meta_key => $revisions_enabled ) { + if ( ! empty( $wp_meta_keys['wp_revisions_enabled'] ) ) { + foreach ( $wp_meta_keys['wp_revisions_enabled'] as $meta_key => $revisions_enabled ) { if ( $revisions_enabled ) { $revisioned_keys[] = $meta_key; } From 5d6e5b94d992e2f6f64c93eabcf949fdc39dfd0a Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Wed, 26 Jul 2023 08:05:37 -0600 Subject: [PATCH 019/101] remove unused $revisioned_keys --- tests/phpunit/tests/post/metaRevisions.php | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/tests/phpunit/tests/post/metaRevisions.php b/tests/phpunit/tests/post/metaRevisions.php index b4467997438c2..a6e8427a538fc 100644 --- a/tests/phpunit/tests/post/metaRevisions.php +++ b/tests/phpunit/tests/post/metaRevisions.php @@ -11,23 +11,6 @@ */ class MetaRevisionTests extends WP_UnitTestCase { - /** - * Array of meta keys to revision. - * - * @var array - */ - protected $revisioned_keys; - - /** - * {@inheritDoc} - */ - public function setUp() { - parent::setUp(); - - // Reset for current test. - $this->revisioned_keys = array( 'meta_revision_test' ); - } - /** * Callback function to add the revisioned keys. * @@ -429,9 +412,6 @@ public function test_revisions_stores_meta_values() { * Verify that only existing meta is revisioned. */ public function only_existing_meta_is_revisioned() { - $this->revisioned_keys[] = 'foo'; - $this->revisioned_keys[] = 'bar'; - add_filter( 'wp_post_revision_meta_keys', array( $this, 'add_revisioned_keys' ) ); // Set up a new post. @@ -482,7 +462,6 @@ public function only_existing_meta_is_revisioned() { * Verify that blank strings are revisioned correctly. */ public function blank_meta_is_revisioned() { - $this->revisioned_keys[] = 'foo'; add_filter( 'wp_post_revision_meta_keys', array( $this, 'add_revisioned_keys' ) ); From b4fa33f74bd8dcda51c1a390bfb93961ec0fba7f Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Wed, 26 Jul 2023 08:48:38 -0600 Subject: [PATCH 020/101] Add testing for enabling revisions in `register_meta` --- src/wp-includes/revision.php | 2 +- tests/phpunit/tests/post/metaRevisions.php | 48 ++++++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/src/wp-includes/revision.php b/src/wp-includes/revision.php index 8d39bdc50ac3b..1ef4dc855d9eb 100644 --- a/src/wp-includes/revision.php +++ b/src/wp-includes/revision.php @@ -552,7 +552,7 @@ function wp_post_revision_meta_keys() { * * @since 6.4.0 * - * @param array $keys An array of default meta fields to be revisioned. + * @param array $keys An array of meta fields to be revisioned. */ return apply_filters( 'wp_post_revision_meta_keys', $revisioned_keys ); } diff --git a/tests/phpunit/tests/post/metaRevisions.php b/tests/phpunit/tests/post/metaRevisions.php index a6e8427a538fc..7dfcfe6bb1ffc 100644 --- a/tests/phpunit/tests/post/metaRevisions.php +++ b/tests/phpunit/tests/post/metaRevisions.php @@ -494,6 +494,54 @@ public function blank_meta_is_revisioned() { $this->assertEquals( '', $stored_data[0] ); } + + /** + * @dataProvider data_register_post_meta_supports_revisions + */ + public function test_register_post_meta_supports_revisions( $post_type, $meta_key, $args, $expected_is_revisioned ) { + register_post_meta( $post_type, $meta_key, $args ); + + // Set up a new post. + $post_id = $this->factory->post->create( + array( + 'post_content' => 'initial content', + 'post_type' => $post_type, + 'meta_input' => array( + $meta_key => 'foo', + ), + ) + ); + + // Update the post meta and post to save. + update_post_meta( $post_id, $meta_key, 'bar' ); + wp_update_post( + array( + 'ID' => $post_id, + 'post_title' => 'updated title', + ) + ); + + // Check the last revision for the post to see if the meta key was revisioned + $revisions = wp_get_post_revisions( $post_id ); + $revision = array_shift( $revisions ); + $revisioned_meta = get_post_meta( $revision->ID, $meta_key, true ); + $this->assertEquals( $expected_is_revisioned, 'bar' === $revisioned_meta ); + + // Reset global so subsequent data tests do not get polluted. + $GLOBALS['wp_meta_keys'] = array(); + } + + public function data_register_post_meta_supports_revisions() { + return array( + array( 'post', 'registered_key1', array( 'single' => true ), false ), + array( 'post', 'registered_key1', array( 'single' => true, 'revisions_enabled' => true ), true ), + array( 'page', 'registered_key2', array( 'revisions_enabled' => false ), false ), + array( 'page', 'registered_key2', array( 'revisions_enabled' => true ), true ), + array( '', 'registered_key3', array( 'revisions_enabled' => false ), false ), + array( '', 'registered_key3', array( 'revisions_enabled' => true ), true ), + ); + } + /** * Assert the a post has a meta key. * From 1603c552c167b9c935e7c4bfb4a06b0d5e1132d4 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Wed, 26 Jul 2023 08:55:00 -0600 Subject: [PATCH 021/101] phpcbf --- tests/phpunit/tests/post/metaRevisions.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/phpunit/tests/post/metaRevisions.php b/tests/phpunit/tests/post/metaRevisions.php index 7dfcfe6bb1ffc..d06f8006f0956 100644 --- a/tests/phpunit/tests/post/metaRevisions.php +++ b/tests/phpunit/tests/post/metaRevisions.php @@ -534,7 +534,15 @@ public function test_register_post_meta_supports_revisions( $post_type, $meta_ke public function data_register_post_meta_supports_revisions() { return array( array( 'post', 'registered_key1', array( 'single' => true ), false ), - array( 'post', 'registered_key1', array( 'single' => true, 'revisions_enabled' => true ), true ), + array( + 'post', + 'registered_key1', + array( + 'single' => true, + 'revisions_enabled' => true, + ), + true, + ), array( 'page', 'registered_key2', array( 'revisions_enabled' => false ), false ), array( 'page', 'registered_key2', array( 'revisions_enabled' => true ), true ), array( '', 'registered_key3', array( 'revisions_enabled' => false ), false ), From d244a39e6871d9ac4853ece9c08663e6128abb72 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Wed, 26 Jul 2023 09:30:32 -0600 Subject: [PATCH 022/101] Use a separate global for revisioned keys `$wp_revisioned_meta_keys` --- src/wp-includes/meta.php | 15 ++++++++++----- src/wp-includes/revision.php | 13 ++----------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/src/wp-includes/meta.php b/src/wp-includes/meta.php index e81f8dc88ab85..429a07efe697b 100644 --- a/src/wp-includes/meta.php +++ b/src/wp-includes/meta.php @@ -1402,11 +1402,16 @@ function sanitize_meta( $meta_key, $meta_value, $object_type, $object_subtype = */ function register_meta( $object_type, $meta_key, $args, $deprecated = null ) { global $wp_meta_keys; + global $wp_revisioned_meta_keys; if ( ! is_array( $wp_meta_keys ) ) { $wp_meta_keys = array(); } + if ( ! is_array( $wp_revisioned_meta_keys ) ) { + $wp_revisioned_meta_keys = array(); + } + $defaults = array( 'object_subtype' => '', 'type' => 'string', @@ -1462,6 +1467,11 @@ function register_meta( $object_type, $meta_key, $args, $deprecated = null ) { } } + // Store the revisioned meta fields. + if ( isset( $args['revisions_enabled'] ) && $args['revisions_enabled'] ) { + array_push( $wp_revisioned_meta_keys, $meta_key ); + } + $object_subtype = ! empty( $args['object_subtype'] ) ? $args['object_subtype'] : ''; // If `auth_callback` is not provided, fall back to `is_protected_meta()`. @@ -1517,11 +1527,6 @@ function register_meta( $object_type, $meta_key, $args, $deprecated = null ) { return true; } - // Store the revisioned meta fields. - if ( isset( $args['revisions_enabled'] ) && $args['revisions_enabled'] ) { - $wp_meta_keys['wp_revisions_enabled'][ $meta_key ] = $args['revisions_enabled']; - } - return false; } diff --git a/src/wp-includes/revision.php b/src/wp-includes/revision.php index 1ef4dc855d9eb..0a01b672f2a55 100644 --- a/src/wp-includes/revision.php +++ b/src/wp-includes/revision.php @@ -536,16 +536,7 @@ function _wp_copy_post_meta( $source_post_id, $target_post_id, $meta_key ) { * @return array An array of meta keys to be revisioned. */ function wp_post_revision_meta_keys() { - global $wp_meta_keys; - - $revisioned_keys = array(); - if ( ! empty( $wp_meta_keys['wp_revisions_enabled'] ) ) { - foreach ( $wp_meta_keys['wp_revisions_enabled'] as $meta_key => $revisions_enabled ) { - if ( $revisions_enabled ) { - $revisioned_keys[] = $meta_key; - } - } - } + global $wp_revisioned_meta_keys; /** * Filter the list of post meta keys to be revisioned. @@ -554,7 +545,7 @@ function wp_post_revision_meta_keys() { * * @param array $keys An array of meta fields to be revisioned. */ - return apply_filters( 'wp_post_revision_meta_keys', $revisioned_keys ); + return apply_filters( 'wp_post_revision_meta_keys', $wp_revisioned_meta_keys ); } /** From a80620a249281f5288775cab3989f3dfcaf2ecb7 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Wed, 26 Jul 2023 09:48:29 -0600 Subject: [PATCH 023/101] Meta test cleanup to match new defaults --- tests/phpunit/tests/meta/registerMeta.php | 5 + .../tests/rest-api/rest-post-meta-fields.php | 93 ++++++++++++++++++- 2 files changed, 96 insertions(+), 2 deletions(-) diff --git a/tests/phpunit/tests/meta/registerMeta.php b/tests/phpunit/tests/meta/registerMeta.php index ae2c1d6e639c2..9a127295b0204 100644 --- a/tests/phpunit/tests/meta/registerMeta.php +++ b/tests/phpunit/tests/meta/registerMeta.php @@ -97,6 +97,7 @@ public function test_register_meta_with_post_object_type_populates_wp_meta_keys( 'sanitize_callback' => null, 'auth_callback' => '__return_true', 'show_in_rest' => false, + 'revisions_enabled' => false, ), ), ), @@ -121,6 +122,7 @@ public function test_register_meta_with_term_object_type_populates_wp_meta_keys( 'sanitize_callback' => null, 'auth_callback' => '__return_true', 'show_in_rest' => false, + 'revisions_enabled' => false, ), ), ), @@ -175,6 +177,7 @@ public function test_register_meta_with_current_sanitize_callback_populates_wp_m 'sanitize_callback' => array( $this, '_new_sanitize_meta_cb' ), 'auth_callback' => '__return_true', 'show_in_rest' => false, + 'revisions_enabled' => false, ), ), ), @@ -342,6 +345,7 @@ public function test_register_meta_with_subtype_populates_wp_meta_keys( $type, $ 'sanitize_callback' => null, 'auth_callback' => '__return_true', 'show_in_rest' => false, + 'revisions_enabled' => false, ), ), ), @@ -395,6 +399,7 @@ public function test_unregister_meta_without_subtype_keeps_subtype_meta_key( $ty 'sanitize_callback' => null, 'auth_callback' => '__return_true', 'show_in_rest' => false, + 'revisions_enabled' => false, ), ), ), diff --git a/tests/phpunit/tests/rest-api/rest-post-meta-fields.php b/tests/phpunit/tests/rest-api/rest-post-meta-fields.php index af2b95a761cb2..1ce9a6431ec5c 100644 --- a/tests/phpunit/tests/rest-api/rest-post-meta-fields.php +++ b/tests/phpunit/tests/rest-api/rest-post-meta-fields.php @@ -3114,11 +3114,11 @@ public function error_delete_query( $query ) { } /** - * Test that post meta is revisioned when saving to the REST API as part of a post update. + * Test that post meta is revisioned when saving to the revisions REST API endpoint. * * @ticket 20564 */ - public function test_revisioned_post_meta() { + public function test_revisioned_post_meta_with_revisions_endpoint() { $this->grant_write_permission(); register_post_meta( @@ -3201,4 +3201,93 @@ public function test_revisioned_post_meta() { $this->assertSame( 'bar', get_post_meta( $post_id, 'foo', true ) ); $this->assertSame( 'Revision 1', get_post( $post_id )->post_title ); } + + /** + * Test that post meta is revisioned when saving to the posts REST API endpoint. + * + * @ticket 20564 + */ + public function test_revisioned_post_meta_with_posts_endpoint() { + $this->grant_write_permission(); + + register_post_meta( + 'post', + 'foo', + array( + 'single' => true, + 'show_in_rest' => true, + ) + ); + + $post_id = self::$post_id; + + // Create a revision. + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d/revisions', $post_id ) ); + $request->set_body_params( + array( + 'title' => 'Initial revision', + 'meta' => array( + 'foo' => 'initial', + ), + ) + ); + $response = rest_get_server()->dispatch( $request ); + $this->assertSame( 201, $response->get_status() ); + $revision_id = $response->get_data()['id']; + + // Update the post. + $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', $post_id ) ); + $request->set_body_params( + array( + 'title' => 'Revision 1', + 'meta' => array( + 'foo' => 'bar', + ), + ) + ); + $response = rest_get_server()->dispatch( $request ); + $this->assertSame( 200, $response->get_status() ); + + // Check that the revision has the old value. + $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d/revisions/%d', $post_id, $revision_id ) ); + $response = rest_get_server()->dispatch( $request ); + $this->assertSame( 200, $response->get_status() ); + $this->assertSame( 'bar', $response->get_data()['meta']['foo'] ); + + // Create two more revisions with different meta values for the foo key. + $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', $post_id ) ); + $request->set_body_params( + array( + 'title' => 'Revision 2', + 'meta' => array( + 'foo' => 'baz', + ), + ) + ); + $response = rest_get_server()->dispatch( $request ); + $this->assertSame( 201, $response->get_status() ); + $revision_id_2 = $response->get_data()['id']; + + // One more revision! + $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', $post_id ) ); + $request->set_body_params( + array( + 'title' => 'Revision 3', + 'meta' => array( + 'foo' => 'qux', + ), + ) + ); + + // Get the 3rd oldest revision. + $revisions = wp_get_post_revisions( $post_id ); + $revision_id_3 = $revisions[1]->ID; + + // Restore it by id. + wp_restore_post_revision( $revision_id_3 ); + + // Check that the revision's 'foo' post meta has the proper value. + $this->assertSame( 'bar', get_post_meta( $post_id, 'foo', true ) ); + $this->assertSame( 'Revision 1', get_post( $post_id )->post_title ); + } } From 885640d8c0a5501deb963546735231bdee4c18a6 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Wed, 26 Jul 2023 10:03:44 -0600 Subject: [PATCH 024/101] Adjust expected for `test_should_register_persisted_preferences_meta` --- tests/phpunit/tests/user/wpRegisterPersistedPreferencesMeta.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/phpunit/tests/user/wpRegisterPersistedPreferencesMeta.php b/tests/phpunit/tests/user/wpRegisterPersistedPreferencesMeta.php index c3c4bd7a09e08..68b3c867423fb 100644 --- a/tests/phpunit/tests/user/wpRegisterPersistedPreferencesMeta.php +++ b/tests/phpunit/tests/user/wpRegisterPersistedPreferencesMeta.php @@ -52,6 +52,7 @@ public function test_should_register_persisted_preferences_meta() { 'additionalProperties' => true, ), ), + 'revisions_enabled' => false, ), $wp_meta_keys['user'][''][ $meta_key ], 'The registered metadata did not have the expected structure' From ea4f1812ad036809c921f02ba5f608a9ac2276d2 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Wed, 26 Jul 2023 13:44:50 -0600 Subject: [PATCH 025/101] Refine REST tests for meta revisioning --- .../tests/rest-api/rest-post-meta-fields.php | 147 ++++-------------- 1 file changed, 33 insertions(+), 114 deletions(-) diff --git a/tests/phpunit/tests/rest-api/rest-post-meta-fields.php b/tests/phpunit/tests/rest-api/rest-post-meta-fields.php index 1ce9a6431ec5c..04716a9453cd3 100644 --- a/tests/phpunit/tests/rest-api/rest-post-meta-fields.php +++ b/tests/phpunit/tests/rest-api/rest-post-meta-fields.php @@ -3113,94 +3113,6 @@ public function error_delete_query( $query ) { return $query; } - /** - * Test that post meta is revisioned when saving to the revisions REST API endpoint. - * - * @ticket 20564 - */ - public function test_revisioned_post_meta_with_revisions_endpoint() { - $this->grant_write_permission(); - - register_post_meta( - 'post', - 'foo', - array( - 'single' => true, - 'show_in_rest' => true, - ) - ); - - $post_id = self::$post_id; - - // Create a revision. - $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d/revisions', $post_id ) ); - $request->set_body_params( - array( - 'title' => 'Initial revision', - 'meta' => array( - 'foo' => 'initial', - ), - ) - ); - $response = rest_get_server()->dispatch( $request ); - $this->assertSame( 201, $response->get_status() ); - $revision_id = $response->get_data()['id']; - - // Update the post. - $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', $post_id ) ); - $request->set_body_params( - array( - 'title' => 'Revision 1', - 'meta' => array( - 'foo' => 'bar', - ), - ) - ); - $response = rest_get_server()->dispatch( $request ); - $this->assertSame( 200, $response->get_status() ); - - // Check that the revision has the old value. - $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d/revisions/%d', $post_id, $revision_id ) ); - $response = rest_get_server()->dispatch( $request ); - $this->assertSame( 200, $response->get_status() ); - $this->assertSame( 'bar', $response->get_data()['meta']['foo'] ); - - // Create two more revisions with different meta values for the foo key. - $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d/revisions', $post_id ) ); - $request->set_body_params( - array( - 'title' => 'Revision 2', - 'meta' => array( - 'foo' => 'baz', - ), - ) - ); - $response = rest_get_server()->dispatch( $request ); - $this->assertSame( 201, $response->get_status() ); - $revision_id_2 = $response->get_data()['id']; - - // One more revision! - $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d/revisions', $post_id ) ); - $request->set_body_params( - array( - 'title' => 'Revision 3', - 'meta' => array( - 'foo' => 'qux', - ), - ) - ); - - // Get the 3rd oldest revision. - $revisions = wp_get_post_revisions( $post_id ); - $revision_id_3 = $revisions[1]->ID; - - // Restore it by id. - wp_restore_post_revision( $revision_id_3 ); - - // Check that the revision's 'foo' post meta has the proper value. - $this->assertSame( 'bar', get_post_meta( $post_id, 'foo', true ) ); - $this->assertSame( 'Revision 1', get_post( $post_id )->post_title ); - } /** * Test that post meta is revisioned when saving to the posts REST API endpoint. @@ -3214,28 +3126,15 @@ public function test_revisioned_post_meta_with_posts_endpoint() { 'post', 'foo', array( - 'single' => true, - 'show_in_rest' => true, + 'single' => true, + 'show_in_rest' => true, + 'revisions_supported' => true, ) ); $post_id = self::$post_id; - // Create a revision. - $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d/revisions', $post_id ) ); - $request->set_body_params( - array( - 'title' => 'Initial revision', - 'meta' => array( - 'foo' => 'initial', - ), - ) - ); - $response = rest_get_server()->dispatch( $request ); - $this->assertSame( 201, $response->get_status() ); - $revision_id = $response->get_data()['id']; - - // Update the post. + // Update the post, saving the meta. $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', $post_id ) ); $request->set_body_params( array( @@ -3248,7 +3147,11 @@ public function test_revisioned_post_meta_with_posts_endpoint() { $response = rest_get_server()->dispatch( $request ); $this->assertSame( 200, $response->get_status() ); - // Check that the revision has the old value. + // Get the last revision. + $revisions = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) ); + $revision_id = $revisions[0]->ID; + + // Check that the revision has the correct meta value. $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d/revisions/%d', $post_id, $revision_id ) ); $response = rest_get_server()->dispatch( $request ); $this->assertSame( 200, $response->get_status() ); @@ -3266,7 +3169,16 @@ public function test_revisioned_post_meta_with_posts_endpoint() { ); $response = rest_get_server()->dispatch( $request ); $this->assertSame( 201, $response->get_status() ); - $revision_id_2 = $response->get_data()['id']; + + // Get the last revision. + $revisions = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) ); + $revision_id_2 = $revisions[0]->ID; + + // Check that the revision has the correct meta value. + $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d/revisions/%d', $post_id, $revision_id_2 ) ); + $response = rest_get_server()->dispatch( $request ); + $this->assertSame( 200, $response->get_status() ); + $this->assertSame( 'baz', $response->get_data()['meta']['foo'] ); // One more revision! $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', $post_id ) ); @@ -3279,15 +3191,22 @@ public function test_revisioned_post_meta_with_posts_endpoint() { ) ); - // Get the 3rd oldest revision. - $revisions = wp_get_post_revisions( $post_id ); - $revision_id_3 = $revisions[1]->ID; + // Get the last revision. + $revisions = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) ); + $revision_id_3 = $revisions[0]->ID; + + // Check that the revision has the correct meta value. + $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d/revisions/%d', $post_id, $revision_id_3 ) ); + $response = rest_get_server()->dispatch( $request ); + $this->assertSame( 200, $response->get_status() ); + $this->assertSame( 'qux', $response->get_data()['meta']['foo'] ); - // Restore it by id. + // Restore Revision 3 and verify the post gets the correct meta value. wp_restore_post_revision( $revision_id_3 ); + $this->assertSame( 'qux', get_post_meta( $post_id, 'foo', true ) ); - // Check that the revision's 'foo' post meta has the proper value. - $this->assertSame( 'bar', get_post_meta( $post_id, 'foo', true ) ); - $this->assertSame( 'Revision 1', get_post( $post_id )->post_title ); + // Restore Revision 2 and verify the post gets the correct meta value. + wp_restore_post_revision( $revision_id_2 ); + $this->assertSame( 'baz', get_post_meta( $post_id, 'foo', true ) ); } } From e63334bf6f2b6a7b39ef41de608b2d34b44969ae Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Wed, 26 Jul 2023 15:46:05 -0600 Subject: [PATCH 026/101] Avoid duplicate keys in `wp_revisioned_meta_keys` --- src/wp-includes/meta.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/meta.php b/src/wp-includes/meta.php index 429a07efe697b..f4ea49aa4ded2 100644 --- a/src/wp-includes/meta.php +++ b/src/wp-includes/meta.php @@ -1468,7 +1468,7 @@ function register_meta( $object_type, $meta_key, $args, $deprecated = null ) { } // Store the revisioned meta fields. - if ( isset( $args['revisions_enabled'] ) && $args['revisions_enabled'] ) { + if ( isset( $args['revisions_enabled'] ) && $args['revisions_enabled'] && ! in_array( $meta_key, $wp_revisioned_meta_keys ) ) { array_push( $wp_revisioned_meta_keys, $meta_key ); } From f4234f8faece0727ceb6708c1ba150db3f23a066 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Wed, 26 Jul 2023 15:46:27 -0600 Subject: [PATCH 027/101] remove unused `original_post_id` --- tests/phpunit/tests/post/metaRevisions.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/phpunit/tests/post/metaRevisions.php b/tests/phpunit/tests/post/metaRevisions.php index d06f8006f0956..84e88e45a3b75 100644 --- a/tests/phpunit/tests/post/metaRevisions.php +++ b/tests/phpunit/tests/post/metaRevisions.php @@ -38,7 +38,6 @@ public function add_revisioned_keys( $keys ) { public function test_revisions_stores_meta_values_with_slashes( $passed, $expected ) { // Set up a new post. $post_id = $this->factory->post->create(); - $original_post_id = $post_id; // And update to store an initial revision. wp_update_post( From 1f1e5a0b80aeddfc3e0bdf149f7fa1cd471eebab Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Wed, 26 Jul 2023 16:45:41 -0600 Subject: [PATCH 028/101] REST posts endpoint: update revision meta when saving meta --- .../endpoints/class-wp-rest-posts-controller.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php index 4ec972347fb8f..450f5843dac8f 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php @@ -741,6 +741,13 @@ public function create_item( $request ) { if ( is_wp_error( $meta_update ) ) { return $meta_update; } + + // Update the revision meta as well. + $revisions = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) ); + $revision = array_shift( $revisions ); + if ( ! empty( $revisions ) ) { + wp_save_revisioned_meta_fields( $post_id, $revision->ID ); + } } $post = get_post( $post_id ); @@ -927,6 +934,13 @@ public function update_item( $request ) { if ( is_wp_error( $meta_update ) ) { return $meta_update; } + + // Update the revision meta as well. + $revisions = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) ); + $revision = array_shift( $revisions ); + if ( ! empty( $revisions ) ) { + wp_save_revisioned_meta_fields( $post_id, $revision->ID ); + } } $post = get_post( $post_id ); From 6c5061b65ef365cdae8e883affff8ff9a903896f Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Wed, 26 Jul 2023 16:45:48 -0600 Subject: [PATCH 029/101] test work --- .../tests/rest-api/rest-post-meta-fields.php | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/tests/phpunit/tests/rest-api/rest-post-meta-fields.php b/tests/phpunit/tests/rest-api/rest-post-meta-fields.php index 04716a9453cd3..c4b077fa620ce 100644 --- a/tests/phpunit/tests/rest-api/rest-post-meta-fields.php +++ b/tests/phpunit/tests/rest-api/rest-post-meta-fields.php @@ -3126,9 +3126,9 @@ public function test_revisioned_post_meta_with_posts_endpoint() { 'post', 'foo', array( - 'single' => true, - 'show_in_rest' => true, - 'revisions_supported' => true, + 'single' => true, + 'show_in_rest' => true, + 'revisions_enabled' => true, ) ); @@ -3149,13 +3149,19 @@ public function test_revisioned_post_meta_with_posts_endpoint() { // Get the last revision. $revisions = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) ); - $revision_id = $revisions[0]->ID; + $revision_id = array_shift( $revisions )->ID; + // @todo Ensure the revisions endpoint returns the correct meta values // Check that the revision has the correct meta value. + /* $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d/revisions/%d', $post_id, $revision_id ) ); $response = rest_get_server()->dispatch( $request ); $this->assertSame( 200, $response->get_status() ); $this->assertSame( 'bar', $response->get_data()['meta']['foo'] ); + */ + + // Check that the post meta is set correctly. + $this->assertSame( 'bar', get_post_meta( $revision_id, 'foo', true ) ); // Create two more revisions with different meta values for the foo key. $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', $post_id ) ); @@ -3172,13 +3178,17 @@ public function test_revisioned_post_meta_with_posts_endpoint() { // Get the last revision. $revisions = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) ); - $revision_id_2 = $revisions[0]->ID; + $revision_id_2 = array_shift( $revisions )->ID; + /* // Check that the revision has the correct meta value. $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d/revisions/%d', $post_id, $revision_id_2 ) ); $response = rest_get_server()->dispatch( $request ); $this->assertSame( 200, $response->get_status() ); $this->assertSame( 'baz', $response->get_data()['meta']['foo'] ); + */ + // Check that the post meta is set correctly. + $this->assertSame( 'baz', get_post_meta( $revision_id_2, 'foo', true ) ); // One more revision! $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', $post_id ) ); @@ -3193,13 +3203,17 @@ public function test_revisioned_post_meta_with_posts_endpoint() { // Get the last revision. $revisions = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) ); - $revision_id_3 = $revisions[0]->ID; + $revision_id_3 = array_shift( $revisions )->ID; + /* // Check that the revision has the correct meta value. $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d/revisions/%d', $post_id, $revision_id_3 ) ); $response = rest_get_server()->dispatch( $request ); $this->assertSame( 200, $response->get_status() ); $this->assertSame( 'qux', $response->get_data()['meta']['foo'] ); + */ + // Check that the post meta is set correctly. + $this->assertSame( 'qux', get_post_meta( $revision_id_3, 'foo', true ) ); // Restore Revision 3 and verify the post gets the correct meta value. wp_restore_post_revision( $revision_id_3 ); From 5bd7e7bf9eba9ccfc718078fbaeaeed407fe1abf Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Thu, 27 Jul 2023 14:09:31 -0600 Subject: [PATCH 030/101] Hook `wp_save_revisioned_meta_fields` on `_wp_put_post_revision` so it can be unhooked in the REST API handler --- src/wp-includes/default-filters.php | 3 +++ src/wp-includes/revision.php | 11 +++++------ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/wp-includes/default-filters.php b/src/wp-includes/default-filters.php index 6b7a1cdc841af..666b30368b2bf 100644 --- a/src/wp-includes/default-filters.php +++ b/src/wp-includes/default-filters.php @@ -722,4 +722,7 @@ // Including revisioned meta when considering whether a post revision has changed. add_filter( 'wp_save_post_revision_post_has_changed', 'wp_check_revisioned_meta_fields_have_changed', 10, 3 ); +// Save revisioned post meta immediately after a revision is saved +add_action( '_wp_put_post_revision', 'wp_save_revisioned_meta_fields', 10, 2 ); + unset( $filter, $action ); diff --git a/src/wp-includes/revision.php b/src/wp-includes/revision.php index 0a01b672f2a55..a5fe608bf6bf7 100644 --- a/src/wp-includes/revision.php +++ b/src/wp-includes/revision.php @@ -356,18 +356,17 @@ function _wp_put_post_revision( $post = null, $autosave = false ) { return $revision_id; } - // Save any revisioned post meta. - wp_save_revisioned_meta_fields( $post['post_parent'], $revision_id ); - if ( $revision_id ) { /** * Fires once a revision has been saved. * * @since 2.6.0 + * @since 6.4.0 The post_id parameter was added. * * @param int $revision_id Post revision ID. + * @param int $post_id Post ID. */ - do_action( '_wp_put_post_revision', $revision_id ); + do_action( '_wp_put_post_revision', $revision_id, $post['post_parent'] ); } return $revision_id; @@ -377,12 +376,12 @@ function _wp_put_post_revision( $post = null, $autosave = false ) { /** * Save the revisioned meta fields. * - * @param int $post_id The ID of the post the revision is associated with. * @param int $revision_id The ID of the revision to save the meta to. + * @param int $post_id The ID of the post the revision is associated with. * * @since 6.4.0 */ -function wp_save_revisioned_meta_fields( $post_id, $revision_id ) { +function wp_save_revisioned_meta_fields( $revision_id, $post_id ) { // Save revisioned meta fields. foreach ( wp_post_revision_meta_keys() as $meta_key ) { From 65bfe81a4a5c314aecb3b7d97c7315ff06f649e3 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Thu, 27 Jul 2023 14:10:36 -0600 Subject: [PATCH 031/101] Test cleanup --- tests/phpunit/tests/rest-api/rest-post-meta-fields.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/phpunit/tests/rest-api/rest-post-meta-fields.php b/tests/phpunit/tests/rest-api/rest-post-meta-fields.php index c4b077fa620ce..d70bf0e084148 100644 --- a/tests/phpunit/tests/rest-api/rest-post-meta-fields.php +++ b/tests/phpunit/tests/rest-api/rest-post-meta-fields.php @@ -3174,7 +3174,7 @@ public function test_revisioned_post_meta_with_posts_endpoint() { ) ); $response = rest_get_server()->dispatch( $request ); - $this->assertSame( 201, $response->get_status() ); + $this->assertSame( 200, $response->get_status() ); // Get the last revision. $revisions = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) ); @@ -3200,6 +3200,8 @@ public function test_revisioned_post_meta_with_posts_endpoint() { ), ) ); + $response = rest_get_server()->dispatch( $request ); + $this->assertSame( 200, $response->get_status() ); // Get the last revision. $revisions = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) ); From 7254e3add283cfb8ca69638af4852308727a2903 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Thu, 27 Jul 2023 14:45:44 -0600 Subject: [PATCH 032/101] Improve footnote display, show number and content --- src/wp-includes/blocks/footnotes.php | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/wp-includes/blocks/footnotes.php b/src/wp-includes/blocks/footnotes.php index bb76396ff13f5..9fd3e653e4f5c 100644 --- a/src/wp-includes/blocks/footnotes.php +++ b/src/wp-includes/blocks/footnotes.php @@ -110,6 +110,19 @@ function wp_add_footnotes_to_revision( $fields ) { * @return string The field value. */ function wp_get_footnotes_from_revision( $revision_field, $field, $revision ) { - return get_metadata( 'post', $revision->ID, $field, true ); + $footnotes = json_decode( get_metadata( 'post', $revision->ID, $field, true ) ); + if ( empty( $footnotes ) ) { + return $revision_field; + } + $footnotes_html = ''; + $x = 1; + foreach( $footnotes as $footnote ) { + $footnotes_html .= sprintf( + "%s. %s\n", + $x++, + $footnote->content + ); + } + return $footnotes_html; } -add_filter( 'wp_post_revision_field_footnotes', 'wp_get_footnotes_from_revision', 10, 3 ); +add_filter( '_wp_post_revision_field_footnotes', 'wp_get_footnotes_from_revision', 10, 3 ); From 8c3fba265e2c6be1706bfa1af7fd7ee13233c00e Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Thu, 27 Jul 2023 14:46:45 -0600 Subject: [PATCH 033/101] Save the revisioned meta directly in the REST endpoint --- .../endpoints/class-wp-rest-posts-controller.php | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php index 450f5843dac8f..2283a0d707e88 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php @@ -673,6 +673,9 @@ public function create_item( $request ) { ); } + // Don't save the post meta revision because they aren't stored yet. + remove_action( '_wp_put_post_revision', 'wp_save_revisioned_meta_fields', 10, 2 ); + $post_id = wp_insert_post( wp_slash( (array) $prepared_post ), true, false ); if ( is_wp_error( $post_id ) ) { @@ -744,9 +747,9 @@ public function create_item( $request ) { // Update the revision meta as well. $revisions = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) ); - $revision = array_shift( $revisions ); if ( ! empty( $revisions ) ) { - wp_save_revisioned_meta_fields( $post_id, $revision->ID ); + $revision = array_shift( $revisions ); + wp_save_revisioned_meta_fields( $revision->ID, $post_id ); } } @@ -883,6 +886,9 @@ public function update_item( $request ) { ); } + // Don't save the post meta revision because they aren't stored yet. + remove_action( '_wp_put_post_revision', 'wp_save_revisioned_meta_fields', 10, 2 ); + // Convert the post object to an array, otherwise wp_update_post() will expect non-escaped input. $post_id = wp_update_post( wp_slash( (array) $post ), true, false ); @@ -937,9 +943,9 @@ public function update_item( $request ) { // Update the revision meta as well. $revisions = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) ); - $revision = array_shift( $revisions ); if ( ! empty( $revisions ) ) { - wp_save_revisioned_meta_fields( $post_id, $revision->ID ); + $revision = array_shift( $revisions ); + wp_save_revisioned_meta_fields( $revision->ID, $post_id ); } } From 26a9d0b053ab9d5f5a61831ff4765c0a67daf0c6 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Thu, 27 Jul 2023 14:50:58 -0600 Subject: [PATCH 034/101] spacing --- tests/phpunit/tests/post/metaRevisions.php | 2 +- tests/phpunit/tests/rest-api/rest-post-meta-fields.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/phpunit/tests/post/metaRevisions.php b/tests/phpunit/tests/post/metaRevisions.php index 84e88e45a3b75..c132f01e40f79 100644 --- a/tests/phpunit/tests/post/metaRevisions.php +++ b/tests/phpunit/tests/post/metaRevisions.php @@ -37,7 +37,7 @@ public function add_revisioned_keys( $keys ) { */ public function test_revisions_stores_meta_values_with_slashes( $passed, $expected ) { // Set up a new post. - $post_id = $this->factory->post->create(); + $post_id = $this->factory->post->create(); // And update to store an initial revision. wp_update_post( diff --git a/tests/phpunit/tests/rest-api/rest-post-meta-fields.php b/tests/phpunit/tests/rest-api/rest-post-meta-fields.php index d70bf0e084148..be2533e83bc74 100644 --- a/tests/phpunit/tests/rest-api/rest-post-meta-fields.php +++ b/tests/phpunit/tests/rest-api/rest-post-meta-fields.php @@ -3148,8 +3148,8 @@ public function test_revisioned_post_meta_with_posts_endpoint() { $this->assertSame( 200, $response->get_status() ); // Get the last revision. - $revisions = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) ); - $revision_id = array_shift( $revisions )->ID; + $revisions = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) ); + $revision_id = array_shift( $revisions )->ID; // @todo Ensure the revisions endpoint returns the correct meta values // Check that the revision has the correct meta value. From 485f20b0feea03b9328de99cd89d2e124259b393 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Mon, 31 Jul 2023 12:39:12 -0600 Subject: [PATCH 035/101] add meta to revisions endpoint --- .../class-wp-rest-revisions-controller.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php index 6fde5f13f545d..31e3d506eaa95 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php @@ -24,6 +24,14 @@ class WP_REST_Revisions_Controller extends WP_REST_Controller { */ private $parent_post_type; + /** + * Instance of a revision meta fields object. + * + * @since 4.7.0 + * @var WP_REST_Revision_Meta_Fields + */ + protected $meta; + /** * Parent controller. * @@ -60,6 +68,7 @@ public function __construct( $parent_post_type ) { $this->rest_base = 'revisions'; $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->meta = new WP_REST_Revision_Meta_Fields( 'revision' ); } /** @@ -619,6 +628,10 @@ public function prepare_item_for_response( $item, $request ) { ); } + if ( rest_is_field_included( 'meta', $fields ) ) { + $data['meta'] = $this->meta->get_value( $post->ID, $request ); + } + $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; $data = $this->add_additional_fields_to_object( $data, $request ); $data = $this->filter_response_by_context( $data, $context ); @@ -752,6 +765,8 @@ public function get_item_schema() { $schema['properties']['guid'] = $parent_schema['properties']['guid']; } + $schema['properties']['meta'] = $this->meta->get_field_schema(); + $this->schema = $schema; return $this->add_additional_fields_schema( $this->schema ); From 167261ea149efd2fdb612074f96af5d9bb8843a7 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Mon, 31 Jul 2023 12:42:27 -0600 Subject: [PATCH 036/101] test revisions endpoint returns meta --- .../tests/rest-api/rest-post-meta-fields.php | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/phpunit/tests/rest-api/rest-post-meta-fields.php b/tests/phpunit/tests/rest-api/rest-post-meta-fields.php index be2533e83bc74..39997f9f0838d 100644 --- a/tests/phpunit/tests/rest-api/rest-post-meta-fields.php +++ b/tests/phpunit/tests/rest-api/rest-post-meta-fields.php @@ -3152,13 +3152,13 @@ public function test_revisioned_post_meta_with_posts_endpoint() { $revision_id = array_shift( $revisions )->ID; // @todo Ensure the revisions endpoint returns the correct meta values - // Check that the revision has the correct meta value. - /* + // Check that the revisions endpoint returns the correct meta value. $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d/revisions/%d', $post_id, $revision_id ) ); $response = rest_get_server()->dispatch( $request ); $this->assertSame( 200, $response->get_status() ); - $this->assertSame( 'bar', $response->get_data()['meta']['foo'] ); - */ + $data = $response->get_data(); + $this->assertSame( array( 'bar' ), $response->get_data()['meta']['foo'] ); + // Check that the post meta is set correctly. $this->assertSame( 'bar', get_post_meta( $revision_id, 'foo', true ) ); @@ -3180,13 +3180,13 @@ public function test_revisioned_post_meta_with_posts_endpoint() { $revisions = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) ); $revision_id_2 = array_shift( $revisions )->ID; - /* + // Check that the revision has the correct meta value. $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d/revisions/%d', $post_id, $revision_id_2 ) ); $response = rest_get_server()->dispatch( $request ); $this->assertSame( 200, $response->get_status() ); - $this->assertSame( 'baz', $response->get_data()['meta']['foo'] ); - */ + $this->assertSame( array( 'baz' ), $response->get_data()['meta']['foo'] ); + // Check that the post meta is set correctly. $this->assertSame( 'baz', get_post_meta( $revision_id_2, 'foo', true ) ); @@ -3207,13 +3207,13 @@ public function test_revisioned_post_meta_with_posts_endpoint() { $revisions = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) ); $revision_id_3 = array_shift( $revisions )->ID; - /* + // Check that the revision has the correct meta value. $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d/revisions/%d', $post_id, $revision_id_3 ) ); $response = rest_get_server()->dispatch( $request ); $this->assertSame( 200, $response->get_status() ); - $this->assertSame( 'qux', $response->get_data()['meta']['foo'] ); - */ + $this->assertSame( array( 'qux' ), $response->get_data()['meta']['foo'] ); + // Check that the post meta is set correctly. $this->assertSame( 'qux', get_post_meta( $revision_id_3, 'foo', true ) ); From 5b5dc0df29482f4ebd621a3ba853f2034c419ed1 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Mon, 31 Jul 2023 12:56:28 -0600 Subject: [PATCH 037/101] new revision meta fields class --- .../class-wp-rest-revision-meta-fields.php | 111 ++++++++++++++++++ .../tests/rest-api/rest-post-meta-fields.php | 1 - 2 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 src/wp-includes/rest-api/fields/class-wp-rest-revision-meta-fields.php diff --git a/src/wp-includes/rest-api/fields/class-wp-rest-revision-meta-fields.php b/src/wp-includes/rest-api/fields/class-wp-rest-revision-meta-fields.php new file mode 100644 index 0000000000000..248e06e79b56b --- /dev/null +++ b/src/wp-includes/rest-api/fields/class-wp-rest-revision-meta-fields.php @@ -0,0 +1,111 @@ +post_type = $post_type; + } + + /** + * Retrieves the post meta type. + * + * @since 6.4.0 + * + * @return string The meta type. + */ + protected function get_meta_type() { + return 'revision'; + } + + /** + * Retrieves the post meta subtype. + * + * @since 6.4.0 + * + * @return string Subtype for the meta type, or empty string if no specific subtype. + */ + protected function get_meta_subtype() { + return $this->post_type; + } + + /** + * Retrieves the type for register_rest_field(). + * + * @since 6.4.0 + * + * @see register_rest_field() + * + * @return string The REST field type. + */ + public function get_rest_field_type() { + return $this->post_type; + } + + /** + * Retrieves the meta field value. + * + * @since 4.7.0 + * + * @param int $object_id Object ID to fetch meta for. + * @param WP_REST_Request $request Full details about the request. + * @return array Array containing the meta values keyed by name. + */ + public function get_value( $object_id, $request ) { + $data = get_post_meta( $object_id ); + return $data; + } + + /** + * Retrieves raw metadata value for the specified object. + * + * @since 5.5.0 + * + * @param string $meta_type Type of object metadata is for. Accepts 'post', 'comment', 'term', 'user', + * or any other object type with an associated meta table. + * @param int $object_id ID of the object metadata is for. + * @param string $meta_key Optional. Metadata key. If not specified, retrieve all metadata for + * the specified object. Default empty string. + * @param bool $single Optional. If true, return only the first value of the specified `$meta_key`. + * This parameter has no effect if `$meta_key` is not specified. Default false. + * @return mixed An array of values if `$single` is false. + * The value of the meta field if `$single` is true. + * False for an invalid `$object_id` (non-numeric, zero, or negative value), + * or if `$meta_type` is not specified. + * Null if the value does not exist. + */ + function get_metadata_raw( $meta_type, $object_id, $meta_key = '', $single = false ) { + $data = get_post_meta( $object_id, $request ); + return $data; + } + + +} diff --git a/tests/phpunit/tests/rest-api/rest-post-meta-fields.php b/tests/phpunit/tests/rest-api/rest-post-meta-fields.php index 39997f9f0838d..dcc549b872d7e 100644 --- a/tests/phpunit/tests/rest-api/rest-post-meta-fields.php +++ b/tests/phpunit/tests/rest-api/rest-post-meta-fields.php @@ -3207,7 +3207,6 @@ public function test_revisioned_post_meta_with_posts_endpoint() { $revisions = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) ); $revision_id_3 = array_shift( $revisions )->ID; - // Check that the revision has the correct meta value. $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d/revisions/%d', $post_id, $revision_id_3 ) ); $response = rest_get_server()->dispatch( $request ); From f8d0d302c17565933e1f661200713ea8fc0407f1 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 1 Aug 2023 10:12:28 -0600 Subject: [PATCH 038/101] INCLUDE class-wp-rest-revision-meta-fields --- src/wp-settings.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/wp-settings.php b/src/wp-settings.php index 2479420742d0a..aec891e6b2097 100644 --- a/src/wp-settings.php +++ b/src/wp-settings.php @@ -306,6 +306,7 @@ require ABSPATH . WPINC . '/rest-api/fields/class-wp-rest-post-meta-fields.php'; require ABSPATH . WPINC . '/rest-api/fields/class-wp-rest-term-meta-fields.php'; require ABSPATH . WPINC . '/rest-api/fields/class-wp-rest-user-meta-fields.php'; +require ABSPATH . WPINC . '/rest-api/fields/class-wp-rest-revision-meta-fields.php'; require ABSPATH . WPINC . '/rest-api/search/class-wp-rest-search-handler.php'; require ABSPATH . WPINC . '/rest-api/search/class-wp-rest-post-search-handler.php'; require ABSPATH . WPINC . '/rest-api/search/class-wp-rest-term-search-handler.php'; From ea9b8ce4d85416d7a854672fb59bc2da98b94287 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 1 Aug 2023 10:45:11 -0600 Subject: [PATCH 039/101] Revision existing meta even when update/insert lacks meta --- .../class-wp-rest-posts-controller.php | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php index 2283a0d707e88..86a41b0e0d0fb 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php @@ -744,13 +744,13 @@ public function create_item( $request ) { if ( is_wp_error( $meta_update ) ) { return $meta_update; } + } - // Update the revision meta as well. - $revisions = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) ); - if ( ! empty( $revisions ) ) { - $revision = array_shift( $revisions ); - wp_save_revisioned_meta_fields( $revision->ID, $post_id ); - } + // Update any revisioned meta. + $revisions = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) ); + if ( ! empty( $revisions ) ) { + $revision = array_shift( $revisions ); + wp_save_revisioned_meta_fields( $revision->ID, $post_id ); } $post = get_post( $post_id ); @@ -940,13 +940,12 @@ public function update_item( $request ) { if ( is_wp_error( $meta_update ) ) { return $meta_update; } - - // Update the revision meta as well. - $revisions = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) ); - if ( ! empty( $revisions ) ) { - $revision = array_shift( $revisions ); - wp_save_revisioned_meta_fields( $revision->ID, $post_id ); - } + } + // Update any revisioned meta. + $revisions = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) ); + if ( ! empty( $revisions ) ) { + $revision = array_shift( $revisions ); + wp_save_revisioned_meta_fields( $revision->ID, $post_id ); } $post = get_post( $post_id ); From 15e8bbd4f4b9532cfc46c912ae28cc36384b0cba Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 1 Aug 2023 10:45:59 -0600 Subject: [PATCH 040/101] Test that the revisions endpoint includes meta --- .../tests/rest-api/rest-post-meta-fields.php | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/tests/phpunit/tests/rest-api/rest-post-meta-fields.php b/tests/phpunit/tests/rest-api/rest-post-meta-fields.php index dcc549b872d7e..5c2e9e7db87fc 100644 --- a/tests/phpunit/tests/rest-api/rest-post-meta-fields.php +++ b/tests/phpunit/tests/rest-api/rest-post-meta-fields.php @@ -3223,5 +3223,50 @@ public function test_revisioned_post_meta_with_posts_endpoint() { // Restore Revision 2 and verify the post gets the correct meta value. wp_restore_post_revision( $revision_id_2 ); $this->assertSame( 'baz', get_post_meta( $post_id, 'foo', true ) ); + + // Test with multiple meta values. Note that the posts endpoint doesn't accept multiple meta values, so we'll add those manually. + $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', $post_id ) ); + $request->set_body_params( + array( + 'title' => 'Revision 4', + 'meta' => array( + 'foo' => 'bar', + ), + ) + ); + $response = rest_get_server()->dispatch( $request ); + $this->assertSame( 200, $response->get_status() ); + + // Add additional meta values. + add_post_meta( $post_id, 'foo', 'bat' ); + add_post_meta( $post_id, 'foo', 'baz' ); + + // Log the current post meta. + $meta = get_post_meta( $post_id ); + + // Update the post. + $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', $post_id ) ); + $request->set_body_params( + array( + 'title' => 'Revision 4 update', + ) + ); + $response = rest_get_server()->dispatch( $request ); + $this->assertSame( 200, $response->get_status() ); + + // Get the last revision. + $revisions = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) ); + $revision_id_4 = array_shift( $revisions )->ID; + + // Check that the revision has the correct meta value. + $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d/revisions/%d', $post_id, $revision_id_4 ) ); + $response = rest_get_server()->dispatch( $request ); + $this->assertSame( 200, $response->get_status() ); + + $this->assertSame( + array( 'bar', 'bat', 'baz' ), + $response->get_data()['meta']['foo'] + ); + } } From a3fa561d1c1240cd993550dbe69ce92da3a61e45 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 1 Aug 2023 12:01:55 -0600 Subject: [PATCH 041/101] phpcbf --- tests/phpunit/tests/rest-api/rest-post-meta-fields.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/phpunit/tests/rest-api/rest-post-meta-fields.php b/tests/phpunit/tests/rest-api/rest-post-meta-fields.php index 5c2e9e7db87fc..a184ba20220dc 100644 --- a/tests/phpunit/tests/rest-api/rest-post-meta-fields.php +++ b/tests/phpunit/tests/rest-api/rest-post-meta-fields.php @@ -3159,7 +3159,6 @@ public function test_revisioned_post_meta_with_posts_endpoint() { $data = $response->get_data(); $this->assertSame( array( 'bar' ), $response->get_data()['meta']['foo'] ); - // Check that the post meta is set correctly. $this->assertSame( 'bar', get_post_meta( $revision_id, 'foo', true ) ); @@ -3180,7 +3179,6 @@ public function test_revisioned_post_meta_with_posts_endpoint() { $revisions = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) ); $revision_id_2 = array_shift( $revisions )->ID; - // Check that the revision has the correct meta value. $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d/revisions/%d', $post_id, $revision_id_2 ) ); $response = rest_get_server()->dispatch( $request ); From f57b79b37f88f7291b96077c745733e7b299819e Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Thu, 17 Aug 2023 17:12:13 -0600 Subject: [PATCH 042/101] Update REST tests to reflect changed schema --- tests/phpunit/tests/rest-api/rest-revisions-controller.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/phpunit/tests/rest-api/rest-revisions-controller.php b/tests/phpunit/tests/rest-api/rest-revisions-controller.php index e0b713c882bc7..74cd040d85f68 100644 --- a/tests/phpunit/tests/rest-api/rest-revisions-controller.php +++ b/tests/phpunit/tests/rest-api/rest-revisions-controller.php @@ -179,6 +179,7 @@ public function test_get_item() { 'modified_gmt', 'guid', 'id', + 'meta', 'parent', 'slug', 'title', @@ -335,7 +336,7 @@ public function test_get_item_schema() { $response = rest_get_server()->dispatch( $request ); $data = $response->get_data(); $properties = $data['schema']['properties']; - $this->assertCount( 12, $properties ); + $this->assertCount( 13, $properties ); $this->assertArrayHasKey( 'author', $properties ); $this->assertArrayHasKey( 'content', $properties ); $this->assertArrayHasKey( 'date', $properties ); @@ -348,6 +349,7 @@ public function test_get_item_schema() { $this->assertArrayHasKey( 'parent', $properties ); $this->assertArrayHasKey( 'slug', $properties ); $this->assertArrayHasKey( 'title', $properties ); + $this->assertArrayHasKey( 'meta', $properties ); } public function test_create_item() { From cad92721e3c2756436295c6576b5e8f6c75df131 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Thu, 17 Aug 2023 22:57:09 -0600 Subject: [PATCH 043/101] phpcbf --- src/wp-includes/blocks/footnotes.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wp-includes/blocks/footnotes.php b/src/wp-includes/blocks/footnotes.php index 5f578909565bf..3327070b70a2e 100644 --- a/src/wp-includes/blocks/footnotes.php +++ b/src/wp-includes/blocks/footnotes.php @@ -115,8 +115,8 @@ function wp_get_footnotes_from_revision( $revision_field, $field, $revision ) { return $revision_field; } $footnotes_html = ''; - $x = 1; - foreach( $footnotes as $footnote ) { + $x = 1; + foreach ( $footnotes as $footnote ) { $footnotes_html .= sprintf( "%s. %s\n", $x++, From 57c872a02f16a793af15b5d35eaeff2cdba77978 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Thu, 17 Aug 2023 23:24:32 -0600 Subject: [PATCH 044/101] Improve tests --- tests/phpunit/tests/rest-api/rest-autosaves-controller.php | 5 +++-- tests/phpunit/tests/rest-api/rest-revisions-controller.php | 3 +-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/phpunit/tests/rest-api/rest-autosaves-controller.php b/tests/phpunit/tests/rest-api/rest-autosaves-controller.php index e987a5f699983..3f2ceeb844572 100644 --- a/tests/phpunit/tests/rest-api/rest-autosaves-controller.php +++ b/tests/phpunit/tests/rest-api/rest-autosaves-controller.php @@ -215,12 +215,12 @@ public function test_get_item() { 'author', 'date', 'date_gmt', + 'id', 'modified', 'modified_gmt', - 'guid', - 'id', 'parent', 'slug', + 'guid', 'title', 'excerpt', 'content', @@ -303,6 +303,7 @@ public function test_get_item_schema() { $this->assertArrayHasKey( 'slug', $properties ); $this->assertArrayHasKey( 'title', $properties ); $this->assertArrayHasKey( 'preview_link', $properties ); + $this->assertArrayHasKey( 'meta', $properties ); } public function test_create_item() { diff --git a/tests/phpunit/tests/rest-api/rest-revisions-controller.php b/tests/phpunit/tests/rest-api/rest-revisions-controller.php index 74cd040d85f68..3004055f15543 100644 --- a/tests/phpunit/tests/rest-api/rest-revisions-controller.php +++ b/tests/phpunit/tests/rest-api/rest-revisions-controller.php @@ -336,7 +336,7 @@ public function test_get_item_schema() { $response = rest_get_server()->dispatch( $request ); $data = $response->get_data(); $properties = $data['schema']['properties']; - $this->assertCount( 13, $properties ); + $this->assertCount( 12, $properties ); $this->assertArrayHasKey( 'author', $properties ); $this->assertArrayHasKey( 'content', $properties ); $this->assertArrayHasKey( 'date', $properties ); @@ -349,7 +349,6 @@ public function test_get_item_schema() { $this->assertArrayHasKey( 'parent', $properties ); $this->assertArrayHasKey( 'slug', $properties ); $this->assertArrayHasKey( 'title', $properties ); - $this->assertArrayHasKey( 'meta', $properties ); } public function test_create_item() { From ca010585d4e062228c7437c838be6e4e597fda97 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Fri, 18 Aug 2023 13:47:37 -0600 Subject: [PATCH 045/101] Finetune tests based on git action failures --- tests/phpunit/tests/rest-api/rest-autosaves-controller.php | 1 + tests/phpunit/tests/rest-api/rest-revisions-controller.php | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/phpunit/tests/rest-api/rest-autosaves-controller.php b/tests/phpunit/tests/rest-api/rest-autosaves-controller.php index 3f2ceeb844572..e56be2a9106b5 100644 --- a/tests/phpunit/tests/rest-api/rest-autosaves-controller.php +++ b/tests/phpunit/tests/rest-api/rest-autosaves-controller.php @@ -216,6 +216,7 @@ public function test_get_item() { 'date', 'date_gmt', 'id', + 'meta', 'modified', 'modified_gmt', 'parent', diff --git a/tests/phpunit/tests/rest-api/rest-revisions-controller.php b/tests/phpunit/tests/rest-api/rest-revisions-controller.php index 3004055f15543..74cd040d85f68 100644 --- a/tests/phpunit/tests/rest-api/rest-revisions-controller.php +++ b/tests/phpunit/tests/rest-api/rest-revisions-controller.php @@ -336,7 +336,7 @@ public function test_get_item_schema() { $response = rest_get_server()->dispatch( $request ); $data = $response->get_data(); $properties = $data['schema']['properties']; - $this->assertCount( 12, $properties ); + $this->assertCount( 13, $properties ); $this->assertArrayHasKey( 'author', $properties ); $this->assertArrayHasKey( 'content', $properties ); $this->assertArrayHasKey( 'date', $properties ); @@ -349,6 +349,7 @@ public function test_get_item_schema() { $this->assertArrayHasKey( 'parent', $properties ); $this->assertArrayHasKey( 'slug', $properties ); $this->assertArrayHasKey( 'title', $properties ); + $this->assertArrayHasKey( 'meta', $properties ); } public function test_create_item() { From 92590aacc8eb8df54c2acbf0313dbdda9c4a6bbf Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Fri, 18 Aug 2023 14:07:34 -0600 Subject: [PATCH 046/101] test --- tests/phpunit/tests/rest-api/rest-autosaves-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/phpunit/tests/rest-api/rest-autosaves-controller.php b/tests/phpunit/tests/rest-api/rest-autosaves-controller.php index e56be2a9106b5..fac47287f5b2e 100644 --- a/tests/phpunit/tests/rest-api/rest-autosaves-controller.php +++ b/tests/phpunit/tests/rest-api/rest-autosaves-controller.php @@ -290,7 +290,7 @@ public function test_get_item_schema() { $response = rest_get_server()->dispatch( $request ); $data = $response->get_data(); $properties = $data['schema']['properties']; - $this->assertCount( 13, $properties ); + $this->assertCount( 14, $properties ); $this->assertArrayHasKey( 'author', $properties ); $this->assertArrayHasKey( 'content', $properties ); $this->assertArrayHasKey( 'date', $properties ); From 6fd9a9ee68c34f38d92b511c7219b242cf31cac5 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Thu, 24 Aug 2023 06:46:30 -0600 Subject: [PATCH 047/101] restore footnotes.php from trunk --- src/wp-includes/blocks/footnotes.php | 122 +++++++++++++++++++++++---- 1 file changed, 104 insertions(+), 18 deletions(-) diff --git a/src/wp-includes/blocks/footnotes.php b/src/wp-includes/blocks/footnotes.php index 3327070b70a2e..bd7734d7d02d1 100644 --- a/src/wp-includes/blocks/footnotes.php +++ b/src/wp-includes/blocks/footnotes.php @@ -68,10 +68,9 @@ function register_block_core_footnotes() { $post_type, 'footnotes', array( - 'show_in_rest' => true, - 'single' => true, - 'type' => 'string', - 'revisions_enabled' => true, + 'show_in_rest' => true, + 'single' => true, + 'type' => 'string', ) ); } @@ -84,6 +83,106 @@ function register_block_core_footnotes() { } add_action( 'init', 'register_block_core_footnotes' ); +/** + * Saves the footnotes meta value to the revision. + * + * @since 6.3.0 + * + * @param int $revision_id The revision ID. + */ +function wp_save_footnotes_meta( $revision_id ) { + $post_id = wp_is_post_revision( $revision_id ); + + if ( $post_id ) { + $footnotes = get_post_meta( $post_id, 'footnotes', true ); + + if ( $footnotes ) { + // Can't use update_post_meta() because it doesn't allow revisions. + update_metadata( 'post', $revision_id, 'footnotes', $footnotes ); + } + } +} +add_action( 'wp_after_insert_post', 'wp_save_footnotes_meta' ); + +/** + * Keeps track of the revision ID for "rest_after_insert_{$post_type}". + * + * @since 6.3.0 + * + * @global int $wp_temporary_footnote_revision_id The footnote revision ID. + * + * @param int $revision_id The revision ID. + */ +function wp_keep_footnotes_revision_id( $revision_id ) { + global $wp_temporary_footnote_revision_id; + $wp_temporary_footnote_revision_id = $revision_id; +} +add_action( '_wp_put_post_revision', 'wp_keep_footnotes_revision_id' ); + +/** + * This is a specific fix for the REST API. The REST API doesn't update + * the post and post meta in one go (through `meta_input`). While it + * does fix the `wp_after_insert_post` hook to be called correctly after + * updating meta, it does NOT fix hooks such as post_updated and + * save_post, which are normally also fired after post meta is updated + * in `wp_insert_post()`. Unfortunately, `wp_save_post_revision` is + * added to the `post_updated` action, which means the meta is not + * available at the time, so we have to add it afterwards through the + * `"rest_after_insert_{$post_type}"` action. + * + * @since 6.3.0 + * + * @global int $wp_temporary_footnote_revision_id The footnote revision ID. + * + * @param WP_Post $post The post object. + */ +function wp_add_footnotes_revisions_to_post_meta( $post ) { + global $wp_temporary_footnote_revision_id; + + if ( $wp_temporary_footnote_revision_id ) { + $revision = get_post( $wp_temporary_footnote_revision_id ); + + if ( ! $revision ) { + return; + } + + $post_id = $revision->post_parent; + + // Just making sure we're updating the right revision. + if ( $post->ID === $post_id ) { + $footnotes = get_post_meta( $post_id, 'footnotes', true ); + + if ( $footnotes ) { + // Can't use update_post_meta() because it doesn't allow revisions. + update_metadata( 'post', $wp_temporary_footnote_revision_id, 'footnotes', $footnotes ); + } + } + } +} + +foreach ( array( 'post', 'page' ) as $post_type ) { + add_action( "rest_after_insert_{$post_type}", 'wp_add_footnotes_revisions_to_post_meta' ); +} + +/** + * Restores the footnotes meta value from the revision. + * + * @since 6.3.0 + * + * @param int $post_id The post ID. + * @param int $revision_id The revision ID. + */ +function wp_restore_footnotes_from_revision( $post_id, $revision_id ) { + $footnotes = get_post_meta( $revision_id, 'footnotes', true ); + + if ( $footnotes ) { + update_post_meta( $post_id, 'footnotes', $footnotes ); + } else { + delete_post_meta( $post_id, 'footnotes' ); + } +} +add_action( 'wp_restore_post_revision', 'wp_restore_footnotes_from_revision', 10, 2 ); + /** * Adds the footnotes field to the revision. * @@ -110,20 +209,7 @@ function wp_add_footnotes_to_revision( $fields ) { * @return string The field value. */ function wp_get_footnotes_from_revision( $revision_field, $field, $revision ) { - $footnotes = json_decode( get_metadata( 'post', $revision->ID, $field, true ) ); - if ( empty( $footnotes ) ) { - return $revision_field; - } - $footnotes_html = ''; - $x = 1; - foreach ( $footnotes as $footnote ) { - $footnotes_html .= sprintf( - "%s. %s\n", - $x++, - $footnote->content - ); - } - return $footnotes_html; + return get_metadata( 'post', $revision->ID, $field, true ); } add_filter( '_wp_post_revision_field_footnotes', 'wp_get_footnotes_from_revision', 10, 3 ); From 7f70990e341e5044708eb03bc85af3c528cf6e58 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Thu, 24 Aug 2023 07:01:19 -0600 Subject: [PATCH 048/101] Update wp-api-generated fixtures --- tests/qunit/fixtures/wp-api-generated.js | 26 ++++++++++++++---------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/tests/qunit/fixtures/wp-api-generated.js b/tests/qunit/fixtures/wp-api-generated.js index 27aa52edcc352..cc82fc90bf386 100644 --- a/tests/qunit/fixtures/wp-api-generated.js +++ b/tests/qunit/fixtures/wp-api-generated.js @@ -18,13 +18,7 @@ mockedApiResponse.Schema = { "wp-site-health/v1", "wp-block-editor/v1" ], - "authentication": { - "application-passwords": { - "endpoints": { - "authorization": "http://example.org/wp-admin/authorize-application.php" - } - } - }, + "authentication": [], "routes": { "/": { "namespace": "", @@ -11417,6 +11411,7 @@ mockedApiResponse.postRevisions = [ "excerpt": { "rendered": "" }, + "meta": [], "_links": { "parent": [ { @@ -11446,6 +11441,7 @@ mockedApiResponse.postRevisions = [ "excerpt": { "rendered": "

REST API Client Fixture: Post

\n" }, + "meta": [], "_links": { "parent": [ { @@ -11476,7 +11472,8 @@ mockedApiResponse.revision = { }, "excerpt": { "rendered": "

REST API Client Fixture: Post

\n" - } + }, + "meta": [] }; mockedApiResponse.postAutosaves = [ @@ -11501,6 +11498,7 @@ mockedApiResponse.postAutosaves = [ "excerpt": { "rendered": "" }, + "meta": [], "_links": { "parent": [ { @@ -11531,7 +11529,8 @@ mockedApiResponse.autosave = { }, "excerpt": { "rendered": "" - } + }, + "meta": [] }; mockedApiResponse.PagesCollection = [ @@ -11677,6 +11676,7 @@ mockedApiResponse.pageRevisions = [ "excerpt": { "rendered": "" }, + "meta": [], "_links": { "parent": [ { @@ -11706,6 +11706,7 @@ mockedApiResponse.pageRevisions = [ "excerpt": { "rendered": "

REST API Client Fixture: Page

\n" }, + "meta": [], "_links": { "parent": [ { @@ -11736,7 +11737,8 @@ mockedApiResponse.pageRevision = { }, "excerpt": { "rendered": "

REST API Client Fixture: Page

\n" - } + }, + "meta": [] }; mockedApiResponse.pageAutosaves = [ @@ -11761,6 +11763,7 @@ mockedApiResponse.pageAutosaves = [ "excerpt": { "rendered": "" }, + "meta": [], "_links": { "parent": [ { @@ -11791,7 +11794,8 @@ mockedApiResponse.pageAutosave = { }, "excerpt": { "rendered": "" - } + }, + "meta": [] }; mockedApiResponse.MediaCollection = [ From 432a810a2101c257eccd895bfc3560a7a523bf01 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Thu, 24 Aug 2023 08:07:39 -0600 Subject: [PATCH 049/101] fix generated --- tests/qunit/fixtures/wp-api-generated.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/qunit/fixtures/wp-api-generated.js b/tests/qunit/fixtures/wp-api-generated.js index cc82fc90bf386..5c872e1dd1b50 100644 --- a/tests/qunit/fixtures/wp-api-generated.js +++ b/tests/qunit/fixtures/wp-api-generated.js @@ -18,7 +18,13 @@ mockedApiResponse.Schema = { "wp-site-health/v1", "wp-block-editor/v1" ], - "authentication": [], + "authentication": { + "application-passwords": { + "endpoints": { + "authorization": "http://example.org/wp-admin/authorize-application.php" + } + } + }, "routes": { "/": { "namespace": "", From d80b7b4aebf3415df420d0d7a7612521e6ad6b2b Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Thu, 24 Aug 2023 08:38:14 -0600 Subject: [PATCH 050/101] Try: move hooking of revisions --- src/wp-includes/default-filters.php | 2 +- .../rest-api/endpoints/class-wp-rest-posts-controller.php | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/wp-includes/default-filters.php b/src/wp-includes/default-filters.php index 666b30368b2bf..4e4af10eac647 100644 --- a/src/wp-includes/default-filters.php +++ b/src/wp-includes/default-filters.php @@ -415,7 +415,7 @@ add_action( 'plugins_loaded', 'wp_maybe_load_embeds', 0 ); add_action( 'shutdown', 'wp_ob_end_flush_all', 1 ); // Create a revision whenever a post is updated. -add_action( 'post_updated', 'wp_save_post_revision', 10, 1 ); +add_action( 'wp_after_insert_post', 'wp_save_post_revision', 9, 1 ); add_action( 'publish_post', '_publish_post_hook', 5, 1 ); add_action( 'transition_post_status', '_transition_post_status', 5, 3 ); add_action( 'transition_post_status', '_update_term_count_on_transition_post_status', 10, 3 ); diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php index 998221cd3eacd..31f4d43408245 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php @@ -673,9 +673,6 @@ public function create_item( $request ) { ); } - // Don't save the post meta revision because they aren't stored yet. - remove_action( '_wp_put_post_revision', 'wp_save_revisioned_meta_fields', 10, 2 ); - $post_id = wp_insert_post( wp_slash( (array) $prepared_post ), true, false ); if ( is_wp_error( $post_id ) ) { @@ -886,9 +883,6 @@ public function update_item( $request ) { ); } - // Don't save the post meta revision because they aren't stored yet. - remove_action( '_wp_put_post_revision', 'wp_save_revisioned_meta_fields', 10, 2 ); - // Convert the post object to an array, otherwise wp_update_post() will expect non-escaped input. $post_id = wp_update_post( wp_slash( (array) $post ), true, false ); From 7160abb64ca9d08d8ab3bd960d3558d5cce87bd2 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Thu, 24 Aug 2023 09:09:53 -0600 Subject: [PATCH 051/101] fire after hooks in revisions controller test --- .../rest-api/rest-global-styles-revisions-controller.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/phpunit/tests/rest-api/rest-global-styles-revisions-controller.php b/tests/phpunit/tests/rest-api/rest-global-styles-revisions-controller.php index 9e580d5f3c978..30e5b983eae9c 100644 --- a/tests/phpunit/tests/rest-api/rest-global-styles-revisions-controller.php +++ b/tests/phpunit/tests/rest-api/rest-global-styles-revisions-controller.php @@ -132,7 +132,7 @@ public static function wpSetupBeforeClass( $factory ) { ), ); - wp_update_post( $new_styles_post, true, false ); + wp_update_post( $new_styles_post, true ); $new_styles_post = array( 'ID' => self::$global_styles_id, @@ -162,7 +162,7 @@ public static function wpSetupBeforeClass( $factory ) { ), ); - wp_update_post( $new_styles_post, true, false ); + wp_update_post( $new_styles_post, true ); $new_styles_post = array( 'ID' => self::$global_styles_id, @@ -192,7 +192,7 @@ public static function wpSetupBeforeClass( $factory ) { ), ); - wp_update_post( $new_styles_post, true, false ); + wp_update_post( $new_styles_post, true ); wp_set_current_user( 0 ); } @@ -326,7 +326,7 @@ public function test_get_items_eligible_roles() { 'post_content' => wp_json_encode( $config ), ); - wp_update_post( $updated_styles_post, true, false ); + wp_update_post( $updated_styles_post, true ); $request = new WP_REST_Request( 'GET', '/wp/v2/global-styles/' . self::$global_styles_id . '/revisions' ); $response = rest_get_server()->dispatch( $request ); From 96301dabc8de405528a028a076f0915bfa52f025 Mon Sep 17 00:00:00 2001 From: Timothy Jacobs Date: Thu, 24 Aug 2023 11:28:35 -0400 Subject: [PATCH 052/101] Try only adding revision on update. --- src/wp-includes/default-filters.php | 3 +- src/wp-includes/revision.php | 353 +++++++++++++++------------- 2 files changed, 197 insertions(+), 159 deletions(-) diff --git a/src/wp-includes/default-filters.php b/src/wp-includes/default-filters.php index 4e4af10eac647..79870ee358bb0 100644 --- a/src/wp-includes/default-filters.php +++ b/src/wp-includes/default-filters.php @@ -415,7 +415,8 @@ add_action( 'plugins_loaded', 'wp_maybe_load_embeds', 0 ); add_action( 'shutdown', 'wp_ob_end_flush_all', 1 ); // Create a revision whenever a post is updated. -add_action( 'wp_after_insert_post', 'wp_save_post_revision', 9, 1 ); +add_action( 'wp_after_insert_post', 'wp_save_post_revision_on_insert', 9, 1 ); +add_action( 'post_updated', 'wp_save_post_revision' ); add_action( 'publish_post', '_publish_post_hook', 5, 1 ); add_action( 'transition_post_status', '_transition_post_status', 5, 3 ); add_action( 'transition_post_status', '_update_term_count_on_transition_post_status', 10, 3 ); diff --git a/src/wp-includes/revision.php b/src/wp-includes/revision.php index 662117b873ed6..61bb27bb7e6a2 100644 --- a/src/wp-includes/revision.php +++ b/src/wp-includes/revision.php @@ -9,17 +9,18 @@ /** * Determines which fields of posts are to be saved in revisions. * + * @param array|WP_Post $post Optional. A post array or a WP_Post object being processed + * for insertion as a post revision. Default empty array. + * @param bool $deprecated Not used. + * + * @return string[] Array of fields that can be versioned. * @since 2.6.0 * @since 4.5.0 A `WP_Post` object can now be passed to the `$post` parameter. * @since 4.5.0 The optional `$autosave` parameter was deprecated and renamed to `$deprecated`. * @access private * - * @param array|WP_Post $post Optional. A post array or a WP_Post object being processed - * for insertion as a post revision. Default empty array. - * @param bool $deprecated Not used. - * @return string[] Array of fields that can be versioned. */ -function _wp_post_revision_fields( $post = array(), $deprecated = false ) { +function _wp_post_revision_fields( $post = [], $deprecated = false ) { static $fields = null; if ( ! is_array( $post ) ) { @@ -28,11 +29,11 @@ function _wp_post_revision_fields( $post = array(), $deprecated = false ) { if ( is_null( $fields ) ) { // Allow these to be versioned. - $fields = array( + $fields = [ 'post_title' => __( 'Title' ), 'post_content' => __( 'Content' ), 'post_excerpt' => __( 'Excerpt' ), - ); + ]; } /** @@ -44,17 +45,30 @@ function _wp_post_revision_fields( $post = array(), $deprecated = false ) { * 'post_date_gmt', 'post_status', 'post_type', 'comment_count', * and 'post_author'. * + * @param string[] $fields List of fields to revision. Contains 'post_title', + * 'post_content', and 'post_excerpt' by default. + * @param array $post A post array being processed for insertion as a post revision. + * * @since 2.6.0 * @since 4.5.0 The `$post` parameter was added. * - * @param string[] $fields List of fields to revision. Contains 'post_title', - * 'post_content', and 'post_excerpt' by default. - * @param array $post A post array being processed for insertion as a post revision. */ $fields = apply_filters( '_wp_post_revision_fields', $fields, $post ); // WP uses these internally either in versioning or elsewhere - they cannot be versioned. - foreach ( array( 'ID', 'post_name', 'post_parent', 'post_date', 'post_date_gmt', 'post_status', 'post_type', 'comment_count', 'post_author' ) as $protect ) { + foreach ( + [ + 'ID', + 'post_name', + 'post_parent', + 'post_date', + 'post_date_gmt', + 'post_status', + 'post_type', + 'comment_count', + 'post_author', + ] as $protect + ) { unset( $fields[ $protect ] ); } @@ -64,22 +78,23 @@ function _wp_post_revision_fields( $post = array(), $deprecated = false ) { /** * Returns a post array ready to be inserted into the posts table as a post revision. * + * @param array|WP_Post $post Optional. A post array or a WP_Post object to be processed + * for insertion as a post revision. Default empty array. + * @param bool $autosave Optional. Is the revision an autosave? Default false. + * + * @return array Post array ready to be inserted as a post revision. * @since 4.5.0 * @access private * - * @param array|WP_Post $post Optional. A post array or a WP_Post object to be processed - * for insertion as a post revision. Default empty array. - * @param bool $autosave Optional. Is the revision an autosave? Default false. - * @return array Post array ready to be inserted as a post revision. */ -function _wp_post_revision_data( $post = array(), $autosave = false ) { +function _wp_post_revision_data( $post = [], $autosave = false ) { if ( ! is_array( $post ) ) { $post = get_post( $post, ARRAY_A ); } $fields = _wp_post_revision_fields( $post ); - $revision_data = array(); + $revision_data = []; foreach ( array_intersect( array_keys( $post ), array_keys( $fields ) ) as $field ) { $revision_data[ $field ] = $post[ $field ]; @@ -95,22 +110,39 @@ function _wp_post_revision_data( $post = array(), $autosave = false ) { return $revision_data; } +function wp_save_post_revision_on_insert( $post_id, $post, $update ) { + if ( ! $update ) { + return; + } + + if ( ! has_action( 'post_updated', 'wp_save_post_revision' ) ) { + return; + } + + wp_save_post_revision( $post_id ); +} + /** * Creates a revision for the current version of a post. * * Typically used immediately after a post update, as every update is a revision, * and the most recent revision always matches the current post. * - * @since 2.6.0 - * * @param int $post_id The ID of the post to save as a revision. + * * @return int|WP_Error|void Void or 0 if error, new revision ID, if success. + * @since 2.6.0 + * */ function wp_save_post_revision( $post_id ) { if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { return; } + if ( doing_action( 'post_updated' ) ) { + return; + } + $post = get_post( $post_id ); if ( ! $post ) { @@ -150,12 +182,13 @@ function wp_save_post_revision( $post_id ) { * By default a revision is saved only if one of the revisioned fields has changed. * This filter can override that so a revision is saved even if nothing has changed. * + * @param bool $check_for_changes Whether to check for changes before saving a new revision. + * Default true. + * @param WP_Post $latest_revision The latest revision post object. + * @param WP_Post $post The post object. + * * @since 3.6.0 * - * @param bool $check_for_changes Whether to check for changes before saving a new revision. - * Default true. - * @param WP_Post $latest_revision The latest revision post object. - * @param WP_Post $post The post object. */ if ( isset( $latest_revision ) && apply_filters( 'wp_save_post_revision_check_for_changes', true, $latest_revision, $post ) ) { $post_has_changed = false; @@ -173,11 +206,12 @@ function wp_save_post_revision( $post_id ) { * By default a revision is saved only if one of the revisioned fields has changed. * This filter allows for additional checks to determine if there were changes. * + * @param bool $post_has_changed Whether the post has changed. + * @param WP_Post $latest_revision The latest revision post object. + * @param WP_Post $post The post object. + * * @since 4.1.0 * - * @param bool $post_has_changed Whether the post has changed. - * @param WP_Post $latest_revision The latest revision post object. - * @param WP_Post $post The post object. */ $post_has_changed = (bool) apply_filters( 'wp_save_post_revision_post_has_changed', $post_has_changed, $latest_revision, $post ); @@ -200,21 +234,18 @@ function wp_save_post_revision( $post_id ) { return $return; } - $revisions = wp_get_post_revisions( $post_id, array( 'order' => 'ASC' ) ); + $revisions = wp_get_post_revisions( $post_id, [ 'order' => 'ASC' ] ); /** * Filters the revisions to be considered for deletion. * + * @param WP_Post[] $revisions Array of revisions, or an empty array if none. + * @param int $post_id The ID of the post to save as a revision. + * * @since 6.2.0 * - * @param WP_Post[] $revisions Array of revisions, or an empty array if none. - * @param int $post_id The ID of the post to save as a revision. */ - $revisions = apply_filters( - 'wp_save_post_revision_revisions_before_deletion', - $revisions, - $post_id - ); + $revisions = apply_filters( 'wp_save_post_revision_revisions_before_deletion', $revisions, $post_id ); $delete = count( $revisions ) - $revisions_to_keep; @@ -224,7 +255,7 @@ function wp_save_post_revision( $post_id ) { $revisions = array_slice( $revisions, 0, $delete ); - for ( $i = 0; isset( $revisions[ $i ] ); $i++ ) { + for ( $i = 0; isset( $revisions[ $i ] ); $i ++ ) { if ( str_contains( $revisions[ $i ]->post_name, 'autosave' ) ) { continue; } @@ -242,13 +273,14 @@ function wp_save_post_revision( $post_id ) { * If the optional $user_id is passed, returns the autosave for that user, otherwise * returns the latest autosave. * - * @since 2.6.0 - * - * @global wpdb $wpdb WordPress database abstraction object. - * * @param int $post_id The post ID. * @param int $user_id Optional. The post author ID. Default 0. + * * @return WP_Post|false The autosaved data or false on failure or when no autosave exists. + * @global wpdb $wpdb WordPress database abstraction object. + * + * @since 2.6.0 + * */ function wp_get_post_autosave( $post_id, $user_id = 0 ) { global $wpdb; @@ -267,13 +299,7 @@ function wp_get_post_autosave( $post_id, $user_id = 0 ) { ORDER BY post_date DESC LIMIT 1'; - $autosave = $wpdb->get_results( - $wpdb->prepare( - $autosave_query, - $post_id, - $autosave_name - ) - ); + $autosave = $wpdb->get_results( $wpdb->prepare( $autosave_query, $post_id, $autosave_name ) ); if ( ! $autosave ) { return false; @@ -285,10 +311,11 @@ function wp_get_post_autosave( $post_id, $user_id = 0 ) { /** * Determines if the specified post is a revision. * - * @since 2.6.0 - * * @param int|WP_Post $post Post ID or post object. + * * @return int|false ID of revision's parent on success, false if not a revision. + * @since 2.6.0 + * */ function wp_is_post_revision( $post ) { $post = wp_get_post_revision( $post ); @@ -303,10 +330,11 @@ function wp_is_post_revision( $post ) { /** * Determines if the specified post is an autosave. * - * @since 2.6.0 - * * @param int|WP_Post $post Post ID or post object. + * * @return int|false ID of autosave's parent on success, false if not a revision. + * @since 2.6.0 + * */ function wp_is_post_autosave( $post ) { $post = wp_get_post_revision( $post ); @@ -325,13 +353,14 @@ function wp_is_post_autosave( $post ) { /** * Inserts post data into the posts table as a post revision. * + * @param int|WP_Post|array|null $post Post ID, post object OR post array. + * @param bool $autosave Optional. Whether the revision is an autosave or not. + * Default false. + * + * @return int|WP_Error WP_Error or 0 if error, new revision ID if success. * @since 2.6.0 * @access private * - * @param int|WP_Post|array|null $post Post ID, post object OR post array. - * @param bool $autosave Optional. Whether the revision is an autosave or not. - * Default false. - * @return int|WP_Error WP_Error or 0 if error, new revision ID if success. */ function _wp_put_post_revision( $post = null, $autosave = false ) { if ( is_object( $post ) ) { @@ -360,11 +389,12 @@ function _wp_put_post_revision( $post = null, $autosave = false ) { /** * Fires once a revision has been saved. * + * @param int $revision_id Post revision ID. + * @param int $post_id Post ID. + * * @since 2.6.0 * @since 6.4.0 The post_id parameter was added. * - * @param int $revision_id Post revision ID. - * @param int $post_id Post ID. */ do_action( '_wp_put_post_revision', $revision_id, $post['post_parent'] ); } @@ -372,12 +402,11 @@ function _wp_put_post_revision( $post = null, $autosave = false ) { return $revision_id; } - /** * Save the revisioned meta fields. * * @param int $revision_id The ID of the revision to save the meta to. - * @param int $post_id The ID of the post the revision is associated with. + * @param int $post_id The ID of the post the revision is associated with. * * @since 6.4.0 */ @@ -394,14 +423,15 @@ function wp_save_revisioned_meta_fields( $revision_id, $post_id ) { /** * Gets a post revision. * - * @since 2.6.0 - * - * @param int|WP_Post $post Post ID or post object. - * @param string $output Optional. The required return type. One of OBJECT, ARRAY_A, or ARRAY_N, which + * @param int|WP_Post $post Post ID or post object. + * @param string $output Optional. The required return type. One of OBJECT, ARRAY_A, or ARRAY_N, which * correspond to a WP_Post object, an associative array, or a numeric array, * respectively. Default OBJECT. - * @param string $filter Optional sanitization filter. See sanitize_post(). Default 'raw'. + * @param string $filter Optional sanitization filter. See sanitize_post(). Default 'raw'. + * * @return WP_Post|array|null WP_Post (or array) on success, or null on failure. + * @since 2.6.0 + * */ function wp_get_post_revision( &$post, $output = OBJECT, $filter = 'raw' ) { $revision = get_post( $post, OBJECT, $filter ); @@ -418,9 +448,11 @@ function wp_get_post_revision( &$post, $output = OBJECT, $filter = 'raw' ) { return $revision; } elseif ( ARRAY_A === $output ) { $_revision = get_object_vars( $revision ); + return $_revision; } elseif ( ARRAY_N === $output ) { $_revision = array_values( get_object_vars( $revision ) ); + return $_revision; } @@ -432,11 +464,12 @@ function wp_get_post_revision( &$post, $output = OBJECT, $filter = 'raw' ) { * * Can restore a past revision using all fields of the post revision, or only selected fields. * - * @since 2.6.0 - * * @param int|WP_Post $revision Revision ID or revision object. - * @param array $fields Optional. What fields to restore from. Defaults to all. + * @param array $fields Optional. What fields to restore from. Defaults to all. + * * @return int|false|null Null if error, false if no fields to restore, (int) post ID if success. + * @since 2.6.0 + * */ function wp_restore_post_revision( $revision, $fields = null ) { $revision = wp_get_post_revision( $revision, ARRAY_A ); @@ -449,7 +482,7 @@ function wp_restore_post_revision( $revision, $fields = null ) { $fields = array_keys( _wp_post_revision_fields( $revision ) ); } - $update = array(); + $update = []; foreach ( array_intersect( array_keys( $revision ), $fields ) as $field ) { $update[ $field ] = $revision[ $field ]; } @@ -477,10 +510,11 @@ function wp_restore_post_revision( $revision, $fields = null ) { /** * Fires after a post revision has been restored. * + * @param int $post_id Post ID. + * @param int $revision_id Post revision ID. + * * @since 2.6.0 * - * @param int $post_id Post ID. - * @param int $revision_id Post revision ID. */ do_action( 'wp_restore_post_revision', $post_id, $revision['ID'] ); @@ -490,7 +524,7 @@ function wp_restore_post_revision( $revision, $fields = null ) { /** * Restore the revisioned meta values for a post. * - * @param int $post_id The ID of the post to restore the meta to. + * @param int $post_id The ID of the post to restore the meta to. * @param int $revision_id The ID of the revision to restore the meta from. * * @since 6.4.0 @@ -510,9 +544,9 @@ function wp_restore_post_revision_meta( $post_id, $revision_id ) { /** * Copy post meta for the given key from one post to another. * - * @param int $source_post_id Post ID to copy meta value(s) from. - * @param int $target_post_id Post ID to copy meta value(s) to. - * @param string $meta_key Meta key to copy. + * @param int $source_post_id Post ID to copy meta value(s) from. + * @param int $target_post_id Post ID to copy meta value(s) to. + * @param string $meta_key Meta key to copy. * * @since 6.4.0 */ @@ -530,9 +564,9 @@ function _wp_copy_post_meta( $source_post_id, $target_post_id, $meta_key ) { /** * Determine which post meta fields should be revisioned. * + * @return array An array of meta keys to be revisioned. * @since 6.4.0 * - * @return array An array of meta keys to be revisioned. */ function wp_post_revision_meta_keys() { global $wp_revisioned_meta_keys; @@ -540,9 +574,10 @@ function wp_post_revision_meta_keys() { /** * Filter the list of post meta keys to be revisioned. * + * @param array $keys An array of meta fields to be revisioned. + * * @since 6.4.0 * - * @param array $keys An array of meta fields to be revisioned. */ return apply_filters( 'wp_post_revision_meta_keys', $wp_revisioned_meta_keys ); } @@ -550,9 +585,9 @@ function wp_post_revision_meta_keys() { /** * Check whether revisioned post meta fields have changed. * - * @param bool $post_has_changed Whether the post has changed. - * @param WP_Post $last_revision The last revision post object. - * @param WP_Post $post The post object. + * @param bool $post_has_changed Whether the post has changed. + * @param WP_Post $last_revision The last revision post object. + * @param WP_Post $post The post object. * * @since 6.4.0 */ @@ -563,6 +598,7 @@ function wp_check_revisioned_meta_fields_have_changed( $post_has_changed, WP_Pos break; } } + return $post_has_changed; } @@ -571,10 +607,11 @@ function wp_check_revisioned_meta_fields_have_changed( $post_has_changed, WP_Pos * * Deletes the row from the posts table corresponding to the specified revision. * - * @since 2.6.0 - * * @param int|WP_Post $revision Revision ID or revision object. + * * @return WP_Post|false|null Null or false if error, deleted post object if success. + * @since 2.6.0 + * */ function wp_delete_post_revision( $revision ) { $revision = wp_get_post_revision( $revision ); @@ -589,10 +626,11 @@ function wp_delete_post_revision( $revision ) { /** * Fires once a post revision has been deleted. * + * @param int $revision_id Post revision ID. + * @param WP_Post $revision Post revision object. + * * @since 2.6.0 * - * @param int $revision_id Post revision ID. - * @param WP_Post $revision Post revision object. */ do_action( 'wp_delete_post_revision', $revision->ID, $revision ); } @@ -603,45 +641,43 @@ function wp_delete_post_revision( $revision ) { /** * Returns all revisions of specified post. * - * @since 2.6.0 + * @param int|WP_Post $post Optional. Post ID or WP_Post object. Default is global `$post`. + * @param array|null $args Optional. Arguments for retrieving post revisions. Default null. * + * @return WP_Post[]|int[] Array of revision objects or IDs, or an empty array if none. * @see get_children() * - * @param int|WP_Post $post Optional. Post ID or WP_Post object. Default is global `$post`. - * @param array|null $args Optional. Arguments for retrieving post revisions. Default null. - * @return WP_Post[]|int[] Array of revision objects or IDs, or an empty array if none. + * @since 2.6.0 + * */ function wp_get_post_revisions( $post = 0, $args = null ) { $post = get_post( $post ); if ( ! $post || empty( $post->ID ) ) { - return array(); + return []; } - $defaults = array( + $defaults = [ 'order' => 'DESC', 'orderby' => 'date ID', 'check_enabled' => true, - ); + ]; $args = wp_parse_args( $args, $defaults ); if ( $args['check_enabled'] && ! wp_revisions_enabled( $post ) ) { - return array(); + return []; } - $args = array_merge( - $args, - array( + $args = array_merge( $args, [ 'post_parent' => $post->ID, 'post_type' => 'revision', 'post_status' => 'inherit', - ) - ); + ] ); $revisions = get_children( $args ); if ( ! $revisions ) { - return array(); + return []; } return $revisions; @@ -650,16 +686,17 @@ function wp_get_post_revisions( $post = 0, $args = null ) { /** * Returns the latest revision ID and count of revisions for a post. * - * @since 6.1.0 - * * @param int|WP_Post $post Optional. Post ID or WP_Post object. Default is global $post. + * * @return array|WP_Error { * Returns associative array with latest revision ID and total count, * or a WP_Error if the post does not exist or revisions are not enabled. * - * @type int $latest_id The latest revision post ID or 0 if no revisions exist. - * @type int $count The total count of revisions for the given post. + * @type int $latest_id The latest revision post ID or 0 if no revisions exist. + * @type int $count The total count of revisions for the given post. * } + * @since 6.1.0 + * */ function wp_get_latest_revision_id_and_total_count( $post = 0 ) { $post = get_post( $post ); @@ -672,7 +709,7 @@ function wp_get_latest_revision_id_and_total_count( $post = 0 ) { return new WP_Error( 'revisions_not_enabled', __( 'Revisions not enabled.' ) ); } - $args = array( + $args = [ 'post_parent' => $post->ID, 'fields' => 'ids', 'post_type' => 'revision', @@ -681,31 +718,32 @@ function wp_get_latest_revision_id_and_total_count( $post = 0 ) { 'orderby' => 'date ID', 'posts_per_page' => 1, 'ignore_sticky_posts' => true, - ); + ]; $revision_query = new WP_Query(); $revisions = $revision_query->query( $args ); if ( ! $revisions ) { - return array( + return [ 'latest_id' => 0, 'count' => 0, - ); + ]; } - return array( + return [ 'latest_id' => $revisions[0], 'count' => $revision_query->found_posts, - ); + ]; } /** * Returns the url for viewing and potentially restoring revisions of a given post. * - * @since 5.9.0 - * * @param int|WP_Post $post Optional. Post ID or WP_Post object. Default is global `$post`. + * * @return string|null The URL for editing revisions on the given post, otherwise null. + * @since 5.9.0 + * */ function wp_get_post_revisions_url( $post = 0 ) { $post = get_post( $post ); @@ -735,10 +773,11 @@ function wp_get_post_revisions_url( $post = 0 ) { /** * Determines whether revisions are enabled for a given post. * - * @since 3.6.0 - * * @param WP_Post $post The post object. + * * @return bool True if number of revisions to keep isn't zero, false otherwise. + * @since 3.6.0 + * */ function wp_revisions_enabled( $post ) { return wp_revisions_to_keep( $post ) !== 0; @@ -752,16 +791,17 @@ function wp_revisions_enabled( $post ) { * The constant WP_POST_REVISIONS can be set in wp-config to specify the limit * of revisions to keep. * - * @since 3.6.0 - * * @param WP_Post $post The post object. + * * @return int The number of revisions to keep. + * @since 3.6.0 + * */ function wp_revisions_to_keep( $post ) { $num = WP_POST_REVISIONS; if ( true === $num ) { - $num = -1; + $num = - 1; } else { $num = (int) $num; } @@ -775,10 +815,11 @@ function wp_revisions_to_keep( $post ) { * * Overrides the value of WP_POST_REVISIONS. * + * @param int $num Number of revisions to store. + * @param WP_Post $post Post object. + * * @since 3.6.0 * - * @param int $num Number of revisions to store. - * @param WP_Post $post Post object. */ $num = apply_filters( 'wp_revisions_to_keep', $num, $post ); @@ -795,10 +836,11 @@ function wp_revisions_to_keep( $post ) { * - `wp_post_revisions_to_keep` * - `wp_page_revisions_to_keep` * + * @param int $num Number of revisions to store. + * @param WP_Post $post Post object. + * * @since 5.8.0 * - * @param int $num Number of revisions to store. - * @param WP_Post $post Post object. */ $num = apply_filters( "wp_{$post->post_type}_revisions_to_keep", $num, $post ); @@ -808,11 +850,12 @@ function wp_revisions_to_keep( $post ) { /** * Sets up the post object for preview based on the post autosave. * + * @param WP_Post $post + * + * @return WP_Post|false * @since 2.7.0 * @access private * - * @param WP_Post $post - * @return WP_Post|false */ function _set_preview( $post ) { if ( ! is_object( $post ) ) { @@ -857,13 +900,14 @@ function _show_post_preview() { /** * Filters terms lookup to set the post format. * + * @param array $terms + * @param int $post_id + * @param string $taxonomy + * + * @return array * @since 3.6.0 * @access private * - * @param array $terms - * @param int $post_id - * @param string $taxonomy - * @return array */ function _wp_preview_terms_filter( $terms, $post_id, $taxonomy ) { $post = get_post(); @@ -872,19 +916,17 @@ function _wp_preview_terms_filter( $terms, $post_id, $taxonomy ) { return $terms; } - if ( empty( $_REQUEST['post_format'] ) || $post->ID !== $post_id - || 'post_format' !== $taxonomy || 'revision' === $post->post_type - ) { + if ( empty( $_REQUEST['post_format'] ) || $post->ID !== $post_id || 'post_format' !== $taxonomy || 'revision' === $post->post_type ) { return $terms; } if ( 'standard' === $_REQUEST['post_format'] ) { - $terms = array(); + $terms = []; } else { $term = get_term_by( 'slug', 'post-format-' . sanitize_key( $_REQUEST['post_format'] ), 'post_format' ); if ( $term ) { - $terms = array( $term ); // Can only have one post format. + $terms = [ $term ]; // Can only have one post format. } } @@ -894,13 +936,14 @@ function _wp_preview_terms_filter( $terms, $post_id, $taxonomy ) { /** * Filters post thumbnail lookup to set the post thumbnail. * + * @param null|array|string $value The value to return - a single metadata value, or an array of values. + * @param int $post_id Post ID. + * @param string $meta_key Meta key. + * + * @return null|array The default return value or the post thumbnail meta array. * @since 4.6.0 * @access private * - * @param null|array|string $value The value to return - a single metadata value, or an array of values. - * @param int $post_id Post ID. - * @param string $meta_key Meta key. - * @return null|array The default return value or the post thumbnail meta array. */ function _wp_preview_post_thumbnail_filter( $value, $post_id, $meta_key ) { $post = get_post(); @@ -909,10 +952,7 @@ function _wp_preview_post_thumbnail_filter( $value, $post_id, $meta_key ) { return $value; } - if ( empty( $_REQUEST['_thumbnail_id'] ) || empty( $_REQUEST['preview_id'] ) - || $post->ID !== $post_id || $post_id !== (int) $_REQUEST['preview_id'] - || '_thumbnail_id' !== $meta_key || 'revision' === $post->post_type - ) { + if ( empty( $_REQUEST['_thumbnail_id'] ) || empty( $_REQUEST['preview_id'] ) || $post->ID !== $post_id || $post_id !== (int) $_REQUEST['preview_id'] || '_thumbnail_id' !== $meta_key || 'revision' === $post->post_type ) { return $value; } @@ -928,11 +968,12 @@ function _wp_preview_post_thumbnail_filter( $value, $post_id, $meta_key ) { /** * Gets the post revision version. * + * @param WP_Post $revision + * + * @return int|false * @since 3.6.0 * @access private * - * @param WP_Post $revision - * @return int|false */ function _wp_get_post_revision_version( $revision ) { if ( is_object( $revision ) ) { @@ -951,14 +992,15 @@ function _wp_get_post_revision_version( $revision ) { /** * Upgrades the revisions author, adds the current post as a revision and sets the revisions version to 1. * - * @since 3.6.0 - * @access private + * @param WP_Post $post Post object. + * @param array $revisions Current revisions of the post. * + * @return bool true if the revisions were upgraded, false if problems. * @global wpdb $wpdb WordPress database abstraction object. * - * @param WP_Post $post Post object. - * @param array $revisions Current revisions of the post. - * @return bool true if the revisions were upgraded, false if problems. + * @since 3.6.0 + * @access private + * */ function _wp_upgrade_revisions_of_post( $post, $revisions ) { global $wpdb; @@ -984,7 +1026,6 @@ function _wp_upgrade_revisions_of_post( $post, $revisions ) { // Lock is not too old: some other process may be upgrading this post. Bail. return false; } - // Lock is too old - update it (below) and continue. } @@ -1015,9 +1056,9 @@ function _wp_upgrade_revisions_of_post( $post, $revisions ) { } // Always update the revision version. - $update = array( + $update = [ 'post_name' => preg_replace( '/^(\d+-(?:autosave|revision))[\d-]*$/', '$1-v1', $this_revision->post_name ), - ); + ]; /* * If this revision is the oldest revision of the post, i.e. no $prev_revision, @@ -1034,7 +1075,7 @@ function _wp_upgrade_revisions_of_post( $post, $revisions ) { } // Upgrade this revision. - $result = $wpdb->update( $wpdb->posts, $update, array( 'ID' => $this_revision->ID ) ); + $result = $wpdb->update( $wpdb->posts, $update, [ 'ID' => $this_revision->ID ] ); if ( $result ) { wp_cache_delete( $this_revision->ID, 'posts' ); @@ -1056,25 +1097,21 @@ function _wp_upgrade_revisions_of_post( $post, $revisions ) { * * Filters revisioned meta keys only. * - * @since 6.4.0 + * @param mixed $value Meta value to filter. + * @param int $object_id Object ID. + * @param string $meta_key Meta key to filter a value for. + * @param bool $single Whether to return a single value. Default false. * - * @param mixed $value Meta value to filter. - * @param int $object_id Object ID. - * @param string $meta_key Meta key to filter a value for. - * @param bool $single Whether to return a single value. Default false. * @return mixed Original meta value if the meta key isn't revisioned, the object doesn't exist, * the post type is a revision or the post ID doesn't match the object ID. * Otherwise, the revisioned meta value is returned for the preview. + * @since 6.4.0 + * */ function _wp_preview_meta_filter( $value, $object_id, $meta_key, $single ) { $post = get_post(); - if ( - empty( $post ) || - $post->ID !== $object_id || - ! in_array( $meta_key, wp_post_revision_meta_keys(), true ) || - 'revision' === $post->post_type - ) { + if ( empty( $post ) || $post->ID !== $object_id || ! in_array( $meta_key, wp_post_revision_meta_keys(), true ) || 'revision' === $post->post_type ) { return $value; } From e2d0cc7ceb82b8b4747a818365fd364851413c13 Mon Sep 17 00:00:00 2001 From: Timothy Jacobs Date: Thu, 24 Aug 2023 11:46:20 -0400 Subject: [PATCH 053/101] Use actions properly --- src/wp-includes/default-filters.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wp-includes/default-filters.php b/src/wp-includes/default-filters.php index 79870ee358bb0..cb262033cb979 100644 --- a/src/wp-includes/default-filters.php +++ b/src/wp-includes/default-filters.php @@ -415,8 +415,8 @@ add_action( 'plugins_loaded', 'wp_maybe_load_embeds', 0 ); add_action( 'shutdown', 'wp_ob_end_flush_all', 1 ); // Create a revision whenever a post is updated. -add_action( 'wp_after_insert_post', 'wp_save_post_revision_on_insert', 9, 1 ); -add_action( 'post_updated', 'wp_save_post_revision' ); +add_action( 'wp_after_insert_post', 'wp_save_post_revision_on_insert', 9, 3 ); +add_action( 'post_updated', 'wp_save_post_revision', 10, 1 ); add_action( 'publish_post', '_publish_post_hook', 5, 1 ); add_action( 'transition_post_status', '_transition_post_status', 5, 3 ); add_action( 'transition_post_status', '_update_term_count_on_transition_post_status', 10, 3 ); From 5f071456d65d6784e942f6b08d5f0f919c672ea3 Mon Sep 17 00:00:00 2001 From: Timothy Jacobs Date: Thu, 24 Aug 2023 11:48:52 -0400 Subject: [PATCH 054/101] Revert "Try only adding revision on update." This reverts commit 96301dabc8de405528a028a076f0915bfa52f025. --- src/wp-includes/revision.php | 353 ++++++++++++++++------------------- 1 file changed, 158 insertions(+), 195 deletions(-) diff --git a/src/wp-includes/revision.php b/src/wp-includes/revision.php index 61bb27bb7e6a2..662117b873ed6 100644 --- a/src/wp-includes/revision.php +++ b/src/wp-includes/revision.php @@ -9,18 +9,17 @@ /** * Determines which fields of posts are to be saved in revisions. * - * @param array|WP_Post $post Optional. A post array or a WP_Post object being processed - * for insertion as a post revision. Default empty array. - * @param bool $deprecated Not used. - * - * @return string[] Array of fields that can be versioned. * @since 2.6.0 * @since 4.5.0 A `WP_Post` object can now be passed to the `$post` parameter. * @since 4.5.0 The optional `$autosave` parameter was deprecated and renamed to `$deprecated`. * @access private * + * @param array|WP_Post $post Optional. A post array or a WP_Post object being processed + * for insertion as a post revision. Default empty array. + * @param bool $deprecated Not used. + * @return string[] Array of fields that can be versioned. */ -function _wp_post_revision_fields( $post = [], $deprecated = false ) { +function _wp_post_revision_fields( $post = array(), $deprecated = false ) { static $fields = null; if ( ! is_array( $post ) ) { @@ -29,11 +28,11 @@ function _wp_post_revision_fields( $post = [], $deprecated = false ) { if ( is_null( $fields ) ) { // Allow these to be versioned. - $fields = [ + $fields = array( 'post_title' => __( 'Title' ), 'post_content' => __( 'Content' ), 'post_excerpt' => __( 'Excerpt' ), - ]; + ); } /** @@ -45,30 +44,17 @@ function _wp_post_revision_fields( $post = [], $deprecated = false ) { * 'post_date_gmt', 'post_status', 'post_type', 'comment_count', * and 'post_author'. * - * @param string[] $fields List of fields to revision. Contains 'post_title', - * 'post_content', and 'post_excerpt' by default. - * @param array $post A post array being processed for insertion as a post revision. - * * @since 2.6.0 * @since 4.5.0 The `$post` parameter was added. * + * @param string[] $fields List of fields to revision. Contains 'post_title', + * 'post_content', and 'post_excerpt' by default. + * @param array $post A post array being processed for insertion as a post revision. */ $fields = apply_filters( '_wp_post_revision_fields', $fields, $post ); // WP uses these internally either in versioning or elsewhere - they cannot be versioned. - foreach ( - [ - 'ID', - 'post_name', - 'post_parent', - 'post_date', - 'post_date_gmt', - 'post_status', - 'post_type', - 'comment_count', - 'post_author', - ] as $protect - ) { + foreach ( array( 'ID', 'post_name', 'post_parent', 'post_date', 'post_date_gmt', 'post_status', 'post_type', 'comment_count', 'post_author' ) as $protect ) { unset( $fields[ $protect ] ); } @@ -78,23 +64,22 @@ function _wp_post_revision_fields( $post = [], $deprecated = false ) { /** * Returns a post array ready to be inserted into the posts table as a post revision. * - * @param array|WP_Post $post Optional. A post array or a WP_Post object to be processed - * for insertion as a post revision. Default empty array. - * @param bool $autosave Optional. Is the revision an autosave? Default false. - * - * @return array Post array ready to be inserted as a post revision. * @since 4.5.0 * @access private * + * @param array|WP_Post $post Optional. A post array or a WP_Post object to be processed + * for insertion as a post revision. Default empty array. + * @param bool $autosave Optional. Is the revision an autosave? Default false. + * @return array Post array ready to be inserted as a post revision. */ -function _wp_post_revision_data( $post = [], $autosave = false ) { +function _wp_post_revision_data( $post = array(), $autosave = false ) { if ( ! is_array( $post ) ) { $post = get_post( $post, ARRAY_A ); } $fields = _wp_post_revision_fields( $post ); - $revision_data = []; + $revision_data = array(); foreach ( array_intersect( array_keys( $post ), array_keys( $fields ) ) as $field ) { $revision_data[ $field ] = $post[ $field ]; @@ -110,39 +95,22 @@ function _wp_post_revision_data( $post = [], $autosave = false ) { return $revision_data; } -function wp_save_post_revision_on_insert( $post_id, $post, $update ) { - if ( ! $update ) { - return; - } - - if ( ! has_action( 'post_updated', 'wp_save_post_revision' ) ) { - return; - } - - wp_save_post_revision( $post_id ); -} - /** * Creates a revision for the current version of a post. * * Typically used immediately after a post update, as every update is a revision, * and the most recent revision always matches the current post. * - * @param int $post_id The ID of the post to save as a revision. - * - * @return int|WP_Error|void Void or 0 if error, new revision ID, if success. * @since 2.6.0 * + * @param int $post_id The ID of the post to save as a revision. + * @return int|WP_Error|void Void or 0 if error, new revision ID, if success. */ function wp_save_post_revision( $post_id ) { if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { return; } - if ( doing_action( 'post_updated' ) ) { - return; - } - $post = get_post( $post_id ); if ( ! $post ) { @@ -182,13 +150,12 @@ function wp_save_post_revision( $post_id ) { * By default a revision is saved only if one of the revisioned fields has changed. * This filter can override that so a revision is saved even if nothing has changed. * - * @param bool $check_for_changes Whether to check for changes before saving a new revision. - * Default true. - * @param WP_Post $latest_revision The latest revision post object. - * @param WP_Post $post The post object. - * * @since 3.6.0 * + * @param bool $check_for_changes Whether to check for changes before saving a new revision. + * Default true. + * @param WP_Post $latest_revision The latest revision post object. + * @param WP_Post $post The post object. */ if ( isset( $latest_revision ) && apply_filters( 'wp_save_post_revision_check_for_changes', true, $latest_revision, $post ) ) { $post_has_changed = false; @@ -206,12 +173,11 @@ function wp_save_post_revision( $post_id ) { * By default a revision is saved only if one of the revisioned fields has changed. * This filter allows for additional checks to determine if there were changes. * - * @param bool $post_has_changed Whether the post has changed. - * @param WP_Post $latest_revision The latest revision post object. - * @param WP_Post $post The post object. - * * @since 4.1.0 * + * @param bool $post_has_changed Whether the post has changed. + * @param WP_Post $latest_revision The latest revision post object. + * @param WP_Post $post The post object. */ $post_has_changed = (bool) apply_filters( 'wp_save_post_revision_post_has_changed', $post_has_changed, $latest_revision, $post ); @@ -234,18 +200,21 @@ function wp_save_post_revision( $post_id ) { return $return; } - $revisions = wp_get_post_revisions( $post_id, [ 'order' => 'ASC' ] ); + $revisions = wp_get_post_revisions( $post_id, array( 'order' => 'ASC' ) ); /** * Filters the revisions to be considered for deletion. * - * @param WP_Post[] $revisions Array of revisions, or an empty array if none. - * @param int $post_id The ID of the post to save as a revision. - * * @since 6.2.0 * + * @param WP_Post[] $revisions Array of revisions, or an empty array if none. + * @param int $post_id The ID of the post to save as a revision. */ - $revisions = apply_filters( 'wp_save_post_revision_revisions_before_deletion', $revisions, $post_id ); + $revisions = apply_filters( + 'wp_save_post_revision_revisions_before_deletion', + $revisions, + $post_id + ); $delete = count( $revisions ) - $revisions_to_keep; @@ -255,7 +224,7 @@ function wp_save_post_revision( $post_id ) { $revisions = array_slice( $revisions, 0, $delete ); - for ( $i = 0; isset( $revisions[ $i ] ); $i ++ ) { + for ( $i = 0; isset( $revisions[ $i ] ); $i++ ) { if ( str_contains( $revisions[ $i ]->post_name, 'autosave' ) ) { continue; } @@ -273,14 +242,13 @@ function wp_save_post_revision( $post_id ) { * If the optional $user_id is passed, returns the autosave for that user, otherwise * returns the latest autosave. * - * @param int $post_id The post ID. - * @param int $user_id Optional. The post author ID. Default 0. + * @since 2.6.0 * - * @return WP_Post|false The autosaved data or false on failure or when no autosave exists. * @global wpdb $wpdb WordPress database abstraction object. * - * @since 2.6.0 - * + * @param int $post_id The post ID. + * @param int $user_id Optional. The post author ID. Default 0. + * @return WP_Post|false The autosaved data or false on failure or when no autosave exists. */ function wp_get_post_autosave( $post_id, $user_id = 0 ) { global $wpdb; @@ -299,7 +267,13 @@ function wp_get_post_autosave( $post_id, $user_id = 0 ) { ORDER BY post_date DESC LIMIT 1'; - $autosave = $wpdb->get_results( $wpdb->prepare( $autosave_query, $post_id, $autosave_name ) ); + $autosave = $wpdb->get_results( + $wpdb->prepare( + $autosave_query, + $post_id, + $autosave_name + ) + ); if ( ! $autosave ) { return false; @@ -311,11 +285,10 @@ function wp_get_post_autosave( $post_id, $user_id = 0 ) { /** * Determines if the specified post is a revision. * - * @param int|WP_Post $post Post ID or post object. - * - * @return int|false ID of revision's parent on success, false if not a revision. * @since 2.6.0 * + * @param int|WP_Post $post Post ID or post object. + * @return int|false ID of revision's parent on success, false if not a revision. */ function wp_is_post_revision( $post ) { $post = wp_get_post_revision( $post ); @@ -330,11 +303,10 @@ function wp_is_post_revision( $post ) { /** * Determines if the specified post is an autosave. * - * @param int|WP_Post $post Post ID or post object. - * - * @return int|false ID of autosave's parent on success, false if not a revision. * @since 2.6.0 * + * @param int|WP_Post $post Post ID or post object. + * @return int|false ID of autosave's parent on success, false if not a revision. */ function wp_is_post_autosave( $post ) { $post = wp_get_post_revision( $post ); @@ -353,14 +325,13 @@ function wp_is_post_autosave( $post ) { /** * Inserts post data into the posts table as a post revision. * - * @param int|WP_Post|array|null $post Post ID, post object OR post array. - * @param bool $autosave Optional. Whether the revision is an autosave or not. - * Default false. - * - * @return int|WP_Error WP_Error or 0 if error, new revision ID if success. * @since 2.6.0 * @access private * + * @param int|WP_Post|array|null $post Post ID, post object OR post array. + * @param bool $autosave Optional. Whether the revision is an autosave or not. + * Default false. + * @return int|WP_Error WP_Error or 0 if error, new revision ID if success. */ function _wp_put_post_revision( $post = null, $autosave = false ) { if ( is_object( $post ) ) { @@ -389,12 +360,11 @@ function _wp_put_post_revision( $post = null, $autosave = false ) { /** * Fires once a revision has been saved. * - * @param int $revision_id Post revision ID. - * @param int $post_id Post ID. - * * @since 2.6.0 * @since 6.4.0 The post_id parameter was added. * + * @param int $revision_id Post revision ID. + * @param int $post_id Post ID. */ do_action( '_wp_put_post_revision', $revision_id, $post['post_parent'] ); } @@ -402,11 +372,12 @@ function _wp_put_post_revision( $post = null, $autosave = false ) { return $revision_id; } + /** * Save the revisioned meta fields. * * @param int $revision_id The ID of the revision to save the meta to. - * @param int $post_id The ID of the post the revision is associated with. + * @param int $post_id The ID of the post the revision is associated with. * * @since 6.4.0 */ @@ -423,15 +394,14 @@ function wp_save_revisioned_meta_fields( $revision_id, $post_id ) { /** * Gets a post revision. * - * @param int|WP_Post $post Post ID or post object. - * @param string $output Optional. The required return type. One of OBJECT, ARRAY_A, or ARRAY_N, which + * @since 2.6.0 + * + * @param int|WP_Post $post Post ID or post object. + * @param string $output Optional. The required return type. One of OBJECT, ARRAY_A, or ARRAY_N, which * correspond to a WP_Post object, an associative array, or a numeric array, * respectively. Default OBJECT. - * @param string $filter Optional sanitization filter. See sanitize_post(). Default 'raw'. - * + * @param string $filter Optional sanitization filter. See sanitize_post(). Default 'raw'. * @return WP_Post|array|null WP_Post (or array) on success, or null on failure. - * @since 2.6.0 - * */ function wp_get_post_revision( &$post, $output = OBJECT, $filter = 'raw' ) { $revision = get_post( $post, OBJECT, $filter ); @@ -448,11 +418,9 @@ function wp_get_post_revision( &$post, $output = OBJECT, $filter = 'raw' ) { return $revision; } elseif ( ARRAY_A === $output ) { $_revision = get_object_vars( $revision ); - return $_revision; } elseif ( ARRAY_N === $output ) { $_revision = array_values( get_object_vars( $revision ) ); - return $_revision; } @@ -464,12 +432,11 @@ function wp_get_post_revision( &$post, $output = OBJECT, $filter = 'raw' ) { * * Can restore a past revision using all fields of the post revision, or only selected fields. * - * @param int|WP_Post $revision Revision ID or revision object. - * @param array $fields Optional. What fields to restore from. Defaults to all. - * - * @return int|false|null Null if error, false if no fields to restore, (int) post ID if success. * @since 2.6.0 * + * @param int|WP_Post $revision Revision ID or revision object. + * @param array $fields Optional. What fields to restore from. Defaults to all. + * @return int|false|null Null if error, false if no fields to restore, (int) post ID if success. */ function wp_restore_post_revision( $revision, $fields = null ) { $revision = wp_get_post_revision( $revision, ARRAY_A ); @@ -482,7 +449,7 @@ function wp_restore_post_revision( $revision, $fields = null ) { $fields = array_keys( _wp_post_revision_fields( $revision ) ); } - $update = []; + $update = array(); foreach ( array_intersect( array_keys( $revision ), $fields ) as $field ) { $update[ $field ] = $revision[ $field ]; } @@ -510,11 +477,10 @@ function wp_restore_post_revision( $revision, $fields = null ) { /** * Fires after a post revision has been restored. * - * @param int $post_id Post ID. - * @param int $revision_id Post revision ID. - * * @since 2.6.0 * + * @param int $post_id Post ID. + * @param int $revision_id Post revision ID. */ do_action( 'wp_restore_post_revision', $post_id, $revision['ID'] ); @@ -524,7 +490,7 @@ function wp_restore_post_revision( $revision, $fields = null ) { /** * Restore the revisioned meta values for a post. * - * @param int $post_id The ID of the post to restore the meta to. + * @param int $post_id The ID of the post to restore the meta to. * @param int $revision_id The ID of the revision to restore the meta from. * * @since 6.4.0 @@ -544,9 +510,9 @@ function wp_restore_post_revision_meta( $post_id, $revision_id ) { /** * Copy post meta for the given key from one post to another. * - * @param int $source_post_id Post ID to copy meta value(s) from. - * @param int $target_post_id Post ID to copy meta value(s) to. - * @param string $meta_key Meta key to copy. + * @param int $source_post_id Post ID to copy meta value(s) from. + * @param int $target_post_id Post ID to copy meta value(s) to. + * @param string $meta_key Meta key to copy. * * @since 6.4.0 */ @@ -564,9 +530,9 @@ function _wp_copy_post_meta( $source_post_id, $target_post_id, $meta_key ) { /** * Determine which post meta fields should be revisioned. * - * @return array An array of meta keys to be revisioned. * @since 6.4.0 * + * @return array An array of meta keys to be revisioned. */ function wp_post_revision_meta_keys() { global $wp_revisioned_meta_keys; @@ -574,10 +540,9 @@ function wp_post_revision_meta_keys() { /** * Filter the list of post meta keys to be revisioned. * - * @param array $keys An array of meta fields to be revisioned. - * * @since 6.4.0 * + * @param array $keys An array of meta fields to be revisioned. */ return apply_filters( 'wp_post_revision_meta_keys', $wp_revisioned_meta_keys ); } @@ -585,9 +550,9 @@ function wp_post_revision_meta_keys() { /** * Check whether revisioned post meta fields have changed. * - * @param bool $post_has_changed Whether the post has changed. - * @param WP_Post $last_revision The last revision post object. - * @param WP_Post $post The post object. + * @param bool $post_has_changed Whether the post has changed. + * @param WP_Post $last_revision The last revision post object. + * @param WP_Post $post The post object. * * @since 6.4.0 */ @@ -598,7 +563,6 @@ function wp_check_revisioned_meta_fields_have_changed( $post_has_changed, WP_Pos break; } } - return $post_has_changed; } @@ -607,11 +571,10 @@ function wp_check_revisioned_meta_fields_have_changed( $post_has_changed, WP_Pos * * Deletes the row from the posts table corresponding to the specified revision. * - * @param int|WP_Post $revision Revision ID or revision object. - * - * @return WP_Post|false|null Null or false if error, deleted post object if success. * @since 2.6.0 * + * @param int|WP_Post $revision Revision ID or revision object. + * @return WP_Post|false|null Null or false if error, deleted post object if success. */ function wp_delete_post_revision( $revision ) { $revision = wp_get_post_revision( $revision ); @@ -626,11 +589,10 @@ function wp_delete_post_revision( $revision ) { /** * Fires once a post revision has been deleted. * - * @param int $revision_id Post revision ID. - * @param WP_Post $revision Post revision object. - * * @since 2.6.0 * + * @param int $revision_id Post revision ID. + * @param WP_Post $revision Post revision object. */ do_action( 'wp_delete_post_revision', $revision->ID, $revision ); } @@ -641,43 +603,45 @@ function wp_delete_post_revision( $revision ) { /** * Returns all revisions of specified post. * - * @param int|WP_Post $post Optional. Post ID or WP_Post object. Default is global `$post`. - * @param array|null $args Optional. Arguments for retrieving post revisions. Default null. + * @since 2.6.0 * - * @return WP_Post[]|int[] Array of revision objects or IDs, or an empty array if none. * @see get_children() * - * @since 2.6.0 - * + * @param int|WP_Post $post Optional. Post ID or WP_Post object. Default is global `$post`. + * @param array|null $args Optional. Arguments for retrieving post revisions. Default null. + * @return WP_Post[]|int[] Array of revision objects or IDs, or an empty array if none. */ function wp_get_post_revisions( $post = 0, $args = null ) { $post = get_post( $post ); if ( ! $post || empty( $post->ID ) ) { - return []; + return array(); } - $defaults = [ + $defaults = array( 'order' => 'DESC', 'orderby' => 'date ID', 'check_enabled' => true, - ]; + ); $args = wp_parse_args( $args, $defaults ); if ( $args['check_enabled'] && ! wp_revisions_enabled( $post ) ) { - return []; + return array(); } - $args = array_merge( $args, [ + $args = array_merge( + $args, + array( 'post_parent' => $post->ID, 'post_type' => 'revision', 'post_status' => 'inherit', - ] ); + ) + ); $revisions = get_children( $args ); if ( ! $revisions ) { - return []; + return array(); } return $revisions; @@ -686,17 +650,16 @@ function wp_get_post_revisions( $post = 0, $args = null ) { /** * Returns the latest revision ID and count of revisions for a post. * - * @param int|WP_Post $post Optional. Post ID or WP_Post object. Default is global $post. + * @since 6.1.0 * + * @param int|WP_Post $post Optional. Post ID or WP_Post object. Default is global $post. * @return array|WP_Error { * Returns associative array with latest revision ID and total count, * or a WP_Error if the post does not exist or revisions are not enabled. * - * @type int $latest_id The latest revision post ID or 0 if no revisions exist. - * @type int $count The total count of revisions for the given post. + * @type int $latest_id The latest revision post ID or 0 if no revisions exist. + * @type int $count The total count of revisions for the given post. * } - * @since 6.1.0 - * */ function wp_get_latest_revision_id_and_total_count( $post = 0 ) { $post = get_post( $post ); @@ -709,7 +672,7 @@ function wp_get_latest_revision_id_and_total_count( $post = 0 ) { return new WP_Error( 'revisions_not_enabled', __( 'Revisions not enabled.' ) ); } - $args = [ + $args = array( 'post_parent' => $post->ID, 'fields' => 'ids', 'post_type' => 'revision', @@ -718,32 +681,31 @@ function wp_get_latest_revision_id_and_total_count( $post = 0 ) { 'orderby' => 'date ID', 'posts_per_page' => 1, 'ignore_sticky_posts' => true, - ]; + ); $revision_query = new WP_Query(); $revisions = $revision_query->query( $args ); if ( ! $revisions ) { - return [ + return array( 'latest_id' => 0, 'count' => 0, - ]; + ); } - return [ + return array( 'latest_id' => $revisions[0], 'count' => $revision_query->found_posts, - ]; + ); } /** * Returns the url for viewing and potentially restoring revisions of a given post. * - * @param int|WP_Post $post Optional. Post ID or WP_Post object. Default is global `$post`. - * - * @return string|null The URL for editing revisions on the given post, otherwise null. * @since 5.9.0 * + * @param int|WP_Post $post Optional. Post ID or WP_Post object. Default is global `$post`. + * @return string|null The URL for editing revisions on the given post, otherwise null. */ function wp_get_post_revisions_url( $post = 0 ) { $post = get_post( $post ); @@ -773,11 +735,10 @@ function wp_get_post_revisions_url( $post = 0 ) { /** * Determines whether revisions are enabled for a given post. * - * @param WP_Post $post The post object. - * - * @return bool True if number of revisions to keep isn't zero, false otherwise. * @since 3.6.0 * + * @param WP_Post $post The post object. + * @return bool True if number of revisions to keep isn't zero, false otherwise. */ function wp_revisions_enabled( $post ) { return wp_revisions_to_keep( $post ) !== 0; @@ -791,17 +752,16 @@ function wp_revisions_enabled( $post ) { * The constant WP_POST_REVISIONS can be set in wp-config to specify the limit * of revisions to keep. * - * @param WP_Post $post The post object. - * - * @return int The number of revisions to keep. * @since 3.6.0 * + * @param WP_Post $post The post object. + * @return int The number of revisions to keep. */ function wp_revisions_to_keep( $post ) { $num = WP_POST_REVISIONS; if ( true === $num ) { - $num = - 1; + $num = -1; } else { $num = (int) $num; } @@ -815,11 +775,10 @@ function wp_revisions_to_keep( $post ) { * * Overrides the value of WP_POST_REVISIONS. * - * @param int $num Number of revisions to store. - * @param WP_Post $post Post object. - * * @since 3.6.0 * + * @param int $num Number of revisions to store. + * @param WP_Post $post Post object. */ $num = apply_filters( 'wp_revisions_to_keep', $num, $post ); @@ -836,11 +795,10 @@ function wp_revisions_to_keep( $post ) { * - `wp_post_revisions_to_keep` * - `wp_page_revisions_to_keep` * - * @param int $num Number of revisions to store. - * @param WP_Post $post Post object. - * * @since 5.8.0 * + * @param int $num Number of revisions to store. + * @param WP_Post $post Post object. */ $num = apply_filters( "wp_{$post->post_type}_revisions_to_keep", $num, $post ); @@ -850,12 +808,11 @@ function wp_revisions_to_keep( $post ) { /** * Sets up the post object for preview based on the post autosave. * - * @param WP_Post $post - * - * @return WP_Post|false * @since 2.7.0 * @access private * + * @param WP_Post $post + * @return WP_Post|false */ function _set_preview( $post ) { if ( ! is_object( $post ) ) { @@ -900,14 +857,13 @@ function _show_post_preview() { /** * Filters terms lookup to set the post format. * - * @param array $terms - * @param int $post_id - * @param string $taxonomy - * - * @return array * @since 3.6.0 * @access private * + * @param array $terms + * @param int $post_id + * @param string $taxonomy + * @return array */ function _wp_preview_terms_filter( $terms, $post_id, $taxonomy ) { $post = get_post(); @@ -916,17 +872,19 @@ function _wp_preview_terms_filter( $terms, $post_id, $taxonomy ) { return $terms; } - if ( empty( $_REQUEST['post_format'] ) || $post->ID !== $post_id || 'post_format' !== $taxonomy || 'revision' === $post->post_type ) { + if ( empty( $_REQUEST['post_format'] ) || $post->ID !== $post_id + || 'post_format' !== $taxonomy || 'revision' === $post->post_type + ) { return $terms; } if ( 'standard' === $_REQUEST['post_format'] ) { - $terms = []; + $terms = array(); } else { $term = get_term_by( 'slug', 'post-format-' . sanitize_key( $_REQUEST['post_format'] ), 'post_format' ); if ( $term ) { - $terms = [ $term ]; // Can only have one post format. + $terms = array( $term ); // Can only have one post format. } } @@ -936,14 +894,13 @@ function _wp_preview_terms_filter( $terms, $post_id, $taxonomy ) { /** * Filters post thumbnail lookup to set the post thumbnail. * - * @param null|array|string $value The value to return - a single metadata value, or an array of values. - * @param int $post_id Post ID. - * @param string $meta_key Meta key. - * - * @return null|array The default return value or the post thumbnail meta array. * @since 4.6.0 * @access private * + * @param null|array|string $value The value to return - a single metadata value, or an array of values. + * @param int $post_id Post ID. + * @param string $meta_key Meta key. + * @return null|array The default return value or the post thumbnail meta array. */ function _wp_preview_post_thumbnail_filter( $value, $post_id, $meta_key ) { $post = get_post(); @@ -952,7 +909,10 @@ function _wp_preview_post_thumbnail_filter( $value, $post_id, $meta_key ) { return $value; } - if ( empty( $_REQUEST['_thumbnail_id'] ) || empty( $_REQUEST['preview_id'] ) || $post->ID !== $post_id || $post_id !== (int) $_REQUEST['preview_id'] || '_thumbnail_id' !== $meta_key || 'revision' === $post->post_type ) { + if ( empty( $_REQUEST['_thumbnail_id'] ) || empty( $_REQUEST['preview_id'] ) + || $post->ID !== $post_id || $post_id !== (int) $_REQUEST['preview_id'] + || '_thumbnail_id' !== $meta_key || 'revision' === $post->post_type + ) { return $value; } @@ -968,12 +928,11 @@ function _wp_preview_post_thumbnail_filter( $value, $post_id, $meta_key ) { /** * Gets the post revision version. * - * @param WP_Post $revision - * - * @return int|false * @since 3.6.0 * @access private * + * @param WP_Post $revision + * @return int|false */ function _wp_get_post_revision_version( $revision ) { if ( is_object( $revision ) ) { @@ -992,15 +951,14 @@ function _wp_get_post_revision_version( $revision ) { /** * Upgrades the revisions author, adds the current post as a revision and sets the revisions version to 1. * - * @param WP_Post $post Post object. - * @param array $revisions Current revisions of the post. - * - * @return bool true if the revisions were upgraded, false if problems. - * @global wpdb $wpdb WordPress database abstraction object. - * * @since 3.6.0 * @access private * + * @global wpdb $wpdb WordPress database abstraction object. + * + * @param WP_Post $post Post object. + * @param array $revisions Current revisions of the post. + * @return bool true if the revisions were upgraded, false if problems. */ function _wp_upgrade_revisions_of_post( $post, $revisions ) { global $wpdb; @@ -1026,6 +984,7 @@ function _wp_upgrade_revisions_of_post( $post, $revisions ) { // Lock is not too old: some other process may be upgrading this post. Bail. return false; } + // Lock is too old - update it (below) and continue. } @@ -1056,9 +1015,9 @@ function _wp_upgrade_revisions_of_post( $post, $revisions ) { } // Always update the revision version. - $update = [ + $update = array( 'post_name' => preg_replace( '/^(\d+-(?:autosave|revision))[\d-]*$/', '$1-v1', $this_revision->post_name ), - ]; + ); /* * If this revision is the oldest revision of the post, i.e. no $prev_revision, @@ -1075,7 +1034,7 @@ function _wp_upgrade_revisions_of_post( $post, $revisions ) { } // Upgrade this revision. - $result = $wpdb->update( $wpdb->posts, $update, [ 'ID' => $this_revision->ID ] ); + $result = $wpdb->update( $wpdb->posts, $update, array( 'ID' => $this_revision->ID ) ); if ( $result ) { wp_cache_delete( $this_revision->ID, 'posts' ); @@ -1097,21 +1056,25 @@ function _wp_upgrade_revisions_of_post( $post, $revisions ) { * * Filters revisioned meta keys only. * - * @param mixed $value Meta value to filter. - * @param int $object_id Object ID. - * @param string $meta_key Meta key to filter a value for. - * @param bool $single Whether to return a single value. Default false. + * @since 6.4.0 * + * @param mixed $value Meta value to filter. + * @param int $object_id Object ID. + * @param string $meta_key Meta key to filter a value for. + * @param bool $single Whether to return a single value. Default false. * @return mixed Original meta value if the meta key isn't revisioned, the object doesn't exist, * the post type is a revision or the post ID doesn't match the object ID. * Otherwise, the revisioned meta value is returned for the preview. - * @since 6.4.0 - * */ function _wp_preview_meta_filter( $value, $object_id, $meta_key, $single ) { $post = get_post(); - if ( empty( $post ) || $post->ID !== $object_id || ! in_array( $meta_key, wp_post_revision_meta_keys(), true ) || 'revision' === $post->post_type ) { + if ( + empty( $post ) || + $post->ID !== $object_id || + ! in_array( $meta_key, wp_post_revision_meta_keys(), true ) || + 'revision' === $post->post_type + ) { return $value; } From afc110922c54f8943c133621e8450246d3957401 Mon Sep 17 00:00:00 2001 From: Timothy Jacobs Date: Thu, 24 Aug 2023 11:50:58 -0400 Subject: [PATCH 055/101] Don't rewrite everything --- src/wp-includes/revision.php | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/wp-includes/revision.php b/src/wp-includes/revision.php index 662117b873ed6..2ba20d4101e98 100644 --- a/src/wp-includes/revision.php +++ b/src/wp-includes/revision.php @@ -95,6 +95,27 @@ function _wp_post_revision_data( $post = array(), $autosave = false ) { return $revision_data; } +/** + * Saves revisions for a post after all changes have been made. + * + * @since 6.4.0 + * + * @param int $post_id The post id that was inserted. + * @param WP_Post $post The post object that was inserted. + * @param bool $update Whether this insert is updating an existing post. + */ +function wp_save_post_revision_on_insert( $post_id, $post, $update ) { + if ( $update ) { + return; + } + + if ( ! has_action( 'post_updated', 'wp_save_post_revision' ) ) { + return; + } + + wp_save_post_revision( $post_id ); +} + /** * Creates a revision for the current version of a post. * From ac279e30a58fdbdf0567dbb13614a89bc2c62a4d Mon Sep 17 00:00:00 2001 From: Timothy Jacobs Date: Thu, 24 Aug 2023 12:01:14 -0400 Subject: [PATCH 056/101] Correct logic --- src/wp-includes/revision.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/revision.php b/src/wp-includes/revision.php index 2ba20d4101e98..6d07c04e4ff4f 100644 --- a/src/wp-includes/revision.php +++ b/src/wp-includes/revision.php @@ -105,7 +105,7 @@ function _wp_post_revision_data( $post = array(), $autosave = false ) { * @param bool $update Whether this insert is updating an existing post. */ function wp_save_post_revision_on_insert( $post_id, $post, $update ) { - if ( $update ) { + if ( ! $update ) { return; } From 4205d65ae996905522b371413cc0959a5d05bc29 Mon Sep 17 00:00:00 2001 From: Timothy Jacobs Date: Thu, 24 Aug 2023 12:15:50 -0400 Subject: [PATCH 057/101] Check if the legacy hook is being used --- src/wp-includes/revision.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/wp-includes/revision.php b/src/wp-includes/revision.php index 6d07c04e4ff4f..2ec4e2bb55dff 100644 --- a/src/wp-includes/revision.php +++ b/src/wp-includes/revision.php @@ -132,6 +132,10 @@ function wp_save_post_revision( $post_id ) { return; } + if ( doing_action( 'post_updated' ) ) { + return; + } + $post = get_post( $post_id ); if ( ! $post ) { From e7b4495918ecf3419458dfe621790fd22ae17eb9 Mon Sep 17 00:00:00 2001 From: Timothy Jacobs Date: Thu, 24 Aug 2023 13:51:33 -0400 Subject: [PATCH 058/101] Separate single and multi meta tests. Use native meta fields handler. --- .../class-wp-rest-revisions-controller.php | 2 +- .../class-wp-rest-revision-meta-fields.php | 94 +------------- .../tests/rest-api/rest-post-meta-fields.php | 118 +++++++++++++++--- 3 files changed, 105 insertions(+), 109 deletions(-) diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php index 31e3d506eaa95..670f6e94e3abb 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php @@ -68,7 +68,7 @@ public function __construct( $parent_post_type ) { $this->rest_base = 'revisions'; $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->meta = new WP_REST_Revision_Meta_Fields( 'revision' ); + $this->meta = new WP_REST_Revision_Meta_Fields( $parent_post_type ); } /** diff --git a/src/wp-includes/rest-api/fields/class-wp-rest-revision-meta-fields.php b/src/wp-includes/rest-api/fields/class-wp-rest-revision-meta-fields.php index 248e06e79b56b..dc32dc5ab46c3 100644 --- a/src/wp-includes/rest-api/fields/class-wp-rest-revision-meta-fields.php +++ b/src/wp-includes/rest-api/fields/class-wp-rest-revision-meta-fields.php @@ -14,98 +14,6 @@ * * @see WP_REST_Meta_Fields */ -class WP_REST_Revision_Meta_Fields extends WP_REST_Meta_Fields { - - /** - * Revision type to register fields for. - * - * @since 6.4.0 - * @var string - */ - protected $post_type; - - /** - * Constructor. - * - * @since 6.4.0 - * - * @param string $post_type Revision type to register fields for. - */ - public function __construct( $post_type ) { - $this->post_type = $post_type; - } - - /** - * Retrieves the post meta type. - * - * @since 6.4.0 - * - * @return string The meta type. - */ - protected function get_meta_type() { - return 'revision'; - } - - /** - * Retrieves the post meta subtype. - * - * @since 6.4.0 - * - * @return string Subtype for the meta type, or empty string if no specific subtype. - */ - protected function get_meta_subtype() { - return $this->post_type; - } - - /** - * Retrieves the type for register_rest_field(). - * - * @since 6.4.0 - * - * @see register_rest_field() - * - * @return string The REST field type. - */ - public function get_rest_field_type() { - return $this->post_type; - } - - /** - * Retrieves the meta field value. - * - * @since 4.7.0 - * - * @param int $object_id Object ID to fetch meta for. - * @param WP_REST_Request $request Full details about the request. - * @return array Array containing the meta values keyed by name. - */ - public function get_value( $object_id, $request ) { - $data = get_post_meta( $object_id ); - return $data; - } - - /** - * Retrieves raw metadata value for the specified object. - * - * @since 5.5.0 - * - * @param string $meta_type Type of object metadata is for. Accepts 'post', 'comment', 'term', 'user', - * or any other object type with an associated meta table. - * @param int $object_id ID of the object metadata is for. - * @param string $meta_key Optional. Metadata key. If not specified, retrieve all metadata for - * the specified object. Default empty string. - * @param bool $single Optional. If true, return only the first value of the specified `$meta_key`. - * This parameter has no effect if `$meta_key` is not specified. Default false. - * @return mixed An array of values if `$single` is false. - * The value of the meta field if `$single` is true. - * False for an invalid `$object_id` (non-numeric, zero, or negative value), - * or if `$meta_type` is not specified. - * Null if the value does not exist. - */ - function get_metadata_raw( $meta_type, $object_id, $meta_key = '', $single = false ) { - $data = get_post_meta( $object_id, $request ); - return $data; - } - +class WP_REST_Revision_Meta_Fields extends WP_REST_Post_Meta_Fields { } diff --git a/tests/phpunit/tests/rest-api/rest-post-meta-fields.php b/tests/phpunit/tests/rest-api/rest-post-meta-fields.php index a184ba20220dc..c84c4269c7ef4 100644 --- a/tests/phpunit/tests/rest-api/rest-post-meta-fields.php +++ b/tests/phpunit/tests/rest-api/rest-post-meta-fields.php @@ -3115,11 +3115,11 @@ public function error_delete_query( $query ) { /** - * Test that post meta is revisioned when saving to the posts REST API endpoint. + * Test that single post meta is revisioned when saving to the posts REST API endpoint. * * @ticket 20564 */ - public function test_revisioned_post_meta_with_posts_endpoint() { + public function test_revisioned_single_post_meta_with_posts_endpoint() { $this->grant_write_permission(); register_post_meta( @@ -3157,7 +3157,7 @@ public function test_revisioned_post_meta_with_posts_endpoint() { $response = rest_get_server()->dispatch( $request ); $this->assertSame( 200, $response->get_status() ); $data = $response->get_data(); - $this->assertSame( array( 'bar' ), $response->get_data()['meta']['foo'] ); + $this->assertSame( 'bar', $response->get_data()['meta']['foo'] ); // Check that the post meta is set correctly. $this->assertSame( 'bar', get_post_meta( $revision_id, 'foo', true ) ); @@ -3183,7 +3183,7 @@ public function test_revisioned_post_meta_with_posts_endpoint() { $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d/revisions/%d', $post_id, $revision_id_2 ) ); $response = rest_get_server()->dispatch( $request ); $this->assertSame( 200, $response->get_status() ); - $this->assertSame( array( 'baz' ), $response->get_data()['meta']['foo'] ); + $this->assertSame( 'baz', $response->get_data()['meta']['foo'] ); // Check that the post meta is set correctly. $this->assertSame( 'baz', get_post_meta( $revision_id_2, 'foo', true ) ); @@ -3209,7 +3209,7 @@ public function test_revisioned_post_meta_with_posts_endpoint() { $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d/revisions/%d', $post_id, $revision_id_3 ) ); $response = rest_get_server()->dispatch( $request ); $this->assertSame( 200, $response->get_status() ); - $this->assertSame( array( 'qux' ), $response->get_data()['meta']['foo'] ); + $this->assertSame( 'qux', $response->get_data()['meta']['foo'] ); // Check that the post meta is set correctly. $this->assertSame( 'qux', get_post_meta( $revision_id_3, 'foo', true ) ); @@ -3221,24 +3221,44 @@ public function test_revisioned_post_meta_with_posts_endpoint() { // Restore Revision 2 and verify the post gets the correct meta value. wp_restore_post_revision( $revision_id_2 ); $this->assertSame( 'baz', get_post_meta( $post_id, 'foo', true ) ); + } + + /** + * Test that multi-post meta is revisioned when saving to the posts REST API endpoint. + * + * @ticket 20564 + */ + public function test_revisioned_multiple_post_meta_with_posts_endpoint() { + $this->grant_write_permission(); + + register_post_meta( + 'post', + 'foo', + array( + 'single' => false, + 'show_in_rest' => true, + 'revisions_enabled' => true, + ) + ); + + $post_id = self::$post_id; - // Test with multiple meta values. Note that the posts endpoint doesn't accept multiple meta values, so we'll add those manually. $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', $post_id ) ); $request->set_body_params( array( - 'title' => 'Revision 4', + 'title' => 'Revision 1', 'meta' => array( - 'foo' => 'bar', + 'foo' => array( + 'bar', + 'bat', + 'baz', + ), ), ) ); $response = rest_get_server()->dispatch( $request ); $this->assertSame( 200, $response->get_status() ); - // Add additional meta values. - add_post_meta( $post_id, 'foo', 'bat' ); - add_post_meta( $post_id, 'foo', 'baz' ); - // Log the current post meta. $meta = get_post_meta( $post_id ); @@ -3246,7 +3266,7 @@ public function test_revisioned_post_meta_with_posts_endpoint() { $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', $post_id ) ); $request->set_body_params( array( - 'title' => 'Revision 4 update', + 'title' => 'Revision 1 update', ) ); $response = rest_get_server()->dispatch( $request ); @@ -3254,10 +3274,10 @@ public function test_revisioned_post_meta_with_posts_endpoint() { // Get the last revision. $revisions = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) ); - $revision_id_4 = array_shift( $revisions )->ID; + $revision_id_1 = array_shift( $revisions )->ID; // Check that the revision has the correct meta value. - $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d/revisions/%d', $post_id, $revision_id_4 ) ); + $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d/revisions/%d', $post_id, $revision_id_1 ) ); $response = rest_get_server()->dispatch( $request ); $this->assertSame( 200, $response->get_status() ); @@ -3265,6 +3285,74 @@ public function test_revisioned_post_meta_with_posts_endpoint() { array( 'bar', 'bat', 'baz' ), $response->get_data()['meta']['foo'] ); + $this->assertSame( + array( 'bar', 'bat', 'baz' ), + get_post_meta( $revision_id_1, 'foo' ) + ); + + $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', $post_id ) ); + $request->set_body_params( + array( + 'title' => 'Revision 2', + 'meta' => array( + 'foo' => array( + 'car', + 'cat', + ), + ), + ) + ); + $response = rest_get_server()->dispatch( $request ); + $this->assertSame( 200, $response->get_status() ); + // Get the last revision. + $revisions = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) ); + $revision_id_2 = array_shift( $revisions )->ID; + + // Check that the revision has the correct meta value. + $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d/revisions/%d', $post_id, $revision_id_2 ) ); + $response = rest_get_server()->dispatch( $request ); + $this->assertSame( 200, $response->get_status() ); + + $this->assertSame( + array( 'car', 'cat' ), + $response->get_data()['meta']['foo'] + ); + $this->assertSame( array( 'car', 'cat' ), get_post_meta( $revision_id_2, 'foo' ) ); + + $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', $post_id ) ); + $request->set_body_params( + array( + 'title' => 'Revision 3', + 'meta' => array( + 'foo' => null, + ), + ) + ); + $response = rest_get_server()->dispatch( $request ); + $this->assertSame( 200, $response->get_status() ); + + // Get the last revision. + $revisions = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) ); + $revision_id_3 = array_shift( $revisions )->ID; + + // Check that the revision has the correct meta value. + $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d/revisions/%d', $post_id, $revision_id_3 ) ); + $response = rest_get_server()->dispatch( $request ); + $this->assertSame( 200, $response->get_status() ); + + $this->assertSame( + array(), + $response->get_data()['meta']['foo'] + ); + $this->assertSame( array(), get_post_meta( $revision_id_3, 'foo' ) ); + + // Restore Revision 3 and verify the post gets the correct meta value. + wp_restore_post_revision( $revision_id_3 ); + $this->assertSame( array(), get_post_meta( $post_id, 'foo' ) ); + + // Restore Revision 2 and verify the post gets the correct meta value. + wp_restore_post_revision( $revision_id_2 ); + $this->assertSame( array( 'car', 'cat' ), get_post_meta( $post_id, 'foo' ) ); } } From 68f64108cc1d3eb18d38157310e5e7672c370769 Mon Sep 17 00:00:00 2001 From: Timothy Jacobs Date: Thu, 24 Aug 2023 14:02:43 -0400 Subject: [PATCH 059/101] Regenerate fixtures --- tests/qunit/fixtures/wp-api-generated.js | 40 ++++++++++++++++++------ 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/tests/qunit/fixtures/wp-api-generated.js b/tests/qunit/fixtures/wp-api-generated.js index 5c872e1dd1b50..acd8d3e694db9 100644 --- a/tests/qunit/fixtures/wp-api-generated.js +++ b/tests/qunit/fixtures/wp-api-generated.js @@ -11417,7 +11417,9 @@ mockedApiResponse.postRevisions = [ "excerpt": { "rendered": "" }, - "meta": [], + "meta": { + "meta_key": "" + }, "_links": { "parent": [ { @@ -11447,7 +11449,9 @@ mockedApiResponse.postRevisions = [ "excerpt": { "rendered": "

REST API Client Fixture: Post

\n" }, - "meta": [], + "meta": { + "meta_key": "" + }, "_links": { "parent": [ { @@ -11479,7 +11483,9 @@ mockedApiResponse.revision = { "excerpt": { "rendered": "

REST API Client Fixture: Post

\n" }, - "meta": [] + "meta": { + "meta_key": "" + } }; mockedApiResponse.postAutosaves = [ @@ -11504,7 +11510,9 @@ mockedApiResponse.postAutosaves = [ "excerpt": { "rendered": "" }, - "meta": [], + "meta": { + "meta_key": "" + }, "_links": { "parent": [ { @@ -11536,7 +11544,9 @@ mockedApiResponse.autosave = { "excerpt": { "rendered": "" }, - "meta": [] + "meta": { + "meta_key": "" + } }; mockedApiResponse.PagesCollection = [ @@ -11682,7 +11692,9 @@ mockedApiResponse.pageRevisions = [ "excerpt": { "rendered": "" }, - "meta": [], + "meta": { + "meta_key": "" + }, "_links": { "parent": [ { @@ -11712,7 +11724,9 @@ mockedApiResponse.pageRevisions = [ "excerpt": { "rendered": "

REST API Client Fixture: Page

\n" }, - "meta": [], + "meta": { + "meta_key": "" + }, "_links": { "parent": [ { @@ -11744,7 +11758,9 @@ mockedApiResponse.pageRevision = { "excerpt": { "rendered": "

REST API Client Fixture: Page

\n" }, - "meta": [] + "meta": { + "meta_key": "" + } }; mockedApiResponse.pageAutosaves = [ @@ -11769,7 +11785,9 @@ mockedApiResponse.pageAutosaves = [ "excerpt": { "rendered": "" }, - "meta": [], + "meta": { + "meta_key": "" + }, "_links": { "parent": [ { @@ -11801,7 +11819,9 @@ mockedApiResponse.pageAutosave = { "excerpt": { "rendered": "" }, - "meta": [] + "meta": { + "meta_key": "" + } }; mockedApiResponse.MediaCollection = [ From ee35085e73a2f01d3a1aeec855de3606977c0503 Mon Sep 17 00:00:00 2001 From: Timothy Jacobs Date: Thu, 24 Aug 2023 14:09:40 -0400 Subject: [PATCH 060/101] Use post meta fields directly --- .../class-wp-rest-revisions-controller.php | 4 ++-- .../class-wp-rest-revision-meta-fields.php | 19 ------------------- src/wp-settings.php | 1 - 3 files changed, 2 insertions(+), 22 deletions(-) delete mode 100644 src/wp-includes/rest-api/fields/class-wp-rest-revision-meta-fields.php diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php index 670f6e94e3abb..9eec927cfd168 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php @@ -28,7 +28,7 @@ class WP_REST_Revisions_Controller extends WP_REST_Controller { * Instance of a revision meta fields object. * * @since 4.7.0 - * @var WP_REST_Revision_Meta_Fields + * @var WP_REST_Post_Meta_Fields */ protected $meta; @@ -68,7 +68,7 @@ public function __construct( $parent_post_type ) { $this->rest_base = 'revisions'; $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->meta = new WP_REST_Revision_Meta_Fields( $parent_post_type ); + $this->meta = new WP_REST_Post_Meta_Fields( $parent_post_type ); } /** diff --git a/src/wp-includes/rest-api/fields/class-wp-rest-revision-meta-fields.php b/src/wp-includes/rest-api/fields/class-wp-rest-revision-meta-fields.php deleted file mode 100644 index dc32dc5ab46c3..0000000000000 --- a/src/wp-includes/rest-api/fields/class-wp-rest-revision-meta-fields.php +++ /dev/null @@ -1,19 +0,0 @@ - Date: Thu, 24 Aug 2023 14:28:23 -0400 Subject: [PATCH 061/101] Respect the object subtype. --- src/wp-admin/includes/post.php | 4 +++- src/wp-includes/meta.php | 13 ++----------- src/wp-includes/revision.php | 32 ++++++++++++++++++++++++-------- 3 files changed, 29 insertions(+), 20 deletions(-) diff --git a/src/wp-admin/includes/post.php b/src/wp-admin/includes/post.php index 4bc92d1e4cc7c..7d8d0c83df2a0 100644 --- a/src/wp-admin/includes/post.php +++ b/src/wp-admin/includes/post.php @@ -1999,12 +1999,14 @@ function wp_autosave_post_revisioned_meta_fields( $new_autosave ) { $posted_data = isset( $_POST['data']['wp_autosave'] ) ? $_POST['data']['wp_autosave'] : $_POST; // phpcs:enable + $post_type = get_post_type( $new_autosave['post_parent'] ); + /* * Go thru the revisioned meta keys and save them as part of the autosave, if * the meta key is part of the posted data, the meta value is not blank and * the the meta value has changes from the last autosaved value. */ - foreach ( wp_post_revision_meta_keys() as $meta_key ) { + foreach ( wp_post_revision_meta_keys( $post_type ) as $meta_key ) { if ( isset( $posted_data[ $meta_key ] ) && diff --git a/src/wp-includes/meta.php b/src/wp-includes/meta.php index f4ea49aa4ded2..99e6920f9adba 100644 --- a/src/wp-includes/meta.php +++ b/src/wp-includes/meta.php @@ -1393,7 +1393,8 @@ function sanitize_meta( $meta_key, $meta_value, $object_type, $object_subtype = * support for custom fields for registered meta to be accessible via REST. * When registering complex meta values this argument may optionally be an * array with 'schema' or 'prepare_callback' keys instead of a boolean. - * @type bool $revisions_enabled Whether to enable revisions support for this meta_key. + * @type bool $revisions_enabled Whether to enable revisions support for this meta_key. Can only be used when the + * object type is 'post'. * } * @param string|array $deprecated Deprecated. Use `$args` instead. * @return bool True if the meta key was successfully registered in the global array, false if not. @@ -1402,16 +1403,11 @@ function sanitize_meta( $meta_key, $meta_value, $object_type, $object_subtype = */ function register_meta( $object_type, $meta_key, $args, $deprecated = null ) { global $wp_meta_keys; - global $wp_revisioned_meta_keys; if ( ! is_array( $wp_meta_keys ) ) { $wp_meta_keys = array(); } - if ( ! is_array( $wp_revisioned_meta_keys ) ) { - $wp_revisioned_meta_keys = array(); - } - $defaults = array( 'object_subtype' => '', 'type' => 'string', @@ -1467,11 +1463,6 @@ function register_meta( $object_type, $meta_key, $args, $deprecated = null ) { } } - // Store the revisioned meta fields. - if ( isset( $args['revisions_enabled'] ) && $args['revisions_enabled'] && ! in_array( $meta_key, $wp_revisioned_meta_keys ) ) { - array_push( $wp_revisioned_meta_keys, $meta_key ); - } - $object_subtype = ! empty( $args['object_subtype'] ) ? $args['object_subtype'] : ''; // If `auth_callback` is not provided, fall back to `is_protected_meta()`. diff --git a/src/wp-includes/revision.php b/src/wp-includes/revision.php index 2ec4e2bb55dff..d073e008bc657 100644 --- a/src/wp-includes/revision.php +++ b/src/wp-includes/revision.php @@ -409,7 +409,7 @@ function _wp_put_post_revision( $post = null, $autosave = false ) { function wp_save_revisioned_meta_fields( $revision_id, $post_id ) { // Save revisioned meta fields. - foreach ( wp_post_revision_meta_keys() as $meta_key ) { + foreach ( wp_post_revision_meta_keys( get_post_type( $post_id ) ) as $meta_key ) { if ( metadata_exists( 'post', $post_id, $meta_key ) ) { _wp_copy_post_meta( $post_id, $revision_id, $meta_key ); } @@ -523,7 +523,7 @@ function wp_restore_post_revision( $revision, $fields = null ) { function wp_restore_post_revision_meta( $post_id, $revision_id ) { // Restore revisioned meta fields. - foreach ( (array) wp_post_revision_meta_keys() as $meta_key ) { + foreach ( wp_post_revision_meta_keys( get_post_type( $post_id ) ) as $meta_key ) { // Clear any existing meta. delete_post_meta( $post_id, $meta_key ); @@ -557,19 +557,35 @@ function _wp_copy_post_meta( $source_post_id, $target_post_id, $meta_key ) { * * @since 6.4.0 * + * @param string $post_type The post type being revisioned. + * * @return array An array of meta keys to be revisioned. */ -function wp_post_revision_meta_keys() { - global $wp_revisioned_meta_keys; +function wp_post_revision_meta_keys( $post_type ) { + $registered_meta = array_merge( + get_registered_meta_keys( 'post' ), + get_registered_meta_keys( 'post', $post_type ) + ); + + $wp_revisioned_meta_keys = array(); + + foreach ( $registered_meta as $name => $args ) { + if ( $args['revisions_enabled'] ) { + $wp_revisioned_meta_keys[ $name ] = true; + } + } + + $wp_revisioned_meta_keys = array_keys( $wp_revisioned_meta_keys ); /** * Filter the list of post meta keys to be revisioned. * * @since 6.4.0 * - * @param array $keys An array of meta fields to be revisioned. + * @param array $keys An array of meta fields to be revisioned. + * @param string $post_type The post type being revisioned. */ - return apply_filters( 'wp_post_revision_meta_keys', $wp_revisioned_meta_keys ); + return apply_filters( 'wp_post_revision_meta_keys', $wp_revisioned_meta_keys, $post_type ); } /** @@ -582,7 +598,7 @@ function wp_post_revision_meta_keys() { * @since 6.4.0 */ function wp_check_revisioned_meta_fields_have_changed( $post_has_changed, WP_Post $last_revision, WP_Post $post ) { - foreach ( wp_post_revision_meta_keys() as $meta_key ) { + foreach ( wp_post_revision_meta_keys( $post->post_type ) as $meta_key ) { if ( get_post_meta( $post->ID, $meta_key ) !== get_post_meta( $last_revision->ID, $meta_key ) ) { $post_has_changed = true; break; @@ -1097,7 +1113,7 @@ function _wp_preview_meta_filter( $value, $object_id, $meta_key, $single ) { if ( empty( $post ) || $post->ID !== $object_id || - ! in_array( $meta_key, wp_post_revision_meta_keys(), true ) || + ! in_array( $meta_key, wp_post_revision_meta_keys( $post->post_type ), true ) || 'revision' === $post->post_type ) { return $value; From c27258a53f103652ace07bb0de8795d62a25eaef Mon Sep 17 00:00:00 2001 From: Timothy Jacobs Date: Thu, 24 Aug 2023 14:40:47 -0400 Subject: [PATCH 062/101] Always pass a post array --- src/wp-admin/includes/post.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wp-admin/includes/post.php b/src/wp-admin/includes/post.php index 7d8d0c83df2a0..177d9a02a7817 100644 --- a/src/wp-admin/includes/post.php +++ b/src/wp-admin/includes/post.php @@ -1972,7 +1972,7 @@ function wp_create_post_autosave( $post_data ) { $revision = _wp_put_post_revision( $post_data, true ); // Update the revisioned meta data as well. - wp_autosave_post_revisioned_meta_fields( $revision ); + wp_autosave_post_revisioned_meta_fields( get_post( $revision, ARRAY_A ) ); return $revision; } @@ -1984,7 +1984,7 @@ function wp_create_post_autosave( $post_data ) { * * @since 6.4.0 * - * @param Post object $new_autosave The new post being autosaved. + * @param array $new_autosave The new post data being autosaved. */ function wp_autosave_post_revisioned_meta_fields( $new_autosave ) { /* From 20874e3d3c47e03bed0eb6850d6517ec593a7e74 Mon Sep 17 00:00:00 2001 From: Timothy Jacobs Date: Thu, 24 Aug 2023 15:05:26 -0400 Subject: [PATCH 063/101] Remove manually revisioning meta. This happens automatically now. --- .../endpoints/class-wp-rest-posts-controller.php | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php index 31f4d43408245..356c321129894 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php @@ -743,13 +743,6 @@ public function create_item( $request ) { } } - // Update any revisioned meta. - $revisions = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) ); - if ( ! empty( $revisions ) ) { - $revision = array_shift( $revisions ); - wp_save_revisioned_meta_fields( $revision->ID, $post_id ); - } - $post = get_post( $post_id ); $fields_update = $this->update_additional_fields_for_object( $post, $request ); @@ -935,12 +928,6 @@ public function update_item( $request ) { return $meta_update; } } - // Update any revisioned meta. - $revisions = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) ); - if ( ! empty( $revisions ) ) { - $revision = array_shift( $revisions ); - wp_save_revisioned_meta_fields( $revision->ID, $post_id ); - } $post = get_post( $post_id ); $fields_update = $this->update_additional_fields_for_object( $post, $request ); From 68a419b3d1dd199f132710e5e004fb18330f550c Mon Sep 17 00:00:00 2001 From: Timothy Jacobs Date: Thu, 24 Aug 2023 15:28:07 -0400 Subject: [PATCH 064/101] Correct has meta key assertion --- tests/phpunit/tests/post/metaRevisions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/phpunit/tests/post/metaRevisions.php b/tests/phpunit/tests/post/metaRevisions.php index c132f01e40f79..1d49ccb0fd579 100644 --- a/tests/phpunit/tests/post/metaRevisions.php +++ b/tests/phpunit/tests/post/metaRevisions.php @@ -556,7 +556,7 @@ public function data_register_post_meta_supports_revisions() { * @param string $meta_key The meta key to check for. */ protected function assertPostHasMetaKey( $post_id, $meta_key ) { - $this->assertEquals( $expect, $stored_array ); + $this->assertArrayHasKey( $meta_key, get_metadata( 'post', $post_id ) ); } /** From 3ee633f4889c5d1d52d50ef884b0e25ede9b9a9a Mon Sep 17 00:00:00 2001 From: Timothy Jacobs Date: Thu, 24 Aug 2023 16:21:30 -0400 Subject: [PATCH 065/101] Add clarifying comment and some more fail safes --- src/wp-includes/revision.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/wp-includes/revision.php b/src/wp-includes/revision.php index d073e008bc657..609ce4b532cc7 100644 --- a/src/wp-includes/revision.php +++ b/src/wp-includes/revision.php @@ -132,7 +132,8 @@ function wp_save_post_revision( $post_id ) { return; } - if ( doing_action( 'post_updated' ) ) { + // Prevent saving post revisions if revisions should be saved on wp_after_insert_post. + if ( doing_action( 'post_updated' ) && has_action( 'wp_after_insert_post', 'wp_save_post_revision_on_insert' ) ) { return; } From d04574a802acc8ce7ac33acc2fabdf7d2ed35c06 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 29 Aug 2023 09:20:14 -0600 Subject: [PATCH 066/101] Test cleanup, add page and cpt tests --- tests/phpunit/tests/post/metaRevisions.php | 80 ++++++++++- .../tests/rest-api/rest-post-meta-fields.php | 125 +++++++++++++++++- 2 files changed, 199 insertions(+), 6 deletions(-) diff --git a/tests/phpunit/tests/post/metaRevisions.php b/tests/phpunit/tests/post/metaRevisions.php index 1d49ccb0fd579..596ff4b094fe3 100644 --- a/tests/phpunit/tests/post/metaRevisions.php +++ b/tests/phpunit/tests/post/metaRevisions.php @@ -73,7 +73,7 @@ public function test_revisions_stores_meta_values_with_slashes( $passed, $expect // Restore the previous revision. $revisions = (array) wp_get_post_revisions( $post_id ); - // Go back two to load the previous revision. + // Go back to load the previous revision. array_shift( $revisions ); $last_revision = array_shift( $revisions ); @@ -81,7 +81,6 @@ public function test_revisions_stores_meta_values_with_slashes( $passed, $expect wp_restore_post_revision( $last_revision->ID ); $this->assertEquals( $expected, get_post_meta( $post_id, 'meta_revision_test', true ) ); - remove_filter( 'wp_post_revision_meta_keys', array( $this, 'add_revisioned_keys' ) ); } /** @@ -568,4 +567,81 @@ protected function assertPostHasMetaKey( $post_id, $meta_key ) { protected function assertPostNotHasMetaKey( $post_id, $meta_key ) { $this->assertArrayNotHasKey( $meta_key, get_metadata( 'post', $post_id ) ); } + + /** + * Test post meta revisioning with a custom post type, as well as the "page" post type. + * + * @dataProvider page_post_type_data_provider + */ + public function test_revisions_stores_meta_values_page_and_cpt( $passed, $expected, $post_type, $supports_revisions = false ) { + + // If the post type doesn't exist, create it, potentially supporting revisions. + if ( ! post_type_exists( $post_type ) ) { + register_post_type( + $post_type, + array( + 'public' => true, + 'supports' => $supports_revisions ? array( 'revisions' ) : array(), + ) + ); + } + + // Create a test post. + $page_id = $this->factory->post->create( + array( + 'post_type' => $post_type, + 'post_content' => 'some initial content', + ) + ); + + // Add the revisioning filter. + add_filter( 'wp_post_revision_meta_keys', array( $this, 'add_revisioned_keys' ) ); + + // Test revisioning. + update_post_meta( $page_id, 'meta_revision_test', wp_slash( $passed ) ); + + // Update the post, storing a revision. + wp_update_post( + array( + 'post_content' => 'some more content', + 'ID' => $page_id, + ) + ); + + // Retrieve the created revision. + $revisions = (array) wp_get_post_revisions( $page_id ); + + if ( $expected ) { + // Go back to load the previous revision. + $last_revision = array_shift( $revisions ); + wp_restore_post_revision( $last_revision->ID ); + $this->assertEquals( $expected, get_post_meta( $page_id, 'meta_revision_test', true ) ); + } else { + $this->assertEmpty( $revisions ); + } + } + + /** + * Provide data for the page post type tests. + */ + public function page_post_type_data_provider() { + return array( + array( + 'Test string', + 'Test string', + 'page', + ), + array( + 'Test string', + false, + 'custom_type', + ), + array( + 'Test string', + 'Test string', + 'custom_type', + true, + ), + ); + } } diff --git a/tests/phpunit/tests/rest-api/rest-post-meta-fields.php b/tests/phpunit/tests/rest-api/rest-post-meta-fields.php index c84c4269c7ef4..781d1adc19c8c 100644 --- a/tests/phpunit/tests/rest-api/rest-post-meta-fields.php +++ b/tests/phpunit/tests/rest-api/rest-post-meta-fields.php @@ -17,7 +17,7 @@ public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) { 'cpt', array( 'show_in_rest' => true, - 'supports' => array( 'custom-fields' ), + 'supports' => array( 'custom-fields', 'revisions' ), ) ); @@ -157,7 +157,7 @@ public function set_up() { 'cpt', array( 'show_in_rest' => true, - 'supports' => array( 'custom-fields' ), + 'supports' => array( 'custom-fields', 'revisions' ), ) ); @@ -1376,8 +1376,6 @@ public function data_set_subtype_meta_value() { * @dataProvider data_update_value_return_success_with_same_value */ public function test_update_value_return_success_with_same_value( $meta_key, $meta_value ) { - add_post_meta( self::$post_id, $meta_key, $meta_value ); - $this->grant_write_permission(); $data = array( @@ -1392,6 +1390,13 @@ public function test_update_value_return_success_with_same_value( $meta_key, $me $response = rest_get_server()->dispatch( $request ); $this->assertSame( 200, $response->get_status() ); + + // Verify the returned meta value is correct. + $data = $response->get_data(); + $this->assertArrayHasKey( 'meta', $data ); + $this->assertArrayHasKey( $meta_key, $data['meta'] ); + $this->assertSame( $meta_value, $data['meta'][ $meta_key ] ); + } public function data_update_value_return_success_with_same_value() { @@ -3355,4 +3360,116 @@ public function test_revisioned_multiple_post_meta_with_posts_endpoint() { wp_restore_post_revision( $revision_id_2 ); $this->assertSame( array( 'car', 'cat' ), get_post_meta( $post_id, 'foo' ) ); } + + /** + * Test post meta revisions with a custom post type and the page post type. + * + * @group revision + * @dataProvider test_revisioned_single_post_meta_with_posts_endpoint_page_and_cpt_data_provider + */ + public function test_revisioned_single_post_meta_with_posts_endpoint_page_and_cpt( $passed, $expected, $post_type ) { + + $this->grant_write_permission(); + + // Create the custom meta. + register_post_meta( + $post_type, + 'foo', + array( + 'show_in_rest' => true, + 'revisions_enabled' => true, + 'single' => true, + 'type' => 'string', + ) + ); + + // Set up a new post. + $post_id = $this->factory->post->create( + array( + 'post_content' => 'initial content', + 'post_type' => $post_type, + 'meta_input' => array( + 'foo' => 'foo', + ), + ) + ); + + $plural_mapping = array( + 'page' => 'pages', + 'cpt' => 'cpt', + ); + $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/%s', $plural_mapping[ $post_type ] ) ); + + $response = rest_get_server()->dispatch( $request ); + + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/%s/%d', $plural_mapping[ $post_type ], $post_id ) ); + $request->set_body_params( + array( + 'title' => 'Revision 1', + 'meta' => array( + 'foo' => $passed, + ), + ), + ); + + $response = rest_get_server()->dispatch( $request ); + $this->assertSame( 200, $response->get_status() ); + + // Update the post. + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/%s/%d', $plural_mapping[ $post_type ], $post_id ) ); + $request->set_body_params( + array( + 'title' => 'Revision 1 update', + ) + ); + $response = rest_get_server()->dispatch( $request ); + $this->assertSame( 200, $response->get_status() ); + + // Get the last revision. + $revisions = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) ); + + $revision_id_1 = array_shift( $revisions )->ID; + + // Check that the revision has the correct meta value. + $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/%s/%d/revisions/%d', $plural_mapping[ $post_type ], $post_id, $revision_id_1 ) ); + $response = rest_get_server()->dispatch( $request ); + $this->assertSame( 200, $response->get_status() ); + + $this->assertSame( + $passed, + $response->get_data()['meta']['foo'] + ); + + $this->assertSame( + array( $passed ), + get_post_meta( $revision_id_1, 'foo' ) + ); + + unregister_post_meta( $post_type, 'foo' ); + wp_delete_post( $post_id, true ); + } + + /** + * Provide data for the meta revision checks. + */ + public function test_revisioned_single_post_meta_with_posts_endpoint_page_and_cpt_data_provider() { + return array( + array( + 'Test string', + 'Test string', + 'cpt', + ), + array( + 'Test string', + 'Test string', + 'page', + ), + array( + 'Test string', + false, + 'cpt', + ), + + ); + } } From 3a8f6fc42bfff8554b0205d2883668f67a80a3c5 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 29 Aug 2023 16:20:33 -0600 Subject: [PATCH 067/101] omyac --- tests/phpunit/tests/rest-api/rest-post-meta-fields.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/phpunit/tests/rest-api/rest-post-meta-fields.php b/tests/phpunit/tests/rest-api/rest-post-meta-fields.php index 781d1adc19c8c..b32ebaab80810 100644 --- a/tests/phpunit/tests/rest-api/rest-post-meta-fields.php +++ b/tests/phpunit/tests/rest-api/rest-post-meta-fields.php @@ -3409,7 +3409,7 @@ public function test_revisioned_single_post_meta_with_posts_endpoint_page_and_cp 'meta' => array( 'foo' => $passed, ), - ), + ) ); $response = rest_get_server()->dispatch( $request ); From 225a91bf5404c62d3d336fa9ec0b36089dd21510 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Fri, 8 Sep 2023 13:15:49 -0600 Subject: [PATCH 068/101] Clean up default filters after trunk merge --- src/wp-includes/default-filters.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/wp-includes/default-filters.php b/src/wp-includes/default-filters.php index 6400eb452e04c..f0cbaea7da608 100644 --- a/src/wp-includes/default-filters.php +++ b/src/wp-includes/default-filters.php @@ -722,12 +722,13 @@ // CPT wp_block custom postmeta field. add_action( 'init', 'wp_create_initial_post_meta' ); -// Including revisioned meta when considering whether a post revision has changed. -// Font management. +// Include revisioned meta when considering whether a post revision has changed. add_filter( 'wp_save_post_revision_post_has_changed', 'wp_check_revisioned_meta_fields_have_changed', 10, 3 ); // Save revisioned post meta immediately after a revision is saved add_action( '_wp_put_post_revision', 'wp_save_revisioned_meta_fields', 10, 2 ); + +// Font management. add_action( 'wp_head', 'wp_print_font_faces', 50 ); unset( $filter, $action ); From c2482fda786f508e261b641b0bb8d3a6d273fc2e Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Fri, 8 Sep 2023 22:13:12 -0600 Subject: [PATCH 069/101] Avoid duplicate revisions since hook change --- src/wp-includes/revision.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/revision.php b/src/wp-includes/revision.php index 609ce4b532cc7..616397a85cde2 100644 --- a/src/wp-includes/revision.php +++ b/src/wp-includes/revision.php @@ -377,7 +377,7 @@ function _wp_put_post_revision( $post = null, $autosave = false ) { $post = _wp_post_revision_data( $post, $autosave ); $post = wp_slash( $post ); // Since data is from DB. - $revision_id = wp_insert_post( $post, true ); + $revision_id = wp_insert_post( $post, true, false ); if ( is_wp_error( $revision_id ) ) { return $revision_id; } From 5c48c7ab9d03cf0f5b6bd26422a8948a6c3039ca Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Sun, 10 Sep 2023 22:22:30 -0600 Subject: [PATCH 070/101] Restore hooks on wp_insert_post for revision - issue was plugin related --- src/wp-includes/revision.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/revision.php b/src/wp-includes/revision.php index 616397a85cde2..609ce4b532cc7 100644 --- a/src/wp-includes/revision.php +++ b/src/wp-includes/revision.php @@ -377,7 +377,7 @@ function _wp_put_post_revision( $post = null, $autosave = false ) { $post = _wp_post_revision_data( $post, $autosave ); $post = wp_slash( $post ); // Since data is from DB. - $revision_id = wp_insert_post( $post, true, false ); + $revision_id = wp_insert_post( $post, true ); if ( is_wp_error( $revision_id ) ) { return $revision_id; } From e211ca166ca2204789465b2235bb951cfe2be015 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 12 Sep 2023 15:22:32 -0600 Subject: [PATCH 071/101] Ensure meta data is revisioned with autosaves --- .../class-wp-rest-autosaves-controller.php | 36 +++++++++++++++---- 1 file changed, 30 insertions(+), 6 deletions(-) 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 689e86355ca75..5279b6613f1d4 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 @@ -234,8 +234,8 @@ public function create_item( $request ) { */ $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, $request->get_param( 'meta' ) ); } if ( is_wp_error( $autosave_id ) ) { @@ -348,11 +348,13 @@ public function get_item_schema() { * 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, $meta = array() ) { $post_id = (int) $post_data['ID']; $post = get_post( $post_id ); @@ -372,6 +374,18 @@ public function create_post_autosave( $post_data ) { } } + // Check if meta values have changed. + $revisioned_meda_keys = wp_post_revision_meta_keys( $post->post_type ); + foreach ( $revisioned_meda_keys as $meta_key ) { + $old_meta = get_post_meta( $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. @@ -390,11 +404,21 @@ public function create_post_autosave( $post_data ) { 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 ); } - // Create the new autosave as a special post revision. - return _wp_put_post_revision( $post_data, true ); + // Attached any passed meta values that have revisioning enabled. + if ( $revision_id ) { + foreach ( $revisioned_meda_keys as $meta_key ) { + if ( isset( $meta[ $meta_key ] ) ) { + update_metadata( 'post', $revision_id, $meta_key, $meta[ $meta_key ] ); + } + } + } + return $revision_id; } /** From 7247e93a4588310fc41b01aa65757fa55a31dd01 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 12 Sep 2023 15:32:04 -0600 Subject: [PATCH 072/101] Cleanup, only check meta when passed --- .../class-wp-rest-autosaves-controller.php | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) 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 5279b6613f1d4..aca27e4e1fc92 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 @@ -375,14 +375,16 @@ public function create_post_autosave( $post_data, $meta = array() ) { } // Check if meta values have changed. - $revisioned_meda_keys = wp_post_revision_meta_keys( $post->post_type ); - foreach ( $revisioned_meda_keys as $meta_key ) { - $old_meta = get_post_meta( $post_id, $meta_key, true ); - $new_meta = isset( $meta[ $meta_key ] ) ? $meta[ $meta_key ] : ''; - - if ( $new_meta !== $old_meta ) { - $autosave_is_different = true; - break; + if ( ! empty( $meta ) ) { + $revisioned_meta_keys = wp_post_revision_meta_keys( $post->post_type ); + foreach ( $revisioned_meta_keys as $meta_key ) { + $old_meta = get_post_meta( $post_id, $meta_key, true ); + $new_meta = isset( $meta[ $meta_key ] ) ? $meta[ $meta_key ] : ''; + + if ( $new_meta !== $old_meta ) { + $autosave_is_different = true; + break; + } } } @@ -410,9 +412,9 @@ public function create_post_autosave( $post_data, $meta = array() ) { $revision_id = _wp_put_post_revision( $post_data, true ); } - // Attached any passed meta values that have revisioning enabled. - if ( $revision_id ) { - foreach ( $revisioned_meda_keys as $meta_key ) { + // Attached any passed meta values that have revisions enabled. + if ( ! empty( $meta ) && $revision_id ) { + foreach ( $revisioned_meta_keys as $meta_key ) { if ( isset( $meta[ $meta_key ] ) ) { update_metadata( 'post', $revision_id, $meta_key, $meta[ $meta_key ] ); } From 750dcdc81cbcc1de7085d776516e87fef9c70938 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Mon, 25 Sep 2023 08:32:46 -0600 Subject: [PATCH 073/101] Hook wp_autosave_post_revisioned_meta_fields on wp_creating_autosave Also, add a second triggering of `wp_creating_autosave` and add a parameter indicating if this is an update or a new autosave revision. --- src/wp-admin/includes/post.php | 14 ++++++++++---- src/wp-includes/default-filters.php | 3 +++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/wp-admin/includes/post.php b/src/wp-admin/includes/post.php index 8ec2c322ee8e4..2f99381258ce9 100644 --- a/src/wp-admin/includes/post.php +++ b/src/wp-admin/includes/post.php @@ -1957,11 +1957,12 @@ function wp_create_post_autosave( $post_data ) { * Fires before an autosave is stored. * * @since 4.1.0 + * @since 6.4.0 The `$is_update` parameter was added to indicate if the autosave is being updated or was newly created. * * @param array $new_autosave Post array - the autosave that is about to be saved. + * @param bool $is_update Whether this is an existing autosave. */ - do_action( 'wp_creating_autosave', $new_autosave ); - wp_autosave_post_revisioned_meta_fields( $new_autosave ); + do_action( 'wp_creating_autosave', $new_autosave, true ); return wp_update_post( $new_autosave ); } @@ -1971,8 +1972,13 @@ function wp_create_post_autosave( $post_data ) { // Otherwise create the new autosave as a special post revision. $revision = _wp_put_post_revision( $post_data, true ); - // Update the revisioned meta data as well. - wp_autosave_post_revisioned_meta_fields( get_post( $revision, ARRAY_A ) ); + /** + * Fires before an autosave is stored. + * + * This filter is documented in `src/wp-admin/includes/post.php`. + */ + do_action( 'wp_creating_autosave', get_post( $revision, ARRAY_A ), false ); + return $revision; } diff --git a/src/wp-includes/default-filters.php b/src/wp-includes/default-filters.php index 541410bdce280..77c9a3db3aa16 100644 --- a/src/wp-includes/default-filters.php +++ b/src/wp-includes/default-filters.php @@ -723,6 +723,9 @@ // Save revisioned post meta immediately after a revision is saved add_action( '_wp_put_post_revision', 'wp_save_revisioned_meta_fields', 10, 2 ); +// Include revisioned meta when creating or updating an autosave revision. +add_action( 'wp_creating_autosave', 'wp_autosave_post_revisioned_meta_fields' ); + // Font management. add_action( 'wp_head', 'wp_print_font_faces', 50 ); From 0b88e507e8850af4e699f89e9bc19240c9d96339 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Mon, 25 Sep 2023 08:44:19 -0600 Subject: [PATCH 074/101] Improve error checking --- src/wp-admin/includes/post.php | 15 +++++++++------ .../class-wp-rest-autosaves-controller.php | 2 +- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/wp-admin/includes/post.php b/src/wp-admin/includes/post.php index 2f99381258ce9..91f4b8be98a36 100644 --- a/src/wp-admin/includes/post.php +++ b/src/wp-admin/includes/post.php @@ -1972,12 +1972,15 @@ function wp_create_post_autosave( $post_data ) { // Otherwise create the new autosave as a special post revision. $revision = _wp_put_post_revision( $post_data, true ); - /** - * Fires before an autosave is stored. - * - * This filter is documented in `src/wp-admin/includes/post.php`. - */ - do_action( 'wp_creating_autosave', get_post( $revision, ARRAY_A ), false ); + if ( ! is_wp_error( $revision ) ) { + + /** + * Fires before an autosave is stored. + * + * This filter is documented in `src/wp-admin/includes/post.php`. + */ + do_action( 'wp_creating_autosave', get_post( $revision, ARRAY_A ), false ); + } return $revision; } 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 5ff367320aa9c..9997c95d30b13 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 @@ -413,7 +413,7 @@ public function create_post_autosave( $post_data, $meta = array() ) { } // Attached any passed meta values that have revisions enabled. - if ( ! empty( $meta ) && $revision_id ) { + if ( ! empty( $meta ) && $revision_id && ! is_wp_error( $revision_id ) ) { foreach ( $revisioned_meta_keys as $meta_key ) { if ( isset( $meta[ $meta_key ] ) ) { update_metadata( 'post', $revision_id, $meta_key, $meta[ $meta_key ] ); From 2c02dbc371900689da97a28b32c1cb0330c2140a Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Mon, 25 Sep 2023 08:45:14 -0600 Subject: [PATCH 075/101] Update src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php Co-authored-by: Jonny Harris --- .../rest-api/endpoints/class-wp-rest-revisions-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php index a1d10d2ae077b..5501c190c13ee 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php @@ -27,7 +27,7 @@ class WP_REST_Revisions_Controller extends WP_REST_Controller { /** * Instance of a revision meta fields object. * - * @since 4.7.0 + * @since 6.4.0 * @var WP_REST_Post_Meta_Fields */ protected $meta; From b5ba2d39831abb137e637396014dbdf77492943c Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Mon, 25 Sep 2023 08:45:47 -0600 Subject: [PATCH 076/101] Update src/wp-includes/revision.php Co-authored-by: Jonny Harris --- src/wp-includes/revision.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/revision.php b/src/wp-includes/revision.php index 609ce4b532cc7..50e075ea703e8 100644 --- a/src/wp-includes/revision.php +++ b/src/wp-includes/revision.php @@ -1121,7 +1121,7 @@ function _wp_preview_meta_filter( $value, $object_id, $meta_key, $single ) { } $preview = wp_get_post_autosave( $post->ID ); - if ( ! is_object( $preview ) ) { + if ( false === $preview ) { return $value; } From 95508d959d69e6be174c922c8308dac51077b7ff Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Mon, 25 Sep 2023 08:48:53 -0600 Subject: [PATCH 077/101] Hook revision restore on action --- src/wp-includes/default-filters.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/wp-includes/default-filters.php b/src/wp-includes/default-filters.php index 77c9a3db3aa16..0c2ecb262ad1e 100644 --- a/src/wp-includes/default-filters.php +++ b/src/wp-includes/default-filters.php @@ -726,6 +726,9 @@ // Include revisioned meta when creating or updating an autosave revision. add_action( 'wp_creating_autosave', 'wp_autosave_post_revisioned_meta_fields' ); +// When restoring revisions, also restore revisioned meta. +add_action( 'wp_restore_post_revision', 'wp_restore_post_revision_meta', 10, 2 ); + // Font management. add_action( 'wp_head', 'wp_print_font_faces', 50 ); From b0b880be91d935c27353631537d439a1406e7d35 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Mon, 25 Sep 2023 11:48:13 -0600 Subject: [PATCH 078/101] Test revisioning of meta with a default value --- tests/phpunit/tests/post/metaRevisions.php | 70 ++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/tests/phpunit/tests/post/metaRevisions.php b/tests/phpunit/tests/post/metaRevisions.php index 596ff4b094fe3..788341c2c158e 100644 --- a/tests/phpunit/tests/post/metaRevisions.php +++ b/tests/phpunit/tests/post/metaRevisions.php @@ -492,6 +492,76 @@ public function blank_meta_is_revisioned() { $this->assertEquals( '', $stored_data[0] ); } + /** + * Test revisioning of meta with a default value. + */ + public function test_revisionining_of_meta_with_default_value() { + + // Add a meta field to revision that includes a default value. + register_post_meta( + 'post', + 'meta_revision_test', + array( + 'single' => true, + 'default' => 'default value', + 'revisions_enabled' => true, + ) + ); + + // Set up a new post. + $post_id = $this->factory->post->create( + array( + 'post_content' => 'initial content', + 'meta_input' => array( + 'meta_revision_test' => 'foo', + ), + ) + ); + + // Set the test meta to an empty string. + update_post_meta( $post_id, 'meta_revision_test', '' ); + + // Update to save. + wp_update_post( array( 'ID' => $post_id ) ); + + // Check that the meta is blank. + $stored_data = get_post_meta( $post_id, 'meta_revision_test', true ); + $this->assertEquals( '', $stored_data ); + + // Also verify that the latest revision has blank stored for the meta. + $revisions = wp_get_post_revisions( $post_id ); + $last_revision = array_shift( $revisions ); + $stored_data = get_post_meta( $last_revision->ID, 'meta_revision_test', true ); + $this->assertEquals( '', $stored_data ); + + // Delete the meta. + delete_post_meta( $post_id, 'meta_revision_test' ); + + // Update to save. + wp_update_post( array( 'ID' => $post_id, 'post_content' => 'content update 1' ) ); + + // Check that the default meta value is returned. + $this->assertEquals( 'default value', get_post_meta( $post_id, 'meta_revision_test', true ) ); + + // Also verify that the latest revision has the default value returned for the meta. + $revisions = wp_get_post_revisions( $post_id ); + $last_revision = array_shift( $revisions ); + + // No ,eta data should be stored in the revision. + $this->assertEquals( array(), get_post_meta( $last_revision->ID ) ); + + // Set the test meta again. + update_post_meta( $post_id, 'meta_revision_test', 'test' ); + + // Update to save. + wp_update_post( array( 'ID' => $post_id ) ); + + // Now restore the previous revision. + wp_restore_post_revision( $last_revision->ID ); + + // Verify the default meta value is still returned. + $this->assertEquals( 'default value', get_post_meta( $post_id, 'meta_revision_test', true ) ); + } /** * @dataProvider data_register_post_meta_supports_revisions From f04a319470366c8cdab79224e186239029f01faa Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Mon, 25 Sep 2023 11:52:10 -0600 Subject: [PATCH 079/101] phpcs --- tests/phpunit/tests/post/metaRevisions.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/phpunit/tests/post/metaRevisions.php b/tests/phpunit/tests/post/metaRevisions.php index 788341c2c158e..4fc1252d2a0bc 100644 --- a/tests/phpunit/tests/post/metaRevisions.php +++ b/tests/phpunit/tests/post/metaRevisions.php @@ -538,7 +538,12 @@ public function test_revisionining_of_meta_with_default_value() { delete_post_meta( $post_id, 'meta_revision_test' ); // Update to save. - wp_update_post( array( 'ID' => $post_id, 'post_content' => 'content update 1' ) ); + wp_update_post( + array( + 'ID' => $post_id, + 'post_content' => 'content update 1', + ) + ); // Check that the default meta value is returned. $this->assertEquals( 'default value', get_post_meta( $post_id, 'meta_revision_test', true ) ); From e2318868e1733aa2001cb595c6c019f5a2472685 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Mon, 25 Sep 2023 12:02:21 -0600 Subject: [PATCH 080/101] phpcbf --- tests/phpunit/tests/post/metaRevisions.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/phpunit/tests/post/metaRevisions.php b/tests/phpunit/tests/post/metaRevisions.php index 4fc1252d2a0bc..74442b53c82ad 100644 --- a/tests/phpunit/tests/post/metaRevisions.php +++ b/tests/phpunit/tests/post/metaRevisions.php @@ -529,9 +529,9 @@ public function test_revisionining_of_meta_with_default_value() { $this->assertEquals( '', $stored_data ); // Also verify that the latest revision has blank stored for the meta. - $revisions = wp_get_post_revisions( $post_id ); - $last_revision = array_shift( $revisions ); - $stored_data = get_post_meta( $last_revision->ID, 'meta_revision_test', true ); + $revisions = wp_get_post_revisions( $post_id ); + $last_revision = array_shift( $revisions ); + $stored_data = get_post_meta( $last_revision->ID, 'meta_revision_test', true ); $this->assertEquals( '', $stored_data ); // Delete the meta. @@ -540,7 +540,7 @@ public function test_revisionining_of_meta_with_default_value() { // Update to save. wp_update_post( array( - 'ID' => $post_id, + 'ID' => $post_id, 'post_content' => 'content update 1', ) ); @@ -549,8 +549,8 @@ public function test_revisionining_of_meta_with_default_value() { $this->assertEquals( 'default value', get_post_meta( $post_id, 'meta_revision_test', true ) ); // Also verify that the latest revision has the default value returned for the meta. - $revisions = wp_get_post_revisions( $post_id ); - $last_revision = array_shift( $revisions ); + $revisions = wp_get_post_revisions( $post_id ); + $last_revision = array_shift( $revisions ); // No ,eta data should be stored in the revision. $this->assertEquals( array(), get_post_meta( $last_revision->ID ) ); From e1950b0debef4eeb629f932a9dbe0dbe81cd6a16 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Mon, 25 Sep 2023 12:05:56 -0600 Subject: [PATCH 081/101] Autosaves controller - get_metadata_raw is used to avoid retrieving the default value --- .../rest-api/endpoints/class-wp-rest-autosaves-controller.php | 3 ++- 1 file changed, 2 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 9997c95d30b13..86cf0f7b4a468 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 @@ -378,7 +378,8 @@ public function create_post_autosave( $post_data, $meta = array() ) { if ( ! empty( $meta ) ) { $revisioned_meta_keys = wp_post_revision_meta_keys( $post->post_type ); foreach ( $revisioned_meta_keys as $meta_key ) { - $old_meta = get_post_meta( $post_id, $meta_key, true ); + // 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 ) { From 6f6e3ce6de571f2bcb4321e3c4964c5e59098bc8 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Mon, 25 Sep 2023 12:08:14 -0600 Subject: [PATCH 082/101] Update src/wp-includes/revision.php Correct doc block order Co-authored-by: Tonya Mork --- src/wp-includes/revision.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wp-includes/revision.php b/src/wp-includes/revision.php index 50e075ea703e8..47a43be327da3 100644 --- a/src/wp-includes/revision.php +++ b/src/wp-includes/revision.php @@ -402,10 +402,10 @@ function _wp_put_post_revision( $post = null, $autosave = false ) { /** * Save the revisioned meta fields. * + * @since 6.4.0 + * * @param int $revision_id The ID of the revision to save the meta to. * @param int $post_id The ID of the post the revision is associated with. - * - * @since 6.4.0 */ function wp_save_revisioned_meta_fields( $revision_id, $post_id ) { From b77bf22b8ba812c5640450284f631658aad7c18e Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Mon, 25 Sep 2023 12:13:54 -0600 Subject: [PATCH 083/101] Check post_type available in wp_save_revisioned_meta_fields --- src/wp-includes/revision.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/wp-includes/revision.php b/src/wp-includes/revision.php index 47a43be327da3..7c95733c47152 100644 --- a/src/wp-includes/revision.php +++ b/src/wp-includes/revision.php @@ -408,9 +408,12 @@ function _wp_put_post_revision( $post = null, $autosave = false ) { * @param int $post_id The ID of the post the revision is associated with. */ function wp_save_revisioned_meta_fields( $revision_id, $post_id ) { + $post_type = get_post_type( $post_id ); + if ( ! $post_type ) { + return; + } - // Save revisioned meta fields. - foreach ( wp_post_revision_meta_keys( get_post_type( $post_id ) ) as $meta_key ) { + foreach ( wp_post_revision_meta_keys( $post_type ) as $meta_key ) { if ( metadata_exists( 'post', $post_id, $meta_key ) ) { _wp_copy_post_meta( $post_id, $revision_id, $meta_key ); } From b6e221a16b30f65031ce6b7ebedebe2a9c4d2257 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Mon, 25 Sep 2023 12:16:00 -0600 Subject: [PATCH 084/101] Ensure post_type available in wp_restore_post_revision_meta --- src/wp-includes/revision.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/wp-includes/revision.php b/src/wp-includes/revision.php index 7c95733c47152..72f528572c1fd 100644 --- a/src/wp-includes/revision.php +++ b/src/wp-includes/revision.php @@ -525,9 +525,13 @@ function wp_restore_post_revision( $revision, $fields = null ) { * @since 6.4.0 */ function wp_restore_post_revision_meta( $post_id, $revision_id ) { + $post_type = get_post_type( $post_id ); + if ( ! $post_type ) { + return; + } // Restore revisioned meta fields. - foreach ( wp_post_revision_meta_keys( get_post_type( $post_id ) ) as $meta_key ) { + foreach ( wp_post_revision_meta_keys( $post_type as $meta_key ) { // Clear any existing meta. delete_post_meta( $post_id, $meta_key ); From 40febf25db4f09699a702b5b630319edf9bdc861 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Mon, 25 Sep 2023 12:24:54 -0600 Subject: [PATCH 085/101] typo - missing closing parenthesis --- src/wp-includes/revision.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/revision.php b/src/wp-includes/revision.php index 72f528572c1fd..3b3afed75c950 100644 --- a/src/wp-includes/revision.php +++ b/src/wp-includes/revision.php @@ -531,7 +531,7 @@ function wp_restore_post_revision_meta( $post_id, $revision_id ) { } // Restore revisioned meta fields. - foreach ( wp_post_revision_meta_keys( $post_type as $meta_key ) { + foreach ( wp_post_revision_meta_keys( $post_type ) as $meta_key ) { // Clear any existing meta. delete_post_meta( $post_id, $meta_key ); From 69c6107c1aea112f4722e81fe7175cd54275be4e Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Mon, 25 Sep 2023 12:37:23 -0600 Subject: [PATCH 086/101] phpcbf --- src/wp-includes/revision.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/revision.php b/src/wp-includes/revision.php index 3b3afed75c950..d17c79b1b6c67 100644 --- a/src/wp-includes/revision.php +++ b/src/wp-includes/revision.php @@ -413,7 +413,7 @@ function wp_save_revisioned_meta_fields( $revision_id, $post_id ) { return; } - foreach ( wp_post_revision_meta_keys( $post_type ) as $meta_key ) { + foreach ( wp_post_revision_meta_keys( $post_type ) as $meta_key ) { if ( metadata_exists( 'post', $post_id, $meta_key ) ) { _wp_copy_post_meta( $post_id, $revision_id, $meta_key ); } From f9b760b7d5fb9de3e56e4ebadb60c9fcda4c6ccd Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Mon, 25 Sep 2023 12:56:07 -0600 Subject: [PATCH 087/101] If the object_type is set, require a type that supports revisions when setting revisions_enabled to true --- src/wp-includes/meta.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/wp-includes/meta.php b/src/wp-includes/meta.php index 99e6920f9adba..50f7b16fef7fa 100644 --- a/src/wp-includes/meta.php +++ b/src/wp-includes/meta.php @@ -1463,6 +1463,13 @@ function register_meta( $object_type, $meta_key, $args, $deprecated = null ) { } } + // If the object_type is set, require a type that supports revisions when setting revisions_enabled to true. + if ( ! empty( $object_type ) && $args['revisions_enabled'] && ! post_type_supports( $object_type, 'revisions' ) ) { + _doing_it_wrong( __FUNCTION__, __( 'Meta keys cannot enable revisions support unless the object type supports revisions.' ), '6.4.0' ); + + return false; + } + $object_subtype = ! empty( $args['object_subtype'] ) ? $args['object_subtype'] : ''; // If `auth_callback` is not provided, fall back to `is_protected_meta()`. From 8360faa26e9aa07718aba603d37f62f70039615f Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Mon, 25 Sep 2023 12:56:30 -0600 Subject: [PATCH 088/101] =?UTF-8?q?Test=20enabling=20revisions=20for=20met?= =?UTF-8?q?a=20when=20object=20type=20doesn=E2=80=99t=20support=20revision?= =?UTF-8?q?s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/phpunit/tests/meta/registerMeta.php | 32 +++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/phpunit/tests/meta/registerMeta.php b/tests/phpunit/tests/meta/registerMeta.php index 2699c636a8590..5ed1ea91e5a41 100644 --- a/tests/phpunit/tests/meta/registerMeta.php +++ b/tests/phpunit/tests/meta/registerMeta.php @@ -1086,4 +1086,36 @@ public function data_get_types_and_subtypes() { array( 'user', 'user' ), ); } + + /** + * Test that attempting to register meta with revisions_enabled set to true on a + * post type that does not have revisions enabled fails and throws a `doing_it_wrong` notice. + * + * @ticket 20564 + */ + public function test_register_meta_with_revisions_enabled_on_post_type_without_revisions() { + $this->setExpectedIncorrectUsage( 'register_meta' ); + + // Set up a custom post type with revisions disabled. + register_post_type( + 'test_post_type', + array( + 'supports' => array( 'title', 'editor' ), + ) + ); + + $meta_key = 'registered_key1'; + $args = array( + 'revisions_enabled' => true, + ); + + $register = register_meta( + 'test_post_type', + $meta_key, + $args + ); + + $this->assertFalse( $register ); + } + } From a1971f7c81cfa66f8c2d07c2e4d87677e2aded99 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Mon, 25 Sep 2023 13:12:04 -0600 Subject: [PATCH 089/101] phpcbf --- tests/phpunit/tests/meta/registerMeta.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/phpunit/tests/meta/registerMeta.php b/tests/phpunit/tests/meta/registerMeta.php index 5ed1ea91e5a41..9909d360007d9 100644 --- a/tests/phpunit/tests/meta/registerMeta.php +++ b/tests/phpunit/tests/meta/registerMeta.php @@ -1100,12 +1100,12 @@ public function test_register_meta_with_revisions_enabled_on_post_type_without_r register_post_type( 'test_post_type', array( - 'supports' => array( 'title', 'editor' ), + 'supports' => array( 'title', 'editor' ), ) ); - $meta_key = 'registered_key1'; - $args = array( + $meta_key = 'registered_key1'; + $args = array( 'revisions_enabled' => true, ); From 99b2aa84366347b7cde2f9b32d192795d63934b4 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 26 Sep 2023 07:54:45 -0600 Subject: [PATCH 090/101] Update src/wp-includes/rest-api/endpoints/class-wp-rest-autosaves-controller.php Co-authored-by: Jonny Harris --- .../rest-api/endpoints/class-wp-rest-autosaves-controller.php | 2 +- 1 file changed, 1 insertion(+), 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 86cf0f7b4a468..99704e30df8fd 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 @@ -354,7 +354,7 @@ public function get_item_schema() { * @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, $meta = array() ) { + public function create_post_autosave( $post_data, array $meta = array() ) { $post_id = (int) $post_data['ID']; $post = get_post( $post_id ); From b94e1a65ae66c781938d4f1a621e990c7b24fe8f Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 26 Sep 2023 07:56:00 -0600 Subject: [PATCH 091/101] Update src/wp-includes/rest-api/endpoints/class-wp-rest-autosaves-controller.php Co-authored-by: Jonny Harris --- .../endpoints/class-wp-rest-autosaves-controller.php | 7 ++++++- 1 file changed, 6 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 99704e30df8fd..41d0a4b531a56 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 @@ -413,14 +413,19 @@ public function create_post_autosave( $post_data, array $meta = array() ) { $revision_id = _wp_put_post_revision( $post_data, true ); } + if( is_wp_error( $revision_id ) ) { + return $revision_id; + } + // Attached any passed meta values that have revisions enabled. - if ( ! empty( $meta ) && $revision_id && ! is_wp_error( $revision_id ) ) { + if ( ! empty( $meta ) && $revision_id ) { foreach ( $revisioned_meta_keys as $meta_key ) { if ( isset( $meta[ $meta_key ] ) ) { update_metadata( 'post', $revision_id, $meta_key, $meta[ $meta_key ] ); } } } + return $revision_id; } From e09f0110a553fdb9bed928a1245681381938850d Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 26 Sep 2023 07:56:31 -0600 Subject: [PATCH 092/101] Update src/wp-admin/includes/post.php Co-authored-by: Pascal Birchler --- src/wp-admin/includes/post.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/wp-admin/includes/post.php b/src/wp-admin/includes/post.php index 91f4b8be98a36..fc2a4adc4cc28 100644 --- a/src/wp-admin/includes/post.php +++ b/src/wp-admin/includes/post.php @@ -1974,11 +1974,7 @@ function wp_create_post_autosave( $post_data ) { if ( ! is_wp_error( $revision ) ) { - /** - * Fires before an autosave is stored. - * - * This filter is documented in `src/wp-admin/includes/post.php`. - */ + /** This action is documented in wp-admin/includes/post.php */ do_action( 'wp_creating_autosave', get_post( $revision, ARRAY_A ), false ); } From 01a478829e89a9c716358ff919593e2a6253cb61 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 26 Sep 2023 07:56:41 -0600 Subject: [PATCH 093/101] Update src/wp-admin/includes/post.php Co-authored-by: Pascal Birchler --- src/wp-admin/includes/post.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-admin/includes/post.php b/src/wp-admin/includes/post.php index fc2a4adc4cc28..45214e60427a8 100644 --- a/src/wp-admin/includes/post.php +++ b/src/wp-admin/includes/post.php @@ -1984,7 +1984,7 @@ function wp_create_post_autosave( $post_data ) { /** * Autosave the revisioned meta fields. * - * Iterates thru the revisioned meta fields and checks each to see if they are set, + * Iterates through the revisioned meta fields and checks each to see if they are set, * and have a changed value. If so, the meta value is saved and attached to the autosave. * * @since 6.4.0 From 8e7b72a31e13ebaca8471746fc4361784d060493 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 26 Sep 2023 08:01:54 -0600 Subject: [PATCH 094/101] spacing --- .../endpoints/class-wp-rest-autosaves-controller.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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 41d0a4b531a56..256f9bab8af48 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 @@ -413,10 +413,10 @@ public function create_post_autosave( $post_data, array $meta = array() ) { $revision_id = _wp_put_post_revision( $post_data, true ); } - if( is_wp_error( $revision_id ) ) { - return $revision_id; - } - + if( is_wp_error( $revision_id ) ) { + return $revision_id; + } + // Attached any passed meta values that have revisions enabled. if ( ! empty( $meta ) && $revision_id ) { foreach ( $revisioned_meta_keys as $meta_key ) { @@ -425,7 +425,7 @@ public function create_post_autosave( $post_data, array $meta = array() ) { } } } - + return $revision_id; } From d6c6ca2d25a9603b622d2fbe3c988e9f69e396b3 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 26 Sep 2023 08:05:55 -0600 Subject: [PATCH 095/101] Guard against _wp_put_post_revision returning 0 --- src/wp-admin/includes/post.php | 2 +- .../rest-api/endpoints/class-wp-rest-autosaves-controller.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wp-admin/includes/post.php b/src/wp-admin/includes/post.php index 45214e60427a8..66b7b8c561736 100644 --- a/src/wp-admin/includes/post.php +++ b/src/wp-admin/includes/post.php @@ -1972,7 +1972,7 @@ function wp_create_post_autosave( $post_data ) { // Otherwise create the new autosave as a special post revision. $revision = _wp_put_post_revision( $post_data, true ); - if ( ! is_wp_error( $revision ) ) { + if ( ! is_wp_error( $revision ) && 0 !== $revision ) { /** This action is documented in wp-admin/includes/post.php */ do_action( 'wp_creating_autosave', get_post( $revision, ARRAY_A ), false ); 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 256f9bab8af48..17a2b259a5eeb 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 @@ -413,7 +413,7 @@ public function create_post_autosave( $post_data, array $meta = array() ) { $revision_id = _wp_put_post_revision( $post_data, true ); } - if( is_wp_error( $revision_id ) ) { + if( is_wp_error( $revision_id ) || 0 === $revision_id ) { return $revision_id; } From 73f6a2328ba4aedcc47aab8d392ce44a5401c3ad Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 26 Sep 2023 08:17:53 -0600 Subject: [PATCH 096/101] Update src/wp-includes/meta.php Co-authored-by: Jonny Harris --- src/wp-includes/meta.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/wp-includes/meta.php b/src/wp-includes/meta.php index 50f7b16fef7fa..42b36c699461a 100644 --- a/src/wp-includes/meta.php +++ b/src/wp-includes/meta.php @@ -1463,15 +1463,19 @@ function register_meta( $object_type, $meta_key, $args, $deprecated = null ) { } } - // If the object_type is set, require a type that supports revisions when setting revisions_enabled to true. - if ( ! empty( $object_type ) && $args['revisions_enabled'] && ! post_type_supports( $object_type, 'revisions' ) ) { + $object_subtype = ! empty( $args['object_subtype'] ) ? $args['object_subtype'] : ''; + if( $args['revisions_enabled'] ) { + if( 'post' !== $object_type ) { + _doing_it_wrong( __FUNCTION__, __( 'Meta keys cannot enable revisions support unless the object type supports revisions.' ), '6.4.0' ); + + return false; + } else if ( ! empty( $object_subtype ) && ! post_type_supports( $object_subtype, 'revisions' ) ) { _doing_it_wrong( __FUNCTION__, __( 'Meta keys cannot enable revisions support unless the object type supports revisions.' ), '6.4.0' ); return false; + } } - $object_subtype = ! empty( $args['object_subtype'] ) ? $args['object_subtype'] : ''; - // If `auth_callback` is not provided, fall back to `is_protected_meta()`. if ( empty( $args['auth_callback'] ) ) { if ( is_protected_meta( $meta_key, $object_type ) ) { From 3db644c1f339d9f2c0549b8f92a59f2f8300bc47 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 26 Sep 2023 08:21:20 -0600 Subject: [PATCH 097/101] tabs vs spaces --- src/wp-includes/meta.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/wp-includes/meta.php b/src/wp-includes/meta.php index 42b36c699461a..b01f24cc6f15b 100644 --- a/src/wp-includes/meta.php +++ b/src/wp-includes/meta.php @@ -1465,15 +1465,15 @@ function register_meta( $object_type, $meta_key, $args, $deprecated = null ) { $object_subtype = ! empty( $args['object_subtype'] ) ? $args['object_subtype'] : ''; if( $args['revisions_enabled'] ) { - if( 'post' !== $object_type ) { - _doing_it_wrong( __FUNCTION__, __( 'Meta keys cannot enable revisions support unless the object type supports revisions.' ), '6.4.0' ); + if( 'post' !== $object_type ) { + _doing_it_wrong( __FUNCTION__, __( 'Meta keys cannot enable revisions support unless the object type supports revisions.' ), '6.4.0' ); - return false; - } else if ( ! empty( $object_subtype ) && ! post_type_supports( $object_subtype, 'revisions' ) ) { - _doing_it_wrong( __FUNCTION__, __( 'Meta keys cannot enable revisions support unless the object type supports revisions.' ), '6.4.0' ); + return false; + } else if ( ! empty( $object_subtype ) && ! post_type_supports( $object_subtype, 'revisions' ) ) { + _doing_it_wrong( __FUNCTION__, __( 'Meta keys cannot enable revisions support unless the object type supports revisions.' ), '6.4.0' ); - return false; - } + return false; + } } // If `auth_callback` is not provided, fall back to `is_protected_meta()`. From 95c9e9d38a6cf2d1f90ff547f72ba11328501eaf Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 26 Sep 2023 09:01:27 -0600 Subject: [PATCH 098/101] Update src/wp-includes/rest-api/endpoints/class-wp-rest-autosaves-controller.php Co-authored-by: Jonny Harris --- .../rest-api/endpoints/class-wp-rest-autosaves-controller.php | 2 +- 1 file changed, 1 insertion(+), 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 17a2b259a5eeb..dbecd676dedcf 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 @@ -235,7 +235,7 @@ public function create_item( $request ) { $autosave_id = wp_update_post( wp_slash( (array) $prepared_post ), true ); } else { // Non-draft posts: create or update the post autosave. Pass the meta data. - $autosave_id = $this->create_post_autosave( (array) $prepared_post, $request->get_param( 'meta' ) ); + $autosave_id = $this->create_post_autosave( (array) $prepared_post, (array) $request->get_param( 'meta' ) ); } if ( is_wp_error( $autosave_id ) ) { From 9bab9a9f296df6e6cad3c2ad7bb4c7a940532304 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 26 Sep 2023 09:04:20 -0600 Subject: [PATCH 099/101] Improve error message --- src/wp-includes/meta.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/meta.php b/src/wp-includes/meta.php index b01f24cc6f15b..8ee9386ff0937 100644 --- a/src/wp-includes/meta.php +++ b/src/wp-includes/meta.php @@ -1470,7 +1470,7 @@ function register_meta( $object_type, $meta_key, $args, $deprecated = null ) { return false; } else if ( ! empty( $object_subtype ) && ! post_type_supports( $object_subtype, 'revisions' ) ) { - _doing_it_wrong( __FUNCTION__, __( 'Meta keys cannot enable revisions support unless the object type supports revisions.' ), '6.4.0' ); + _doing_it_wrong( __FUNCTION__, __( 'Meta keys cannot enable revisions support unless the object subtype supports revisions.' ), '6.4.0' ); return false; } From 8bf1727a7da7dd005b10be7e39ebce2c579932df Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 26 Sep 2023 09:12:33 -0600 Subject: [PATCH 100/101] phpcbf --- src/wp-includes/meta.php | 6 +++--- tests/phpunit/tests/meta/registerMeta.php | 1 - tests/phpunit/tests/rest-api/rest-post-meta-fields.php | 1 - 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/wp-includes/meta.php b/src/wp-includes/meta.php index 8ee9386ff0937..96ace4e9726ba 100644 --- a/src/wp-includes/meta.php +++ b/src/wp-includes/meta.php @@ -1464,12 +1464,12 @@ function register_meta( $object_type, $meta_key, $args, $deprecated = null ) { } $object_subtype = ! empty( $args['object_subtype'] ) ? $args['object_subtype'] : ''; - if( $args['revisions_enabled'] ) { - if( 'post' !== $object_type ) { + if ( $args['revisions_enabled'] ) { + if ( 'post' !== $object_type ) { _doing_it_wrong( __FUNCTION__, __( 'Meta keys cannot enable revisions support unless the object type supports revisions.' ), '6.4.0' ); return false; - } else if ( ! empty( $object_subtype ) && ! post_type_supports( $object_subtype, 'revisions' ) ) { + } elseif ( ! empty( $object_subtype ) && ! post_type_supports( $object_subtype, 'revisions' ) ) { _doing_it_wrong( __FUNCTION__, __( 'Meta keys cannot enable revisions support unless the object subtype supports revisions.' ), '6.4.0' ); return false; diff --git a/tests/phpunit/tests/meta/registerMeta.php b/tests/phpunit/tests/meta/registerMeta.php index 9909d360007d9..329321dd9bc67 100644 --- a/tests/phpunit/tests/meta/registerMeta.php +++ b/tests/phpunit/tests/meta/registerMeta.php @@ -1117,5 +1117,4 @@ public function test_register_meta_with_revisions_enabled_on_post_type_without_r $this->assertFalse( $register ); } - } diff --git a/tests/phpunit/tests/rest-api/rest-post-meta-fields.php b/tests/phpunit/tests/rest-api/rest-post-meta-fields.php index d049f7cce494c..c164c406b0159 100644 --- a/tests/phpunit/tests/rest-api/rest-post-meta-fields.php +++ b/tests/phpunit/tests/rest-api/rest-post-meta-fields.php @@ -1396,7 +1396,6 @@ public function test_update_value_return_success_with_same_value( $meta_key, $me $this->assertArrayHasKey( 'meta', $data ); $this->assertArrayHasKey( $meta_key, $data['meta'] ); $this->assertSame( $meta_value, $data['meta'][ $meta_key ] ); - } public function data_update_value_return_success_with_same_value() { From c7904cefc112feafa8e05c007b22760b25aeecb8 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 26 Sep 2023 09:14:04 -0600 Subject: [PATCH 101/101] phpcbf pt 2 --- .../rest-api/endpoints/class-wp-rest-autosaves-controller.php | 2 +- 1 file changed, 1 insertion(+), 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 dbecd676dedcf..fa403e950cfe0 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 @@ -413,7 +413,7 @@ public function create_post_autosave( $post_data, array $meta = array() ) { $revision_id = _wp_put_post_revision( $post_data, true ); } - if( is_wp_error( $revision_id ) || 0 === $revision_id ) { + if ( is_wp_error( $revision_id ) || 0 === $revision_id ) { return $revision_id; }