Ticket #40510: 40510.3.diff
File 40510.3.diff, 26.8 KB (added by , 7 years ago) |
---|
-
src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php
diff --git src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php index 471c4c4..8566776 100644
class WP_REST_Revisions_Controller extends WP_REST_Controller { 201 201 return $parent; 202 202 } 203 203 204 $revisions = wp_get_post_revisions( $request['parent'] ); 204 // Ensure a search string is set in case the orderby is set to 'relevance'. 205 if ( ! empty( $request['orderby'] ) && 'relevance' === $request['orderby'] && empty( $request['search'] ) ) { 206 return new WP_Error( 'rest_no_search_term_defined', __( 'You need to define a search term to order by relevance.' ), array( 'status' => 400 ) ); 207 } 208 209 // Ensure an include parameter is set in case the orderby is set to 'include'. 210 if ( ! empty( $request['orderby'] ) && 'include' === $request['orderby'] && empty( $request['include'] ) ) { 211 return new WP_Error( 'rest_orderby_include_missing_include', __( 'You need to define an include parameter to order by include.' ), array( 'status' => 400 ) ); 212 } 213 214 if ( wp_revisions_enabled( $parent ) ) { 215 $registered = $this->get_collection_params(); 216 $args = array( 217 'post_parent' => $parent->ID, 218 'post_type' => 'revision', 219 'post_status' => 'inherit', 220 'posts_per_page' => -1, 221 'suppress_filters' => true, 222 ); 223 224 $parameter_mappings = array( 225 'exclude' => 'post__not_in', 226 'include' => 'post__in', 227 'offset' => 'offset', 228 'order' => 'order', 229 'orderby' => 'orderby', 230 'page' => 'paged', 231 'per_page' => 'posts_per_page', 232 'search' => 's', 233 ); 234 235 foreach ( $parameter_mappings as $api_param => $wp_param ) { 236 if ( isset( $registered[ $api_param ], $request[ $api_param ] ) ) { 237 $args[ $wp_param ] = $request[ $api_param ]; 238 } 239 } 240 241 /** This filter is documented in wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php */ 242 $args = apply_filters( "rest_revision_query", $args, $request ); 243 $query_args = $this->prepare_items_query( $args, $request ); 244 245 $revisions_query = new WP_Query(); 246 $revisions = $revisions_query->query( $query_args ); 247 $offset = isset( $query_args['offset'] ) ? (int) $query_args['offset'] : 0; 248 $page = (int) $query_args['paged']; 249 $total_revisions = $revisions_query->found_posts; 250 251 if ( $total_revisions < 1 ) { 252 // Out-of-bounds, run the query again without LIMIT for total count. 253 unset( $query_args['paged'], $query_args['offset'] ); 254 255 $count_query = new WP_Query(); 256 $count_query->query( $query_args ); 257 258 $total_revisions = $count_query->found_posts; 259 } 260 261 if ( $revisions_query->query_vars['posts_per_page'] > 0 ) { 262 $max_pages = ceil( $total_revisions / (int) $revisions_query->query_vars['posts_per_page'] ); 263 } else { 264 $max_pages = $total_revisions > 0 ? 1 : 0; 265 } 266 267 if ( $total_revisions > 0 ) { 268 if ( $page > $max_pages ) { 269 return new WP_Error( 'rest_revision_invalid_page_number', __( 'The page number requested is larger than the number of pages available.' ), array( 'status' => 400 ) ); 270 } elseif ( $offset > $total_revisions ) { 271 return new WP_Error( 'rest_revision_invalid_offset_number', __( 'The offset number requested is larger than the number of available revisions.' ), array( 'status' => 400 ) ); 272 } 273 } 274 } else { 275 $revisions = array(); 276 $total_revisions = 0; 277 $max_pages = 0; 278 $page = (int) $request['page']; 279 } 205 280 206 281 $response = array(); 207 282 foreach ( $revisions as $revision ) { 208 283 $data = $this->prepare_item_for_response( $revision, $request ); 209 284 $response[] = $this->prepare_response_for_collection( $data ); 210 285 } 211 return rest_ensure_response( $response ); 286 287 $response = rest_ensure_response( $response ); 288 289 $response->header( 'X-WP-Total', (int) $total_revisions ); 290 $response->header( 'X-WP-TotalPages', (int) $max_pages ); 291 292 $request_params = $request->get_query_params(); 293 $base = add_query_arg( $request_params, rest_url( sprintf( '%s/%s/%d/%s', $this->namespace, $this->parent_base, $request['parent'], $this->rest_base ) ) ); 294 295 if ( $page > 1 ) { 296 $prev_page = $page - 1; 297 298 if ( $prev_page > $max_pages ) { 299 $prev_page = $max_pages; 300 } 301 302 $prev_link = add_query_arg( 'page', $prev_page, $base ); 303 $response->link_header( 'prev', $prev_link ); 304 } 305 if ( $max_pages > $page ) { 306 $next_page = $page + 1; 307 $next_link = add_query_arg( 'page', $next_page, $base ); 308 309 $response->link_header( 'next', $next_link ); 310 } 311 312 return $response; 212 313 } 213 314 214 315 /** … … class WP_REST_Revisions_Controller extends WP_REST_Controller { 327 428 } 328 429 329 430 /** 431 * Determines the allowed query_vars for a get_items() response and prepares 432 * them for WP_Query. 433 * 434 * @since 5.0.0 435 * 436 * @param array $prepared_args Optional. Prepared WP_Query arguments. Default empty array. 437 * @param WP_REST_Request $request Optional. Full details about the request. 438 * @return array Items query arguments. 439 */ 440 protected function prepare_items_query( $prepared_args = array(), $request = null ) { 441 $query_args = array(); 442 443 foreach ( $prepared_args as $key => $value ) { 444 /** This filter is documented in wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php */ 445 $query_args[ $key ] = apply_filters( "rest_query_var-{$key}", $value ); 446 } 447 448 // Map to proper WP_Query orderby param. 449 if ( isset( $query_args['orderby'] ) && isset( $request['orderby'] ) ) { 450 $orderby_mappings = array( 451 'id' => 'ID', 452 'include' => 'post__in', 453 'slug' => 'post_name', 454 'include_slugs' => 'post_name__in', 455 ); 456 457 if ( isset( $orderby_mappings[ $request['orderby'] ] ) ) { 458 $query_args['orderby'] = $orderby_mappings[ $request['orderby'] ]; 459 } 460 } 461 462 return $query_args; 463 } 464 465 /** 330 466 * Prepares the revision for the REST response. 331 467 * 332 468 * @since 4.7.0 … … class WP_REST_Revisions_Controller extends WP_REST_Controller { 547 683 * @return array Collection parameters. 548 684 */ 549 685 public function get_collection_params() { 550 return array( 551 'context' => $this->get_context_param( array( 'default' => 'view' ) ), 686 $query_params = parent::get_collection_params(); 687 688 $query_params['context']['default'] = 'view'; 689 690 unset( $query_params['per_page']['default'] ); 691 692 $query_params['exclude'] = array( 693 'description' => __( 'Ensure result set excludes specific IDs.' ), 694 'type' => 'array', 695 'items' => array( 696 'type' => 'integer', 697 ), 698 'default' => array(), 699 ); 700 701 $query_params['include'] = array( 702 'description' => __( 'Limit result set to specific IDs.' ), 703 'type' => 'array', 704 'items' => array( 705 'type' => 'integer', 706 ), 707 'default' => array(), 708 ); 709 710 $query_params['offset'] = array( 711 'description' => __( 'Offset the result set by a specific number of items.' ), 712 'type' => 'integer', 552 713 ); 714 715 $query_params['order'] = array( 716 'description' => __( 'Order sort attribute ascending or descending.' ), 717 'type' => 'string', 718 'default' => 'desc', 719 'enum' => array( 'asc', 'desc' ), 720 ); 721 722 $query_params['orderby'] = array( 723 'description' => __( 'Sort collection by object attribute.' ), 724 'type' => 'string', 725 'default' => 'date', 726 'enum' => array( 727 'date', 728 'id', 729 'include', 730 'relevance', 731 'slug', 732 'include_slugs', 733 'title', 734 ), 735 ); 736 737 return $query_params; 553 738 } 554 739 555 740 /** -
tests/phpunit/tests/rest-api/rest-revisions-controller.php
diff --git tests/phpunit/tests/rest-api/rest-revisions-controller.php tests/phpunit/tests/rest-api/rest-revisions-controller.php index c30ab17..3624c2b 100644
class WP_Test_REST_Revisions_Controller extends WP_Test_REST_Controller_Testcase 44 44 'ID' => self::$post_id, 45 45 ) 46 46 ); 47 wp_update_post( 48 array( 49 'post_content' => 'This content is fantastic.', 50 'ID' => self::$post_id, 51 ) 52 ); 47 53 wp_set_current_user( 0 ); 48 54 } 49 55 … … class WP_Test_REST_Revisions_Controller extends WP_Test_REST_Controller_Testcase 59 65 public function setUp() { 60 66 parent::setUp(); 61 67 62 $revisions = wp_get_post_revisions( self::$post_id ); 63 $this->revision_1 = array_pop( $revisions ); 64 $this->revision_id1 = $this->revision_1->ID; 65 $this->revision_2 = array_pop( $revisions ); 66 $this->revision_id2 = $this->revision_2->ID; 68 $revisions = wp_get_post_revisions( self::$post_id ); 69 $this->total_revisions = count( $revisions ); 70 $this->revisions = $revisions; 71 $this->revision_1 = array_pop( $revisions ); 72 $this->revision_id1 = $this->revision_1->ID; 73 $this->revision_2 = array_pop( $revisions ); 74 $this->revision_id2 = $this->revision_2->ID; 75 $this->revision_3 = array_pop( $revisions ); 76 $this->revision_id3 = $this->revision_3->ID; 67 77 } 68 78 69 79 public function test_register_routes() { … … class WP_Test_REST_Revisions_Controller extends WP_Test_REST_Controller_Testcase 95 105 $response = rest_get_server()->dispatch( $request ); 96 106 $data = $response->get_data(); 97 107 $this->assertEquals( 200, $response->get_status() ); 98 $this->assertCount( 2, $data );108 $this->assertCount( $this->total_revisions, $data ); 99 109 100 110 // Reverse chron 101 $this->assertEquals( $this->revision_id2, $data[0]['id'] ); 102 $this->check_get_revision_response( $data[0], $this->revision_2 ); 103 104 $this->assertEquals( $this->revision_id1, $data[1]['id'] ); 105 $this->check_get_revision_response( $data[1], $this->revision_1 ); 111 $this->assertEquals( $this->revision_id3, $data[2]['id'] ); 112 $this->check_get_revision_response( $data[2], $this->revision_3 ); 113 114 $this->assertEquals( $this->revision_id2, $data[1]['id'] ); 115 $this->check_get_revision_response( $data[1], $this->revision_2 ); 116 117 $this->assertEquals( $this->revision_id1, $data[0]['id'] ); 118 $this->check_get_revision_response( $data[0], $this->revision_1 ); 106 119 } 107 120 108 121 public function test_get_items_no_permission() { … … class WP_Test_REST_Revisions_Controller extends WP_Test_REST_Controller_Testcase 363 376 $this->assertEquals( $parent_post_id, self::$post_id ); 364 377 } 365 378 379 /** 380 * Test the pagination header of the first page. 381 * 382 * @ticket 40510 383 */ 384 public function test_get_items_pagination_header_of_the_first_page() { 385 wp_set_current_user( self::$editor_id ); 386 387 $rest_route = '/wp/v2/posts/' . self::$post_id . '/revisions'; 388 $per_page = 2; 389 $total_pages = (int) ceil( $this->total_revisions / $per_page ); 390 $page = 1; // First page. 391 392 $request = new WP_REST_Request( 'GET', $rest_route ); 393 $request->set_query_params( array( 394 'per_page' => $per_page, 395 'page' => $page, 396 )); 397 $response = rest_get_server()->dispatch( $request ); 398 $headers = $response->get_headers(); 399 $this->assertSame( $this->total_revisions, $headers['X-WP-Total'] ); 400 $this->assertSame( $total_pages, $headers['X-WP-TotalPages'] ); 401 $next_link = add_query_arg( 402 array( 403 'per_page' => $per_page, 404 'page' => $page + 1, 405 ), rest_url( $rest_route ) 406 ); 407 $this->assertFalse( stripos( $headers['Link'], 'rel="prev"' ) ); 408 $this->assertContains( '<' . $next_link . '>; rel="next"', $headers['Link'] ); 409 } 410 411 /** 412 * Test the pagination header of the last page. 413 * 414 * @ticket 40510 415 */ 416 public function test_get_items_pagination_header_of_the_last_page() { 417 wp_set_current_user( self::$editor_id ); 418 419 $rest_route = '/wp/v2/posts/' . self::$post_id . '/revisions'; 420 $per_page = 2; 421 $total_pages = (int) ceil( $this->total_revisions / $per_page ); 422 $page = 2; // Last page. 423 424 $request = new WP_REST_Request( 'GET', $rest_route ); 425 $request->set_query_params( array( 426 'per_page' => $per_page, 427 'page' => $page, 428 )); 429 $response = rest_get_server()->dispatch( $request ); 430 $headers = $response->get_headers(); 431 $this->assertSame( $this->total_revisions, $headers['X-WP-Total'] ); 432 $this->assertSame( $total_pages, $headers['X-WP-TotalPages'] ); 433 $prev_link = add_query_arg( 434 array( 435 'per_page' => $per_page, 436 'page' => $page - 1, 437 ), rest_url( $rest_route ) 438 ); 439 $this->assertContains( '<' . $prev_link . '>; rel="prev"', $headers['Link'] ); 440 } 441 442 /** 443 * Test that invalid 'per_page' query should error. 444 * 445 * @ticket 40510 446 */ 447 public function test_get_items_invalid_per_page_should_error() { 448 wp_set_current_user( self::$editor_id ); 449 450 $per_page = -1; // Invalid number. 451 $expected_error = 'rest_invalid_param'; 452 $expected_status = 400; 453 454 $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions' ); 455 $request->set_param( 'per_page', $per_page ); 456 $response = rest_get_server()->dispatch( $request ); 457 $this->assertErrorResponse( $expected_error, $response, $expected_status ); 458 } 459 460 /** 461 * Test that out of bounds 'page' query should error. 462 * 463 * @ticket 40510 464 */ 465 public function test_get_items_out_of_bounds_page_should_error() { 466 wp_set_current_user( self::$editor_id ); 467 468 $per_page = 2; 469 $total_pages = (int) ceil( $this->total_revisions / $per_page ); 470 $page = $total_pages + 1; // Out of bound page. 471 $expected_error = 'rest_revision_invalid_page_number'; 472 $expected_status = 400; 473 474 $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions' ); 475 $request->set_query_params( array( 476 'per_page' => $per_page, 477 'page' => $page, 478 )); 479 $response = rest_get_server()->dispatch( $request ); 480 $this->assertErrorResponse( $expected_error, $response, $expected_status ); 481 } 482 483 /** 484 * Test that impossibly high 'page' query should error. 485 * 486 * @ticket 40510 487 */ 488 public function test_get_items_invalid_max_pages_should_error() { 489 wp_set_current_user( self::$editor_id ); 490 491 $per_page = 2; 492 $page = REST_TESTS_IMPOSSIBLY_HIGH_NUMBER; // Invalid number. 493 $expected_error = 'rest_revision_invalid_page_number'; 494 $expected_status = 400; 495 496 $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions' ); 497 $request->set_query_params( array( 498 'per_page' => $per_page, 499 'page' => $page, 500 )); 501 $response = rest_get_server()->dispatch( $request ); 502 $this->assertErrorResponse( $expected_error, $response, $expected_status ); 503 } 504 505 /** 506 * Test the search query. 507 * 508 * @ticket 40510 509 */ 510 public function test_get_items_search_query() { 511 wp_set_current_user( self::$editor_id ); 512 513 $search_string = 'better'; 514 $expected_count = 1; 515 $expected_content = 'This content is better.'; 516 517 $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions' ); 518 $request->set_param( 'search', $search_string ); 519 $response = rest_get_server()->dispatch( $request ); 520 $data = $response->get_data(); 521 $this->assertCount( $expected_count, $data ); 522 $this->assertContains( $expected_content, $data[0]['content']['rendered'] ); 523 } 524 525 /** 526 * Test that the default query should fetch all revisions. 527 * 528 * @ticket 40510 529 */ 530 public function test_get_items_default_query_should_fetch_all_revisons() { 531 wp_set_current_user( self::$editor_id ); 532 533 $expected_count = $this->total_revisions; 534 535 $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions' ); 536 $response = rest_get_server()->dispatch( $request ); 537 $this->assertCount( $expected_count, $response->get_data() ); 538 } 539 540 /** 541 * Test that 'offset' query shouldn't work without 'per_page' (fallback -1). 542 * 543 * @ticket 40510 544 */ 545 public function test_get_items_offset_should_not_work_without_per_page() { 546 wp_set_current_user( self::$editor_id ); 547 548 $offset = 1; 549 $expected_count = $this->total_revisions; 550 551 $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions' ); 552 $request->set_param( 'offset', $offset ); 553 $response = rest_get_server()->dispatch( $request ); 554 $this->assertCount( $expected_count, $response->get_data() ); 555 } 556 557 /** 558 * Test that 'offset' query should work with 'per_page'. 559 * 560 * @ticket 40510 561 */ 562 public function test_get_items_offset_should_work_with_per_page() { 563 wp_set_current_user( self::$editor_id ); 564 565 $per_page = 2; 566 $offset = 1; 567 $expected_count = 2; 568 569 $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions' ); 570 $request->set_query_params( array( 571 'offset' => $offset, 572 'per_page' => $per_page, 573 )); 574 $response = rest_get_server()->dispatch( $request ); 575 $this->assertCount( $expected_count, $response->get_data() ); 576 } 577 578 /** 579 * Test that 'offset' query should take priority over 'page'. 580 * 581 * @ticket 40510 582 */ 583 public function test_get_items_offset_should_take_priority_over_page() { 584 wp_set_current_user( self::$editor_id ); 585 586 $per_page = 2; 587 $offset = 1; 588 $page = 1; 589 $expected_count = 2; 590 591 $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions' ); 592 $request->set_query_params( array( 593 'offset' => $offset, 594 'per_page' => $per_page, 595 'page' => $page, 596 )); 597 $response = rest_get_server()->dispatch( $request ); 598 $this->assertCount( $expected_count, $response->get_data() ); 599 } 600 601 /** 602 * Test that 'offset' query, as the total revisions count, should return empty data. 603 * 604 * @ticket 40510 605 */ 606 public function test_get_items_total_revisions_offset_should_return_empty_data() { 607 wp_set_current_user( self::$editor_id ); 608 609 $per_page = 2; 610 $offset = $this->total_revisions; 611 $expected_count = 0; 612 613 $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions' ); 614 $request->set_query_params( array( 615 'offset' => $offset, 616 'per_page' => $per_page, 617 )); 618 $response = rest_get_server()->dispatch( $request ); 619 $this->assertCount( $expected_count, $response->get_data() ); 620 } 621 622 /** 623 * Test that out of bound 'offset' query should error. 624 * 625 * @ticket 40510 626 */ 627 public function test_get_items_out_of_bound_offset_should_error() { 628 wp_set_current_user( self::$editor_id ); 629 630 $per_page = 2; 631 $offset = $this->total_revisions + 1; 632 $expected_error = 'rest_revision_invalid_offset_number'; 633 $expected_status = 400; 634 635 $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions' ); 636 $request->set_query_params( array( 637 'offset' => $offset, 638 'per_page' => $per_page, 639 )); 640 $response = rest_get_server()->dispatch( $request ); 641 $this->assertErrorResponse( $expected_error, $response, $expected_status ); 642 } 643 644 /** 645 * Test that impossible high number for 'offset' query should error. 646 * 647 * @ticket 40510 648 */ 649 public function test_get_items_impossible_high_number_offset_should_error() { 650 wp_set_current_user( self::$editor_id ); 651 652 $per_page = 2; 653 $offset = REST_TESTS_IMPOSSIBLY_HIGH_NUMBER; 654 $expected_error = 'rest_revision_invalid_offset_number'; 655 $expected_status = 400; 656 657 $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions' ); 658 $request->set_query_params( array( 659 'offset' => $offset, 660 'per_page' => $per_page, 661 )); 662 $response = rest_get_server()->dispatch( $request ); 663 $this->assertErrorResponse( $expected_error, $response, $expected_status ); 664 } 665 666 /** 667 * Test that invalid 'offset' query should error. 668 * 669 * @ticket 40510 670 */ 671 public function test_get_items_invalid_offset_should_error() { 672 wp_set_current_user( self::$editor_id ); 673 674 $per_page = 2; 675 $offset = 'moreplease'; 676 $expected_error = 'rest_invalid_param'; 677 $expected_status = 400; 678 679 $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions' ); 680 $request->set_query_params( array( 681 'offset' => $offset, 682 'per_page' => $per_page, 683 )); 684 $response = rest_get_server()->dispatch( $request ); 685 $this->assertErrorResponse( $expected_error, $response, $expected_status ); 686 } 366 687 } -
tests/qunit/fixtures/wp-api-generated.js
diff --git tests/qunit/fixtures/wp-api-generated.js tests/qunit/fixtures/wp-api-generated.js index 583843f..73967d8 100644
mockedApiResponse.Schema = { 718 718 ], 719 719 "description": "Scope under which the request is made; determines fields present in response.", 720 720 "type": "string" 721 }, 722 "page": { 723 "required": false, 724 "default": 1, 725 "description": "Current page of the collection.", 726 "type": "integer" 727 }, 728 "per_page": { 729 "required": false, 730 "description": "Maximum number of items to be returned in result set.", 731 "type": "integer" 732 }, 733 "search": { 734 "required": false, 735 "description": "Limit results to those matching a string.", 736 "type": "string" 737 }, 738 "exclude": { 739 "required": false, 740 "default": [], 741 "description": "Ensure result set excludes specific IDs.", 742 "type": "array", 743 "items": { 744 "type": "integer" 745 } 746 }, 747 "include": { 748 "required": false, 749 "default": [], 750 "description": "Limit result set to specific IDs.", 751 "type": "array", 752 "items": { 753 "type": "integer" 754 } 755 }, 756 "offset": { 757 "required": false, 758 "description": "Offset the result set by a specific number of items.", 759 "type": "integer" 760 }, 761 "order": { 762 "required": false, 763 "default": "desc", 764 "enum": [ 765 "asc", 766 "desc" 767 ], 768 "description": "Order sort attribute ascending or descending.", 769 "type": "string" 770 }, 771 "orderby": { 772 "required": false, 773 "default": "date", 774 "enum": [ 775 "date", 776 "id", 777 "include", 778 "relevance", 779 "slug", 780 "include_slugs", 781 "title" 782 ], 783 "description": "Sort collection by object attribute.", 784 "type": "string" 721 785 } 722 786 } 723 787 } … … mockedApiResponse.Schema = { 1256 1320 ], 1257 1321 "description": "Scope under which the request is made; determines fields present in response.", 1258 1322 "type": "string" 1323 }, 1324 "page": { 1325 "required": false, 1326 "default": 1, 1327 "description": "Current page of the collection.", 1328 "type": "integer" 1329 }, 1330 "per_page": { 1331 "required": false, 1332 "description": "Maximum number of items to be returned in result set.", 1333 "type": "integer" 1334 }, 1335 "search": { 1336 "required": false, 1337 "description": "Limit results to those matching a string.", 1338 "type": "string" 1339 }, 1340 "exclude": { 1341 "required": false, 1342 "default": [], 1343 "description": "Ensure result set excludes specific IDs.", 1344 "type": "array", 1345 "items": { 1346 "type": "integer" 1347 } 1348 }, 1349 "include": { 1350 "required": false, 1351 "default": [], 1352 "description": "Limit result set to specific IDs.", 1353 "type": "array", 1354 "items": { 1355 "type": "integer" 1356 } 1357 }, 1358 "offset": { 1359 "required": false, 1360 "description": "Offset the result set by a specific number of items.", 1361 "type": "integer" 1362 }, 1363 "order": { 1364 "required": false, 1365 "default": "desc", 1366 "enum": [ 1367 "asc", 1368 "desc" 1369 ], 1370 "description": "Order sort attribute ascending or descending.", 1371 "type": "string" 1372 }, 1373 "orderby": { 1374 "required": false, 1375 "default": "date", 1376 "enum": [ 1377 "date", 1378 "id", 1379 "include", 1380 "relevance", 1381 "slug", 1382 "include_slugs", 1383 "title" 1384 ], 1385 "description": "Sort collection by object attribute.", 1386 "type": "string" 1259 1387 } 1260 1388 } 1261 1389 }