wp/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php
changeset 16 a86126ab1dd4
parent 9 177826044cd9
child 18 be944660c56a
equal deleted inserted replaced
15:3d4e9c994f10 16:a86126ab1dd4
    13  * @since 4.7.0
    13  * @since 4.7.0
    14  *
    14  *
    15  * @see WP_REST_Controller
    15  * @see WP_REST_Controller
    16  */
    16  */
    17 class WP_REST_Posts_Controller extends WP_REST_Controller {
    17 class WP_REST_Posts_Controller extends WP_REST_Controller {
    18 
       
    19 	/**
    18 	/**
    20 	 * Post type.
    19 	 * Post type.
    21 	 *
    20 	 *
    22 	 * @since 4.7.0
    21 	 * @since 4.7.0
    23 	 * @var string
    22 	 * @var string
   115 					'permission_callback' => array( $this, 'delete_item_permissions_check' ),
   114 					'permission_callback' => array( $this, 'delete_item_permissions_check' ),
   116 					'args'                => array(
   115 					'args'                => array(
   117 						'force' => array(
   116 						'force' => array(
   118 							'type'        => 'boolean',
   117 							'type'        => 'boolean',
   119 							'default'     => false,
   118 							'default'     => false,
   120 							'description' => __( 'Whether to bypass trash and force deletion.' ),
   119 							'description' => __( 'Whether to bypass Trash and force deletion.' ),
   121 						),
   120 						),
   122 					),
   121 					),
   123 				),
   122 				),
   124 				'schema' => array( $this, 'get_public_item_schema' ),
   123 				'schema' => array( $this, 'get_public_item_schema' ),
   125 			)
   124 			)
   129 	/**
   128 	/**
   130 	 * Checks if a given request has access to read posts.
   129 	 * Checks if a given request has access to read posts.
   131 	 *
   130 	 *
   132 	 * @since 4.7.0
   131 	 * @since 4.7.0
   133 	 *
   132 	 *
   134 	 * @param  WP_REST_Request $request Full details about the request.
   133 	 * @param WP_REST_Request $request Full details about the request.
   135 	 * @return true|WP_Error True if the request has read access, WP_Error object otherwise.
   134 	 * @return true|WP_Error True if the request has read access, WP_Error object otherwise.
   136 	 */
   135 	 */
   137 	public function get_items_permissions_check( $request ) {
   136 	public function get_items_permissions_check( $request ) {
   138 
   137 
   139 		$post_type = get_post_type_object( $this->post_type );
   138 		$post_type = get_post_type_object( $this->post_type );
   140 
   139 
   141 		if ( 'edit' === $request['context'] && ! current_user_can( $post_type->cap->edit_posts ) ) {
   140 		if ( 'edit' === $request['context'] && ! current_user_can( $post_type->cap->edit_posts ) ) {
   142 			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() ) );
   141 			return new WP_Error(
       
   142 				'rest_forbidden_context',
       
   143 				__( 'Sorry, you are not allowed to edit posts in this post type.' ),
       
   144 				array( 'status' => rest_authorization_required_code() )
       
   145 			);
   143 		}
   146 		}
   144 
   147 
   145 		return true;
   148 		return true;
   146 	}
   149 	}
   147 
   150 
   155 	 */
   158 	 */
   156 	public function get_items( $request ) {
   159 	public function get_items( $request ) {
   157 
   160 
   158 		// Ensure a search string is set in case the orderby is set to 'relevance'.
   161 		// Ensure a search string is set in case the orderby is set to 'relevance'.
   159 		if ( ! empty( $request['orderby'] ) && 'relevance' === $request['orderby'] && empty( $request['search'] ) ) {
   162 		if ( ! empty( $request['orderby'] ) && 'relevance' === $request['orderby'] && empty( $request['search'] ) ) {
   160 			return new WP_Error( 'rest_no_search_term_defined', __( 'You need to define a search term to order by relevance.' ), array( 'status' => 400 ) );
   163 			return new WP_Error(
       
   164 				'rest_no_search_term_defined',
       
   165 				__( 'You need to define a search term to order by relevance.' ),
       
   166 				array( 'status' => 400 )
       
   167 			);
   161 		}
   168 		}
   162 
   169 
   163 		// Ensure an include parameter is set in case the orderby is set to 'include'.
   170 		// Ensure an include parameter is set in case the orderby is set to 'include'.
   164 		if ( ! empty( $request['orderby'] ) && 'include' === $request['orderby'] && empty( $request['include'] ) ) {
   171 		if ( ! empty( $request['orderby'] ) && 'include' === $request['orderby'] && empty( $request['include'] ) ) {
   165 			return new WP_Error( 'rest_orderby_include_missing_include', __( 'You need to define an include parameter to order by include.' ), array( 'status' => 400 ) );
   172 			return new WP_Error(
       
   173 				'rest_orderby_include_missing_include',
       
   174 				__( 'You need to define an include parameter to order by include.' ),
       
   175 				array( 'status' => 400 )
       
   176 			);
   166 		}
   177 		}
   167 
   178 
   168 		// Retrieve the list of registered collection query parameters.
   179 		// Retrieve the list of registered collection query parameters.
   169 		$registered = $this->get_collection_params();
   180 		$registered = $this->get_collection_params();
   170 		$args       = array();
   181 		$args       = array();
   232 				 * specified.
   243 				 * specified.
   233 				 */
   244 				 */
   234 				$args['post__in'] = $args['post__in'] ? array_intersect( $sticky_posts, $args['post__in'] ) : $sticky_posts;
   245 				$args['post__in'] = $args['post__in'] ? array_intersect( $sticky_posts, $args['post__in'] ) : $sticky_posts;
   235 
   246 
   236 				/*
   247 				/*
   237 				 * If we intersected, but there are no post ids in common,
   248 				 * If we intersected, but there are no post IDs in common,
   238 				 * WP_Query won't return "no posts" for post__in = array()
   249 				 * WP_Query won't return "no posts" for post__in = array()
   239 				 * so we have to fake it a bit.
   250 				 * so we have to fake it a bit.
   240 				 */
   251 				 */
   241 				if ( ! $args['post__in'] ) {
   252 				if ( ! $args['post__in'] ) {
   242 					$args['post__in'] = array( 0 );
   253 					$args['post__in'] = array( 0 );
   269 		$args       = apply_filters( "rest_{$this->post_type}_query", $args, $request );
   280 		$args       = apply_filters( "rest_{$this->post_type}_query", $args, $request );
   270 		$query_args = $this->prepare_items_query( $args, $request );
   281 		$query_args = $this->prepare_items_query( $args, $request );
   271 
   282 
   272 		$taxonomies = wp_list_filter( get_object_taxonomies( $this->post_type, 'objects' ), array( 'show_in_rest' => true ) );
   283 		$taxonomies = wp_list_filter( get_object_taxonomies( $this->post_type, 'objects' ), array( 'show_in_rest' => true ) );
   273 
   284 
       
   285 		if ( ! empty( $request['tax_relation'] ) ) {
       
   286 			$query_args['tax_query'] = array( 'relation' => $request['tax_relation'] );
       
   287 		}
       
   288 
   274 		foreach ( $taxonomies as $taxonomy ) {
   289 		foreach ( $taxonomies as $taxonomy ) {
   275 			$base        = ! empty( $taxonomy->rest_base ) ? $taxonomy->rest_base : $taxonomy->name;
   290 			$base        = ! empty( $taxonomy->rest_base ) ? $taxonomy->rest_base : $taxonomy->name;
   276 			$tax_exclude = $base . '_exclude';
   291 			$tax_exclude = $base . '_exclude';
   277 
   292 
   278 			if ( ! empty( $request[ $base ] ) ) {
   293 			if ( ! empty( $request[ $base ] ) ) {
   332 		}
   347 		}
   333 
   348 
   334 		$max_pages = ceil( $total_posts / (int) $posts_query->query_vars['posts_per_page'] );
   349 		$max_pages = ceil( $total_posts / (int) $posts_query->query_vars['posts_per_page'] );
   335 
   350 
   336 		if ( $page > $max_pages && $total_posts > 0 ) {
   351 		if ( $page > $max_pages && $total_posts > 0 ) {
   337 			return new WP_Error( 'rest_post_invalid_page_number', __( 'The page number requested is larger than the number of pages available.' ), array( 'status' => 400 ) );
   352 			return new WP_Error(
       
   353 				'rest_post_invalid_page_number',
       
   354 				__( 'The page number requested is larger than the number of pages available.' ),
       
   355 				array( 'status' => 400 )
       
   356 			);
   338 		}
   357 		}
   339 
   358 
   340 		$response = rest_ensure_response( $posts );
   359 		$response = rest_ensure_response( $posts );
   341 
   360 
   342 		$response->header( 'X-WP-Total', (int) $total_posts );
   361 		$response->header( 'X-WP-Total', (int) $total_posts );
   372 	 *
   391 	 *
   373 	 * @param int $id Supplied ID.
   392 	 * @param int $id Supplied ID.
   374 	 * @return WP_Post|WP_Error Post object if ID is valid, WP_Error otherwise.
   393 	 * @return WP_Post|WP_Error Post object if ID is valid, WP_Error otherwise.
   375 	 */
   394 	 */
   376 	protected function get_post( $id ) {
   395 	protected function get_post( $id ) {
   377 		$error = new WP_Error( 'rest_post_invalid_id', __( 'Invalid post ID.' ), array( 'status' => 404 ) );
   396 		$error = new WP_Error(
       
   397 			'rest_post_invalid_id',
       
   398 			__( 'Invalid post ID.' ),
       
   399 			array( 'status' => 404 )
       
   400 		);
       
   401 
   378 		if ( (int) $id <= 0 ) {
   402 		if ( (int) $id <= 0 ) {
   379 			return $error;
   403 			return $error;
   380 		}
   404 		}
   381 
   405 
   382 		$post = get_post( (int) $id );
   406 		$post = get_post( (int) $id );
   400 		if ( is_wp_error( $post ) ) {
   424 		if ( is_wp_error( $post ) ) {
   401 			return $post;
   425 			return $post;
   402 		}
   426 		}
   403 
   427 
   404 		if ( 'edit' === $request['context'] && $post && ! $this->check_update_permission( $post ) ) {
   428 		if ( 'edit' === $request['context'] && $post && ! $this->check_update_permission( $post ) ) {
   405 			return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you are not allowed to edit this post.' ), array( 'status' => rest_authorization_required_code() ) );
   429 			return new WP_Error(
       
   430 				'rest_forbidden_context',
       
   431 				__( 'Sorry, you are not allowed to edit this post.' ),
       
   432 				array( 'status' => rest_authorization_required_code() )
       
   433 			);
   406 		}
   434 		}
   407 
   435 
   408 		if ( $post && ! empty( $request['password'] ) ) {
   436 		if ( $post && ! empty( $request['password'] ) ) {
   409 			// Check post password, and return error if invalid.
   437 			// Check post password, and return error if invalid.
   410 			if ( ! hash_equals( $post->post_password, $request['password'] ) ) {
   438 			if ( ! hash_equals( $post->post_password, $request['password'] ) ) {
   411 				return new WP_Error( 'rest_post_incorrect_password', __( 'Incorrect post password.' ), array( 'status' => 403 ) );
   439 				return new WP_Error(
       
   440 					'rest_post_incorrect_password',
       
   441 					__( 'Incorrect post password.' ),
       
   442 					array( 'status' => 403 )
       
   443 				);
   412 			}
   444 			}
   413 		}
   445 		}
   414 
   446 
   415 		// Allow access to all password protected posts if the context is edit.
   447 		// Allow access to all password protected posts if the context is edit.
   416 		if ( 'edit' === $request['context'] ) {
   448 		if ( 'edit' === $request['context'] ) {
   488 	 * @param WP_REST_Request $request Full details about the request.
   520 	 * @param WP_REST_Request $request Full details about the request.
   489 	 * @return true|WP_Error True if the request has access to create items, WP_Error object otherwise.
   521 	 * @return true|WP_Error True if the request has access to create items, WP_Error object otherwise.
   490 	 */
   522 	 */
   491 	public function create_item_permissions_check( $request ) {
   523 	public function create_item_permissions_check( $request ) {
   492 		if ( ! empty( $request['id'] ) ) {
   524 		if ( ! empty( $request['id'] ) ) {
   493 			return new WP_Error( 'rest_post_exists', __( 'Cannot create existing post.' ), array( 'status' => 400 ) );
   525 			return new WP_Error(
       
   526 				'rest_post_exists',
       
   527 				__( 'Cannot create existing post.' ),
       
   528 				array( 'status' => 400 )
       
   529 			);
   494 		}
   530 		}
   495 
   531 
   496 		$post_type = get_post_type_object( $this->post_type );
   532 		$post_type = get_post_type_object( $this->post_type );
   497 
   533 
   498 		if ( ! empty( $request['author'] ) && get_current_user_id() !== $request['author'] && ! current_user_can( $post_type->cap->edit_others_posts ) ) {
   534 		if ( ! empty( $request['author'] ) && get_current_user_id() !== $request['author'] && ! current_user_can( $post_type->cap->edit_others_posts ) ) {
   499 			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() ) );
   535 			return new WP_Error(
   500 		}
   536 				'rest_cannot_edit_others',
   501 
   537 				__( 'Sorry, you are not allowed to create posts as this user.' ),
   502 		if ( ! empty( $request['sticky'] ) && ! current_user_can( $post_type->cap->edit_others_posts ) ) {
   538 				array( 'status' => rest_authorization_required_code() )
   503 			return new WP_Error( 'rest_cannot_assign_sticky', __( 'Sorry, you are not allowed to make posts sticky.' ), array( 'status' => rest_authorization_required_code() ) );
   539 			);
       
   540 		}
       
   541 
       
   542 		if ( ! empty( $request['sticky'] ) && ! current_user_can( $post_type->cap->edit_others_posts ) && ! current_user_can( $post_type->cap->publish_posts ) ) {
       
   543 			return new WP_Error(
       
   544 				'rest_cannot_assign_sticky',
       
   545 				__( 'Sorry, you are not allowed to make posts sticky.' ),
       
   546 				array( 'status' => rest_authorization_required_code() )
       
   547 			);
   504 		}
   548 		}
   505 
   549 
   506 		if ( ! current_user_can( $post_type->cap->create_posts ) ) {
   550 		if ( ! current_user_can( $post_type->cap->create_posts ) ) {
   507 			return new WP_Error( 'rest_cannot_create', __( 'Sorry, you are not allowed to create posts as this user.' ), array( 'status' => rest_authorization_required_code() ) );
   551 			return new WP_Error(
       
   552 				'rest_cannot_create',
       
   553 				__( 'Sorry, you are not allowed to create posts as this user.' ),
       
   554 				array( 'status' => rest_authorization_required_code() )
       
   555 			);
   508 		}
   556 		}
   509 
   557 
   510 		if ( ! $this->check_assign_terms_permission( $request ) ) {
   558 		if ( ! $this->check_assign_terms_permission( $request ) ) {
   511 			return new WP_Error( 'rest_cannot_assign_term', __( 'Sorry, you are not allowed to assign the provided terms.' ), array( 'status' => rest_authorization_required_code() ) );
   559 			return new WP_Error(
       
   560 				'rest_cannot_assign_term',
       
   561 				__( 'Sorry, you are not allowed to assign the provided terms.' ),
       
   562 				array( 'status' => rest_authorization_required_code() )
       
   563 			);
   512 		}
   564 		}
   513 
   565 
   514 		return true;
   566 		return true;
   515 	}
   567 	}
   516 
   568 
   522 	 * @param WP_REST_Request $request Full details about the request.
   574 	 * @param WP_REST_Request $request Full details about the request.
   523 	 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
   575 	 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
   524 	 */
   576 	 */
   525 	public function create_item( $request ) {
   577 	public function create_item( $request ) {
   526 		if ( ! empty( $request['id'] ) ) {
   578 		if ( ! empty( $request['id'] ) ) {
   527 			return new WP_Error( 'rest_post_exists', __( 'Cannot create existing post.' ), array( 'status' => 400 ) );
   579 			return new WP_Error(
       
   580 				'rest_post_exists',
       
   581 				__( 'Cannot create existing post.' ),
       
   582 				array( 'status' => 400 )
       
   583 			);
   528 		}
   584 		}
   529 
   585 
   530 		$prepared_post = $this->prepare_item_for_database( $request );
   586 		$prepared_post = $this->prepare_item_for_database( $request );
   531 
   587 
   532 		if ( is_wp_error( $prepared_post ) ) {
   588 		if ( is_wp_error( $prepared_post ) ) {
   645 		}
   701 		}
   646 
   702 
   647 		$post_type = get_post_type_object( $this->post_type );
   703 		$post_type = get_post_type_object( $this->post_type );
   648 
   704 
   649 		if ( $post && ! $this->check_update_permission( $post ) ) {
   705 		if ( $post && ! $this->check_update_permission( $post ) ) {
   650 			return new WP_Error( 'rest_cannot_edit', __( 'Sorry, you are not allowed to edit this post.' ), array( 'status' => rest_authorization_required_code() ) );
   706 			return new WP_Error(
       
   707 				'rest_cannot_edit',
       
   708 				__( 'Sorry, you are not allowed to edit this post.' ),
       
   709 				array( 'status' => rest_authorization_required_code() )
       
   710 			);
   651 		}
   711 		}
   652 
   712 
   653 		if ( ! empty( $request['author'] ) && get_current_user_id() !== $request['author'] && ! current_user_can( $post_type->cap->edit_others_posts ) ) {
   713 		if ( ! empty( $request['author'] ) && get_current_user_id() !== $request['author'] && ! current_user_can( $post_type->cap->edit_others_posts ) ) {
   654 			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() ) );
   714 			return new WP_Error(
   655 		}
   715 				'rest_cannot_edit_others',
   656 
   716 				__( 'Sorry, you are not allowed to update posts as this user.' ),
   657 		if ( ! empty( $request['sticky'] ) && ! current_user_can( $post_type->cap->edit_others_posts ) ) {
   717 				array( 'status' => rest_authorization_required_code() )
   658 			return new WP_Error( 'rest_cannot_assign_sticky', __( 'Sorry, you are not allowed to make posts sticky.' ), array( 'status' => rest_authorization_required_code() ) );
   718 			);
       
   719 		}
       
   720 
       
   721 		if ( ! empty( $request['sticky'] ) && ! current_user_can( $post_type->cap->edit_others_posts ) && ! current_user_can( $post_type->cap->publish_posts ) ) {
       
   722 			return new WP_Error(
       
   723 				'rest_cannot_assign_sticky',
       
   724 				__( 'Sorry, you are not allowed to make posts sticky.' ),
       
   725 				array( 'status' => rest_authorization_required_code() )
       
   726 			);
   659 		}
   727 		}
   660 
   728 
   661 		if ( ! $this->check_assign_terms_permission( $request ) ) {
   729 		if ( ! $this->check_assign_terms_permission( $request ) ) {
   662 			return new WP_Error( 'rest_cannot_assign_term', __( 'Sorry, you are not allowed to assign the provided terms.' ), array( 'status' => rest_authorization_required_code() ) );
   730 			return new WP_Error(
       
   731 				'rest_cannot_assign_term',
       
   732 				__( 'Sorry, you are not allowed to assign the provided terms.' ),
       
   733 				array( 'status' => rest_authorization_required_code() )
       
   734 			);
   663 		}
   735 		}
   664 
   736 
   665 		return true;
   737 		return true;
   666 	}
   738 	}
   667 
   739 
   683 
   755 
   684 		if ( is_wp_error( $post ) ) {
   756 		if ( is_wp_error( $post ) ) {
   685 			return $post;
   757 			return $post;
   686 		}
   758 		}
   687 
   759 
   688 		// convert the post object to an array, otherwise wp_update_post will expect non-escaped input.
   760 		// Convert the post object to an array, otherwise wp_update_post() will expect non-escaped input.
   689 		$post_id = wp_update_post( wp_slash( (array) $post ), true );
   761 		$post_id = wp_update_post( wp_slash( (array) $post ), true );
   690 
   762 
   691 		if ( is_wp_error( $post_id ) ) {
   763 		if ( is_wp_error( $post_id ) ) {
   692 			if ( 'db_update_error' === $post_id->get_error_code() ) {
   764 			if ( 'db_update_error' === $post_id->get_error_code() ) {
   693 				$post_id->add_data( array( 'status' => 500 ) );
   765 				$post_id->add_data( array( 'status' => 500 ) );
   774 		if ( is_wp_error( $post ) ) {
   846 		if ( is_wp_error( $post ) ) {
   775 			return $post;
   847 			return $post;
   776 		}
   848 		}
   777 
   849 
   778 		if ( $post && ! $this->check_delete_permission( $post ) ) {
   850 		if ( $post && ! $this->check_delete_permission( $post ) ) {
   779 			return new WP_Error( 'rest_cannot_delete', __( 'Sorry, you are not allowed to delete this post.' ), array( 'status' => rest_authorization_required_code() ) );
   851 			return new WP_Error(
       
   852 				'rest_cannot_delete',
       
   853 				__( 'Sorry, you are not allowed to delete this post.' ),
       
   854 				array( 'status' => rest_authorization_required_code() )
       
   855 			);
   780 		}
   856 		}
   781 
   857 
   782 		return true;
   858 		return true;
   783 	}
   859 	}
   784 
   860 
   808 		/**
   884 		/**
   809 		 * Filters whether a post is trashable.
   885 		 * Filters whether a post is trashable.
   810 		 *
   886 		 *
   811 		 * The dynamic portion of the hook name, `$this->post_type`, refers to the post type slug.
   887 		 * The dynamic portion of the hook name, `$this->post_type`, refers to the post type slug.
   812 		 *
   888 		 *
   813 		 * Pass false to disable trash support for the post.
   889 		 * Pass false to disable Trash support for the post.
   814 		 *
   890 		 *
   815 		 * @since 4.7.0
   891 		 * @since 4.7.0
   816 		 *
   892 		 *
   817 		 * @param bool    $supports_trash Whether the post type support trashing.
   893 		 * @param bool    $supports_trash Whether the post type support trashing.
   818 		 * @param WP_Post $post           The Post object being considered for trashing support.
   894 		 * @param WP_Post $post           The Post object being considered for trashing support.
   819 		 */
   895 		 */
   820 		$supports_trash = apply_filters( "rest_{$this->post_type}_trashable", $supports_trash, $post );
   896 		$supports_trash = apply_filters( "rest_{$this->post_type}_trashable", $supports_trash, $post );
   821 
   897 
   822 		if ( ! $this->check_delete_permission( $post ) ) {
   898 		if ( ! $this->check_delete_permission( $post ) ) {
   823 			return new WP_Error( 'rest_user_cannot_delete_post', __( 'Sorry, you are not allowed to delete this post.' ), array( 'status' => rest_authorization_required_code() ) );
   899 			return new WP_Error(
       
   900 				'rest_user_cannot_delete_post',
       
   901 				__( 'Sorry, you are not allowed to delete this post.' ),
       
   902 				array( 'status' => rest_authorization_required_code() )
       
   903 			);
   824 		}
   904 		}
   825 
   905 
   826 		$request->set_param( 'context', 'edit' );
   906 		$request->set_param( 'context', 'edit' );
   827 
   907 
   828 		// If we're forcing, then delete permanently.
   908 		// If we're forcing, then delete permanently.
   837 				)
   917 				)
   838 			);
   918 			);
   839 		} else {
   919 		} else {
   840 			// If we don't support trashing for this type, error out.
   920 			// If we don't support trashing for this type, error out.
   841 			if ( ! $supports_trash ) {
   921 			if ( ! $supports_trash ) {
   842 				/* translators: %s: force=true */
   922 				return new WP_Error(
   843 				return new WP_Error( 'rest_trash_not_supported', sprintf( __( "The post does not support trashing. Set '%s' to delete." ), 'force=true' ), array( 'status' => 501 ) );
   923 					'rest_trash_not_supported',
       
   924 					/* translators: %s: force=true */
       
   925 					sprintf( __( "The post does not support trashing. Set '%s' to delete." ), 'force=true' ),
       
   926 					array( 'status' => 501 )
       
   927 				);
   844 			}
   928 			}
   845 
   929 
   846 			// Otherwise, only trash if we haven't already.
   930 			// Otherwise, only trash if we haven't already.
   847 			if ( 'trash' === $post->post_status ) {
   931 			if ( 'trash' === $post->post_status ) {
   848 				return new WP_Error( 'rest_already_trashed', __( 'The post has already been deleted.' ), array( 'status' => 410 ) );
   932 				return new WP_Error(
   849 			}
   933 					'rest_already_trashed',
   850 
   934 					__( 'The post has already been deleted.' ),
   851 			// (Note that internally this falls through to `wp_delete_post` if
   935 					array( 'status' => 410 )
   852 			// the trash is disabled.)
   936 				);
       
   937 			}
       
   938 
       
   939 			// (Note that internally this falls through to `wp_delete_post()`
       
   940 			// if the Trash is disabled.)
   853 			$result   = wp_trash_post( $id );
   941 			$result   = wp_trash_post( $id );
   854 			$post     = get_post( $id );
   942 			$post     = get_post( $id );
   855 			$response = $this->prepare_item_for_response( $post, $request );
   943 			$response = $this->prepare_item_for_response( $post, $request );
   856 		}
   944 		}
   857 
   945 
   858 		if ( ! $result ) {
   946 		if ( ! $result ) {
   859 			return new WP_Error( 'rest_cannot_delete', __( 'The post cannot be deleted.' ), array( 'status' => 500 ) );
   947 			return new WP_Error(
       
   948 				'rest_cannot_delete',
       
   949 				__( 'The post cannot be deleted.' ),
       
   950 				array( 'status' => 500 )
       
   951 			);
   860 		}
   952 		}
   861 
   953 
   862 		/**
   954 		/**
   863 		 * Fires immediately after a single post is deleted or trashed via the REST API.
   955 		 * Fires immediately after a single post is deleted or trashed via the REST API.
   864 		 *
   956 		 *
   865 		 * They dynamic portion of the hook name, `$this->post_type`, refers to the post type slug.
   957 		 * They dynamic portion of the hook name, `$this->post_type`, refers to the post type slug.
   866 		 *
   958 		 *
   867 		 * @since 4.7.0
   959 		 * @since 4.7.0
   868 		 *
   960 		 *
   869 		 * @param object           $post     The deleted or trashed post.
   961 		 * @param WP_Post          $post     The deleted or trashed post.
   870 		 * @param WP_REST_Response $response The response data.
   962 		 * @param WP_REST_Response $response The response data.
   871 		 * @param WP_REST_Request  $request  The request sent to the API.
   963 		 * @param WP_REST_Request  $request  The request sent to the API.
   872 		 */
   964 		 */
   873 		do_action( "rest_delete_{$this->post_type}", $post, $response, $request );
   965 		do_action( "rest_delete_{$this->post_type}", $post, $response, $request );
   874 
   966 
   954 	 *
  1046 	 *
   955 	 * @param WP_REST_Request $request Request object.
  1047 	 * @param WP_REST_Request $request Request object.
   956 	 * @return stdClass|WP_Error Post object or WP_Error.
  1048 	 * @return stdClass|WP_Error Post object or WP_Error.
   957 	 */
  1049 	 */
   958 	protected function prepare_item_for_database( $request ) {
  1050 	protected function prepare_item_for_database( $request ) {
   959 		$prepared_post = new stdClass;
  1051 		$prepared_post = new stdClass();
   960 
  1052 
   961 		// Post ID.
  1053 		// Post ID.
   962 		if ( isset( $request['id'] ) ) {
  1054 		if ( isset( $request['id'] ) ) {
   963 			$existing_post = $this->get_post( $request['id'] );
  1055 			$existing_post = $this->get_post( $request['id'] );
   964 			if ( is_wp_error( $existing_post ) ) {
  1056 			if ( is_wp_error( $existing_post ) ) {
  1019 			$prepared_post->post_status = $status;
  1111 			$prepared_post->post_status = $status;
  1020 		}
  1112 		}
  1021 
  1113 
  1022 		// Post date.
  1114 		// Post date.
  1023 		if ( ! empty( $schema['properties']['date'] ) && ! empty( $request['date'] ) ) {
  1115 		if ( ! empty( $schema['properties']['date'] ) && ! empty( $request['date'] ) ) {
  1024 			$date_data = rest_get_date_with_gmt( $request['date'] );
  1116 			$current_date = isset( $prepared_post->ID ) ? get_post( $prepared_post->ID )->post_date : false;
  1025 
  1117 			$date_data    = rest_get_date_with_gmt( $request['date'] );
  1026 			if ( ! empty( $date_data ) ) {
  1118 
       
  1119 			if ( ! empty( $date_data ) && $current_date !== $date_data[0] ) {
  1027 				list( $prepared_post->post_date, $prepared_post->post_date_gmt ) = $date_data;
  1120 				list( $prepared_post->post_date, $prepared_post->post_date_gmt ) = $date_data;
  1028 				$prepared_post->edit_date                                        = true;
  1121 				$prepared_post->edit_date                                        = true;
  1029 			}
  1122 			}
  1030 		} elseif ( ! empty( $schema['properties']['date_gmt'] ) && ! empty( $request['date_gmt'] ) ) {
  1123 		} elseif ( ! empty( $schema['properties']['date_gmt'] ) && ! empty( $request['date_gmt'] ) ) {
  1031 			$date_data = rest_get_date_with_gmt( $request['date_gmt'], true );
  1124 			$current_date = isset( $prepared_post->ID ) ? get_post( $prepared_post->ID )->post_date_gmt : false;
  1032 
  1125 			$date_data    = rest_get_date_with_gmt( $request['date_gmt'], true );
  1033 			if ( ! empty( $date_data ) ) {
  1126 
       
  1127 			if ( ! empty( $date_data ) && $current_date !== $date_data[1] ) {
  1034 				list( $prepared_post->post_date, $prepared_post->post_date_gmt ) = $date_data;
  1128 				list( $prepared_post->post_date, $prepared_post->post_date_gmt ) = $date_data;
  1035 				$prepared_post->edit_date                                        = true;
  1129 				$prepared_post->edit_date                                        = true;
  1036 			}
  1130 			}
       
  1131 		}
       
  1132 
       
  1133 		// Sending a null date or date_gmt value resets date and date_gmt to their
       
  1134 		// default values (`0000-00-00 00:00:00`).
       
  1135 		if (
       
  1136 			( ! empty( $schema['properties']['date_gmt'] ) && $request->has_param( 'date_gmt' ) && null === $request['date_gmt'] ) ||
       
  1137 			( ! empty( $schema['properties']['date'] ) && $request->has_param( 'date' ) && null === $request['date'] )
       
  1138 		) {
       
  1139 			$prepared_post->post_date_gmt = null;
       
  1140 			$prepared_post->post_date     = null;
  1037 		}
  1141 		}
  1038 
  1142 
  1039 		// Post slug.
  1143 		// Post slug.
  1040 		if ( ! empty( $schema['properties']['slug'] ) && isset( $request['slug'] ) ) {
  1144 		if ( ! empty( $schema['properties']['slug'] ) && isset( $request['slug'] ) ) {
  1041 			$prepared_post->post_name = $request['slug'];
  1145 			$prepared_post->post_name = $request['slug'];
  1047 
  1151 
  1048 			if ( get_current_user_id() !== $post_author ) {
  1152 			if ( get_current_user_id() !== $post_author ) {
  1049 				$user_obj = get_userdata( $post_author );
  1153 				$user_obj = get_userdata( $post_author );
  1050 
  1154 
  1051 				if ( ! $user_obj ) {
  1155 				if ( ! $user_obj ) {
  1052 					return new WP_Error( 'rest_invalid_author', __( 'Invalid author ID.' ), array( 'status' => 400 ) );
  1156 					return new WP_Error(
       
  1157 						'rest_invalid_author',
       
  1158 						__( 'Invalid author ID.' ),
       
  1159 						array( 'status' => 400 )
       
  1160 					);
  1053 				}
  1161 				}
  1054 			}
  1162 			}
  1055 
  1163 
  1056 			$prepared_post->post_author = $post_author;
  1164 			$prepared_post->post_author = $post_author;
  1057 		}
  1165 		}
  1060 		if ( ! empty( $schema['properties']['password'] ) && isset( $request['password'] ) ) {
  1168 		if ( ! empty( $schema['properties']['password'] ) && isset( $request['password'] ) ) {
  1061 			$prepared_post->post_password = $request['password'];
  1169 			$prepared_post->post_password = $request['password'];
  1062 
  1170 
  1063 			if ( '' !== $request['password'] ) {
  1171 			if ( '' !== $request['password'] ) {
  1064 				if ( ! empty( $schema['properties']['sticky'] ) && ! empty( $request['sticky'] ) ) {
  1172 				if ( ! empty( $schema['properties']['sticky'] ) && ! empty( $request['sticky'] ) ) {
  1065 					return new WP_Error( 'rest_invalid_field', __( 'A post can not be sticky and have a password.' ), array( 'status' => 400 ) );
  1173 					return new WP_Error(
       
  1174 						'rest_invalid_field',
       
  1175 						__( 'A post can not be sticky and have a password.' ),
       
  1176 						array( 'status' => 400 )
       
  1177 					);
  1066 				}
  1178 				}
  1067 
  1179 
  1068 				if ( ! empty( $prepared_post->ID ) && is_sticky( $prepared_post->ID ) ) {
  1180 				if ( ! empty( $prepared_post->ID ) && is_sticky( $prepared_post->ID ) ) {
  1069 					return new WP_Error( 'rest_invalid_field', __( 'A sticky post can not be password protected.' ), array( 'status' => 400 ) );
  1181 					return new WP_Error(
       
  1182 						'rest_invalid_field',
       
  1183 						__( 'A sticky post can not be password protected.' ),
       
  1184 						array( 'status' => 400 )
       
  1185 					);
  1070 				}
  1186 				}
  1071 			}
  1187 			}
  1072 		}
  1188 		}
  1073 
  1189 
  1074 		if ( ! empty( $schema['properties']['sticky'] ) && ! empty( $request['sticky'] ) ) {
  1190 		if ( ! empty( $schema['properties']['sticky'] ) && ! empty( $request['sticky'] ) ) {
  1075 			if ( ! empty( $prepared_post->ID ) && post_password_required( $prepared_post->ID ) ) {
  1191 			if ( ! empty( $prepared_post->ID ) && post_password_required( $prepared_post->ID ) ) {
  1076 				return new WP_Error( 'rest_invalid_field', __( 'A password protected post can not be set to sticky.' ), array( 'status' => 400 ) );
  1192 				return new WP_Error(
       
  1193 					'rest_invalid_field',
       
  1194 					__( 'A password protected post can not be set to sticky.' ),
       
  1195 					array( 'status' => 400 )
       
  1196 				);
  1077 			}
  1197 			}
  1078 		}
  1198 		}
  1079 
  1199 
  1080 		// Parent.
  1200 		// Parent.
  1081 		if ( ! empty( $schema['properties']['parent'] ) && isset( $request['parent'] ) ) {
  1201 		if ( ! empty( $schema['properties']['parent'] ) && isset( $request['parent'] ) ) {
  1082 			if ( 0 === (int) $request['parent'] ) {
  1202 			if ( 0 === (int) $request['parent'] ) {
  1083 				$prepared_post->post_parent = 0;
  1203 				$prepared_post->post_parent = 0;
  1084 			} else {
  1204 			} else {
  1085 				$parent = get_post( (int) $request['parent'] );
  1205 				$parent = get_post( (int) $request['parent'] );
       
  1206 
  1086 				if ( empty( $parent ) ) {
  1207 				if ( empty( $parent ) ) {
  1087 					return new WP_Error( 'rest_post_invalid_id', __( 'Invalid post parent ID.' ), array( 'status' => 400 ) );
  1208 					return new WP_Error(
       
  1209 						'rest_post_invalid_id',
       
  1210 						__( 'Invalid post parent ID.' ),
       
  1211 						array( 'status' => 400 )
       
  1212 					);
  1088 				}
  1213 				}
       
  1214 
  1089 				$prepared_post->post_parent = (int) $parent->ID;
  1215 				$prepared_post->post_parent = (int) $parent->ID;
  1090 			}
  1216 			}
  1091 		}
  1217 		}
  1092 
  1218 
  1093 		// Menu order.
  1219 		// Menu order.
  1128 	/**
  1254 	/**
  1129 	 * Determines validity and normalizes the given status parameter.
  1255 	 * Determines validity and normalizes the given status parameter.
  1130 	 *
  1256 	 *
  1131 	 * @since 4.7.0
  1257 	 * @since 4.7.0
  1132 	 *
  1258 	 *
  1133 	 * @param string $post_status Post status.
  1259 	 * @param string       $post_status Post status.
  1134 	 * @param object $post_type   Post type.
  1260 	 * @param WP_Post_Type $post_type   Post type.
  1135 	 * @return string|WP_Error Post status or WP_Error if lacking the proper permission.
  1261 	 * @return string|WP_Error Post status or WP_Error if lacking the proper permission.
  1136 	 */
  1262 	 */
  1137 	protected function handle_status_param( $post_status, $post_type ) {
  1263 	protected function handle_status_param( $post_status, $post_type ) {
  1138 
  1264 
  1139 		switch ( $post_status ) {
  1265 		switch ( $post_status ) {
  1140 			case 'draft':
  1266 			case 'draft':
  1141 			case 'pending':
  1267 			case 'pending':
  1142 				break;
  1268 				break;
  1143 			case 'private':
  1269 			case 'private':
  1144 				if ( ! current_user_can( $post_type->cap->publish_posts ) ) {
  1270 				if ( ! current_user_can( $post_type->cap->publish_posts ) ) {
  1145 					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() ) );
  1271 					return new WP_Error(
       
  1272 						'rest_cannot_publish',
       
  1273 						__( 'Sorry, you are not allowed to create private posts in this post type.' ),
       
  1274 						array( 'status' => rest_authorization_required_code() )
       
  1275 					);
  1146 				}
  1276 				}
  1147 				break;
  1277 				break;
  1148 			case 'publish':
  1278 			case 'publish':
  1149 			case 'future':
  1279 			case 'future':
  1150 				if ( ! current_user_can( $post_type->cap->publish_posts ) ) {
  1280 				if ( ! current_user_can( $post_type->cap->publish_posts ) ) {
  1151 					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() ) );
  1281 					return new WP_Error(
       
  1282 						'rest_cannot_publish',
       
  1283 						__( 'Sorry, you are not allowed to publish posts in this post type.' ),
       
  1284 						array( 'status' => rest_authorization_required_code() )
       
  1285 					);
  1152 				}
  1286 				}
  1153 				break;
  1287 				break;
  1154 			default:
  1288 			default:
  1155 				if ( ! get_post_status_object( $post_status ) ) {
  1289 				if ( ! get_post_status_object( $post_status ) ) {
  1156 					$post_status = 'draft';
  1290 					$post_status = 'draft';
  1176 		if ( $featured_media ) {
  1310 		if ( $featured_media ) {
  1177 			$result = set_post_thumbnail( $post_id, $featured_media );
  1311 			$result = set_post_thumbnail( $post_id, $featured_media );
  1178 			if ( $result ) {
  1312 			if ( $result ) {
  1179 				return true;
  1313 				return true;
  1180 			} else {
  1314 			} else {
  1181 				return new WP_Error( 'rest_invalid_featured_media', __( 'Invalid featured media ID.' ), array( 'status' => 400 ) );
  1315 				return new WP_Error(
       
  1316 					'rest_invalid_featured_media',
       
  1317 					__( 'Invalid featured media ID.' ),
       
  1318 					array( 'status' => 400 )
       
  1319 				);
  1182 			}
  1320 			}
  1183 		} else {
  1321 		} else {
  1184 			return delete_post_thumbnail( $post_id );
  1322 			return delete_post_thumbnail( $post_id );
  1185 		}
  1323 		}
  1186 
  1324 
  1217 
  1355 
  1218 		if ( isset( $allowed_templates[ $template ] ) ) {
  1356 		if ( isset( $allowed_templates[ $template ] ) ) {
  1219 			return true;
  1357 			return true;
  1220 		}
  1358 		}
  1221 
  1359 
  1222 		/* translators: 1: parameter, 2: list of valid values */
  1360 		return new WP_Error(
  1223 		return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s is not one of %2$s.' ), 'template', implode( ', ', array_keys( $allowed_templates ) ) ) );
  1361 			'rest_invalid_param',
       
  1362 			/* translators: 1: Parameter, 2: List of valid values. */
       
  1363 			sprintf( __( '%1$s is not one of %2$s.' ), 'template', implode( ', ', array_keys( $allowed_templates ) ) )
       
  1364 		);
  1224 	}
  1365 	}
  1225 
  1366 
  1226 	/**
  1367 	/**
  1227 	 * Sets the template for a post.
  1368 	 * Sets the template for a post.
  1228 	 *
  1369 	 *
  1304 	/**
  1445 	/**
  1305 	 * Checks if a given post type can be viewed or managed.
  1446 	 * Checks if a given post type can be viewed or managed.
  1306 	 *
  1447 	 *
  1307 	 * @since 4.7.0
  1448 	 * @since 4.7.0
  1308 	 *
  1449 	 *
  1309 	 * @param object|string $post_type Post type name or object.
  1450 	 * @param WP_Post_Type|string $post_type Post type name or object.
  1310 	 * @return bool Whether the post type is allowed in REST.
  1451 	 * @return bool Whether the post type is allowed in REST.
  1311 	 */
  1452 	 */
  1312 	protected function check_is_post_type_allowed( $post_type ) {
  1453 	protected function check_is_post_type_allowed( $post_type ) {
  1313 		if ( ! is_object( $post_type ) ) {
  1454 		if ( ! is_object( $post_type ) ) {
  1314 			$post_type = get_post_type_object( $post_type );
  1455 			$post_type = get_post_type_object( $post_type );
  1326 	 *
  1467 	 *
  1327 	 * Correctly handles posts with the inherit status.
  1468 	 * Correctly handles posts with the inherit status.
  1328 	 *
  1469 	 *
  1329 	 * @since 4.7.0
  1470 	 * @since 4.7.0
  1330 	 *
  1471 	 *
  1331 	 * @param object $post Post object.
  1472 	 * @param WP_Post $post Post object.
  1332 	 * @return bool Whether the post can be read.
  1473 	 * @return bool Whether the post can be read.
  1333 	 */
  1474 	 */
  1334 	public function check_read_permission( $post ) {
  1475 	public function check_read_permission( $post ) {
  1335 		$post_type = get_post_type_object( $post->post_type );
  1476 		$post_type = get_post_type_object( $post->post_type );
  1336 		if ( ! $this->check_is_post_type_allowed( $post_type ) ) {
  1477 		if ( ! $this->check_is_post_type_allowed( $post_type ) ) {
  1337 			return false;
  1478 			return false;
  1338 		}
  1479 		}
  1339 
  1480 
  1340 		// Is the post readable?
  1481 		// Is the post readable?
  1341 		if ( 'publish' === $post->post_status || current_user_can( $post_type->cap->read_post, $post->ID ) ) {
  1482 		if ( 'publish' === $post->post_status || current_user_can( 'read_post', $post->ID ) ) {
  1342 			return true;
  1483 			return true;
  1343 		}
  1484 		}
  1344 
  1485 
  1345 		$post_status_obj = get_post_status_object( $post->post_status );
  1486 		$post_status_obj = get_post_status_object( $post->post_status );
  1346 		if ( $post_status_obj && $post_status_obj->public ) {
  1487 		if ( $post_status_obj && $post_status_obj->public ) {
  1369 	/**
  1510 	/**
  1370 	 * Checks if a post can be edited.
  1511 	 * Checks if a post can be edited.
  1371 	 *
  1512 	 *
  1372 	 * @since 4.7.0
  1513 	 * @since 4.7.0
  1373 	 *
  1514 	 *
  1374 	 * @param object $post Post object.
  1515 	 * @param WP_Post $post Post object.
  1375 	 * @return bool Whether the post can be edited.
  1516 	 * @return bool Whether the post can be edited.
  1376 	 */
  1517 	 */
  1377 	protected function check_update_permission( $post ) {
  1518 	protected function check_update_permission( $post ) {
  1378 		$post_type = get_post_type_object( $post->post_type );
  1519 		$post_type = get_post_type_object( $post->post_type );
  1379 
  1520 
  1380 		if ( ! $this->check_is_post_type_allowed( $post_type ) ) {
  1521 		if ( ! $this->check_is_post_type_allowed( $post_type ) ) {
  1381 			return false;
  1522 			return false;
  1382 		}
  1523 		}
  1383 
  1524 
  1384 		return current_user_can( $post_type->cap->edit_post, $post->ID );
  1525 		return current_user_can( 'edit_post', $post->ID );
  1385 	}
  1526 	}
  1386 
  1527 
  1387 	/**
  1528 	/**
  1388 	 * Checks if a post can be created.
  1529 	 * Checks if a post can be created.
  1389 	 *
  1530 	 *
  1390 	 * @since 4.7.0
  1531 	 * @since 4.7.0
  1391 	 *
  1532 	 *
  1392 	 * @param object $post Post object.
  1533 	 * @param WP_Post $post Post object.
  1393 	 * @return bool Whether the post can be created.
  1534 	 * @return bool Whether the post can be created.
  1394 	 */
  1535 	 */
  1395 	protected function check_create_permission( $post ) {
  1536 	protected function check_create_permission( $post ) {
  1396 		$post_type = get_post_type_object( $post->post_type );
  1537 		$post_type = get_post_type_object( $post->post_type );
  1397 
  1538 
  1405 	/**
  1546 	/**
  1406 	 * Checks if a post can be deleted.
  1547 	 * Checks if a post can be deleted.
  1407 	 *
  1548 	 *
  1408 	 * @since 4.7.0
  1549 	 * @since 4.7.0
  1409 	 *
  1550 	 *
  1410 	 * @param object $post Post object.
  1551 	 * @param WP_Post $post Post object.
  1411 	 * @return bool Whether the post can be deleted.
  1552 	 * @return bool Whether the post can be deleted.
  1412 	 */
  1553 	 */
  1413 	protected function check_delete_permission( $post ) {
  1554 	protected function check_delete_permission( $post ) {
  1414 		$post_type = get_post_type_object( $post->post_type );
  1555 		$post_type = get_post_type_object( $post->post_type );
  1415 
  1556 
  1416 		if ( ! $this->check_is_post_type_allowed( $post_type ) ) {
  1557 		if ( ! $this->check_is_post_type_allowed( $post_type ) ) {
  1417 			return false;
  1558 			return false;
  1418 		}
  1559 		}
  1419 
  1560 
  1420 		return current_user_can( $post_type->cap->delete_post, $post->ID );
  1561 		return current_user_can( 'delete_post', $post->ID );
  1421 	}
  1562 	}
  1422 
  1563 
  1423 	/**
  1564 	/**
  1424 	 * Prepares a single post output for response.
  1565 	 * Prepares a single post output for response.
  1425 	 *
  1566 	 *
  1437 		$fields = $this->get_fields_for_response( $request );
  1578 		$fields = $this->get_fields_for_response( $request );
  1438 
  1579 
  1439 		// Base fields for every post.
  1580 		// Base fields for every post.
  1440 		$data = array();
  1581 		$data = array();
  1441 
  1582 
  1442 		if ( in_array( 'id', $fields, true ) ) {
  1583 		if ( rest_is_field_included( 'id', $fields ) ) {
  1443 			$data['id'] = $post->ID;
  1584 			$data['id'] = $post->ID;
  1444 		}
  1585 		}
  1445 
  1586 
  1446 		if ( in_array( 'date', $fields, true ) ) {
  1587 		if ( rest_is_field_included( 'date', $fields ) ) {
  1447 			$data['date'] = $this->prepare_date_response( $post->post_date_gmt, $post->post_date );
  1588 			$data['date'] = $this->prepare_date_response( $post->post_date_gmt, $post->post_date );
  1448 		}
  1589 		}
  1449 
  1590 
  1450 		if ( in_array( 'date_gmt', $fields, true ) ) {
  1591 		if ( rest_is_field_included( 'date_gmt', $fields ) ) {
  1451 			// For drafts, `post_date_gmt` may not be set, indicating that the
  1592 			/*
  1452 			// date of the draft should be updated each time it is saved (see
  1593 			 * For drafts, `post_date_gmt` may not be set, indicating that the date
  1453 			// #38883).  In this case, shim the value based on the `post_date`
  1594 			 * of the draft should be updated each time it is saved (see #38883).
  1454 			// field with the site's timezone offset applied.
  1595 			 * In this case, shim the value based on the `post_date` field
       
  1596 			 * with the site's timezone offset applied.
       
  1597 			 */
  1455 			if ( '0000-00-00 00:00:00' === $post->post_date_gmt ) {
  1598 			if ( '0000-00-00 00:00:00' === $post->post_date_gmt ) {
  1456 				$post_date_gmt = get_gmt_from_date( $post->post_date );
  1599 				$post_date_gmt = get_gmt_from_date( $post->post_date );
  1457 			} else {
  1600 			} else {
  1458 				$post_date_gmt = $post->post_date_gmt;
  1601 				$post_date_gmt = $post->post_date_gmt;
  1459 			}
  1602 			}
  1460 			$data['date_gmt'] = $this->prepare_date_response( $post_date_gmt );
  1603 			$data['date_gmt'] = $this->prepare_date_response( $post_date_gmt );
  1461 		}
  1604 		}
  1462 
  1605 
  1463 		if ( in_array( 'guid', $fields, true ) ) {
  1606 		if ( rest_is_field_included( 'guid', $fields ) ) {
  1464 			$data['guid'] = array(
  1607 			$data['guid'] = array(
  1465 				/** This filter is documented in wp-includes/post-template.php */
  1608 				/** This filter is documented in wp-includes/post-template.php */
  1466 				'rendered' => apply_filters( 'get_the_guid', $post->guid, $post->ID ),
  1609 				'rendered' => apply_filters( 'get_the_guid', $post->guid, $post->ID ),
  1467 				'raw'      => $post->guid,
  1610 				'raw'      => $post->guid,
  1468 			);
  1611 			);
  1469 		}
  1612 		}
  1470 
  1613 
  1471 		if ( in_array( 'modified', $fields, true ) ) {
  1614 		if ( rest_is_field_included( 'modified', $fields ) ) {
  1472 			$data['modified'] = $this->prepare_date_response( $post->post_modified_gmt, $post->post_modified );
  1615 			$data['modified'] = $this->prepare_date_response( $post->post_modified_gmt, $post->post_modified );
  1473 		}
  1616 		}
  1474 
  1617 
  1475 		if ( in_array( 'modified_gmt', $fields, true ) ) {
  1618 		if ( rest_is_field_included( 'modified_gmt', $fields ) ) {
  1476 			// For drafts, `post_modified_gmt` may not be set (see
  1619 			/*
  1477 			// `post_date_gmt` comments above).  In this case, shim the value
  1620 			 * For drafts, `post_modified_gmt` may not be set (see `post_date_gmt` comments
  1478 			// based on the `post_modified` field with the site's timezone
  1621 			 * above). In this case, shim the value based on the `post_modified` field
  1479 			// offset applied.
  1622 			 * with the site's timezone offset applied.
       
  1623 			 */
  1480 			if ( '0000-00-00 00:00:00' === $post->post_modified_gmt ) {
  1624 			if ( '0000-00-00 00:00:00' === $post->post_modified_gmt ) {
  1481 				$post_modified_gmt = date( 'Y-m-d H:i:s', strtotime( $post->post_modified ) - ( get_option( 'gmt_offset' ) * 3600 ) );
  1625 				$post_modified_gmt = gmdate( 'Y-m-d H:i:s', strtotime( $post->post_modified ) - ( get_option( 'gmt_offset' ) * 3600 ) );
  1482 			} else {
  1626 			} else {
  1483 				$post_modified_gmt = $post->post_modified_gmt;
  1627 				$post_modified_gmt = $post->post_modified_gmt;
  1484 			}
  1628 			}
  1485 			$data['modified_gmt'] = $this->prepare_date_response( $post_modified_gmt );
  1629 			$data['modified_gmt'] = $this->prepare_date_response( $post_modified_gmt );
  1486 		}
  1630 		}
  1487 
  1631 
  1488 		if ( in_array( 'password', $fields, true ) ) {
  1632 		if ( rest_is_field_included( 'password', $fields ) ) {
  1489 			$data['password'] = $post->post_password;
  1633 			$data['password'] = $post->post_password;
  1490 		}
  1634 		}
  1491 
  1635 
  1492 		if ( in_array( 'slug', $fields, true ) ) {
  1636 		if ( rest_is_field_included( 'slug', $fields ) ) {
  1493 			$data['slug'] = $post->post_name;
  1637 			$data['slug'] = $post->post_name;
  1494 		}
  1638 		}
  1495 
  1639 
  1496 		if ( in_array( 'status', $fields, true ) ) {
  1640 		if ( rest_is_field_included( 'status', $fields ) ) {
  1497 			$data['status'] = $post->post_status;
  1641 			$data['status'] = $post->post_status;
  1498 		}
  1642 		}
  1499 
  1643 
  1500 		if ( in_array( 'type', $fields, true ) ) {
  1644 		if ( rest_is_field_included( 'type', $fields ) ) {
  1501 			$data['type'] = $post->post_type;
  1645 			$data['type'] = $post->post_type;
  1502 		}
  1646 		}
  1503 
  1647 
  1504 		if ( in_array( 'link', $fields, true ) ) {
  1648 		if ( rest_is_field_included( 'link', $fields ) ) {
  1505 			$data['link'] = get_permalink( $post->ID );
  1649 			$data['link'] = get_permalink( $post->ID );
  1506 		}
  1650 		}
  1507 
  1651 
  1508 		if ( in_array( 'title', $fields, true ) ) {
  1652 		if ( rest_is_field_included( 'title', $fields ) ) {
       
  1653 			$data['title'] = array();
       
  1654 		}
       
  1655 		if ( rest_is_field_included( 'title.raw', $fields ) ) {
       
  1656 			$data['title']['raw'] = $post->post_title;
       
  1657 		}
       
  1658 		if ( rest_is_field_included( 'title.rendered', $fields ) ) {
  1509 			add_filter( 'protected_title_format', array( $this, 'protected_title_format' ) );
  1659 			add_filter( 'protected_title_format', array( $this, 'protected_title_format' ) );
  1510 
  1660 
  1511 			$data['title'] = array(
  1661 			$data['title']['rendered'] = get_the_title( $post->ID );
  1512 				'raw'      => $post->post_title,
       
  1513 				'rendered' => get_the_title( $post->ID ),
       
  1514 			);
       
  1515 
  1662 
  1516 			remove_filter( 'protected_title_format', array( $this, 'protected_title_format' ) );
  1663 			remove_filter( 'protected_title_format', array( $this, 'protected_title_format' ) );
  1517 		}
  1664 		}
  1518 
  1665 
  1519 		$has_password_filter = false;
  1666 		$has_password_filter = false;
  1523 			add_filter( 'post_password_required', '__return_false' );
  1670 			add_filter( 'post_password_required', '__return_false' );
  1524 
  1671 
  1525 			$has_password_filter = true;
  1672 			$has_password_filter = true;
  1526 		}
  1673 		}
  1527 
  1674 
  1528 		if ( in_array( 'content', $fields, true ) ) {
  1675 		if ( rest_is_field_included( 'content', $fields ) ) {
  1529 			$data['content'] = array(
  1676 			$data['content'] = array();
  1530 				'raw'           => $post->post_content,
  1677 		}
  1531 				/** This filter is documented in wp-includes/post-template.php */
  1678 		if ( rest_is_field_included( 'content.raw', $fields ) ) {
  1532 				'rendered'      => post_password_required( $post ) ? '' : apply_filters( 'the_content', $post->post_content ),
  1679 			$data['content']['raw'] = $post->post_content;
  1533 				'protected'     => (bool) $post->post_password,
  1680 		}
  1534 				'block_version' => block_version( $post->post_content ),
  1681 		if ( rest_is_field_included( 'content.rendered', $fields ) ) {
  1535 			);
       
  1536 		}
       
  1537 
       
  1538 		if ( in_array( 'excerpt', $fields, true ) ) {
       
  1539 			/** This filter is documented in wp-includes/post-template.php */
  1682 			/** This filter is documented in wp-includes/post-template.php */
  1540 			$excerpt         = apply_filters( 'the_excerpt', apply_filters( 'get_the_excerpt', $post->post_excerpt, $post ) );
  1683 			$data['content']['rendered'] = post_password_required( $post ) ? '' : apply_filters( 'the_content', $post->post_content );
       
  1684 		}
       
  1685 		if ( rest_is_field_included( 'content.protected', $fields ) ) {
       
  1686 			$data['content']['protected'] = (bool) $post->post_password;
       
  1687 		}
       
  1688 		if ( rest_is_field_included( 'content.block_version', $fields ) ) {
       
  1689 			$data['content']['block_version'] = block_version( $post->post_content );
       
  1690 		}
       
  1691 
       
  1692 		if ( rest_is_field_included( 'excerpt', $fields ) ) {
       
  1693 			/** This filter is documented in wp-includes/post-template.php */
       
  1694 			$excerpt = apply_filters( 'get_the_excerpt', $post->post_excerpt, $post );
       
  1695 
       
  1696 			/** This filter is documented in wp-includes/post-template.php */
       
  1697 			$excerpt = apply_filters( 'the_excerpt', $excerpt );
       
  1698 
  1541 			$data['excerpt'] = array(
  1699 			$data['excerpt'] = array(
  1542 				'raw'       => $post->post_excerpt,
  1700 				'raw'       => $post->post_excerpt,
  1543 				'rendered'  => post_password_required( $post ) ? '' : $excerpt,
  1701 				'rendered'  => post_password_required( $post ) ? '' : $excerpt,
  1544 				'protected' => (bool) $post->post_password,
  1702 				'protected' => (bool) $post->post_password,
  1545 			);
  1703 			);
  1548 		if ( $has_password_filter ) {
  1706 		if ( $has_password_filter ) {
  1549 			// Reset filter.
  1707 			// Reset filter.
  1550 			remove_filter( 'post_password_required', '__return_false' );
  1708 			remove_filter( 'post_password_required', '__return_false' );
  1551 		}
  1709 		}
  1552 
  1710 
  1553 		if ( in_array( 'author', $fields, true ) ) {
  1711 		if ( rest_is_field_included( 'author', $fields ) ) {
  1554 			$data['author'] = (int) $post->post_author;
  1712 			$data['author'] = (int) $post->post_author;
  1555 		}
  1713 		}
  1556 
  1714 
  1557 		if ( in_array( 'featured_media', $fields, true ) ) {
  1715 		if ( rest_is_field_included( 'featured_media', $fields ) ) {
  1558 			$data['featured_media'] = (int) get_post_thumbnail_id( $post->ID );
  1716 			$data['featured_media'] = (int) get_post_thumbnail_id( $post->ID );
  1559 		}
  1717 		}
  1560 
  1718 
  1561 		if ( in_array( 'parent', $fields, true ) ) {
  1719 		if ( rest_is_field_included( 'parent', $fields ) ) {
  1562 			$data['parent'] = (int) $post->post_parent;
  1720 			$data['parent'] = (int) $post->post_parent;
  1563 		}
  1721 		}
  1564 
  1722 
  1565 		if ( in_array( 'menu_order', $fields, true ) ) {
  1723 		if ( rest_is_field_included( 'menu_order', $fields ) ) {
  1566 			$data['menu_order'] = (int) $post->menu_order;
  1724 			$data['menu_order'] = (int) $post->menu_order;
  1567 		}
  1725 		}
  1568 
  1726 
  1569 		if ( in_array( 'comment_status', $fields, true ) ) {
  1727 		if ( rest_is_field_included( 'comment_status', $fields ) ) {
  1570 			$data['comment_status'] = $post->comment_status;
  1728 			$data['comment_status'] = $post->comment_status;
  1571 		}
  1729 		}
  1572 
  1730 
  1573 		if ( in_array( 'ping_status', $fields, true ) ) {
  1731 		if ( rest_is_field_included( 'ping_status', $fields ) ) {
  1574 			$data['ping_status'] = $post->ping_status;
  1732 			$data['ping_status'] = $post->ping_status;
  1575 		}
  1733 		}
  1576 
  1734 
  1577 		if ( in_array( 'sticky', $fields, true ) ) {
  1735 		if ( rest_is_field_included( 'sticky', $fields ) ) {
  1578 			$data['sticky'] = is_sticky( $post->ID );
  1736 			$data['sticky'] = is_sticky( $post->ID );
  1579 		}
  1737 		}
  1580 
  1738 
  1581 		if ( in_array( 'template', $fields, true ) ) {
  1739 		if ( rest_is_field_included( 'template', $fields ) ) {
  1582 			if ( $template = get_page_template_slug( $post->ID ) ) {
  1740 			$template = get_page_template_slug( $post->ID );
       
  1741 			if ( $template ) {
  1583 				$data['template'] = $template;
  1742 				$data['template'] = $template;
  1584 			} else {
  1743 			} else {
  1585 				$data['template'] = '';
  1744 				$data['template'] = '';
  1586 			}
  1745 			}
  1587 		}
  1746 		}
  1588 
  1747 
  1589 		if ( in_array( 'format', $fields, true ) ) {
  1748 		if ( rest_is_field_included( 'format', $fields ) ) {
  1590 			$data['format'] = get_post_format( $post->ID );
  1749 			$data['format'] = get_post_format( $post->ID );
  1591 
  1750 
  1592 			// Fill in blank post format.
  1751 			// Fill in blank post format.
  1593 			if ( empty( $data['format'] ) ) {
  1752 			if ( empty( $data['format'] ) ) {
  1594 				$data['format'] = 'standard';
  1753 				$data['format'] = 'standard';
  1595 			}
  1754 			}
  1596 		}
  1755 		}
  1597 
  1756 
  1598 		if ( in_array( 'meta', $fields, true ) ) {
  1757 		if ( rest_is_field_included( 'meta', $fields ) ) {
  1599 			$data['meta'] = $this->meta->get_value( $post->ID, $request );
  1758 			$data['meta'] = $this->meta->get_value( $post->ID, $request );
  1600 		}
  1759 		}
  1601 
  1760 
  1602 		$taxonomies = wp_list_filter( get_object_taxonomies( $this->post_type, 'objects' ), array( 'show_in_rest' => true ) );
  1761 		$taxonomies = wp_list_filter( get_object_taxonomies( $this->post_type, 'objects' ), array( 'show_in_rest' => true ) );
  1603 
  1762 
  1604 		foreach ( $taxonomies as $taxonomy ) {
  1763 		foreach ( $taxonomies as $taxonomy ) {
  1605 			$base = ! empty( $taxonomy->rest_base ) ? $taxonomy->rest_base : $taxonomy->name;
  1764 			$base = ! empty( $taxonomy->rest_base ) ? $taxonomy->rest_base : $taxonomy->name;
  1606 
  1765 
  1607 			if ( in_array( $base, $fields, true ) ) {
  1766 			if ( rest_is_field_included( $base, $fields ) ) {
  1608 				$terms         = get_the_terms( $post, $taxonomy->name );
  1767 				$terms         = get_the_terms( $post, $taxonomy->name );
  1609 				$data[ $base ] = $terms ? array_values( wp_list_pluck( $terms, 'term_id' ) ) : array();
  1768 				$data[ $base ] = $terms ? array_values( wp_list_pluck( $terms, 'term_id' ) ) : array();
  1610 			}
  1769 			}
  1611 		}
  1770 		}
  1612 
  1771 
  1613 		$post_type_obj = get_post_type_object( $post->post_type );
  1772 		$post_type_obj = get_post_type_object( $post->post_type );
  1614 		if ( is_post_type_viewable( $post_type_obj ) && $post_type_obj->public ) {
  1773 		if ( is_post_type_viewable( $post_type_obj ) && $post_type_obj->public ) {
  1615 
  1774 			$permalink_template_requested = rest_is_field_included( 'permalink_template', $fields );
  1616 			if ( ! function_exists( 'get_sample_permalink' ) ) {
  1775 			$generated_slug_requested     = rest_is_field_included( 'generated_slug', $fields );
  1617 				require_once ABSPATH . 'wp-admin/includes/post.php';
  1776 
  1618 			}
  1777 			if ( $permalink_template_requested || $generated_slug_requested ) {
  1619 
  1778 				if ( ! function_exists( 'get_sample_permalink' ) ) {
  1620 			$sample_permalink = get_sample_permalink( $post->ID, $post->post_title, '' );
  1779 					require_once ABSPATH . 'wp-admin/includes/post.php';
  1621 
  1780 				}
  1622 			if ( in_array( 'permalink_template', $fields, true ) ) {
  1781 
  1623 				$data['permalink_template'] = $sample_permalink[0];
  1782 				$sample_permalink = get_sample_permalink( $post->ID, $post->post_title, '' );
  1624 			}
  1783 
  1625 			if ( in_array( 'generated_slug', $fields, true ) ) {
  1784 				if ( $permalink_template_requested ) {
  1626 				$data['generated_slug'] = $sample_permalink[1];
  1785 					$data['permalink_template'] = $sample_permalink[0];
       
  1786 				}
       
  1787 
       
  1788 				if ( $generated_slug_requested ) {
       
  1789 					$data['generated_slug'] = $sample_permalink[1];
       
  1790 				}
  1627 			}
  1791 			}
  1628 		}
  1792 		}
  1629 
  1793 
  1630 		$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
  1794 		$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
  1631 		$data    = $this->add_additional_fields_to_object( $data, $request );
  1795 		$data    = $this->add_additional_fields_to_object( $data, $request );
  1745 				'embeddable' => true,
  1909 				'embeddable' => true,
  1746 			);
  1910 			);
  1747 		}
  1911 		}
  1748 
  1912 
  1749 		// If we have a featured media, add that.
  1913 		// If we have a featured media, add that.
  1750 		if ( $featured_media = get_post_thumbnail_id( $post->ID ) ) {
  1914 		$featured_media = get_post_thumbnail_id( $post->ID );
       
  1915 		if ( $featured_media ) {
  1751 			$image_url = rest_url( 'wp/v2/media/' . $featured_media );
  1916 			$image_url = rest_url( 'wp/v2/media/' . $featured_media );
  1752 
  1917 
  1753 			$links['https://api.w.org/featuredmedia'] = array(
  1918 			$links['https://api.w.org/featuredmedia'] = array(
  1754 				'href'       => $image_url,
  1919 				'href'       => $image_url,
  1755 				'embeddable' => true,
  1920 				'embeddable' => true,
  1800 	/**
  1965 	/**
  1801 	 * Get the link relations available for the post and current user.
  1966 	 * Get the link relations available for the post and current user.
  1802 	 *
  1967 	 *
  1803 	 * @since 4.9.8
  1968 	 * @since 4.9.8
  1804 	 *
  1969 	 *
  1805 	 * @param WP_Post $post Post object.
  1970 	 * @param WP_Post         $post    Post object.
  1806 	 * @param WP_REST_Request Request object.
  1971 	 * @param WP_REST_Request $request Request object.
  1807 	 *
       
  1808 	 * @return array List of link relations.
  1972 	 * @return array List of link relations.
  1809 	 */
  1973 	 */
  1810 	protected function get_available_actions( $post, $request ) {
  1974 	protected function get_available_actions( $post, $request ) {
  1811 
  1975 
  1812 		if ( 'edit' !== $request['context'] ) {
  1976 		if ( 'edit' !== $request['context'] ) {
  1861 	 * @since 4.7.0
  2025 	 * @since 4.7.0
  1862 	 *
  2026 	 *
  1863 	 * @return array Item schema data.
  2027 	 * @return array Item schema data.
  1864 	 */
  2028 	 */
  1865 	public function get_item_schema() {
  2029 	public function get_item_schema() {
       
  2030 		if ( $this->schema ) {
       
  2031 			return $this->add_additional_fields_schema( $this->schema );
       
  2032 		}
  1866 
  2033 
  1867 		$schema = array(
  2034 		$schema = array(
  1868 			'$schema'    => 'http://json-schema.org/draft-04/schema#',
  2035 			'$schema'    => 'http://json-schema.org/draft-04/schema#',
  1869 			'title'      => $this->post_type,
  2036 			'title'      => $this->post_type,
  1870 			'type'       => 'object',
  2037 			'type'       => 'object',
  1871 			// Base properties for every Post.
  2038 			// Base properties for every Post.
  1872 			'properties' => array(
  2039 			'properties' => array(
  1873 				'date'         => array(
  2040 				'date'         => array(
  1874 					'description' => __( "The date the object was published, in the site's timezone." ),
  2041 					'description' => __( "The date the object was published, in the site's timezone." ),
  1875 					'type'        => 'string',
  2042 					'type'        => array( 'string', 'null' ),
  1876 					'format'      => 'date-time',
  2043 					'format'      => 'date-time',
  1877 					'context'     => array( 'view', 'edit', 'embed' ),
  2044 					'context'     => array( 'view', 'edit', 'embed' ),
  1878 				),
  2045 				),
  1879 				'date_gmt'     => array(
  2046 				'date_gmt'     => array(
  1880 					'description' => __( 'The date the object was published, as GMT.' ),
  2047 					'description' => __( 'The date the object was published, as GMT.' ),
  1881 					'type'        => 'string',
  2048 					'type'        => array( 'string', 'null' ),
  1882 					'format'      => 'date-time',
  2049 					'format'      => 'date-time',
  1883 					'context'     => array( 'view', 'edit' ),
  2050 					'context'     => array( 'view', 'edit' ),
  1884 				),
  2051 				),
  1885 				'guid'         => array(
  2052 				'guid'         => array(
  1886 					'description' => __( 'The globally unique identifier for the object.' ),
  2053 					'description' => __( 'The globally unique identifier for the object.' ),
  2023 				'comments',
  2190 				'comments',
  2024 				'revisions',
  2191 				'revisions',
  2025 				'custom-fields',
  2192 				'custom-fields',
  2026 			),
  2193 			),
  2027 		);
  2194 		);
       
  2195 
  2028 		foreach ( $post_type_attributes as $attribute ) {
  2196 		foreach ( $post_type_attributes as $attribute ) {
  2029 			if ( isset( $fixed_schemas[ $this->post_type ] ) && ! in_array( $attribute, $fixed_schemas[ $this->post_type ], true ) ) {
  2197 			if ( isset( $fixed_schemas[ $this->post_type ] ) && ! in_array( $attribute, $fixed_schemas[ $this->post_type ], true ) ) {
  2030 				continue;
  2198 				continue;
  2031 			} elseif ( ! isset( $fixed_schemas[ $this->post_type ] ) && ! post_type_supports( $this->post_type, $attribute ) ) {
  2199 			} elseif ( ! isset( $fixed_schemas[ $this->post_type ] ) && ! post_type_supports( $this->post_type, $attribute ) ) {
  2032 				continue;
  2200 				continue;
  2038 					$schema['properties']['title'] = array(
  2206 					$schema['properties']['title'] = array(
  2039 						'description' => __( 'The title for the object.' ),
  2207 						'description' => __( 'The title for the object.' ),
  2040 						'type'        => 'object',
  2208 						'type'        => 'object',
  2041 						'context'     => array( 'view', 'edit', 'embed' ),
  2209 						'context'     => array( 'view', 'edit', 'embed' ),
  2042 						'arg_options' => array(
  2210 						'arg_options' => array(
  2043 							'sanitize_callback' => null, // Note: sanitization implemented in self::prepare_item_for_database()
  2211 							'sanitize_callback' => null, // Note: sanitization implemented in self::prepare_item_for_database().
  2044 							'validate_callback' => null, // Note: validation implemented in self::prepare_item_for_database()
  2212 							'validate_callback' => null, // Note: validation implemented in self::prepare_item_for_database().
  2045 						),
  2213 						),
  2046 						'properties'  => array(
  2214 						'properties'  => array(
  2047 							'raw'      => array(
  2215 							'raw'      => array(
  2048 								'description' => __( 'Title for the object, as it exists in the database.' ),
  2216 								'description' => __( 'Title for the object, as it exists in the database.' ),
  2049 								'type'        => 'string',
  2217 								'type'        => 'string',
  2063 					$schema['properties']['content'] = array(
  2231 					$schema['properties']['content'] = array(
  2064 						'description' => __( 'The content for the object.' ),
  2232 						'description' => __( 'The content for the object.' ),
  2065 						'type'        => 'object',
  2233 						'type'        => 'object',
  2066 						'context'     => array( 'view', 'edit' ),
  2234 						'context'     => array( 'view', 'edit' ),
  2067 						'arg_options' => array(
  2235 						'arg_options' => array(
  2068 							'sanitize_callback' => null, // Note: sanitization implemented in self::prepare_item_for_database()
  2236 							'sanitize_callback' => null, // Note: sanitization implemented in self::prepare_item_for_database().
  2069 							'validate_callback' => null, // Note: validation implemented in self::prepare_item_for_database()
  2237 							'validate_callback' => null, // Note: validation implemented in self::prepare_item_for_database().
  2070 						),
  2238 						),
  2071 						'properties'  => array(
  2239 						'properties'  => array(
  2072 							'raw'           => array(
  2240 							'raw'           => array(
  2073 								'description' => __( 'Content for the object, as it exists in the database.' ),
  2241 								'description' => __( 'Content for the object, as it exists in the database.' ),
  2074 								'type'        => 'string',
  2242 								'type'        => 'string',
  2108 					$schema['properties']['excerpt'] = array(
  2276 					$schema['properties']['excerpt'] = array(
  2109 						'description' => __( 'The excerpt for the object.' ),
  2277 						'description' => __( 'The excerpt for the object.' ),
  2110 						'type'        => 'object',
  2278 						'type'        => 'object',
  2111 						'context'     => array( 'view', 'edit', 'embed' ),
  2279 						'context'     => array( 'view', 'edit', 'embed' ),
  2112 						'arg_options' => array(
  2280 						'arg_options' => array(
  2113 							'sanitize_callback' => null, // Note: sanitization implemented in self::prepare_item_for_database()
  2281 							'sanitize_callback' => null, // Note: sanitization implemented in self::prepare_item_for_database().
  2114 							'validate_callback' => null, // Note: validation implemented in self::prepare_item_for_database()
  2282 							'validate_callback' => null, // Note: validation implemented in self::prepare_item_for_database().
  2115 						),
  2283 						),
  2116 						'properties'  => array(
  2284 						'properties'  => array(
  2117 							'raw'       => array(
  2285 							'raw'       => array(
  2118 								'description' => __( 'Excerpt for the object, as it exists in the database.' ),
  2286 								'description' => __( 'Excerpt for the object, as it exists in the database.' ),
  2119 								'type'        => 'string',
  2287 								'type'        => 'string',
  2201 				'validate_callback' => array( $this, 'check_template' ),
  2369 				'validate_callback' => array( $this, 'check_template' ),
  2202 			),
  2370 			),
  2203 		);
  2371 		);
  2204 
  2372 
  2205 		$taxonomies = wp_list_filter( get_object_taxonomies( $this->post_type, 'objects' ), array( 'show_in_rest' => true ) );
  2373 		$taxonomies = wp_list_filter( get_object_taxonomies( $this->post_type, 'objects' ), array( 'show_in_rest' => true ) );
       
  2374 
  2206 		foreach ( $taxonomies as $taxonomy ) {
  2375 		foreach ( $taxonomies as $taxonomy ) {
  2207 			$base                          = ! empty( $taxonomy->rest_base ) ? $taxonomy->rest_base : $taxonomy->name;
  2376 			$base = ! empty( $taxonomy->rest_base ) ? $taxonomy->rest_base : $taxonomy->name;
       
  2377 
       
  2378 			if ( array_key_exists( $base, $schema['properties'] ) ) {
       
  2379 				$taxonomy_field_name_with_conflict = ! empty( $taxonomy->rest_base ) ? 'rest_base' : 'name';
       
  2380 				_doing_it_wrong(
       
  2381 					'register_taxonomy',
       
  2382 					sprintf(
       
  2383 						/* translators: 1. The taxonomy name, 2. The property name, either 'rest_base' or 'name', 3. The conflicting value. */
       
  2384 						__( 'The "%1$s" taxonomy "%2$s" property (%3$s) conflicts with an existing property on the REST API Posts Controller. Specify a custom "rest_base" when registering the taxonomy to avoid this error.' ),
       
  2385 						$taxonomy->name,
       
  2386 						$taxonomy_field_name_with_conflict,
       
  2387 						$base
       
  2388 					),
       
  2389 					'5.4.0'
       
  2390 				);
       
  2391 			}
       
  2392 
  2208 			$schema['properties'][ $base ] = array(
  2393 			$schema['properties'][ $base ] = array(
  2209 				/* translators: %s: taxonomy name */
  2394 				/* translators: %s: Taxonomy name. */
  2210 				'description' => sprintf( __( 'The terms assigned to the object in the %s taxonomy.' ), $taxonomy->name ),
  2395 				'description' => sprintf( __( 'The terms assigned to the object in the %s taxonomy.' ), $taxonomy->name ),
  2211 				'type'        => 'array',
  2396 				'type'        => 'array',
  2212 				'items'       => array(
  2397 				'items'       => array(
  2213 					'type' => 'integer',
  2398 					'type' => 'integer',
  2214 				),
  2399 				),
  2220 
  2405 
  2221 		if ( $schema_links ) {
  2406 		if ( $schema_links ) {
  2222 			$schema['links'] = $schema_links;
  2407 			$schema['links'] = $schema_links;
  2223 		}
  2408 		}
  2224 
  2409 
  2225 		return $this->add_additional_fields_schema( $schema );
  2410 		// Take a snapshot of which fields are in the schema pre-filtering.
       
  2411 		$schema_fields = array_keys( $schema['properties'] );
       
  2412 
       
  2413 		/**
       
  2414 		 * Filter the post's schema.
       
  2415 		 *
       
  2416 		 * The dynamic portion of the filter, `$this->post_type`, refers to the
       
  2417 		 * post type slug for the controller.
       
  2418 		 *
       
  2419 		 * @since 5.4.0
       
  2420 		 *
       
  2421 		 * @param array $schema Item schema data.
       
  2422 		 */
       
  2423 		$schema = apply_filters( "rest_{$this->post_type}_item_schema", $schema );
       
  2424 
       
  2425 		// Emit a _doing_it_wrong warning if user tries to add new properties using this filter.
       
  2426 		$new_fields = array_diff( array_keys( $schema['properties'] ), $schema_fields );
       
  2427 		if ( count( $new_fields ) > 0 ) {
       
  2428 			_doing_it_wrong(
       
  2429 				__METHOD__,
       
  2430 				sprintf(
       
  2431 					/* translators: %s: register_rest_field */
       
  2432 					__( 'Please use %s to add new schema properties.' ),
       
  2433 					'register_rest_field'
       
  2434 				),
       
  2435 				'5.4.0'
       
  2436 			);
       
  2437 		}
       
  2438 
       
  2439 		$this->schema = $schema;
       
  2440 
       
  2441 		return $this->add_additional_fields_schema( $this->schema );
  2226 	}
  2442 	}
  2227 
  2443 
  2228 	/**
  2444 	/**
  2229 	 * Retrieve Link Description Objects that should be added to the Schema for the posts collection.
  2445 	 * Retrieve Link Description Objects that should be added to the Schema for the posts collection.
  2230 	 *
  2446 	 *
  2306 		$taxonomies = wp_list_filter( get_object_taxonomies( $this->post_type, 'objects' ), array( 'show_in_rest' => true ) );
  2522 		$taxonomies = wp_list_filter( get_object_taxonomies( $this->post_type, 'objects' ), array( 'show_in_rest' => true ) );
  2307 
  2523 
  2308 		foreach ( $taxonomies as $tax ) {
  2524 		foreach ( $taxonomies as $tax ) {
  2309 			$tax_base = ! empty( $tax->rest_base ) ? $tax->rest_base : $tax->name;
  2525 			$tax_base = ! empty( $tax->rest_base ) ? $tax->rest_base : $tax->name;
  2310 
  2526 
  2311 			/* translators: %s: taxonomy name */
  2527 			/* translators: %s: Taxonomy name. */
  2312 			$assign_title = sprintf( __( 'The current user can assign terms in the %s taxonomy.' ), $tax->name );
  2528 			$assign_title = sprintf( __( 'The current user can assign terms in the %s taxonomy.' ), $tax->name );
  2313 			/* translators: %s: taxonomy name */
  2529 			/* translators: %s: Taxonomy name. */
  2314 			$create_title = sprintf( __( 'The current user can create terms in the %s taxonomy.' ), $tax->name );
  2530 			$create_title = sprintf( __( 'The current user can create terms in the %s taxonomy.' ), $tax->name );
  2315 
  2531 
  2316 			$links[] = array(
  2532 			$links[] = array(
  2317 				'rel'          => 'https://api.w.org/action-assign-' . $tax_base,
  2533 				'rel'          => 'https://api.w.org/action-assign-' . $tax_base,
  2318 				'title'        => $assign_title,
  2534 				'title'        => $assign_title,
  2494 			'sanitize_callback' => array( $this, 'sanitize_post_statuses' ),
  2710 			'sanitize_callback' => array( $this, 'sanitize_post_statuses' ),
  2495 		);
  2711 		);
  2496 
  2712 
  2497 		$taxonomies = wp_list_filter( get_object_taxonomies( $this->post_type, 'objects' ), array( 'show_in_rest' => true ) );
  2713 		$taxonomies = wp_list_filter( get_object_taxonomies( $this->post_type, 'objects' ), array( 'show_in_rest' => true ) );
  2498 
  2714 
       
  2715 		if ( ! empty( $taxonomies ) ) {
       
  2716 			$query_params['tax_relation'] = array(
       
  2717 				'description' => __( 'Limit result set based on relationship between multiple taxonomies.' ),
       
  2718 				'type'        => 'string',
       
  2719 				'enum'        => array( 'AND', 'OR' ),
       
  2720 			);
       
  2721 		}
       
  2722 
  2499 		foreach ( $taxonomies as $taxonomy ) {
  2723 		foreach ( $taxonomies as $taxonomy ) {
  2500 			$base = ! empty( $taxonomy->rest_base ) ? $taxonomy->rest_base : $taxonomy->name;
  2724 			$base = ! empty( $taxonomy->rest_base ) ? $taxonomy->rest_base : $taxonomy->name;
  2501 
  2725 
  2502 			$query_params[ $base ] = array(
  2726 			$query_params[ $base ] = array(
  2503 				/* translators: %s: taxonomy name */
  2727 				/* translators: %s: Taxonomy name. */
  2504 				'description' => sprintf( __( 'Limit result set to all items that have the specified term assigned in the %s taxonomy.' ), $base ),
  2728 				'description' => sprintf( __( 'Limit result set to all items that have the specified term assigned in the %s taxonomy.' ), $base ),
  2505 				'type'        => 'array',
  2729 				'type'        => 'array',
  2506 				'items'       => array(
  2730 				'items'       => array(
  2507 					'type' => 'integer',
  2731 					'type' => 'integer',
  2508 				),
  2732 				),
  2509 				'default'     => array(),
  2733 				'default'     => array(),
  2510 			);
  2734 			);
  2511 
  2735 
  2512 			$query_params[ $base . '_exclude' ] = array(
  2736 			$query_params[ $base . '_exclude' ] = array(
  2513 				/* translators: %s: taxonomy name */
  2737 				/* translators: %s: Taxonomy name. */
  2514 				'description' => sprintf( __( 'Limit result set to all items except those that have the specified term assigned in the %s taxonomy.' ), $base ),
  2738 				'description' => sprintf( __( 'Limit result set to all items except those that have the specified term assigned in the %s taxonomy.' ), $base ),
  2515 				'type'        => 'array',
  2739 				'type'        => 'array',
  2516 				'items'       => array(
  2740 				'items'       => array(
  2517 					'type' => 'integer',
  2741 					'type' => 'integer',
  2518 				),
  2742 				),
  2549 	 * Sanitizes and validates the list of post statuses, including whether the
  2773 	 * Sanitizes and validates the list of post statuses, including whether the
  2550 	 * user can query private statuses.
  2774 	 * user can query private statuses.
  2551 	 *
  2775 	 *
  2552 	 * @since 4.7.0
  2776 	 * @since 4.7.0
  2553 	 *
  2777 	 *
  2554 	 * @param  string|array    $statuses  One or more post statuses.
  2778 	 * @param string|array    $statuses  One or more post statuses.
  2555 	 * @param  WP_REST_Request $request   Full details about the request.
  2779 	 * @param WP_REST_Request $request   Full details about the request.
  2556 	 * @param  string          $parameter Additional parameter to pass to validation.
  2780 	 * @param string          $parameter Additional parameter to pass to validation.
  2557 	 * @return array|WP_Error A list of valid statuses, otherwise WP_Error object.
  2781 	 * @return array|WP_Error A list of valid statuses, otherwise WP_Error object.
  2558 	 */
  2782 	 */
  2559 	public function sanitize_post_statuses( $statuses, $request, $parameter ) {
  2783 	public function sanitize_post_statuses( $statuses, $request, $parameter ) {
  2560 		$statuses = wp_parse_slug_list( $statuses );
  2784 		$statuses = wp_parse_slug_list( $statuses );
  2561 
  2785 
  2562 		// The default status is different in WP_REST_Attachments_Controller
  2786 		// The default status is different in WP_REST_Attachments_Controller.
  2563 		$attributes     = $request->get_attributes();
  2787 		$attributes     = $request->get_attributes();
  2564 		$default_status = $attributes['args']['status']['default'];
  2788 		$default_status = $attributes['args']['status']['default'];
  2565 
  2789 
  2566 		foreach ( $statuses as $status ) {
  2790 		foreach ( $statuses as $status ) {
  2567 			if ( $status === $default_status ) {
  2791 			if ( $status === $default_status ) {
  2574 				$result = rest_validate_request_arg( $status, $request, $parameter );
  2798 				$result = rest_validate_request_arg( $status, $request, $parameter );
  2575 				if ( is_wp_error( $result ) ) {
  2799 				if ( is_wp_error( $result ) ) {
  2576 					return $result;
  2800 					return $result;
  2577 				}
  2801 				}
  2578 			} else {
  2802 			} else {
  2579 				return new WP_Error( 'rest_forbidden_status', __( 'Status is forbidden.' ), array( 'status' => rest_authorization_required_code() ) );
  2803 				return new WP_Error(
       
  2804 					'rest_forbidden_status',
       
  2805 					__( 'Status is forbidden.' ),
       
  2806 					array( 'status' => rest_authorization_required_code() )
       
  2807 				);
  2580 			}
  2808 			}
  2581 		}
  2809 		}
  2582 
  2810 
  2583 		return $statuses;
  2811 		return $statuses;
  2584 	}
  2812 	}