From f71d5f06b2e3327c40bfb6102fec5d37ee87faef Mon Sep 17 00:00:00 2001 From: Peter Wilson Date: Mon, 10 Feb 2025 22:21:51 +0000 Subject: [PATCH] REST API: Add support for the `ignore_sticky_posts` argument. Introduce `ignore_sticky` as a boolean argument for the posts endpoint for requests without the sticky posts being stuck. The new argument defaults to `false` with the value of the argument passed to `WP_Query`'s `ignore_sticky_posts` parameter. Props audrasjb, danielbachhuber, joemcgill, johnbillion, jorbin, mamaduka, rmccue. Fixes #35907. git-svn-id: https://develop.svn.wordpress.org/trunk@59801 602fd350-edb4-49c9-b593-d223f7449a82 --- .../class-wp-rest-posts-controller.php | 15 +++++ .../tests/rest-api/rest-posts-controller.php | 66 +++++++++++++++++++ tests/qunit/fixtures/wp-api-generated.js | 6 ++ 3 files changed, 87 insertions(+) diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php index 6f872be3df..cb25207fc5 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php @@ -247,6 +247,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller { 'author_exclude' => 'author__not_in', 'exclude' => 'post__not_in', 'include' => 'post__in', + 'ignore_sticky' => 'ignore_sticky_posts', 'menu_order' => 'menu_order', 'offset' => 'offset', 'order' => 'order', @@ -337,6 +338,14 @@ class WP_REST_Posts_Controller extends WP_REST_Controller { } } + /* + * Honor the original REST API `post__in` behavior. Don't prepend sticky posts + * when `post__in` has been specified. + */ + if ( ! empty( $args['post__in'] ) ) { + unset( $args['ignore_sticky_posts'] ); + } + if ( isset( $registered['search_semantics'], $request['search_semantics'] ) && 'exact' === $request['search_semantics'] @@ -3045,6 +3054,12 @@ class WP_REST_Posts_Controller extends WP_REST_Controller { 'description' => __( 'Limit result set to items that are sticky.' ), 'type' => 'boolean', ); + + $query_params['ignore_sticky'] = array( + 'description' => __( 'Whether to ignore sticky posts or not.' ), + 'type' => 'boolean', + 'default' => false, + ); } if ( post_type_supports( $this->post_type, 'post-formats' ) ) { diff --git a/tests/phpunit/tests/rest-api/rest-posts-controller.php b/tests/phpunit/tests/rest-api/rest-posts-controller.php index c42fded132..a09d6d33ba 100644 --- a/tests/phpunit/tests/rest-api/rest-posts-controller.php +++ b/tests/phpunit/tests/rest-api/rest-posts-controller.php @@ -197,6 +197,7 @@ class WP_Test_REST_Posts_Controller extends WP_Test_REST_Post_Type_Controller_Te 'context', 'exclude', 'format', + 'ignore_sticky', 'include', 'modified_after', 'modified_before', @@ -5715,6 +5716,71 @@ Shankle pork chop prosciutto ribeye ham hock pastrami. T-bone shank brisket baco $this->assertErrorResponse( 'rest_post_invalid_page_number', $response, 400 ); } + /** + * Test the REST API support for `ignore_sticky_posts`. + * + * @ticket 35907 + * + * @covers WP_REST_Posts_Controller::get_items + */ + public function test_get_posts_ignore_sticky_default_prepends_sticky_posts() { + $id1 = self::$post_id; + // Create more recent post to avoid automatically placing other at the top. + $id2 = self::factory()->post->create( array( 'post_status' => 'publish' ) ); + + update_option( 'sticky_posts', array( $id1 ) ); + + $request = new WP_REST_Request( 'GET', '/wp/v2/posts' ); + $response = rest_get_server()->dispatch( $request ); + $data = $response->get_data(); + + $this->assertSame( $data[0]['id'], $id1, 'Response has sticky post at the top.' ); + $this->assertSame( $data[1]['id'], $id2, 'It is followed by most recent post.' ); + } + + /** + * Test the REST API support for `ignore_sticky_posts`. + * + * @ticket 35907 + * + * @covers WP_REST_Posts_Controller::get_items + */ + public function test_get_posts_ignore_sticky_ignores_post_stickiness() { + $id1 = self::$post_id; + $id2 = self::factory()->post->create( array( 'post_status' => 'publish' ) ); + + update_option( 'sticky_posts', array( $id1 ) ); + + $request = new WP_REST_Request( 'GET', '/wp/v2/posts' ); + $request->set_param( 'ignore_sticky', true ); + $response = rest_get_server()->dispatch( $request ); + $data = $response->get_data(); + + $this->assertSame( $data[0]['id'], $id2, 'Response has no sticky post at the top.' ); + } + + /** + * Test the REST API support for `ignore_sticky_posts`. + * + * @ticket 35907 + * + * @covers WP_REST_Posts_Controller::get_items + */ + public function test_get_posts_ignore_sticky_honors_include() { + $id1 = self::$post_id; + $id2 = self::factory()->post->create( array( 'post_status' => 'publish' ) ); + + update_option( 'sticky_posts', array( $id1 ) ); + + $request = new WP_REST_Request( 'GET', '/wp/v2/posts' ); + $request->set_param( 'include', array( $id2 ) ); + $response = rest_get_server()->dispatch( $request ); + $data = $response->get_data(); + + $this->assertCount( 1, $data, 'Only one post is expected to be returned.' ); + $this->assertSame( $data[0]['id'], $id2, 'Returns the included post.' ); + } + /** * Internal function used to disable an insert query which * will trigger a wpdb error for testing purposes. diff --git a/tests/qunit/fixtures/wp-api-generated.js b/tests/qunit/fixtures/wp-api-generated.js index 448548aab0..f3a1f21e48 100644 --- a/tests/qunit/fixtures/wp-api-generated.js +++ b/tests/qunit/fixtures/wp-api-generated.js @@ -627,6 +627,12 @@ mockedApiResponse.Schema = { "type": "boolean", "required": false }, + "ignore_sticky": { + "description": "Whether to ignore sticky posts or not.", + "type": "boolean", + "default": false, + "required": false + }, "format": { "description": "Limit result set to items assigned one or more given formats.", "type": "array",