--- a/wp/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php Thu Sep 29 08:06:27 2022 +0200
+++ b/wp/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php Fri Sep 05 18:40:08 2025 +0200
@@ -97,6 +97,12 @@
$get_item_args = array(
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
);
+ if ( isset( $schema['properties']['excerpt'] ) ) {
+ $get_item_args['excerpt_length'] = array(
+ 'description' => __( 'Override the default excerpt length.' ),
+ 'type' => 'integer',
+ );
+ }
if ( isset( $schema['properties']['password'] ) ) {
$get_item_args['password'] = array(
'description' => __( 'The password for the post if it is password protected.' ),
@@ -167,7 +173,7 @@
}
/**
- * Override the result of the post password check for REST requested posts.
+ * Overrides the result of the post password check for REST requested posts.
*
* Allow users to read the content of password protected posts if they have
* previously passed a permission check or if they have the `edit_post` capability
@@ -249,6 +255,7 @@
'parent' => 'post_parent__in',
'parent_exclude' => 'post_parent__not_in',
'search' => 's',
+ 'search_columns' => 'search_columns',
'slug' => 'post_name__in',
'status' => 'post_status',
);
@@ -369,6 +376,13 @@
$posts = array();
+ update_post_author_caches( $query_result );
+ update_post_parent_caches( $query_result );
+
+ if ( post_type_supports( $this->post_type, 'thumbnail' ) ) {
+ update_post_thumbnail_cache( $posts_query );
+ }
+
foreach ( $query_result as $post ) {
if ( ! $this->check_read_permission( $post ) ) {
continue;
@@ -386,7 +400,7 @@
$page = (int) $query_args['paged'];
$total_posts = $posts_query->found_posts;
- if ( $total_posts < 1 ) {
+ if ( $total_posts < 1 && $page > 1 ) {
// Out-of-bounds, run the query again without LIMIT for total count.
unset( $query_args['paged'] );
@@ -395,7 +409,7 @@
$total_posts = $count_query->found_posts;
}
- $max_pages = ceil( $total_posts / (int) $posts_query->query_vars['posts_per_page'] );
+ $max_pages = (int) ceil( $total_posts / (int) $posts_query->query_vars['posts_per_page'] );
if ( $page > $max_pages && $total_posts > 0 ) {
return new WP_Error(
@@ -411,7 +425,8 @@
$response->header( 'X-WP-TotalPages', (int) $max_pages );
$request_params = $request->get_query_params();
- $base = add_query_arg( urlencode_deep( $request_params ), rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ) );
+ $collection_url = rest_url( rest_get_route_for_post_type_items( $this->post_type ) );
+ $base = add_query_arg( urlencode_deep( $request_params ), $collection_url );
if ( $page > 1 ) {
$prev_page = $page - 1;
@@ -434,7 +449,7 @@
}
/**
- * Get the post, if the ID is valid.
+ * Gets the post, if the ID is valid.
*
* @since 4.7.2
*
@@ -466,7 +481,7 @@
* @since 4.7.0
*
* @param WP_REST_Request $request Full details about the request.
- * @return true|WP_Error True if the request has read access for the item, WP_Error object otherwise.
+ * @return bool|WP_Error True if the request has read access for the item, WP_Error object or false otherwise.
*/
public function get_item_permissions_check( $request ) {
$post = $this->get_post( $request['id'] );
@@ -646,6 +661,24 @@
$prepared_post->post_type = $this->post_type;
+ if ( ! empty( $prepared_post->post_name )
+ && ! empty( $prepared_post->post_status )
+ && in_array( $prepared_post->post_status, array( 'draft', 'pending' ), true )
+ ) {
+ /*
+ * `wp_unique_post_slug()` returns the same slug for 'draft' or 'pending' posts.
+ *
+ * To ensure that a unique slug is generated, pass the post data with the 'publish' status.
+ */
+ $prepared_post->post_name = wp_unique_post_slug(
+ $prepared_post->post_name,
+ $prepared_post->id,
+ 'publish',
+ $prepared_post->post_type,
+ $prepared_post->post_parent
+ );
+ }
+
$post_id = wp_insert_post( wp_slash( (array) $prepared_post ), true, false );
if ( is_wp_error( $post_id ) ) {
@@ -750,7 +783,7 @@
$response = rest_ensure_response( $response );
$response->set_status( 201 );
- $response->header( 'Location', rest_url( sprintf( '%s/%s/%d', $this->namespace, $this->rest_base, $post_id ) ) );
+ $response->header( 'Location', rest_url( rest_get_route_for_post( $post ) ) );
return $response;
}
@@ -827,6 +860,28 @@
return $post;
}
+ if ( ! empty( $post->post_status ) ) {
+ $post_status = $post->post_status;
+ } else {
+ $post_status = $post_before->post_status;
+ }
+
+ /*
+ * `wp_unique_post_slug()` returns the same slug for 'draft' or 'pending' posts.
+ *
+ * To ensure that a unique slug is generated, pass the post data with the 'publish' status.
+ */
+ if ( ! empty( $post->post_name ) && in_array( $post_status, array( 'draft', 'pending' ), true ) ) {
+ $post_parent = ! empty( $post->post_parent ) ? $post->post_parent : 0;
+ $post->post_name = wp_unique_post_slug(
+ $post->post_name,
+ $post->ID,
+ 'publish',
+ $post->post_type,
+ $post_parent
+ );
+ }
+
// 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, false );
@@ -1014,8 +1069,10 @@
);
}
- // (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 );
@@ -1220,8 +1277,10 @@
}
}
- // Sending a null date or date_gmt value resets date and date_gmt to their
- // default values (`0000-00-00 00:00:00`).
+ /*
+ * 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'] )
@@ -1344,7 +1403,6 @@
* @param WP_REST_Request $request Request object.
*/
return apply_filters( "rest_pre_insert_{$this->post_type}", $prepared_post, $request );
-
}
/**
@@ -1443,17 +1501,16 @@
} else {
return delete_post_thumbnail( $post_id );
}
-
}
/**
- * Check whether the template is valid for the given post.
+ * Checks whether the template is valid for the given post.
*
* @since 4.9.0
*
* @param string $template Page template filename.
* @param WP_REST_Request $request Request.
- * @return bool|WP_Error True if template is still valid or if the same as existing value, or false if template not supported.
+ * @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.
*/
public function check_template( $template, $request ) {
@@ -1691,13 +1748,16 @@
* @since 4.7.0
* @since 5.9.0 Renamed `$post` to `$item` to match parent class for PHP 8 named parameter support.
*
+ * @global WP_Post $post Global post object.
+ *
* @param WP_Post $item Post object.
* @param WP_REST_Request $request Request object.
* @return WP_REST_Response Response object.
*/
public function prepare_item_for_response( $item, $request ) {
// Restores the more descriptive, specific name for use within this method.
- $post = $item;
+ $post = $item;
+
$GLOBALS['post'] = $post;
setup_postdata( $post );
@@ -1749,7 +1809,7 @@
* with the site's timezone offset applied.
*/
if ( '0000-00-00 00:00:00' === $post->post_modified_gmt ) {
- $post_modified_gmt = gmdate( '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' ) * HOUR_IN_SECONDS ) );
} else {
$post_modified_gmt = $post->post_modified_gmt;
}
@@ -1818,6 +1878,19 @@
}
if ( rest_is_field_included( 'excerpt', $fields ) ) {
+ if ( isset( $request['excerpt_length'] ) ) {
+ $excerpt_length = $request['excerpt_length'];
+ $override_excerpt_length = static function () use ( $excerpt_length ) {
+ return $excerpt_length;
+ };
+
+ add_filter(
+ 'excerpt_length',
+ $override_excerpt_length,
+ 20
+ );
+ }
+
/** This filter is documented in wp-includes/post-template.php */
$excerpt = apply_filters( 'get_the_excerpt', $post->post_excerpt, $post );
@@ -1829,6 +1902,14 @@
'rendered' => post_password_required( $post ) ? '' : $excerpt,
'protected' => (bool) $post->post_password,
);
+
+ if ( isset( $override_excerpt_length ) ) {
+ remove_filter(
+ 'excerpt_length',
+ $override_excerpt_length,
+ 20
+ );
+ }
}
if ( $has_password_filter ) {
@@ -1917,6 +1998,10 @@
$data['generated_slug'] = $sample_permalink[1];
}
}
+
+ if ( rest_is_field_included( 'class_list', $fields ) ) {
+ $data['class_list'] = get_post_class( array(), $post->ID );
+ }
}
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
@@ -1926,16 +2011,18 @@
// Wrap the data in a response object.
$response = rest_ensure_response( $data );
- $links = $this->prepare_links( $post );
- $response->add_links( $links );
-
- if ( ! empty( $links['self']['href'] ) ) {
- $actions = $this->get_available_actions( $post, $request );
-
- $self = $links['self']['href'];
-
- foreach ( $actions as $rel ) {
- $response->add_link( $rel, $self );
+ if ( rest_is_field_included( '_links', $fields ) || rest_is_field_included( '_embedded', $fields ) ) {
+ $links = $this->prepare_links( $post );
+ $response->add_links( $links );
+
+ if ( ! empty( $links['self']['href'] ) ) {
+ $actions = $this->get_available_actions( $post, $request );
+
+ $self = $links['self']['href'];
+
+ foreach ( $actions as $rel ) {
+ $response->add_link( $rel, $self );
+ }
}
}
@@ -1983,15 +2070,13 @@
* @return array Links for the given post.
*/
protected function prepare_links( $post ) {
- $base = sprintf( '%s/%s', $this->namespace, $this->rest_base );
-
// Entity meta.
$links = array(
'self' => array(
- 'href' => rest_url( trailingslashit( $base ) . $post->ID ),
+ 'href' => rest_url( rest_get_route_for_post( $post->ID ) ),
),
'collection' => array(
- 'href' => rest_url( $base ),
+ 'href' => rest_url( rest_get_route_for_post_type_items( $this->post_type ) ),
),
'about' => array(
'href' => rest_url( 'wp/v2/types/' . $this->post_type ),
@@ -2017,20 +2102,19 @@
}
if ( in_array( $post->post_type, array( 'post', 'page' ), true ) || post_type_supports( $post->post_type, 'revisions' ) ) {
- $revisions = wp_get_post_revisions( $post->ID, array( 'fields' => 'ids' ) );
- $revisions_count = count( $revisions );
+ $revisions = wp_get_latest_revision_id_and_total_count( $post->ID );
+ $revisions_count = ! is_wp_error( $revisions ) ? $revisions['count'] : 0;
+ $revisions_base = sprintf( '/%s/%s/%d/revisions', $this->namespace, $this->rest_base, $post->ID );
$links['version-history'] = array(
- 'href' => rest_url( trailingslashit( $base ) . $post->ID . '/revisions' ),
+ 'href' => rest_url( $revisions_base ),
'count' => $revisions_count,
);
if ( $revisions_count > 0 ) {
- $last_revision = array_shift( $revisions );
-
$links['predecessor-version'] = array(
- 'href' => rest_url( trailingslashit( $base ) . $post->ID . '/revisions/' . $last_revision ),
- 'id' => $last_revision,
+ 'href' => rest_url( $revisions_base . '/' . $revisions['latest_id'] ),
+ 'id' => $revisions['latest_id'],
);
}
}
@@ -2094,7 +2178,7 @@
}
/**
- * Get the link relations available for the post and current user.
+ * Gets the link relations available for the post and current user.
*
* @since 4.9.8
*
@@ -2273,6 +2357,16 @@
'context' => array( 'edit' ),
'readonly' => true,
);
+
+ $schema['properties']['class_list'] = array(
+ 'description' => __( 'An array of the class names for the post container element.' ),
+ 'type' => 'array',
+ 'context' => array( 'view', 'edit' ),
+ 'readonly' => true,
+ 'items' => array(
+ 'type' => 'string',
+ ),
+ );
}
if ( $post_type_obj->hierarchical ) {
@@ -2324,6 +2418,7 @@
'comments',
'revisions',
'custom-fields',
+ 'thumbnail',
),
);
@@ -2582,7 +2677,7 @@
}
/**
- * Retrieve Link Description Objects that should be added to the Schema for the posts collection.
+ * Retrieves Link Description Objects that should be added to the Schema for the posts collection.
*
* @since 4.9.8
*
@@ -2844,13 +2939,22 @@
);
}
- $query_params['slug'] = array(
- 'description' => __( 'Limit result set to posts with one or more specific slugs.' ),
- 'type' => 'array',
- 'items' => array(
+ $query_params['search_columns'] = array(
+ 'default' => array(),
+ 'description' => __( 'Array of column names to be searched.' ),
+ 'type' => 'array',
+ 'items' => array(
+ 'enum' => array( 'post_title', 'post_content', 'post_excerpt' ),
'type' => 'string',
),
- 'sanitize_callback' => 'wp_parse_slug_list',
+ );
+
+ $query_params['slug'] = array(
+ 'description' => __( 'Limit result set to posts with one or more specific slugs.' ),
+ 'type' => 'array',
+ 'items' => array(
+ 'type' => 'string',
+ ),
);
$query_params['status'] = array(