[ Index ]

WordPress 5.4.1

[ Index ]     [ Classes ]     [ Functions ]     [ Variables ]     [ Constants ]     [ Statistics ]    

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 the 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 details 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  
 165          if ( ! current_user_can( $parent_post_type_obj->cap->edit_post, $parent->ID ) ) {
 166              return new WP_Error(
 167                  'rest_cannot_read',
 168                  __( 'Sorry, you are not allowed to view autosaves of this post.' ),
 169                  array( 'status' => rest_authorization_required_code() )
 170              );
 171          }
 172  
 173          return true;
 174      }
 175  
 176      /**
 177       * Checks if a given request has access to create an autosave revision.
 178       *
 179       * Autosave revisions inherit permissions from the parent post,
 180       * check if the current user has permission to edit the post.
 181       *
 182       * @since 5.0.0
 183       *
 184       * @param WP_REST_Request $request Full details about the request.
 185       * @return true|WP_Error True if the request has access to create the item, WP_Error object otherwise.
 186       */
 187  	public function create_item_permissions_check( $request ) {
 188          $id = $request->get_param( 'id' );
 189  
 190          if ( empty( $id ) ) {
 191              return new WP_Error(
 192                  'rest_post_invalid_id',
 193                  __( 'Invalid item ID.' ),
 194                  array( 'status' => 404 )
 195              );
 196          }
 197  
 198          return $this->parent_controller->update_item_permissions_check( $request );
 199      }
 200  
 201      /**
 202       * Creates, updates or deletes an autosave revision.
 203       *
 204       * @since 5.0.0
 205       *
 206       * @param WP_REST_Request $request Full details about the request.
 207       * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
 208       */
 209  	public function create_item( $request ) {
 210  
 211          if ( ! defined( 'DOING_AUTOSAVE' ) ) {
 212              define( 'DOING_AUTOSAVE', true );
 213          }
 214  
 215          $post = get_post( $request['id'] );
 216  
 217          if ( is_wp_error( $post ) ) {
 218              return $post;
 219          }
 220  
 221          $prepared_post     = $this->parent_controller->prepare_item_for_database( $request );
 222          $prepared_post->ID = $post->ID;
 223          $user_id           = get_current_user_id();
 224  
 225          if ( ( 'draft' === $post->post_status || 'auto-draft' === $post->post_status ) && $post->post_author == $user_id ) {
 226              // Draft posts for the same author: autosaving updates the post and does not create a revision.
 227              // Convert the post object to an array and add slashes, wp_update_post() expects escaped array.
 228              $autosave_id = wp_update_post( wp_slash( (array) $prepared_post ), true );
 229          } else {
 230              // Non-draft posts: create or update the post autosave.
 231              $autosave_id = $this->create_post_autosave( (array) $prepared_post );
 232          }
 233  
 234          if ( is_wp_error( $autosave_id ) ) {
 235              return $autosave_id;
 236          }
 237  
 238          $autosave = get_post( $autosave_id );
 239          $request->set_param( 'context', 'edit' );
 240  
 241          $response = $this->prepare_item_for_response( $autosave, $request );
 242          $response = rest_ensure_response( $response );
 243  
 244          return $response;
 245      }
 246  
 247      /**
 248       * Get the autosave, if the ID is valid.
 249       *
 250       * @since 5.0.0
 251       *
 252       * @param WP_REST_Request $request Full details about the request.
 253       * @return WP_Post|WP_Error Revision post object if ID is valid, WP_Error otherwise.
 254       */
 255  	public function get_item( $request ) {
 256          $parent_id = (int) $request->get_param( 'parent' );
 257  
 258          if ( $parent_id <= 0 ) {
 259              return new WP_Error(
 260                  'rest_post_invalid_id',
 261                  __( 'Invalid post parent ID.' ),
 262                  array( 'status' => 404 )
 263              );
 264          }
 265  
 266          $autosave = wp_get_post_autosave( $parent_id );
 267  
 268          if ( ! $autosave ) {
 269              return new WP_Error(
 270                  'rest_post_no_autosave',
 271                  __( 'There is no autosave revision for this post.' ),
 272                  array( 'status' => 404 )
 273              );
 274          }
 275  
 276          $response = $this->prepare_item_for_response( $autosave, $request );
 277          return $response;
 278      }
 279  
 280      /**
 281       * Gets a collection of autosaves using wp_get_post_autosave.
 282       *
 283       * Contains the user's autosave, for empty if it doesn't exist.
 284       *
 285       * @since 5.0.0
 286       *
 287       * @param WP_REST_Request $request Full details about the request.
 288       * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
 289       */
 290  	public function get_items( $request ) {
 291          $parent = $this->get_parent( $request['id'] );
 292          if ( is_wp_error( $parent ) ) {
 293              return $parent;
 294          }
 295  
 296          $response  = array();
 297          $parent_id = $parent->ID;
 298          $revisions = wp_get_post_revisions( $parent_id, array( 'check_enabled' => false ) );
 299  
 300          foreach ( $revisions as $revision ) {
 301              if ( false !== strpos( $revision->post_name, "{$parent_id}-autosave" ) ) {
 302                  $data       = $this->prepare_item_for_response( $revision, $request );
 303                  $response[] = $this->prepare_response_for_collection( $data );
 304              }
 305          }
 306  
 307          return rest_ensure_response( $response );
 308      }
 309  
 310  
 311      /**
 312       * Retrieves the autosave's schema, conforming to JSON Schema.
 313       *
 314       * @since 5.0.0
 315       *
 316       * @return array Item schema data.
 317       */
 318  	public function get_item_schema() {
 319          if ( $this->schema ) {
 320              return $this->add_additional_fields_schema( $this->schema );
 321          }
 322  
 323          $schema = $this->revisions_controller->get_item_schema();
 324  
 325          $schema['properties']['preview_link'] = array(
 326              'description' => __( 'Preview link for the post.' ),
 327              'type'        => 'string',
 328              'format'      => 'uri',
 329              'context'     => array( 'edit' ),
 330              'readonly'    => true,
 331          );
 332  
 333          $this->schema = $schema;
 334  
 335          return $this->add_additional_fields_schema( $this->schema );
 336      }
 337  
 338      /**
 339       * Creates autosave for the specified post.
 340       *
 341       * From wp-admin/post.php.
 342       *
 343       * @since 5.0.0
 344       *
 345       * @param array $post_data Associative array containing the post data.
 346       * @return mixed The autosave revision ID or WP_Error.
 347       */
 348  	public function create_post_autosave( $post_data ) {
 349  
 350          $post_id = (int) $post_data['ID'];
 351          $post    = get_post( $post_id );
 352  
 353          if ( is_wp_error( $post ) ) {
 354              return $post;
 355          }
 356  
 357          $user_id = get_current_user_id();
 358  
 359          // Store one autosave per author. If there is already an autosave, overwrite it.
 360          $old_autosave = wp_get_post_autosave( $post_id, $user_id );
 361  
 362          if ( $old_autosave ) {
 363              $new_autosave                = _wp_post_revision_data( $post_data, true );
 364              $new_autosave['ID']          = $old_autosave->ID;
 365              $new_autosave['post_author'] = $user_id;
 366  
 367              // If the new autosave has the same content as the post, delete the autosave.
 368              $autosave_is_different = false;
 369  
 370              foreach ( array_intersect( array_keys( $new_autosave ), array_keys( _wp_post_revision_fields( $post ) ) ) as $field ) {
 371                  if ( normalize_whitespace( $new_autosave[ $field ] ) !== normalize_whitespace( $post->$field ) ) {
 372                      $autosave_is_different = true;
 373                      break;
 374                  }
 375              }
 376  
 377              if ( ! $autosave_is_different ) {
 378                  wp_delete_post_revision( $old_autosave->ID );
 379                  return new WP_Error(
 380                      'rest_autosave_no_changes',
 381                      __( 'There is nothing to save. The autosave and the post content are the same.' ),
 382                      array( 'status' => 400 )
 383                  );
 384              }
 385  
 386              /** This filter is documented in wp-admin/post.php */
 387              do_action( 'wp_creating_autosave', $new_autosave );
 388  
 389              // wp_update_post() expects escaped array.
 390              return wp_update_post( wp_slash( $new_autosave ) );
 391          }
 392  
 393          // Create the new autosave as a special post revision.
 394          return _wp_put_post_revision( $post_data, true );
 395      }
 396  
 397      /**
 398       * Prepares the revision for the REST response.
 399       *
 400       * @since 5.0.0
 401       *
 402       * @param WP_Post         $post    Post revision object.
 403       * @param WP_REST_Request $request Request object.
 404       *
 405       * @return WP_REST_Response Response object.
 406       */
 407  	public function prepare_item_for_response( $post, $request ) {
 408  
 409          $response = $this->revisions_controller->prepare_item_for_response( $post, $request );
 410  
 411          $fields = $this->get_fields_for_response( $request );
 412  
 413          if ( in_array( 'preview_link', $fields, true ) ) {
 414              $parent_id          = wp_is_post_autosave( $post );
 415              $preview_post_id    = false === $parent_id ? $post->ID : $parent_id;
 416              $preview_query_args = array();
 417  
 418              if ( false !== $parent_id ) {
 419                  $preview_query_args['preview_id']    = $parent_id;
 420                  $preview_query_args['preview_nonce'] = wp_create_nonce( 'post_preview_' . $parent_id );
 421              }
 422  
 423              $response->data['preview_link'] = get_preview_post_link( $preview_post_id, $preview_query_args );
 424          }
 425  
 426          $context        = ! empty( $request['context'] ) ? $request['context'] : 'view';
 427          $response->data = $this->add_additional_fields_to_object( $response->data, $request );
 428          $response->data = $this->filter_response_by_context( $response->data, $context );
 429  
 430          /**
 431           * Filters a revision returned from the API.
 432           *
 433           * Allows modification of the revision right before it is returned.
 434           *
 435           * @since 5.0.0
 436           *
 437           * @param WP_REST_Response $response The response object.
 438           * @param WP_Post          $post     The original revision object.
 439           * @param WP_REST_Request  $request  Request used to generate the response.
 440           */
 441          return apply_filters( 'rest_prepare_autosave', $response, $post, $request );
 442      }
 443  
 444      /**
 445       * Retrieves the query params for the autosaves collection.
 446       *
 447       * @since 5.0.0
 448       *
 449       * @return array Collection parameters.
 450       */
 451  	public function get_collection_params() {
 452          return array(
 453              'context' => $this->get_context_param( array( 'default' => 'view' ) ),
 454          );
 455      }
 456  }


Generated: Tue May 19 15:51:04 2020 Cross-referenced by PHPXref 0.7.1