Make WordPress Core

Ticket #20564: 20564.25.diff

File 20564.25.diff, 55.0 KB (added by adamsilverstein, 12 months ago)
  • src/wp-admin/includes/post.php

    diff --git src/wp-admin/includes/post.php src/wp-admin/includes/post.php
    index ed73b8ff5f..177d9a02a7 100644
    function wp_create_post_autosave( $post_data ) { 
    19611961                 * @param array $new_autosave Post array - the autosave that is about to be saved.
    19621962                 */
    19631963                do_action( 'wp_creating_autosave', $new_autosave );
    1964 
     1964                wp_autosave_post_revisioned_meta_fields( $new_autosave );
    19651965                return wp_update_post( $new_autosave );
    19661966        }
    19671967
    function wp_create_post_autosave( $post_data ) { 
    19691969        $post_data = wp_unslash( $post_data );
    19701970
    19711971        // Otherwise create the new autosave as a special post revision.
    1972         return _wp_put_post_revision( $post_data, true );
     1972        $revision = _wp_put_post_revision( $post_data, true );
     1973
     1974        // Update the revisioned meta data as well.
     1975        wp_autosave_post_revisioned_meta_fields( get_post( $revision, ARRAY_A ) );
     1976        return $revision;
     1977}
     1978
     1979/**
     1980 * Autosave the revisioned meta fields.
     1981 *
     1982 * Iterates thru the revisioned meta fields and checks each to see if they are set,
     1983 * and have a changed value. If so, the meta value is saved and attached to the autosave.
     1984 *
     1985 * @since 6.4.0
     1986 *
     1987 * @param array $new_autosave The new post data being autosaved.
     1988 */
     1989function wp_autosave_post_revisioned_meta_fields( $new_autosave ) {
     1990        /*
     1991         * The post data arrives as either $_POST['data']['wp_autosave'] or the $_POST
     1992         * itself. This sets $posted_data to the correct variable.
     1993         *
     1994         * Ignoring sanitization to avoid altering meta. Ignoring the nonce check because
     1995         * this is hooked on inner core hooks where a valid nonce was already checked.
     1996         *
     1997         * @phpcs:disable WordPress.Security
     1998         */
     1999        $posted_data = isset( $_POST['data']['wp_autosave'] ) ? $_POST['data']['wp_autosave'] : $_POST;
     2000        // phpcs:enable
     2001
     2002        $post_type = get_post_type( $new_autosave['post_parent'] );
     2003
     2004        /*
     2005         * Go thru the revisioned meta keys and save them as part of the autosave, if
     2006         * the meta key is part of the posted data, the meta value is not blank and
     2007         * the the meta value has changes from the last autosaved value.
     2008         */
     2009        foreach ( wp_post_revision_meta_keys( $post_type ) as $meta_key ) {
     2010
     2011                if (
     2012                isset( $posted_data[ $meta_key ] ) &&
     2013                get_post_meta( $new_autosave['ID'], $meta_key, true ) !== wp_unslash( $posted_data[ $meta_key ] )
     2014                ) {
     2015                        /*
     2016                         * Use the underlying delete_metadata() and add_metadata() functions
     2017                         * vs delete_post_meta() and add_post_meta() to make sure we're working
     2018                         * with the actual revision meta.
     2019                         */
     2020                        delete_metadata( 'post', $new_autosave['ID'], $meta_key );
     2021
     2022                        /*
     2023                         * One last check to ensure meta value not empty().
     2024                         */
     2025                        if ( ! empty( $posted_data[ $meta_key ] ) ) {
     2026                                /*
     2027                                 * Add the revisions meta data to the autosave.
     2028                                 */
     2029                                add_metadata( 'post', $new_autosave['ID'], $meta_key, $posted_data[ $meta_key ] );
     2030                        }
     2031                }
     2032        }
    19732033}
    19742034
    19752035/**
  • src/wp-includes/default-filters.php

    diff --git src/wp-includes/default-filters.php src/wp-includes/default-filters.php
    index a3ea1537cb..f0cbaea7da 100644
    add_action( 'plugins_loaded', 'wp_maybe_load_widgets', 0 ); 
    414414add_action( 'plugins_loaded', 'wp_maybe_load_embeds', 0 );
    415415add_action( 'shutdown', 'wp_ob_end_flush_all', 1 );
    416416// Create a revision whenever a post is updated.
     417add_action( 'wp_after_insert_post', 'wp_save_post_revision_on_insert', 9, 3 );
    417418add_action( 'post_updated', 'wp_save_post_revision', 10, 1 );
    418419add_action( 'publish_post', '_publish_post_hook', 5, 1 );
    419420add_action( 'transition_post_status', '_transition_post_status', 5, 3 );
    add_action( 'init', 'wp_register_persisted_preferences_meta' ); 
    721722// CPT wp_block custom postmeta field.
    722723add_action( 'init', 'wp_create_initial_post_meta' );
    723724
     725// Include revisioned meta when considering whether a post revision has changed.
     726add_filter( 'wp_save_post_revision_post_has_changed', 'wp_check_revisioned_meta_fields_have_changed', 10, 3 );
     727
     728// Save revisioned post meta immediately after a revision is saved
     729add_action( '_wp_put_post_revision', 'wp_save_revisioned_meta_fields', 10, 2 );
     730
    724731// Font management.
    725732add_action( 'wp_head', 'wp_print_font_faces', 50 );
    726733
  • src/wp-includes/meta.php

    diff --git src/wp-includes/meta.php src/wp-includes/meta.php
    index 0b9f08544e..99e6920f9a 100644
    function sanitize_meta( $meta_key, $meta_value, $object_type, $object_subtype = 
    13671367 * @since 4.9.8 The `$object_subtype` argument was added to the arguments array.
    13681368 * @since 5.3.0 Valid meta types expanded to include "array" and "object".
    13691369 * @since 5.5.0 The `$default` argument was added to the arguments array.
     1370 * @since 6.4.0 The `$revisions_enabled` argument was added to the arguments array.
    13701371 *
    13711372 * @param string       $object_type Type of object metadata is for. Accepts 'post', 'comment', 'term', 'user',
    13721373 *                                  or any other object type with an associated meta table.
    function sanitize_meta( $meta_key, $meta_value, $object_type, $object_subtype = 
    13921393 *                                         support for custom fields for registered meta to be accessible via REST.
    13931394 *                                         When registering complex meta values this argument may optionally be an
    13941395 *                                         array with 'schema' or 'prepare_callback' keys instead of a boolean.
     1396 *     @type bool       $revisions_enabled Whether to enable revisions support for this meta_key. Can only be used when the
     1397 *                                         object type is 'post'.
    13951398 * }
    13961399 * @param string|array $deprecated Deprecated. Use `$args` instead.
    13971400 * @return bool True if the meta key was successfully registered in the global array, false if not.
    function register_meta( $object_type, $meta_key, $args, $deprecated = null ) { 
    14141417                'sanitize_callback' => null,
    14151418                'auth_callback'     => null,
    14161419                'show_in_rest'      => false,
     1420                'revisions_enabled' => false,
    14171421        );
    14181422
    14191423        // There used to be individual args for sanitize and auth callbacks.
  • src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php

    diff --git src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php
    index 66122a1922..de29fc01a4 100644
    class WP_REST_Revisions_Controller extends WP_REST_Controller { 
    2424         */
    2525        private $parent_post_type;
    2626
     27        /**
     28         * Instance of a revision meta fields object.
     29         *
     30         * @since 4.7.0
     31         * @var WP_REST_Post_Meta_Fields
     32         */
     33        protected $meta;
     34
    2735        /**
    2836         * Parent controller.
    2937         *
    class WP_REST_Revisions_Controller extends WP_REST_Controller { 
    6068                $this->rest_base         = 'revisions';
    6169                $this->parent_base       = ! empty( $post_type_object->rest_base ) ? $post_type_object->rest_base : $post_type_object->name;
    6270                $this->namespace         = ! empty( $post_type_object->rest_namespace ) ? $post_type_object->rest_namespace : 'wp/v2';
     71                $this->meta              = new WP_REST_Post_Meta_Fields( $parent_post_type );
    6372        }
    6473
    6574        /**
    class WP_REST_Revisions_Controller extends WP_REST_Controller { 
    618627                        );
    619628                }
    620629
     630                if ( rest_is_field_included( 'meta', $fields ) ) {
     631                        $data['meta'] = $this->meta->get_value( $post->ID, $request );
     632                }
     633
    621634                $context  = ! empty( $request['context'] ) ? $request['context'] : 'view';
    622635                $data     = $this->add_additional_fields_to_object( $data, $request );
    623636                $data     = $this->filter_response_by_context( $data, $context );
    class WP_REST_Revisions_Controller extends WP_REST_Controller { 
    751764                        $schema['properties']['guid'] = $parent_schema['properties']['guid'];
    752765                }
    753766
     767                $schema['properties']['meta'] = $this->meta->get_field_schema();
     768
    754769                $this->schema = $schema;
    755770
    756771                return $this->add_additional_fields_schema( $this->schema );
  • src/wp-includes/revision.php

    diff --git src/wp-includes/revision.php src/wp-includes/revision.php
    index d5a5829d7e..609ce4b532 100644
    function _wp_post_revision_data( $post = array(), $autosave = false ) { 
    9595        return $revision_data;
    9696}
    9797
     98/**
     99 * Saves revisions for a post after all changes have been made.
     100 *
     101 * @since 6.4.0
     102 *
     103 * @param int     $post_id The post id that was inserted.
     104 * @param WP_Post $post    The post object that was inserted.
     105 * @param bool    $update  Whether this insert is updating an existing post.
     106 */
     107function wp_save_post_revision_on_insert( $post_id, $post, $update ) {
     108        if ( ! $update ) {
     109                return;
     110        }
     111
     112        if ( ! has_action( 'post_updated', 'wp_save_post_revision' ) ) {
     113                return;
     114        }
     115
     116        wp_save_post_revision( $post_id );
     117}
     118
    98119/**
    99120 * Creates a revision for the current version of a post.
    100121 *
    function wp_save_post_revision( $post_id ) { 
    111132                return;
    112133        }
    113134
     135        // Prevent saving post revisions if revisions should be saved on wp_after_insert_post.
     136        if ( doing_action( 'post_updated' ) && has_action( 'wp_after_insert_post', 'wp_save_post_revision_on_insert' ) ) {
     137                return;
     138        }
     139
    114140        $post = get_post( $post_id );
    115141
    116142        if ( ! $post ) {
    function _wp_put_post_revision( $post = null, $autosave = false ) { 
    361387                 * Fires once a revision has been saved.
    362388                 *
    363389                 * @since 2.6.0
     390                 * @since 6.4.0 The post_id parameter was added.
    364391                 *
    365392                 * @param int $revision_id Post revision ID.
     393                 * @param int $post_id     Post ID.
    366394                 */
    367                 do_action( '_wp_put_post_revision', $revision_id );
     395                do_action( '_wp_put_post_revision', $revision_id, $post['post_parent'] );
    368396        }
    369397
    370398        return $revision_id;
    371399}
    372400
     401
     402/**
     403 * Save the revisioned meta fields.
     404 *
     405 * @param int $revision_id The ID of the revision to save the meta to.
     406 * @param int $post_id     The ID of the post the revision is associated with.
     407 *
     408 * @since 6.4.0
     409 */
     410function wp_save_revisioned_meta_fields( $revision_id, $post_id ) {
     411
     412        // Save revisioned meta fields.
     413        foreach ( wp_post_revision_meta_keys( get_post_type( $post_id ) ) as $meta_key ) {
     414                if ( metadata_exists( 'post', $post_id, $meta_key ) ) {
     415                        _wp_copy_post_meta( $post_id, $revision_id, $meta_key );
     416                }
     417        }
     418}
     419
    373420/**
    374421 * Gets a post revision.
    375422 *
    function wp_restore_post_revision( $revision, $fields = null ) { 
    450497        // Update last edit user.
    451498        update_post_meta( $post_id, '_edit_last', get_current_user_id() );
    452499
     500        // Restore any revisioned meta fields.
     501        wp_restore_post_revision_meta( $post_id, $revision['ID'] );
     502
    453503        /**
    454504         * Fires after a post revision has been restored.
    455505         *
    function wp_restore_post_revision( $revision, $fields = null ) { 
    463513        return $post_id;
    464514}
    465515
     516/**
     517 * Restore the revisioned meta values for a post.
     518 *
     519 * @param int $post_id     The ID of the post to restore the meta to.
     520 * @param int $revision_id The ID of the revision to restore the meta from.
     521 *
     522 * @since 6.4.0
     523 */
     524function wp_restore_post_revision_meta( $post_id, $revision_id ) {
     525
     526        // Restore revisioned meta fields.
     527        foreach ( wp_post_revision_meta_keys( get_post_type( $post_id ) ) as $meta_key ) {
     528
     529                // Clear any existing meta.
     530                delete_post_meta( $post_id, $meta_key );
     531
     532                _wp_copy_post_meta( $revision_id, $post_id, $meta_key );
     533        }
     534}
     535
     536/**
     537 * Copy post meta for the given key from one post to another.
     538 *
     539 * @param int    $source_post_id Post ID to copy meta value(s) from.
     540 * @param int    $target_post_id Post ID to copy meta value(s) to.
     541 * @param string $meta_key       Meta key to copy.
     542 *
     543 * @since 6.4.0
     544 */
     545function _wp_copy_post_meta( $source_post_id, $target_post_id, $meta_key ) {
     546
     547        foreach ( get_post_meta( $source_post_id, $meta_key ) as $meta_value ) {
     548                /**
     549                 * We use add_metadata() function vs add_post_meta() here
     550                 * to allow for a revision post target OR regular post.
     551                 */
     552                add_metadata( 'post', $target_post_id, $meta_key, wp_slash( $meta_value ) );
     553        }
     554}
     555
     556/**
     557 * Determine which post meta fields should be revisioned.
     558 *
     559 * @since 6.4.0
     560 *
     561 * @param string $post_type The post type being revisioned.
     562 *
     563 * @return array An array of meta keys to be revisioned.
     564 */
     565function wp_post_revision_meta_keys( $post_type ) {
     566        $registered_meta = array_merge(
     567                get_registered_meta_keys( 'post' ),
     568                get_registered_meta_keys( 'post', $post_type )
     569        );
     570
     571        $wp_revisioned_meta_keys = array();
     572
     573        foreach ( $registered_meta as $name => $args ) {
     574                if ( $args['revisions_enabled'] ) {
     575                        $wp_revisioned_meta_keys[ $name ] = true;
     576                }
     577        }
     578
     579        $wp_revisioned_meta_keys = array_keys( $wp_revisioned_meta_keys );
     580
     581        /**
     582         * Filter the list of post meta keys to be revisioned.
     583         *
     584         * @since 6.4.0
     585         *
     586         * @param array $keys       An array of meta fields to be revisioned.
     587         * @param string $post_type The post type being revisioned.
     588         */
     589        return apply_filters( 'wp_post_revision_meta_keys', $wp_revisioned_meta_keys, $post_type );
     590}
     591
     592/**
     593 * Check whether revisioned post meta fields have changed.
     594 *
     595 * @param bool    $post_has_changed Whether the post has changed.
     596 * @param WP_Post $last_revision    The last revision post object.
     597 * @param WP_Post $post             The post object.
     598 *
     599 * @since 6.4.0
     600 */
     601function wp_check_revisioned_meta_fields_have_changed( $post_has_changed, WP_Post $last_revision, WP_Post $post ) {
     602        foreach ( wp_post_revision_meta_keys( $post->post_type ) as $meta_key ) {
     603                if ( get_post_meta( $post->ID, $meta_key ) !== get_post_meta( $last_revision->ID, $meta_key ) ) {
     604                        $post_has_changed = true;
     605                        break;
     606                }
     607        }
     608        return $post_has_changed;
     609}
     610
    466611/**
    467612 * Deletes a revision.
    468613 *
    function _set_preview( $post ) { 
    728873
    729874        add_filter( 'get_the_terms', '_wp_preview_terms_filter', 10, 3 );
    730875        add_filter( 'get_post_metadata', '_wp_preview_post_thumbnail_filter', 10, 3 );
     876        add_filter( 'get_post_metadata', '_wp_preview_meta_filter', 10, 4 );
    731877
    732878        return $post;
    733879}
    function _wp_upgrade_revisions_of_post( $post, $revisions ) { 
    9461092
    9471093        return true;
    9481094}
     1095
     1096/**
     1097 * Filters preview post meta retrieval to get values from the autosave.
     1098 *
     1099 * Filters revisioned meta keys only.
     1100 *
     1101 * @since 6.4.0
     1102 *
     1103 * @param mixed  $value     Meta value to filter.
     1104 * @param int    $object_id Object ID.
     1105 * @param string $meta_key  Meta key to filter a value for.
     1106 * @param bool   $single    Whether to return a single value. Default false.
     1107 * @return mixed Original meta value if the meta key isn't revisioned, the object doesn't exist,
     1108 *               the post type is a revision or the post ID doesn't match the object ID.
     1109 *               Otherwise, the revisioned meta value is returned for the preview.
     1110 */
     1111function _wp_preview_meta_filter( $value, $object_id, $meta_key, $single ) {
     1112
     1113        $post = get_post();
     1114        if (
     1115                empty( $post ) ||
     1116                $post->ID !== $object_id ||
     1117                ! in_array( $meta_key, wp_post_revision_meta_keys( $post->post_type ), true ) ||
     1118                'revision' === $post->post_type
     1119        ) {
     1120                return $value;
     1121        }
     1122
     1123        $preview = wp_get_post_autosave( $post->ID );
     1124        if ( ! is_object( $preview ) ) {
     1125                return $value;
     1126        }
     1127
     1128        return get_post_meta( $preview->ID, $meta_key, $single );
     1129}
  • tests/phpunit/tests/meta/registerMeta.php

    diff --git tests/phpunit/tests/meta/registerMeta.php tests/phpunit/tests/meta/registerMeta.php
    index 4b08e4d82b..2699c636a8 100644
    class Tests_Meta_Register_Meta extends WP_UnitTestCase { 
    9797                                                'sanitize_callback' => null,
    9898                                                'auth_callback'     => '__return_true',
    9999                                                'show_in_rest'      => false,
     100                                                'revisions_enabled' => false,
    100101                                        ),
    101102                                ),
    102103                        ),
    class Tests_Meta_Register_Meta extends WP_UnitTestCase { 
    121122                                                'sanitize_callback' => null,
    122123                                                'auth_callback'     => '__return_true',
    123124                                                'show_in_rest'      => false,
     125                                                'revisions_enabled' => false,
    124126                                        ),
    125127                                ),
    126128                        ),
    class Tests_Meta_Register_Meta extends WP_UnitTestCase { 
    175177                                                'sanitize_callback' => array( $this, '_new_sanitize_meta_cb' ),
    176178                                                'auth_callback'     => '__return_true',
    177179                                                'show_in_rest'      => false,
     180                                                'revisions_enabled' => false,
    178181                                        ),
    179182                                ),
    180183                        ),
    class Tests_Meta_Register_Meta extends WP_UnitTestCase { 
    342345                                                'sanitize_callback' => null,
    343346                                                'auth_callback'     => '__return_true',
    344347                                                'show_in_rest'      => false,
     348                                                'revisions_enabled' => false,
    345349                                        ),
    346350                                ),
    347351                        ),
    class Tests_Meta_Register_Meta extends WP_UnitTestCase { 
    395399                                                'sanitize_callback' => null,
    396400                                                'auth_callback'     => '__return_true',
    397401                                                'show_in_rest'      => false,
     402                                                'revisions_enabled' => false,
    398403                                        ),
    399404                                ),
    400405                        ),
  • new file tests/phpunit/tests/post/metaRevisions.php

    diff --git tests/phpunit/tests/post/metaRevisions.php tests/phpunit/tests/post/metaRevisions.php
    new file mode 100644
    index 0000000000..596ff4b094
    - +  
     1<?php
     2
     3/**
     4 *
     5 * Tests for post meta revisioning.
     6 *
     7 * @group post
     8 * @group revision
     9 * @group meta
     10 * @group meta-revisions
     11 */
     12class MetaRevisionTests extends WP_UnitTestCase {
     13
     14        /**
     15         * Callback function to add the revisioned keys.
     16         *
     17         * @param array $keys The array of revisioned keys.
     18         *
     19         * @return array
     20         */
     21        public function add_revisioned_keys( $keys ) {
     22                $keys[] = 'meta_revision_test';
     23                $keys[] = 'meta_multiples_test';
     24                return $keys;
     25        }
     26
     27        /**
     28         * Test the revisions system for storage of meta values with slashes.
     29         *
     30         * @param string $passed   The passed data for testing.
     31         *
     32         * @param string $expected The expected value after storing & retrieving.
     33         *
     34         * @group revision
     35         * @group slashed
     36         * @dataProvider slashed_data_provider
     37         */
     38        public function test_revisions_stores_meta_values_with_slashes( $passed, $expected ) {
     39                // Set up a new post.
     40                $post_id = $this->factory->post->create();
     41
     42                // And update to store an initial revision.
     43                wp_update_post(
     44                        array(
     45                                'post_content' => 'some initial content',
     46                                'ID'           => $post_id,
     47                        )
     48                );
     49                add_filter( 'wp_post_revision_meta_keys', array( $this, 'add_revisioned_keys' ) );
     50
     51                // Store a custom meta value, which is not revisioned by default.
     52                update_post_meta( $post_id, 'meta_revision_test', wp_slash( $passed ) );
     53                $this->assertEquals( $expected, get_post_meta( $post_id, 'meta_revision_test', true ) );
     54
     55                // Update the post, storing a revision.
     56                wp_update_post(
     57                        array(
     58                                'post_content' => 'some more content',
     59                                'ID'           => $post_id,
     60                        )
     61                );
     62
     63                // Overwrite.
     64                update_post_meta( $post_id, 'meta_revision_test', 'original' );
     65                // Update the post, storing a revision.
     66                wp_update_post(
     67                        array(
     68                                'post_content' => 'some more content again',
     69                                'ID'           => $post_id,
     70                        )
     71                );
     72
     73                // Restore the previous revision.
     74                $revisions = (array) wp_get_post_revisions( $post_id );
     75
     76                // Go back to load the previous revision.
     77                array_shift( $revisions );
     78                $last_revision = array_shift( $revisions );
     79
     80                // Restore!
     81                wp_restore_post_revision( $last_revision->ID );
     82
     83                $this->assertEquals( $expected, get_post_meta( $post_id, 'meta_revision_test', true ) );
     84        }
     85
     86        /**
     87         * Provide data for the slashed data tests.
     88         */
     89        public function slashed_data_provider() {
     90                return array(
     91                        array(
     92                                'some\text',
     93                                'some\text',
     94                        ),
     95                        array(
     96                                'test some\ \\extra \\\slashed \\\\text ',
     97                                'test some\ \\extra \\\slashed \\\\text ',
     98                        ),
     99                        array(
     100                                "This \'is\' an example \n of a \"quoted\" string",
     101                                "This \'is\' an example \n of a \"quoted\" string",
     102                        ),
     103                        array(
     104                                'some unslashed text just to test! % & * ( ) #',
     105                                'some unslashed text just to test! % & * ( ) #',
     106                        ),
     107                );
     108        }
     109
     110        /**
     111         * Test the revisions system for storage of meta values.
     112         *
     113         * @group revision
     114         */
     115        public function test_revisions_stores_meta_values() {
     116                /*
     117                 * Set Up.
     118                 */
     119
     120                // Set up a new post.
     121                $post_id          = $this->factory->post->create();
     122                $original_post_id = $post_id;
     123
     124                // And update to store an initial revision.
     125                wp_update_post(
     126                        array(
     127                                'post_content' => 'some initial content',
     128                                'ID'           => $post_id,
     129                        )
     130                );
     131
     132                // One revision so far.
     133                $revisions = wp_get_post_revisions( $post_id );
     134                $this->assertCount( 1, $revisions );
     135
     136                /*
     137                 * First set up a meta value.
     138                 */
     139
     140                // Store a custom meta value, which is not revisioned by default.
     141                update_post_meta( $post_id, 'meta_revision_test', 'original' );
     142
     143                // Update the post, storing a revision.
     144                wp_update_post(
     145                        array(
     146                                'post_content' => 'some more content',
     147                                'ID'           => $post_id,
     148                        )
     149                );
     150
     151                $revisions = wp_get_post_revisions( $post_id );
     152                $this->assertCount( 2, $revisions );
     153
     154                // Next, store some updated meta values for the same key.
     155                update_post_meta( $post_id, 'meta_revision_test', 'update1' );
     156
     157                // Save the post, changing content to force a revision.
     158                wp_update_post(
     159                        array(
     160                                'post_content' => 'some updated content',
     161                                'ID'           => $post_id,
     162                        )
     163                );
     164
     165                $revisions = wp_get_post_revisions( $post_id );
     166                $this->assertCount( 3, $revisions );
     167
     168                /*
     169                 * Now restore the original revision.
     170                 */
     171
     172                // Restore the previous revision.
     173                $revisions = (array) wp_get_post_revisions( $post_id );
     174
     175                // Go back two to load the previous revision.
     176                array_shift( $revisions );
     177                $last_revision = array_shift( $revisions );
     178
     179                // Restore!
     180                wp_restore_post_revision( $last_revision->ID );
     181
     182                wp_update_post( array( 'ID' => $post_id ) );
     183                $revisions = wp_get_post_revisions( $post_id );
     184                $this->assertCount( 4, $revisions );
     185
     186                /*
     187                 * Check the meta values to verify they are NOT revisioned - they are not revisioned by default.
     188                 */
     189
     190                // Custom post meta should NOT be restored, orignal value should not be restored, value still 'update1'.
     191                $this->assertEquals( 'update1', get_post_meta( $post_id, 'meta_revision_test', true ) );
     192
     193                update_post_meta( $post_id, 'meta_revision_test', 'update2' );
     194
     195                /*
     196                 * Test the revisioning of custom meta when enabled by the wp_post_revision_meta_keys filter.
     197                 */
     198
     199                // Add the custom field to be revised via the wp_post_revision_meta_keys filter.
     200                add_filter( 'wp_post_revision_meta_keys', array( $this, 'add_revisioned_keys' ) );
     201
     202                // Save the post, changing content to force a revision.
     203                wp_update_post(
     204                        array(
     205                                'post_content' => 'more updated content',
     206                                'ID'           => $post_id,
     207                        )
     208                );
     209
     210                $revisions = array_values( wp_get_post_revisions( $post_id ) );
     211                $this->assertCount( 5, $revisions );
     212                $this->assertEquals( 'update2', get_post_meta( $revisions[0]->ID, 'meta_revision_test', true ) );
     213
     214                // Store custom meta values, which should now be revisioned.
     215                update_post_meta( $post_id, 'meta_revision_test', 'update3' );
     216
     217                /*
     218                 * Save the post again, custom meta should now be revisioned.
     219                 *
     220                 * Note that a revision is saved even though there is no change
     221                 * in post content, because the revisioned post_meta has changed.
     222                 */
     223                wp_update_post(
     224                        array(
     225                                'ID' => $post_id,
     226                        )
     227                );
     228
     229                // This revision contains the existing post meta ('update3').
     230                $revisions = wp_get_post_revisions( $post_id );
     231                $this->assertCount( 6, $revisions );
     232
     233                // Verify that previous post meta is set.
     234                $this->assertEquals( 'update3', get_post_meta( $post_id, 'meta_revision_test', true ) );
     235
     236                // Restore the previous revision.
     237                $revisions = wp_get_post_revisions( $post_id );
     238
     239                // Go back two to load the previous revision.
     240                array_shift( $revisions );
     241                $last_revision = array_shift( $revisions );
     242                wp_restore_post_revision( $last_revision->ID );
     243
     244                /*
     245                 * Verify that previous post meta is restored.
     246                 */
     247                $this->assertEquals( 'update2', get_post_meta( $post_id, 'meta_revision_test', true ) );
     248
     249                // Try storing a blank meta.
     250                update_post_meta( $post_id, 'meta_revision_test', '' );
     251                wp_update_post(
     252                        array(
     253                                'ID' => $post_id,
     254                        )
     255                );
     256
     257                update_post_meta( $post_id, 'meta_revision_test', 'update 4' );
     258                wp_update_post(
     259                        array(
     260                                'ID' => $post_id,
     261                        )
     262                );
     263
     264                // Restore the previous revision.
     265                $revisions = wp_get_post_revisions( $post_id );
     266                array_shift( $revisions );
     267                $last_revision = array_shift( $revisions );
     268                wp_restore_post_revision( $last_revision->ID );
     269
     270                /*
     271                 * Verify that previous blank post meta is restored.
     272                 */
     273                $this->assertEquals( '', get_post_meta( $post_id, 'meta_revision_test', true ) );
     274
     275                /*
     276                 * Test not tracking a key - remove the key from the revisioned meta.
     277                 */
     278                remove_all_filters( 'wp_post_revision_meta_keys' );
     279
     280                // Meta should no longer be revisioned.
     281                update_post_meta( $post_id, 'meta_revision_test', 'update 5' );
     282                wp_update_post(
     283                        array(
     284                                'ID'           => $post_id,
     285                                'post_content' => 'changed content',
     286                        )
     287                );
     288                update_post_meta( $post_id, 'meta_revision_test', 'update 6' );
     289                wp_update_post(
     290                        array(
     291                                'ID'           => $post_id,
     292                                'post_content' => 'go updated content',
     293                        )
     294                );
     295
     296                // Restore the previous revision.
     297                $revisions = wp_get_post_revisions( $post_id );
     298                array_shift( $revisions );
     299                $last_revision = array_shift( $revisions );
     300                wp_restore_post_revision( $last_revision->ID );
     301
     302                /*
     303                 * Verify that previous post meta is NOT restored.
     304                 */
     305                $this->assertEquals( 'update 6', get_post_meta( $post_id, 'meta_revision_test', true ) );
     306
     307                // Add the custom field to be revised via the wp_post_revision_meta_keys filter.
     308                add_filter( 'wp_post_revision_meta_keys', array( $this, 'add_revisioned_keys' ) );
     309
     310                /*
     311                 * Test the revisioning of multiple meta keys.
     312                 */
     313
     314                // Add three values for meta.
     315                update_post_meta( $post_id, 'meta_revision_test', 'update 7' );
     316                add_post_meta( $post_id, 'meta_revision_test', 'update 7 number 2' );
     317                add_post_meta( $post_id, 'meta_revision_test', 'update 7 number 3' );
     318                wp_update_post( array( 'ID' => $post_id ) );
     319
     320                // Update all three values.
     321                update_post_meta( $post_id, 'meta_revision_test', 'update 8', 'update 7' );
     322                update_post_meta( $post_id, 'meta_revision_test', 'update 8 number 2', 'update 7 number 2' );
     323                update_post_meta( $post_id, 'meta_revision_test', 'update 8 number 3', 'update 7 number 3' );
     324
     325                // Restore the previous revision.
     326                $revisions     = wp_get_post_revisions( $post_id );
     327                $last_revision = array_shift( $revisions );
     328                wp_restore_post_revision( $last_revision->ID );
     329
     330                /*
     331                 * Verify that multiple metas stored correctly.
     332                 */
     333                $this->assertEquals( array( 'update 7', 'update 7 number 2', 'update 7 number 3' ), get_post_meta( $post_id, 'meta_revision_test' ) );
     334
     335                /*
     336                 * Test the revisioning of a multidimensional array.
     337                 */
     338                $test_array = array(
     339                        'a' => array(
     340                                '1',
     341                                '2',
     342                                '3',
     343                        ),
     344                        'b' => 'ok',
     345                        'c' => array(
     346                                'multi' => array(
     347                                        'a',
     348                                        'b',
     349                                        'c',
     350                                ),
     351                                'not'   => 'ok',
     352                        ),
     353                );
     354
     355                // Clear any old value.
     356                delete_post_meta( $post_id, 'meta_revision_test' );
     357
     358                // Set the test meta to the array.
     359                update_post_meta( $post_id, 'meta_revision_test', $test_array );
     360
     361                // Update to save.
     362                wp_update_post( array( 'ID' => $post_id ) );
     363
     364                // Set the test meta blank.
     365                update_post_meta( $post_id, 'meta_revision_test', '' );
     366
     367                // Restore the previous revision.
     368                $revisions     = wp_get_post_revisions( $post_id );
     369                $last_revision = array_shift( $revisions );
     370                wp_restore_post_revision( $last_revision->ID );
     371
     372                /*
     373                 * Verify  multidimensional array stored correctly.
     374                 */
     375                $stored_array = get_post_meta( $post_id, 'meta_revision_test' );
     376                $this->assertEquals( $test_array, $stored_array[0] );
     377                /*
     378
     379                 * Test multiple revisions on the same key.
     380                 */
     381
     382                // Set the test meta to the array.
     383                add_post_meta( $post_id, 'meta_multiples_test', 'test1' );
     384                add_post_meta( $post_id, 'meta_multiples_test', 'test2' );
     385                add_post_meta( $post_id, 'meta_multiples_test', 'test3' );
     386
     387                // Update to save.
     388                wp_update_post( array( 'ID' => $post_id ) );
     389
     390                $stored_array = get_post_meta( $post_id, 'meta_multiples_test' );
     391                $expect       = array( 'test1', 'test2', 'test3' );
     392
     393                $this->assertEquals( $expect, $stored_array );
     394
     395                // Restore the previous revision.
     396                $revisions     = wp_get_post_revisions( $post_id );
     397                $last_revision = array_shift( $revisions );
     398                wp_restore_post_revision( $last_revision->ID );
     399
     400                $stored_array = get_post_meta( $post_id, 'meta_multiples_test' );
     401                $expect       = array( 'test1', 'test2', 'test3' );
     402
     403                $this->assertEquals( $expect, $stored_array );
     404
     405                // Cleanup!
     406                wp_delete_post( $original_post_id );
     407        }
     408
     409        /**
     410         * Verify that only existing meta is revisioned.
     411         */
     412        public function only_existing_meta_is_revisioned() {
     413                add_filter( 'wp_post_revision_meta_keys', array( $this, 'add_revisioned_keys' ) );
     414
     415                // Set up a new post.
     416                $post_id = $this->factory->post->create(
     417                        array(
     418                                'post_content' => 'initial content',
     419                        )
     420                );
     421
     422                // Revision v1.
     423                wp_update_post(
     424                        array(
     425                                'ID'           => $post_id,
     426                                'post_content' => 'updated content v1',
     427                        )
     428                );
     429
     430                $this->assertPostNotHasMetaKey( $post_id, 'foo' );
     431                $this->assertPostNotHasMetaKey( $post_id, 'bar' );
     432
     433                $revisions = wp_get_post_revisions( $post_id );
     434                $revision  = array_shift( $revisions );
     435                $this->assertEmpty( get_metadata( 'post', $revision->ID ) );
     436
     437                // Revision v2.
     438                wp_update_post(
     439                        array(
     440                                'ID'           => $post_id,
     441                                'post_content' => 'updated content v2',
     442                                'meta_input'   => array(
     443                                        'foo' => 'foo v2',
     444                                ),
     445                        )
     446                );
     447
     448                $this->assertPostHasMetaKey( $post_id, 'foo' );
     449                $this->assertPostNotHasMetaKey( $post_id, 'bar' );
     450                $this->assertPostNotHasMetaKey( $post_id, 'meta_revision_test' );
     451
     452                $revisions = wp_get_post_revisions( $post_id );
     453                $revision  = array_shift( $revisions );
     454                $this->assertPostHasMetaKey( $revision->ID, 'foo' );
     455                $this->assertPostNotHasMetaKey( $revision->ID, 'bar' );
     456                $this->assertPostNotHasMetaKey( $revision->ID, 'meta_revision_test' );
     457        }
     458
     459        /**
     460         * Verify that blank strings are revisioned correctly.
     461         */
     462        public function blank_meta_is_revisioned() {
     463
     464                add_filter( 'wp_post_revision_meta_keys', array( $this, 'add_revisioned_keys' ) );
     465
     466                // Set up a new post.
     467                $post_id = $this->factory->post->create(
     468                        array(
     469                                'post_content' => 'initial content',
     470                                'meta_input'   => array(
     471                                        'foo' => 'foo',
     472                                ),
     473                        )
     474                );
     475
     476                // Set the test meta to an empty string.
     477                update_post_meta( $post_id, 'foo', '' );
     478
     479                // Update to save.
     480                wp_update_post( array( 'ID' => $post_id ) );
     481
     482                $stored_array = get_post_meta( $post_id, 'meta_multiples_test' );
     483                $expect       = array( 'test1', 'test2', 'test3' );
     484
     485                $this->assertEquals( $expect, $stored_array );
     486
     487                // Restore the previous revision.
     488                $revisions     = wp_get_post_revisions( $post_id );
     489                $last_revision = array_shift( $revisions );
     490                wp_restore_post_revision( $last_revision->ID );
     491                $stored_data = get_post_meta( $post_id, 'foo' );
     492                $this->assertEquals( '', $stored_data[0] );
     493        }
     494
     495
     496        /**
     497         * @dataProvider data_register_post_meta_supports_revisions
     498         */
     499        public function test_register_post_meta_supports_revisions( $post_type, $meta_key, $args, $expected_is_revisioned ) {
     500                register_post_meta( $post_type, $meta_key, $args );
     501
     502                // Set up a new post.
     503                $post_id = $this->factory->post->create(
     504                        array(
     505                                'post_content' => 'initial content',
     506                                'post_type'    => $post_type,
     507                                'meta_input'   => array(
     508                                        $meta_key => 'foo',
     509                                ),
     510                        )
     511                );
     512
     513                // Update the post meta and post to save.
     514                update_post_meta( $post_id, $meta_key, 'bar' );
     515                wp_update_post(
     516                        array(
     517                                'ID'         => $post_id,
     518                                'post_title' => 'updated title',
     519                        )
     520                );
     521
     522                // Check the last revision for the post to see if the meta key was revisioned
     523                $revisions       = wp_get_post_revisions( $post_id );
     524                $revision        = array_shift( $revisions );
     525                $revisioned_meta = get_post_meta( $revision->ID, $meta_key, true );
     526                $this->assertEquals( $expected_is_revisioned, 'bar' === $revisioned_meta );
     527
     528                // Reset global so subsequent data tests do not get polluted.
     529                $GLOBALS['wp_meta_keys'] = array();
     530        }
     531
     532        public function data_register_post_meta_supports_revisions() {
     533                return array(
     534                        array( 'post', 'registered_key1', array( 'single' => true ), false ),
     535                        array(
     536                                'post',
     537                                'registered_key1',
     538                                array(
     539                                        'single'            => true,
     540                                        'revisions_enabled' => true,
     541                                ),
     542                                true,
     543                        ),
     544                        array( 'page', 'registered_key2', array( 'revisions_enabled' => false ), false ),
     545                        array( 'page', 'registered_key2', array( 'revisions_enabled' => true ), true ),
     546                        array( '', 'registered_key3', array( 'revisions_enabled' => false ), false ),
     547                        array( '', 'registered_key3', array( 'revisions_enabled' => true ), true ),
     548                );
     549        }
     550
     551        /**
     552         * Assert the a post has a meta key.
     553         *
     554         * @param int    $post_id        The ID of the post to check.
     555         * @param string $meta_key The meta key to check for.
     556         */
     557        protected function assertPostHasMetaKey( $post_id, $meta_key ) {
     558                $this->assertArrayHasKey( $meta_key, get_metadata( 'post', $post_id ) );
     559        }
     560
     561        /**
     562         * Assert that post does not have a meta key.
     563         *
     564         * @param int    $post_id        The ID of the post to check.
     565         * @param string $meta_key The meta key to check for.
     566         */
     567        protected function assertPostNotHasMetaKey( $post_id, $meta_key ) {
     568                $this->assertArrayNotHasKey( $meta_key, get_metadata( 'post', $post_id ) );
     569        }
     570
     571        /**
     572         * Test post meta revisioning with a custom post type, as well as the "page" post type.
     573         *
     574         * @dataProvider page_post_type_data_provider
     575         */
     576        public function test_revisions_stores_meta_values_page_and_cpt( $passed, $expected, $post_type, $supports_revisions = false ) {
     577
     578                // If the post type doesn't exist, create it, potentially supporting revisions.
     579                if ( ! post_type_exists( $post_type ) ) {
     580                        register_post_type(
     581                                $post_type,
     582                                array(
     583                                        'public'   => true,
     584                                        'supports' => $supports_revisions ? array( 'revisions' ) : array(),
     585                                )
     586                        );
     587                }
     588
     589                // Create a test post.
     590                $page_id = $this->factory->post->create(
     591                        array(
     592                                'post_type'    => $post_type,
     593                                'post_content' => 'some initial content',
     594                        )
     595                );
     596
     597                // Add the revisioning filter.
     598                add_filter( 'wp_post_revision_meta_keys', array( $this, 'add_revisioned_keys' ) );
     599
     600                // Test revisioning.
     601                update_post_meta( $page_id, 'meta_revision_test', wp_slash( $passed ) );
     602
     603                // Update the post, storing a revision.
     604                wp_update_post(
     605                        array(
     606                                'post_content' => 'some more content',
     607                                'ID'           => $page_id,
     608                        )
     609                );
     610
     611                // Retrieve the created revision.
     612                $revisions = (array) wp_get_post_revisions( $page_id );
     613
     614                if ( $expected ) {
     615                        // Go back to load the previous revision.
     616                        $last_revision = array_shift( $revisions );
     617                                wp_restore_post_revision( $last_revision->ID );
     618                        $this->assertEquals( $expected, get_post_meta( $page_id, 'meta_revision_test', true ) );
     619                } else {
     620                        $this->assertEmpty( $revisions );
     621                }
     622        }
     623
     624        /**
     625         * Provide data for the page post type tests.
     626         */
     627        public function page_post_type_data_provider() {
     628                return array(
     629                        array(
     630                                'Test string',
     631                                'Test string',
     632                                'page',
     633                        ),
     634                        array(
     635                                'Test string',
     636                                false,
     637                                'custom_type',
     638                        ),
     639                        array(
     640                                'Test string',
     641                                'Test string',
     642                                'custom_type',
     643                                true,
     644                        ),
     645                );
     646        }
     647}
  • tests/phpunit/tests/rest-api/rest-autosaves-controller.php

    diff --git tests/phpunit/tests/rest-api/rest-autosaves-controller.php tests/phpunit/tests/rest-api/rest-autosaves-controller.php
    index fd8f92f7b0..4108d05efc 100644
    class WP_Test_REST_Autosaves_Controller extends WP_Test_REST_Post_Type_Controlle 
    215215                        'author',
    216216                        'date',
    217217                        'date_gmt',
     218                        'id',
     219                        'meta',
    218220                        'modified',
    219221                        'modified_gmt',
    220                         'guid',
    221                         'id',
    222222                        'parent',
    223223                        'slug',
     224                        'guid',
    224225                        'title',
    225226                        'excerpt',
    226227                        'content',
    class WP_Test_REST_Autosaves_Controller extends WP_Test_REST_Post_Type_Controlle 
    288289                $response   = rest_get_server()->dispatch( $request );
    289290                $data       = $response->get_data();
    290291                $properties = $data['schema']['properties'];
    291                 $this->assertCount( 13, $properties );
     292                $this->assertCount( 14, $properties );
    292293                $this->assertArrayHasKey( 'author', $properties );
    293294                $this->assertArrayHasKey( 'content', $properties );
    294295                $this->assertArrayHasKey( 'date', $properties );
    class WP_Test_REST_Autosaves_Controller extends WP_Test_REST_Post_Type_Controlle 
    302303                $this->assertArrayHasKey( 'slug', $properties );
    303304                $this->assertArrayHasKey( 'title', $properties );
    304305                $this->assertArrayHasKey( 'preview_link', $properties );
     306                $this->assertArrayHasKey( 'meta', $properties );
    305307        }
    306308
    307309        public function test_create_item() {
  • tests/phpunit/tests/rest-api/rest-global-styles-revisions-controller.php

    diff --git tests/phpunit/tests/rest-api/rest-global-styles-revisions-controller.php tests/phpunit/tests/rest-api/rest-global-styles-revisions-controller.php
    index 9e580d5f3c..30e5b983ea 100644
    class WP_REST_Global_Styles_Revisions_Controller_Test extends WP_Test_REST_Contr 
    132132                        ),
    133133                );
    134134
    135                 wp_update_post( $new_styles_post, true, false );
     135                wp_update_post( $new_styles_post, true );
    136136
    137137                $new_styles_post = array(
    138138                        'ID'           => self::$global_styles_id,
    class WP_REST_Global_Styles_Revisions_Controller_Test extends WP_Test_REST_Contr 
    162162                        ),
    163163                );
    164164
    165                 wp_update_post( $new_styles_post, true, false );
     165                wp_update_post( $new_styles_post, true );
    166166
    167167                $new_styles_post = array(
    168168                        'ID'           => self::$global_styles_id,
    class WP_REST_Global_Styles_Revisions_Controller_Test extends WP_Test_REST_Contr 
    192192                        ),
    193193                );
    194194
    195                 wp_update_post( $new_styles_post, true, false );
     195                wp_update_post( $new_styles_post, true );
    196196                wp_set_current_user( 0 );
    197197        }
    198198
    class WP_REST_Global_Styles_Revisions_Controller_Test extends WP_Test_REST_Contr 
    326326                        'post_content' => wp_json_encode( $config ),
    327327                );
    328328
    329                 wp_update_post( $updated_styles_post, true, false );
     329                wp_update_post( $updated_styles_post, true );
    330330
    331331                $request  = new WP_REST_Request( 'GET', '/wp/v2/global-styles/' . self::$global_styles_id . '/revisions' );
    332332                $response = rest_get_server()->dispatch( $request );
  • tests/phpunit/tests/rest-api/rest-post-meta-fields.php

    diff --git tests/phpunit/tests/rest-api/rest-post-meta-fields.php tests/phpunit/tests/rest-api/rest-post-meta-fields.php
    index 2142e80582..b32ebaab80 100644
    class WP_Test_REST_Post_Meta_Fields extends WP_Test_REST_TestCase { 
    1717                        'cpt',
    1818                        array(
    1919                                'show_in_rest' => true,
    20                                 'supports'     => array( 'custom-fields' ),
     20                                'supports'     => array( 'custom-fields', 'revisions' ),
    2121                        )
    2222                );
    2323
    class WP_Test_REST_Post_Meta_Fields extends WP_Test_REST_TestCase { 
    157157                        'cpt',
    158158                        array(
    159159                                'show_in_rest' => true,
    160                                 'supports'     => array( 'custom-fields' ),
     160                                'supports'     => array( 'custom-fields', 'revisions' ),
    161161                        )
    162162                );
    163163
    class WP_Test_REST_Post_Meta_Fields extends WP_Test_REST_TestCase { 
    13761376         * @dataProvider data_update_value_return_success_with_same_value
    13771377         */
    13781378        public function test_update_value_return_success_with_same_value( $meta_key, $meta_value ) {
    1379                 add_post_meta( self::$post_id, $meta_key, $meta_value );
    1380 
    13811379                $this->grant_write_permission();
    13821380
    13831381                $data = array(
    class WP_Test_REST_Post_Meta_Fields extends WP_Test_REST_TestCase { 
    13921390                $response = rest_get_server()->dispatch( $request );
    13931391
    13941392                $this->assertSame( 200, $response->get_status() );
     1393
     1394                // Verify the returned meta value is correct.
     1395                $data = $response->get_data();
     1396                $this->assertArrayHasKey( 'meta', $data );
     1397                $this->assertArrayHasKey( $meta_key, $data['meta'] );
     1398                $this->assertSame( $meta_value, $data['meta'][ $meta_key ] );
     1399
    13951400        }
    13961401
    13971402        public function data_update_value_return_success_with_same_value() {
    class WP_Test_REST_Post_Meta_Fields extends WP_Test_REST_TestCase { 
    31123117                }
    31133118                return $query;
    31143119        }
     3120
     3121
     3122        /**
     3123         * Test that single post meta is revisioned when saving to the posts REST API endpoint.
     3124         *
     3125         * @ticket 20564
     3126         */
     3127        public function test_revisioned_single_post_meta_with_posts_endpoint() {
     3128                $this->grant_write_permission();
     3129
     3130                register_post_meta(
     3131                        'post',
     3132                        'foo',
     3133                        array(
     3134                                'single'            => true,
     3135                                'show_in_rest'      => true,
     3136                                'revisions_enabled' => true,
     3137                        )
     3138                );
     3139
     3140                $post_id = self::$post_id;
     3141
     3142                // Update the post, saving the meta.
     3143                $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', $post_id ) );
     3144                $request->set_body_params(
     3145                        array(
     3146                                'title' => 'Revision 1',
     3147                                'meta'  => array(
     3148                                        'foo' => 'bar',
     3149                                ),
     3150                        )
     3151                );
     3152                $response = rest_get_server()->dispatch( $request );
     3153                $this->assertSame( 200, $response->get_status() );
     3154
     3155                // Get the last revision.
     3156                $revisions   = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) );
     3157                $revision_id = array_shift( $revisions )->ID;
     3158
     3159                // @todo Ensure the revisions endpoint returns the correct meta values
     3160                // Check that the revisions endpoint returns the correct meta value.
     3161                $request  = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d/revisions/%d', $post_id, $revision_id ) );
     3162                $response = rest_get_server()->dispatch( $request );
     3163                $this->assertSame( 200, $response->get_status() );
     3164                $data = $response->get_data();
     3165                $this->assertSame( 'bar', $response->get_data()['meta']['foo'] );
     3166
     3167                // Check that the post meta is set correctly.
     3168                $this->assertSame( 'bar', get_post_meta( $revision_id, 'foo', true ) );
     3169
     3170                // Create two more revisions with different meta values for the foo key.
     3171                $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', $post_id ) );
     3172                $request->set_body_params(
     3173                        array(
     3174                                'title' => 'Revision 2',
     3175                                'meta'  => array(
     3176                                        'foo' => 'baz',
     3177                                ),
     3178                        )
     3179                );
     3180                $response = rest_get_server()->dispatch( $request );
     3181                $this->assertSame( 200, $response->get_status() );
     3182
     3183                // Get the last revision.
     3184                $revisions     = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) );
     3185                $revision_id_2 = array_shift( $revisions )->ID;
     3186
     3187                // Check that the revision has the correct meta value.
     3188                $request  = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d/revisions/%d', $post_id, $revision_id_2 ) );
     3189                $response = rest_get_server()->dispatch( $request );
     3190                $this->assertSame( 200, $response->get_status() );
     3191                $this->assertSame( 'baz', $response->get_data()['meta']['foo'] );
     3192
     3193                // Check that the post meta is set correctly.
     3194                $this->assertSame( 'baz', get_post_meta( $revision_id_2, 'foo', true ) );
     3195
     3196                // One more revision!
     3197                $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', $post_id ) );
     3198                $request->set_body_params(
     3199                        array(
     3200                                'title' => 'Revision 3',
     3201                                'meta'  => array(
     3202                                        'foo' => 'qux',
     3203                                ),
     3204                        )
     3205                );
     3206                $response = rest_get_server()->dispatch( $request );
     3207                $this->assertSame( 200, $response->get_status() );
     3208
     3209                // Get the last revision.
     3210                $revisions     = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) );
     3211                $revision_id_3 = array_shift( $revisions )->ID;
     3212
     3213                // Check that the revision has the correct meta value.
     3214                $request  = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d/revisions/%d', $post_id, $revision_id_3 ) );
     3215                $response = rest_get_server()->dispatch( $request );
     3216                $this->assertSame( 200, $response->get_status() );
     3217                $this->assertSame( 'qux', $response->get_data()['meta']['foo'] );
     3218
     3219                // Check that the post meta is set correctly.
     3220                $this->assertSame( 'qux', get_post_meta( $revision_id_3, 'foo', true ) );
     3221
     3222                // Restore Revision 3 and verify the post gets the correct meta value.
     3223                wp_restore_post_revision( $revision_id_3 );
     3224                $this->assertSame( 'qux', get_post_meta( $post_id, 'foo', true ) );
     3225
     3226                // Restore Revision 2 and verify the post gets the correct meta value.
     3227                wp_restore_post_revision( $revision_id_2 );
     3228                $this->assertSame( 'baz', get_post_meta( $post_id, 'foo', true ) );
     3229        }
     3230
     3231        /**
     3232         * Test that multi-post meta is revisioned when saving to the posts REST API endpoint.
     3233         *
     3234         * @ticket 20564
     3235         */
     3236        public function test_revisioned_multiple_post_meta_with_posts_endpoint() {
     3237                $this->grant_write_permission();
     3238
     3239                register_post_meta(
     3240                        'post',
     3241                        'foo',
     3242                        array(
     3243                                'single'            => false,
     3244                                'show_in_rest'      => true,
     3245                                'revisions_enabled' => true,
     3246                        )
     3247                );
     3248
     3249                $post_id = self::$post_id;
     3250
     3251                $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', $post_id ) );
     3252                $request->set_body_params(
     3253                        array(
     3254                                'title' => 'Revision 1',
     3255                                'meta'  => array(
     3256                                        'foo' => array(
     3257                                                'bar',
     3258                                                'bat',
     3259                                                'baz',
     3260                                        ),
     3261                                ),
     3262                        )
     3263                );
     3264                $response = rest_get_server()->dispatch( $request );
     3265                $this->assertSame( 200, $response->get_status() );
     3266
     3267                // Log the current post meta.
     3268                $meta = get_post_meta( $post_id );
     3269
     3270                // Update the post.
     3271                $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', $post_id ) );
     3272                $request->set_body_params(
     3273                        array(
     3274                                'title' => 'Revision 1 update',
     3275                        )
     3276                );
     3277                $response = rest_get_server()->dispatch( $request );
     3278                $this->assertSame( 200, $response->get_status() );
     3279
     3280                // Get the last revision.
     3281                $revisions     = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) );
     3282                $revision_id_1 = array_shift( $revisions )->ID;
     3283
     3284                // Check that the revision has the correct meta value.
     3285                $request  = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d/revisions/%d', $post_id, $revision_id_1 ) );
     3286                $response = rest_get_server()->dispatch( $request );
     3287                $this->assertSame( 200, $response->get_status() );
     3288
     3289                $this->assertSame(
     3290                        array( 'bar', 'bat', 'baz' ),
     3291                        $response->get_data()['meta']['foo']
     3292                );
     3293                $this->assertSame(
     3294                        array( 'bar', 'bat', 'baz' ),
     3295                        get_post_meta( $revision_id_1, 'foo' )
     3296                );
     3297
     3298                $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', $post_id ) );
     3299                $request->set_body_params(
     3300                        array(
     3301                                'title' => 'Revision 2',
     3302                                'meta'  => array(
     3303                                        'foo' => array(
     3304                                                'car',
     3305                                                'cat',
     3306                                        ),
     3307                                ),
     3308                        )
     3309                );
     3310                $response = rest_get_server()->dispatch( $request );
     3311                $this->assertSame( 200, $response->get_status() );
     3312
     3313                // Get the last revision.
     3314                $revisions     = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) );
     3315                $revision_id_2 = array_shift( $revisions )->ID;
     3316
     3317                // Check that the revision has the correct meta value.
     3318                $request  = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d/revisions/%d', $post_id, $revision_id_2 ) );
     3319                $response = rest_get_server()->dispatch( $request );
     3320                $this->assertSame( 200, $response->get_status() );
     3321
     3322                $this->assertSame(
     3323                        array( 'car', 'cat' ),
     3324                        $response->get_data()['meta']['foo']
     3325                );
     3326                $this->assertSame( array( 'car', 'cat' ), get_post_meta( $revision_id_2, 'foo' ) );
     3327
     3328                $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', $post_id ) );
     3329                $request->set_body_params(
     3330                        array(
     3331                                'title' => 'Revision 3',
     3332                                'meta'  => array(
     3333                                        'foo' => null,
     3334                                ),
     3335                        )
     3336                );
     3337                $response = rest_get_server()->dispatch( $request );
     3338                $this->assertSame( 200, $response->get_status() );
     3339
     3340                // Get the last revision.
     3341                $revisions     = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) );
     3342                $revision_id_3 = array_shift( $revisions )->ID;
     3343
     3344                // Check that the revision has the correct meta value.
     3345                $request  = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d/revisions/%d', $post_id, $revision_id_3 ) );
     3346                $response = rest_get_server()->dispatch( $request );
     3347                $this->assertSame( 200, $response->get_status() );
     3348
     3349                $this->assertSame(
     3350                        array(),
     3351                        $response->get_data()['meta']['foo']
     3352                );
     3353                $this->assertSame( array(), get_post_meta( $revision_id_3, 'foo' ) );
     3354
     3355                // Restore Revision 3 and verify the post gets the correct meta value.
     3356                wp_restore_post_revision( $revision_id_3 );
     3357                $this->assertSame( array(), get_post_meta( $post_id, 'foo' ) );
     3358
     3359                // Restore Revision 2 and verify the post gets the correct meta value.
     3360                wp_restore_post_revision( $revision_id_2 );
     3361                $this->assertSame( array( 'car', 'cat' ), get_post_meta( $post_id, 'foo' ) );
     3362        }
     3363
     3364        /**
     3365         * Test post meta revisions with a custom post type and the page post type.
     3366         *
     3367         * @group revision
     3368         * @dataProvider test_revisioned_single_post_meta_with_posts_endpoint_page_and_cpt_data_provider
     3369         */
     3370        public function test_revisioned_single_post_meta_with_posts_endpoint_page_and_cpt( $passed, $expected, $post_type ) {
     3371
     3372                $this->grant_write_permission();
     3373
     3374                // Create the custom meta.
     3375                register_post_meta(
     3376                        $post_type,
     3377                        'foo',
     3378                        array(
     3379                                'show_in_rest'      => true,
     3380                                'revisions_enabled' => true,
     3381                                'single'            => true,
     3382                                'type'              => 'string',
     3383                        )
     3384                );
     3385
     3386                // Set up a new post.
     3387                $post_id = $this->factory->post->create(
     3388                        array(
     3389                                'post_content' => 'initial content',
     3390                                'post_type'    => $post_type,
     3391                                'meta_input'   => array(
     3392                                        'foo' => 'foo',
     3393                                ),
     3394                        )
     3395                );
     3396
     3397                $plural_mapping = array(
     3398                        'page' => 'pages',
     3399                        'cpt'  => 'cpt',
     3400                );
     3401                $request        = new WP_REST_Request( 'GET', sprintf( '/wp/v2/%s', $plural_mapping[ $post_type ] ) );
     3402
     3403                $response = rest_get_server()->dispatch( $request );
     3404
     3405                $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/%s/%d', $plural_mapping[ $post_type ], $post_id ) );
     3406                $request->set_body_params(
     3407                        array(
     3408                                'title' => 'Revision 1',
     3409                                'meta'  => array(
     3410                                        'foo' => $passed,
     3411                                ),
     3412                        )
     3413                );
     3414
     3415                $response = rest_get_server()->dispatch( $request );
     3416                $this->assertSame( 200, $response->get_status() );
     3417
     3418                // Update the post.
     3419                $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/%s/%d', $plural_mapping[ $post_type ], $post_id ) );
     3420                $request->set_body_params(
     3421                        array(
     3422                                'title' => 'Revision 1 update',
     3423                        )
     3424                );
     3425                $response = rest_get_server()->dispatch( $request );
     3426                $this->assertSame( 200, $response->get_status() );
     3427
     3428                // Get the last revision.
     3429                $revisions = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) );
     3430
     3431                $revision_id_1 = array_shift( $revisions )->ID;
     3432
     3433                // Check that the revision has the correct meta value.
     3434                $request  = new WP_REST_Request( 'GET', sprintf( '/wp/v2/%s/%d/revisions/%d', $plural_mapping[ $post_type ], $post_id, $revision_id_1 ) );
     3435                $response = rest_get_server()->dispatch( $request );
     3436                $this->assertSame( 200, $response->get_status() );
     3437
     3438                $this->assertSame(
     3439                        $passed,
     3440                        $response->get_data()['meta']['foo']
     3441                );
     3442
     3443                $this->assertSame(
     3444                        array( $passed ),
     3445                        get_post_meta( $revision_id_1, 'foo' )
     3446                );
     3447
     3448                unregister_post_meta( $post_type, 'foo' );
     3449                wp_delete_post( $post_id, true );
     3450        }
     3451
     3452        /**
     3453         * Provide data for the meta revision checks.
     3454         */
     3455        public function test_revisioned_single_post_meta_with_posts_endpoint_page_and_cpt_data_provider() {
     3456                return array(
     3457                        array(
     3458                                'Test string',
     3459                                'Test string',
     3460                                'cpt',
     3461                        ),
     3462                        array(
     3463                                'Test string',
     3464                                'Test string',
     3465                                'page',
     3466                        ),
     3467                        array(
     3468                                'Test string',
     3469                                false,
     3470                                'cpt',
     3471                        ),
     3472
     3473                );
     3474        }
    31153475}
  • tests/phpunit/tests/rest-api/rest-revisions-controller.php

    diff --git tests/phpunit/tests/rest-api/rest-revisions-controller.php tests/phpunit/tests/rest-api/rest-revisions-controller.php
    index e0b713c882..74cd040d85 100644
    class WP_Test_REST_Revisions_Controller extends WP_Test_REST_Controller_Testcase 
    179179                        'modified_gmt',
    180180                        'guid',
    181181                        'id',
     182                        'meta',
    182183                        'parent',
    183184                        'slug',
    184185                        'title',
    class WP_Test_REST_Revisions_Controller extends WP_Test_REST_Controller_Testcase 
    335336                $response   = rest_get_server()->dispatch( $request );
    336337                $data       = $response->get_data();
    337338                $properties = $data['schema']['properties'];
    338                 $this->assertCount( 12, $properties );
     339                $this->assertCount( 13, $properties );
    339340                $this->assertArrayHasKey( 'author', $properties );
    340341                $this->assertArrayHasKey( 'content', $properties );
    341342                $this->assertArrayHasKey( 'date', $properties );
    class WP_Test_REST_Revisions_Controller extends WP_Test_REST_Controller_Testcase 
    348349                $this->assertArrayHasKey( 'parent', $properties );
    349350                $this->assertArrayHasKey( 'slug', $properties );
    350351                $this->assertArrayHasKey( 'title', $properties );
     352                $this->assertArrayHasKey( 'meta', $properties );
    351353        }
    352354
    353355        public function test_create_item() {
  • tests/phpunit/tests/user/wpRegisterPersistedPreferencesMeta.php

    diff --git tests/phpunit/tests/user/wpRegisterPersistedPreferencesMeta.php tests/phpunit/tests/user/wpRegisterPersistedPreferencesMeta.php
    index 9c4725ffbf..8af6ae9b81 100644
    class Tests_User_WpRegisterPersistedPreferencesMeta extends WP_UnitTestCase { 
    5252                                                'additionalProperties' => true,
    5353                                        ),
    5454                                ),
     55                                'revisions_enabled' => false,
    5556                        ),
    5657                        $wp_meta_keys['user'][''][ $meta_key ],
    5758                        'The registered metadata did not have the expected structure'
  • tests/qunit/fixtures/wp-api-generated.js

    diff --git tests/qunit/fixtures/wp-api-generated.js tests/qunit/fixtures/wp-api-generated.js
    index 27aa52edcc..acd8d3e694 100644
    mockedApiResponse.postRevisions = [ 
    1141711417        "excerpt": {
    1141811418            "rendered": ""
    1141911419        },
     11420        "meta": {
     11421            "meta_key": ""
     11422        },
    1142011423        "_links": {
    1142111424            "parent": [
    1142211425                {
    mockedApiResponse.postRevisions = [ 
    1144611449        "excerpt": {
    1144711450            "rendered": "<p>REST API Client Fixture: Post</p>\n"
    1144811451        },
     11452        "meta": {
     11453            "meta_key": ""
     11454        },
    1144911455        "_links": {
    1145011456            "parent": [
    1145111457                {
    mockedApiResponse.revision = { 
    1147611482    },
    1147711483    "excerpt": {
    1147811484        "rendered": "<p>REST API Client Fixture: Post</p>\n"
     11485    },
     11486    "meta": {
     11487        "meta_key": ""
    1147911488    }
    1148011489};
    1148111490
    mockedApiResponse.postAutosaves = [ 
    1150111510        "excerpt": {
    1150211511            "rendered": ""
    1150311512        },
     11513        "meta": {
     11514            "meta_key": ""
     11515        },
    1150411516        "_links": {
    1150511517            "parent": [
    1150611518                {
    mockedApiResponse.autosave = { 
    1153111543    },
    1153211544    "excerpt": {
    1153311545        "rendered": ""
     11546    },
     11547    "meta": {
     11548        "meta_key": ""
    1153411549    }
    1153511550};
    1153611551
    mockedApiResponse.pageRevisions = [ 
    1167711692        "excerpt": {
    1167811693            "rendered": ""
    1167911694        },
     11695        "meta": {
     11696            "meta_key": ""
     11697        },
    1168011698        "_links": {
    1168111699            "parent": [
    1168211700                {
    mockedApiResponse.pageRevisions = [ 
    1170611724        "excerpt": {
    1170711725            "rendered": "<p>REST API Client Fixture: Page</p>\n"
    1170811726        },
     11727        "meta": {
     11728            "meta_key": ""
     11729        },
    1170911730        "_links": {
    1171011731            "parent": [
    1171111732                {
    mockedApiResponse.pageRevision = { 
    1173611757    },
    1173711758    "excerpt": {
    1173811759        "rendered": "<p>REST API Client Fixture: Page</p>\n"
     11760    },
     11761    "meta": {
     11762        "meta_key": ""
    1173911763    }
    1174011764};
    1174111765
    mockedApiResponse.pageAutosaves = [ 
    1176111785        "excerpt": {
    1176211786            "rendered": ""
    1176311787        },
     11788        "meta": {
     11789            "meta_key": ""
     11790        },
    1176411791        "_links": {
    1176511792            "parent": [
    1176611793                {
    mockedApiResponse.pageAutosave = { 
    1179111818    },
    1179211819    "excerpt": {
    1179311820        "rendered": ""
     11821    },
     11822    "meta": {
     11823        "meta_key": ""
    1179411824    }
    1179511825};
    1179611826