From 6dd0d834c6c3ddd7ff534340ef7077082bb0f5d9 Mon Sep 17 00:00:00 2001 From: Daniel Bachhuber Date: Wed, 9 May 2018 05:27:49 -0700 Subject: [PATCH 1/3] Permit unbounded requests for Pages and Shared Blocks --- editor/components/page-attributes/parent.js | 2 +- editor/store/effects.js | 2 +- lib/rest-api.php | 54 ++++++++++++++++++++- phpunit/class-gutenberg-rest-api-test.php | 20 ++++++++ 4 files changed, 74 insertions(+), 4 deletions(-) diff --git a/editor/components/page-attributes/parent.js b/editor/components/page-attributes/parent.js index 0b45d8e01747c..09b1a3e0963b3 100644 --- a/editor/components/page-attributes/parent.js +++ b/editor/components/page-attributes/parent.js @@ -63,7 +63,7 @@ const applyWithAPIDataItems = withAPIData( ( props, { type } ) => { const isHierarchical = get( props, [ 'postType', 'hierarchical' ], false ); const queryString = stringify( { context: 'edit', - per_page: 100, + per_page: -1, exclude: postId, parent_exclude: postId, _fields: [ 'id', 'parent', 'title' ], diff --git a/editor/store/effects.js b/editor/store/effects.js index ad08123a54118..ca7f57f15bb43 100644 --- a/editor/store/effects.js +++ b/editor/store/effects.js @@ -398,7 +398,7 @@ export default { if ( id ) { result = wp.apiRequest( { path: `/wp/v2/${ basePath }/${ id }` } ); } else { - result = wp.apiRequest( { path: `/wp/v2/${ basePath }` } ); + result = wp.apiRequest( { path: `/wp/v2/${ basePath }?per_page=-1` } ); } result.then( diff --git a/lib/rest-api.php b/lib/rest-api.php index e69e2933e86d0..660150ab08533 100644 --- a/lib/rest-api.php +++ b/lib/rest-api.php @@ -309,6 +309,8 @@ function gutenberg_register_post_prepare_functions( $post_type ) { add_filter( "rest_prepare_{$post_type}", 'gutenberg_add_permalink_template_to_posts', 10, 3 ); add_filter( "rest_prepare_{$post_type}", 'gutenberg_add_block_format_to_post_content', 10, 3 ); add_filter( "rest_prepare_{$post_type}", 'gutenberg_add_target_schema_to_links', 10, 3 ); + add_filter( "rest_{$post_type}_collection_params", 'gutenberg_filter_post_collection_parameters', 10, 2 ); + add_filter( "rest_{$post_type}_query", 'gutenberg_filter_post_query_arguments', 10, 2 ); return $post_type; } add_filter( 'registered_post_type', 'gutenberg_register_post_prepare_functions' ); @@ -423,7 +425,12 @@ function gutenberg_ensure_wp_json_has_theme_supports( $response ) { * @param WP_REST_Request $request Request used to generate the response. */ function gutenberg_handle_early_callback_checks( $response, $handler, $request ) { - if ( '/wp/v2/users' === $request->get_route() ) { + $routes = array( + '/wp/v2/blocks', + '/wp/v2/pages', + '/wp/v2/users', + ); + if ( in_array( $request->get_route(), $routes ) ) { $can_view_authors = false; $can_unbounded_query = false; $types = get_post_types( array( 'show_in_rest' => true ), 'objects' ); @@ -440,7 +447,8 @@ function gutenberg_handle_early_callback_checks( $response, $handler, $request ) return new WP_Error( 'rest_forbidden_per_page', __( 'Sorry, you are not allowed make unbounded queries.', 'gutenberg' ), array( 'status' => rest_authorization_required_code() ) ); } } - if ( ! empty( $request['who'] ) && 'authors' === $request['who'] ) { + if ( '/wp/v2/users' === $request->get_route() + && ! empty( $request['who'] ) && 'authors' === $request['who'] ) { if ( ! $can_view_authors ) { return new WP_Error( 'rest_forbidden_who', __( 'Sorry, you are not allowed to query users by this parameter.', 'gutenberg' ), array( 'status' => rest_authorization_required_code() ) ); } @@ -450,6 +458,48 @@ function gutenberg_handle_early_callback_checks( $response, $handler, $request ) } add_filter( 'rest_request_before_callbacks', 'gutenberg_handle_early_callback_checks', 10, 3 ); +/** + * Include additional query parameters on the posts query endpoint. + * + * @see https://core.trac.wordpress.org/ticket/43998 + * + * @param array $query_params JSON Schema-formatted collection parameters. + * @param string $post_type Post type being accessed. + * @return array + */ +function gutenberg_filter_post_collection_parameters( $query_params, $post_type ) { + $post_types = array( 'page', 'wp_block' ); + if ( in_array( $post_type->name, $post_types, true ) + && isset( $query_params['per_page'] ) ) { + // Change from '1' to '-1', which means unlimited. + $query_params['per_page']['minimum'] = -1; + // Default sanitize callback is 'absint', which won't work in our case. + $query_params['per_page']['sanitize_callback'] = 'rest_sanitize_request_arg'; + } + return $query_params; +} + +/** + * Filter post collection query parameters to include specific behavior. + * + * @see https://core.trac.wordpress.org/ticket/43998 + * + * @param array $prepared_args Array of arguments for WP_Query. + * @param WP_REST_Request $request The current request. + * @return array + */ +function gutenberg_filter_post_query_arguments( $prepared_args, $request ) { + $post_types = array( 'page', 'wp_block' ); + if ( in_array( $prepared_args['post_type'], $post_types, true ) ) { + // Avoid triggering 'rest_post_invalid_page_number' error + // which will need to be addressed in https://core.trac.wordpress.org/ticket/43998 + if ( -1 === $prepared_args['posts_per_page'] ) { + $prepared_args['posts_per_page'] = 100000; + } + } + return $prepared_args; +} + /** * Include additional query parameters on the user query endpoint. * diff --git a/phpunit/class-gutenberg-rest-api-test.php b/phpunit/class-gutenberg-rest-api-test.php index 2d6f142702e58..70d19d6657080 100644 --- a/phpunit/class-gutenberg-rest-api-test.php +++ b/phpunit/class-gutenberg-rest-api-test.php @@ -216,4 +216,24 @@ public function test_get_items_unbounded_per_page_unauthorized() { $data = $response->get_data(); $this->assertEquals( 'rest_forbidden_per_page', $data['code'] ); } + + public function test_get_pages_unbounded_per_page() { + wp_set_current_user( $this->author ); + $this->factory->post->create( array( 'post_type' => 'page' ) ); + $request = new WP_REST_Request( 'GET', '/wp/v2/pages' ); + $request->set_param( 'per_page', '-1' ); + $response = rest_get_server()->dispatch( $request ); + $this->assertEquals( 200, $response->get_status() ); + } + + public function test_get_pages_unbounded_per_page_unauthorized() { + wp_set_current_user( $this->subscriber ); + $this->factory->post->create( array( 'post_type' => 'page' ) ); + $request = new WP_REST_Request( 'GET', '/wp/v2/pages' ); + $request->set_param( 'per_page', '-1' ); + $response = rest_get_server()->dispatch( $request ); + $this->assertEquals( 403, $response->get_status() ); + $data = $response->get_data(); + $this->assertEquals( 'rest_forbidden_per_page', $data['code'] ); + } } From 020592369b99b5248aae077899f7a9659d7abbc0 Mon Sep 17 00:00:00 2001 From: Daniel Bachhuber Date: Wed, 9 May 2018 05:45:22 -0700 Subject: [PATCH 2/3] Fix PHPCS issue --- lib/rest-api.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rest-api.php b/lib/rest-api.php index 660150ab08533..91861fe767a30 100644 --- a/lib/rest-api.php +++ b/lib/rest-api.php @@ -492,7 +492,7 @@ function gutenberg_filter_post_query_arguments( $prepared_args, $request ) { $post_types = array( 'page', 'wp_block' ); if ( in_array( $prepared_args['post_type'], $post_types, true ) ) { // Avoid triggering 'rest_post_invalid_page_number' error - // which will need to be addressed in https://core.trac.wordpress.org/ticket/43998 + // which will need to be addressed in https://core.trac.wordpress.org/ticket/43998. if ( -1 === $prepared_args['posts_per_page'] ) { $prepared_args['posts_per_page'] = 100000; } From 3cf6f5e450a3cc9a667e979e931923653a97a56c Mon Sep 17 00:00:00 2001 From: Daniel Bachhuber Date: Thu, 10 May 2018 10:19:14 -0700 Subject: [PATCH 3/3] Use a strict check --- lib/rest-api.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rest-api.php b/lib/rest-api.php index 91861fe767a30..0ba2ea7649f22 100644 --- a/lib/rest-api.php +++ b/lib/rest-api.php @@ -430,7 +430,7 @@ function gutenberg_handle_early_callback_checks( $response, $handler, $request ) '/wp/v2/pages', '/wp/v2/users', ); - if ( in_array( $request->get_route(), $routes ) ) { + if ( in_array( $request->get_route(), $routes, true ) ) { $can_view_authors = false; $can_unbounded_query = false; $types = get_post_types( array( 'show_in_rest' => true ), 'objects' );