--- a/wp/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php Tue Oct 22 16:11:46 2019 +0200
+++ b/wp/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php Tue Dec 15 13:49:49 2020 +0100
@@ -15,7 +15,6 @@
* @see WP_REST_Controller
*/
class WP_REST_Posts_Controller extends WP_REST_Controller {
-
/**
* Post type.
*
@@ -117,7 +116,7 @@
'force' => array(
'type' => 'boolean',
'default' => false,
- 'description' => __( 'Whether to bypass trash and force deletion.' ),
+ 'description' => __( 'Whether to bypass Trash and force deletion.' ),
),
),
),
@@ -131,7 +130,7 @@
*
* @since 4.7.0
*
- * @param WP_REST_Request $request Full details about the request.
+ * @param WP_REST_Request $request Full details about the request.
* @return true|WP_Error True if the request has read access, WP_Error object otherwise.
*/
public function get_items_permissions_check( $request ) {
@@ -139,7 +138,11 @@
$post_type = get_post_type_object( $this->post_type );
if ( 'edit' === $request['context'] && ! current_user_can( $post_type->cap->edit_posts ) ) {
- return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you are not allowed to edit posts in this post type.' ), array( 'status' => rest_authorization_required_code() ) );
+ return new WP_Error(
+ 'rest_forbidden_context',
+ __( 'Sorry, you are not allowed to edit posts in this post type.' ),
+ array( 'status' => rest_authorization_required_code() )
+ );
}
return true;
@@ -157,12 +160,20 @@
// Ensure a search string is set in case the orderby is set to 'relevance'.
if ( ! empty( $request['orderby'] ) && 'relevance' === $request['orderby'] && empty( $request['search'] ) ) {
- return new WP_Error( 'rest_no_search_term_defined', __( 'You need to define a search term to order by relevance.' ), array( 'status' => 400 ) );
+ return new WP_Error(
+ 'rest_no_search_term_defined',
+ __( 'You need to define a search term to order by relevance.' ),
+ array( 'status' => 400 )
+ );
}
// Ensure an include parameter is set in case the orderby is set to 'include'.
if ( ! empty( $request['orderby'] ) && 'include' === $request['orderby'] && empty( $request['include'] ) ) {
- return new WP_Error( 'rest_orderby_include_missing_include', __( 'You need to define an include parameter to order by include.' ), array( 'status' => 400 ) );
+ return new WP_Error(
+ 'rest_orderby_include_missing_include',
+ __( 'You need to define an include parameter to order by include.' ),
+ array( 'status' => 400 )
+ );
}
// Retrieve the list of registered collection query parameters.
@@ -234,7 +245,7 @@
$args['post__in'] = $args['post__in'] ? array_intersect( $sticky_posts, $args['post__in'] ) : $sticky_posts;
/*
- * If we intersected, but there are no post ids in common,
+ * If we intersected, but there are no post IDs in common,
* WP_Query won't return "no posts" for post__in = array()
* so we have to fake it a bit.
*/
@@ -271,6 +282,10 @@
$taxonomies = wp_list_filter( get_object_taxonomies( $this->post_type, 'objects' ), array( 'show_in_rest' => true ) );
+ if ( ! empty( $request['tax_relation'] ) ) {
+ $query_args['tax_query'] = array( 'relation' => $request['tax_relation'] );
+ }
+
foreach ( $taxonomies as $taxonomy ) {
$base = ! empty( $taxonomy->rest_base ) ? $taxonomy->rest_base : $taxonomy->name;
$tax_exclude = $base . '_exclude';
@@ -334,7 +349,11 @@
$max_pages = ceil( $total_posts / (int) $posts_query->query_vars['posts_per_page'] );
if ( $page > $max_pages && $total_posts > 0 ) {
- return new WP_Error( 'rest_post_invalid_page_number', __( 'The page number requested is larger than the number of pages available.' ), array( 'status' => 400 ) );
+ return new WP_Error(
+ 'rest_post_invalid_page_number',
+ __( 'The page number requested is larger than the number of pages available.' ),
+ array( 'status' => 400 )
+ );
}
$response = rest_ensure_response( $posts );
@@ -374,7 +393,12 @@
* @return WP_Post|WP_Error Post object if ID is valid, WP_Error otherwise.
*/
protected function get_post( $id ) {
- $error = new WP_Error( 'rest_post_invalid_id', __( 'Invalid post ID.' ), array( 'status' => 404 ) );
+ $error = new WP_Error(
+ 'rest_post_invalid_id',
+ __( 'Invalid post ID.' ),
+ array( 'status' => 404 )
+ );
+
if ( (int) $id <= 0 ) {
return $error;
}
@@ -402,13 +426,21 @@
}
if ( 'edit' === $request['context'] && $post && ! $this->check_update_permission( $post ) ) {
- return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you are not allowed to edit this post.' ), array( 'status' => rest_authorization_required_code() ) );
+ return new WP_Error(
+ 'rest_forbidden_context',
+ __( 'Sorry, you are not allowed to edit this post.' ),
+ array( 'status' => rest_authorization_required_code() )
+ );
}
if ( $post && ! empty( $request['password'] ) ) {
// Check post password, and return error if invalid.
if ( ! hash_equals( $post->post_password, $request['password'] ) ) {
- return new WP_Error( 'rest_post_incorrect_password', __( 'Incorrect post password.' ), array( 'status' => 403 ) );
+ return new WP_Error(
+ 'rest_post_incorrect_password',
+ __( 'Incorrect post password.' ),
+ array( 'status' => 403 )
+ );
}
}
@@ -490,25 +522,45 @@
*/
public function create_item_permissions_check( $request ) {
if ( ! empty( $request['id'] ) ) {
- return new WP_Error( 'rest_post_exists', __( 'Cannot create existing post.' ), array( 'status' => 400 ) );
+ return new WP_Error(
+ 'rest_post_exists',
+ __( 'Cannot create existing post.' ),
+ array( 'status' => 400 )
+ );
}
$post_type = get_post_type_object( $this->post_type );
if ( ! empty( $request['author'] ) && get_current_user_id() !== $request['author'] && ! current_user_can( $post_type->cap->edit_others_posts ) ) {
- return new WP_Error( 'rest_cannot_edit_others', __( 'Sorry, you are not allowed to create posts as this user.' ), array( 'status' => rest_authorization_required_code() ) );
+ return new WP_Error(
+ 'rest_cannot_edit_others',
+ __( 'Sorry, you are not allowed to create posts as this user.' ),
+ array( 'status' => rest_authorization_required_code() )
+ );
}
- if ( ! empty( $request['sticky'] ) && ! current_user_can( $post_type->cap->edit_others_posts ) ) {
- return new WP_Error( 'rest_cannot_assign_sticky', __( 'Sorry, you are not allowed to make posts sticky.' ), array( 'status' => rest_authorization_required_code() ) );
+ if ( ! empty( $request['sticky'] ) && ! current_user_can( $post_type->cap->edit_others_posts ) && ! current_user_can( $post_type->cap->publish_posts ) ) {
+ return new WP_Error(
+ 'rest_cannot_assign_sticky',
+ __( 'Sorry, you are not allowed to make posts sticky.' ),
+ array( 'status' => rest_authorization_required_code() )
+ );
}
if ( ! current_user_can( $post_type->cap->create_posts ) ) {
- return new WP_Error( 'rest_cannot_create', __( 'Sorry, you are not allowed to create posts as this user.' ), array( 'status' => rest_authorization_required_code() ) );
+ return new WP_Error(
+ 'rest_cannot_create',
+ __( 'Sorry, you are not allowed to create posts as this user.' ),
+ array( 'status' => rest_authorization_required_code() )
+ );
}
if ( ! $this->check_assign_terms_permission( $request ) ) {
- return new WP_Error( 'rest_cannot_assign_term', __( 'Sorry, you are not allowed to assign the provided terms.' ), array( 'status' => rest_authorization_required_code() ) );
+ return new WP_Error(
+ 'rest_cannot_assign_term',
+ __( 'Sorry, you are not allowed to assign the provided terms.' ),
+ array( 'status' => rest_authorization_required_code() )
+ );
}
return true;
@@ -524,7 +576,11 @@
*/
public function create_item( $request ) {
if ( ! empty( $request['id'] ) ) {
- return new WP_Error( 'rest_post_exists', __( 'Cannot create existing post.' ), array( 'status' => 400 ) );
+ return new WP_Error(
+ 'rest_post_exists',
+ __( 'Cannot create existing post.' ),
+ array( 'status' => 400 )
+ );
}
$prepared_post = $this->prepare_item_for_database( $request );
@@ -647,19 +703,35 @@
$post_type = get_post_type_object( $this->post_type );
if ( $post && ! $this->check_update_permission( $post ) ) {
- return new WP_Error( 'rest_cannot_edit', __( 'Sorry, you are not allowed to edit this post.' ), array( 'status' => rest_authorization_required_code() ) );
+ return new WP_Error(
+ 'rest_cannot_edit',
+ __( 'Sorry, you are not allowed to edit this post.' ),
+ array( 'status' => rest_authorization_required_code() )
+ );
}
if ( ! empty( $request['author'] ) && get_current_user_id() !== $request['author'] && ! current_user_can( $post_type->cap->edit_others_posts ) ) {
- return new WP_Error( 'rest_cannot_edit_others', __( 'Sorry, you are not allowed to update posts as this user.' ), array( 'status' => rest_authorization_required_code() ) );
+ return new WP_Error(
+ 'rest_cannot_edit_others',
+ __( 'Sorry, you are not allowed to update posts as this user.' ),
+ array( 'status' => rest_authorization_required_code() )
+ );
}
- if ( ! empty( $request['sticky'] ) && ! current_user_can( $post_type->cap->edit_others_posts ) ) {
- return new WP_Error( 'rest_cannot_assign_sticky', __( 'Sorry, you are not allowed to make posts sticky.' ), array( 'status' => rest_authorization_required_code() ) );
+ if ( ! empty( $request['sticky'] ) && ! current_user_can( $post_type->cap->edit_others_posts ) && ! current_user_can( $post_type->cap->publish_posts ) ) {
+ return new WP_Error(
+ 'rest_cannot_assign_sticky',
+ __( 'Sorry, you are not allowed to make posts sticky.' ),
+ array( 'status' => rest_authorization_required_code() )
+ );
}
if ( ! $this->check_assign_terms_permission( $request ) ) {
- return new WP_Error( 'rest_cannot_assign_term', __( 'Sorry, you are not allowed to assign the provided terms.' ), array( 'status' => rest_authorization_required_code() ) );
+ return new WP_Error(
+ 'rest_cannot_assign_term',
+ __( 'Sorry, you are not allowed to assign the provided terms.' ),
+ array( 'status' => rest_authorization_required_code() )
+ );
}
return true;
@@ -685,7 +757,7 @@
return $post;
}
- // convert the post object to an array, otherwise wp_update_post will expect non-escaped input.
+ // Convert the post object to an array, otherwise wp_update_post() will expect non-escaped input.
$post_id = wp_update_post( wp_slash( (array) $post ), true );
if ( is_wp_error( $post_id ) ) {
@@ -776,7 +848,11 @@
}
if ( $post && ! $this->check_delete_permission( $post ) ) {
- return new WP_Error( 'rest_cannot_delete', __( 'Sorry, you are not allowed to delete this post.' ), array( 'status' => rest_authorization_required_code() ) );
+ return new WP_Error(
+ 'rest_cannot_delete',
+ __( 'Sorry, you are not allowed to delete this post.' ),
+ array( 'status' => rest_authorization_required_code() )
+ );
}
return true;
@@ -810,7 +886,7 @@
*
* The dynamic portion of the hook name, `$this->post_type`, refers to the post type slug.
*
- * Pass false to disable trash support for the post.
+ * Pass false to disable Trash support for the post.
*
* @since 4.7.0
*
@@ -820,7 +896,11 @@
$supports_trash = apply_filters( "rest_{$this->post_type}_trashable", $supports_trash, $post );
if ( ! $this->check_delete_permission( $post ) ) {
- return new WP_Error( 'rest_user_cannot_delete_post', __( 'Sorry, you are not allowed to delete this post.' ), array( 'status' => rest_authorization_required_code() ) );
+ return new WP_Error(
+ 'rest_user_cannot_delete_post',
+ __( 'Sorry, you are not allowed to delete this post.' ),
+ array( 'status' => rest_authorization_required_code() )
+ );
}
$request->set_param( 'context', 'edit' );
@@ -839,24 +919,36 @@
} else {
// If we don't support trashing for this type, error out.
if ( ! $supports_trash ) {
- /* translators: %s: force=true */
- return new WP_Error( 'rest_trash_not_supported', sprintf( __( "The post does not support trashing. Set '%s' to delete." ), 'force=true' ), array( 'status' => 501 ) );
+ return new WP_Error(
+ 'rest_trash_not_supported',
+ /* translators: %s: force=true */
+ sprintf( __( "The post does not support trashing. Set '%s' to delete." ), 'force=true' ),
+ array( 'status' => 501 )
+ );
}
// Otherwise, only trash if we haven't already.
if ( 'trash' === $post->post_status ) {
- return new WP_Error( 'rest_already_trashed', __( 'The post has already been deleted.' ), array( 'status' => 410 ) );
+ return new WP_Error(
+ 'rest_already_trashed',
+ __( 'The post has already been deleted.' ),
+ array( 'status' => 410 )
+ );
}
- // (Note that internally this falls through to `wp_delete_post` if
- // the trash is disabled.)
+ // (Note that internally this falls through to `wp_delete_post()`
+ // if the Trash is disabled.)
$result = wp_trash_post( $id );
$post = get_post( $id );
$response = $this->prepare_item_for_response( $post, $request );
}
if ( ! $result ) {
- return new WP_Error( 'rest_cannot_delete', __( 'The post cannot be deleted.' ), array( 'status' => 500 ) );
+ return new WP_Error(
+ 'rest_cannot_delete',
+ __( 'The post cannot be deleted.' ),
+ array( 'status' => 500 )
+ );
}
/**
@@ -866,7 +958,7 @@
*
* @since 4.7.0
*
- * @param object $post The deleted or trashed post.
+ * @param WP_Post $post The deleted or trashed post.
* @param WP_REST_Response $response The response data.
* @param WP_REST_Request $request The request sent to the API.
*/
@@ -956,7 +1048,7 @@
* @return stdClass|WP_Error Post object or WP_Error.
*/
protected function prepare_item_for_database( $request ) {
- $prepared_post = new stdClass;
+ $prepared_post = new stdClass();
// Post ID.
if ( isset( $request['id'] ) ) {
@@ -1021,21 +1113,33 @@
// Post date.
if ( ! empty( $schema['properties']['date'] ) && ! empty( $request['date'] ) ) {
- $date_data = rest_get_date_with_gmt( $request['date'] );
-
- if ( ! empty( $date_data ) ) {
+ $current_date = isset( $prepared_post->ID ) ? get_post( $prepared_post->ID )->post_date : false;
+ $date_data = rest_get_date_with_gmt( $request['date'] );
+
+ if ( ! empty( $date_data ) && $current_date !== $date_data[0] ) {
list( $prepared_post->post_date, $prepared_post->post_date_gmt ) = $date_data;
$prepared_post->edit_date = true;
}
} elseif ( ! empty( $schema['properties']['date_gmt'] ) && ! empty( $request['date_gmt'] ) ) {
- $date_data = rest_get_date_with_gmt( $request['date_gmt'], true );
-
- if ( ! empty( $date_data ) ) {
+ $current_date = isset( $prepared_post->ID ) ? get_post( $prepared_post->ID )->post_date_gmt : false;
+ $date_data = rest_get_date_with_gmt( $request['date_gmt'], true );
+
+ if ( ! empty( $date_data ) && $current_date !== $date_data[1] ) {
list( $prepared_post->post_date, $prepared_post->post_date_gmt ) = $date_data;
$prepared_post->edit_date = true;
}
}
+ // Sending a null date or date_gmt value resets date and date_gmt to their
+ // default values (`0000-00-00 00:00:00`).
+ if (
+ ( ! empty( $schema['properties']['date_gmt'] ) && $request->has_param( 'date_gmt' ) && null === $request['date_gmt'] ) ||
+ ( ! empty( $schema['properties']['date'] ) && $request->has_param( 'date' ) && null === $request['date'] )
+ ) {
+ $prepared_post->post_date_gmt = null;
+ $prepared_post->post_date = null;
+ }
+
// Post slug.
if ( ! empty( $schema['properties']['slug'] ) && isset( $request['slug'] ) ) {
$prepared_post->post_name = $request['slug'];
@@ -1049,7 +1153,11 @@
$user_obj = get_userdata( $post_author );
if ( ! $user_obj ) {
- return new WP_Error( 'rest_invalid_author', __( 'Invalid author ID.' ), array( 'status' => 400 ) );
+ return new WP_Error(
+ 'rest_invalid_author',
+ __( 'Invalid author ID.' ),
+ array( 'status' => 400 )
+ );
}
}
@@ -1062,18 +1170,30 @@
if ( '' !== $request['password'] ) {
if ( ! empty( $schema['properties']['sticky'] ) && ! empty( $request['sticky'] ) ) {
- return new WP_Error( 'rest_invalid_field', __( 'A post can not be sticky and have a password.' ), array( 'status' => 400 ) );
+ return new WP_Error(
+ 'rest_invalid_field',
+ __( 'A post can not be sticky and have a password.' ),
+ array( 'status' => 400 )
+ );
}
if ( ! empty( $prepared_post->ID ) && is_sticky( $prepared_post->ID ) ) {
- return new WP_Error( 'rest_invalid_field', __( 'A sticky post can not be password protected.' ), array( 'status' => 400 ) );
+ return new WP_Error(
+ 'rest_invalid_field',
+ __( 'A sticky post can not be password protected.' ),
+ array( 'status' => 400 )
+ );
}
}
}
if ( ! empty( $schema['properties']['sticky'] ) && ! empty( $request['sticky'] ) ) {
if ( ! empty( $prepared_post->ID ) && post_password_required( $prepared_post->ID ) ) {
- return new WP_Error( 'rest_invalid_field', __( 'A password protected post can not be set to sticky.' ), array( 'status' => 400 ) );
+ return new WP_Error(
+ 'rest_invalid_field',
+ __( 'A password protected post can not be set to sticky.' ),
+ array( 'status' => 400 )
+ );
}
}
@@ -1083,9 +1203,15 @@
$prepared_post->post_parent = 0;
} else {
$parent = get_post( (int) $request['parent'] );
+
if ( empty( $parent ) ) {
- return new WP_Error( 'rest_post_invalid_id', __( 'Invalid post parent ID.' ), array( 'status' => 400 ) );
+ return new WP_Error(
+ 'rest_post_invalid_id',
+ __( 'Invalid post parent ID.' ),
+ array( 'status' => 400 )
+ );
}
+
$prepared_post->post_parent = (int) $parent->ID;
}
}
@@ -1130,8 +1256,8 @@
*
* @since 4.7.0
*
- * @param string $post_status Post status.
- * @param object $post_type Post type.
+ * @param string $post_status Post status.
+ * @param WP_Post_Type $post_type Post type.
* @return string|WP_Error Post status or WP_Error if lacking the proper permission.
*/
protected function handle_status_param( $post_status, $post_type ) {
@@ -1142,13 +1268,21 @@
break;
case 'private':
if ( ! current_user_can( $post_type->cap->publish_posts ) ) {
- return new WP_Error( 'rest_cannot_publish', __( 'Sorry, you are not allowed to create private posts in this post type.' ), array( 'status' => rest_authorization_required_code() ) );
+ return new WP_Error(
+ 'rest_cannot_publish',
+ __( 'Sorry, you are not allowed to create private posts in this post type.' ),
+ array( 'status' => rest_authorization_required_code() )
+ );
}
break;
case 'publish':
case 'future':
if ( ! current_user_can( $post_type->cap->publish_posts ) ) {
- return new WP_Error( 'rest_cannot_publish', __( 'Sorry, you are not allowed to publish posts in this post type.' ), array( 'status' => rest_authorization_required_code() ) );
+ return new WP_Error(
+ 'rest_cannot_publish',
+ __( 'Sorry, you are not allowed to publish posts in this post type.' ),
+ array( 'status' => rest_authorization_required_code() )
+ );
}
break;
default:
@@ -1178,7 +1312,11 @@
if ( $result ) {
return true;
} else {
- return new WP_Error( 'rest_invalid_featured_media', __( 'Invalid featured media ID.' ), array( 'status' => 400 ) );
+ return new WP_Error(
+ 'rest_invalid_featured_media',
+ __( 'Invalid featured media ID.' ),
+ array( 'status' => 400 )
+ );
}
} else {
return delete_post_thumbnail( $post_id );
@@ -1219,8 +1357,11 @@
return true;
}
- /* translators: 1: parameter, 2: list of valid values */
- return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s is not one of %2$s.' ), 'template', implode( ', ', array_keys( $allowed_templates ) ) ) );
+ return new WP_Error(
+ 'rest_invalid_param',
+ /* translators: 1: Parameter, 2: List of valid values. */
+ sprintf( __( '%1$s is not one of %2$s.' ), 'template', implode( ', ', array_keys( $allowed_templates ) ) )
+ );
}
/**
@@ -1306,7 +1447,7 @@
*
* @since 4.7.0
*
- * @param object|string $post_type Post type name or object.
+ * @param WP_Post_Type|string $post_type Post type name or object.
* @return bool Whether the post type is allowed in REST.
*/
protected function check_is_post_type_allowed( $post_type ) {
@@ -1328,7 +1469,7 @@
*
* @since 4.7.0
*
- * @param object $post Post object.
+ * @param WP_Post $post Post object.
* @return bool Whether the post can be read.
*/
public function check_read_permission( $post ) {
@@ -1338,7 +1479,7 @@
}
// Is the post readable?
- if ( 'publish' === $post->post_status || current_user_can( $post_type->cap->read_post, $post->ID ) ) {
+ if ( 'publish' === $post->post_status || current_user_can( 'read_post', $post->ID ) ) {
return true;
}
@@ -1371,7 +1512,7 @@
*
* @since 4.7.0
*
- * @param object $post Post object.
+ * @param WP_Post $post Post object.
* @return bool Whether the post can be edited.
*/
protected function check_update_permission( $post ) {
@@ -1381,7 +1522,7 @@
return false;
}
- return current_user_can( $post_type->cap->edit_post, $post->ID );
+ return current_user_can( 'edit_post', $post->ID );
}
/**
@@ -1389,7 +1530,7 @@
*
* @since 4.7.0
*
- * @param object $post Post object.
+ * @param WP_Post $post Post object.
* @return bool Whether the post can be created.
*/
protected function check_create_permission( $post ) {
@@ -1407,7 +1548,7 @@
*
* @since 4.7.0
*
- * @param object $post Post object.
+ * @param WP_Post $post Post object.
* @return bool Whether the post can be deleted.
*/
protected function check_delete_permission( $post ) {
@@ -1417,7 +1558,7 @@
return false;
}
- return current_user_can( $post_type->cap->delete_post, $post->ID );
+ return current_user_can( 'delete_post', $post->ID );
}
/**
@@ -1439,19 +1580,21 @@
// Base fields for every post.
$data = array();
- if ( in_array( 'id', $fields, true ) ) {
+ if ( rest_is_field_included( 'id', $fields ) ) {
$data['id'] = $post->ID;
}
- if ( in_array( 'date', $fields, true ) ) {
+ if ( rest_is_field_included( 'date', $fields ) ) {
$data['date'] = $this->prepare_date_response( $post->post_date_gmt, $post->post_date );
}
- if ( in_array( 'date_gmt', $fields, true ) ) {
- // For drafts, `post_date_gmt` may not be set, indicating that the
- // date of the draft should be updated each time it is saved (see
- // #38883). In this case, shim the value based on the `post_date`
- // field with the site's timezone offset applied.
+ if ( rest_is_field_included( 'date_gmt', $fields ) ) {
+ /*
+ * For drafts, `post_date_gmt` may not be set, indicating that the date
+ * of the draft should be updated each time it is saved (see #38883).
+ * In this case, shim the value based on the `post_date` field
+ * with the site's timezone offset applied.
+ */
if ( '0000-00-00 00:00:00' === $post->post_date_gmt ) {
$post_date_gmt = get_gmt_from_date( $post->post_date );
} else {
@@ -1460,7 +1603,7 @@
$data['date_gmt'] = $this->prepare_date_response( $post_date_gmt );
}
- if ( in_array( 'guid', $fields, true ) ) {
+ if ( rest_is_field_included( 'guid', $fields ) ) {
$data['guid'] = array(
/** This filter is documented in wp-includes/post-template.php */
'rendered' => apply_filters( 'get_the_guid', $post->guid, $post->ID ),
@@ -1468,50 +1611,54 @@
);
}
- if ( in_array( 'modified', $fields, true ) ) {
+ if ( rest_is_field_included( 'modified', $fields ) ) {
$data['modified'] = $this->prepare_date_response( $post->post_modified_gmt, $post->post_modified );
}
- if ( in_array( 'modified_gmt', $fields, true ) ) {
- // For drafts, `post_modified_gmt` may not be set (see
- // `post_date_gmt` comments above). In this case, shim the value
- // based on the `post_modified` field with the site's timezone
- // offset applied.
+ if ( rest_is_field_included( 'modified_gmt', $fields ) ) {
+ /*
+ * For drafts, `post_modified_gmt` may not be set (see `post_date_gmt` comments
+ * above). In this case, shim the value based on the `post_modified` field
+ * with the site's timezone offset applied.
+ */
if ( '0000-00-00 00:00:00' === $post->post_modified_gmt ) {
- $post_modified_gmt = date( 'Y-m-d H:i:s', strtotime( $post->post_modified ) - ( get_option( 'gmt_offset' ) * 3600 ) );
+ $post_modified_gmt = gmdate( 'Y-m-d H:i:s', strtotime( $post->post_modified ) - ( get_option( 'gmt_offset' ) * 3600 ) );
} else {
$post_modified_gmt = $post->post_modified_gmt;
}
$data['modified_gmt'] = $this->prepare_date_response( $post_modified_gmt );
}
- if ( in_array( 'password', $fields, true ) ) {
+ if ( rest_is_field_included( 'password', $fields ) ) {
$data['password'] = $post->post_password;
}
- if ( in_array( 'slug', $fields, true ) ) {
+ if ( rest_is_field_included( 'slug', $fields ) ) {
$data['slug'] = $post->post_name;
}
- if ( in_array( 'status', $fields, true ) ) {
+ if ( rest_is_field_included( 'status', $fields ) ) {
$data['status'] = $post->post_status;
}
- if ( in_array( 'type', $fields, true ) ) {
+ if ( rest_is_field_included( 'type', $fields ) ) {
$data['type'] = $post->post_type;
}
- if ( in_array( 'link', $fields, true ) ) {
+ if ( rest_is_field_included( 'link', $fields ) ) {
$data['link'] = get_permalink( $post->ID );
}
- if ( in_array( 'title', $fields, true ) ) {
+ if ( rest_is_field_included( 'title', $fields ) ) {
+ $data['title'] = array();
+ }
+ if ( rest_is_field_included( 'title.raw', $fields ) ) {
+ $data['title']['raw'] = $post->post_title;
+ }
+ if ( rest_is_field_included( 'title.rendered', $fields ) ) {
add_filter( 'protected_title_format', array( $this, 'protected_title_format' ) );
- $data['title'] = array(
- 'raw' => $post->post_title,
- 'rendered' => get_the_title( $post->ID ),
- );
+ $data['title']['rendered'] = get_the_title( $post->ID );
remove_filter( 'protected_title_format', array( $this, 'protected_title_format' ) );
}
@@ -1525,19 +1672,30 @@
$has_password_filter = true;
}
- if ( in_array( 'content', $fields, true ) ) {
- $data['content'] = array(
- 'raw' => $post->post_content,
- /** This filter is documented in wp-includes/post-template.php */
- 'rendered' => post_password_required( $post ) ? '' : apply_filters( 'the_content', $post->post_content ),
- 'protected' => (bool) $post->post_password,
- 'block_version' => block_version( $post->post_content ),
- );
+ if ( rest_is_field_included( 'content', $fields ) ) {
+ $data['content'] = array();
+ }
+ if ( rest_is_field_included( 'content.raw', $fields ) ) {
+ $data['content']['raw'] = $post->post_content;
+ }
+ if ( rest_is_field_included( 'content.rendered', $fields ) ) {
+ /** This filter is documented in wp-includes/post-template.php */
+ $data['content']['rendered'] = post_password_required( $post ) ? '' : apply_filters( 'the_content', $post->post_content );
}
-
- if ( in_array( 'excerpt', $fields, true ) ) {
+ if ( rest_is_field_included( 'content.protected', $fields ) ) {
+ $data['content']['protected'] = (bool) $post->post_password;
+ }
+ if ( rest_is_field_included( 'content.block_version', $fields ) ) {
+ $data['content']['block_version'] = block_version( $post->post_content );
+ }
+
+ if ( rest_is_field_included( 'excerpt', $fields ) ) {
/** This filter is documented in wp-includes/post-template.php */
- $excerpt = apply_filters( 'the_excerpt', apply_filters( 'get_the_excerpt', $post->post_excerpt, $post ) );
+ $excerpt = apply_filters( 'get_the_excerpt', $post->post_excerpt, $post );
+
+ /** This filter is documented in wp-includes/post-template.php */
+ $excerpt = apply_filters( 'the_excerpt', $excerpt );
+
$data['excerpt'] = array(
'raw' => $post->post_excerpt,
'rendered' => post_password_required( $post ) ? '' : $excerpt,
@@ -1550,43 +1708,44 @@
remove_filter( 'post_password_required', '__return_false' );
}
- if ( in_array( 'author', $fields, true ) ) {
+ if ( rest_is_field_included( 'author', $fields ) ) {
$data['author'] = (int) $post->post_author;
}
- if ( in_array( 'featured_media', $fields, true ) ) {
+ if ( rest_is_field_included( 'featured_media', $fields ) ) {
$data['featured_media'] = (int) get_post_thumbnail_id( $post->ID );
}
- if ( in_array( 'parent', $fields, true ) ) {
+ if ( rest_is_field_included( 'parent', $fields ) ) {
$data['parent'] = (int) $post->post_parent;
}
- if ( in_array( 'menu_order', $fields, true ) ) {
+ if ( rest_is_field_included( 'menu_order', $fields ) ) {
$data['menu_order'] = (int) $post->menu_order;
}
- if ( in_array( 'comment_status', $fields, true ) ) {
+ if ( rest_is_field_included( 'comment_status', $fields ) ) {
$data['comment_status'] = $post->comment_status;
}
- if ( in_array( 'ping_status', $fields, true ) ) {
+ if ( rest_is_field_included( 'ping_status', $fields ) ) {
$data['ping_status'] = $post->ping_status;
}
- if ( in_array( 'sticky', $fields, true ) ) {
+ if ( rest_is_field_included( 'sticky', $fields ) ) {
$data['sticky'] = is_sticky( $post->ID );
}
- if ( in_array( 'template', $fields, true ) ) {
- if ( $template = get_page_template_slug( $post->ID ) ) {
+ if ( rest_is_field_included( 'template', $fields ) ) {
+ $template = get_page_template_slug( $post->ID );
+ if ( $template ) {
$data['template'] = $template;
} else {
$data['template'] = '';
}
}
- if ( in_array( 'format', $fields, true ) ) {
+ if ( rest_is_field_included( 'format', $fields ) ) {
$data['format'] = get_post_format( $post->ID );
// Fill in blank post format.
@@ -1595,7 +1754,7 @@
}
}
- if ( in_array( 'meta', $fields, true ) ) {
+ if ( rest_is_field_included( 'meta', $fields ) ) {
$data['meta'] = $this->meta->get_value( $post->ID, $request );
}
@@ -1604,7 +1763,7 @@
foreach ( $taxonomies as $taxonomy ) {
$base = ! empty( $taxonomy->rest_base ) ? $taxonomy->rest_base : $taxonomy->name;
- if ( in_array( $base, $fields, true ) ) {
+ if ( rest_is_field_included( $base, $fields ) ) {
$terms = get_the_terms( $post, $taxonomy->name );
$data[ $base ] = $terms ? array_values( wp_list_pluck( $terms, 'term_id' ) ) : array();
}
@@ -1612,18 +1771,23 @@
$post_type_obj = get_post_type_object( $post->post_type );
if ( is_post_type_viewable( $post_type_obj ) && $post_type_obj->public ) {
-
- if ( ! function_exists( 'get_sample_permalink' ) ) {
- require_once ABSPATH . 'wp-admin/includes/post.php';
- }
-
- $sample_permalink = get_sample_permalink( $post->ID, $post->post_title, '' );
-
- if ( in_array( 'permalink_template', $fields, true ) ) {
- $data['permalink_template'] = $sample_permalink[0];
- }
- if ( in_array( 'generated_slug', $fields, true ) ) {
- $data['generated_slug'] = $sample_permalink[1];
+ $permalink_template_requested = rest_is_field_included( 'permalink_template', $fields );
+ $generated_slug_requested = rest_is_field_included( 'generated_slug', $fields );
+
+ if ( $permalink_template_requested || $generated_slug_requested ) {
+ if ( ! function_exists( 'get_sample_permalink' ) ) {
+ require_once ABSPATH . 'wp-admin/includes/post.php';
+ }
+
+ $sample_permalink = get_sample_permalink( $post->ID, $post->post_title, '' );
+
+ if ( $permalink_template_requested ) {
+ $data['permalink_template'] = $sample_permalink[0];
+ }
+
+ if ( $generated_slug_requested ) {
+ $data['generated_slug'] = $sample_permalink[1];
+ }
}
}
@@ -1747,7 +1911,8 @@
}
// If we have a featured media, add that.
- if ( $featured_media = get_post_thumbnail_id( $post->ID ) ) {
+ $featured_media = get_post_thumbnail_id( $post->ID );
+ if ( $featured_media ) {
$image_url = rest_url( 'wp/v2/media/' . $featured_media );
$links['https://api.w.org/featuredmedia'] = array(
@@ -1802,9 +1967,8 @@
*
* @since 4.9.8
*
- * @param WP_Post $post Post object.
- * @param WP_REST_Request Request object.
- *
+ * @param WP_Post $post Post object.
+ * @param WP_REST_Request $request Request object.
* @return array List of link relations.
*/
protected function get_available_actions( $post, $request ) {
@@ -1863,6 +2027,9 @@
* @return array Item schema data.
*/
public function get_item_schema() {
+ if ( $this->schema ) {
+ return $this->add_additional_fields_schema( $this->schema );
+ }
$schema = array(
'$schema' => 'http://json-schema.org/draft-04/schema#',
@@ -1872,13 +2039,13 @@
'properties' => array(
'date' => array(
'description' => __( "The date the object was published, in the site's timezone." ),
- 'type' => 'string',
+ 'type' => array( 'string', 'null' ),
'format' => 'date-time',
'context' => array( 'view', 'edit', 'embed' ),
),
'date_gmt' => array(
'description' => __( 'The date the object was published, as GMT.' ),
- 'type' => 'string',
+ 'type' => array( 'string', 'null' ),
'format' => 'date-time',
'context' => array( 'view', 'edit' ),
),
@@ -2025,6 +2192,7 @@
'custom-fields',
),
);
+
foreach ( $post_type_attributes as $attribute ) {
if ( isset( $fixed_schemas[ $this->post_type ] ) && ! in_array( $attribute, $fixed_schemas[ $this->post_type ], true ) ) {
continue;
@@ -2040,8 +2208,8 @@
'type' => 'object',
'context' => array( 'view', 'edit', 'embed' ),
'arg_options' => array(
- 'sanitize_callback' => null, // Note: sanitization implemented in self::prepare_item_for_database()
- 'validate_callback' => null, // Note: validation implemented in self::prepare_item_for_database()
+ 'sanitize_callback' => null, // Note: sanitization implemented in self::prepare_item_for_database().
+ 'validate_callback' => null, // Note: validation implemented in self::prepare_item_for_database().
),
'properties' => array(
'raw' => array(
@@ -2065,8 +2233,8 @@
'type' => 'object',
'context' => array( 'view', 'edit' ),
'arg_options' => array(
- 'sanitize_callback' => null, // Note: sanitization implemented in self::prepare_item_for_database()
- 'validate_callback' => null, // Note: validation implemented in self::prepare_item_for_database()
+ 'sanitize_callback' => null, // Note: sanitization implemented in self::prepare_item_for_database().
+ 'validate_callback' => null, // Note: validation implemented in self::prepare_item_for_database().
),
'properties' => array(
'raw' => array(
@@ -2110,8 +2278,8 @@
'type' => 'object',
'context' => array( 'view', 'edit', 'embed' ),
'arg_options' => array(
- 'sanitize_callback' => null, // Note: sanitization implemented in self::prepare_item_for_database()
- 'validate_callback' => null, // Note: validation implemented in self::prepare_item_for_database()
+ 'sanitize_callback' => null, // Note: sanitization implemented in self::prepare_item_for_database().
+ 'validate_callback' => null, // Note: validation implemented in self::prepare_item_for_database().
),
'properties' => array(
'raw' => array(
@@ -2203,10 +2371,27 @@
);
$taxonomies = wp_list_filter( get_object_taxonomies( $this->post_type, 'objects' ), array( 'show_in_rest' => true ) );
+
foreach ( $taxonomies as $taxonomy ) {
- $base = ! empty( $taxonomy->rest_base ) ? $taxonomy->rest_base : $taxonomy->name;
+ $base = ! empty( $taxonomy->rest_base ) ? $taxonomy->rest_base : $taxonomy->name;
+
+ if ( array_key_exists( $base, $schema['properties'] ) ) {
+ $taxonomy_field_name_with_conflict = ! empty( $taxonomy->rest_base ) ? 'rest_base' : 'name';
+ _doing_it_wrong(
+ 'register_taxonomy',
+ sprintf(
+ /* translators: 1. The taxonomy name, 2. The property name, either 'rest_base' or 'name', 3. The conflicting value. */
+ __( 'The "%1$s" taxonomy "%2$s" property (%3$s) conflicts with an existing property on the REST API Posts Controller. Specify a custom "rest_base" when registering the taxonomy to avoid this error.' ),
+ $taxonomy->name,
+ $taxonomy_field_name_with_conflict,
+ $base
+ ),
+ '5.4.0'
+ );
+ }
+
$schema['properties'][ $base ] = array(
- /* translators: %s: taxonomy name */
+ /* translators: %s: Taxonomy name. */
'description' => sprintf( __( 'The terms assigned to the object in the %s taxonomy.' ), $taxonomy->name ),
'type' => 'array',
'items' => array(
@@ -2222,7 +2407,38 @@
$schema['links'] = $schema_links;
}
- return $this->add_additional_fields_schema( $schema );
+ // Take a snapshot of which fields are in the schema pre-filtering.
+ $schema_fields = array_keys( $schema['properties'] );
+
+ /**
+ * Filter the post's schema.
+ *
+ * The dynamic portion of the filter, `$this->post_type`, refers to the
+ * post type slug for the controller.
+ *
+ * @since 5.4.0
+ *
+ * @param array $schema Item schema data.
+ */
+ $schema = apply_filters( "rest_{$this->post_type}_item_schema", $schema );
+
+ // Emit a _doing_it_wrong warning if user tries to add new properties using this filter.
+ $new_fields = array_diff( array_keys( $schema['properties'] ), $schema_fields );
+ if ( count( $new_fields ) > 0 ) {
+ _doing_it_wrong(
+ __METHOD__,
+ sprintf(
+ /* translators: %s: register_rest_field */
+ __( 'Please use %s to add new schema properties.' ),
+ 'register_rest_field'
+ ),
+ '5.4.0'
+ );
+ }
+
+ $this->schema = $schema;
+
+ return $this->add_additional_fields_schema( $this->schema );
}
/**
@@ -2308,9 +2524,9 @@
foreach ( $taxonomies as $tax ) {
$tax_base = ! empty( $tax->rest_base ) ? $tax->rest_base : $tax->name;
- /* translators: %s: taxonomy name */
+ /* translators: %s: Taxonomy name. */
$assign_title = sprintf( __( 'The current user can assign terms in the %s taxonomy.' ), $tax->name );
- /* translators: %s: taxonomy name */
+ /* translators: %s: Taxonomy name. */
$create_title = sprintf( __( 'The current user can create terms in the %s taxonomy.' ), $tax->name );
$links[] = array(
@@ -2496,11 +2712,19 @@
$taxonomies = wp_list_filter( get_object_taxonomies( $this->post_type, 'objects' ), array( 'show_in_rest' => true ) );
+ if ( ! empty( $taxonomies ) ) {
+ $query_params['tax_relation'] = array(
+ 'description' => __( 'Limit result set based on relationship between multiple taxonomies.' ),
+ 'type' => 'string',
+ 'enum' => array( 'AND', 'OR' ),
+ );
+ }
+
foreach ( $taxonomies as $taxonomy ) {
$base = ! empty( $taxonomy->rest_base ) ? $taxonomy->rest_base : $taxonomy->name;
$query_params[ $base ] = array(
- /* translators: %s: taxonomy name */
+ /* translators: %s: Taxonomy name. */
'description' => sprintf( __( 'Limit result set to all items that have the specified term assigned in the %s taxonomy.' ), $base ),
'type' => 'array',
'items' => array(
@@ -2510,7 +2734,7 @@
);
$query_params[ $base . '_exclude' ] = array(
- /* translators: %s: taxonomy name */
+ /* translators: %s: Taxonomy name. */
'description' => sprintf( __( 'Limit result set to all items except those that have the specified term assigned in the %s taxonomy.' ), $base ),
'type' => 'array',
'items' => array(
@@ -2551,15 +2775,15 @@
*
* @since 4.7.0
*
- * @param string|array $statuses One or more post statuses.
- * @param WP_REST_Request $request Full details about the request.
- * @param string $parameter Additional parameter to pass to validation.
+ * @param string|array $statuses One or more post statuses.
+ * @param WP_REST_Request $request Full details about the request.
+ * @param string $parameter Additional parameter to pass to validation.
* @return array|WP_Error A list of valid statuses, otherwise WP_Error object.
*/
public function sanitize_post_statuses( $statuses, $request, $parameter ) {
$statuses = wp_parse_slug_list( $statuses );
- // The default status is different in WP_REST_Attachments_Controller
+ // The default status is different in WP_REST_Attachments_Controller.
$attributes = $request->get_attributes();
$default_status = $attributes['args']['status']['default'];
@@ -2576,7 +2800,11 @@
return $result;
}
} else {
- return new WP_Error( 'rest_forbidden_status', __( 'Status is forbidden.' ), array( 'status' => rest_authorization_required_code() ) );
+ return new WP_Error(
+ 'rest_forbidden_status',
+ __( 'Status is forbidden.' ),
+ array( 'status' => rest_authorization_required_code() )
+ );
}
}