[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

title

Body

[close]

/wp-includes/rest-api/endpoints/ -> class-wp-rest-autosaves-controller.php (source)

   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_Revisions_Controller
  16   * @see WP_REST_Controller
  17   */
  18  class WP_REST_Autosaves_Controller extends WP_REST_Revisions_Controller {
  19  
  20      /**
  21       * Parent post type.
  22       *
  23       * @since 5.0.0
  24       * @var string
  25       */
  26      private $parent_post_type;
  27  
  28      /**
  29       * Parent post controller.
  30       *
  31       * @since 5.0.0
  32       * @var WP_REST_Controller
  33       */
  34      private $parent_controller;
  35  
  36      /**
  37       * Revision controller.
  38       *
  39       * @since 5.0.0
  40       * @var WP_REST_Controller
  41       */
  42      private $revisions_controller;
  43  
  44      /**
  45       * The base of the parent controller's route.
  46       *
  47       * @since 5.0.0
  48       * @var string
  49       */
  50      private $parent_base;
  51  
  52      /**
  53       * Constructor.
  54       *
  55       * @since 5.0.0
  56       *
  57       * @param string $parent_post_type Post type of the parent.
  58       */
  59  	public function __construct( $parent_post_type ) {
  60          $this->parent_post_type = $parent_post_type;
  61          $post_type_object       = get_post_type_object( $parent_post_type );
  62          $parent_controller      = $post_type_object->get_rest_controller();
  63  
  64          if ( ! $parent_controller ) {
  65              $parent_controller = new WP_REST_Posts_Controller( $parent_post_type );
  66          }
  67  
  68          $this->parent_controller    = $parent_controller;
  69          $this->revisions_controller = new WP_REST_Revisions_Controller( $parent_post_type );
  70          $this->rest_namespace       = 'wp/v2';
  71          $this->rest_base            = 'autosaves';
  72          $this->parent_base          = ! empty( $post_type_object->rest_base ) ? $post_type_object->rest_base : $post_type_object->name;
  73      }
  74  
  75      /**
  76       * Registers routes for autosaves.
  77       *
  78       * @since 5.0.0
  79       *
  80       * @see register_rest_route()
  81       */
  82  	public function register_routes() {
  83          register_rest_route(
  84              $this->rest_namespace,
  85              '/' . $this->parent_base . '/(?P<id>[\d]+)/' . $this->rest_base,
  86              array(
  87                  'args'   => array(
  88                      'parent' => array(
  89                          'description' => __( 'The ID for the parent of the object.' ),
  90                          'type'        => 'integer',
  91                      ),
  92                  ),
  93                  array(
  94                      'methods'             => WP_REST_Server::READABLE,
  95                      'callback'            => array( $this, 'get_items' ),
  96                      'permission_callback' => array( $this, 'get_items_permissions_check' ),
  97                      'args'                => $this->get_collection_params(),
  98                  ),
  99                  array(
 100                      'methods'             => WP_REST_Server::CREATABLE,
 101                      'callback'            => array( $this, 'create_item' ),
 102                      'permission_callback' => array( $this, 'create_item_permissions_check' ),
 103                      'args'                => $this->parent_controller->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
 104                  ),
 105                  'schema' => array( $this, 'get_public_item_schema' ),
 106              )
 107          );
 108  
 109          register_rest_route(
 110              $this->rest_namespace,
 111              '/' . $this->parent_base . '/(?P<parent>[\d]+)/' . $this->rest_base . '/(?P<id>[\d]+)',
 112              array(
 113                  'args'   => array(
 114                      'parent' => array(
 115                          'description' => __( 'The ID for the parent of the object.' ),
 116                          'type'        => 'integer',
 117                      ),
 118                      'id'     => array(
 119                          'description' => __( 'The ID for the object.' ),
 120                          'type'        => 'integer',
 121                      ),
 122                  ),
 123                  array(
 124                      'methods'             => WP_REST_Server::READABLE,
 125                      'callback'            => array( $this, 'get_item' ),
 126                      'permission_callback' => array( $this->revisions_controller, 'get_item_permissions_check' ),
 127                      'args'                => array(
 128                          'context' => $this->get_context_param( array( 'default' => 'view' ) ),
 129                      ),
 130                  ),
 131                  'schema' => array( $this, 'get_public_item_schema' ),
 132              )
 133          );
 134  
 135      }
 136  
 137      /**
 138       * Get the parent post.
 139       *
 140       * @since 5.0.0
 141       *
 142       * @param int $parent_id Supplied ID.
 143       * @return WP_Post|WP_Error Post object if ID is valid, WP_Error otherwise.
 144       */
 145  	protected function get_parent( $parent_id ) {
 146          return $this->revisions_controller->get_parent( $parent_id );
 147      }
 148  
 149      /**
 150       * Checks if a given request has access to get autosaves.
 151       *
 152       * @since 5.0.0
 153       *
 154       * @param WP_REST_Request $request Full data about the request.
 155       * @return true|WP_Error True if the request has read access, WP_Error object otherwise.
 156       */
 157  	public function get_items_permissions_check( $request ) {
 158          $parent = $this->get_parent( $request['id'] );
 159          if ( is_wp_error( $parent ) ) {
 160              return $parent;
 161          }
 162  
 163          $parent_post_type_obj = get_post_type_object( $parent->post_type );
 164          if ( ! current_user_can( $parent_post_type_obj->cap->edit_post, $parent->ID ) ) {
 165              return new WP_Error( 'rest_cannot_read', __( 'Sorry, you are not allowed to view autosaves of this post.' ), array( 'status' => rest_authorization_required_code() ) );
 166          }
 167  
 168          return true;
 169      }
 170  
 171      /**
 172       * Checks if a given request has access to create an autosave revision.
 173       *
 174       * Autosave revisions inherit permissions from the parent post,
 175       * check if the current user has permission to edit the post.
 176       *
 177       * @since 5.0.0
 178       *
 179       * @param WP_REST_Request $request Full details about the request.
 180       * @return true|WP_Error True if the request has access to create the item, WP_Error object otherwise.
 181       */
 182  	public function create_item_permissions_check( $request ) {
 183          $id = $request->get_param( 'id' );
 184          if ( empty( $id ) ) {
 185              return new WP_Error( 'rest_post_invalid_id', __( 'Invalid item ID.' ), array( 'status' => 404 ) );
 186          }
 187  
 188          return $this->parent_controller->update_item_permissions_check( $request );
 189      }
 190  
 191      /**
 192       * Creates, updates or deletes an autosave revision.
 193       *
 194       * @since 5.0.0
 195       *
 196       * @param WP_REST_Request $request Full details about the request.
 197       * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
 198       */
 199  	public function create_item( $request ) {
 200  
 201          if ( ! defined( 'DOING_AUTOSAVE' ) ) {
 202              define( 'DOING_AUTOSAVE', true );
 203          }
 204  
 205          $post = get_post( $request['id'] );
 206  
 207          if ( is_wp_error( $post ) ) {
 208              return $post;
 209          }
 210  
 211          $prepared_post     = $this->parent_controller->prepare_item_for_database( $request );
 212          $prepared_post->ID = $post->ID;
 213          $user_id           = get_current_user_id();
 214  
 215          if ( ( 'draft' === $post->post_status || 'auto-draft' === $post->post_status ) && $post->post_author == $user_id ) {
 216              // Draft posts for the same author: autosaving updates the post and does not create a revision.
 217              // Convert the post object to an array and add slashes, wp_update_post expects escaped array.
 218              $autosave_id = wp_update_post( wp_slash( (array) $prepared_post ), true );
 219          } else {
 220              // Non-draft posts: create or update the post autosave.
 221              $autosave_id = $this->create_post_autosave( (array) $prepared_post );
 222          }
 223  
 224          if ( is_wp_error( $autosave_id ) ) {
 225              return $autosave_id;
 226          }
 227  
 228          $autosave = get_post( $autosave_id );
 229          $request->set_param( 'context', 'edit' );
 230  
 231          $response = $this->prepare_item_for_response( $autosave, $request );
 232          $response = rest_ensure_response( $response );
 233  
 234          return $response;
 235      }
 236  
 237      /**
 238       * Get the autosave, if the ID is valid.
 239       *
 240       * @since 5.0.0
 241       *
 242       * @param WP_REST_Request $request Full data about the request.
 243       * @return WP_Post|WP_Error Revision post object if ID is valid, WP_Error otherwise.
 244       */
 245  	public function get_item( $request ) {
 246          $parent_id = (int) $request->get_param( 'parent' );
 247  
 248          if ( $parent_id <= 0 ) {
 249              return new WP_Error( 'rest_post_invalid_id', __( 'Invalid post parent ID.' ), array( 'status' => 404 ) );
 250          }
 251  
 252          $autosave = wp_get_post_autosave( $parent_id );
 253  
 254          if ( ! $autosave ) {
 255              return new WP_Error( 'rest_post_no_autosave', __( 'There is no autosave revision for this post.' ), array( 'status' => 404 ) );
 256          }
 257  
 258          $response = $this->prepare_item_for_response( $autosave, $request );
 259          return $response;
 260      }
 261  
 262      /**
 263       * Gets a collection of autosaves using wp_get_post_autosave.
 264       *
 265       * Contains the user's autosave, for empty if it doesn't exist.
 266       *
 267       * @since 5.0.0
 268       *
 269       * @param WP_REST_Request $request Full data about the request.
 270       * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
 271       */
 272  	public function get_items( $request ) {
 273          $parent = $this->get_parent( $request['id'] );
 274          if ( is_wp_error( $parent ) ) {
 275              return $parent;
 276          }
 277  
 278          $response  = array();
 279          $parent_id = $parent->ID;
 280          $revisions = wp_get_post_revisions( $parent_id, array( 'check_enabled' => false ) );
 281  
 282          foreach ( $revisions as $revision ) {
 283              if ( false !== strpos( $revision->post_name, "{$parent_id}-autosave" ) ) {
 284                  $data       = $this->prepare_item_for_response( $revision, $request );
 285                  $response[] = $this->prepare_response_for_collection( $data );
 286              }
 287          }
 288  
 289          return rest_ensure_response( $response );
 290      }
 291  
 292  
 293      /**
 294       * Retrieves the autosave's schema, conforming to JSON Schema.
 295       *
 296       * @since 5.0.0
 297       *
 298       * @return array Item schema data.
 299       */
 300  	public function get_item_schema() {
 301          if ( $this->schema ) {
 302              return $this->add_additional_fields_schema( $this->schema );
 303          }
 304  
 305          $schema = $this->revisions_controller->get_item_schema();
 306  
 307          $schema['properties']['preview_link'] = array(
 308              'description' => __( 'Preview link for the post.' ),
 309              'type'        => 'string',
 310              'format'      => 'uri',
 311              'context'     => array( 'edit' ),
 312              'readonly'    => true,
 313          );
 314  
 315          $this->schema = $schema;
 316          return $this->add_additional_fields_schema( $this->schema );
 317      }
 318  
 319      /**
 320       * Creates autosave for the specified post.
 321       *
 322       * From wp-admin/post.php.
 323       *
 324       * @since 5.0.0
 325       *
 326       * @param mixed $post_data Associative array containing the post data.
 327       * @return mixed The autosave revision ID or WP_Error.
 328       */
 329  	public function create_post_autosave( $post_data ) {
 330  
 331          $post_id = (int) $post_data['ID'];
 332          $post    = get_post( $post_id );
 333  
 334          if ( is_wp_error( $post ) ) {
 335              return $post;
 336          }
 337  
 338          $user_id = get_current_user_id();
 339  
 340          // Store one autosave per author. If there is already an autosave, overwrite it.
 341          $old_autosave = wp_get_post_autosave( $post_id, $user_id );
 342  
 343          if ( $old_autosave ) {
 344              $new_autosave                = _wp_post_revision_data( $post_data, true );
 345              $new_autosave['ID']          = $old_autosave->ID;
 346              $new_autosave['post_author'] = $user_id;
 347  
 348              // If the new autosave has the same content as the post, delete the autosave.
 349              $autosave_is_different = false;
 350  
 351              foreach ( array_intersect( array_keys( $new_autosave ), array_keys( _wp_post_revision_fields( $post ) ) ) as $field ) {
 352                  if ( normalize_whitespace( $new_autosave[ $field ] ) != normalize_whitespace( $post->$field ) ) {
 353                      $autosave_is_different = true;
 354                      break;
 355                  }
 356              }
 357  
 358              if ( ! $autosave_is_different ) {
 359                  wp_delete_post_revision( $old_autosave->ID );
 360                  return new WP_Error( 'rest_autosave_no_changes', __( 'There is nothing to save. The autosave and the post content are the same.' ), array( 'status' => 400 ) );
 361              }
 362  
 363              /** This filter is documented in wp-admin/post.php */
 364              do_action( 'wp_creating_autosave', $new_autosave );
 365  
 366              // wp_update_post expects escaped array.
 367              return wp_update_post( wp_slash( $new_autosave ) );
 368          }
 369  
 370          // Create the new autosave as a special post revision.
 371          return _wp_put_post_revision( $post_data, true );
 372      }
 373  
 374      /**
 375       * Prepares the revision for the REST response.
 376       *
 377       * @since 5.0.0
 378       *
 379       * @param WP_Post         $post    Post revision object.
 380       * @param WP_REST_Request $request Request object.
 381       *
 382       * @return WP_REST_Response Response object.
 383       */
 384  	public function prepare_item_for_response( $post, $request ) {
 385  
 386          $response = $this->revisions_controller->prepare_item_for_response( $post, $request );
 387  
 388          $fields = $this->get_fields_for_response( $request );
 389  
 390          if ( in_array( 'preview_link', $fields, true ) ) {
 391              $parent_id          = wp_is_post_autosave( $post );
 392              $preview_post_id    = false === $parent_id ? $post->ID : $parent_id;
 393              $preview_query_args = array();
 394  
 395              if ( false !== $parent_id ) {
 396                  $preview_query_args['preview_id']    = $parent_id;
 397                  $preview_query_args['preview_nonce'] = wp_create_nonce( 'post_preview_' . $parent_id );
 398              }
 399  
 400              $response->data['preview_link'] = get_preview_post_link( $preview_post_id, $preview_query_args );
 401          }
 402  
 403          $context        = ! empty( $request['context'] ) ? $request['context'] : 'view';
 404          $response->data = $this->add_additional_fields_to_object( $response->data, $request );
 405          $response->data = $this->filter_response_by_context( $response->data, $context );
 406  
 407          /**
 408           * Filters a revision returned from the API.
 409           *
 410           * Allows modification of the revision right before it is returned.
 411           *
 412           * @since 5.0.0
 413           *
 414           * @param WP_REST_Response $response The response object.
 415           * @param WP_Post          $post     The original revision object.
 416           * @param WP_REST_Request  $request  Request used to generate the response.
 417           */
 418          return apply_filters( 'rest_prepare_autosave', $response, $post, $request );
 419      }
 420  
 421      /**
 422       * Retrieves the query params for the autosaves collection.
 423       *
 424       * @since 5.0.0
 425       *
 426       * @return array Collection parameters.
 427       */
 428  	public function get_collection_params() {
 429          return array(
 430              'context' => $this->get_context_param( array( 'default' => 'view' ) ),
 431          );
 432      }
 433  }


Generated: Wed Oct 23 08:20:01 2019 Cross-referenced by PHPXref 0.7