wp/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php
changeset 21 48c4eec2b7e6
parent 19 3d72ae0968f4
child 22 8c2e4d02f4ef
equal deleted inserted replaced
20:7b1b88e27a20 21:48c4eec2b7e6
    95 
    95 
    96 		$schema        = $this->get_item_schema();
    96 		$schema        = $this->get_item_schema();
    97 		$get_item_args = array(
    97 		$get_item_args = array(
    98 			'context' => $this->get_context_param( array( 'default' => 'view' ) ),
    98 			'context' => $this->get_context_param( array( 'default' => 'view' ) ),
    99 		);
    99 		);
       
   100 		if ( isset( $schema['properties']['excerpt'] ) ) {
       
   101 			$get_item_args['excerpt_length'] = array(
       
   102 				'description' => __( 'Override the default excerpt length.' ),
       
   103 				'type'        => 'integer',
       
   104 			);
       
   105 		}
   100 		if ( isset( $schema['properties']['password'] ) ) {
   106 		if ( isset( $schema['properties']['password'] ) ) {
   101 			$get_item_args['password'] = array(
   107 			$get_item_args['password'] = array(
   102 				'description' => __( 'The password for the post if it is password protected.' ),
   108 				'description' => __( 'The password for the post if it is password protected.' ),
   103 				'type'        => 'string',
   109 				'type'        => 'string',
   104 			);
   110 			);
   165 
   171 
   166 		return true;
   172 		return true;
   167 	}
   173 	}
   168 
   174 
   169 	/**
   175 	/**
   170 	 * Override the result of the post password check for REST requested posts.
   176 	 * Overrides the result of the post password check for REST requested posts.
   171 	 *
   177 	 *
   172 	 * Allow users to read the content of password protected posts if they have
   178 	 * Allow users to read the content of password protected posts if they have
   173 	 * previously passed a permission check or if they have the `edit_post` capability
   179 	 * previously passed a permission check or if they have the `edit_post` capability
   174 	 * for the post being checked.
   180 	 * for the post being checked.
   175 	 *
   181 	 *
   247 			'orderby'        => 'orderby',
   253 			'orderby'        => 'orderby',
   248 			'page'           => 'paged',
   254 			'page'           => 'paged',
   249 			'parent'         => 'post_parent__in',
   255 			'parent'         => 'post_parent__in',
   250 			'parent_exclude' => 'post_parent__not_in',
   256 			'parent_exclude' => 'post_parent__not_in',
   251 			'search'         => 's',
   257 			'search'         => 's',
       
   258 			'search_columns' => 'search_columns',
   252 			'slug'           => 'post_name__in',
   259 			'slug'           => 'post_name__in',
   253 			'status'         => 'post_status',
   260 			'status'         => 'post_status',
   254 		);
   261 		);
   255 
   262 
   256 		/*
   263 		/*
   367 			add_filter( 'post_password_required', array( $this, 'check_password_required' ), 10, 2 );
   374 			add_filter( 'post_password_required', array( $this, 'check_password_required' ), 10, 2 );
   368 		}
   375 		}
   369 
   376 
   370 		$posts = array();
   377 		$posts = array();
   371 
   378 
       
   379 		update_post_author_caches( $query_result );
       
   380 		update_post_parent_caches( $query_result );
       
   381 
       
   382 		if ( post_type_supports( $this->post_type, 'thumbnail' ) ) {
       
   383 			update_post_thumbnail_cache( $posts_query );
       
   384 		}
       
   385 
   372 		foreach ( $query_result as $post ) {
   386 		foreach ( $query_result as $post ) {
   373 			if ( ! $this->check_read_permission( $post ) ) {
   387 			if ( ! $this->check_read_permission( $post ) ) {
   374 				continue;
   388 				continue;
   375 			}
   389 			}
   376 
   390 
   384 		}
   398 		}
   385 
   399 
   386 		$page        = (int) $query_args['paged'];
   400 		$page        = (int) $query_args['paged'];
   387 		$total_posts = $posts_query->found_posts;
   401 		$total_posts = $posts_query->found_posts;
   388 
   402 
   389 		if ( $total_posts < 1 ) {
   403 		if ( $total_posts < 1 && $page > 1 ) {
   390 			// Out-of-bounds, run the query again without LIMIT for total count.
   404 			// Out-of-bounds, run the query again without LIMIT for total count.
   391 			unset( $query_args['paged'] );
   405 			unset( $query_args['paged'] );
   392 
   406 
   393 			$count_query = new WP_Query();
   407 			$count_query = new WP_Query();
   394 			$count_query->query( $query_args );
   408 			$count_query->query( $query_args );
   395 			$total_posts = $count_query->found_posts;
   409 			$total_posts = $count_query->found_posts;
   396 		}
   410 		}
   397 
   411 
   398 		$max_pages = ceil( $total_posts / (int) $posts_query->query_vars['posts_per_page'] );
   412 		$max_pages = (int) ceil( $total_posts / (int) $posts_query->query_vars['posts_per_page'] );
   399 
   413 
   400 		if ( $page > $max_pages && $total_posts > 0 ) {
   414 		if ( $page > $max_pages && $total_posts > 0 ) {
   401 			return new WP_Error(
   415 			return new WP_Error(
   402 				'rest_post_invalid_page_number',
   416 				'rest_post_invalid_page_number',
   403 				__( 'The page number requested is larger than the number of pages available.' ),
   417 				__( 'The page number requested is larger than the number of pages available.' ),
   409 
   423 
   410 		$response->header( 'X-WP-Total', (int) $total_posts );
   424 		$response->header( 'X-WP-Total', (int) $total_posts );
   411 		$response->header( 'X-WP-TotalPages', (int) $max_pages );
   425 		$response->header( 'X-WP-TotalPages', (int) $max_pages );
   412 
   426 
   413 		$request_params = $request->get_query_params();
   427 		$request_params = $request->get_query_params();
   414 		$base           = add_query_arg( urlencode_deep( $request_params ), rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ) );
   428 		$collection_url = rest_url( rest_get_route_for_post_type_items( $this->post_type ) );
       
   429 		$base           = add_query_arg( urlencode_deep( $request_params ), $collection_url );
   415 
   430 
   416 		if ( $page > 1 ) {
   431 		if ( $page > 1 ) {
   417 			$prev_page = $page - 1;
   432 			$prev_page = $page - 1;
   418 
   433 
   419 			if ( $prev_page > $max_pages ) {
   434 			if ( $prev_page > $max_pages ) {
   432 
   447 
   433 		return $response;
   448 		return $response;
   434 	}
   449 	}
   435 
   450 
   436 	/**
   451 	/**
   437 	 * Get the post, if the ID is valid.
   452 	 * Gets the post, if the ID is valid.
   438 	 *
   453 	 *
   439 	 * @since 4.7.2
   454 	 * @since 4.7.2
   440 	 *
   455 	 *
   441 	 * @param int $id Supplied ID.
   456 	 * @param int $id Supplied ID.
   442 	 * @return WP_Post|WP_Error Post object if ID is valid, WP_Error otherwise.
   457 	 * @return WP_Post|WP_Error Post object if ID is valid, WP_Error otherwise.
   464 	 * Checks if a given request has access to read a post.
   479 	 * Checks if a given request has access to read a post.
   465 	 *
   480 	 *
   466 	 * @since 4.7.0
   481 	 * @since 4.7.0
   467 	 *
   482 	 *
   468 	 * @param WP_REST_Request $request Full details about the request.
   483 	 * @param WP_REST_Request $request Full details about the request.
   469 	 * @return true|WP_Error True if the request has read access for the item, WP_Error object otherwise.
   484 	 * @return bool|WP_Error True if the request has read access for the item, WP_Error object or false otherwise.
   470 	 */
   485 	 */
   471 	public function get_item_permissions_check( $request ) {
   486 	public function get_item_permissions_check( $request ) {
   472 		$post = $this->get_post( $request['id'] );
   487 		$post = $this->get_post( $request['id'] );
   473 		if ( is_wp_error( $post ) ) {
   488 		if ( is_wp_error( $post ) ) {
   474 			return $post;
   489 			return $post;
   644 			return $prepared_post;
   659 			return $prepared_post;
   645 		}
   660 		}
   646 
   661 
   647 		$prepared_post->post_type = $this->post_type;
   662 		$prepared_post->post_type = $this->post_type;
   648 
   663 
       
   664 		if ( ! empty( $prepared_post->post_name )
       
   665 			&& ! empty( $prepared_post->post_status )
       
   666 			&& in_array( $prepared_post->post_status, array( 'draft', 'pending' ), true )
       
   667 		) {
       
   668 			/*
       
   669 			 * `wp_unique_post_slug()` returns the same slug for 'draft' or 'pending' posts.
       
   670 			 *
       
   671 			 * To ensure that a unique slug is generated, pass the post data with the 'publish' status.
       
   672 			 */
       
   673 			$prepared_post->post_name = wp_unique_post_slug(
       
   674 				$prepared_post->post_name,
       
   675 				$prepared_post->id,
       
   676 				'publish',
       
   677 				$prepared_post->post_type,
       
   678 				$prepared_post->post_parent
       
   679 			);
       
   680 		}
       
   681 
   649 		$post_id = wp_insert_post( wp_slash( (array) $prepared_post ), true, false );
   682 		$post_id = wp_insert_post( wp_slash( (array) $prepared_post ), true, false );
   650 
   683 
   651 		if ( is_wp_error( $post_id ) ) {
   684 		if ( is_wp_error( $post_id ) ) {
   652 
   685 
   653 			if ( 'db_insert_error' === $post_id->get_error_code() ) {
   686 			if ( 'db_insert_error' === $post_id->get_error_code() ) {
   748 
   781 
   749 		$response = $this->prepare_item_for_response( $post, $request );
   782 		$response = $this->prepare_item_for_response( $post, $request );
   750 		$response = rest_ensure_response( $response );
   783 		$response = rest_ensure_response( $response );
   751 
   784 
   752 		$response->set_status( 201 );
   785 		$response->set_status( 201 );
   753 		$response->header( 'Location', rest_url( sprintf( '%s/%s/%d', $this->namespace, $this->rest_base, $post_id ) ) );
   786 		$response->header( 'Location', rest_url( rest_get_route_for_post( $post ) ) );
   754 
   787 
   755 		return $response;
   788 		return $response;
   756 	}
   789 	}
   757 
   790 
   758 	/**
   791 	/**
   823 		$post_before = get_post( $request['id'] );
   856 		$post_before = get_post( $request['id'] );
   824 		$post        = $this->prepare_item_for_database( $request );
   857 		$post        = $this->prepare_item_for_database( $request );
   825 
   858 
   826 		if ( is_wp_error( $post ) ) {
   859 		if ( is_wp_error( $post ) ) {
   827 			return $post;
   860 			return $post;
       
   861 		}
       
   862 
       
   863 		if ( ! empty( $post->post_status ) ) {
       
   864 			$post_status = $post->post_status;
       
   865 		} else {
       
   866 			$post_status = $post_before->post_status;
       
   867 		}
       
   868 
       
   869 		/*
       
   870 		 * `wp_unique_post_slug()` returns the same slug for 'draft' or 'pending' posts.
       
   871 		 *
       
   872 		 * To ensure that a unique slug is generated, pass the post data with the 'publish' status.
       
   873 		 */
       
   874 		if ( ! empty( $post->post_name ) && in_array( $post_status, array( 'draft', 'pending' ), true ) ) {
       
   875 			$post_parent     = ! empty( $post->post_parent ) ? $post->post_parent : 0;
       
   876 			$post->post_name = wp_unique_post_slug(
       
   877 				$post->post_name,
       
   878 				$post->ID,
       
   879 				'publish',
       
   880 				$post->post_type,
       
   881 				$post_parent
       
   882 			);
   828 		}
   883 		}
   829 
   884 
   830 		// Convert the post object to an array, otherwise wp_update_post() will expect non-escaped input.
   885 		// Convert the post object to an array, otherwise wp_update_post() will expect non-escaped input.
   831 		$post_id = wp_update_post( wp_slash( (array) $post ), true, false );
   886 		$post_id = wp_update_post( wp_slash( (array) $post ), true, false );
   832 
   887 
  1012 					__( 'The post has already been deleted.' ),
  1067 					__( 'The post has already been deleted.' ),
  1013 					array( 'status' => 410 )
  1068 					array( 'status' => 410 )
  1014 				);
  1069 				);
  1015 			}
  1070 			}
  1016 
  1071 
  1017 			// (Note that internally this falls through to `wp_delete_post()`
  1072 			/*
  1018 			// if the Trash is disabled.)
  1073 			 * (Note that internally this falls through to `wp_delete_post()`
       
  1074 			 * if the Trash is disabled.)
       
  1075 			 */
  1019 			$result   = wp_trash_post( $id );
  1076 			$result   = wp_trash_post( $id );
  1020 			$post     = get_post( $id );
  1077 			$post     = get_post( $id );
  1021 			$response = $this->prepare_item_for_response( $post, $request );
  1078 			$response = $this->prepare_item_for_response( $post, $request );
  1022 		}
  1079 		}
  1023 
  1080 
  1218 				list( $prepared_post->post_date, $prepared_post->post_date_gmt ) = $date_data;
  1275 				list( $prepared_post->post_date, $prepared_post->post_date_gmt ) = $date_data;
  1219 				$prepared_post->edit_date                                        = true;
  1276 				$prepared_post->edit_date                                        = true;
  1220 			}
  1277 			}
  1221 		}
  1278 		}
  1222 
  1279 
  1223 		// Sending a null date or date_gmt value resets date and date_gmt to their
  1280 		/*
  1224 		// default values (`0000-00-00 00:00:00`).
  1281 		 * Sending a null date or date_gmt value resets date and date_gmt to their
       
  1282 		 * default values (`0000-00-00 00:00:00`).
       
  1283 		 */
  1225 		if (
  1284 		if (
  1226 			( ! empty( $schema['properties']['date_gmt'] ) && $request->has_param( 'date_gmt' ) && null === $request['date_gmt'] ) ||
  1285 			( ! empty( $schema['properties']['date_gmt'] ) && $request->has_param( 'date_gmt' ) && null === $request['date_gmt'] ) ||
  1227 			( ! empty( $schema['properties']['date'] ) && $request->has_param( 'date' ) && null === $request['date'] )
  1286 			( ! empty( $schema['properties']['date'] ) && $request->has_param( 'date' ) && null === $request['date'] )
  1228 		) {
  1287 		) {
  1229 			$prepared_post->post_date_gmt = null;
  1288 			$prepared_post->post_date_gmt = null;
  1342 		 * @param stdClass        $prepared_post An object representing a single post prepared
  1401 		 * @param stdClass        $prepared_post An object representing a single post prepared
  1343 		 *                                       for inserting or updating the database.
  1402 		 *                                       for inserting or updating the database.
  1344 		 * @param WP_REST_Request $request       Request object.
  1403 		 * @param WP_REST_Request $request       Request object.
  1345 		 */
  1404 		 */
  1346 		return apply_filters( "rest_pre_insert_{$this->post_type}", $prepared_post, $request );
  1405 		return apply_filters( "rest_pre_insert_{$this->post_type}", $prepared_post, $request );
  1347 
       
  1348 	}
  1406 	}
  1349 
  1407 
  1350 	/**
  1408 	/**
  1351 	 * Checks whether the status is valid for the given post.
  1409 	 * Checks whether the status is valid for the given post.
  1352 	 *
  1410 	 *
  1441 				);
  1499 				);
  1442 			}
  1500 			}
  1443 		} else {
  1501 		} else {
  1444 			return delete_post_thumbnail( $post_id );
  1502 			return delete_post_thumbnail( $post_id );
  1445 		}
  1503 		}
  1446 
  1504 	}
  1447 	}
  1505 
  1448 
  1506 	/**
  1449 	/**
  1507 	 * Checks whether the template is valid for the given post.
  1450 	 * Check whether the template is valid for the given post.
       
  1451 	 *
  1508 	 *
  1452 	 * @since 4.9.0
  1509 	 * @since 4.9.0
  1453 	 *
  1510 	 *
  1454 	 * @param string          $template Page template filename.
  1511 	 * @param string          $template Page template filename.
  1455 	 * @param WP_REST_Request $request  Request.
  1512 	 * @param WP_REST_Request $request  Request.
  1456 	 * @return bool|WP_Error True if template is still valid or if the same as existing value, or false if template not supported.
  1513 	 * @return true|WP_Error True if template is still valid or if the same as existing value, or a WP_Error if template not supported.
  1457 	 */
  1514 	 */
  1458 	public function check_template( $template, $request ) {
  1515 	public function check_template( $template, $request ) {
  1459 
  1516 
  1460 		if ( ! $template ) {
  1517 		if ( ! $template ) {
  1461 			return true;
  1518 			return true;
  1689 	 * Prepares a single post output for response.
  1746 	 * Prepares a single post output for response.
  1690 	 *
  1747 	 *
  1691 	 * @since 4.7.0
  1748 	 * @since 4.7.0
  1692 	 * @since 5.9.0 Renamed `$post` to `$item` to match parent class for PHP 8 named parameter support.
  1749 	 * @since 5.9.0 Renamed `$post` to `$item` to match parent class for PHP 8 named parameter support.
  1693 	 *
  1750 	 *
       
  1751 	 * @global WP_Post $post Global post object.
       
  1752 	 *
  1694 	 * @param WP_Post         $item    Post object.
  1753 	 * @param WP_Post         $item    Post object.
  1695 	 * @param WP_REST_Request $request Request object.
  1754 	 * @param WP_REST_Request $request Request object.
  1696 	 * @return WP_REST_Response Response object.
  1755 	 * @return WP_REST_Response Response object.
  1697 	 */
  1756 	 */
  1698 	public function prepare_item_for_response( $item, $request ) {
  1757 	public function prepare_item_for_response( $item, $request ) {
  1699 		// Restores the more descriptive, specific name for use within this method.
  1758 		// Restores the more descriptive, specific name for use within this method.
  1700 		$post            = $item;
  1759 		$post = $item;
       
  1760 
  1701 		$GLOBALS['post'] = $post;
  1761 		$GLOBALS['post'] = $post;
  1702 
  1762 
  1703 		setup_postdata( $post );
  1763 		setup_postdata( $post );
  1704 
  1764 
  1705 		$fields = $this->get_fields_for_response( $request );
  1765 		$fields = $this->get_fields_for_response( $request );
  1747 			 * For drafts, `post_modified_gmt` may not be set (see `post_date_gmt` comments
  1807 			 * For drafts, `post_modified_gmt` may not be set (see `post_date_gmt` comments
  1748 			 * above). In this case, shim the value based on the `post_modified` field
  1808 			 * above). In this case, shim the value based on the `post_modified` field
  1749 			 * with the site's timezone offset applied.
  1809 			 * with the site's timezone offset applied.
  1750 			 */
  1810 			 */
  1751 			if ( '0000-00-00 00:00:00' === $post->post_modified_gmt ) {
  1811 			if ( '0000-00-00 00:00:00' === $post->post_modified_gmt ) {
  1752 				$post_modified_gmt = gmdate( 'Y-m-d H:i:s', strtotime( $post->post_modified ) - ( get_option( 'gmt_offset' ) * 3600 ) );
  1812 				$post_modified_gmt = gmdate( 'Y-m-d H:i:s', strtotime( $post->post_modified ) - ( get_option( 'gmt_offset' ) * HOUR_IN_SECONDS ) );
  1753 			} else {
  1813 			} else {
  1754 				$post_modified_gmt = $post->post_modified_gmt;
  1814 				$post_modified_gmt = $post->post_modified_gmt;
  1755 			}
  1815 			}
  1756 			$data['modified_gmt'] = $this->prepare_date_response( $post_modified_gmt );
  1816 			$data['modified_gmt'] = $this->prepare_date_response( $post_modified_gmt );
  1757 		}
  1817 		}
  1816 		if ( rest_is_field_included( 'content.block_version', $fields ) ) {
  1876 		if ( rest_is_field_included( 'content.block_version', $fields ) ) {
  1817 			$data['content']['block_version'] = block_version( $post->post_content );
  1877 			$data['content']['block_version'] = block_version( $post->post_content );
  1818 		}
  1878 		}
  1819 
  1879 
  1820 		if ( rest_is_field_included( 'excerpt', $fields ) ) {
  1880 		if ( rest_is_field_included( 'excerpt', $fields ) ) {
       
  1881 			if ( isset( $request['excerpt_length'] ) ) {
       
  1882 				$excerpt_length          = $request['excerpt_length'];
       
  1883 				$override_excerpt_length = static function () use ( $excerpt_length ) {
       
  1884 					return $excerpt_length;
       
  1885 				};
       
  1886 
       
  1887 				add_filter(
       
  1888 					'excerpt_length',
       
  1889 					$override_excerpt_length,
       
  1890 					20
       
  1891 				);
       
  1892 			}
       
  1893 
  1821 			/** This filter is documented in wp-includes/post-template.php */
  1894 			/** This filter is documented in wp-includes/post-template.php */
  1822 			$excerpt = apply_filters( 'get_the_excerpt', $post->post_excerpt, $post );
  1895 			$excerpt = apply_filters( 'get_the_excerpt', $post->post_excerpt, $post );
  1823 
  1896 
  1824 			/** This filter is documented in wp-includes/post-template.php */
  1897 			/** This filter is documented in wp-includes/post-template.php */
  1825 			$excerpt = apply_filters( 'the_excerpt', $excerpt );
  1898 			$excerpt = apply_filters( 'the_excerpt', $excerpt );
  1827 			$data['excerpt'] = array(
  1900 			$data['excerpt'] = array(
  1828 				'raw'       => $post->post_excerpt,
  1901 				'raw'       => $post->post_excerpt,
  1829 				'rendered'  => post_password_required( $post ) ? '' : $excerpt,
  1902 				'rendered'  => post_password_required( $post ) ? '' : $excerpt,
  1830 				'protected' => (bool) $post->post_password,
  1903 				'protected' => (bool) $post->post_password,
  1831 			);
  1904 			);
       
  1905 
       
  1906 			if ( isset( $override_excerpt_length ) ) {
       
  1907 				remove_filter(
       
  1908 					'excerpt_length',
       
  1909 					$override_excerpt_length,
       
  1910 					20
       
  1911 				);
       
  1912 			}
  1832 		}
  1913 		}
  1833 
  1914 
  1834 		if ( $has_password_filter ) {
  1915 		if ( $has_password_filter ) {
  1835 			// Reset filter.
  1916 			// Reset filter.
  1836 			remove_filter( 'post_password_required', array( $this, 'check_password_required' ) );
  1917 			remove_filter( 'post_password_required', array( $this, 'check_password_required' ) );
  1915 
  1996 
  1916 				if ( $generated_slug_requested ) {
  1997 				if ( $generated_slug_requested ) {
  1917 					$data['generated_slug'] = $sample_permalink[1];
  1998 					$data['generated_slug'] = $sample_permalink[1];
  1918 				}
  1999 				}
  1919 			}
  2000 			}
       
  2001 
       
  2002 			if ( rest_is_field_included( 'class_list', $fields ) ) {
       
  2003 				$data['class_list'] = get_post_class( array(), $post->ID );
       
  2004 			}
  1920 		}
  2005 		}
  1921 
  2006 
  1922 		$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
  2007 		$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
  1923 		$data    = $this->add_additional_fields_to_object( $data, $request );
  2008 		$data    = $this->add_additional_fields_to_object( $data, $request );
  1924 		$data    = $this->filter_response_by_context( $data, $context );
  2009 		$data    = $this->filter_response_by_context( $data, $context );
  1925 
  2010 
  1926 		// Wrap the data in a response object.
  2011 		// Wrap the data in a response object.
  1927 		$response = rest_ensure_response( $data );
  2012 		$response = rest_ensure_response( $data );
  1928 
  2013 
  1929 		$links = $this->prepare_links( $post );
  2014 		if ( rest_is_field_included( '_links', $fields ) || rest_is_field_included( '_embedded', $fields ) ) {
  1930 		$response->add_links( $links );
  2015 			$links = $this->prepare_links( $post );
  1931 
  2016 			$response->add_links( $links );
  1932 		if ( ! empty( $links['self']['href'] ) ) {
  2017 
  1933 			$actions = $this->get_available_actions( $post, $request );
  2018 			if ( ! empty( $links['self']['href'] ) ) {
  1934 
  2019 				$actions = $this->get_available_actions( $post, $request );
  1935 			$self = $links['self']['href'];
  2020 
  1936 
  2021 				$self = $links['self']['href'];
  1937 			foreach ( $actions as $rel ) {
  2022 
  1938 				$response->add_link( $rel, $self );
  2023 				foreach ( $actions as $rel ) {
       
  2024 					$response->add_link( $rel, $self );
       
  2025 				}
  1939 			}
  2026 			}
  1940 		}
  2027 		}
  1941 
  2028 
  1942 		/**
  2029 		/**
  1943 		 * Filters the post data for a REST API response.
  2030 		 * Filters the post data for a REST API response.
  1981 	 *
  2068 	 *
  1982 	 * @param WP_Post $post Post object.
  2069 	 * @param WP_Post $post Post object.
  1983 	 * @return array Links for the given post.
  2070 	 * @return array Links for the given post.
  1984 	 */
  2071 	 */
  1985 	protected function prepare_links( $post ) {
  2072 	protected function prepare_links( $post ) {
  1986 		$base = sprintf( '%s/%s', $this->namespace, $this->rest_base );
       
  1987 
       
  1988 		// Entity meta.
  2073 		// Entity meta.
  1989 		$links = array(
  2074 		$links = array(
  1990 			'self'       => array(
  2075 			'self'       => array(
  1991 				'href' => rest_url( trailingslashit( $base ) . $post->ID ),
  2076 				'href' => rest_url( rest_get_route_for_post( $post->ID ) ),
  1992 			),
  2077 			),
  1993 			'collection' => array(
  2078 			'collection' => array(
  1994 				'href' => rest_url( $base ),
  2079 				'href' => rest_url( rest_get_route_for_post_type_items( $this->post_type ) ),
  1995 			),
  2080 			),
  1996 			'about'      => array(
  2081 			'about'      => array(
  1997 				'href' => rest_url( 'wp/v2/types/' . $this->post_type ),
  2082 				'href' => rest_url( 'wp/v2/types/' . $this->post_type ),
  1998 			),
  2083 			),
  1999 		);
  2084 		);
  2015 				'embeddable' => true,
  2100 				'embeddable' => true,
  2016 			);
  2101 			);
  2017 		}
  2102 		}
  2018 
  2103 
  2019 		if ( in_array( $post->post_type, array( 'post', 'page' ), true ) || post_type_supports( $post->post_type, 'revisions' ) ) {
  2104 		if ( in_array( $post->post_type, array( 'post', 'page' ), true ) || post_type_supports( $post->post_type, 'revisions' ) ) {
  2020 			$revisions       = wp_get_post_revisions( $post->ID, array( 'fields' => 'ids' ) );
  2105 			$revisions       = wp_get_latest_revision_id_and_total_count( $post->ID );
  2021 			$revisions_count = count( $revisions );
  2106 			$revisions_count = ! is_wp_error( $revisions ) ? $revisions['count'] : 0;
       
  2107 			$revisions_base  = sprintf( '/%s/%s/%d/revisions', $this->namespace, $this->rest_base, $post->ID );
  2022 
  2108 
  2023 			$links['version-history'] = array(
  2109 			$links['version-history'] = array(
  2024 				'href'  => rest_url( trailingslashit( $base ) . $post->ID . '/revisions' ),
  2110 				'href'  => rest_url( $revisions_base ),
  2025 				'count' => $revisions_count,
  2111 				'count' => $revisions_count,
  2026 			);
  2112 			);
  2027 
  2113 
  2028 			if ( $revisions_count > 0 ) {
  2114 			if ( $revisions_count > 0 ) {
  2029 				$last_revision = array_shift( $revisions );
       
  2030 
       
  2031 				$links['predecessor-version'] = array(
  2115 				$links['predecessor-version'] = array(
  2032 					'href' => rest_url( trailingslashit( $base ) . $post->ID . '/revisions/' . $last_revision ),
  2116 					'href' => rest_url( $revisions_base . '/' . $revisions['latest_id'] ),
  2033 					'id'   => $last_revision,
  2117 					'id'   => $revisions['latest_id'],
  2034 				);
  2118 				);
  2035 			}
  2119 			}
  2036 		}
  2120 		}
  2037 
  2121 
  2038 		$post_type_obj = get_post_type_object( $post->post_type );
  2122 		$post_type_obj = get_post_type_object( $post->post_type );
  2092 
  2176 
  2093 		return $links;
  2177 		return $links;
  2094 	}
  2178 	}
  2095 
  2179 
  2096 	/**
  2180 	/**
  2097 	 * Get the link relations available for the post and current user.
  2181 	 * Gets the link relations available for the post and current user.
  2098 	 *
  2182 	 *
  2099 	 * @since 4.9.8
  2183 	 * @since 4.9.8
  2100 	 *
  2184 	 *
  2101 	 * @param WP_Post         $post    Post object.
  2185 	 * @param WP_Post         $post    Post object.
  2102 	 * @param WP_REST_Request $request Request object.
  2186 	 * @param WP_REST_Request $request Request object.
  2271 				'description' => __( 'Slug automatically generated from the post title.' ),
  2355 				'description' => __( 'Slug automatically generated from the post title.' ),
  2272 				'type'        => 'string',
  2356 				'type'        => 'string',
  2273 				'context'     => array( 'edit' ),
  2357 				'context'     => array( 'edit' ),
  2274 				'readonly'    => true,
  2358 				'readonly'    => true,
  2275 			);
  2359 			);
       
  2360 
       
  2361 			$schema['properties']['class_list'] = array(
       
  2362 				'description' => __( 'An array of the class names for the post container element.' ),
       
  2363 				'type'        => 'array',
       
  2364 				'context'     => array( 'view', 'edit' ),
       
  2365 				'readonly'    => true,
       
  2366 				'items'       => array(
       
  2367 					'type' => 'string',
       
  2368 				),
       
  2369 			);
  2276 		}
  2370 		}
  2277 
  2371 
  2278 		if ( $post_type_obj->hierarchical ) {
  2372 		if ( $post_type_obj->hierarchical ) {
  2279 			$schema['properties']['parent'] = array(
  2373 			$schema['properties']['parent'] = array(
  2280 				'description' => __( 'The ID for the parent of the post.' ),
  2374 				'description' => __( 'The ID for the parent of the post.' ),
  2322 				'title',
  2416 				'title',
  2323 				'author',
  2417 				'author',
  2324 				'comments',
  2418 				'comments',
  2325 				'revisions',
  2419 				'revisions',
  2326 				'custom-fields',
  2420 				'custom-fields',
       
  2421 				'thumbnail',
  2327 			),
  2422 			),
  2328 		);
  2423 		);
  2329 
  2424 
  2330 		foreach ( $post_type_attributes as $attribute ) {
  2425 		foreach ( $post_type_attributes as $attribute ) {
  2331 			if ( isset( $fixed_schemas[ $this->post_type ] ) && ! in_array( $attribute, $fixed_schemas[ $this->post_type ], true ) ) {
  2426 			if ( isset( $fixed_schemas[ $this->post_type ] ) && ! in_array( $attribute, $fixed_schemas[ $this->post_type ], true ) ) {
  2580 
  2675 
  2581 		return $this->add_additional_fields_schema( $this->schema );
  2676 		return $this->add_additional_fields_schema( $this->schema );
  2582 	}
  2677 	}
  2583 
  2678 
  2584 	/**
  2679 	/**
  2585 	 * Retrieve Link Description Objects that should be added to the Schema for the posts collection.
  2680 	 * Retrieves Link Description Objects that should be added to the Schema for the posts collection.
  2586 	 *
  2681 	 *
  2587 	 * @since 4.9.8
  2682 	 * @since 4.9.8
  2588 	 *
  2683 	 *
  2589 	 * @return array
  2684 	 * @return array
  2590 	 */
  2685 	 */
  2842 				),
  2937 				),
  2843 				'default'     => array(),
  2938 				'default'     => array(),
  2844 			);
  2939 			);
  2845 		}
  2940 		}
  2846 
  2941 
  2847 		$query_params['slug'] = array(
  2942 		$query_params['search_columns'] = array(
  2848 			'description'       => __( 'Limit result set to posts with one or more specific slugs.' ),
  2943 			'default'     => array(),
  2849 			'type'              => 'array',
  2944 			'description' => __( 'Array of column names to be searched.' ),
  2850 			'items'             => array(
  2945 			'type'        => 'array',
       
  2946 			'items'       => array(
       
  2947 				'enum' => array( 'post_title', 'post_content', 'post_excerpt' ),
  2851 				'type' => 'string',
  2948 				'type' => 'string',
  2852 			),
  2949 			),
  2853 			'sanitize_callback' => 'wp_parse_slug_list',
  2950 		);
       
  2951 
       
  2952 		$query_params['slug'] = array(
       
  2953 			'description' => __( 'Limit result set to posts with one or more specific slugs.' ),
       
  2954 			'type'        => 'array',
       
  2955 			'items'       => array(
       
  2956 				'type' => 'string',
       
  2957 			),
  2854 		);
  2958 		);
  2855 
  2959 
  2856 		$query_params['status'] = array(
  2960 		$query_params['status'] = array(
  2857 			'default'           => 'publish',
  2961 			'default'           => 'publish',
  2858 			'description'       => __( 'Limit result set to posts assigned one or more statuses.' ),
  2962 			'description'       => __( 'Limit result set to posts assigned one or more statuses.' ),