WordPress.org

Make WordPress Core

Ticket #43316: 43316.2.diff

File 43316.2.diff, 11.2 KB (added by adamsilverstein, 4 years ago)
  • src/wp-includes/rest-api.php

    diff --git a/src/wp-includes/rest-api.php b/src/wp-includes/rest-api.php
    index 0cce9fa62f..8ebb1f6fa7 100644
    a b function create_initial_rest_routes() { 
    193193                        $revisions_controller = new WP_REST_Revisions_Controller( $post_type->name );
    194194                        $revisions_controller->register_routes();
    195195                }
     196
     197                // Autosaves.
     198                $controller = new WP_REST_Autosaves_Controller( $post_type->name );
     199                $controller->register_routes();
    196200        }
    197201
    198202        // Post types.
  • new file src/wp-includes/rest-api/endpoints/class-wp-rest-autosaves-controller.php

    diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-autosaves-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-autosaves-controller.php
    new file mode 100644
    index 0000000000..3f1c6c8031
    - +  
     1<?php
     2/**
     3 * REST API: WP_REST_Autosaves_Controller class.
     4 *
     5 * @package WordPress
     6 * @subpackage REST_API
     7 * @since 5.0.0
     8 */
     9
     10/**
     11 * Core class used to access autosaves via the REST API.
     12 *
     13 * @since 5.0.0
     14 *
     15 * @see WP_REST_Controller
     16 */
     17class WP_REST_Autosaves_Controller extends WP_REST_Revisions_Controller {
     18
     19        /**
     20         * Parent post type.
     21         *
     22         * @since 5.0.0
     23         * @var string
     24         */
     25        private $parent_post_type;
     26
     27        /**
     28         * Parent controller.
     29         *
     30         * @since 5.0.0
     31         * @var WP_REST_Controller
     32         */
     33        private $parent_controller;
     34
     35        /**
     36         * Parent controller.
     37         *
     38         * @since 5.0.0
     39         * @var WP_REST_Controller
     40         */
     41        private $revision_controller;
     42
     43        /**
     44         * The base of the parent controller's route.
     45         *
     46         * @since 5.0.0
     47         * @var string
     48         */
     49        private $parent_base;
     50
     51        /**
     52         * Constructor.
     53         *
     54         * @since 5.0.0
     55         *
     56         * @param string $parent_post_type Post type of the parent.
     57         */
     58        public function __construct( $parent_post_type ) {
     59                $this->parent_post_type    = $parent_post_type;
     60                $this->parent_controller   = new WP_REST_Posts_Controller( $parent_post_type );
     61                $this->revision_controller = new WP_REST_Revisions_Controller( $parent_post_type );
     62                $this->rest_namespace      = 'wp/v2';
     63                $this->rest_base           = 'autosaves';
     64                $post_type_object          = get_post_type_object( $parent_post_type );
     65                $this->parent_base         = ! empty( $post_type_object->rest_base ) ? $post_type_object->rest_base : $post_type_object->name;
     66        }
     67
     68        /**
     69         * Registers routes for autosaves based on post types supporting autosaves.
     70         *
     71         * @since 5.0.0
     72         *
     73         * @see register_rest_route()
     74         */
     75        public function register_routes() {
     76                register_rest_route(
     77                        $this->rest_namespace, '/' . $this->parent_base . '/(?P<parent>[\d]+)/' . $this->rest_base, array(
     78                                'args'   => array(
     79                                        'parent' => array(
     80                                                'description' => __( 'The ID for the parent of the object.', 'gutenberg' ),
     81                                                'type'        => 'integer',
     82                                        ),
     83                                ),
     84                                array(
     85                                        'methods'             => WP_REST_Server::READABLE,
     86                                        'callback'            => array( $this, 'get_items' ),
     87                                        'permission_callback' => array( $this->revision_controller, 'get_items_permissions_check' ),
     88                                        'args'                => $this->get_collection_params(),
     89                                ),
     90                                array(
     91                                        'methods'             => WP_REST_Server::CREATABLE,
     92                                        'callback'            => array( $this, 'create_item' ),
     93                                        'permission_callback' => array( $this->parent_controller, 'create_item_permissions_check' ),
     94                                        'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ),
     95                                ),
     96                                'schema' => array( $this, 'get_public_item_schema' ),
     97                        )
     98                );
     99
     100                register_rest_route(
     101                        $this->rest_namespace, '/' . $this->parent_base . '/(?P<parent>[\d]+)/' . $this->rest_base . '/(?P<id>[\d]+)', array(
     102                                'args'   => array(
     103                                        'parent' => array(
     104                                                'description' => __( 'The ID for the parent of the object.', 'gutenberg' ),
     105                                                'type'        => 'integer',
     106                                        ),
     107                                        'id'     => array(
     108                                                'description' => __( 'Unique identifier for the object.', 'gutenberg' ),
     109                                                'type'        => 'integer',
     110                                        ),
     111                                ),
     112                                array(
     113                                        'methods'             => WP_REST_Server::READABLE,
     114                                        'callback'            => array( $this, 'get_item' ),
     115                                        'permission_callback' => array( $this->revision_controller, 'get_item_permissions_check' ),
     116                                        'args'                => array(
     117                                                'context' => $this->get_context_param( array( 'default' => 'view' ) ),
     118                                        ),
     119                                ),
     120                                array(
     121                                        'methods'             => WP_REST_Server::DELETABLE,
     122                                        'callback'            => array( $this, 'delete_item' ),
     123                                        'permission_callback' => array( $this->revision_controller, 'delete_item_permissions_check' ),
     124                                        'args'                => array(
     125                                                'force' => array(
     126                                                        'type'        => 'boolean',
     127                                                        'default'     => false,
     128                                                        'description' => __( 'Required to be true, as autosaves do not support trashing.', 'gutenberg' ),
     129                                                ),
     130                                        ),
     131                                ),
     132                                array(
     133                                        'methods'             => WP_REST_Server::CREATABLE,
     134                                        'callback'            => array( $this, 'create_item' ),
     135                                        'permission_callback' => array( $this->parent_controller, 'create_item_permissions_check' ),
     136                                        'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ),
     137                                ),
     138                                'schema' => array( $this, 'get_public_item_schema' ),
     139                        )
     140                );
     141
     142        }
     143
     144        /**
     145         * Creates a single autosave.
     146         *
     147         * @since 5.0.0
     148         *
     149         * @param WP_REST_Request $request Full details about the request.
     150         * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
     151         */
     152        public function create_item( $request ) {
     153
     154                // Map new fields onto the existing post data.
     155                $parent            = $this->revision_controller->get_parent( $request->get_param( 'parent' ) );
     156                $prepared_post     = $this->parent_controller->prepare_item_for_database( $request );
     157                $prepared_post->ID = $parent->ID;
     158
     159                // If the parent post a draft, autosaving updates it and does not create a revision.
     160                if ( 'draft' === $parent->post_status ) {
     161
     162                        // Disable revisions.
     163                        remove_action( 'post_updated', 'wp_save_post_revision' );
     164
     165                        $autosave_id = wp_update_post( (array) $prepared_post, true );
     166
     167                        // Re-enable revisions.
     168                        add_action( 'post_updated', 'wp_save_post_revision' );
     169                        if ( ! is_wp_error( $autosave_id ) ) {
     170                                $post = get_post( $autosave_id );
     171                        }
     172                } else {
     173
     174                        // Non-draft posts - update the post, creating an autosave.
     175                        $autosave_id = $this->create_post_autosave( (array) $prepared_post );
     176                        $post        = get_post( $autosave_id );
     177                }
     178                $request->set_param( 'context', 'edit' );
     179
     180                $response = $this->prepare_item_for_response( $post, $request );
     181                $response = rest_ensure_response( $response );
     182
     183                $response->set_status( 201 );
     184                $response->header( 'Location', rest_url( sprintf( '%s/%s/%d', $this->rest_namespace, $this->rest_base, $autosave_id ) ) );
     185
     186                return $response;
     187        }
     188
     189        /**
     190         * Get the autosave, if the ID is valid.
     191         *
     192         * @since 5.0.0
     193         *
     194         * @param WP_REST_Request $request Full data about the request.
     195         * @return WP_Post|WP_Error Revision post object if ID is valid, WP_Error otherwise.
     196         */
     197        public function get_item( $request ) {
     198                $id = $request->get_param( 'id' );
     199                $error = new WP_Error( 'rest_post_invalid_id', __( 'Invalid autosave ID.', 'gutenberg' ), array( 'status' => 404 ) );
     200                if ( (int) $id <= 0 ) {
     201                        return $error;
     202                }
     203
     204                $autosave = get_post( (int) $id );
     205                if ( empty( $autosave ) || empty( $autosave->ID ) || 'revision' !== $autosave->post_type ) {
     206                        return $error;
     207                }
     208
     209                return $autosave;
     210        }
     211
     212        /**
     213         * Gets a collection of autosaves using wp_get_post_autosave.
     214         *
     215         * Contains the user's autosave, for empty if it doesn't exist.
     216         *
     217         * @since 5.0.0
     218         *
     219         * @param WP_REST_Request $request Full data about the request.
     220         * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
     221         */
     222        public function get_items( $request ) {
     223                $parent = $this->revision_controller->get_parent( $request->get_param( 'parent' ) );
     224                if ( is_wp_error( $parent ) ) {
     225                        return $parent;
     226                }
     227
     228                $autosave = wp_get_post_autosave( $request->get_param( 'parent' ) );
     229
     230                if ( ! $autosave ) {
     231                        return array();
     232                }
     233
     234                $response   = array();
     235                $data       = $this->prepare_item_for_response( $autosave, $request );
     236                $response[] = $this->prepare_response_for_collection( $data );
     237
     238                return rest_ensure_response( $response );
     239        }
     240
     241
     242        /**
     243         * Retrieves the autosave's schema, conforming to JSON Schema.
     244         *
     245         * @since 5.0.0
     246         *
     247         * @return array Item schema data.
     248         */
     249        public function get_item_schema() {
     250                return $this->revision_controller->get_item_schema();
     251        }
     252
     253        /**
     254         * Creates autosave data for the specified post from $_POST data.
     255         *
     256         * From core post.php.
     257         *
     258         * @since 2.6.0
     259         *
     260         * @param mixed $post_data Associative array containing the post data or int post ID.
     261         * @return mixed The autosave revision ID. WP_Error or 0 on error.
     262         */
     263        public function create_post_autosave( $post_data ) {
     264
     265                $post_id     = (int) $post_data['ID'];
     266                $post_author = get_current_user_id();
     267
     268                // Store one autosave per author. If there is already an autosave, overwrite it.
     269                $old_autosave = wp_get_post_autosave( $post_id, $post_author );
     270                if ( $old_autosave ) {
     271                        $new_autosave                = _wp_post_revision_data( $post_data, true );
     272                        $new_autosave['ID']          = $old_autosave->ID;
     273                        $new_autosave['post_author'] = $post_author;
     274
     275                        // If the new autosave has the same content as the post, delete the autosave.
     276                        $post                  = get_post( $post_id );
     277                        $autosave_is_different = false;
     278                        foreach ( array_intersect( array_keys( $new_autosave ), array_keys( _wp_post_revision_fields( $post ) ) ) as $field ) {
     279                                if ( normalize_whitespace( $new_autosave[ $field ] ) != normalize_whitespace( $post->$field ) ) {
     280                                        $autosave_is_different = true;
     281                                        break;
     282                                }
     283                        }
     284
     285                        if ( ! $autosave_is_different ) {
     286                                wp_delete_post_revision( $old_autosave->ID );
     287                                return 0;
     288                        }
     289
     290                        /**
     291                         * Fires before an autosave is stored.
     292                         *
     293                         * @since 4.1.0
     294                         *
     295                         * @param array $new_autosave Post array - the autosave that is about to be saved.
     296                         */
     297                        do_action( 'wp_creating_autosave', $new_autosave );
     298
     299                        return wp_update_post( $new_autosave );
     300                }
     301
     302                // _wp_put_post_revision() expects unescaped.
     303                $post_data = wp_unslash( $post_data );
     304
     305                // Otherwise create the new autosave as a special post revision.
     306                return _wp_put_post_revision( $post_data, true );
     307        }
     308}
  • src/wp-settings.php

    diff --git a/src/wp-settings.php b/src/wp-settings.php
    index 6edd3c98ca..b2c4189c64 100644
    a b require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-attachments-contro 
    230230require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-post-types-controller.php' );
    231231require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-post-statuses-controller.php' );
    232232require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-revisions-controller.php' );
     233require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-autosaves-controller.php' );
    233234require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-taxonomies-controller.php' );
    234235require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-terms-controller.php' );
    235236require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-users-controller.php' );