[ Index ]

PHP Cross Reference of WordPress

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


Generated: Wed Sep 18 01:00:03 2019 Cross-referenced by PHPXref 0.7.1