[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

title

Body

[close]

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

   1  <?php
   2  /**
   3   * REST API: WP_REST_Posts_Controller class
   4   *
   5   * @package WordPress
   6   * @subpackage REST_API
   7   * @since 4.7.0
   8   */
   9  
  10  /**
  11   * Core class to access posts via the REST API.
  12   *
  13   * @since 4.7.0
  14   *
  15   * @see WP_REST_Controller
  16   */
  17  class WP_REST_Posts_Controller extends WP_REST_Controller {
  18      /**
  19       * Post type.
  20       *
  21       * @since 4.7.0
  22       * @var string
  23       */
  24      protected $post_type;
  25  
  26      /**
  27       * Instance of a post meta fields object.
  28       *
  29       * @since 4.7.0
  30       * @var WP_REST_Post_Meta_Fields
  31       */
  32      protected $meta;
  33  
  34      /**
  35       * Constructor.
  36       *
  37       * @since 4.7.0
  38       *
  39       * @param string $post_type Post type.
  40       */
  41  	public function __construct( $post_type ) {
  42          $this->post_type = $post_type;
  43          $this->namespace = 'wp/v2';
  44          $obj             = get_post_type_object( $post_type );
  45          $this->rest_base = ! empty( $obj->rest_base ) ? $obj->rest_base : $obj->name;
  46  
  47          $this->meta = new WP_REST_Post_Meta_Fields( $this->post_type );
  48      }
  49  
  50      /**
  51       * Registers the routes for the objects of the controller.
  52       *
  53       * @since 4.7.0
  54       *
  55       * @see register_rest_route()
  56       */
  57  	public function register_routes() {
  58  
  59          register_rest_route(
  60              $this->namespace,
  61              '/' . $this->rest_base,
  62              array(
  63                  array(
  64                      'methods'             => WP_REST_Server::READABLE,
  65                      'callback'            => array( $this, 'get_items' ),
  66                      'permission_callback' => array( $this, 'get_items_permissions_check' ),
  67                      'args'                => $this->get_collection_params(),
  68                  ),
  69                  array(
  70                      'methods'             => WP_REST_Server::CREATABLE,
  71                      'callback'            => array( $this, 'create_item' ),
  72                      'permission_callback' => array( $this, 'create_item_permissions_check' ),
  73                      'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ),
  74                  ),
  75                  'schema' => array( $this, 'get_public_item_schema' ),
  76              )
  77          );
  78  
  79          $schema        = $this->get_item_schema();
  80          $get_item_args = array(
  81              'context' => $this->get_context_param( array( 'default' => 'view' ) ),
  82          );
  83          if ( isset( $schema['properties']['password'] ) ) {
  84              $get_item_args['password'] = array(
  85                  'description' => __( 'The password for the post if it is password protected.' ),
  86                  'type'        => 'string',
  87              );
  88          }
  89          register_rest_route(
  90              $this->namespace,
  91              '/' . $this->rest_base . '/(?P<id>[\d]+)',
  92              array(
  93                  'args'   => array(
  94                      'id' => array(
  95                          'description' => __( 'Unique identifier for the object.' ),
  96                          'type'        => 'integer',
  97                      ),
  98                  ),
  99                  array(
 100                      'methods'             => WP_REST_Server::READABLE,
 101                      'callback'            => array( $this, 'get_item' ),
 102                      'permission_callback' => array( $this, 'get_item_permissions_check' ),
 103                      'args'                => $get_item_args,
 104                  ),
 105                  array(
 106                      'methods'             => WP_REST_Server::EDITABLE,
 107                      'callback'            => array( $this, 'update_item' ),
 108                      'permission_callback' => array( $this, 'update_item_permissions_check' ),
 109                      'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
 110                  ),
 111                  array(
 112                      'methods'             => WP_REST_Server::DELETABLE,
 113                      'callback'            => array( $this, 'delete_item' ),
 114                      'permission_callback' => array( $this, 'delete_item_permissions_check' ),
 115                      'args'                => array(
 116                          'force' => array(
 117                              'type'        => 'boolean',
 118                              'default'     => false,
 119                              'description' => __( 'Whether to bypass trash and force deletion.' ),
 120                          ),
 121                      ),
 122                  ),
 123                  'schema' => array( $this, 'get_public_item_schema' ),
 124              )
 125          );
 126      }
 127  
 128      /**
 129       * Checks if a given request has access to read posts.
 130       *
 131       * @since 4.7.0
 132       *
 133       * @param WP_REST_Request $request Full details about the request.
 134       * @return true|WP_Error True if the request has read access, WP_Error object otherwise.
 135       */
 136  	public function get_items_permissions_check( $request ) {
 137  
 138          $post_type = get_post_type_object( $this->post_type );
 139  
 140          if ( 'edit' === $request['context'] && ! current_user_can( $post_type->cap->edit_posts ) ) {
 141              return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you are not allowed to edit posts in this post type.' ), array( 'status' => rest_authorization_required_code() ) );
 142          }
 143  
 144          return true;
 145      }
 146  
 147      /**
 148       * Retrieves a collection of posts.
 149       *
 150       * @since 4.7.0
 151       *
 152       * @param WP_REST_Request $request Full details about the request.
 153       * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
 154       */
 155  	public function get_items( $request ) {
 156  
 157          // Ensure a search string is set in case the orderby is set to 'relevance'.
 158          if ( ! empty( $request['orderby'] ) && 'relevance' === $request['orderby'] && empty( $request['search'] ) ) {
 159              return new WP_Error( 'rest_no_search_term_defined', __( 'You need to define a search term to order by relevance.' ), array( 'status' => 400 ) );
 160          }
 161  
 162          // Ensure an include parameter is set in case the orderby is set to 'include'.
 163          if ( ! empty( $request['orderby'] ) && 'include' === $request['orderby'] && empty( $request['include'] ) ) {
 164              return new WP_Error( 'rest_orderby_include_missing_include', __( 'You need to define an include parameter to order by include.' ), array( 'status' => 400 ) );
 165          }
 166  
 167          // Retrieve the list of registered collection query parameters.
 168          $registered = $this->get_collection_params();
 169          $args       = array();
 170  
 171          /*
 172           * This array defines mappings between public API query parameters whose
 173           * values are accepted as-passed, and their internal WP_Query parameter
 174           * name equivalents (some are the same). Only values which are also
 175           * present in $registered will be set.
 176           */
 177          $parameter_mappings = array(
 178              'author'         => 'author__in',
 179              'author_exclude' => 'author__not_in',
 180              'exclude'        => 'post__not_in',
 181              'include'        => 'post__in',
 182              'menu_order'     => 'menu_order',
 183              'offset'         => 'offset',
 184              'order'          => 'order',
 185              'orderby'        => 'orderby',
 186              'page'           => 'paged',
 187              'parent'         => 'post_parent__in',
 188              'parent_exclude' => 'post_parent__not_in',
 189              'search'         => 's',
 190              'slug'           => 'post_name__in',
 191              'status'         => 'post_status',
 192          );
 193  
 194          /*
 195           * For each known parameter which is both registered and present in the request,
 196           * set the parameter's value on the query $args.
 197           */
 198          foreach ( $parameter_mappings as $api_param => $wp_param ) {
 199              if ( isset( $registered[ $api_param ], $request[ $api_param ] ) ) {
 200                  $args[ $wp_param ] = $request[ $api_param ];
 201              }
 202          }
 203  
 204          // Check for & assign any parameters which require special handling or setting.
 205          $args['date_query'] = array();
 206  
 207          // Set before into date query. Date query must be specified as an array of an array.
 208          if ( isset( $registered['before'], $request['before'] ) ) {
 209              $args['date_query'][0]['before'] = $request['before'];
 210          }
 211  
 212          // Set after into date query. Date query must be specified as an array of an array.
 213          if ( isset( $registered['after'], $request['after'] ) ) {
 214              $args['date_query'][0]['after'] = $request['after'];
 215          }
 216  
 217          // Ensure our per_page parameter overrides any provided posts_per_page filter.
 218          if ( isset( $registered['per_page'] ) ) {
 219              $args['posts_per_page'] = $request['per_page'];
 220          }
 221  
 222          if ( isset( $registered['sticky'], $request['sticky'] ) ) {
 223              $sticky_posts = get_option( 'sticky_posts', array() );
 224              if ( ! is_array( $sticky_posts ) ) {
 225                  $sticky_posts = array();
 226              }
 227              if ( $request['sticky'] ) {
 228                  /*
 229                   * As post__in will be used to only get sticky posts,
 230                   * we have to support the case where post__in was already
 231                   * specified.
 232                   */
 233                  $args['post__in'] = $args['post__in'] ? array_intersect( $sticky_posts, $args['post__in'] ) : $sticky_posts;
 234  
 235                  /*
 236                   * If we intersected, but there are no post ids in common,
 237                   * WP_Query won't return "no posts" for post__in = array()
 238                   * so we have to fake it a bit.
 239                   */
 240                  if ( ! $args['post__in'] ) {
 241                      $args['post__in'] = array( 0 );
 242                  }
 243              } elseif ( $sticky_posts ) {
 244                  /*
 245                   * As post___not_in will be used to only get posts that
 246                   * are not sticky, we have to support the case where post__not_in
 247                   * was already specified.
 248                   */
 249                  $args['post__not_in'] = array_merge( $args['post__not_in'], $sticky_posts );
 250              }
 251          }
 252  
 253          // Force the post_type argument, since it's not a user input variable.
 254          $args['post_type'] = $this->post_type;
 255  
 256          /**
 257           * Filters the query arguments for a request.
 258           *
 259           * Enables adding extra arguments or setting defaults for a post collection request.
 260           *
 261           * @since 4.7.0
 262           *
 263           * @link https://developer.wordpress.org/reference/classes/wp_query/
 264           *
 265           * @param array           $args    Key value array of query var to query value.
 266           * @param WP_REST_Request $request The request used.
 267           */
 268          $args       = apply_filters( "rest_{$this->post_type}_query", $args, $request );
 269          $query_args = $this->prepare_items_query( $args, $request );
 270  
 271          $taxonomies = wp_list_filter( get_object_taxonomies( $this->post_type, 'objects' ), array( 'show_in_rest' => true ) );
 272  
 273          foreach ( $taxonomies as $taxonomy ) {
 274              $base        = ! empty( $taxonomy->rest_base ) ? $taxonomy->rest_base : $taxonomy->name;
 275              $tax_exclude = $base . '_exclude';
 276  
 277              if ( ! empty( $request[ $base ] ) ) {
 278                  $query_args['tax_query'][] = array(
 279                      'taxonomy'         => $taxonomy->name,
 280                      'field'            => 'term_id',
 281                      'terms'            => $request[ $base ],
 282                      'include_children' => false,
 283                  );
 284              }
 285  
 286              if ( ! empty( $request[ $tax_exclude ] ) ) {
 287                  $query_args['tax_query'][] = array(
 288                      'taxonomy'         => $taxonomy->name,
 289                      'field'            => 'term_id',
 290                      'terms'            => $request[ $tax_exclude ],
 291                      'include_children' => false,
 292                      'operator'         => 'NOT IN',
 293                  );
 294              }
 295          }
 296  
 297          $posts_query  = new WP_Query();
 298          $query_result = $posts_query->query( $query_args );
 299  
 300          // Allow access to all password protected posts if the context is edit.
 301          if ( 'edit' === $request['context'] ) {
 302              add_filter( 'post_password_required', '__return_false' );
 303          }
 304  
 305          $posts = array();
 306  
 307          foreach ( $query_result as $post ) {
 308              if ( ! $this->check_read_permission( $post ) ) {
 309                  continue;
 310              }
 311  
 312              $data    = $this->prepare_item_for_response( $post, $request );
 313              $posts[] = $this->prepare_response_for_collection( $data );
 314          }
 315  
 316          // Reset filter.
 317          if ( 'edit' === $request['context'] ) {
 318              remove_filter( 'post_password_required', '__return_false' );
 319          }
 320  
 321          $page        = (int) $query_args['paged'];
 322          $total_posts = $posts_query->found_posts;
 323  
 324          if ( $total_posts < 1 ) {
 325              // Out-of-bounds, run the query again without LIMIT for total count.
 326              unset( $query_args['paged'] );
 327  
 328              $count_query = new WP_Query();
 329              $count_query->query( $query_args );
 330              $total_posts = $count_query->found_posts;
 331          }
 332  
 333          $max_pages = ceil( $total_posts / (int) $posts_query->query_vars['posts_per_page'] );
 334  
 335          if ( $page > $max_pages && $total_posts > 0 ) {
 336              return new WP_Error( 'rest_post_invalid_page_number', __( 'The page number requested is larger than the number of pages available.' ), array( 'status' => 400 ) );
 337          }
 338  
 339          $response = rest_ensure_response( $posts );
 340  
 341          $response->header( 'X-WP-Total', (int) $total_posts );
 342          $response->header( 'X-WP-TotalPages', (int) $max_pages );
 343  
 344          $request_params = $request->get_query_params();
 345          $base           = add_query_arg( urlencode_deep( $request_params ), rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ) );
 346  
 347          if ( $page > 1 ) {
 348              $prev_page = $page - 1;
 349  
 350              if ( $prev_page > $max_pages ) {
 351                  $prev_page = $max_pages;
 352              }
 353  
 354              $prev_link = add_query_arg( 'page', $prev_page, $base );
 355              $response->link_header( 'prev', $prev_link );
 356          }
 357          if ( $max_pages > $page ) {
 358              $next_page = $page + 1;
 359              $next_link = add_query_arg( 'page', $next_page, $base );
 360  
 361              $response->link_header( 'next', $next_link );
 362          }
 363  
 364          return $response;
 365      }
 366  
 367      /**
 368       * Get the post, if the ID is valid.
 369       *
 370       * @since 4.7.2
 371       *
 372       * @param int $id Supplied ID.
 373       * @return WP_Post|WP_Error Post object if ID is valid, WP_Error otherwise.
 374       */
 375  	protected function get_post( $id ) {
 376          $error = new WP_Error( 'rest_post_invalid_id', __( 'Invalid post ID.' ), array( 'status' => 404 ) );
 377          if ( (int) $id <= 0 ) {
 378              return $error;
 379          }
 380  
 381          $post = get_post( (int) $id );
 382          if ( empty( $post ) || empty( $post->ID ) || $this->post_type !== $post->post_type ) {
 383              return $error;
 384          }
 385  
 386          return $post;
 387      }
 388  
 389      /**
 390       * Checks if a given request has access to read a post.
 391       *
 392       * @since 4.7.0
 393       *
 394       * @param WP_REST_Request $request Full details about the request.
 395       * @return bool|WP_Error True if the request has read access for the item, WP_Error object otherwise.
 396       */
 397  	public function get_item_permissions_check( $request ) {
 398          $post = $this->get_post( $request['id'] );
 399          if ( is_wp_error( $post ) ) {
 400              return $post;
 401          }
 402  
 403          if ( 'edit' === $request['context'] && $post && ! $this->check_update_permission( $post ) ) {
 404              return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you are not allowed to edit this post.' ), array( 'status' => rest_authorization_required_code() ) );
 405          }
 406  
 407          if ( $post && ! empty( $request['password'] ) ) {
 408              // Check post password, and return error if invalid.
 409              if ( ! hash_equals( $post->post_password, $request['password'] ) ) {
 410                  return new WP_Error( 'rest_post_incorrect_password', __( 'Incorrect post password.' ), array( 'status' => 403 ) );
 411              }
 412          }
 413  
 414          // Allow access to all password protected posts if the context is edit.
 415          if ( 'edit' === $request['context'] ) {
 416              add_filter( 'post_password_required', '__return_false' );
 417          }
 418  
 419          if ( $post ) {
 420              return $this->check_read_permission( $post );
 421          }
 422  
 423          return true;
 424      }
 425  
 426      /**
 427       * Checks if the user can access password-protected content.
 428       *
 429       * This method determines whether we need to override the regular password
 430       * check in core with a filter.
 431       *
 432       * @since 4.7.0
 433       *
 434       * @param WP_Post         $post    Post to check against.
 435       * @param WP_REST_Request $request Request data to check.
 436       * @return bool True if the user can access password-protected content, otherwise false.
 437       */
 438  	public function can_access_password_content( $post, $request ) {
 439          if ( empty( $post->post_password ) ) {
 440              // No filter required.
 441              return false;
 442          }
 443  
 444          // Edit context always gets access to password-protected posts.
 445          if ( 'edit' === $request['context'] ) {
 446              return true;
 447          }
 448  
 449          // No password, no auth.
 450          if ( empty( $request['password'] ) ) {
 451              return false;
 452          }
 453  
 454          // Double-check the request password.
 455          return hash_equals( $post->post_password, $request['password'] );
 456      }
 457  
 458      /**
 459       * Retrieves a single post.
 460       *
 461       * @since 4.7.0
 462       *
 463       * @param WP_REST_Request $request Full details about the request.
 464       * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
 465       */
 466  	public function get_item( $request ) {
 467          $post = $this->get_post( $request['id'] );
 468          if ( is_wp_error( $post ) ) {
 469              return $post;
 470          }
 471  
 472          $data     = $this->prepare_item_for_response( $post, $request );
 473          $response = rest_ensure_response( $data );
 474  
 475          if ( is_post_type_viewable( get_post_type_object( $post->post_type ) ) ) {
 476              $response->link_header( 'alternate', get_permalink( $post->ID ), array( 'type' => 'text/html' ) );
 477          }
 478  
 479          return $response;
 480      }
 481  
 482      /**
 483       * Checks if a given request has access to create a post.
 484       *
 485       * @since 4.7.0
 486       *
 487       * @param WP_REST_Request $request Full details about the request.
 488       * @return true|WP_Error True if the request has access to create items, WP_Error object otherwise.
 489       */
 490  	public function create_item_permissions_check( $request ) {
 491          if ( ! empty( $request['id'] ) ) {
 492              return new WP_Error( 'rest_post_exists', __( 'Cannot create existing post.' ), array( 'status' => 400 ) );
 493          }
 494  
 495          $post_type = get_post_type_object( $this->post_type );
 496  
 497          if ( ! empty( $request['author'] ) && get_current_user_id() !== $request['author'] && ! current_user_can( $post_type->cap->edit_others_posts ) ) {
 498              return new WP_Error( 'rest_cannot_edit_others', __( 'Sorry, you are not allowed to create posts as this user.' ), array( 'status' => rest_authorization_required_code() ) );
 499          }
 500  
 501          if ( ! empty( $request['sticky'] ) && ! current_user_can( $post_type->cap->edit_others_posts ) ) {
 502              return new WP_Error( 'rest_cannot_assign_sticky', __( 'Sorry, you are not allowed to make posts sticky.' ), array( 'status' => rest_authorization_required_code() ) );
 503          }
 504  
 505          if ( ! current_user_can( $post_type->cap->create_posts ) ) {
 506              return new WP_Error( 'rest_cannot_create', __( 'Sorry, you are not allowed to create posts as this user.' ), array( 'status' => rest_authorization_required_code() ) );
 507          }
 508  
 509          if ( ! $this->check_assign_terms_permission( $request ) ) {
 510              return new WP_Error( 'rest_cannot_assign_term', __( 'Sorry, you are not allowed to assign the provided terms.' ), array( 'status' => rest_authorization_required_code() ) );
 511          }
 512  
 513          return true;
 514      }
 515  
 516      /**
 517       * Creates a single post.
 518       *
 519       * @since 4.7.0
 520       *
 521       * @param WP_REST_Request $request Full details about the request.
 522       * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
 523       */
 524  	public function create_item( $request ) {
 525          if ( ! empty( $request['id'] ) ) {
 526              return new WP_Error( 'rest_post_exists', __( 'Cannot create existing post.' ), array( 'status' => 400 ) );
 527          }
 528  
 529          $prepared_post = $this->prepare_item_for_database( $request );
 530  
 531          if ( is_wp_error( $prepared_post ) ) {
 532              return $prepared_post;
 533          }
 534  
 535          $prepared_post->post_type = $this->post_type;
 536  
 537          $post_id = wp_insert_post( wp_slash( (array) $prepared_post ), true );
 538  
 539          if ( is_wp_error( $post_id ) ) {
 540  
 541              if ( 'db_insert_error' === $post_id->get_error_code() ) {
 542                  $post_id->add_data( array( 'status' => 500 ) );
 543              } else {
 544                  $post_id->add_data( array( 'status' => 400 ) );
 545              }
 546  
 547              return $post_id;
 548          }
 549  
 550          $post = get_post( $post_id );
 551  
 552          /**
 553           * Fires after a single post is created or updated via the REST API.
 554           *
 555           * The dynamic portion of the hook name, `$this->post_type`, refers to the post type slug.
 556           *
 557           * @since 4.7.0
 558           *
 559           * @param WP_Post         $post     Inserted or updated post object.
 560           * @param WP_REST_Request $request  Request object.
 561           * @param bool            $creating True when creating a post, false when updating.
 562           */
 563          do_action( "rest_insert_{$this->post_type}", $post, $request, true );
 564  
 565          $schema = $this->get_item_schema();
 566  
 567          if ( ! empty( $schema['properties']['sticky'] ) ) {
 568              if ( ! empty( $request['sticky'] ) ) {
 569                  stick_post( $post_id );
 570              } else {
 571                  unstick_post( $post_id );
 572              }
 573          }
 574  
 575          if ( ! empty( $schema['properties']['featured_media'] ) && isset( $request['featured_media'] ) ) {
 576              $this->handle_featured_media( $request['featured_media'], $post_id );
 577          }
 578  
 579          if ( ! empty( $schema['properties']['format'] ) && ! empty( $request['format'] ) ) {
 580              set_post_format( $post, $request['format'] );
 581          }
 582  
 583          if ( ! empty( $schema['properties']['template'] ) && isset( $request['template'] ) ) {
 584              $this->handle_template( $request['template'], $post_id, true );
 585          }
 586  
 587          $terms_update = $this->handle_terms( $post_id, $request );
 588  
 589          if ( is_wp_error( $terms_update ) ) {
 590              return $terms_update;
 591          }
 592  
 593          if ( ! empty( $schema['properties']['meta'] ) && isset( $request['meta'] ) ) {
 594              $meta_update = $this->meta->update_value( $request['meta'], $post_id );
 595  
 596              if ( is_wp_error( $meta_update ) ) {
 597                  return $meta_update;
 598              }
 599          }
 600  
 601          $post          = get_post( $post_id );
 602          $fields_update = $this->update_additional_fields_for_object( $post, $request );
 603  
 604          if ( is_wp_error( $fields_update ) ) {
 605              return $fields_update;
 606          }
 607  
 608          $request->set_param( 'context', 'edit' );
 609  
 610          /**
 611           * Fires after a single post is completely created or updated via the REST API.
 612           *
 613           * The dynamic portion of the hook name, `$this->post_type`, refers to the post type slug.
 614           *
 615           * @since 5.0.0
 616           *
 617           * @param WP_Post         $post     Inserted or updated post object.
 618           * @param WP_REST_Request $request  Request object.
 619           * @param bool            $creating True when creating a post, false when updating.
 620           */
 621          do_action( "rest_after_insert_{$this->post_type}", $post, $request, true );
 622  
 623          $response = $this->prepare_item_for_response( $post, $request );
 624          $response = rest_ensure_response( $response );
 625  
 626          $response->set_status( 201 );
 627          $response->header( 'Location', rest_url( sprintf( '%s/%s/%d', $this->namespace, $this->rest_base, $post_id ) ) );
 628  
 629          return $response;
 630      }
 631  
 632      /**
 633       * Checks if a given request has access to update a post.
 634       *
 635       * @since 4.7.0
 636       *
 637       * @param WP_REST_Request $request Full details about the request.
 638       * @return true|WP_Error True if the request has access to update the item, WP_Error object otherwise.
 639       */
 640  	public function update_item_permissions_check( $request ) {
 641          $post = $this->get_post( $request['id'] );
 642          if ( is_wp_error( $post ) ) {
 643              return $post;
 644          }
 645  
 646          $post_type = get_post_type_object( $this->post_type );
 647  
 648          if ( $post && ! $this->check_update_permission( $post ) ) {
 649              return new WP_Error( 'rest_cannot_edit', __( 'Sorry, you are not allowed to edit this post.' ), array( 'status' => rest_authorization_required_code() ) );
 650          }
 651  
 652          if ( ! empty( $request['author'] ) && get_current_user_id() !== $request['author'] && ! current_user_can( $post_type->cap->edit_others_posts ) ) {
 653              return new WP_Error( 'rest_cannot_edit_others', __( 'Sorry, you are not allowed to update posts as this user.' ), array( 'status' => rest_authorization_required_code() ) );
 654          }
 655  
 656          if ( ! empty( $request['sticky'] ) && ! current_user_can( $post_type->cap->edit_others_posts ) ) {
 657              return new WP_Error( 'rest_cannot_assign_sticky', __( 'Sorry, you are not allowed to make posts sticky.' ), array( 'status' => rest_authorization_required_code() ) );
 658          }
 659  
 660          if ( ! $this->check_assign_terms_permission( $request ) ) {
 661              return new WP_Error( 'rest_cannot_assign_term', __( 'Sorry, you are not allowed to assign the provided terms.' ), array( 'status' => rest_authorization_required_code() ) );
 662          }
 663  
 664          return true;
 665      }
 666  
 667      /**
 668       * Updates a single post.
 669       *
 670       * @since 4.7.0
 671       *
 672       * @param WP_REST_Request $request Full details about the request.
 673       * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
 674       */
 675  	public function update_item( $request ) {
 676          $valid_check = $this->get_post( $request['id'] );
 677          if ( is_wp_error( $valid_check ) ) {
 678              return $valid_check;
 679          }
 680  
 681          $post = $this->prepare_item_for_database( $request );
 682  
 683          if ( is_wp_error( $post ) ) {
 684              return $post;
 685          }
 686  
 687          // convert the post object to an array, otherwise wp_update_post will expect non-escaped input.
 688          $post_id = wp_update_post( wp_slash( (array) $post ), true );
 689  
 690          if ( is_wp_error( $post_id ) ) {
 691              if ( 'db_update_error' === $post_id->get_error_code() ) {
 692                  $post_id->add_data( array( 'status' => 500 ) );
 693              } else {
 694                  $post_id->add_data( array( 'status' => 400 ) );
 695              }
 696              return $post_id;
 697          }
 698  
 699          $post = get_post( $post_id );
 700  
 701          /** This action is documented in wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php */
 702          do_action( "rest_insert_{$this->post_type}", $post, $request, false );
 703  
 704          $schema = $this->get_item_schema();
 705  
 706          if ( ! empty( $schema['properties']['format'] ) && ! empty( $request['format'] ) ) {
 707              set_post_format( $post, $request['format'] );
 708          }
 709  
 710          if ( ! empty( $schema['properties']['featured_media'] ) && isset( $request['featured_media'] ) ) {
 711              $this->handle_featured_media( $request['featured_media'], $post_id );
 712          }
 713  
 714          if ( ! empty( $schema['properties']['sticky'] ) && isset( $request['sticky'] ) ) {
 715              if ( ! empty( $request['sticky'] ) ) {
 716                  stick_post( $post_id );
 717              } else {
 718                  unstick_post( $post_id );
 719              }
 720          }
 721  
 722          if ( ! empty( $schema['properties']['template'] ) && isset( $request['template'] ) ) {
 723              $this->handle_template( $request['template'], $post->ID );
 724          }
 725  
 726          $terms_update = $this->handle_terms( $post->ID, $request );
 727  
 728          if ( is_wp_error( $terms_update ) ) {
 729              return $terms_update;
 730          }
 731  
 732          if ( ! empty( $schema['properties']['meta'] ) && isset( $request['meta'] ) ) {
 733              $meta_update = $this->meta->update_value( $request['meta'], $post->ID );
 734  
 735              if ( is_wp_error( $meta_update ) ) {
 736                  return $meta_update;
 737              }
 738          }
 739  
 740          $post          = get_post( $post_id );
 741          $fields_update = $this->update_additional_fields_for_object( $post, $request );
 742  
 743          if ( is_wp_error( $fields_update ) ) {
 744              return $fields_update;
 745          }
 746  
 747          $request->set_param( 'context', 'edit' );
 748  
 749          // Filter is fired in WP_REST_Attachments_Controller subclass.
 750          if ( 'attachment' === $this->post_type ) {
 751              $response = $this->prepare_item_for_response( $post, $request );
 752              return rest_ensure_response( $response );
 753          }
 754  
 755          /** This action is documented in wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php */
 756          do_action( "rest_after_insert_{$this->post_type}", $post, $request, false );
 757  
 758          $response = $this->prepare_item_for_response( $post, $request );
 759  
 760          return rest_ensure_response( $response );
 761      }
 762  
 763      /**
 764       * Checks if a given request has access to delete a post.
 765       *
 766       * @since 4.7.0
 767       *
 768       * @param WP_REST_Request $request Full details about the request.
 769       * @return true|WP_Error True if the request has access to delete the item, WP_Error object otherwise.
 770       */
 771  	public function delete_item_permissions_check( $request ) {
 772          $post = $this->get_post( $request['id'] );
 773          if ( is_wp_error( $post ) ) {
 774              return $post;
 775          }
 776  
 777          if ( $post && ! $this->check_delete_permission( $post ) ) {
 778              return new WP_Error( 'rest_cannot_delete', __( 'Sorry, you are not allowed to delete this post.' ), array( 'status' => rest_authorization_required_code() ) );
 779          }
 780  
 781          return true;
 782      }
 783  
 784      /**
 785       * Deletes a single post.
 786       *
 787       * @since 4.7.0
 788       *
 789       * @param WP_REST_Request $request Full details about the request.
 790       * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
 791       */
 792  	public function delete_item( $request ) {
 793          $post = $this->get_post( $request['id'] );
 794          if ( is_wp_error( $post ) ) {
 795              return $post;
 796          }
 797  
 798          $id    = $post->ID;
 799          $force = (bool) $request['force'];
 800  
 801          $supports_trash = ( EMPTY_TRASH_DAYS > 0 );
 802  
 803          if ( 'attachment' === $post->post_type ) {
 804              $supports_trash = $supports_trash && MEDIA_TRASH;
 805          }
 806  
 807          /**
 808           * Filters whether a post is trashable.
 809           *
 810           * The dynamic portion of the hook name, `$this->post_type`, refers to the post type slug.
 811           *
 812           * Pass false to disable trash support for the post.
 813           *
 814           * @since 4.7.0
 815           *
 816           * @param bool    $supports_trash Whether the post type support trashing.
 817           * @param WP_Post $post           The Post object being considered for trashing support.
 818           */
 819          $supports_trash = apply_filters( "rest_{$this->post_type}_trashable", $supports_trash, $post );
 820  
 821          if ( ! $this->check_delete_permission( $post ) ) {
 822              return new WP_Error( 'rest_user_cannot_delete_post', __( 'Sorry, you are not allowed to delete this post.' ), array( 'status' => rest_authorization_required_code() ) );
 823          }
 824  
 825          $request->set_param( 'context', 'edit' );
 826  
 827          // If we're forcing, then delete permanently.
 828          if ( $force ) {
 829              $previous = $this->prepare_item_for_response( $post, $request );
 830              $result   = wp_delete_post( $id, true );
 831              $response = new WP_REST_Response();
 832              $response->set_data(
 833                  array(
 834                      'deleted'  => true,
 835                      'previous' => $previous->get_data(),
 836                  )
 837              );
 838          } else {
 839              // If we don't support trashing for this type, error out.
 840              if ( ! $supports_trash ) {
 841                  /* translators: %s: force=true */
 842                  return new WP_Error( 'rest_trash_not_supported', sprintf( __( "The post does not support trashing. Set '%s' to delete." ), 'force=true' ), array( 'status' => 501 ) );
 843              }
 844  
 845              // Otherwise, only trash if we haven't already.
 846              if ( 'trash' === $post->post_status ) {
 847                  return new WP_Error( 'rest_already_trashed', __( 'The post has already been deleted.' ), array( 'status' => 410 ) );
 848              }
 849  
 850              // (Note that internally this falls through to `wp_delete_post` if
 851              // the trash is disabled.)
 852              $result   = wp_trash_post( $id );
 853              $post     = get_post( $id );
 854              $response = $this->prepare_item_for_response( $post, $request );
 855          }
 856  
 857          if ( ! $result ) {
 858              return new WP_Error( 'rest_cannot_delete', __( 'The post cannot be deleted.' ), array( 'status' => 500 ) );
 859          }
 860  
 861          /**
 862           * Fires immediately after a single post is deleted or trashed via the REST API.
 863           *
 864           * They dynamic portion of the hook name, `$this->post_type`, refers to the post type slug.
 865           *
 866           * @since 4.7.0
 867           *
 868           * @param object           $post     The deleted or trashed post.
 869           * @param WP_REST_Response $response The response data.
 870           * @param WP_REST_Request  $request  The request sent to the API.
 871           */
 872          do_action( "rest_delete_{$this->post_type}", $post, $response, $request );
 873  
 874          return $response;
 875      }
 876  
 877      /**
 878       * Determines the allowed query_vars for a get_items() response and prepares
 879       * them for WP_Query.
 880       *
 881       * @since 4.7.0
 882       *
 883       * @param array           $prepared_args Optional. Prepared WP_Query arguments. Default empty array.
 884       * @param WP_REST_Request $request       Optional. Full details about the request.
 885       * @return array Items query arguments.
 886       */
 887  	protected function prepare_items_query( $prepared_args = array(), $request = null ) {
 888          $query_args = array();
 889  
 890          foreach ( $prepared_args as $key => $value ) {
 891              /**
 892               * Filters the query_vars used in get_items() for the constructed query.
 893               *
 894               * The dynamic portion of the hook name, `$key`, refers to the query_var key.
 895               *
 896               * @since 4.7.0
 897               *
 898               * @param string $value The query_var value.
 899               */
 900              $query_args[ $key ] = apply_filters( "rest_query_var-{$key}", $value ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
 901          }
 902  
 903          if ( 'post' !== $this->post_type || ! isset( $query_args['ignore_sticky_posts'] ) ) {
 904              $query_args['ignore_sticky_posts'] = true;
 905          }
 906  
 907          // Map to proper WP_Query orderby param.
 908          if ( isset( $query_args['orderby'] ) && isset( $request['orderby'] ) ) {
 909              $orderby_mappings = array(
 910                  'id'            => 'ID',
 911                  'include'       => 'post__in',
 912                  'slug'          => 'post_name',
 913                  'include_slugs' => 'post_name__in',
 914              );
 915  
 916              if ( isset( $orderby_mappings[ $request['orderby'] ] ) ) {
 917                  $query_args['orderby'] = $orderby_mappings[ $request['orderby'] ];
 918              }
 919          }
 920  
 921          return $query_args;
 922      }
 923  
 924      /**
 925       * Checks the post_date_gmt or modified_gmt and prepare any post or
 926       * modified date for single post output.
 927       *
 928       * @since 4.7.0
 929       *
 930       * @param string      $date_gmt GMT publication time.
 931       * @param string|null $date     Optional. Local publication time. Default null.
 932       * @return string|null ISO8601/RFC3339 formatted datetime.
 933       */
 934  	protected function prepare_date_response( $date_gmt, $date = null ) {
 935          // Use the date if passed.
 936          if ( isset( $date ) ) {
 937              return mysql_to_rfc3339( $date );
 938          }
 939  
 940          // Return null if $date_gmt is empty/zeros.
 941          if ( '0000-00-00 00:00:00' === $date_gmt ) {
 942              return null;
 943          }
 944  
 945          // Return the formatted datetime.
 946          return mysql_to_rfc3339( $date_gmt );
 947      }
 948  
 949      /**
 950       * Prepares a single post for create or update.
 951       *
 952       * @since 4.7.0
 953       *
 954       * @param WP_REST_Request $request Request object.
 955       * @return stdClass|WP_Error Post object or WP_Error.
 956       */
 957  	protected function prepare_item_for_database( $request ) {
 958          $prepared_post = new stdClass;
 959  
 960          // Post ID.
 961          if ( isset( $request['id'] ) ) {
 962              $existing_post = $this->get_post( $request['id'] );
 963              if ( is_wp_error( $existing_post ) ) {
 964                  return $existing_post;
 965              }
 966  
 967              $prepared_post->ID = $existing_post->ID;
 968          }
 969  
 970          $schema = $this->get_item_schema();
 971  
 972          // Post title.
 973          if ( ! empty( $schema['properties']['title'] ) && isset( $request['title'] ) ) {
 974              if ( is_string( $request['title'] ) ) {
 975                  $prepared_post->post_title = $request['title'];
 976              } elseif ( ! empty( $request['title']['raw'] ) ) {
 977                  $prepared_post->post_title = $request['title']['raw'];
 978              }
 979          }
 980  
 981          // Post content.
 982          if ( ! empty( $schema['properties']['content'] ) && isset( $request['content'] ) ) {
 983              if ( is_string( $request['content'] ) ) {
 984                  $prepared_post->post_content = $request['content'];
 985              } elseif ( isset( $request['content']['raw'] ) ) {
 986                  $prepared_post->post_content = $request['content']['raw'];
 987              }
 988          }
 989  
 990          // Post excerpt.
 991          if ( ! empty( $schema['properties']['excerpt'] ) && isset( $request['excerpt'] ) ) {
 992              if ( is_string( $request['excerpt'] ) ) {
 993                  $prepared_post->post_excerpt = $request['excerpt'];
 994              } elseif ( isset( $request['excerpt']['raw'] ) ) {
 995                  $prepared_post->post_excerpt = $request['excerpt']['raw'];
 996              }
 997          }
 998  
 999          // Post type.
1000          if ( empty( $request['id'] ) ) {
1001              // Creating new post, use default type for the controller.
1002              $prepared_post->post_type = $this->post_type;
1003          } else {
1004              // Updating a post, use previous type.
1005              $prepared_post->post_type = get_post_type( $request['id'] );
1006          }
1007  
1008          $post_type = get_post_type_object( $prepared_post->post_type );
1009  
1010          // Post status.
1011          if ( ! empty( $schema['properties']['status'] ) && isset( $request['status'] ) ) {
1012              $status = $this->handle_status_param( $request['status'], $post_type );
1013  
1014              if ( is_wp_error( $status ) ) {
1015                  return $status;
1016              }
1017  
1018              $prepared_post->post_status = $status;
1019          }
1020  
1021          // Post date.
1022          if ( ! empty( $schema['properties']['date'] ) && ! empty( $request['date'] ) ) {
1023              $current_date = isset( $prepared_post->ID ) ? get_post( $prepared_post->ID )->post_date : false;
1024              $date_data    = rest_get_date_with_gmt( $request['date'] );
1025  
1026              if ( ! empty( $date_data ) && $current_date !== $date_data[0] ) {
1027                  list( $prepared_post->post_date, $prepared_post->post_date_gmt ) = $date_data;
1028                  $prepared_post->edit_date                                        = true;
1029              }
1030          } elseif ( ! empty( $schema['properties']['date_gmt'] ) && ! empty( $request['date_gmt'] ) ) {
1031              $current_date = isset( $prepared_post->ID ) ? get_post( $prepared_post->ID )->post_date_gmt : false;
1032              $date_data    = rest_get_date_with_gmt( $request['date_gmt'], true );
1033  
1034              if ( ! empty( $date_data ) && $current_date !== $date_data[1] ) {
1035                  list( $prepared_post->post_date, $prepared_post->post_date_gmt ) = $date_data;
1036                  $prepared_post->edit_date                                        = true;
1037              }
1038          }
1039  
1040          // Sending a null date or date_gmt value resets date and date_gmt to their
1041          // default values (`0000-00-00 00:00:00`).
1042          if (
1043              ( ! empty( $schema['properties']['date_gmt'] ) && $request->has_param( 'date_gmt' ) && null === $request['date_gmt'] ) ||
1044              ( ! empty( $schema['properties']['date'] ) && $request->has_param( 'date' ) && null === $request['date'] )
1045          ) {
1046              $prepared_post->post_date_gmt = null;
1047              $prepared_post->post_date     = null;
1048          }
1049  
1050          // Post slug.
1051          if ( ! empty( $schema['properties']['slug'] ) && isset( $request['slug'] ) ) {
1052              $prepared_post->post_name = $request['slug'];
1053          }
1054  
1055          // Author.
1056          if ( ! empty( $schema['properties']['author'] ) && ! empty( $request['author'] ) ) {
1057              $post_author = (int) $request['author'];
1058  
1059              if ( get_current_user_id() !== $post_author ) {
1060                  $user_obj = get_userdata( $post_author );
1061  
1062                  if ( ! $user_obj ) {
1063                      return new WP_Error( 'rest_invalid_author', __( 'Invalid author ID.' ), array( 'status' => 400 ) );
1064                  }
1065              }
1066  
1067              $prepared_post->post_author = $post_author;
1068          }
1069  
1070          // Post password.
1071          if ( ! empty( $schema['properties']['password'] ) && isset( $request['password'] ) ) {
1072              $prepared_post->post_password = $request['password'];
1073  
1074              if ( '' !== $request['password'] ) {
1075                  if ( ! empty( $schema['properties']['sticky'] ) && ! empty( $request['sticky'] ) ) {
1076                      return new WP_Error( 'rest_invalid_field', __( 'A post can not be sticky and have a password.' ), array( 'status' => 400 ) );
1077                  }
1078  
1079                  if ( ! empty( $prepared_post->ID ) && is_sticky( $prepared_post->ID ) ) {
1080                      return new WP_Error( 'rest_invalid_field', __( 'A sticky post can not be password protected.' ), array( 'status' => 400 ) );
1081                  }
1082              }
1083          }
1084  
1085          if ( ! empty( $schema['properties']['sticky'] ) && ! empty( $request['sticky'] ) ) {
1086              if ( ! empty( $prepared_post->ID ) && post_password_required( $prepared_post->ID ) ) {
1087                  return new WP_Error( 'rest_invalid_field', __( 'A password protected post can not be set to sticky.' ), array( 'status' => 400 ) );
1088              }
1089          }
1090  
1091          // Parent.
1092          if ( ! empty( $schema['properties']['parent'] ) && isset( $request['parent'] ) ) {
1093              if ( 0 === (int) $request['parent'] ) {
1094                  $prepared_post->post_parent = 0;
1095              } else {
1096                  $parent = get_post( (int) $request['parent'] );
1097                  if ( empty( $parent ) ) {
1098                      return new WP_Error( 'rest_post_invalid_id', __( 'Invalid post parent ID.' ), array( 'status' => 400 ) );
1099                  }
1100                  $prepared_post->post_parent = (int) $parent->ID;
1101              }
1102          }
1103  
1104          // Menu order.
1105          if ( ! empty( $schema['properties']['menu_order'] ) && isset( $request['menu_order'] ) ) {
1106              $prepared_post->menu_order = (int) $request['menu_order'];
1107          }
1108  
1109          // Comment status.
1110          if ( ! empty( $schema['properties']['comment_status'] ) && ! empty( $request['comment_status'] ) ) {
1111              $prepared_post->comment_status = $request['comment_status'];
1112          }
1113  
1114          // Ping status.
1115          if ( ! empty( $schema['properties']['ping_status'] ) && ! empty( $request['ping_status'] ) ) {
1116              $prepared_post->ping_status = $request['ping_status'];
1117          }
1118  
1119          if ( ! empty( $schema['properties']['template'] ) ) {
1120              // Force template to null so that it can be handled exclusively by the REST controller.
1121              $prepared_post->page_template = null;
1122          }
1123  
1124          /**
1125           * Filters a post before it is inserted via the REST API.
1126           *
1127           * The dynamic portion of the hook name, `$this->post_type`, refers to the post type slug.
1128           *
1129           * @since 4.7.0
1130           *
1131           * @param stdClass        $prepared_post An object representing a single post prepared
1132           *                                       for inserting or updating the database.
1133           * @param WP_REST_Request $request       Request object.
1134           */
1135          return apply_filters( "rest_pre_insert_{$this->post_type}", $prepared_post, $request );
1136  
1137      }
1138  
1139      /**
1140       * Determines validity and normalizes the given status parameter.
1141       *
1142       * @since 4.7.0
1143       *
1144       * @param string $post_status Post status.
1145       * @param object $post_type   Post type.
1146       * @return string|WP_Error Post status or WP_Error if lacking the proper permission.
1147       */
1148  	protected function handle_status_param( $post_status, $post_type ) {
1149  
1150          switch ( $post_status ) {
1151              case 'draft':
1152              case 'pending':
1153                  break;
1154              case 'private':
1155                  if ( ! current_user_can( $post_type->cap->publish_posts ) ) {
1156                      return new WP_Error( 'rest_cannot_publish', __( 'Sorry, you are not allowed to create private posts in this post type.' ), array( 'status' => rest_authorization_required_code() ) );
1157                  }
1158                  break;
1159              case 'publish':
1160              case 'future':
1161                  if ( ! current_user_can( $post_type->cap->publish_posts ) ) {
1162                      return new WP_Error( 'rest_cannot_publish', __( 'Sorry, you are not allowed to publish posts in this post type.' ), array( 'status' => rest_authorization_required_code() ) );
1163                  }
1164                  break;
1165              default:
1166                  if ( ! get_post_status_object( $post_status ) ) {
1167                      $post_status = 'draft';
1168                  }
1169                  break;
1170          }
1171  
1172          return $post_status;
1173      }
1174  
1175      /**
1176       * Determines the featured media based on a request param.
1177       *
1178       * @since 4.7.0
1179       *
1180       * @param int $featured_media Featured Media ID.
1181       * @param int $post_id        Post ID.
1182       * @return bool|WP_Error Whether the post thumbnail was successfully deleted, otherwise WP_Error.
1183       */
1184  	protected function handle_featured_media( $featured_media, $post_id ) {
1185  
1186          $featured_media = (int) $featured_media;
1187          if ( $featured_media ) {
1188              $result = set_post_thumbnail( $post_id, $featured_media );
1189              if ( $result ) {
1190                  return true;
1191              } else {
1192                  return new WP_Error( 'rest_invalid_featured_media', __( 'Invalid featured media ID.' ), array( 'status' => 400 ) );
1193              }
1194          } else {
1195              return delete_post_thumbnail( $post_id );
1196          }
1197  
1198      }
1199  
1200      /**
1201       * Check whether the template is valid for the given post.
1202       *
1203       * @since 4.9.0
1204       *
1205       * @param string          $template Page template filename.
1206       * @param WP_REST_Request $request  Request.
1207       * @return bool|WP_Error True if template is still valid or if the same as existing value, or false if template not supported.
1208       */
1209  	public function check_template( $template, $request ) {
1210  
1211          if ( ! $template ) {
1212              return true;
1213          }
1214  
1215          if ( $request['id'] ) {
1216              $current_template = get_page_template_slug( $request['id'] );
1217          } else {
1218              $current_template = '';
1219          }
1220  
1221          // Always allow for updating a post to the same template, even if that template is no longer supported.
1222          if ( $template === $current_template ) {
1223              return true;
1224          }
1225  
1226          // If this is a create request, get_post() will return null and wp theme will fallback to the passed post type.
1227          $allowed_templates = wp_get_theme()->get_page_templates( get_post( $request['id'] ), $this->post_type );
1228  
1229          if ( isset( $allowed_templates[ $template ] ) ) {
1230              return true;
1231          }
1232  
1233          /* translators: 1: Parameter, 2: List of valid values. */
1234          return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s is not one of %2$s.' ), 'template', implode( ', ', array_keys( $allowed_templates ) ) ) );
1235      }
1236  
1237      /**
1238       * Sets the template for a post.
1239       *
1240       * @since 4.7.0
1241       * @since 4.9.0 Added the `$validate` parameter.
1242       *
1243       * @param string  $template Page template filename.
1244       * @param integer $post_id  Post ID.
1245       * @param bool    $validate Whether to validate that the template selected is valid.
1246       */
1247  	public function handle_template( $template, $post_id, $validate = false ) {
1248  
1249          if ( $validate && ! array_key_exists( $template, wp_get_theme()->get_page_templates( get_post( $post_id ) ) ) ) {
1250              $template = '';
1251          }
1252  
1253          update_post_meta( $post_id, '_wp_page_template', $template );
1254      }
1255  
1256      /**
1257       * Updates the post's terms from a REST request.
1258       *
1259       * @since 4.7.0
1260       *
1261       * @param int             $post_id The post ID to update the terms form.
1262       * @param WP_REST_Request $request The request object with post and terms data.
1263       * @return null|WP_Error WP_Error on an error assigning any of the terms, otherwise null.
1264       */
1265  	protected function handle_terms( $post_id, $request ) {
1266          $taxonomies = wp_list_filter( get_object_taxonomies( $this->post_type, 'objects' ), array( 'show_in_rest' => true ) );
1267  
1268          foreach ( $taxonomies as $taxonomy ) {
1269              $base = ! empty( $taxonomy->rest_base ) ? $taxonomy->rest_base : $taxonomy->name;
1270  
1271              if ( ! isset( $request[ $base ] ) ) {
1272                  continue;
1273              }
1274  
1275              $result = wp_set_object_terms( $post_id, $request[ $base ], $taxonomy->name );
1276  
1277              if ( is_wp_error( $result ) ) {
1278                  return $result;
1279              }
1280          }
1281      }
1282  
1283      /**
1284       * Checks whether current user can assign all terms sent with the current request.
1285       *
1286       * @since 4.7.0
1287       *
1288       * @param WP_REST_Request $request The request object with post and terms data.
1289       * @return bool Whether the current user can assign the provided terms.
1290       */
1291  	protected function check_assign_terms_permission( $request ) {
1292          $taxonomies = wp_list_filter( get_object_taxonomies( $this->post_type, 'objects' ), array( 'show_in_rest' => true ) );
1293          foreach ( $taxonomies as $taxonomy ) {
1294              $base = ! empty( $taxonomy->rest_base ) ? $taxonomy->rest_base : $taxonomy->name;
1295  
1296              if ( ! isset( $request[ $base ] ) ) {
1297                  continue;
1298              }
1299  
1300              foreach ( $request[ $base ] as $term_id ) {
1301                  // Invalid terms will be rejected later.
1302                  if ( ! get_term( $term_id, $taxonomy->name ) ) {
1303                      continue;
1304                  }
1305  
1306                  if ( ! current_user_can( 'assign_term', (int) $term_id ) ) {
1307                      return false;
1308                  }
1309              }
1310          }
1311  
1312          return true;
1313      }
1314  
1315      /**
1316       * Checks if a given post type can be viewed or managed.
1317       *
1318       * @since 4.7.0
1319       *
1320       * @param object|string $post_type Post type name or object.
1321       * @return bool Whether the post type is allowed in REST.
1322       */
1323  	protected function check_is_post_type_allowed( $post_type ) {
1324          if ( ! is_object( $post_type ) ) {
1325              $post_type = get_post_type_object( $post_type );
1326          }
1327  
1328          if ( ! empty( $post_type ) && ! empty( $post_type->show_in_rest ) ) {
1329              return true;
1330          }
1331  
1332          return false;
1333      }
1334  
1335      /**
1336       * Checks if a post can be read.
1337       *
1338       * Correctly handles posts with the inherit status.
1339       *
1340       * @since 4.7.0
1341       *
1342       * @param object $post Post object.
1343       * @return bool Whether the post can be read.
1344       */
1345  	public function check_read_permission( $post ) {
1346          $post_type = get_post_type_object( $post->post_type );
1347          if ( ! $this->check_is_post_type_allowed( $post_type ) ) {
1348              return false;
1349          }
1350  
1351          // Is the post readable?
1352          if ( 'publish' === $post->post_status || current_user_can( $post_type->cap->read_post, $post->ID ) ) {
1353              return true;
1354          }
1355  
1356          $post_status_obj = get_post_status_object( $post->post_status );
1357          if ( $post_status_obj && $post_status_obj->public ) {
1358              return true;
1359          }
1360  
1361          // Can we read the parent if we're inheriting?
1362          if ( 'inherit' === $post->post_status && $post->post_parent > 0 ) {
1363              $parent = get_post( $post->post_parent );
1364              if ( $parent ) {
1365                  return $this->check_read_permission( $parent );
1366              }
1367          }
1368  
1369          /*
1370           * If there isn't a parent, but the status is set to inherit, assume
1371           * it's published (as per get_post_status()).
1372           */
1373          if ( 'inherit' === $post->post_status ) {
1374              return true;
1375          }
1376  
1377          return false;
1378      }
1379  
1380      /**
1381       * Checks if a post can be edited.
1382       *
1383       * @since 4.7.0
1384       *
1385       * @param object $post Post object.
1386       * @return bool Whether the post can be edited.
1387       */
1388  	protected function check_update_permission( $post ) {
1389          $post_type = get_post_type_object( $post->post_type );
1390  
1391          if ( ! $this->check_is_post_type_allowed( $post_type ) ) {
1392              return false;
1393          }
1394  
1395          return current_user_can( $post_type->cap->edit_post, $post->ID );
1396      }
1397  
1398      /**
1399       * Checks if a post can be created.
1400       *
1401       * @since 4.7.0
1402       *
1403       * @param object $post Post object.
1404       * @return bool Whether the post can be created.
1405       */
1406  	protected function check_create_permission( $post ) {
1407          $post_type = get_post_type_object( $post->post_type );
1408  
1409          if ( ! $this->check_is_post_type_allowed( $post_type ) ) {
1410              return false;
1411          }
1412  
1413          return current_user_can( $post_type->cap->create_posts );
1414      }
1415  
1416      /**
1417       * Checks if a post can be deleted.
1418       *
1419       * @since 4.7.0
1420       *
1421       * @param object $post Post object.
1422       * @return bool Whether the post can be deleted.
1423       */
1424  	protected function check_delete_permission( $post ) {
1425          $post_type = get_post_type_object( $post->post_type );
1426  
1427          if ( ! $this->check_is_post_type_allowed( $post_type ) ) {
1428              return false;
1429          }
1430  
1431          return current_user_can( $post_type->cap->delete_post, $post->ID );
1432      }
1433  
1434      /**
1435       * Prepares a single post output for response.
1436       *
1437       * @since 4.7.0
1438       *
1439       * @param WP_Post         $post    Post object.
1440       * @param WP_REST_Request $request Request object.
1441       * @return WP_REST_Response Response object.
1442       */
1443  	public function prepare_item_for_response( $post, $request ) {
1444          $GLOBALS['post'] = $post;
1445  
1446          setup_postdata( $post );
1447  
1448          $fields = $this->get_fields_for_response( $request );
1449  
1450          // Base fields for every post.
1451          $data = array();
1452  
1453          if ( rest_is_field_included( 'id', $fields ) ) {
1454              $data['id'] = $post->ID;
1455          }
1456  
1457          if ( rest_is_field_included( 'date', $fields ) ) {
1458              $data['date'] = $this->prepare_date_response( $post->post_date_gmt, $post->post_date );
1459          }
1460  
1461          if ( rest_is_field_included( 'date_gmt', $fields ) ) {
1462              // For drafts, `post_date_gmt` may not be set, indicating that the
1463              // date of the draft should be updated each time it is saved (see
1464              // #38883).  In this case, shim the value based on the `post_date`
1465              // field with the site's timezone offset applied.
1466              if ( '0000-00-00 00:00:00' === $post->post_date_gmt ) {
1467                  $post_date_gmt = get_gmt_from_date( $post->post_date );
1468              } else {
1469                  $post_date_gmt = $post->post_date_gmt;
1470              }
1471              $data['date_gmt'] = $this->prepare_date_response( $post_date_gmt );
1472          }
1473  
1474          if ( rest_is_field_included( 'guid', $fields ) ) {
1475              $data['guid'] = array(
1476                  /** This filter is documented in wp-includes/post-template.php */
1477                  'rendered' => apply_filters( 'get_the_guid', $post->guid, $post->ID ),
1478                  'raw'      => $post->guid,
1479              );
1480          }
1481  
1482          if ( rest_is_field_included( 'modified', $fields ) ) {
1483              $data['modified'] = $this->prepare_date_response( $post->post_modified_gmt, $post->post_modified );
1484          }
1485  
1486          if ( rest_is_field_included( 'modified_gmt', $fields ) ) {
1487              // For drafts, `post_modified_gmt` may not be set (see
1488              // `post_date_gmt` comments above).  In this case, shim the value
1489              // based on the `post_modified` field with the site's timezone
1490              // offset applied.
1491              if ( '0000-00-00 00:00:00' === $post->post_modified_gmt ) {
1492                  $post_modified_gmt = gmdate( 'Y-m-d H:i:s', strtotime( $post->post_modified ) - ( get_option( 'gmt_offset' ) * 3600 ) );
1493              } else {
1494                  $post_modified_gmt = $post->post_modified_gmt;
1495              }
1496              $data['modified_gmt'] = $this->prepare_date_response( $post_modified_gmt );
1497          }
1498  
1499          if ( rest_is_field_included( 'password', $fields ) ) {
1500              $data['password'] = $post->post_password;
1501          }
1502  
1503          if ( rest_is_field_included( 'slug', $fields ) ) {
1504              $data['slug'] = $post->post_name;
1505          }
1506  
1507          if ( rest_is_field_included( 'status', $fields ) ) {
1508              $data['status'] = $post->post_status;
1509          }
1510  
1511          if ( rest_is_field_included( 'type', $fields ) ) {
1512              $data['type'] = $post->post_type;
1513          }
1514  
1515          if ( rest_is_field_included( 'link', $fields ) ) {
1516              $data['link'] = get_permalink( $post->ID );
1517          }
1518  
1519          if ( rest_is_field_included( 'title', $fields ) ) {
1520              $data['title'] = array();
1521          }
1522          if ( rest_is_field_included( 'title.raw', $fields ) ) {
1523              $data['title']['raw'] = $post->post_title;
1524          }
1525          if ( rest_is_field_included( 'title.rendered', $fields ) ) {
1526              add_filter( 'protected_title_format', array( $this, 'protected_title_format' ) );
1527  
1528              $data['title']['rendered'] = get_the_title( $post->ID );
1529  
1530              remove_filter( 'protected_title_format', array( $this, 'protected_title_format' ) );
1531          }
1532  
1533          $has_password_filter = false;
1534  
1535          if ( $this->can_access_password_content( $post, $request ) ) {
1536              // Allow access to the post, permissions already checked before.
1537              add_filter( 'post_password_required', '__return_false' );
1538  
1539              $has_password_filter = true;
1540          }
1541  
1542          if ( rest_is_field_included( 'content', $fields ) ) {
1543              $data['content'] = array();
1544          }
1545          if ( rest_is_field_included( 'content.raw', $fields ) ) {
1546              $data['content']['raw'] = $post->post_content;
1547          }
1548          if ( rest_is_field_included( 'content.rendered', $fields ) ) {
1549              /** This filter is documented in wp-includes/post-template.php */
1550              $data['content']['rendered'] = post_password_required( $post ) ? '' : apply_filters( 'the_content', $post->post_content );
1551          }
1552          if ( rest_is_field_included( 'content.protected', $fields ) ) {
1553              $data['content']['protected'] = (bool) $post->post_password;
1554          }
1555          if ( rest_is_field_included( 'content.block_version', $fields ) ) {
1556              $data['content']['block_version'] = block_version( $post->post_content );
1557          }
1558  
1559          if ( rest_is_field_included( 'excerpt', $fields ) ) {
1560              /** This filter is documented in wp-includes/post-template.php */
1561              $excerpt         = apply_filters( 'the_excerpt', apply_filters( 'get_the_excerpt', $post->post_excerpt, $post ) );
1562              $data['excerpt'] = array(
1563                  'raw'       => $post->post_excerpt,
1564                  'rendered'  => post_password_required( $post ) ? '' : $excerpt,
1565                  'protected' => (bool) $post->post_password,
1566              );
1567          }
1568  
1569          if ( $has_password_filter ) {
1570              // Reset filter.
1571              remove_filter( 'post_password_required', '__return_false' );
1572          }
1573  
1574          if ( rest_is_field_included( 'author', $fields ) ) {
1575              $data['author'] = (int) $post->post_author;
1576          }
1577  
1578          if ( rest_is_field_included( 'featured_media', $fields ) ) {
1579              $data['featured_media'] = (int) get_post_thumbnail_id( $post->ID );
1580          }
1581  
1582          if ( rest_is_field_included( 'parent', $fields ) ) {
1583              $data['parent'] = (int) $post->post_parent;
1584          }
1585  
1586          if ( rest_is_field_included( 'menu_order', $fields ) ) {
1587              $data['menu_order'] = (int) $post->menu_order;
1588          }
1589  
1590          if ( rest_is_field_included( 'comment_status', $fields ) ) {
1591              $data['comment_status'] = $post->comment_status;
1592          }
1593  
1594          if ( rest_is_field_included( 'ping_status', $fields ) ) {
1595              $data['ping_status'] = $post->ping_status;
1596          }
1597  
1598          if ( rest_is_field_included( 'sticky', $fields ) ) {
1599              $data['sticky'] = is_sticky( $post->ID );
1600          }
1601  
1602          if ( rest_is_field_included( 'template', $fields ) ) {
1603              $template = get_page_template_slug( $post->ID );
1604              if ( $template ) {
1605                  $data['template'] = $template;
1606              } else {
1607                  $data['template'] = '';
1608              }
1609          }
1610  
1611          if ( rest_is_field_included( 'format', $fields ) ) {
1612              $data['format'] = get_post_format( $post->ID );
1613  
1614              // Fill in blank post format.
1615              if ( empty( $data['format'] ) ) {
1616                  $data['format'] = 'standard';
1617              }
1618          }
1619  
1620          if ( rest_is_field_included( 'meta', $fields ) ) {
1621              $data['meta'] = $this->meta->get_value( $post->ID, $request );
1622          }
1623  
1624          $taxonomies = wp_list_filter( get_object_taxonomies( $this->post_type, 'objects' ), array( 'show_in_rest' => true ) );
1625  
1626          foreach ( $taxonomies as $taxonomy ) {
1627              $base = ! empty( $taxonomy->rest_base ) ? $taxonomy->rest_base : $taxonomy->name;
1628  
1629              if ( rest_is_field_included( $base, $fields ) ) {
1630                  $terms         = get_the_terms( $post, $taxonomy->name );
1631                  $data[ $base ] = $terms ? array_values( wp_list_pluck( $terms, 'term_id' ) ) : array();
1632              }
1633          }
1634  
1635          $post_type_obj = get_post_type_object( $post->post_type );
1636          if ( is_post_type_viewable( $post_type_obj ) && $post_type_obj->public ) {
1637              $permalink_template_requested = rest_is_field_included( 'permalink_template', $fields );
1638              $generated_slug_requested     = rest_is_field_included( 'generated_slug', $fields );
1639  
1640              if ( $permalink_template_requested || $generated_slug_requested ) {
1641                  if ( ! function_exists( 'get_sample_permalink' ) ) {
1642                      require_once  ABSPATH . 'wp-admin/includes/post.php';
1643                  }
1644  
1645                  $sample_permalink = get_sample_permalink( $post->ID, $post->post_title, '' );
1646  
1647                  if ( $permalink_template_requested ) {
1648                      $data['permalink_template'] = $sample_permalink[0];
1649                  }
1650  
1651                  if ( $generated_slug_requested ) {
1652                      $data['generated_slug'] = $sample_permalink[1];
1653                  }
1654              }
1655          }
1656  
1657          $context = ! empty( $request['context'] ) ? $request['context'] : 'view';
1658          $data    = $this->add_additional_fields_to_object( $data, $request );
1659          $data    = $this->filter_response_by_context( $data, $context );
1660  
1661          // Wrap the data in a response object.
1662          $response = rest_ensure_response( $data );
1663  
1664          $links = $this->prepare_links( $post );
1665          $response->add_links( $links );
1666  
1667          if ( ! empty( $links['self']['href'] ) ) {
1668              $actions = $this->get_available_actions( $post, $request );
1669  
1670              $self = $links['self']['href'];
1671  
1672              foreach ( $actions as $rel ) {
1673                  $response->add_link( $rel, $self );
1674              }
1675          }
1676  
1677          /**
1678           * Filters the post data for a response.
1679           *
1680           * The dynamic portion of the hook name, `$this->post_type`, refers to the post type slug.
1681           *
1682           * @since 4.7.0
1683           *
1684           * @param WP_REST_Response $response The response object.
1685           * @param WP_Post          $post     Post object.
1686           * @param WP_REST_Request  $request  Request object.
1687           */
1688          return apply_filters( "rest_prepare_{$this->post_type}", $response, $post, $request );
1689      }
1690  
1691      /**
1692       * Overwrites the default protected title format.
1693       *
1694       * By default, WordPress will show password protected posts with a title of
1695       * "Protected: %s", as the REST API communicates the protected status of a post
1696       * in a machine readable format, we remove the "Protected: " prefix.
1697       *
1698       * @since 4.7.0
1699       *
1700       * @return string Protected title format.
1701       */
1702  	public function protected_title_format() {
1703          return '%s';
1704      }
1705  
1706      /**
1707       * Prepares links for the request.
1708       *
1709       * @since 4.7.0
1710       *
1711       * @param WP_Post $post Post object.
1712       * @return array Links for the given post.
1713       */
1714  	protected function prepare_links( $post ) {
1715          $base = sprintf( '%s/%s', $this->namespace, $this->rest_base );
1716  
1717          // Entity meta.
1718          $links = array(
1719              'self'       => array(
1720                  'href' => rest_url( trailingslashit( $base ) . $post->ID ),
1721              ),
1722              'collection' => array(
1723                  'href' => rest_url( $base ),
1724              ),
1725              'about'      => array(
1726                  'href' => rest_url( 'wp/v2/types/' . $this->post_type ),
1727              ),
1728          );
1729  
1730          if ( ( in_array( $post->post_type, array( 'post', 'page' ), true ) || post_type_supports( $post->post_type, 'author' ) )
1731              && ! empty( $post->post_author ) ) {
1732              $links['author'] = array(
1733                  'href'       => rest_url( 'wp/v2/users/' . $post->post_author ),
1734                  'embeddable' => true,
1735              );
1736          }
1737  
1738          if ( in_array( $post->post_type, array( 'post', 'page' ), true ) || post_type_supports( $post->post_type, 'comments' ) ) {
1739              $replies_url = rest_url( 'wp/v2/comments' );
1740              $replies_url = add_query_arg( 'post', $post->ID, $replies_url );
1741  
1742              $links['replies'] = array(
1743                  'href'       => $replies_url,
1744                  'embeddable' => true,
1745              );
1746          }
1747  
1748          if ( in_array( $post->post_type, array( 'post', 'page' ), true ) || post_type_supports( $post->post_type, 'revisions' ) ) {
1749              $revisions       = wp_get_post_revisions( $post->ID, array( 'fields' => 'ids' ) );
1750              $revisions_count = count( $revisions );
1751  
1752              $links['version-history'] = array(
1753                  'href'  => rest_url( trailingslashit( $base ) . $post->ID . '/revisions' ),
1754                  'count' => $revisions_count,
1755              );
1756  
1757              if ( $revisions_count > 0 ) {
1758                  $last_revision = array_shift( $revisions );
1759  
1760                  $links['predecessor-version'] = array(
1761                      'href' => rest_url( trailingslashit( $base ) . $post->ID . '/revisions/' . $last_revision ),
1762                      'id'   => $last_revision,
1763                  );
1764              }
1765          }
1766  
1767          $post_type_obj = get_post_type_object( $post->post_type );
1768  
1769          if ( $post_type_obj->hierarchical && ! empty( $post->post_parent ) ) {
1770              $links['up'] = array(
1771                  'href'       => rest_url( trailingslashit( $base ) . (int) $post->post_parent ),
1772                  'embeddable' => true,
1773              );
1774          }
1775  
1776          // If we have a featured media, add that.
1777          $featured_media = get_post_thumbnail_id( $post->ID );
1778          if ( $featured_media ) {
1779              $image_url = rest_url( 'wp/v2/media/' . $featured_media );
1780  
1781              $links['https://api.w.org/featuredmedia'] = array(
1782                  'href'       => $image_url,
1783                  'embeddable' => true,
1784              );
1785          }
1786  
1787          if ( ! in_array( $post->post_type, array( 'attachment', 'nav_menu_item', 'revision' ), true ) ) {
1788              $attachments_url = rest_url( 'wp/v2/media' );
1789              $attachments_url = add_query_arg( 'parent', $post->ID, $attachments_url );
1790  
1791              $links['https://api.w.org/attachment'] = array(
1792                  'href' => $attachments_url,
1793              );
1794          }
1795  
1796          $taxonomies = get_object_taxonomies( $post->post_type );
1797  
1798          if ( ! empty( $taxonomies ) ) {
1799              $links['https://api.w.org/term'] = array();
1800  
1801              foreach ( $taxonomies as $tax ) {
1802                  $taxonomy_obj = get_taxonomy( $tax );
1803  
1804                  // Skip taxonomies that are not public.
1805                  if ( empty( $taxonomy_obj->show_in_rest ) ) {
1806                      continue;
1807                  }
1808  
1809                  $tax_base = ! empty( $taxonomy_obj->rest_base ) ? $taxonomy_obj->rest_base : $tax;
1810  
1811                  $terms_url = add_query_arg(
1812                      'post',
1813                      $post->ID,
1814                      rest_url( 'wp/v2/' . $tax_base )
1815                  );
1816  
1817                  $links['https://api.w.org/term'][] = array(
1818                      'href'       => $terms_url,
1819                      'taxonomy'   => $tax,
1820                      'embeddable' => true,
1821                  );
1822              }
1823          }
1824  
1825          return $links;
1826      }
1827  
1828      /**
1829       * Get the link relations available for the post and current user.
1830       *
1831       * @since 4.9.8
1832       *
1833       * @param WP_Post         $post    Post object.
1834       * @param WP_REST_Request $request Request object.
1835       * @return array List of link relations.
1836       */
1837  	protected function get_available_actions( $post, $request ) {
1838  
1839          if ( 'edit' !== $request['context'] ) {
1840              return array();
1841          }
1842  
1843          $rels = array();
1844  
1845          $post_type = get_post_type_object( $post->post_type );
1846  
1847          if ( 'attachment' !== $this->post_type && current_user_can( $post_type->cap->publish_posts ) ) {
1848              $rels[] = 'https://api.w.org/action-publish';
1849          }
1850  
1851          if ( current_user_can( 'unfiltered_html' ) ) {
1852              $rels[] = 'https://api.w.org/action-unfiltered-html';
1853          }
1854  
1855          if ( 'post' === $post_type->name ) {
1856              if ( current_user_can( $post_type->cap->edit_others_posts ) && current_user_can( $post_type->cap->publish_posts ) ) {
1857                  $rels[] = 'https://api.w.org/action-sticky';
1858              }
1859          }
1860  
1861          if ( post_type_supports( $post_type->name, 'author' ) ) {
1862              if ( current_user_can( $post_type->cap->edit_others_posts ) ) {
1863                  $rels[] = 'https://api.w.org/action-assign-author';
1864              }
1865          }
1866  
1867          $taxonomies = wp_list_filter( get_object_taxonomies( $this->post_type, 'objects' ), array( 'show_in_rest' => true ) );
1868  
1869          foreach ( $taxonomies as $tax ) {
1870              $tax_base   = ! empty( $tax->rest_base ) ? $tax->rest_base : $tax->name;
1871              $create_cap = is_taxonomy_hierarchical( $tax->name ) ? $tax->cap->edit_terms : $tax->cap->assign_terms;
1872  
1873              if ( current_user_can( $create_cap ) ) {
1874                  $rels[] = 'https://api.w.org/action-create-' . $tax_base;
1875              }
1876  
1877              if ( current_user_can( $tax->cap->assign_terms ) ) {
1878                  $rels[] = 'https://api.w.org/action-assign-' . $tax_base;
1879              }
1880          }
1881  
1882          return $rels;
1883      }
1884  
1885      /**
1886       * Retrieves the post's schema, conforming to JSON Schema.
1887       *
1888       * @since 4.7.0
1889       *
1890       * @return array Item schema data.
1891       */
1892  	public function get_item_schema() {
1893          if ( $this->schema ) {
1894              return $this->add_additional_fields_schema( $this->schema );
1895          }
1896  
1897          $schema = array(
1898              '$schema'    => 'http://json-schema.org/draft-04/schema#',
1899              'title'      => $this->post_type,
1900              'type'       => 'object',
1901              // Base properties for every Post.
1902              'properties' => array(
1903                  'date'         => array(
1904                      'description' => __( "The date the object was published, in the site's timezone." ),
1905                      'type'        => array( 'string', 'null' ),
1906                      'format'      => 'date-time',
1907                      'context'     => array( 'view', 'edit', 'embed' ),
1908                  ),
1909                  'date_gmt'     => array(
1910                      'description' => __( 'The date the object was published, as GMT.' ),
1911                      'type'        => array( 'string', 'null' ),
1912                      'format'      => 'date-time',
1913                      'context'     => array( 'view', 'edit' ),
1914                  ),
1915                  'guid'         => array(
1916                      'description' => __( 'The globally unique identifier for the object.' ),
1917                      'type'        => 'object',
1918                      'context'     => array( 'view', 'edit' ),
1919                      'readonly'    => true,
1920                      'properties'  => array(
1921                          'raw'      => array(
1922                              'description' => __( 'GUID for the object, as it exists in the database.' ),
1923                              'type'        => 'string',
1924                              'context'     => array( 'edit' ),
1925                              'readonly'    => true,
1926                          ),
1927                          'rendered' => array(
1928                              'description' => __( 'GUID for the object, transformed for display.' ),
1929                              'type'        => 'string',
1930                              'context'     => array( 'view', 'edit' ),
1931                              'readonly'    => true,
1932                          ),
1933                      ),
1934                  ),
1935                  'id'           => array(
1936                      'description' => __( 'Unique identifier for the object.' ),
1937                      'type'        => 'integer',
1938                      'context'     => array( 'view', 'edit', 'embed' ),
1939                      'readonly'    => true,
1940                  ),
1941                  'link'         => array(
1942                      'description' => __( 'URL to the object.' ),
1943                      'type'        => 'string',
1944                      'format'      => 'uri',
1945                      'context'     => array( 'view', 'edit', 'embed' ),
1946                      'readonly'    => true,
1947                  ),
1948                  'modified'     => array(
1949                      'description' => __( "The date the object was last modified, in the site's timezone." ),
1950                      'type'        => 'string',
1951                      'format'      => 'date-time',
1952                      'context'     => array( 'view', 'edit' ),
1953                      'readonly'    => true,
1954                  ),
1955                  'modified_gmt' => array(
1956                      'description' => __( 'The date the object was last modified, as GMT.' ),
1957                      'type'        => 'string',
1958                      'format'      => 'date-time',
1959                      'context'     => array( 'view', 'edit' ),
1960                      'readonly'    => true,
1961                  ),
1962                  'slug'         => array(
1963                      'description' => __( 'An alphanumeric identifier for the object unique to its type.' ),
1964                      'type'        => 'string',
1965                      'context'     => array( 'view', 'edit', 'embed' ),
1966                      'arg_options' => array(
1967                          'sanitize_callback' => array( $this, 'sanitize_slug' ),
1968                      ),
1969                  ),
1970                  'status'       => array(
1971                      'description' => __( 'A named status for the object.' ),
1972                      'type'        => 'string',
1973                      'enum'        => array_keys( get_post_stati( array( 'internal' => false ) ) ),
1974                      'context'     => array( 'view', 'edit' ),
1975                  ),
1976                  'type'         => array(
1977                      'description' => __( 'Type of Post for the object.' ),
1978                      'type'        => 'string',
1979                      'context'     => array( 'view', 'edit', 'embed' ),
1980                      'readonly'    => true,
1981                  ),
1982                  'password'     => array(
1983                      'description' => __( 'A password to protect access to the content and excerpt.' ),
1984                      'type'        => 'string',
1985                      'context'     => array( 'edit' ),
1986                  ),
1987              ),
1988          );
1989  
1990          $post_type_obj = get_post_type_object( $this->post_type );
1991          if ( is_post_type_viewable( $post_type_obj ) && $post_type_obj->public ) {
1992              $schema['properties']['permalink_template'] = array(
1993                  'description' => __( 'Permalink template for the object.' ),
1994                  'type'        => 'string',
1995                  'context'     => array( 'edit' ),
1996                  'readonly'    => true,
1997              );
1998  
1999              $schema['properties']['generated_slug'] = array(
2000                  'description' => __( 'Slug automatically generated from the object title.' ),
2001                  'type'        => 'string',
2002                  'context'     => array( 'edit' ),
2003                  'readonly'    => true,
2004              );
2005          }
2006  
2007          if ( $post_type_obj->hierarchical ) {
2008              $schema['properties']['parent'] = array(
2009                  'description' => __( 'The ID for the parent of the object.' ),
2010                  'type'        => 'integer',
2011                  'context'     => array( 'view', 'edit' ),
2012              );
2013          }
2014  
2015          $post_type_attributes = array(
2016              'title',
2017              'editor',
2018              'author',
2019              'excerpt',
2020              'thumbnail',
2021              'comments',
2022              'revisions',
2023              'page-attributes',
2024              'post-formats',
2025              'custom-fields',
2026          );
2027          $fixed_schemas        = array(
2028              'post'       => array(
2029                  'title',
2030                  'editor',
2031                  'author',
2032                  'excerpt',
2033                  'thumbnail',
2034                  'comments',
2035                  'revisions',
2036                  'post-formats',
2037                  'custom-fields',
2038              ),
2039              'page'       => array(
2040                  'title',
2041                  'editor',
2042                  'author',
2043                  'excerpt',
2044                  'thumbnail',
2045                  'comments',
2046                  'revisions',
2047                  'page-attributes',
2048                  'custom-fields',
2049              ),
2050              'attachment' => array(
2051                  'title',
2052                  'author',
2053                  'comments',
2054                  'revisions',
2055                  'custom-fields',
2056              ),
2057          );
2058          foreach ( $post_type_attributes as $attribute ) {
2059              if ( isset( $fixed_schemas[ $this->post_type ] ) && ! in_array( $attribute, $fixed_schemas[ $this->post_type ], true ) ) {
2060                  continue;
2061              } elseif ( ! isset( $fixed_schemas[ $this->post_type ] ) && ! post_type_supports( $this->post_type, $attribute ) ) {
2062                  continue;
2063              }
2064  
2065              switch ( $attribute ) {
2066  
2067                  case 'title':
2068                      $schema['properties']['title'] = array(
2069                          'description' => __( 'The title for the object.' ),
2070                          'type'        => 'object',
2071                          'context'     => array( 'view', 'edit', 'embed' ),
2072                          'arg_options' => array(
2073                              'sanitize_callback' => null, // Note: sanitization implemented in self::prepare_item_for_database()
2074                              'validate_callback' => null, // Note: validation implemented in self::prepare_item_for_database()
2075                          ),
2076                          'properties'  => array(
2077                              'raw'      => array(
2078                                  'description' => __( 'Title for the object, as it exists in the database.' ),
2079                                  'type'        => 'string',
2080                                  'context'     => array( 'edit' ),
2081                              ),
2082                              'rendered' => array(
2083                                  'description' => __( 'HTML title for the object, transformed for display.' ),
2084                                  'type'        => 'string',
2085                                  'context'     => array( 'view', 'edit', 'embed' ),
2086                                  'readonly'    => true,
2087                              ),
2088                          ),
2089                      );
2090                      break;
2091  
2092                  case 'editor':
2093                      $schema['properties']['content'] = array(
2094                          'description' => __( 'The content for the object.' ),
2095                          'type'        => 'object',
2096                          'context'     => array( 'view', 'edit' ),
2097                          'arg_options' => array(
2098                              'sanitize_callback' => null, // Note: sanitization implemented in self::prepare_item_for_database()
2099                              'validate_callback' => null, // Note: validation implemented in self::prepare_item_for_database()
2100                          ),
2101                          'properties'  => array(
2102                              'raw'           => array(
2103                                  'description' => __( 'Content for the object, as it exists in the database.' ),
2104                                  'type'        => 'string',
2105                                  'context'     => array( 'edit' ),
2106                              ),
2107                              'rendered'      => array(
2108                                  'description' => __( 'HTML content for the object, transformed for display.' ),
2109                                  'type'        => 'string',
2110                                  'context'     => array( 'view', 'edit' ),
2111                                  'readonly'    => true,
2112                              ),
2113                              'block_version' => array(
2114                                  'description' => __( 'Version of the content block format used by the object.' ),
2115                                  'type'        => 'integer',
2116                                  'context'     => array( 'edit' ),
2117                                  'readonly'    => true,
2118                              ),
2119                              'protected'     => array(
2120                                  'description' => __( 'Whether the content is protected with a password.' ),
2121                                  'type'        => 'boolean',
2122                                  'context'     => array( 'view', 'edit', 'embed' ),
2123                                  'readonly'    => true,
2124                              ),
2125                          ),
2126                      );
2127                      break;
2128  
2129                  case 'author':
2130                      $schema['properties']['author'] = array(
2131                          'description' => __( 'The ID for the author of the object.' ),
2132                          'type'        => 'integer',
2133                          'context'     => array( 'view', 'edit', 'embed' ),
2134                      );
2135                      break;
2136  
2137                  case 'excerpt':
2138                      $schema['properties']['excerpt'] = array(
2139                          'description' => __( 'The excerpt for the object.' ),
2140                          'type'        => 'object',
2141                          'context'     => array( 'view', 'edit', 'embed' ),
2142                          'arg_options' => array(
2143                              'sanitize_callback' => null, // Note: sanitization implemented in self::prepare_item_for_database()
2144                              'validate_callback' => null, // Note: validation implemented in self::prepare_item_for_database()
2145                          ),
2146                          'properties'  => array(
2147                              'raw'       => array(
2148                                  'description' => __( 'Excerpt for the object, as it exists in the database.' ),
2149                                  'type'        => 'string',
2150                                  'context'     => array( 'edit' ),
2151                              ),
2152                              'rendered'  => array(
2153                                  'description' => __( 'HTML excerpt for the object, transformed for display.' ),
2154                                  'type'        => 'string',
2155                                  'context'     => array( 'view', 'edit', 'embed' ),
2156                                  'readonly'    => true,
2157                              ),
2158                              'protected' => array(
2159                                  'description' => __( 'Whether the excerpt is protected with a password.' ),
2160                                  'type'        => 'boolean',
2161                                  'context'     => array( 'view', 'edit', 'embed' ),
2162                                  'readonly'    => true,
2163                              ),
2164                          ),
2165                      );
2166                      break;
2167  
2168                  case 'thumbnail':
2169                      $schema['properties']['featured_media'] = array(
2170                          'description' => __( 'The ID of the featured media for the object.' ),
2171                          'type'        => 'integer',
2172                          'context'     => array( 'view', 'edit', 'embed' ),
2173                      );
2174                      break;
2175  
2176                  case 'comments':
2177                      $schema['properties']['comment_status'] = array(
2178                          'description' => __( 'Whether or not comments are open on the object.' ),
2179                          'type'        => 'string',
2180                          'enum'        => array( 'open', 'closed' ),
2181                          'context'     => array( 'view', 'edit' ),
2182                      );
2183                      $schema['properties']['ping_status']    = array(
2184                          'description' => __( 'Whether or not the object can be pinged.' ),
2185                          'type'        => 'string',
2186                          'enum'        => array( 'open', 'closed' ),
2187                          'context'     => array( 'view', 'edit' ),
2188                      );
2189                      break;
2190  
2191                  case 'page-attributes':
2192                      $schema['properties']['menu_order'] = array(
2193                          'description' => __( 'The order of the object in relation to other object of its type.' ),
2194                          'type'        => 'integer',
2195                          'context'     => array( 'view', 'edit' ),
2196                      );
2197                      break;
2198  
2199                  case 'post-formats':
2200                      // Get the native post formats and remove the array keys.
2201                      $formats = array_values( get_post_format_slugs() );
2202  
2203                      $schema['properties']['format'] = array(
2204                          'description' => __( 'The format for the object.' ),
2205                          'type'        => 'string',
2206                          'enum'        => $formats,
2207                          'context'     => array( 'view', 'edit' ),
2208                      );
2209                      break;
2210  
2211                  case 'custom-fields':
2212                      $schema['properties']['meta'] = $this->meta->get_field_schema();
2213                      break;
2214  
2215              }
2216          }
2217  
2218          if ( 'post' === $this->post_type ) {
2219              $schema['properties']['sticky'] = array(
2220                  'description' => __( 'Whether or not the object should be treated as sticky.' ),
2221                  'type'        => 'boolean',
2222                  'context'     => array( 'view', 'edit' ),
2223              );
2224          }
2225  
2226          $schema['properties']['template'] = array(
2227              'description' => __( 'The theme file to use to display the object.' ),
2228              'type'        => 'string',
2229              'context'     => array( 'view', 'edit' ),
2230              'arg_options' => array(
2231                  'validate_callback' => array( $this, 'check_template' ),
2232              ),
2233          );
2234  
2235          $taxonomies = wp_list_filter( get_object_taxonomies( $this->post_type, 'objects' ), array( 'show_in_rest' => true ) );
2236          foreach ( $taxonomies as $taxonomy ) {
2237              $base                          = ! empty( $taxonomy->rest_base ) ? $taxonomy->rest_base : $taxonomy->name;
2238              $schema['properties'][ $base ] = array(
2239                  /* translators: %s: Taxonomy name. */
2240                  'description' => sprintf( __( 'The terms assigned to the object in the %s taxonomy.' ), $taxonomy->name ),
2241                  'type'        => 'array',
2242                  'items'       => array(
2243                      'type' => 'integer',
2244                  ),
2245                  'context'     => array( 'view', 'edit' ),
2246              );
2247          }
2248  
2249          $schema_links = $this->get_schema_links();
2250  
2251          if ( $schema_links ) {
2252              $schema['links'] = $schema_links;
2253          }
2254  
2255          $this->schema = $schema;
2256          return $this->add_additional_fields_schema( $this->schema );
2257      }
2258  
2259      /**
2260       * Retrieve Link Description Objects that should be added to the Schema for the posts collection.
2261       *
2262       * @since 4.9.8
2263       *
2264       * @return array
2265       */
2266  	protected function get_schema_links() {
2267  
2268          $href = rest_url( "{$this->namespace}/{$this->rest_base}/{id}" );
2269  
2270          $links = array();
2271  
2272          if ( 'attachment' !== $this->post_type ) {
2273              $links[] = array(
2274                  'rel'          => 'https://api.w.org/action-publish',
2275                  'title'        => __( 'The current user can publish this post.' ),
2276                  'href'         => $href,
2277                  'targetSchema' => array(
2278                      'type'       => 'object',
2279                      'properties' => array(
2280                          'status' => array(
2281                              'type' => 'string',
2282                              'enum' => array( 'publish', 'future' ),
2283                          ),
2284                      ),
2285                  ),
2286              );
2287          }
2288  
2289          $links[] = array(
2290              'rel'          => 'https://api.w.org/action-unfiltered-html',
2291              'title'        => __( 'The current user can post unfiltered HTML markup and JavaScript.' ),
2292              'href'         => $href,
2293              'targetSchema' => array(
2294                  'type'       => 'object',
2295                  'properties' => array(
2296                      'content' => array(
2297                          'raw' => array(
2298                              'type' => 'string',
2299                          ),
2300                      ),
2301                  ),
2302              ),
2303          );
2304  
2305          if ( 'post' === $this->post_type ) {
2306              $links[] = array(
2307                  'rel'          => 'https://api.w.org/action-sticky',
2308                  'title'        => __( 'The current user can sticky this post.' ),
2309                  'href'         => $href,
2310                  'targetSchema' => array(
2311                      'type'       => 'object',
2312                      'properties' => array(
2313                          'sticky' => array(
2314                              'type' => 'boolean',
2315                          ),
2316                      ),
2317                  ),
2318              );
2319          }
2320  
2321          if ( post_type_supports( $this->post_type, 'author' ) ) {
2322              $links[] = array(
2323                  'rel'          => 'https://api.w.org/action-assign-author',
2324                  'title'        => __( 'The current user can change the author on this post.' ),
2325                  'href'         => $href,
2326                  'targetSchema' => array(
2327                      'type'       => 'object',
2328                      'properties' => array(
2329                          'author' => array(
2330                              'type' => 'integer',
2331                          ),
2332                      ),
2333                  ),
2334              );
2335          }
2336  
2337          $taxonomies = wp_list_filter( get_object_taxonomies( $this->post_type, 'objects' ), array( 'show_in_rest' => true ) );
2338  
2339          foreach ( $taxonomies as $tax ) {
2340              $tax_base = ! empty( $tax->rest_base ) ? $tax->rest_base : $tax->name;
2341  
2342              /* translators: %s: Taxonomy name. */
2343              $assign_title = sprintf( __( 'The current user can assign terms in the %s taxonomy.' ), $tax->name );
2344              /* translators: %s: Taxonomy name. */
2345              $create_title = sprintf( __( 'The current user can create terms in the %s taxonomy.' ), $tax->name );
2346  
2347              $links[] = array(
2348                  'rel'          => 'https://api.w.org/action-assign-' . $tax_base,
2349                  'title'        => $assign_title,
2350                  'href'         => $href,
2351                  'targetSchema' => array(
2352                      'type'       => 'object',
2353                      'properties' => array(
2354                          $tax_base => array(
2355                              'type'  => 'array',
2356                              'items' => array(
2357                                  'type' => 'integer',
2358                              ),
2359                          ),
2360                      ),
2361                  ),
2362              );
2363  
2364              $links[] = array(
2365                  'rel'          => 'https://api.w.org/action-create-' . $tax_base,
2366                  'title'        => $create_title,
2367                  'href'         => $href,
2368                  'targetSchema' => array(
2369                      'type'       => 'object',
2370                      'properties' => array(
2371                          $tax_base => array(
2372                              'type'  => 'array',
2373                              'items' => array(
2374                                  'type' => 'integer',
2375                              ),
2376                          ),
2377                      ),
2378                  ),
2379              );
2380          }
2381  
2382          return $links;
2383      }
2384  
2385      /**
2386       * Retrieves the query params for the posts collection.
2387       *
2388       * @since 4.7.0
2389       *
2390       * @return array Collection parameters.
2391       */
2392  	public function get_collection_params() {
2393          $query_params = parent::get_collection_params();
2394  
2395          $query_params['context']['default'] = 'view';
2396  
2397          $query_params['after'] = array(
2398              'description' => __( 'Limit response to posts published after a given ISO8601 compliant date.' ),
2399              'type'        => 'string',
2400              'format'      => 'date-time',
2401          );
2402  
2403          if ( post_type_supports( $this->post_type, 'author' ) ) {
2404              $query_params['author']         = array(
2405                  'description' => __( 'Limit result set to posts assigned to specific authors.' ),
2406                  'type'        => 'array',
2407                  'items'       => array(
2408                      'type' => 'integer',
2409                  ),
2410                  'default'     => array(),
2411              );
2412              $query_params['author_exclude'] = array(
2413                  'description' => __( 'Ensure result set excludes posts assigned to specific authors.' ),
2414                  'type'        => 'array',
2415                  'items'       => array(
2416                      'type' => 'integer',
2417                  ),
2418                  'default'     => array(),
2419              );
2420          }
2421  
2422          $query_params['before'] = array(
2423              'description' => __( 'Limit response to posts published before a given ISO8601 compliant date.' ),
2424              'type'        => 'string',
2425              'format'      => 'date-time',
2426          );
2427  
2428          $query_params['exclude'] = array(
2429              'description' => __( 'Ensure result set excludes specific IDs.' ),
2430              'type'        => 'array',
2431              'items'       => array(
2432                  'type' => 'integer',
2433              ),
2434              'default'     => array(),
2435          );
2436  
2437          $query_params['include'] = array(
2438              'description' => __( 'Limit result set to specific IDs.' ),
2439              'type'        => 'array',
2440              'items'       => array(
2441                  'type' => 'integer',
2442              ),
2443              'default'     => array(),
2444          );
2445  
2446          if ( 'page' === $this->post_type || post_type_supports( $this->post_type, 'page-attributes' ) ) {
2447              $query_params['menu_order'] = array(
2448                  'description' => __( 'Limit result set to posts with a specific menu_order value.' ),
2449                  'type'        => 'integer',
2450              );
2451          }
2452  
2453          $query_params['offset'] = array(
2454              'description' => __( 'Offset the result set by a specific number of items.' ),
2455              'type'        => 'integer',
2456          );
2457  
2458          $query_params['order'] = array(
2459              'description' => __( 'Order sort attribute ascending or descending.' ),
2460              'type'        => 'string',
2461              'default'     => 'desc',
2462              'enum'        => array( 'asc', 'desc' ),
2463          );
2464  
2465          $query_params['orderby'] = array(
2466              'description' => __( 'Sort collection by object attribute.' ),
2467              'type'        => 'string',
2468              'default'     => 'date',
2469              'enum'        => array(
2470                  'author',
2471                  'date',
2472                  'id',
2473                  'include',
2474                  'modified',
2475                  'parent',
2476                  'relevance',
2477                  'slug',
2478                  'include_slugs',
2479                  'title',
2480              ),
2481          );
2482  
2483          if ( 'page' === $this->post_type || post_type_supports( $this->post_type, 'page-attributes' ) ) {
2484              $query_params['orderby']['enum'][] = 'menu_order';
2485          }
2486  
2487          $post_type = get_post_type_object( $this->post_type );
2488  
2489          if ( $post_type->hierarchical || 'attachment' === $this->post_type ) {
2490              $query_params['parent']         = array(
2491                  'description' => __( 'Limit result set to items with particular parent IDs.' ),
2492                  'type'        => 'array',
2493                  'items'       => array(
2494                      'type' => 'integer',
2495                  ),
2496                  'default'     => array(),
2497              );
2498              $query_params['parent_exclude'] = array(
2499                  'description' => __( 'Limit result set to all items except those of a particular parent ID.' ),
2500                  'type'        => 'array',
2501                  'items'       => array(
2502                      'type' => 'integer',
2503                  ),
2504                  'default'     => array(),
2505              );
2506          }
2507  
2508          $query_params['slug'] = array(
2509              'description'       => __( 'Limit result set to posts with one or more specific slugs.' ),
2510              'type'              => 'array',
2511              'items'             => array(
2512                  'type' => 'string',
2513              ),
2514              'sanitize_callback' => 'wp_parse_slug_list',
2515          );
2516  
2517          $query_params['status'] = array(
2518              'default'           => 'publish',
2519              'description'       => __( 'Limit result set to posts assigned one or more statuses.' ),
2520              'type'              => 'array',
2521              'items'             => array(
2522                  'enum' => array_merge( array_keys( get_post_stati() ), array( 'any' ) ),
2523                  'type' => 'string',
2524              ),
2525              'sanitize_callback' => array( $this, 'sanitize_post_statuses' ),
2526          );
2527  
2528          $taxonomies = wp_list_filter( get_object_taxonomies( $this->post_type, 'objects' ), array( 'show_in_rest' => true ) );
2529  
2530          foreach ( $taxonomies as $taxonomy ) {
2531              $base = ! empty( $taxonomy->rest_base ) ? $taxonomy->rest_base : $taxonomy->name;
2532  
2533              $query_params[ $base ] = array(
2534                  /* translators: %s: Taxonomy name. */
2535                  'description' => sprintf( __( 'Limit result set to all items that have the specified term assigned in the %s taxonomy.' ), $base ),
2536                  'type'        => 'array',
2537                  'items'       => array(
2538                      'type' => 'integer',
2539                  ),
2540                  'default'     => array(),
2541              );
2542  
2543              $query_params[ $base . '_exclude' ] = array(
2544                  /* translators: %s: Taxonomy name. */
2545                  'description' => sprintf( __( 'Limit result set to all items except those that have the specified term assigned in the %s taxonomy.' ), $base ),
2546                  'type'        => 'array',
2547                  'items'       => array(
2548                      'type' => 'integer',
2549                  ),
2550                  'default'     => array(),
2551              );
2552          }
2553  
2554          if ( 'post' === $this->post_type ) {
2555              $query_params['sticky'] = array(
2556                  'description' => __( 'Limit result set to items that are sticky.' ),
2557                  'type'        => 'boolean',
2558              );
2559          }
2560  
2561          /**
2562           * Filter collection parameters for the posts controller.
2563           *
2564           * The dynamic part of the filter `$this->post_type` refers to the post
2565           * type slug for the controller.
2566           *
2567           * This filter registers the collection parameter, but does not map the
2568           * collection parameter to an internal WP_Query parameter. Use the
2569           * `rest_{$this->post_type}_query` filter to set WP_Query parameters.
2570           *
2571           * @since 4.7.0
2572           *
2573           * @param array        $query_params JSON Schema-formatted collection parameters.
2574           * @param WP_Post_Type $post_type    Post type object.
2575           */
2576          return apply_filters( "rest_{$this->post_type}_collection_params", $query_params, $post_type );
2577      }
2578  
2579      /**
2580       * Sanitizes and validates the list of post statuses, including whether the
2581       * user can query private statuses.
2582       *
2583       * @since 4.7.0
2584       *
2585       * @param string|array    $statuses  One or more post statuses.
2586       * @param WP_REST_Request $request   Full details about the request.
2587       * @param string          $parameter Additional parameter to pass to validation.
2588       * @return array|WP_Error A list of valid statuses, otherwise WP_Error object.
2589       */
2590  	public function sanitize_post_statuses( $statuses, $request, $parameter ) {
2591          $statuses = wp_parse_slug_list( $statuses );
2592  
2593          // The default status is different in WP_REST_Attachments_Controller
2594          $attributes     = $request->get_attributes();
2595          $default_status = $attributes['args']['status']['default'];
2596  
2597          foreach ( $statuses as $status ) {
2598              if ( $status === $default_status ) {
2599                  continue;
2600              }
2601  
2602              $post_type_obj = get_post_type_object( $this->post_type );
2603  
2604              if ( current_user_can( $post_type_obj->cap->edit_posts ) || 'private' === $status && current_user_can( $post_type_obj->cap->read_private_posts ) ) {
2605                  $result = rest_validate_request_arg( $status, $request, $parameter );
2606                  if ( is_wp_error( $result ) ) {
2607                      return $result;
2608                  }
2609              } else {
2610                  return new WP_Error( 'rest_forbidden_status', __( 'Status is forbidden.' ), array( 'status' => rest_authorization_required_code() ) );
2611              }
2612          }
2613  
2614          return $statuses;
2615      }
2616  }


Generated: Tue Oct 22 08:20:01 2019 Cross-referenced by PHPXref 0.7