Make WordPress Core

Ticket #20564: 20564.24.diff

File 20564.24.diff, 48.7 KB (added by adamsilverstein, 20 months ago)
  • src/wp-admin/includes/post.php

    diff --git src/wp-admin/includes/post.php src/wp-admin/includes/post.php
    index ed73b8ff5f..4bc92d1e4c 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( $revision );
     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 Post object $new_autosave The new post 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        /*
     2003         * Go thru the revisioned meta keys and save them as part of the autosave, if
     2004         * the meta key is part of the posted data, the meta value is not blank and
     2005         * the the meta value has changes from the last autosaved value.
     2006         */
     2007        foreach ( wp_post_revision_meta_keys() as $meta_key ) {
     2008
     2009                if (
     2010                isset( $posted_data[ $meta_key ] ) &&
     2011                get_post_meta( $new_autosave['ID'], $meta_key, true ) !== wp_unslash( $posted_data[ $meta_key ] )
     2012                ) {
     2013                        /*
     2014                         * Use the underlying delete_metadata() and add_metadata() functions
     2015                         * vs delete_post_meta() and add_post_meta() to make sure we're working
     2016                         * with the actual revision meta.
     2017                         */
     2018                        delete_metadata( 'post', $new_autosave['ID'], $meta_key );
     2019
     2020                        /*
     2021                         * One last check to ensure meta value not empty().
     2022                         */
     2023                        if ( ! empty( $posted_data[ $meta_key ] ) ) {
     2024                                /*
     2025                                 * Add the revisions meta data to the autosave.
     2026                                 */
     2027                                add_metadata( 'post', $new_autosave['ID'], $meta_key, $posted_data[ $meta_key ] );
     2028                        }
     2029                }
     2030        }
    19732031}
    19742032
    19752033/**
  • src/wp-includes/blocks/footnotes.php

    diff --git src/wp-includes/blocks/footnotes.php src/wp-includes/blocks/footnotes.php
    index bd7734d7d0..5f57890956 100644
    function register_block_core_footnotes() { 
    6868                        $post_type,
    6969                        'footnotes',
    7070                        array(
    71                                 'show_in_rest' => true,
    72                                 'single'       => true,
    73                                 'type'         => 'string',
     71                                'show_in_rest'      => true,
     72                                'single'            => true,
     73                                'type'              => 'string',
     74                                'revisions_enabled' => true,
    7475                        )
    7576                );
    7677        }
    function register_block_core_footnotes() { 
    8384}
    8485add_action( 'init', 'register_block_core_footnotes' );
    8586
    86 /**
    87  * Saves the footnotes meta value to the revision.
    88  *
    89  * @since 6.3.0
    90  *
    91  * @param int $revision_id The revision ID.
    92  */
    93 function wp_save_footnotes_meta( $revision_id ) {
    94         $post_id = wp_is_post_revision( $revision_id );
    95 
    96         if ( $post_id ) {
    97                 $footnotes = get_post_meta( $post_id, 'footnotes', true );
    98 
    99                 if ( $footnotes ) {
    100                         // Can't use update_post_meta() because it doesn't allow revisions.
    101                         update_metadata( 'post', $revision_id, 'footnotes', $footnotes );
    102                 }
    103         }
    104 }
    105 add_action( 'wp_after_insert_post', 'wp_save_footnotes_meta' );
    106 
    107 /**
    108  * Keeps track of the revision ID for "rest_after_insert_{$post_type}".
    109  *
    110  * @since 6.3.0
    111  *
    112  * @global int $wp_temporary_footnote_revision_id The footnote revision ID.
    113  *
    114  * @param int $revision_id The revision ID.
    115  */
    116 function wp_keep_footnotes_revision_id( $revision_id ) {
    117         global $wp_temporary_footnote_revision_id;
    118         $wp_temporary_footnote_revision_id = $revision_id;
    119 }
    120 add_action( '_wp_put_post_revision', 'wp_keep_footnotes_revision_id' );
    121 
    122 /**
    123  * This is a specific fix for the REST API. The REST API doesn't update
    124  * the post and post meta in one go (through `meta_input`). While it
    125  * does fix the `wp_after_insert_post` hook to be called correctly after
    126  * updating meta, it does NOT fix hooks such as post_updated and
    127  * save_post, which are normally also fired after post meta is updated
    128  * in `wp_insert_post()`. Unfortunately, `wp_save_post_revision` is
    129  * added to the `post_updated` action, which means the meta is not
    130  * available at the time, so we have to add it afterwards through the
    131  * `"rest_after_insert_{$post_type}"` action.
    132  *
    133  * @since 6.3.0
    134  *
    135  * @global int $wp_temporary_footnote_revision_id The footnote revision ID.
    136  *
    137  * @param WP_Post $post The post object.
    138  */
    139 function wp_add_footnotes_revisions_to_post_meta( $post ) {
    140         global $wp_temporary_footnote_revision_id;
    141 
    142         if ( $wp_temporary_footnote_revision_id ) {
    143                 $revision = get_post( $wp_temporary_footnote_revision_id );
    144 
    145                 if ( ! $revision ) {
    146                         return;
    147                 }
    148 
    149                 $post_id = $revision->post_parent;
    150 
    151                 // Just making sure we're updating the right revision.
    152                 if ( $post->ID === $post_id ) {
    153                         $footnotes = get_post_meta( $post_id, 'footnotes', true );
    154 
    155                         if ( $footnotes ) {
    156                                 // Can't use update_post_meta() because it doesn't allow revisions.
    157                                 update_metadata( 'post', $wp_temporary_footnote_revision_id, 'footnotes', $footnotes );
    158                         }
    159                 }
    160         }
    161 }
    162 
    163 foreach ( array( 'post', 'page' ) as $post_type ) {
    164         add_action( "rest_after_insert_{$post_type}", 'wp_add_footnotes_revisions_to_post_meta' );
    165 }
    166 
    167 /**
    168  * Restores the footnotes meta value from the revision.
    169  *
    170  * @since 6.3.0
    171  *
    172  * @param int $post_id     The post ID.
    173  * @param int $revision_id The revision ID.
    174  */
    175 function wp_restore_footnotes_from_revision( $post_id, $revision_id ) {
    176         $footnotes = get_post_meta( $revision_id, 'footnotes', true );
    177 
    178         if ( $footnotes ) {
    179                 update_post_meta( $post_id, 'footnotes', $footnotes );
    180         } else {
    181                 delete_post_meta( $post_id, 'footnotes' );
    182         }
    183 }
    184 add_action( 'wp_restore_post_revision', 'wp_restore_footnotes_from_revision', 10, 2 );
    185 
    18687/**
    18788 * Adds the footnotes field to the revision.
    18889 *
    add_filter( '_wp_post_revision_fields', 'wp_add_footnotes_to_revision' ); 
    209110 * @return string The field value.
    210111 */
    211112function wp_get_footnotes_from_revision( $revision_field, $field, $revision ) {
    212         return get_metadata( 'post', $revision->ID, $field, true );
     113        $footnotes = json_decode( get_metadata( 'post', $revision->ID, $field, true ) );
     114        if ( empty( $footnotes ) ) {
     115                return $revision_field;
     116        }
     117        $footnotes_html = '';
     118        $x = 1;
     119        foreach( $footnotes as $footnote ) {
     120                $footnotes_html .= sprintf(
     121                        "%s.  %s\n",
     122                        $x++,
     123                        $footnote->content
     124                );
     125        }
     126        return $footnotes_html;
    213127}
    214128add_filter( '_wp_post_revision_field_footnotes', 'wp_get_footnotes_from_revision', 10, 3 );
    215129
  • src/wp-includes/default-filters.php

    diff --git src/wp-includes/default-filters.php src/wp-includes/default-filters.php
    index bf39f29b4d..666b30368b 100644
    add_action( 'init', 'wp_register_persisted_preferences_meta' ); 
    719719// CPT wp_block custom postmeta field.
    720720add_action( 'init', 'wp_create_initial_post_meta' );
    721721
     722// Including revisioned meta when considering whether a post revision has changed.
     723add_filter( 'wp_save_post_revision_post_has_changed', 'wp_check_revisioned_meta_fields_have_changed', 10, 3 );
     724
     725// Save revisioned post meta immediately after a revision is saved
     726add_action( '_wp_put_post_revision', 'wp_save_revisioned_meta_fields', 10, 2 );
     727
    722728unset( $filter, $action );
  • src/wp-includes/meta.php

    diff --git src/wp-includes/meta.php src/wp-includes/meta.php
    index 0b9f08544e..f4ea49aa4d 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.
    13951397 * }
    13961398 * @param string|array $deprecated Deprecated. Use `$args` instead.
    13971399 * @return bool True if the meta key was successfully registered in the global array, false if not.
    function sanitize_meta( $meta_key, $meta_value, $object_type, $object_subtype = 
    14001402 */
    14011403function register_meta( $object_type, $meta_key, $args, $deprecated = null ) {
    14021404        global $wp_meta_keys;
     1405        global $wp_revisioned_meta_keys;
    14031406
    14041407        if ( ! is_array( $wp_meta_keys ) ) {
    14051408                $wp_meta_keys = array();
    14061409        }
    14071410
     1411        if ( ! is_array( $wp_revisioned_meta_keys ) ) {
     1412                $wp_revisioned_meta_keys = array();
     1413        }
     1414
    14081415        $defaults = array(
    14091416                'object_subtype'    => '',
    14101417                'type'              => 'string',
    function register_meta( $object_type, $meta_key, $args, $deprecated = null ) { 
    14141421                'sanitize_callback' => null,
    14151422                'auth_callback'     => null,
    14161423                'show_in_rest'      => false,
     1424                'revisions_enabled' => false,
    14171425        );
    14181426
    14191427        // There used to be individual args for sanitize and auth callbacks.
    function register_meta( $object_type, $meta_key, $args, $deprecated = null ) { 
    14591467                }
    14601468        }
    14611469
     1470        // Store the revisioned meta fields.
     1471        if ( isset( $args['revisions_enabled'] ) && $args['revisions_enabled'] && ! in_array( $meta_key, $wp_revisioned_meta_keys ) ) {
     1472                array_push( $wp_revisioned_meta_keys, $meta_key );
     1473        }
     1474
    14621475        $object_subtype = ! empty( $args['object_subtype'] ) ? $args['object_subtype'] : '';
    14631476
    14641477        // If `auth_callback` is not provided, fall back to `is_protected_meta()`.
  • src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php

    diff --git src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php
    index 4ec972347f..86a41b0e0d 100644
    class WP_REST_Posts_Controller extends WP_REST_Controller { 
    673673                        );
    674674                }
    675675
     676                // Don't save the post meta revision because they aren't stored yet.
     677                remove_action( '_wp_put_post_revision', 'wp_save_revisioned_meta_fields', 10, 2 );
     678
    676679                $post_id = wp_insert_post( wp_slash( (array) $prepared_post ), true, false );
    677680
    678681                if ( is_wp_error( $post_id ) ) {
    class WP_REST_Posts_Controller extends WP_REST_Controller { 
    743746                        }
    744747                }
    745748
     749                // Update any revisioned meta.
     750                $revisions = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) );
     751                if ( ! empty( $revisions ) ) {
     752                        $revision  = array_shift( $revisions );
     753                        wp_save_revisioned_meta_fields( $revision->ID, $post_id );
     754                }
     755
    746756                $post          = get_post( $post_id );
    747757                $fields_update = $this->update_additional_fields_for_object( $post, $request );
    748758
    class WP_REST_Posts_Controller extends WP_REST_Controller { 
    876886                        );
    877887                }
    878888
     889                // Don't save the post meta revision because they aren't stored yet.
     890                remove_action( '_wp_put_post_revision', 'wp_save_revisioned_meta_fields', 10, 2 );
     891
    879892                // Convert the post object to an array, otherwise wp_update_post() will expect non-escaped input.
    880893                $post_id = wp_update_post( wp_slash( (array) $post ), true, false );
    881894
    class WP_REST_Posts_Controller extends WP_REST_Controller { 
    928941                                return $meta_update;
    929942                        }
    930943                }
     944                // Update any revisioned meta.
     945                $revisions = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) );
     946                if ( ! empty( $revisions ) ) {
     947                        $revision  = array_shift( $revisions );
     948                        wp_save_revisioned_meta_fields( $revision->ID, $post_id );
     949                }
    931950
    932951                $post          = get_post( $post_id );
    933952                $fields_update = $this->update_additional_fields_for_object( $post, $request );
  • 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 6fde5f13f5..31e3d506ea 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_Revision_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_Revision_Meta_Fields( 'revision' );
    6372        }
    6473
    6574        /**
    class WP_REST_Revisions_Controller extends WP_REST_Controller { 
    619628                        );
    620629                }
    621630
     631                if ( rest_is_field_included( 'meta', $fields ) ) {
     632                        $data['meta'] = $this->meta->get_value( $post->ID, $request );
     633                }
     634
    622635                $context  = ! empty( $request['context'] ) ? $request['context'] : 'view';
    623636                $data     = $this->add_additional_fields_to_object( $data, $request );
    624637                $data     = $this->filter_response_by_context( $data, $context );
    class WP_REST_Revisions_Controller extends WP_REST_Controller { 
    752765                        $schema['properties']['guid'] = $parent_schema['properties']['guid'];
    753766                }
    754767
     768                $schema['properties']['meta'] = $this->meta->get_field_schema();
     769
    755770                $this->schema = $schema;
    756771
    757772                return $this->add_additional_fields_schema( $this->schema );
  • new file src/wp-includes/rest-api/fields/class-wp-rest-revision-meta-fields.php

    diff --git src/wp-includes/rest-api/fields/class-wp-rest-revision-meta-fields.php src/wp-includes/rest-api/fields/class-wp-rest-revision-meta-fields.php
    new file mode 100644
    index 0000000000..248e06e79b
    - +  
     1<?php
     2/**
     3 * REST API: WP_REST_Revision_Meta_Fields class
     4 *
     5 * @package WordPress
     6 * @subpackage REST_API
     7 * @since 6.4.0
     8 */
     9
     10/**
     11 * Core class used to manage meta values for revisions via the REST API.
     12 *
     13 * @since 6.4.0
     14 *
     15 * @see WP_REST_Meta_Fields
     16 */
     17class WP_REST_Revision_Meta_Fields extends WP_REST_Meta_Fields {
     18
     19        /**
     20         * Revision type to register fields for.
     21         *
     22         * @since 6.4.0
     23         * @var string
     24         */
     25        protected $post_type;
     26
     27        /**
     28         * Constructor.
     29         *
     30         * @since 6.4.0
     31         *
     32         * @param string $post_type Revision type to register fields for.
     33         */
     34        public function __construct( $post_type ) {
     35                $this->post_type = $post_type;
     36        }
     37
     38        /**
     39         * Retrieves the post meta type.
     40         *
     41         * @since 6.4.0
     42         *
     43         * @return string The meta type.
     44         */
     45        protected function get_meta_type() {
     46                return 'revision';
     47        }
     48
     49        /**
     50         * Retrieves the post meta subtype.
     51         *
     52         * @since 6.4.0
     53         *
     54         * @return string Subtype for the meta type, or empty string if no specific subtype.
     55         */
     56        protected function get_meta_subtype() {
     57                return $this->post_type;
     58        }
     59
     60        /**
     61         * Retrieves the type for register_rest_field().
     62         *
     63         * @since 6.4.0
     64         *
     65         * @see register_rest_field()
     66         *
     67         * @return string The REST field type.
     68         */
     69        public function get_rest_field_type() {
     70                return $this->post_type;
     71        }
     72
     73        /**
     74         * Retrieves the meta field value.
     75         *
     76         * @since 4.7.0
     77         *
     78         * @param int             $object_id Object ID to fetch meta for.
     79         * @param WP_REST_Request $request   Full details about the request.
     80         * @return array Array containing the meta values keyed by name.
     81         */
     82        public function get_value( $object_id, $request ) {
     83                $data = get_post_meta( $object_id );
     84                return $data;
     85        }
     86
     87        /**
     88         * Retrieves raw metadata value for the specified object.
     89         *
     90         * @since 5.5.0
     91         *
     92         * @param string $meta_type Type of object metadata is for. Accepts 'post', 'comment', 'term', 'user',
     93         *                          or any other object type with an associated meta table.
     94         * @param int    $object_id ID of the object metadata is for.
     95         * @param string $meta_key  Optional. Metadata key. If not specified, retrieve all metadata for
     96         *                          the specified object. Default empty string.
     97         * @param bool   $single    Optional. If true, return only the first value of the specified `$meta_key`.
     98         *                          This parameter has no effect if `$meta_key` is not specified. Default false.
     99         * @return mixed An array of values if `$single` is false.
     100         *               The value of the meta field if `$single` is true.
     101         *               False for an invalid `$object_id` (non-numeric, zero, or negative value),
     102         *               or if `$meta_type` is not specified.
     103         *               Null if the value does not exist.
     104         */
     105        function get_metadata_raw( $meta_type, $object_id, $meta_key = '', $single = false ) {
     106                $data = get_post_meta( $object_id, $request );
     107                return $data;
     108        }
     109
     110
     111}
  • src/wp-includes/revision.php

    diff --git src/wp-includes/revision.php src/wp-includes/revision.php
    index e8bbcdbce4..a5fe608bf6 100644
    function _wp_put_post_revision( $post = null, $autosave = false ) { 
    361361                 * Fires once a revision has been saved.
    362362                 *
    363363                 * @since 2.6.0
     364                 * @since 6.4.0 The post_id parameter was added.
    364365                 *
    365366                 * @param int $revision_id Post revision ID.
     367                 * @param int $post_id     Post ID.
    366368                 */
    367                 do_action( '_wp_put_post_revision', $revision_id );
     369                do_action( '_wp_put_post_revision', $revision_id, $post['post_parent'] );
    368370        }
    369371
    370372        return $revision_id;
    371373}
    372374
     375
     376/**
     377 * Save the revisioned meta fields.
     378 *
     379 * @param int $revision_id The ID of the revision to save the meta to.
     380 * @param int $post_id     The ID of the post the revision is associated with.
     381 *
     382 * @since 6.4.0
     383 */
     384function wp_save_revisioned_meta_fields( $revision_id, $post_id ) {
     385
     386        // Save revisioned meta fields.
     387        foreach ( wp_post_revision_meta_keys() as $meta_key ) {
     388                if ( metadata_exists( 'post', $post_id, $meta_key ) ) {
     389                        _wp_copy_post_meta( $post_id, $revision_id, $meta_key );
     390                }
     391        }
     392}
     393
    373394/**
    374395 * Gets a post revision.
    375396 *
    function wp_restore_post_revision( $revision, $fields = null ) { 
    450471        // Update last edit user.
    451472        update_post_meta( $post_id, '_edit_last', get_current_user_id() );
    452473
     474        // Restore any revisioned meta fields.
     475        wp_restore_post_revision_meta( $post_id, $revision['ID'] );
     476
    453477        /**
    454478         * Fires after a post revision has been restored.
    455479         *
    function wp_restore_post_revision( $revision, $fields = null ) { 
    463487        return $post_id;
    464488}
    465489
     490/**
     491 * Restore the revisioned meta values for a post.
     492 *
     493 * @param int $post_id     The ID of the post to restore the meta to.
     494 * @param int $revision_id The ID of the revision to restore the meta from.
     495 *
     496 * @since 6.4.0
     497 */
     498function wp_restore_post_revision_meta( $post_id, $revision_id ) {
     499
     500        // Restore revisioned meta fields.
     501        foreach ( (array) wp_post_revision_meta_keys() as $meta_key ) {
     502
     503                // Clear any existing meta.
     504                delete_post_meta( $post_id, $meta_key );
     505
     506                _wp_copy_post_meta( $revision_id, $post_id, $meta_key );
     507        }
     508}
     509
     510/**
     511 * Copy post meta for the given key from one post to another.
     512 *
     513 * @param int    $source_post_id Post ID to copy meta value(s) from.
     514 * @param int    $target_post_id Post ID to copy meta value(s) to.
     515 * @param string $meta_key       Meta key to copy.
     516 *
     517 * @since 6.4.0
     518 */
     519function _wp_copy_post_meta( $source_post_id, $target_post_id, $meta_key ) {
     520
     521        foreach ( get_post_meta( $source_post_id, $meta_key ) as $meta_value ) {
     522                /**
     523                 * We use add_metadata() function vs add_post_meta() here
     524                 * to allow for a revision post target OR regular post.
     525                 */
     526                add_metadata( 'post', $target_post_id, $meta_key, wp_slash( $meta_value ) );
     527        }
     528}
     529
     530/**
     531 * Determine which post meta fields should be revisioned.
     532 *
     533 * @since 6.4.0
     534 *
     535 * @return array An array of meta keys to be revisioned.
     536 */
     537function wp_post_revision_meta_keys() {
     538        global $wp_revisioned_meta_keys;
     539
     540        /**
     541         * Filter the list of post meta keys to be revisioned.
     542         *
     543         * @since 6.4.0
     544         *
     545         * @param array $keys An array of meta fields to be revisioned.
     546         */
     547        return apply_filters( 'wp_post_revision_meta_keys', $wp_revisioned_meta_keys );
     548}
     549
     550/**
     551 * Check whether revisioned post meta fields have changed.
     552 *
     553 * @param bool    $post_has_changed Whether the post has changed.
     554 * @param WP_Post $last_revision    The last revision post object.
     555 * @param WP_Post $post             The post object.
     556 *
     557 * @since 6.4.0
     558 */
     559function wp_check_revisioned_meta_fields_have_changed( $post_has_changed, WP_Post $last_revision, WP_Post $post ) {
     560        foreach ( wp_post_revision_meta_keys() as $meta_key ) {
     561                if ( get_post_meta( $post->ID, $meta_key ) !== get_post_meta( $last_revision->ID, $meta_key ) ) {
     562                        $post_has_changed = true;
     563                        break;
     564                }
     565        }
     566        return $post_has_changed;
     567}
     568
    466569/**
    467570 * Deletes a revision.
    468571 *
    function _set_preview( $post ) { 
    728831
    729832        add_filter( 'get_the_terms', '_wp_preview_terms_filter', 10, 3 );
    730833        add_filter( 'get_post_metadata', '_wp_preview_post_thumbnail_filter', 10, 3 );
     834        add_filter( 'get_post_metadata', '_wp_preview_meta_filter', 10, 4 );
    731835
    732836        return $post;
    733837}
    function _wp_upgrade_revisions_of_post( $post, $revisions ) { 
    9481052
    9491053        return true;
    9501054}
     1055
     1056/**
     1057 * Filters preview post meta retrieval to get values from the autosave.
     1058 *
     1059 * Filters revisioned meta keys only.
     1060 *
     1061 * @since 6.4.0
     1062 *
     1063 * @param mixed  $value     Meta value to filter.
     1064 * @param int    $object_id Object ID.
     1065 * @param string $meta_key  Meta key to filter a value for.
     1066 * @param bool   $single    Whether to return a single value. Default false.
     1067 * @return mixed Original meta value if the meta key isn't revisioned, the object doesn't exist,
     1068 *               the post type is a revision or the post ID doesn't match the object ID.
     1069 *               Otherwise, the revisioned meta value is returned for the preview.
     1070 */
     1071function _wp_preview_meta_filter( $value, $object_id, $meta_key, $single ) {
     1072
     1073        $post = get_post();
     1074        if (
     1075                empty( $post ) ||
     1076                $post->ID !== $object_id ||
     1077                ! in_array( $meta_key, wp_post_revision_meta_keys(), true ) ||
     1078                'revision' === $post->post_type
     1079        ) {
     1080                return $value;
     1081        }
     1082
     1083        $preview = wp_get_post_autosave( $post->ID );
     1084        if ( ! is_object( $preview ) ) {
     1085                return $value;
     1086        }
     1087
     1088        return get_post_meta( $preview->ID, $meta_key, $single );
     1089}
  • src/wp-settings.php

    diff --git src/wp-settings.php src/wp-settings.php
    index 2479420742..aec891e6b2 100644
    require ABSPATH . WPINC . '/rest-api/fields/class-wp-rest-comment-meta-fields.ph 
    306306require ABSPATH . WPINC . '/rest-api/fields/class-wp-rest-post-meta-fields.php';
    307307require ABSPATH . WPINC . '/rest-api/fields/class-wp-rest-term-meta-fields.php';
    308308require ABSPATH . WPINC . '/rest-api/fields/class-wp-rest-user-meta-fields.php';
     309require ABSPATH . WPINC . '/rest-api/fields/class-wp-rest-revision-meta-fields.php';
    309310require ABSPATH . WPINC . '/rest-api/search/class-wp-rest-search-handler.php';
    310311require ABSPATH . WPINC . '/rest-api/search/class-wp-rest-post-search-handler.php';
    311312require ABSPATH . WPINC . '/rest-api/search/class-wp-rest-term-search-handler.php';
  • tests/phpunit/tests/meta/registerMeta.php

    diff --git tests/phpunit/tests/meta/registerMeta.php tests/phpunit/tests/meta/registerMeta.php
    index ae2c1d6e63..9a127295b0 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..c132f01e40
    - +  
     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 two 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                remove_filter( 'wp_post_revision_meta_keys', array( $this, 'add_revisioned_keys' ) );
     85        }
     86
     87        /**
     88         * Provide data for the slashed data tests.
     89         */
     90        public function slashed_data_provider() {
     91                return array(
     92                        array(
     93                                'some\text',
     94                                'some\text',
     95                        ),
     96                        array(
     97                                'test some\ \\extra \\\slashed \\\\text ',
     98                                'test some\ \\extra \\\slashed \\\\text ',
     99                        ),
     100                        array(
     101                                "This \'is\' an example \n of a \"quoted\" string",
     102                                "This \'is\' an example \n of a \"quoted\" string",
     103                        ),
     104                        array(
     105                                'some unslashed text just to test! % & * ( ) #',
     106                                'some unslashed text just to test! % & * ( ) #',
     107                        ),
     108                );
     109        }
     110
     111        /**
     112         * Test the revisions system for storage of meta values.
     113         *
     114         * @group revision
     115         */
     116        public function test_revisions_stores_meta_values() {
     117                /*
     118                 * Set Up.
     119                 */
     120
     121                // Set up a new post.
     122                $post_id          = $this->factory->post->create();
     123                $original_post_id = $post_id;
     124
     125                // And update to store an initial revision.
     126                wp_update_post(
     127                        array(
     128                                'post_content' => 'some initial content',
     129                                'ID'           => $post_id,
     130                        )
     131                );
     132
     133                // One revision so far.
     134                $revisions = wp_get_post_revisions( $post_id );
     135                $this->assertCount( 1, $revisions );
     136
     137                /*
     138                 * First set up a meta value.
     139                 */
     140
     141                // Store a custom meta value, which is not revisioned by default.
     142                update_post_meta( $post_id, 'meta_revision_test', 'original' );
     143
     144                // Update the post, storing a revision.
     145                wp_update_post(
     146                        array(
     147                                'post_content' => 'some more content',
     148                                'ID'           => $post_id,
     149                        )
     150                );
     151
     152                $revisions = wp_get_post_revisions( $post_id );
     153                $this->assertCount( 2, $revisions );
     154
     155                // Next, store some updated meta values for the same key.
     156                update_post_meta( $post_id, 'meta_revision_test', 'update1' );
     157
     158                // Save the post, changing content to force a revision.
     159                wp_update_post(
     160                        array(
     161                                'post_content' => 'some updated content',
     162                                'ID'           => $post_id,
     163                        )
     164                );
     165
     166                $revisions = wp_get_post_revisions( $post_id );
     167                $this->assertCount( 3, $revisions );
     168
     169                /*
     170                 * Now restore the original revision.
     171                 */
     172
     173                // Restore the previous revision.
     174                $revisions = (array) wp_get_post_revisions( $post_id );
     175
     176                // Go back two to load the previous revision.
     177                array_shift( $revisions );
     178                $last_revision = array_shift( $revisions );
     179
     180                // Restore!
     181                wp_restore_post_revision( $last_revision->ID );
     182
     183                wp_update_post( array( 'ID' => $post_id ) );
     184                $revisions = wp_get_post_revisions( $post_id );
     185                $this->assertCount( 4, $revisions );
     186
     187                /*
     188                 * Check the meta values to verify they are NOT revisioned - they are not revisioned by default.
     189                 */
     190
     191                // Custom post meta should NOT be restored, orignal value should not be restored, value still 'update1'.
     192                $this->assertEquals( 'update1', get_post_meta( $post_id, 'meta_revision_test', true ) );
     193
     194                update_post_meta( $post_id, 'meta_revision_test', 'update2' );
     195
     196                /*
     197                 * Test the revisioning of custom meta when enabled by the wp_post_revision_meta_keys filter.
     198                 */
     199
     200                // Add the custom field to be revised via the wp_post_revision_meta_keys filter.
     201                add_filter( 'wp_post_revision_meta_keys', array( $this, 'add_revisioned_keys' ) );
     202
     203                // Save the post, changing content to force a revision.
     204                wp_update_post(
     205                        array(
     206                                'post_content' => 'more updated content',
     207                                'ID'           => $post_id,
     208                        )
     209                );
     210
     211                $revisions = array_values( wp_get_post_revisions( $post_id ) );
     212                $this->assertCount( 5, $revisions );
     213                $this->assertEquals( 'update2', get_post_meta( $revisions[0]->ID, 'meta_revision_test', true ) );
     214
     215                // Store custom meta values, which should now be revisioned.
     216                update_post_meta( $post_id, 'meta_revision_test', 'update3' );
     217
     218                /*
     219                 * Save the post again, custom meta should now be revisioned.
     220                 *
     221                 * Note that a revision is saved even though there is no change
     222                 * in post content, because the revisioned post_meta has changed.
     223                 */
     224                wp_update_post(
     225                        array(
     226                                'ID' => $post_id,
     227                        )
     228                );
     229
     230                // This revision contains the existing post meta ('update3').
     231                $revisions = wp_get_post_revisions( $post_id );
     232                $this->assertCount( 6, $revisions );
     233
     234                // Verify that previous post meta is set.
     235                $this->assertEquals( 'update3', get_post_meta( $post_id, 'meta_revision_test', true ) );
     236
     237                // Restore the previous revision.
     238                $revisions = wp_get_post_revisions( $post_id );
     239
     240                // Go back two to load the previous revision.
     241                array_shift( $revisions );
     242                $last_revision = array_shift( $revisions );
     243                wp_restore_post_revision( $last_revision->ID );
     244
     245                /*
     246                 * Verify that previous post meta is restored.
     247                 */
     248                $this->assertEquals( 'update2', get_post_meta( $post_id, 'meta_revision_test', true ) );
     249
     250                // Try storing a blank meta.
     251                update_post_meta( $post_id, 'meta_revision_test', '' );
     252                wp_update_post(
     253                        array(
     254                                'ID' => $post_id,
     255                        )
     256                );
     257
     258                update_post_meta( $post_id, 'meta_revision_test', 'update 4' );
     259                wp_update_post(
     260                        array(
     261                                'ID' => $post_id,
     262                        )
     263                );
     264
     265                // Restore the previous revision.
     266                $revisions = wp_get_post_revisions( $post_id );
     267                array_shift( $revisions );
     268                $last_revision = array_shift( $revisions );
     269                wp_restore_post_revision( $last_revision->ID );
     270
     271                /*
     272                 * Verify that previous blank post meta is restored.
     273                 */
     274                $this->assertEquals( '', get_post_meta( $post_id, 'meta_revision_test', true ) );
     275
     276                /*
     277                 * Test not tracking a key - remove the key from the revisioned meta.
     278                 */
     279                remove_all_filters( 'wp_post_revision_meta_keys' );
     280
     281                // Meta should no longer be revisioned.
     282                update_post_meta( $post_id, 'meta_revision_test', 'update 5' );
     283                wp_update_post(
     284                        array(
     285                                'ID'           => $post_id,
     286                                'post_content' => 'changed content',
     287                        )
     288                );
     289                update_post_meta( $post_id, 'meta_revision_test', 'update 6' );
     290                wp_update_post(
     291                        array(
     292                                'ID'           => $post_id,
     293                                'post_content' => 'go updated content',
     294                        )
     295                );
     296
     297                // Restore the previous revision.
     298                $revisions = wp_get_post_revisions( $post_id );
     299                array_shift( $revisions );
     300                $last_revision = array_shift( $revisions );
     301                wp_restore_post_revision( $last_revision->ID );
     302
     303                /*
     304                 * Verify that previous post meta is NOT restored.
     305                 */
     306                $this->assertEquals( 'update 6', get_post_meta( $post_id, 'meta_revision_test', true ) );
     307
     308                // Add the custom field to be revised via the wp_post_revision_meta_keys filter.
     309                add_filter( 'wp_post_revision_meta_keys', array( $this, 'add_revisioned_keys' ) );
     310
     311                /*
     312                 * Test the revisioning of multiple meta keys.
     313                 */
     314
     315                // Add three values for meta.
     316                update_post_meta( $post_id, 'meta_revision_test', 'update 7' );
     317                add_post_meta( $post_id, 'meta_revision_test', 'update 7 number 2' );
     318                add_post_meta( $post_id, 'meta_revision_test', 'update 7 number 3' );
     319                wp_update_post( array( 'ID' => $post_id ) );
     320
     321                // Update all three values.
     322                update_post_meta( $post_id, 'meta_revision_test', 'update 8', 'update 7' );
     323                update_post_meta( $post_id, 'meta_revision_test', 'update 8 number 2', 'update 7 number 2' );
     324                update_post_meta( $post_id, 'meta_revision_test', 'update 8 number 3', 'update 7 number 3' );
     325
     326                // Restore the previous revision.
     327                $revisions     = wp_get_post_revisions( $post_id );
     328                $last_revision = array_shift( $revisions );
     329                wp_restore_post_revision( $last_revision->ID );
     330
     331                /*
     332                 * Verify that multiple metas stored correctly.
     333                 */
     334                $this->assertEquals( array( 'update 7', 'update 7 number 2', 'update 7 number 3' ), get_post_meta( $post_id, 'meta_revision_test' ) );
     335
     336                /*
     337                 * Test the revisioning of a multidimensional array.
     338                 */
     339                $test_array = array(
     340                        'a' => array(
     341                                '1',
     342                                '2',
     343                                '3',
     344                        ),
     345                        'b' => 'ok',
     346                        'c' => array(
     347                                'multi' => array(
     348                                        'a',
     349                                        'b',
     350                                        'c',
     351                                ),
     352                                'not'   => 'ok',
     353                        ),
     354                );
     355
     356                // Clear any old value.
     357                delete_post_meta( $post_id, 'meta_revision_test' );
     358
     359                // Set the test meta to the array.
     360                update_post_meta( $post_id, 'meta_revision_test', $test_array );
     361
     362                // Update to save.
     363                wp_update_post( array( 'ID' => $post_id ) );
     364
     365                // Set the test meta blank.
     366                update_post_meta( $post_id, 'meta_revision_test', '' );
     367
     368                // Restore the previous revision.
     369                $revisions     = wp_get_post_revisions( $post_id );
     370                $last_revision = array_shift( $revisions );
     371                wp_restore_post_revision( $last_revision->ID );
     372
     373                /*
     374                 * Verify  multidimensional array stored correctly.
     375                 */
     376                $stored_array = get_post_meta( $post_id, 'meta_revision_test' );
     377                $this->assertEquals( $test_array, $stored_array[0] );
     378                /*
     379
     380                 * Test multiple revisions on the same key.
     381                 */
     382
     383                // Set the test meta to the array.
     384                add_post_meta( $post_id, 'meta_multiples_test', 'test1' );
     385                add_post_meta( $post_id, 'meta_multiples_test', 'test2' );
     386                add_post_meta( $post_id, 'meta_multiples_test', 'test3' );
     387
     388                // Update to save.
     389                wp_update_post( array( 'ID' => $post_id ) );
     390
     391                $stored_array = get_post_meta( $post_id, 'meta_multiples_test' );
     392                $expect       = array( 'test1', 'test2', 'test3' );
     393
     394                $this->assertEquals( $expect, $stored_array );
     395
     396                // Restore the previous revision.
     397                $revisions     = wp_get_post_revisions( $post_id );
     398                $last_revision = array_shift( $revisions );
     399                wp_restore_post_revision( $last_revision->ID );
     400
     401                $stored_array = get_post_meta( $post_id, 'meta_multiples_test' );
     402                $expect       = array( 'test1', 'test2', 'test3' );
     403
     404                $this->assertEquals( $expect, $stored_array );
     405
     406                // Cleanup!
     407                wp_delete_post( $original_post_id );
     408        }
     409
     410        /**
     411         * Verify that only existing meta is revisioned.
     412         */
     413        public function only_existing_meta_is_revisioned() {
     414                add_filter( 'wp_post_revision_meta_keys', array( $this, 'add_revisioned_keys' ) );
     415
     416                // Set up a new post.
     417                $post_id = $this->factory->post->create(
     418                        array(
     419                                'post_content' => 'initial content',
     420                        )
     421                );
     422
     423                // Revision v1.
     424                wp_update_post(
     425                        array(
     426                                'ID'           => $post_id,
     427                                'post_content' => 'updated content v1',
     428                        )
     429                );
     430
     431                $this->assertPostNotHasMetaKey( $post_id, 'foo' );
     432                $this->assertPostNotHasMetaKey( $post_id, 'bar' );
     433
     434                $revisions = wp_get_post_revisions( $post_id );
     435                $revision  = array_shift( $revisions );
     436                $this->assertEmpty( get_metadata( 'post', $revision->ID ) );
     437
     438                // Revision v2.
     439                wp_update_post(
     440                        array(
     441                                'ID'           => $post_id,
     442                                'post_content' => 'updated content v2',
     443                                'meta_input'   => array(
     444                                        'foo' => 'foo v2',
     445                                ),
     446                        )
     447                );
     448
     449                $this->assertPostHasMetaKey( $post_id, 'foo' );
     450                $this->assertPostNotHasMetaKey( $post_id, 'bar' );
     451                $this->assertPostNotHasMetaKey( $post_id, 'meta_revision_test' );
     452
     453                $revisions = wp_get_post_revisions( $post_id );
     454                $revision  = array_shift( $revisions );
     455                $this->assertPostHasMetaKey( $revision->ID, 'foo' );
     456                $this->assertPostNotHasMetaKey( $revision->ID, 'bar' );
     457                $this->assertPostNotHasMetaKey( $revision->ID, 'meta_revision_test' );
     458        }
     459
     460        /**
     461         * Verify that blank strings are revisioned correctly.
     462         */
     463        public function blank_meta_is_revisioned() {
     464
     465                add_filter( 'wp_post_revision_meta_keys', array( $this, 'add_revisioned_keys' ) );
     466
     467                // Set up a new post.
     468                $post_id = $this->factory->post->create(
     469                        array(
     470                                'post_content' => 'initial content',
     471                                'meta_input'   => array(
     472                                        'foo' => 'foo',
     473                                ),
     474                        )
     475                );
     476
     477                // Set the test meta to an empty string.
     478                update_post_meta( $post_id, 'foo', '' );
     479
     480                // Update to save.
     481                wp_update_post( array( 'ID' => $post_id ) );
     482
     483                $stored_array = get_post_meta( $post_id, 'meta_multiples_test' );
     484                $expect       = array( 'test1', 'test2', 'test3' );
     485
     486                $this->assertEquals( $expect, $stored_array );
     487
     488                // Restore the previous revision.
     489                $revisions     = wp_get_post_revisions( $post_id );
     490                $last_revision = array_shift( $revisions );
     491                wp_restore_post_revision( $last_revision->ID );
     492                $stored_data = get_post_meta( $post_id, 'foo' );
     493                $this->assertEquals( '', $stored_data[0] );
     494        }
     495
     496
     497        /**
     498         * @dataProvider data_register_post_meta_supports_revisions
     499         */
     500        public function test_register_post_meta_supports_revisions( $post_type, $meta_key, $args, $expected_is_revisioned ) {
     501                register_post_meta( $post_type, $meta_key, $args );
     502
     503                // Set up a new post.
     504                $post_id = $this->factory->post->create(
     505                        array(
     506                                'post_content' => 'initial content',
     507                                'post_type'    => $post_type,
     508                                'meta_input'   => array(
     509                                        $meta_key => 'foo',
     510                                ),
     511                        )
     512                );
     513
     514                // Update the post meta and post to save.
     515                update_post_meta( $post_id, $meta_key, 'bar' );
     516                wp_update_post(
     517                        array(
     518                                'ID'         => $post_id,
     519                                'post_title' => 'updated title',
     520                        )
     521                );
     522
     523                // Check the last revision for the post to see if the meta key was revisioned
     524                $revisions       = wp_get_post_revisions( $post_id );
     525                $revision        = array_shift( $revisions );
     526                $revisioned_meta = get_post_meta( $revision->ID, $meta_key, true );
     527                $this->assertEquals( $expected_is_revisioned, 'bar' === $revisioned_meta );
     528
     529                // Reset global so subsequent data tests do not get polluted.
     530                $GLOBALS['wp_meta_keys'] = array();
     531        }
     532
     533        public function data_register_post_meta_supports_revisions() {
     534                return array(
     535                        array( 'post', 'registered_key1', array( 'single' => true ), false ),
     536                        array(
     537                                'post',
     538                                'registered_key1',
     539                                array(
     540                                        'single'            => true,
     541                                        'revisions_enabled' => true,
     542                                ),
     543                                true,
     544                        ),
     545                        array( 'page', 'registered_key2', array( 'revisions_enabled' => false ), false ),
     546                        array( 'page', 'registered_key2', array( 'revisions_enabled' => true ), true ),
     547                        array( '', 'registered_key3', array( 'revisions_enabled' => false ), false ),
     548                        array( '', 'registered_key3', array( 'revisions_enabled' => true ), true ),
     549                );
     550        }
     551
     552        /**
     553         * Assert the a post has a meta key.
     554         *
     555         * @param int    $post_id        The ID of the post to check.
     556         * @param string $meta_key The meta key to check for.
     557         */
     558        protected function assertPostHasMetaKey( $post_id, $meta_key ) {
     559                $this->assertEquals( $expect, $stored_array );
     560        }
     561
     562        /**
     563         * Assert that post does not have a meta key.
     564         *
     565         * @param int    $post_id        The ID of the post to check.
     566         * @param string $meta_key The meta key to check for.
     567         */
     568        protected function assertPostNotHasMetaKey( $post_id, $meta_key ) {
     569                $this->assertArrayNotHasKey( $meta_key, get_metadata( 'post', $post_id ) );
     570        }
     571}
  • 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..a184ba2022 100644
    class WP_Test_REST_Post_Meta_Fields extends WP_Test_REST_TestCase { 
    31123112                }
    31133113                return $query;
    31143114        }
     3115
     3116
     3117        /**
     3118         * Test that post meta is revisioned when saving to the posts REST API endpoint.
     3119         *
     3120         * @ticket 20564
     3121         */
     3122        public function test_revisioned_post_meta_with_posts_endpoint() {
     3123                $this->grant_write_permission();
     3124
     3125                register_post_meta(
     3126                        'post',
     3127                        'foo',
     3128                        array(
     3129                                'single'            => true,
     3130                                'show_in_rest'      => true,
     3131                                'revisions_enabled' => true,
     3132                        )
     3133                );
     3134
     3135                $post_id = self::$post_id;
     3136
     3137                // Update the post, saving the meta.
     3138                $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', $post_id ) );
     3139                $request->set_body_params(
     3140                        array(
     3141                                'title' => 'Revision 1',
     3142                                'meta'  => array(
     3143                                        'foo' => 'bar',
     3144                                ),
     3145                        )
     3146                );
     3147                $response = rest_get_server()->dispatch( $request );
     3148                $this->assertSame( 200, $response->get_status() );
     3149
     3150                // Get the last revision.
     3151                $revisions   = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) );
     3152                $revision_id = array_shift( $revisions )->ID;
     3153
     3154                // @todo Ensure the revisions endpoint returns the correct meta values
     3155                // Check that the revisions endpoint returns the correct meta value.
     3156                $request  = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d/revisions/%d', $post_id, $revision_id ) );
     3157                $response = rest_get_server()->dispatch( $request );
     3158                $this->assertSame( 200, $response->get_status() );
     3159                $data = $response->get_data();
     3160                $this->assertSame( array( 'bar' ), $response->get_data()['meta']['foo'] );
     3161
     3162                // Check that the post meta is set correctly.
     3163                $this->assertSame( 'bar', get_post_meta( $revision_id, 'foo', true ) );
     3164
     3165                // Create two more revisions with different meta values for the foo key.
     3166                $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', $post_id ) );
     3167                $request->set_body_params(
     3168                        array(
     3169                                'title' => 'Revision 2',
     3170                                'meta'  => array(
     3171                                        'foo' => 'baz',
     3172                                ),
     3173                        )
     3174                );
     3175                $response = rest_get_server()->dispatch( $request );
     3176                $this->assertSame( 200, $response->get_status() );
     3177
     3178                // Get the last revision.
     3179                $revisions     = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) );
     3180                $revision_id_2 = array_shift( $revisions )->ID;
     3181
     3182                // Check that the revision has the correct meta value.
     3183                $request  = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d/revisions/%d', $post_id, $revision_id_2 ) );
     3184                $response = rest_get_server()->dispatch( $request );
     3185                $this->assertSame( 200, $response->get_status() );
     3186                $this->assertSame( array( 'baz' ), $response->get_data()['meta']['foo'] );
     3187
     3188                // Check that the post meta is set correctly.
     3189                $this->assertSame( 'baz', get_post_meta( $revision_id_2, 'foo', true ) );
     3190
     3191                // One more revision!
     3192                $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', $post_id ) );
     3193                $request->set_body_params(
     3194                        array(
     3195                                'title' => 'Revision 3',
     3196                                'meta'  => array(
     3197                                        'foo' => 'qux',
     3198                                ),
     3199                        )
     3200                );
     3201                $response = rest_get_server()->dispatch( $request );
     3202                $this->assertSame( 200, $response->get_status() );
     3203
     3204                // Get the last revision.
     3205                $revisions     = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) );
     3206                $revision_id_3 = array_shift( $revisions )->ID;
     3207
     3208                // Check that the revision has the correct meta value.
     3209                $request  = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d/revisions/%d', $post_id, $revision_id_3 ) );
     3210                $response = rest_get_server()->dispatch( $request );
     3211                $this->assertSame( 200, $response->get_status() );
     3212                $this->assertSame( array( 'qux' ), $response->get_data()['meta']['foo'] );
     3213
     3214                // Check that the post meta is set correctly.
     3215                $this->assertSame( 'qux', get_post_meta( $revision_id_3, 'foo', true ) );
     3216
     3217                // Restore Revision 3 and verify the post gets the correct meta value.
     3218                wp_restore_post_revision( $revision_id_3 );
     3219                $this->assertSame( 'qux', get_post_meta( $post_id, 'foo', true ) );
     3220
     3221                // Restore Revision 2 and verify the post gets the correct meta value.
     3222                wp_restore_post_revision( $revision_id_2 );
     3223                $this->assertSame( 'baz', get_post_meta( $post_id, 'foo', true ) );
     3224
     3225                // Test with multiple meta values. Note that the posts endpoint doesn't accept multiple meta values, so we'll add those manually.
     3226                $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', $post_id ) );
     3227                $request->set_body_params(
     3228                        array(
     3229                                'title' => 'Revision 4',
     3230                                'meta'  => array(
     3231                                        'foo' => 'bar',
     3232                                ),
     3233                        )
     3234                );
     3235                $response = rest_get_server()->dispatch( $request );
     3236                $this->assertSame( 200, $response->get_status() );
     3237
     3238                // Add additional meta values.
     3239                add_post_meta( $post_id, 'foo', 'bat' );
     3240                add_post_meta( $post_id, 'foo', 'baz' );
     3241
     3242                // Log the current post meta.
     3243                $meta = get_post_meta( $post_id );
     3244
     3245                // Update the post.
     3246                $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', $post_id ) );
     3247                $request->set_body_params(
     3248                        array(
     3249                                'title' => 'Revision 4 update',
     3250                        )
     3251                );
     3252                $response = rest_get_server()->dispatch( $request );
     3253                $this->assertSame( 200, $response->get_status() );
     3254
     3255                // Get the last revision.
     3256                $revisions     = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) );
     3257                $revision_id_4 = array_shift( $revisions )->ID;
     3258
     3259                // Check that the revision has the correct meta value.
     3260                $request  = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d/revisions/%d', $post_id, $revision_id_4 ) );
     3261                $response = rest_get_server()->dispatch( $request );
     3262                $this->assertSame( 200, $response->get_status() );
     3263
     3264                $this->assertSame(
     3265                        array( 'bar', 'bat', 'baz' ),
     3266                        $response->get_data()['meta']['foo']
     3267                );
     3268
     3269        }
    31153270}
  • tests/phpunit/tests/user/wpRegisterPersistedPreferencesMeta.php

    diff --git tests/phpunit/tests/user/wpRegisterPersistedPreferencesMeta.php tests/phpunit/tests/user/wpRegisterPersistedPreferencesMeta.php
    index c3c4bd7a09..68b3c86742 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'