wp/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php
changeset 9 177826044cd9
parent 7 cf61fcea0001
child 16 a86126ab1dd4
equal deleted inserted replaced
8:c7c34916027a 9:177826044cd9
    40 	 * @param string $post_type Post type.
    40 	 * @param string $post_type Post type.
    41 	 */
    41 	 */
    42 	public function __construct( $post_type ) {
    42 	public function __construct( $post_type ) {
    43 		$this->post_type = $post_type;
    43 		$this->post_type = $post_type;
    44 		$this->namespace = 'wp/v2';
    44 		$this->namespace = 'wp/v2';
    45 		$obj = get_post_type_object( $post_type );
    45 		$obj             = get_post_type_object( $post_type );
    46 		$this->rest_base = ! empty( $obj->rest_base ) ? $obj->rest_base : $obj->name;
    46 		$this->rest_base = ! empty( $obj->rest_base ) ? $obj->rest_base : $obj->name;
    47 
    47 
    48 		$this->meta = new WP_REST_Post_Meta_Fields( $this->post_type );
    48 		$this->meta = new WP_REST_Post_Meta_Fields( $this->post_type );
    49 	}
    49 	}
    50 
    50 
    55 	 *
    55 	 *
    56 	 * @see register_rest_route()
    56 	 * @see register_rest_route()
    57 	 */
    57 	 */
    58 	public function register_routes() {
    58 	public function register_routes() {
    59 
    59 
    60 		register_rest_route( $this->namespace, '/' . $this->rest_base, array(
    60 		register_rest_route(
       
    61 			$this->namespace,
       
    62 			'/' . $this->rest_base,
    61 			array(
    63 			array(
    62 				'methods'             => WP_REST_Server::READABLE,
    64 				array(
    63 				'callback'            => array( $this, 'get_items' ),
    65 					'methods'             => WP_REST_Server::READABLE,
    64 				'permission_callback' => array( $this, 'get_items_permissions_check' ),
    66 					'callback'            => array( $this, 'get_items' ),
    65 				'args'                => $this->get_collection_params(),
    67 					'permission_callback' => array( $this, 'get_items_permissions_check' ),
    66 			),
    68 					'args'                => $this->get_collection_params(),
    67 			array(
    69 				),
    68 				'methods'             => WP_REST_Server::CREATABLE,
    70 				array(
    69 				'callback'            => array( $this, 'create_item' ),
    71 					'methods'             => WP_REST_Server::CREATABLE,
    70 				'permission_callback' => array( $this, 'create_item_permissions_check' ),
    72 					'callback'            => array( $this, 'create_item' ),
    71 				'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ),
    73 					'permission_callback' => array( $this, 'create_item_permissions_check' ),
    72 			),
    74 					'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ),
    73 			'schema' => array( $this, 'get_public_item_schema' ),
    75 				),
    74 		) );
    76 				'schema' => array( $this, 'get_public_item_schema' ),
    75 
    77 			)
    76 		$schema = $this->get_item_schema();
    78 		);
       
    79 
       
    80 		$schema        = $this->get_item_schema();
    77 		$get_item_args = array(
    81 		$get_item_args = array(
    78 			'context'  => $this->get_context_param( array( 'default' => 'view' ) ),
    82 			'context' => $this->get_context_param( array( 'default' => 'view' ) ),
    79 		);
    83 		);
    80 		if ( isset( $schema['properties']['password'] ) ) {
    84 		if ( isset( $schema['properties']['password'] ) ) {
    81 			$get_item_args['password'] = array(
    85 			$get_item_args['password'] = array(
    82 				'description' => __( 'The password for the post if it is password protected.' ),
    86 				'description' => __( 'The password for the post if it is password protected.' ),
    83 				'type'        => 'string',
    87 				'type'        => 'string',
    84 			);
    88 			);
    85 		}
    89 		}
    86 		register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)', array(
    90 		register_rest_route(
    87 			'args' => array(
    91 			$this->namespace,
    88 				'id' => array(
    92 			'/' . $this->rest_base . '/(?P<id>[\d]+)',
    89 					'description' => __( 'Unique identifier for the object.' ),
       
    90 					'type'        => 'integer',
       
    91 				),
       
    92 			),
       
    93 			array(
    93 			array(
    94 				'methods'             => WP_REST_Server::READABLE,
    94 				'args'   => array(
    95 				'callback'            => array( $this, 'get_item' ),
    95 					'id' => array(
    96 				'permission_callback' => array( $this, 'get_item_permissions_check' ),
    96 						'description' => __( 'Unique identifier for the object.' ),
    97 				'args'                => $get_item_args,
    97 						'type'        => 'integer',
    98 			),
       
    99 			array(
       
   100 				'methods'             => WP_REST_Server::EDITABLE,
       
   101 				'callback'            => array( $this, 'update_item' ),
       
   102 				'permission_callback' => array( $this, 'update_item_permissions_check' ),
       
   103 				'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
       
   104 			),
       
   105 			array(
       
   106 				'methods'             => WP_REST_Server::DELETABLE,
       
   107 				'callback'            => array( $this, 'delete_item' ),
       
   108 				'permission_callback' => array( $this, 'delete_item_permissions_check' ),
       
   109 				'args'                => array(
       
   110 					'force' => array(
       
   111 						'type'        => 'boolean',
       
   112 						'default'     => false,
       
   113 						'description' => __( 'Whether to bypass trash and force deletion.' ),
       
   114 					),
    98 					),
   115 				),
    99 				),
   116 			),
   100 				array(
   117 			'schema' => array( $this, 'get_public_item_schema' ),
   101 					'methods'             => WP_REST_Server::READABLE,
   118 		) );
   102 					'callback'            => array( $this, 'get_item' ),
       
   103 					'permission_callback' => array( $this, 'get_item_permissions_check' ),
       
   104 					'args'                => $get_item_args,
       
   105 				),
       
   106 				array(
       
   107 					'methods'             => WP_REST_Server::EDITABLE,
       
   108 					'callback'            => array( $this, 'update_item' ),
       
   109 					'permission_callback' => array( $this, 'update_item_permissions_check' ),
       
   110 					'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
       
   111 				),
       
   112 				array(
       
   113 					'methods'             => WP_REST_Server::DELETABLE,
       
   114 					'callback'            => array( $this, 'delete_item' ),
       
   115 					'permission_callback' => array( $this, 'delete_item_permissions_check' ),
       
   116 					'args'                => array(
       
   117 						'force' => array(
       
   118 							'type'        => 'boolean',
       
   119 							'default'     => false,
       
   120 							'description' => __( 'Whether to bypass trash and force deletion.' ),
       
   121 						),
       
   122 					),
       
   123 				),
       
   124 				'schema' => array( $this, 'get_public_item_schema' ),
       
   125 			)
       
   126 		);
   119 	}
   127 	}
   120 
   128 
   121 	/**
   129 	/**
   122 	 * Checks if a given request has access to read posts.
   130 	 * Checks if a given request has access to read posts.
   123 	 *
   131 	 *
   157 			return new WP_Error( 'rest_orderby_include_missing_include', __( 'You need to define an include parameter to order by include.' ), array( 'status' => 400 ) );
   165 			return new WP_Error( 'rest_orderby_include_missing_include', __( 'You need to define an include parameter to order by include.' ), array( 'status' => 400 ) );
   158 		}
   166 		}
   159 
   167 
   160 		// Retrieve the list of registered collection query parameters.
   168 		// Retrieve the list of registered collection query parameters.
   161 		$registered = $this->get_collection_params();
   169 		$registered = $this->get_collection_params();
   162 		$args = array();
   170 		$args       = array();
   163 
   171 
   164 		/*
   172 		/*
   165 		 * This array defines mappings between public API query parameters whose
   173 		 * This array defines mappings between public API query parameters whose
   166 		 * values are accepted as-passed, and their internal WP_Query parameter
   174 		 * values are accepted as-passed, and their internal WP_Query parameter
   167 		 * name equivalents (some are the same). Only values which are also
   175 		 * name equivalents (some are the same). Only values which are also
   256 		 * @link https://developer.wordpress.org/reference/classes/wp_query/
   264 		 * @link https://developer.wordpress.org/reference/classes/wp_query/
   257 		 *
   265 		 *
   258 		 * @param array           $args    Key value array of query var to query value.
   266 		 * @param array           $args    Key value array of query var to query value.
   259 		 * @param WP_REST_Request $request The request used.
   267 		 * @param WP_REST_Request $request The request used.
   260 		 */
   268 		 */
   261 		$args = apply_filters( "rest_{$this->post_type}_query", $args, $request );
   269 		$args       = apply_filters( "rest_{$this->post_type}_query", $args, $request );
   262 		$query_args = $this->prepare_items_query( $args, $request );
   270 		$query_args = $this->prepare_items_query( $args, $request );
   263 
   271 
   264 		$taxonomies = wp_list_filter( get_object_taxonomies( $this->post_type, 'objects' ), array( 'show_in_rest' => true ) );
   272 		$taxonomies = wp_list_filter( get_object_taxonomies( $this->post_type, 'objects' ), array( 'show_in_rest' => true ) );
   265 
   273 
   266 		foreach ( $taxonomies as $taxonomy ) {
   274 		foreach ( $taxonomies as $taxonomy ) {
   267 			$base = ! empty( $taxonomy->rest_base ) ? $taxonomy->rest_base : $taxonomy->name;
   275 			$base        = ! empty( $taxonomy->rest_base ) ? $taxonomy->rest_base : $taxonomy->name;
   268 			$tax_exclude = $base . '_exclude';
   276 			$tax_exclude = $base . '_exclude';
   269 
   277 
   270 			if ( ! empty( $request[ $base ] ) ) {
   278 			if ( ! empty( $request[ $base ] ) ) {
   271 				$query_args['tax_query'][] = array(
   279 				$query_args['tax_query'][] = array(
   272 					'taxonomy'         => $taxonomy->name,
   280 					'taxonomy'         => $taxonomy->name,
   309 		// Reset filter.
   317 		// Reset filter.
   310 		if ( 'edit' === $request['context'] ) {
   318 		if ( 'edit' === $request['context'] ) {
   311 			remove_filter( 'post_password_required', '__return_false' );
   319 			remove_filter( 'post_password_required', '__return_false' );
   312 		}
   320 		}
   313 
   321 
   314 		$page = (int) $query_args['paged'];
   322 		$page        = (int) $query_args['paged'];
   315 		$total_posts = $posts_query->found_posts;
   323 		$total_posts = $posts_query->found_posts;
   316 
   324 
   317 		if ( $total_posts < 1 ) {
   325 		if ( $total_posts < 1 ) {
   318 			// Out-of-bounds, run the query again without LIMIT for total count.
   326 			// Out-of-bounds, run the query again without LIMIT for total count.
   319 			unset( $query_args['paged'] );
   327 			unset( $query_args['paged'] );
   327 
   335 
   328 		if ( $page > $max_pages && $total_posts > 0 ) {
   336 		if ( $page > $max_pages && $total_posts > 0 ) {
   329 			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 			return new WP_Error( 'rest_post_invalid_page_number', __( 'The page number requested is larger than the number of pages available.' ), array( 'status' => 400 ) );
   330 		}
   338 		}
   331 
   339 
   332 		$response  = rest_ensure_response( $posts );
   340 		$response = rest_ensure_response( $posts );
   333 
   341 
   334 		$response->header( 'X-WP-Total', (int) $total_posts );
   342 		$response->header( 'X-WP-Total', (int) $total_posts );
   335 		$response->header( 'X-WP-TotalPages', (int) $max_pages );
   343 		$response->header( 'X-WP-TotalPages', (int) $max_pages );
   336 
   344 
   337 		$request_params = $request->get_query_params();
   345 		$request_params = $request->get_query_params();
   338 		$base = add_query_arg( $request_params, rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ) );
   346 		$base           = add_query_arg( urlencode_deep( $request_params ), rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ) );
   339 
   347 
   340 		if ( $page > 1 ) {
   348 		if ( $page > 1 ) {
   341 			$prev_page = $page - 1;
   349 			$prev_page = $page - 1;
   342 
   350 
   343 			if ( $prev_page > $max_pages ) {
   351 			if ( $prev_page > $max_pages ) {
   464 
   472 
   465 		$data     = $this->prepare_item_for_response( $post, $request );
   473 		$data     = $this->prepare_item_for_response( $post, $request );
   466 		$response = rest_ensure_response( $data );
   474 		$response = rest_ensure_response( $data );
   467 
   475 
   468 		if ( is_post_type_viewable( get_post_type_object( $post->post_type ) ) ) {
   476 		if ( is_post_type_viewable( get_post_type_object( $post->post_type ) ) ) {
   469 			$response->link_header( 'alternate',  get_permalink( $post->ID ), array( 'type' => 'text/html' ) );
   477 			$response->link_header( 'alternate', get_permalink( $post->ID ), array( 'type' => 'text/html' ) );
   470 		}
   478 		}
   471 
   479 
   472 		return $response;
   480 		return $response;
   473 	}
   481 	}
   474 
   482 
   589 			if ( is_wp_error( $meta_update ) ) {
   597 			if ( is_wp_error( $meta_update ) ) {
   590 				return $meta_update;
   598 				return $meta_update;
   591 			}
   599 			}
   592 		}
   600 		}
   593 
   601 
   594 		$post = get_post( $post_id );
   602 		$post          = get_post( $post_id );
   595 		$fields_update = $this->update_additional_fields_for_object( $post, $request );
   603 		$fields_update = $this->update_additional_fields_for_object( $post, $request );
   596 
   604 
   597 		if ( is_wp_error( $fields_update ) ) {
   605 		if ( is_wp_error( $fields_update ) ) {
   598 			return $fields_update;
   606 			return $fields_update;
   599 		}
   607 		}
   600 
   608 
   601 		$request->set_param( 'context', 'edit' );
   609 		$request->set_param( 'context', 'edit' );
       
   610 
       
   611 		/**
       
   612 		 * Fires after a single post is completely created or updated via the REST API.
       
   613 		 *
       
   614 		 * The dynamic portion of the hook name, `$this->post_type`, refers to the post type slug.
       
   615 		 *
       
   616 		 * @since 5.0.0
       
   617 		 *
       
   618 		 * @param WP_Post         $post     Inserted or updated post object.
       
   619 		 * @param WP_REST_Request $request  Request object.
       
   620 		 * @param bool            $creating True when creating a post, false when updating.
       
   621 		 */
       
   622 		do_action( "rest_after_insert_{$this->post_type}", $post, $request, true );
   602 
   623 
   603 		$response = $this->prepare_item_for_response( $post, $request );
   624 		$response = $this->prepare_item_for_response( $post, $request );
   604 		$response = rest_ensure_response( $response );
   625 		$response = rest_ensure_response( $response );
   605 
   626 
   606 		$response->set_status( 201 );
   627 		$response->set_status( 201 );
   715 			if ( is_wp_error( $meta_update ) ) {
   736 			if ( is_wp_error( $meta_update ) ) {
   716 				return $meta_update;
   737 				return $meta_update;
   717 			}
   738 			}
   718 		}
   739 		}
   719 
   740 
   720 		$post = get_post( $post_id );
   741 		$post          = get_post( $post_id );
   721 		$fields_update = $this->update_additional_fields_for_object( $post, $request );
   742 		$fields_update = $this->update_additional_fields_for_object( $post, $request );
   722 
   743 
   723 		if ( is_wp_error( $fields_update ) ) {
   744 		if ( is_wp_error( $fields_update ) ) {
   724 			return $fields_update;
   745 			return $fields_update;
   725 		}
   746 		}
   726 
   747 
   727 		$request->set_param( 'context', 'edit' );
   748 		$request->set_param( 'context', 'edit' );
       
   749 
       
   750 		// Filter is fired in WP_REST_Attachments_Controller subclass.
       
   751 		if ( 'attachment' === $this->post_type ) {
       
   752 			$response = $this->prepare_item_for_response( $post, $request );
       
   753 			return rest_ensure_response( $response );
       
   754 		}
       
   755 
       
   756 		/** This action is documented in wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php */
       
   757 		do_action( "rest_after_insert_{$this->post_type}", $post, $request, false );
   728 
   758 
   729 		$response = $this->prepare_item_for_response( $post, $request );
   759 		$response = $this->prepare_item_for_response( $post, $request );
   730 
   760 
   731 		return rest_ensure_response( $response );
   761 		return rest_ensure_response( $response );
   732 	}
   762 	}
   793 			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 			return new WP_Error( 'rest_user_cannot_delete_post', __( 'Sorry, you are not allowed to delete this post.' ), array( 'status' => rest_authorization_required_code() ) );
   794 		}
   824 		}
   795 
   825 
   796 		$request->set_param( 'context', 'edit' );
   826 		$request->set_param( 'context', 'edit' );
   797 
   827 
   798 
       
   799 		// If we're forcing, then delete permanently.
   828 		// If we're forcing, then delete permanently.
   800 		if ( $force ) {
   829 		if ( $force ) {
   801 			$previous = $this->prepare_item_for_response( $post, $request );
   830 			$previous = $this->prepare_item_for_response( $post, $request );
   802 			$result = wp_delete_post( $id, true );
   831 			$result   = wp_delete_post( $id, true );
   803 			$response = new WP_REST_Response();
   832 			$response = new WP_REST_Response();
   804 			$response->set_data( array( 'deleted' => true, 'previous' => $previous->get_data() ) );
   833 			$response->set_data(
       
   834 				array(
       
   835 					'deleted'  => true,
       
   836 					'previous' => $previous->get_data(),
       
   837 				)
       
   838 			);
   805 		} else {
   839 		} else {
   806 			// If we don't support trashing for this type, error out.
   840 			// If we don't support trashing for this type, error out.
   807 			if ( ! $supports_trash ) {
   841 			if ( ! $supports_trash ) {
   808 				/* translators: %s: force=true */
   842 				/* translators: %s: force=true */
   809 				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 				return new WP_Error( 'rest_trash_not_supported', sprintf( __( "The post does not support trashing. Set '%s' to delete." ), 'force=true' ), array( 'status' => 501 ) );
   814 				return new WP_Error( 'rest_already_trashed', __( 'The post has already been deleted.' ), array( 'status' => 410 ) );
   848 				return new WP_Error( 'rest_already_trashed', __( 'The post has already been deleted.' ), array( 'status' => 410 ) );
   815 			}
   849 			}
   816 
   850 
   817 			// (Note that internally this falls through to `wp_delete_post` if
   851 			// (Note that internally this falls through to `wp_delete_post` if
   818 			// the trash is disabled.)
   852 			// the trash is disabled.)
   819 			$result = wp_trash_post( $id );
   853 			$result   = wp_trash_post( $id );
   820 			$post = get_post( $id );
   854 			$post     = get_post( $id );
   821 			$response = $this->prepare_item_for_response( $post, $request );
   855 			$response = $this->prepare_item_for_response( $post, $request );
   822 		}
   856 		}
   823 
   857 
   824 		if ( ! $result ) {
   858 		if ( ! $result ) {
   825 			return new WP_Error( 'rest_cannot_delete', __( 'The post cannot be deleted.' ), array( 'status' => 500 ) );
   859 			return new WP_Error( 'rest_cannot_delete', __( 'The post cannot be deleted.' ), array( 'status' => 500 ) );
   862 			 *
   896 			 *
   863 			 * @since 4.7.0
   897 			 * @since 4.7.0
   864 			 *
   898 			 *
   865 			 * @param string $value The query_var value.
   899 			 * @param string $value The query_var value.
   866 			 */
   900 			 */
   867 			$query_args[ $key ] = apply_filters( "rest_query_var-{$key}", $value );
   901 			$query_args[ $key ] = apply_filters( "rest_query_var-{$key}", $value ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
   868 		}
   902 		}
   869 
   903 
   870 		if ( 'post' !== $this->post_type || ! isset( $query_args['ignore_sticky_posts'] ) ) {
   904 		if ( 'post' !== $this->post_type || ! isset( $query_args['ignore_sticky_posts'] ) ) {
   871 			$query_args['ignore_sticky_posts'] = true;
   905 			$query_args['ignore_sticky_posts'] = true;
   872 		}
   906 		}
   989 		if ( ! empty( $schema['properties']['date'] ) && ! empty( $request['date'] ) ) {
  1023 		if ( ! empty( $schema['properties']['date'] ) && ! empty( $request['date'] ) ) {
   990 			$date_data = rest_get_date_with_gmt( $request['date'] );
  1024 			$date_data = rest_get_date_with_gmt( $request['date'] );
   991 
  1025 
   992 			if ( ! empty( $date_data ) ) {
  1026 			if ( ! empty( $date_data ) ) {
   993 				list( $prepared_post->post_date, $prepared_post->post_date_gmt ) = $date_data;
  1027 				list( $prepared_post->post_date, $prepared_post->post_date_gmt ) = $date_data;
   994 				$prepared_post->edit_date = true;
  1028 				$prepared_post->edit_date                                        = true;
   995 			}
  1029 			}
   996 		} elseif ( ! empty( $schema['properties']['date_gmt'] ) && ! empty( $request['date_gmt'] ) ) {
  1030 		} elseif ( ! empty( $schema['properties']['date_gmt'] ) && ! empty( $request['date_gmt'] ) ) {
   997 			$date_data = rest_get_date_with_gmt( $request['date_gmt'], true );
  1031 			$date_data = rest_get_date_with_gmt( $request['date_gmt'], true );
   998 
  1032 
   999 			if ( ! empty( $date_data ) ) {
  1033 			if ( ! empty( $date_data ) ) {
  1000 				list( $prepared_post->post_date, $prepared_post->post_date_gmt ) = $date_data;
  1034 				list( $prepared_post->post_date, $prepared_post->post_date_gmt ) = $date_data;
  1001 				$prepared_post->edit_date = true;
  1035 				$prepared_post->edit_date                                        = true;
  1002 			}
  1036 			}
  1003 		}
  1037 		}
  1004 
  1038 
  1005 		// Post slug.
  1039 		// Post slug.
  1006 		if ( ! empty( $schema['properties']['slug'] ) && isset( $request['slug'] ) ) {
  1040 		if ( ! empty( $schema['properties']['slug'] ) && isset( $request['slug'] ) ) {
  1191 
  1225 
  1192 	/**
  1226 	/**
  1193 	 * Sets the template for a post.
  1227 	 * Sets the template for a post.
  1194 	 *
  1228 	 *
  1195 	 * @since 4.7.0
  1229 	 * @since 4.7.0
  1196 	 * @since 4.9.0 Introduced the $validate parameter.
  1230 	 * @since 4.9.0 Added the `$validate` parameter.
  1197 	 *
  1231 	 *
  1198 	 * @param string  $template Page template filename.
  1232 	 * @param string  $template Page template filename.
  1199 	 * @param integer $post_id  Post ID.
  1233 	 * @param integer $post_id  Post ID.
  1200 	 * @param bool    $validate Whether to validate that the template selected is valid.
  1234 	 * @param bool    $validate Whether to validate that the template selected is valid.
  1201 	 */
  1235 	 */
  1491 			$has_password_filter = true;
  1525 			$has_password_filter = true;
  1492 		}
  1526 		}
  1493 
  1527 
  1494 		if ( in_array( 'content', $fields, true ) ) {
  1528 		if ( in_array( 'content', $fields, true ) ) {
  1495 			$data['content'] = array(
  1529 			$data['content'] = array(
  1496 				'raw'       => $post->post_content,
  1530 				'raw'           => $post->post_content,
  1497 				/** This filter is documented in wp-includes/post-template.php */
  1531 				/** This filter is documented in wp-includes/post-template.php */
  1498 				'rendered'  => post_password_required( $post ) ? '' : apply_filters( 'the_content', $post->post_content ),
  1532 				'rendered'      => post_password_required( $post ) ? '' : apply_filters( 'the_content', $post->post_content ),
  1499 				'protected' => (bool) $post->post_password,
  1533 				'protected'     => (bool) $post->post_password,
       
  1534 				'block_version' => block_version( $post->post_content ),
  1500 			);
  1535 			);
  1501 		}
  1536 		}
  1502 
  1537 
  1503 		if ( in_array( 'excerpt', $fields, true ) ) {
  1538 		if ( in_array( 'excerpt', $fields, true ) ) {
  1504 			/** This filter is documented in wp-includes/post-template.php */
  1539 			/** This filter is documented in wp-includes/post-template.php */
  1505 			$excerpt = apply_filters( 'the_excerpt', apply_filters( 'get_the_excerpt', $post->post_excerpt, $post ) );
  1540 			$excerpt         = apply_filters( 'the_excerpt', apply_filters( 'get_the_excerpt', $post->post_excerpt, $post ) );
  1506 			$data['excerpt'] = array(
  1541 			$data['excerpt'] = array(
  1507 				'raw'       => $post->post_excerpt,
  1542 				'raw'       => $post->post_excerpt,
  1508 				'rendered'  => post_password_required( $post ) ? '' : $excerpt,
  1543 				'rendered'  => post_password_required( $post ) ? '' : $excerpt,
  1509 				'protected' => (bool) $post->post_password,
  1544 				'protected' => (bool) $post->post_password,
  1510 			);
  1545 			);
  1568 
  1603 
  1569 		foreach ( $taxonomies as $taxonomy ) {
  1604 		foreach ( $taxonomies as $taxonomy ) {
  1570 			$base = ! empty( $taxonomy->rest_base ) ? $taxonomy->rest_base : $taxonomy->name;
  1605 			$base = ! empty( $taxonomy->rest_base ) ? $taxonomy->rest_base : $taxonomy->name;
  1571 
  1606 
  1572 			if ( in_array( $base, $fields, true ) ) {
  1607 			if ( in_array( $base, $fields, true ) ) {
  1573 				$terms = get_the_terms( $post, $taxonomy->name );
  1608 				$terms         = get_the_terms( $post, $taxonomy->name );
  1574 				$data[ $base ] = $terms ? array_values( wp_list_pluck( $terms, 'term_id' ) ) : array();
  1609 				$data[ $base ] = $terms ? array_values( wp_list_pluck( $terms, 'term_id' ) ) : array();
       
  1610 			}
       
  1611 		}
       
  1612 
       
  1613 		$post_type_obj = get_post_type_object( $post->post_type );
       
  1614 		if ( is_post_type_viewable( $post_type_obj ) && $post_type_obj->public ) {
       
  1615 
       
  1616 			if ( ! function_exists( 'get_sample_permalink' ) ) {
       
  1617 				require_once ABSPATH . 'wp-admin/includes/post.php';
       
  1618 			}
       
  1619 
       
  1620 			$sample_permalink = get_sample_permalink( $post->ID, $post->post_title, '' );
       
  1621 
       
  1622 			if ( in_array( 'permalink_template', $fields, true ) ) {
       
  1623 				$data['permalink_template'] = $sample_permalink[0];
       
  1624 			}
       
  1625 			if ( in_array( 'generated_slug', $fields, true ) ) {
       
  1626 				$data['generated_slug'] = $sample_permalink[1];
  1575 			}
  1627 			}
  1576 		}
  1628 		}
  1577 
  1629 
  1578 		$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
  1630 		$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
  1579 		$data    = $this->add_additional_fields_to_object( $data, $request );
  1631 		$data    = $this->add_additional_fields_to_object( $data, $request );
  1635 	protected function prepare_links( $post ) {
  1687 	protected function prepare_links( $post ) {
  1636 		$base = sprintf( '%s/%s', $this->namespace, $this->rest_base );
  1688 		$base = sprintf( '%s/%s', $this->namespace, $this->rest_base );
  1637 
  1689 
  1638 		// Entity meta.
  1690 		// Entity meta.
  1639 		$links = array(
  1691 		$links = array(
  1640 			'self' => array(
  1692 			'self'       => array(
  1641 				'href'   => rest_url( trailingslashit( $base ) . $post->ID ),
  1693 				'href' => rest_url( trailingslashit( $base ) . $post->ID ),
  1642 			),
  1694 			),
  1643 			'collection' => array(
  1695 			'collection' => array(
  1644 				'href'   => rest_url( $base ),
  1696 				'href' => rest_url( $base ),
  1645 			),
  1697 			),
  1646 			'about'      => array(
  1698 			'about'      => array(
  1647 				'href'   => rest_url( 'wp/v2/types/' . $this->post_type ),
  1699 				'href' => rest_url( 'wp/v2/types/' . $this->post_type ),
  1648 			),
  1700 			),
  1649 		);
  1701 		);
  1650 
  1702 
  1651 		if ( ( in_array( $post->post_type, array( 'post', 'page' ), true ) || post_type_supports( $post->post_type, 'author' ) )
  1703 		if ( ( in_array( $post->post_type, array( 'post', 'page' ), true ) || post_type_supports( $post->post_type, 'author' ) )
  1652 			&& ! empty( $post->post_author ) ) {
  1704 			&& ! empty( $post->post_author ) ) {
  1681 				$links['predecessor-version'] = array(
  1733 				$links['predecessor-version'] = array(
  1682 					'href' => rest_url( trailingslashit( $base ) . $post->ID . '/revisions/' . $last_revision ),
  1734 					'href' => rest_url( trailingslashit( $base ) . $post->ID . '/revisions/' . $last_revision ),
  1683 					'id'   => $last_revision,
  1735 					'id'   => $last_revision,
  1684 				);
  1736 				);
  1685 			}
  1737 			}
  1686 
       
  1687 		}
  1738 		}
  1688 
  1739 
  1689 		$post_type_obj = get_post_type_object( $post->post_type );
  1740 		$post_type_obj = get_post_type_object( $post->post_type );
  1690 
  1741 
  1691 		if ( $post_type_obj->hierarchical && ! empty( $post->post_parent ) ) {
  1742 		if ( $post_type_obj->hierarchical && ! empty( $post->post_parent ) ) {
  1766 
  1817 
  1767 		$post_type = get_post_type_object( $post->post_type );
  1818 		$post_type = get_post_type_object( $post->post_type );
  1768 
  1819 
  1769 		if ( 'attachment' !== $this->post_type && current_user_can( $post_type->cap->publish_posts ) ) {
  1820 		if ( 'attachment' !== $this->post_type && current_user_can( $post_type->cap->publish_posts ) ) {
  1770 			$rels[] = 'https://api.w.org/action-publish';
  1821 			$rels[] = 'https://api.w.org/action-publish';
       
  1822 		}
       
  1823 
       
  1824 		if ( current_user_can( 'unfiltered_html' ) ) {
       
  1825 			$rels[] = 'https://api.w.org/action-unfiltered-html';
  1771 		}
  1826 		}
  1772 
  1827 
  1773 		if ( 'post' === $post_type->name ) {
  1828 		if ( 'post' === $post_type->name ) {
  1774 			if ( current_user_can( $post_type->cap->edit_others_posts ) && current_user_can( $post_type->cap->publish_posts ) ) {
  1829 			if ( current_user_can( $post_type->cap->edit_others_posts ) && current_user_can( $post_type->cap->publish_posts ) ) {
  1775 				$rels[] = 'https://api.w.org/action-sticky';
  1830 				$rels[] = 'https://api.w.org/action-sticky';
  1813 			'$schema'    => 'http://json-schema.org/draft-04/schema#',
  1868 			'$schema'    => 'http://json-schema.org/draft-04/schema#',
  1814 			'title'      => $this->post_type,
  1869 			'title'      => $this->post_type,
  1815 			'type'       => 'object',
  1870 			'type'       => 'object',
  1816 			// Base properties for every Post.
  1871 			// Base properties for every Post.
  1817 			'properties' => array(
  1872 			'properties' => array(
  1818 				'date'            => array(
  1873 				'date'         => array(
  1819 					'description' => __( "The date the object was published, in the site's timezone." ),
  1874 					'description' => __( "The date the object was published, in the site's timezone." ),
  1820 					'type'        => 'string',
  1875 					'type'        => 'string',
  1821 					'format'      => 'date-time',
  1876 					'format'      => 'date-time',
  1822 					'context'     => array( 'view', 'edit', 'embed' ),
  1877 					'context'     => array( 'view', 'edit', 'embed' ),
  1823 				),
  1878 				),
  1824 				'date_gmt'        => array(
  1879 				'date_gmt'     => array(
  1825 					'description' => __( 'The date the object was published, as GMT.' ),
  1880 					'description' => __( 'The date the object was published, as GMT.' ),
  1826 					'type'        => 'string',
  1881 					'type'        => 'string',
  1827 					'format'      => 'date-time',
  1882 					'format'      => 'date-time',
  1828 					'context'     => array( 'view', 'edit' ),
  1883 					'context'     => array( 'view', 'edit' ),
  1829 				),
  1884 				),
  1830 				'guid'            => array(
  1885 				'guid'         => array(
  1831 					'description' => __( 'The globally unique identifier for the object.' ),
  1886 					'description' => __( 'The globally unique identifier for the object.' ),
  1832 					'type'        => 'object',
  1887 					'type'        => 'object',
  1833 					'context'     => array( 'view', 'edit' ),
  1888 					'context'     => array( 'view', 'edit' ),
  1834 					'readonly'    => true,
  1889 					'readonly'    => true,
  1835 					'properties'  => array(
  1890 					'properties'  => array(
  1845 							'context'     => array( 'view', 'edit' ),
  1900 							'context'     => array( 'view', 'edit' ),
  1846 							'readonly'    => true,
  1901 							'readonly'    => true,
  1847 						),
  1902 						),
  1848 					),
  1903 					),
  1849 				),
  1904 				),
  1850 				'id'              => array(
  1905 				'id'           => array(
  1851 					'description' => __( 'Unique identifier for the object.' ),
  1906 					'description' => __( 'Unique identifier for the object.' ),
  1852 					'type'        => 'integer',
  1907 					'type'        => 'integer',
  1853 					'context'     => array( 'view', 'edit', 'embed' ),
  1908 					'context'     => array( 'view', 'edit', 'embed' ),
  1854 					'readonly'    => true,
  1909 					'readonly'    => true,
  1855 				),
  1910 				),
  1856 				'link'            => array(
  1911 				'link'         => array(
  1857 					'description' => __( 'URL to the object.' ),
  1912 					'description' => __( 'URL to the object.' ),
  1858 					'type'        => 'string',
  1913 					'type'        => 'string',
  1859 					'format'      => 'uri',
  1914 					'format'      => 'uri',
  1860 					'context'     => array( 'view', 'edit', 'embed' ),
  1915 					'context'     => array( 'view', 'edit', 'embed' ),
  1861 					'readonly'    => true,
  1916 					'readonly'    => true,
  1862 				),
  1917 				),
  1863 				'modified'        => array(
  1918 				'modified'     => array(
  1864 					'description' => __( "The date the object was last modified, in the site's timezone." ),
  1919 					'description' => __( "The date the object was last modified, in the site's timezone." ),
  1865 					'type'        => 'string',
  1920 					'type'        => 'string',
  1866 					'format'      => 'date-time',
  1921 					'format'      => 'date-time',
  1867 					'context'     => array( 'view', 'edit' ),
  1922 					'context'     => array( 'view', 'edit' ),
  1868 					'readonly'    => true,
  1923 					'readonly'    => true,
  1869 				),
  1924 				),
  1870 				'modified_gmt'    => array(
  1925 				'modified_gmt' => array(
  1871 					'description' => __( 'The date the object was last modified, as GMT.' ),
  1926 					'description' => __( 'The date the object was last modified, as GMT.' ),
  1872 					'type'        => 'string',
  1927 					'type'        => 'string',
  1873 					'format'      => 'date-time',
  1928 					'format'      => 'date-time',
  1874 					'context'     => array( 'view', 'edit' ),
  1929 					'context'     => array( 'view', 'edit' ),
  1875 					'readonly'    => true,
  1930 					'readonly'    => true,
  1876 				),
  1931 				),
  1877 				'slug'            => array(
  1932 				'slug'         => array(
  1878 					'description' => __( 'An alphanumeric identifier for the object unique to its type.' ),
  1933 					'description' => __( 'An alphanumeric identifier for the object unique to its type.' ),
  1879 					'type'        => 'string',
  1934 					'type'        => 'string',
  1880 					'context'     => array( 'view', 'edit', 'embed' ),
  1935 					'context'     => array( 'view', 'edit', 'embed' ),
  1881 					'arg_options' => array(
  1936 					'arg_options' => array(
  1882 						'sanitize_callback' => array( $this, 'sanitize_slug' ),
  1937 						'sanitize_callback' => array( $this, 'sanitize_slug' ),
  1883 					),
  1938 					),
  1884 				),
  1939 				),
  1885 				'status'          => array(
  1940 				'status'       => array(
  1886 					'description' => __( 'A named status for the object.' ),
  1941 					'description' => __( 'A named status for the object.' ),
  1887 					'type'        => 'string',
  1942 					'type'        => 'string',
  1888 					'enum'        => array_keys( get_post_stati( array( 'internal' => false ) ) ),
  1943 					'enum'        => array_keys( get_post_stati( array( 'internal' => false ) ) ),
  1889 					'context'     => array( 'view', 'edit' ),
  1944 					'context'     => array( 'view', 'edit' ),
  1890 				),
  1945 				),
  1891 				'type'            => array(
  1946 				'type'         => array(
  1892 					'description' => __( 'Type of Post for the object.' ),
  1947 					'description' => __( 'Type of Post for the object.' ),
  1893 					'type'        => 'string',
  1948 					'type'        => 'string',
  1894 					'context'     => array( 'view', 'edit', 'embed' ),
  1949 					'context'     => array( 'view', 'edit', 'embed' ),
  1895 					'readonly'    => true,
  1950 					'readonly'    => true,
  1896 				),
  1951 				),
  1897 				'password'        => array(
  1952 				'password'     => array(
  1898 					'description' => __( 'A password to protect access to the content and excerpt.' ),
  1953 					'description' => __( 'A password to protect access to the content and excerpt.' ),
  1899 					'type'        => 'string',
  1954 					'type'        => 'string',
  1900 					'context'     => array( 'edit' ),
  1955 					'context'     => array( 'edit' ),
  1901 				),
  1956 				),
  1902 			),
  1957 			),
  1903 		);
  1958 		);
  1904 
  1959 
  1905 		$post_type_obj = get_post_type_object( $this->post_type );
  1960 		$post_type_obj = get_post_type_object( $this->post_type );
       
  1961 		if ( is_post_type_viewable( $post_type_obj ) && $post_type_obj->public ) {
       
  1962 			$schema['properties']['permalink_template'] = array(
       
  1963 				'description' => __( 'Permalink template for the object.' ),
       
  1964 				'type'        => 'string',
       
  1965 				'context'     => array( 'edit' ),
       
  1966 				'readonly'    => true,
       
  1967 			);
       
  1968 
       
  1969 			$schema['properties']['generated_slug'] = array(
       
  1970 				'description' => __( 'Slug automatically generated from the object title.' ),
       
  1971 				'type'        => 'string',
       
  1972 				'context'     => array( 'edit' ),
       
  1973 				'readonly'    => true,
       
  1974 			);
       
  1975 		}
  1906 
  1976 
  1907 		if ( $post_type_obj->hierarchical ) {
  1977 		if ( $post_type_obj->hierarchical ) {
  1908 			$schema['properties']['parent'] = array(
  1978 			$schema['properties']['parent'] = array(
  1909 				'description' => __( 'The ID for the parent of the object.' ),
  1979 				'description' => __( 'The ID for the parent of the object.' ),
  1910 				'type'        => 'integer',
  1980 				'type'        => 'integer',
  1922 			'revisions',
  1992 			'revisions',
  1923 			'page-attributes',
  1993 			'page-attributes',
  1924 			'post-formats',
  1994 			'post-formats',
  1925 			'custom-fields',
  1995 			'custom-fields',
  1926 		);
  1996 		);
  1927 		$fixed_schemas = array(
  1997 		$fixed_schemas        = array(
  1928 			'post' => array(
  1998 			'post'       => array(
  1929 				'title',
  1999 				'title',
  1930 				'editor',
  2000 				'editor',
  1931 				'author',
  2001 				'author',
  1932 				'excerpt',
  2002 				'excerpt',
  1933 				'thumbnail',
  2003 				'thumbnail',
  1934 				'comments',
  2004 				'comments',
  1935 				'revisions',
  2005 				'revisions',
  1936 				'post-formats',
  2006 				'post-formats',
  1937 				'custom-fields',
  2007 				'custom-fields',
  1938 			),
  2008 			),
  1939 			'page' => array(
  2009 			'page'       => array(
  1940 				'title',
  2010 				'title',
  1941 				'editor',
  2011 				'editor',
  1942 				'author',
  2012 				'author',
  1943 				'excerpt',
  2013 				'excerpt',
  1944 				'thumbnail',
  2014 				'thumbnail',
  1972 						'arg_options' => array(
  2042 						'arg_options' => array(
  1973 							'sanitize_callback' => null, // Note: sanitization implemented in self::prepare_item_for_database()
  2043 							'sanitize_callback' => null, // Note: sanitization implemented in self::prepare_item_for_database()
  1974 							'validate_callback' => null, // Note: validation implemented in self::prepare_item_for_database()
  2044 							'validate_callback' => null, // Note: validation implemented in self::prepare_item_for_database()
  1975 						),
  2045 						),
  1976 						'properties'  => array(
  2046 						'properties'  => array(
  1977 							'raw' => array(
  2047 							'raw'      => array(
  1978 								'description' => __( 'Title for the object, as it exists in the database.' ),
  2048 								'description' => __( 'Title for the object, as it exists in the database.' ),
  1979 								'type'        => 'string',
  2049 								'type'        => 'string',
  1980 								'context'     => array( 'edit' ),
  2050 								'context'     => array( 'edit' ),
  1981 							),
  2051 							),
  1982 							'rendered' => array(
  2052 							'rendered' => array(
  1997 						'arg_options' => array(
  2067 						'arg_options' => array(
  1998 							'sanitize_callback' => null, // Note: sanitization implemented in self::prepare_item_for_database()
  2068 							'sanitize_callback' => null, // Note: sanitization implemented in self::prepare_item_for_database()
  1999 							'validate_callback' => null, // Note: validation implemented in self::prepare_item_for_database()
  2069 							'validate_callback' => null, // Note: validation implemented in self::prepare_item_for_database()
  2000 						),
  2070 						),
  2001 						'properties'  => array(
  2071 						'properties'  => array(
  2002 							'raw' => array(
  2072 							'raw'           => array(
  2003 								'description' => __( 'Content for the object, as it exists in the database.' ),
  2073 								'description' => __( 'Content for the object, as it exists in the database.' ),
  2004 								'type'        => 'string',
  2074 								'type'        => 'string',
  2005 								'context'     => array( 'edit' ),
  2075 								'context'     => array( 'edit' ),
  2006 							),
  2076 							),
  2007 							'rendered' => array(
  2077 							'rendered'      => array(
  2008 								'description' => __( 'HTML content for the object, transformed for display.' ),
  2078 								'description' => __( 'HTML content for the object, transformed for display.' ),
  2009 								'type'        => 'string',
  2079 								'type'        => 'string',
  2010 								'context'     => array( 'view', 'edit' ),
  2080 								'context'     => array( 'view', 'edit' ),
  2011 								'readonly'    => true,
  2081 								'readonly'    => true,
  2012 							),
  2082 							),
  2013 							'protected'       => array(
  2083 							'block_version' => array(
       
  2084 								'description' => __( 'Version of the content block format used by the object.' ),
       
  2085 								'type'        => 'integer',
       
  2086 								'context'     => array( 'edit' ),
       
  2087 								'readonly'    => true,
       
  2088 							),
       
  2089 							'protected'     => array(
  2014 								'description' => __( 'Whether the content is protected with a password.' ),
  2090 								'description' => __( 'Whether the content is protected with a password.' ),
  2015 								'type'        => 'boolean',
  2091 								'type'        => 'boolean',
  2016 								'context'     => array( 'view', 'edit', 'embed' ),
  2092 								'context'     => array( 'view', 'edit', 'embed' ),
  2017 								'readonly'    => true,
  2093 								'readonly'    => true,
  2018 							),
  2094 							),
  2036 						'arg_options' => array(
  2112 						'arg_options' => array(
  2037 							'sanitize_callback' => null, // Note: sanitization implemented in self::prepare_item_for_database()
  2113 							'sanitize_callback' => null, // Note: sanitization implemented in self::prepare_item_for_database()
  2038 							'validate_callback' => null, // Note: validation implemented in self::prepare_item_for_database()
  2114 							'validate_callback' => null, // Note: validation implemented in self::prepare_item_for_database()
  2039 						),
  2115 						),
  2040 						'properties'  => array(
  2116 						'properties'  => array(
  2041 							'raw' => array(
  2117 							'raw'       => array(
  2042 								'description' => __( 'Excerpt for the object, as it exists in the database.' ),
  2118 								'description' => __( 'Excerpt for the object, as it exists in the database.' ),
  2043 								'type'        => 'string',
  2119 								'type'        => 'string',
  2044 								'context'     => array( 'edit' ),
  2120 								'context'     => array( 'edit' ),
  2045 							),
  2121 							),
  2046 							'rendered' => array(
  2122 							'rendered'  => array(
  2047 								'description' => __( 'HTML excerpt for the object, transformed for display.' ),
  2123 								'description' => __( 'HTML excerpt for the object, transformed for display.' ),
  2048 								'type'        => 'string',
  2124 								'type'        => 'string',
  2049 								'context'     => array( 'view', 'edit', 'embed' ),
  2125 								'context'     => array( 'view', 'edit', 'embed' ),
  2050 								'readonly'    => true,
  2126 								'readonly'    => true,
  2051 							),
  2127 							),
  2052 							'protected'       => array(
  2128 							'protected' => array(
  2053 								'description' => __( 'Whether the excerpt is protected with a password.' ),
  2129 								'description' => __( 'Whether the excerpt is protected with a password.' ),
  2054 								'type'        => 'boolean',
  2130 								'type'        => 'boolean',
  2055 								'context'     => array( 'view', 'edit', 'embed' ),
  2131 								'context'     => array( 'view', 'edit', 'embed' ),
  2056 								'readonly'    => true,
  2132 								'readonly'    => true,
  2057 							),
  2133 							),
  2072 						'description' => __( 'Whether or not comments are open on the object.' ),
  2148 						'description' => __( 'Whether or not comments are open on the object.' ),
  2073 						'type'        => 'string',
  2149 						'type'        => 'string',
  2074 						'enum'        => array( 'open', 'closed' ),
  2150 						'enum'        => array( 'open', 'closed' ),
  2075 						'context'     => array( 'view', 'edit' ),
  2151 						'context'     => array( 'view', 'edit' ),
  2076 					);
  2152 					);
  2077 					$schema['properties']['ping_status'] = array(
  2153 					$schema['properties']['ping_status']    = array(
  2078 						'description' => __( 'Whether or not the object can be pinged.' ),
  2154 						'description' => __( 'Whether or not the object can be pinged.' ),
  2079 						'type'        => 'string',
  2155 						'type'        => 'string',
  2080 						'enum'        => array( 'open', 'closed' ),
  2156 						'enum'        => array( 'open', 'closed' ),
  2081 						'context'     => array( 'view', 'edit' ),
  2157 						'context'     => array( 'view', 'edit' ),
  2082 					);
  2158 					);
  2126 			),
  2202 			),
  2127 		);
  2203 		);
  2128 
  2204 
  2129 		$taxonomies = wp_list_filter( get_object_taxonomies( $this->post_type, 'objects' ), array( 'show_in_rest' => true ) );
  2205 		$taxonomies = wp_list_filter( get_object_taxonomies( $this->post_type, 'objects' ), array( 'show_in_rest' => true ) );
  2130 		foreach ( $taxonomies as $taxonomy ) {
  2206 		foreach ( $taxonomies as $taxonomy ) {
  2131 			$base = ! empty( $taxonomy->rest_base ) ? $taxonomy->rest_base : $taxonomy->name;
  2207 			$base                          = ! empty( $taxonomy->rest_base ) ? $taxonomy->rest_base : $taxonomy->name;
  2132 			$schema['properties'][ $base ] = array(
  2208 			$schema['properties'][ $base ] = array(
  2133 				/* translators: %s: taxonomy name */
  2209 				/* translators: %s: taxonomy name */
  2134 				'description' => sprintf( __( 'The terms assigned to the object in the %s taxonomy.' ), $taxonomy->name ),
  2210 				'description' => sprintf( __( 'The terms assigned to the object in the %s taxonomy.' ), $taxonomy->name ),
  2135 				'type'        => 'array',
  2211 				'type'        => 'array',
  2136 				'items'       => array(
  2212 				'items'       => array(
  2137 					'type'    => 'integer',
  2213 					'type' => 'integer',
  2138 				),
  2214 				),
  2139 				'context'     => array( 'view', 'edit' ),
  2215 				'context'     => array( 'view', 'edit' ),
  2140 			);
  2216 			);
  2141 		}
  2217 		}
  2142 
  2218 
  2176 						),
  2252 						),
  2177 					),
  2253 					),
  2178 				),
  2254 				),
  2179 			);
  2255 			);
  2180 		}
  2256 		}
       
  2257 
       
  2258 		$links[] = array(
       
  2259 			'rel'          => 'https://api.w.org/action-unfiltered-html',
       
  2260 			'title'        => __( 'The current user can post unfiltered HTML markup and JavaScript.' ),
       
  2261 			'href'         => $href,
       
  2262 			'targetSchema' => array(
       
  2263 				'type'       => 'object',
       
  2264 				'properties' => array(
       
  2265 					'content' => array(
       
  2266 						'raw' => array(
       
  2267 							'type' => 'string',
       
  2268 						),
       
  2269 					),
       
  2270 				),
       
  2271 			),
       
  2272 		);
  2181 
  2273 
  2182 		if ( 'post' === $this->post_type ) {
  2274 		if ( 'post' === $this->post_type ) {
  2183 			$links[] = array(
  2275 			$links[] = array(
  2184 				'rel'          => 'https://api.w.org/action-sticky',
  2276 				'rel'          => 'https://api.w.org/action-sticky',
  2185 				'title'        => __( 'The current user can sticky this post.' ),
  2277 				'title'        => __( 'The current user can sticky this post.' ),
  2270 		$query_params = parent::get_collection_params();
  2362 		$query_params = parent::get_collection_params();
  2271 
  2363 
  2272 		$query_params['context']['default'] = 'view';
  2364 		$query_params['context']['default'] = 'view';
  2273 
  2365 
  2274 		$query_params['after'] = array(
  2366 		$query_params['after'] = array(
  2275 			'description'        => __( 'Limit response to posts published after a given ISO8601 compliant date.' ),
  2367 			'description' => __( 'Limit response to posts published after a given ISO8601 compliant date.' ),
  2276 			'type'               => 'string',
  2368 			'type'        => 'string',
  2277 			'format'             => 'date-time',
  2369 			'format'      => 'date-time',
  2278 		);
  2370 		);
  2279 
  2371 
  2280 		if ( post_type_supports( $this->post_type, 'author' ) ) {
  2372 		if ( post_type_supports( $this->post_type, 'author' ) ) {
  2281 			$query_params['author'] = array(
  2373 			$query_params['author']         = array(
  2282 				'description'         => __( 'Limit result set to posts assigned to specific authors.' ),
  2374 				'description' => __( 'Limit result set to posts assigned to specific authors.' ),
  2283 				'type'                => 'array',
  2375 				'type'        => 'array',
  2284 				'items'               => array(
  2376 				'items'       => array(
  2285 					'type'            => 'integer',
  2377 					'type' => 'integer',
  2286 				),
  2378 				),
  2287 				'default'             => array(),
  2379 				'default'     => array(),
  2288 			);
  2380 			);
  2289 			$query_params['author_exclude'] = array(
  2381 			$query_params['author_exclude'] = array(
  2290 				'description'         => __( 'Ensure result set excludes posts assigned to specific authors.' ),
  2382 				'description' => __( 'Ensure result set excludes posts assigned to specific authors.' ),
  2291 				'type'                => 'array',
  2383 				'type'        => 'array',
  2292 				'items'               => array(
  2384 				'items'       => array(
  2293 					'type'            => 'integer',
  2385 					'type' => 'integer',
  2294 				),
  2386 				),
  2295 				'default'             => array(),
  2387 				'default'     => array(),
  2296 			);
  2388 			);
  2297 		}
  2389 		}
  2298 
  2390 
  2299 		$query_params['before'] = array(
  2391 		$query_params['before'] = array(
  2300 			'description'        => __( 'Limit response to posts published before a given ISO8601 compliant date.' ),
  2392 			'description' => __( 'Limit response to posts published before a given ISO8601 compliant date.' ),
  2301 			'type'               => 'string',
  2393 			'type'        => 'string',
  2302 			'format'             => 'date-time',
  2394 			'format'      => 'date-time',
  2303 		);
  2395 		);
  2304 
  2396 
  2305 		$query_params['exclude'] = array(
  2397 		$query_params['exclude'] = array(
  2306 			'description'        => __( 'Ensure result set excludes specific IDs.' ),
  2398 			'description' => __( 'Ensure result set excludes specific IDs.' ),
  2307 			'type'               => 'array',
  2399 			'type'        => 'array',
  2308 			'items'              => array(
  2400 			'items'       => array(
  2309 				'type'           => 'integer',
  2401 				'type' => 'integer',
  2310 			),
  2402 			),
  2311 			'default'            => array(),
  2403 			'default'     => array(),
  2312 		);
  2404 		);
  2313 
  2405 
  2314 		$query_params['include'] = array(
  2406 		$query_params['include'] = array(
  2315 			'description'        => __( 'Limit result set to specific IDs.' ),
  2407 			'description' => __( 'Limit result set to specific IDs.' ),
  2316 			'type'               => 'array',
  2408 			'type'        => 'array',
  2317 			'items'              => array(
  2409 			'items'       => array(
  2318 				'type'           => 'integer',
  2410 				'type' => 'integer',
  2319 			),
  2411 			),
  2320 			'default'            => array(),
  2412 			'default'     => array(),
  2321 		);
  2413 		);
  2322 
  2414 
  2323 		if ( 'page' === $this->post_type || post_type_supports( $this->post_type, 'page-attributes' ) ) {
  2415 		if ( 'page' === $this->post_type || post_type_supports( $this->post_type, 'page-attributes' ) ) {
  2324 			$query_params['menu_order'] = array(
  2416 			$query_params['menu_order'] = array(
  2325 				'description'        => __( 'Limit result set to posts with a specific menu_order value.' ),
  2417 				'description' => __( 'Limit result set to posts with a specific menu_order value.' ),
  2326 				'type'               => 'integer',
  2418 				'type'        => 'integer',
  2327 			);
  2419 			);
  2328 		}
  2420 		}
  2329 
  2421 
  2330 		$query_params['offset'] = array(
  2422 		$query_params['offset'] = array(
  2331 			'description'        => __( 'Offset the result set by a specific number of items.' ),
  2423 			'description' => __( 'Offset the result set by a specific number of items.' ),
  2332 			'type'               => 'integer',
  2424 			'type'        => 'integer',
  2333 		);
  2425 		);
  2334 
  2426 
  2335 		$query_params['order'] = array(
  2427 		$query_params['order'] = array(
  2336 			'description'        => __( 'Order sort attribute ascending or descending.' ),
  2428 			'description' => __( 'Order sort attribute ascending or descending.' ),
  2337 			'type'               => 'string',
  2429 			'type'        => 'string',
  2338 			'default'            => 'desc',
  2430 			'default'     => 'desc',
  2339 			'enum'               => array( 'asc', 'desc' ),
  2431 			'enum'        => array( 'asc', 'desc' ),
  2340 		);
  2432 		);
  2341 
  2433 
  2342 		$query_params['orderby'] = array(
  2434 		$query_params['orderby'] = array(
  2343 			'description'        => __( 'Sort collection by object attribute.' ),
  2435 			'description' => __( 'Sort collection by object attribute.' ),
  2344 			'type'               => 'string',
  2436 			'type'        => 'string',
  2345 			'default'            => 'date',
  2437 			'default'     => 'date',
  2346 			'enum'               => array(
  2438 			'enum'        => array(
  2347 				'author',
  2439 				'author',
  2348 				'date',
  2440 				'date',
  2349 				'id',
  2441 				'id',
  2350 				'include',
  2442 				'include',
  2351 				'modified',
  2443 				'modified',
  2362 		}
  2454 		}
  2363 
  2455 
  2364 		$post_type = get_post_type_object( $this->post_type );
  2456 		$post_type = get_post_type_object( $this->post_type );
  2365 
  2457 
  2366 		if ( $post_type->hierarchical || 'attachment' === $this->post_type ) {
  2458 		if ( $post_type->hierarchical || 'attachment' === $this->post_type ) {
  2367 			$query_params['parent'] = array(
  2459 			$query_params['parent']         = array(
  2368 				'description'       => __( 'Limit result set to items with particular parent IDs.' ),
  2460 				'description' => __( 'Limit result set to items with particular parent IDs.' ),
  2369 				'type'              => 'array',
  2461 				'type'        => 'array',
  2370 				'items'             => array(
  2462 				'items'       => array(
  2371 					'type'          => 'integer',
  2463 					'type' => 'integer',
  2372 				),
  2464 				),
  2373 				'default'           => array(),
  2465 				'default'     => array(),
  2374 			);
  2466 			);
  2375 			$query_params['parent_exclude'] = array(
  2467 			$query_params['parent_exclude'] = array(
  2376 				'description'       => __( 'Limit result set to all items except those of a particular parent ID.' ),
  2468 				'description' => __( 'Limit result set to all items except those of a particular parent ID.' ),
  2377 				'type'              => 'array',
  2469 				'type'        => 'array',
  2378 				'items'             => array(
  2470 				'items'       => array(
  2379 					'type'          => 'integer',
  2471 					'type' => 'integer',
  2380 				),
  2472 				),
  2381 				'default'           => array(),
  2473 				'default'     => array(),
  2382 			);
  2474 			);
  2383 		}
  2475 		}
  2384 
  2476 
  2385 		$query_params['slug'] = array(
  2477 		$query_params['slug'] = array(
  2386 			'description'       => __( 'Limit result set to posts with one or more specific slugs.' ),
  2478 			'description'       => __( 'Limit result set to posts with one or more specific slugs.' ),
  2387 			'type'              => 'array',
  2479 			'type'              => 'array',
  2388 			'items'             => array(
  2480 			'items'             => array(
  2389 				'type'          => 'string',
  2481 				'type' => 'string',
  2390 			),
  2482 			),
  2391 			'sanitize_callback' => 'wp_parse_slug_list',
  2483 			'sanitize_callback' => 'wp_parse_slug_list',
  2392 		);
  2484 		);
  2393 
  2485 
  2394 		$query_params['status'] = array(
  2486 		$query_params['status'] = array(
  2395 			'default'           => 'publish',
  2487 			'default'           => 'publish',
  2396 			'description'       => __( 'Limit result set to posts assigned one or more statuses.' ),
  2488 			'description'       => __( 'Limit result set to posts assigned one or more statuses.' ),
  2397 			'type'              => 'array',
  2489 			'type'              => 'array',
  2398 			'items'             => array(
  2490 			'items'             => array(
  2399 				'enum'          => array_merge( array_keys( get_post_stati() ), array( 'any' ) ),
  2491 				'enum' => array_merge( array_keys( get_post_stati() ), array( 'any' ) ),
  2400 				'type'          => 'string',
  2492 				'type' => 'string',
  2401 			),
  2493 			),
  2402 			'sanitize_callback' => array( $this, 'sanitize_post_statuses' ),
  2494 			'sanitize_callback' => array( $this, 'sanitize_post_statuses' ),
  2403 		);
  2495 		);
  2404 
  2496 
  2405 		$taxonomies = wp_list_filter( get_object_taxonomies( $this->post_type, 'objects' ), array( 'show_in_rest' => true ) );
  2497 		$taxonomies = wp_list_filter( get_object_taxonomies( $this->post_type, 'objects' ), array( 'show_in_rest' => true ) );
  2407 		foreach ( $taxonomies as $taxonomy ) {
  2499 		foreach ( $taxonomies as $taxonomy ) {
  2408 			$base = ! empty( $taxonomy->rest_base ) ? $taxonomy->rest_base : $taxonomy->name;
  2500 			$base = ! empty( $taxonomy->rest_base ) ? $taxonomy->rest_base : $taxonomy->name;
  2409 
  2501 
  2410 			$query_params[ $base ] = array(
  2502 			$query_params[ $base ] = array(
  2411 				/* translators: %s: taxonomy name */
  2503 				/* translators: %s: taxonomy name */
  2412 				'description'       => sprintf( __( 'Limit result set to all items that have the specified term assigned in the %s taxonomy.' ), $base ),
  2504 				'description' => sprintf( __( 'Limit result set to all items that have the specified term assigned in the %s taxonomy.' ), $base ),
  2413 				'type'              => 'array',
  2505 				'type'        => 'array',
  2414 				'items'             => array(
  2506 				'items'       => array(
  2415 					'type'          => 'integer',
  2507 					'type' => 'integer',
  2416 				),
  2508 				),
  2417 				'default'           => array(),
  2509 				'default'     => array(),
  2418 			);
  2510 			);
  2419 
  2511 
  2420 			$query_params[ $base . '_exclude' ] = array(
  2512 			$query_params[ $base . '_exclude' ] = array(
  2421 				/* translators: %s: taxonomy name */
  2513 				/* translators: %s: taxonomy name */
  2422 				'description' => sprintf( __( 'Limit result set to all items except those that have the specified term assigned in the %s taxonomy.' ), $base ),
  2514 				'description' => sprintf( __( 'Limit result set to all items except those that have the specified term assigned in the %s taxonomy.' ), $base ),
  2423 				'type'        => 'array',
  2515 				'type'        => 'array',
  2424 				'items'       => array(
  2516 				'items'       => array(
  2425 					'type'    => 'integer',
  2517 					'type' => 'integer',
  2426 				),
  2518 				),
  2427 				'default'           => array(),
  2519 				'default'     => array(),
  2428 			);
  2520 			);
  2429 		}
  2521 		}
  2430 
  2522 
  2431 		if ( 'post' === $this->post_type ) {
  2523 		if ( 'post' === $this->post_type ) {
  2432 			$query_params['sticky'] = array(
  2524 			$query_params['sticky'] = array(
  2433 				'description'       => __( 'Limit result set to items that are sticky.' ),
  2525 				'description' => __( 'Limit result set to items that are sticky.' ),
  2434 				'type'              => 'boolean',
  2526 				'type'        => 'boolean',
  2435 			);
  2527 			);
  2436 		}
  2528 		}
  2437 
  2529 
  2438 		/**
  2530 		/**
  2439 		 * Filter collection parameters for the posts controller.
  2531 		 * Filter collection parameters for the posts controller.
  2466 	 */
  2558 	 */
  2467 	public function sanitize_post_statuses( $statuses, $request, $parameter ) {
  2559 	public function sanitize_post_statuses( $statuses, $request, $parameter ) {
  2468 		$statuses = wp_parse_slug_list( $statuses );
  2560 		$statuses = wp_parse_slug_list( $statuses );
  2469 
  2561 
  2470 		// The default status is different in WP_REST_Attachments_Controller
  2562 		// The default status is different in WP_REST_Attachments_Controller
  2471 		$attributes = $request->get_attributes();
  2563 		$attributes     = $request->get_attributes();
  2472 		$default_status = $attributes['args']['status']['default'];
  2564 		$default_status = $attributes['args']['status']['default'];
  2473 
  2565 
  2474 		foreach ( $statuses as $status ) {
  2566 		foreach ( $statuses as $status ) {
  2475 			if ( $status === $default_status ) {
  2567 			if ( $status === $default_status ) {
  2476 				continue;
  2568 				continue;
  2477 			}
  2569 			}
  2478 
  2570 
  2479 			$post_type_obj = get_post_type_object( $this->post_type );
  2571 			$post_type_obj = get_post_type_object( $this->post_type );
  2480 
  2572 
  2481 			if ( current_user_can( $post_type_obj->cap->edit_posts ) ) {
  2573 			if ( current_user_can( $post_type_obj->cap->edit_posts ) || 'private' === $status && current_user_can( $post_type_obj->cap->read_private_posts ) ) {
  2482 				$result = rest_validate_request_arg( $status, $request, $parameter );
  2574 				$result = rest_validate_request_arg( $status, $request, $parameter );
  2483 				if ( is_wp_error( $result ) ) {
  2575 				if ( is_wp_error( $result ) ) {
  2484 					return $result;
  2576 					return $result;
  2485 				}
  2577 				}
  2486 			} else {
  2578 			} else {