Make WordPress Core

Changeset 56714


Ignore:
Timestamp:
09/26/2023 03:30:34 PM (8 months ago)
Author:
adamsilverstein
Message:

Revisions: framework for storing post meta revisions.

Enable the storing of post meta in revisions including autosaves and previews:

Add a new argument revisions_enabled to the register_meta function which enables storing meta in revisions.

Add a new wp_post_revision_meta_keys filter which developers can use to control which meta is revisioned - it passes an array of the meta keys with revisions enabled as well as the post type.

Meta keys with revisions enabled are also stored for autosaves, and are restored when a revision or autosave is restored. In addition, meta values are now stored with the autosave revision used for previews. Changes to meta can now be previewed correctly without overwriting the published meta (see #20299) or passing data as a query variable, as the editor currently does to preview changes to the featured image.

Changes to meta with revisions enabled are considered when determining if a new revision should be created. A new revision is created if the meta value has changed since the last revision.

Revisions are now saved on the wp_after_insert_post hook instead of post_updated. The wp_after_insert_post action is fired after post meta has been saved by the REST API which enables attaching meta to the revision. To ensure backwards compatibility with existing action uses, wp_save_post_revision_on_insert function exits early if plugins have removed the previous do_action( 'post_updated', 'wp_save_post_revision' ) call.

Props: alexkingorg, johnbillion, markjaquith, WraithKenny, kovshenin, azaozz, tv-productions, p51labs, mattheu, mikeschroder, Mamaduka, ellatrix, timothyblynjacobs, jakemgold, bookwyrm, ryanduff, mintindeed, wonderboymusic, sanchothefat, westonruter, spacedmonkey, hellofromTonya, drewapicture, adamsilverstein, swisspiddy.
Fixes #20564, #20299.

Location:
trunk
Files:
1 added
13 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-admin/includes/post.php

    r56712 r56714  
    19711971         *
    19721972         * @since 4.1.0
     1973         * @since 6.4.0 The `$is_update` parameter was added to indicate if the autosave is being updated or was newly created.
    19731974         *
    19741975         * @param array $new_autosave Post array - the autosave that is about to be saved.
     1976         * @param bool  $is_update    Whether this is an existing autosave.
    19751977         */
    1976         do_action( 'wp_creating_autosave', $new_autosave );
    1977 
     1978        do_action( 'wp_creating_autosave', $new_autosave, true );
    19781979        return wp_update_post( $new_autosave );
    19791980    }
     
    19831984
    19841985    // Otherwise create the new autosave as a special post revision.
    1985     return _wp_put_post_revision( $post_data, true );
     1986    $revision = _wp_put_post_revision( $post_data, true );
     1987
     1988    if ( ! is_wp_error( $revision ) && 0 !== $revision ) {
     1989
     1990        /** This action is documented in wp-admin/includes/post.php */
     1991        do_action( 'wp_creating_autosave', get_post( $revision, ARRAY_A ), false );
     1992    }
     1993
     1994    return $revision;
     1995}
     1996
     1997/**
     1998 * Autosave the revisioned meta fields.
     1999 *
     2000 * Iterates through the revisioned meta fields and checks each to see if they are set,
     2001 * and have a changed value. If so, the meta value is saved and attached to the autosave.
     2002 *
     2003 * @since 6.4.0
     2004 *
     2005 * @param array $new_autosave The new post data being autosaved.
     2006 */
     2007function wp_autosave_post_revisioned_meta_fields( $new_autosave ) {
     2008    /*
     2009     * The post data arrives as either $_POST['data']['wp_autosave'] or the $_POST
     2010     * itself. This sets $posted_data to the correct variable.
     2011     *
     2012     * Ignoring sanitization to avoid altering meta. Ignoring the nonce check because
     2013     * this is hooked on inner core hooks where a valid nonce was already checked.
     2014     *
     2015     * @phpcs:disable WordPress.Security
     2016     */
     2017    $posted_data = isset( $_POST['data']['wp_autosave'] ) ? $_POST['data']['wp_autosave'] : $_POST;
     2018    // phpcs:enable
     2019
     2020    $post_type = get_post_type( $new_autosave['post_parent'] );
     2021
     2022    /*
     2023     * Go thru the revisioned meta keys and save them as part of the autosave, if
     2024     * the meta key is part of the posted data, the meta value is not blank and
     2025     * the the meta value has changes from the last autosaved value.
     2026     */
     2027    foreach ( wp_post_revision_meta_keys( $post_type ) as $meta_key ) {
     2028
     2029        if (
     2030        isset( $posted_data[ $meta_key ] ) &&
     2031        get_post_meta( $new_autosave['ID'], $meta_key, true ) !== wp_unslash( $posted_data[ $meta_key ] )
     2032        ) {
     2033            /*
     2034             * Use the underlying delete_metadata() and add_metadata() functions
     2035             * vs delete_post_meta() and add_post_meta() to make sure we're working
     2036             * with the actual revision meta.
     2037             */
     2038            delete_metadata( 'post', $new_autosave['ID'], $meta_key );
     2039
     2040            /*
     2041             * One last check to ensure meta value not empty().
     2042             */
     2043            if ( ! empty( $posted_data[ $meta_key ] ) ) {
     2044                /*
     2045                 * Add the revisions meta data to the autosave.
     2046                 */
     2047                add_metadata( 'post', $new_autosave['ID'], $meta_key, $posted_data[ $meta_key ] );
     2048            }
     2049        }
     2050    }
    19862051}
    19872052
  • trunk/src/wp-includes/default-filters.php

    r56693 r56714  
    412412add_action( 'shutdown', 'wp_ob_end_flush_all', 1 );
    413413// Create a revision whenever a post is updated.
     414add_action( 'wp_after_insert_post', 'wp_save_post_revision_on_insert', 9, 3 );
    414415add_action( 'post_updated', 'wp_save_post_revision', 10, 1 );
    415416add_action( 'publish_post', '_publish_post_hook', 5, 1 );
     
    720721add_action( 'init', 'wp_create_initial_post_meta' );
    721722
     723// Include revisioned meta when considering whether a post revision has changed.
     724add_filter( 'wp_save_post_revision_post_has_changed', 'wp_check_revisioned_meta_fields_have_changed', 10, 3 );
     725
     726// Save revisioned post meta immediately after a revision is saved
     727add_action( '_wp_put_post_revision', 'wp_save_revisioned_meta_fields', 10, 2 );
     728
     729// Include revisioned meta when creating or updating an autosave revision.
     730add_action( 'wp_creating_autosave', 'wp_autosave_post_revisioned_meta_fields' );
     731
     732// When restoring revisions, also restore revisioned meta.
     733add_action( 'wp_restore_post_revision', 'wp_restore_post_revision_meta', 10, 2 );
     734
    722735// Font management.
    723736add_action( 'wp_head', 'wp_print_font_faces', 50 );
  • trunk/src/wp-includes/meta.php

    r56191 r56714  
    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',
     
    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.
     
    14151418        'auth_callback'     => null,
    14161419        'show_in_rest'      => false,
     1420        'revisions_enabled' => false,
    14171421    );
    14181422
     
    14611465
    14621466    $object_subtype = ! empty( $args['object_subtype'] ) ? $args['object_subtype'] : '';
     1467    if ( $args['revisions_enabled'] ) {
     1468        if ( 'post' !== $object_type ) {
     1469            _doing_it_wrong( __FUNCTION__, __( 'Meta keys cannot enable revisions support unless the object type supports revisions.' ), '6.4.0' );
     1470
     1471            return false;
     1472        } elseif ( ! empty( $object_subtype ) && ! post_type_supports( $object_subtype, 'revisions' ) ) {
     1473            _doing_it_wrong( __FUNCTION__, __( 'Meta keys cannot enable revisions support unless the object subtype supports revisions.' ), '6.4.0' );
     1474
     1475            return false;
     1476        }
     1477    }
    14631478
    14641479    // If `auth_callback` is not provided, fall back to `is_protected_meta()`.
  • trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-autosaves-controller.php

    r56586 r56714  
    235235            $autosave_id = wp_update_post( wp_slash( (array) $prepared_post ), true );
    236236        } else {
    237             // Non-draft posts: create or update the post autosave.
    238             $autosave_id = $this->create_post_autosave( (array) $prepared_post );
     237            // Non-draft posts: create or update the post autosave. Pass the meta data.
     238            $autosave_id = $this->create_post_autosave( (array) $prepared_post, (array) $request->get_param( 'meta' ) );
    239239        }
    240240
     
    349349     *
    350350     * @since 5.0.0
     351     * @since 6.4.0 The `$meta` parameter was added.
    351352     *
    352353     * @param array $post_data Associative array containing the post data.
     354     * @param array $meta      Associative array containing the post meta data.
    353355     * @return mixed The autosave revision ID or WP_Error.
    354356     */
    355     public function create_post_autosave( $post_data ) {
     357    public function create_post_autosave( $post_data, array $meta = array() ) {
    356358
    357359        $post_id = (int) $post_data['ID'];
     
    373375        }
    374376
     377        // Check if meta values have changed.
     378        if ( ! empty( $meta ) ) {
     379            $revisioned_meta_keys = wp_post_revision_meta_keys( $post->post_type );
     380            foreach ( $revisioned_meta_keys as $meta_key ) {
     381                // get_metadata_raw is used to avoid retrieving the default value.
     382                $old_meta = get_metadata_raw( 'post', $post_id, $meta_key, true );
     383                $new_meta = isset( $meta[ $meta_key ] ) ? $meta[ $meta_key ] : '';
     384
     385                if ( $new_meta !== $old_meta ) {
     386                    $autosave_is_different = true;
     387                    break;
     388                }
     389            }
     390        }
     391
    375392        $user_id = get_current_user_id();
    376393
     
    391408
    392409            // wp_update_post() expects escaped array.
    393             return wp_update_post( wp_slash( $new_autosave ) );
    394         }
    395 
    396         // Create the new autosave as a special post revision.
    397         return _wp_put_post_revision( $post_data, true );
     410            $revision_id = wp_update_post( wp_slash( $new_autosave ) );
     411        } else {
     412            // Create the new autosave as a special post revision.
     413            $revision_id = _wp_put_post_revision( $post_data, true );
     414        }
     415
     416        if ( is_wp_error( $revision_id ) || 0 === $revision_id ) {
     417            return $revision_id;
     418        }
     419
     420        // Attached any passed meta values that have revisions enabled.
     421        if ( ! empty( $meta ) ) {
     422            foreach ( $revisioned_meta_keys as $meta_key ) {
     423                if ( isset( $meta[ $meta_key ] ) ) {
     424                    update_metadata( 'post', $revision_id, $meta_key, $meta[ $meta_key ] );
     425                }
     426            }
     427        }
     428
     429        return $revision_id;
    398430    }
    399431
  • trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php

    r56586 r56714  
    2626
    2727    /**
     28     * Instance of a revision meta fields object.
     29     *
     30     * @since 6.4.0
     31     * @var WP_REST_Post_Meta_Fields
     32     */
     33    protected $meta;
     34
     35    /**
    2836     * Parent controller.
    2937     *
     
    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
     
    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 );
     
    752765            $schema['properties']['guid'] = $parent_schema['properties']['guid'];
    753766        }
     767
     768        $schema['properties']['meta'] = $this->meta->get_field_schema();
    754769
    755770        $this->schema = $schema;
  • trunk/src/wp-includes/revision.php

    r56359 r56714  
    9797
    9898/**
     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
     119/**
    99120 * Creates a revision for the current version of a post.
    100121 *
     
    109130function wp_save_post_revision( $post_id ) {
    110131    if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
     132        return;
     133    }
     134
     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' ) ) {
    111137        return;
    112138    }
     
    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;
     399}
     400
     401
     402/**
     403 * Save the revisioned meta fields.
     404 *
     405 * @since 6.4.0
     406 *
     407 * @param int $revision_id The ID of the revision to save the meta to.
     408 * @param int $post_id     The ID of the post the revision is associated with.
     409 */
     410function wp_save_revisioned_meta_fields( $revision_id, $post_id ) {
     411    $post_type = get_post_type( $post_id );
     412    if ( ! $post_type ) {
     413        return;
     414    }
     415
     416    foreach ( wp_post_revision_meta_keys( $post_type ) as $meta_key ) {
     417        if ( metadata_exists( 'post', $post_id, $meta_key ) ) {
     418            _wp_copy_post_meta( $post_id, $revision_id, $meta_key );
     419        }
     420    }
    371421}
    372422
     
    451501    update_post_meta( $post_id, '_edit_last', get_current_user_id() );
    452502
     503    // Restore any revisioned meta fields.
     504    wp_restore_post_revision_meta( $post_id, $revision['ID'] );
     505
    453506    /**
    454507     * Fires after a post revision has been restored.
     
    462515
    463516    return $post_id;
     517}
     518
     519/**
     520 * Restore the revisioned meta values for a post.
     521 *
     522 * @param int $post_id     The ID of the post to restore the meta to.
     523 * @param int $revision_id The ID of the revision to restore the meta from.
     524 *
     525 * @since 6.4.0
     526 */
     527function wp_restore_post_revision_meta( $post_id, $revision_id ) {
     528    $post_type = get_post_type( $post_id );
     529    if ( ! $post_type ) {
     530        return;
     531    }
     532
     533    // Restore revisioned meta fields.
     534    foreach ( wp_post_revision_meta_keys( $post_type ) as $meta_key ) {
     535
     536        // Clear any existing meta.
     537        delete_post_meta( $post_id, $meta_key );
     538
     539        _wp_copy_post_meta( $revision_id, $post_id, $meta_key );
     540    }
     541}
     542
     543/**
     544 * Copy post meta for the given key from one post to another.
     545 *
     546 * @param int    $source_post_id Post ID to copy meta value(s) from.
     547 * @param int    $target_post_id Post ID to copy meta value(s) to.
     548 * @param string $meta_key       Meta key to copy.
     549 *
     550 * @since 6.4.0
     551 */
     552function _wp_copy_post_meta( $source_post_id, $target_post_id, $meta_key ) {
     553
     554    foreach ( get_post_meta( $source_post_id, $meta_key ) as $meta_value ) {
     555        /**
     556         * We use add_metadata() function vs add_post_meta() here
     557         * to allow for a revision post target OR regular post.
     558         */
     559        add_metadata( 'post', $target_post_id, $meta_key, wp_slash( $meta_value ) );
     560    }
     561}
     562
     563/**
     564 * Determine which post meta fields should be revisioned.
     565 *
     566 * @since 6.4.0
     567 *
     568 * @param string $post_type The post type being revisioned.
     569 *
     570 * @return array An array of meta keys to be revisioned.
     571 */
     572function wp_post_revision_meta_keys( $post_type ) {
     573    $registered_meta = array_merge(
     574        get_registered_meta_keys( 'post' ),
     575        get_registered_meta_keys( 'post', $post_type )
     576    );
     577
     578    $wp_revisioned_meta_keys = array();
     579
     580    foreach ( $registered_meta as $name => $args ) {
     581        if ( $args['revisions_enabled'] ) {
     582            $wp_revisioned_meta_keys[ $name ] = true;
     583        }
     584    }
     585
     586    $wp_revisioned_meta_keys = array_keys( $wp_revisioned_meta_keys );
     587
     588    /**
     589     * Filter the list of post meta keys to be revisioned.
     590     *
     591     * @since 6.4.0
     592     *
     593     * @param array $keys       An array of meta fields to be revisioned.
     594     * @param string $post_type The post type being revisioned.
     595     */
     596    return apply_filters( 'wp_post_revision_meta_keys', $wp_revisioned_meta_keys, $post_type );
     597}
     598
     599/**
     600 * Check whether revisioned post meta fields have changed.
     601 *
     602 * @param bool    $post_has_changed Whether the post has changed.
     603 * @param WP_Post $last_revision    The last revision post object.
     604 * @param WP_Post $post             The post object.
     605 *
     606 * @since 6.4.0
     607 */
     608function wp_check_revisioned_meta_fields_have_changed( $post_has_changed, WP_Post $last_revision, WP_Post $post ) {
     609    foreach ( wp_post_revision_meta_keys( $post->post_type ) as $meta_key ) {
     610        if ( get_post_meta( $post->ID, $meta_key ) !== get_post_meta( $last_revision->ID, $meta_key ) ) {
     611            $post_has_changed = true;
     612            break;
     613        }
     614    }
     615    return $post_has_changed;
    464616}
    465617
     
    729881    add_filter( 'get_the_terms', '_wp_preview_terms_filter', 10, 3 );
    730882    add_filter( 'get_post_metadata', '_wp_preview_post_thumbnail_filter', 10, 3 );
     883    add_filter( 'get_post_metadata', '_wp_preview_meta_filter', 10, 4 );
    731884
    732885    return $post;
     
    9471100    return true;
    9481101}
     1102
     1103/**
     1104 * Filters preview post meta retrieval to get values from the autosave.
     1105 *
     1106 * Filters revisioned meta keys only.
     1107 *
     1108 * @since 6.4.0
     1109 *
     1110 * @param mixed  $value     Meta value to filter.
     1111 * @param int    $object_id Object ID.
     1112 * @param string $meta_key  Meta key to filter a value for.
     1113 * @param bool   $single    Whether to return a single value. Default false.
     1114 * @return mixed Original meta value if the meta key isn't revisioned, the object doesn't exist,
     1115 *               the post type is a revision or the post ID doesn't match the object ID.
     1116 *               Otherwise, the revisioned meta value is returned for the preview.
     1117 */
     1118function _wp_preview_meta_filter( $value, $object_id, $meta_key, $single ) {
     1119
     1120    $post = get_post();
     1121    if (
     1122        empty( $post ) ||
     1123        $post->ID !== $object_id ||
     1124        ! in_array( $meta_key, wp_post_revision_meta_keys( $post->post_type ), true ) ||
     1125        'revision' === $post->post_type
     1126    ) {
     1127        return $value;
     1128    }
     1129
     1130    $preview = wp_get_post_autosave( $post->ID );
     1131    if ( false === $preview ) {
     1132        return $value;
     1133    }
     1134
     1135    return get_post_meta( $preview->ID, $meta_key, $single );
     1136}
  • trunk/tests/phpunit/tests/meta/registerMeta.php

    r56548 r56714  
    9898                        'auth_callback'     => '__return_true',
    9999                        'show_in_rest'      => false,
     100                        'revisions_enabled' => false,
    100101                    ),
    101102                ),
     
    122123                        'auth_callback'     => '__return_true',
    123124                        'show_in_rest'      => false,
     125                        'revisions_enabled' => false,
    124126                    ),
    125127                ),
     
    176178                        'auth_callback'     => '__return_true',
    177179                        'show_in_rest'      => false,
     180                        'revisions_enabled' => false,
    178181                    ),
    179182                ),
     
    343346                        'auth_callback'     => '__return_true',
    344347                        'show_in_rest'      => false,
     348                        'revisions_enabled' => false,
    345349                    ),
    346350                ),
     
    396400                        'auth_callback'     => '__return_true',
    397401                        'show_in_rest'      => false,
     402                        'revisions_enabled' => false,
    398403                    ),
    399404                ),
     
    10821087        );
    10831088    }
     1089
     1090    /**
     1091     * Test that attempting to register meta with revisions_enabled set to true on a
     1092     * post type that does not have revisions enabled fails and throws a `doing_it_wrong` notice.
     1093     *
     1094     * @ticket 20564
     1095     */
     1096    public function test_register_meta_with_revisions_enabled_on_post_type_without_revisions() {
     1097        $this->setExpectedIncorrectUsage( 'register_meta' );
     1098
     1099        // Set up a custom post type with revisions disabled.
     1100        register_post_type(
     1101            'test_post_type',
     1102            array(
     1103                'supports' => array( 'title', 'editor' ),
     1104            )
     1105        );
     1106
     1107        $meta_key = 'registered_key1';
     1108        $args     = array(
     1109            'revisions_enabled' => true,
     1110        );
     1111
     1112        $register = register_meta(
     1113            'test_post_type',
     1114            $meta_key,
     1115            $args
     1116        );
     1117
     1118        $this->assertFalse( $register );
     1119    }
    10841120}
  • trunk/tests/phpunit/tests/rest-api/rest-autosaves-controller.php

    r56548 r56714  
    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',
     
    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 );
     
    303304        $this->assertArrayHasKey( 'title', $properties );
    304305        $this->assertArrayHasKey( 'preview_link', $properties );
     306        $this->assertArrayHasKey( 'meta', $properties );
    305307    }
    306308
  • trunk/tests/phpunit/tests/rest-api/rest-global-styles-revisions-controller.php

    r56105 r56714  
    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(
     
    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(
     
    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    }
     
    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' );
  • trunk/tests/phpunit/tests/rest-api/rest-post-meta-fields.php

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

    r55457 r56714  
    180180            'guid',
    181181            'id',
     182            'meta',
    182183            'parent',
    183184            'slug',
     
    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 );
     
    349350        $this->assertArrayHasKey( 'slug', $properties );
    350351        $this->assertArrayHasKey( 'title', $properties );
     352        $this->assertArrayHasKey( 'meta', $properties );
    351353    }
    352354
  • trunk/tests/phpunit/tests/user/wpRegisterPersistedPreferencesMeta.php

    r56547 r56714  
    5353                    ),
    5454                ),
     55                'revisions_enabled' => false,
    5556            ),
    5657            $wp_meta_keys['user'][''][ $meta_key ],
  • trunk/tests/qunit/fixtures/wp-api-generated.js

    r56642 r56714  
    1178311783            "rendered": ""
    1178411784        },
     11785        "meta": {
     11786            "meta_key": ""
     11787        },
    1178511788        "_links": {
    1178611789            "parent": [
     
    1181111814        "excerpt": {
    1181211815            "rendered": "<p>REST API Client Fixture: Post</p>\n"
     11816        },
     11817        "meta": {
     11818            "meta_key": ""
    1181311819        },
    1181411820        "_links": {
     
    1184211848    "excerpt": {
    1184311849        "rendered": "<p>REST API Client Fixture: Post</p>\n"
     11850    },
     11851    "meta": {
     11852        "meta_key": ""
    1184411853    }
    1184511854};
     
    1186611875        "excerpt": {
    1186711876            "rendered": ""
     11877        },
     11878        "meta": {
     11879            "meta_key": ""
    1186811880        },
    1186911881        "_links": {
     
    1189711909    "excerpt": {
    1189811910        "rendered": ""
     11911    },
     11912    "meta": {
     11913        "meta_key": ""
    1189911914    }
    1190011915};
     
    1204312058            "rendered": ""
    1204412059        },
     12060        "meta": {
     12061            "meta_key": ""
     12062        },
    1204512063        "_links": {
    1204612064            "parent": [
     
    1207112089        "excerpt": {
    1207212090            "rendered": "<p>REST API Client Fixture: Page</p>\n"
     12091        },
     12092        "meta": {
     12093            "meta_key": ""
    1207312094        },
    1207412095        "_links": {
     
    1210212123    "excerpt": {
    1210312124        "rendered": "<p>REST API Client Fixture: Page</p>\n"
     12125    },
     12126    "meta": {
     12127        "meta_key": ""
    1210412128    }
    1210512129};
     
    1212612150        "excerpt": {
    1212712151            "rendered": ""
     12152        },
     12153        "meta": {
     12154            "meta_key": ""
    1212812155        },
    1212912156        "_links": {
     
    1215712184    "excerpt": {
    1215812185        "rendered": ""
     12186    },
     12187    "meta": {
     12188        "meta_key": ""
    1215912189    }
    1216012190};
Note: See TracChangeset for help on using the changeset viewer.