Ticket #44975: 44975.6.diff
File 44975.6.diff, 23.0 KB (added by , 5 years ago) |
---|
-
src/wp-includes/rest-api.php
diff --git a/src/wp-includes/rest-api.php b/src/wp-includes/rest-api.php index 4b49ebfdcb..5bd3419fe8 100644
a b function rest_get_avatar_sizes() { 1144 1144 * @return true|WP_Error 1145 1145 */ 1146 1146 function rest_validate_value_from_schema( $value, $args, $param = '' ) { 1147 if ( is_array( $args['type'] ) ) { 1148 foreach ( $args['type'] as $type ) { 1149 $type_args = $args; 1150 $type_args['type'] = $type; 1151 1152 if ( true === rest_validate_value_from_schema( $value, $type_args, $param ) ) { 1153 return true; 1154 } 1155 } 1156 1157 /* translators: 1: Parameter, 2: List of types. */ 1158 return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s is not of type %2$s' ), $param, implode( ',', $args['type'] ) ) ); 1159 } 1160 1147 1161 if ( 'array' === $args['type'] ) { 1148 1162 if ( ! is_null( $value ) ) { 1149 1163 $value = wp_parse_list( $value ); … … function rest_validate_value_from_schema( $value, $args, $param = '' ) { 1196 1210 } 1197 1211 } 1198 1212 1213 if ( 'null' === $args['type'] ) { 1214 if ( null !== $value ) { 1215 /* translators: 1: Parameter, 2: Type name. */ 1216 return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s is not of type %2$s.' ), $param, 'null' ) ); 1217 } 1218 1219 return true; 1220 } 1221 1199 1222 if ( ! empty( $args['enum'] ) ) { 1200 1223 if ( ! in_array( $value, $args['enum'], true ) ) { 1201 1224 /* translators: 1: Parameter, 2: List of valid values. */ … … function rest_validate_value_from_schema( $value, $args, $param = '' ) { 1300 1323 * @return true|WP_Error 1301 1324 */ 1302 1325 function rest_sanitize_value_from_schema( $value, $args ) { 1326 if ( is_array( $args['type'] ) ) { 1327 // Determine which type the value was validated against, and use that type when performing sanitization 1328 $validated_type = ''; 1329 1330 foreach ( $args['type'] as $type ) { 1331 $type_args = $args; 1332 $type_args['type'] = $type; 1333 1334 if ( ! is_wp_error( rest_validate_value_from_schema( $value, $type_args ) ) ) { 1335 $validated_type = $type; 1336 break; 1337 } 1338 } 1339 1340 if ( ! $validated_type ) { 1341 return null; 1342 } 1343 1344 $args['type'] = $validated_type; 1345 } 1346 1303 1347 if ( 'array' === $args['type'] ) { 1304 1348 if ( empty( $args['items'] ) ) { 1305 1349 return (array) $value; … … function rest_sanitize_value_from_schema( $value, $args ) { 1342 1386 return $value; 1343 1387 } 1344 1388 1389 if ( 'null' === $args['type'] ) { 1390 return null; 1391 } 1392 1345 1393 if ( 'integer' === $args['type'] ) { 1346 1394 return (int) $value; 1347 1395 } -
src/wp-includes/rest-api/class-wp-rest-request.php
diff --git a/src/wp-includes/rest-api/class-wp-rest-request.php b/src/wp-includes/rest-api/class-wp-rest-request.php index 7b2a32e618..3c67b6ad2a 100644
a b class WP_REST_Request implements ArrayAccess { 397 397 return null; 398 398 } 399 399 400 /** 401 * Checks if a parameter exists in the request. 402 * 403 * This allows distinguishing between an omitted parameter, 404 * and a parameter specifically set to null. 405 * 406 * @since 5.3.0 407 * 408 * @param string $key Parameter name. 409 * 410 * @return bool True if a param exists for the given key. 411 */ 412 public function has_param( $key ) { 413 $order = $this->get_parameter_order(); 414 415 foreach ( $order as $type ) { 416 if ( array_key_exists( $key, $this->params[ $type ] ) ) { 417 return true; 418 } 419 } 420 421 return false; 422 } 423 400 424 /** 401 425 * Sets a parameter on the request. 402 426 * -
src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php
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 e53ff4b910..c0304de164 100644
a b class WP_REST_Posts_Controller extends WP_REST_Controller { 1036 1036 } 1037 1037 } 1038 1038 1039 // Sending a null date or date_gmt value resets date and date_gmt to their 1040 // default values (`0000-00-00 00:00:00`). 1041 if ( 1042 ( ! empty( $schema['properties']['date_gmt'] ) && $request->has_param( 'date_gmt' ) && null === $request['date_gmt'] ) || 1043 ( ! empty( $schema['properties']['date'] ) && $request->has_param( 'date' ) && null === $request['date'] ) 1044 ) { 1045 $prepared_post->post_date_gmt = null; 1046 $prepared_post->post_date = null; 1047 } 1048 1039 1049 // Post slug. 1040 1050 if ( ! empty( $schema['properties']['slug'] ) && isset( $request['slug'] ) ) { 1041 1051 $prepared_post->post_name = $request['slug']; … … class WP_REST_Posts_Controller extends WP_REST_Controller { 1881 1891 'properties' => array( 1882 1892 'date' => array( 1883 1893 'description' => __( "The date the object was published, in the site's timezone." ), 1884 'type' => 'string',1894 'type' => array( 'string', 'null' ), 1885 1895 'format' => 'date-time', 1886 1896 'context' => array( 'view', 'edit', 'embed' ), 1887 1897 ), 1888 1898 'date_gmt' => array( 1889 1899 'description' => __( 'The date the object was published, as GMT.' ), 1890 'type' => 'string',1900 'type' => array( 'string', 'null' ), 1891 1901 'format' => 'date-time', 1892 1902 'context' => array( 'view', 'edit' ), 1893 1903 ), -
tests/phpunit/tests/rest-api/rest-posts-controller.php
diff --git a/tests/phpunit/tests/rest-api/rest-posts-controller.php b/tests/phpunit/tests/rest-api/rest-posts-controller.php index ba174c448e..9d0a97f324 100644
a b class WP_Test_REST_Posts_Controller extends WP_Test_REST_Post_Type_Controller_Te 2555 2555 $this->assertEquals( $params['excerpt'], $post->post_excerpt ); 2556 2556 } 2557 2557 2558 /** 2559 * Verify that updating a post with a `null` date or date_gmt results in a reset post, where all 2560 * date values are equal (date, date_gmt, date_modified and date_modofied_gmt) in the API response. 2561 * In the database, the post_date_gmt field is reset to the default `0000-00-00 00:00:00`. 2562 * 2563 * @ticket 44975 2564 */ 2565 public function test_rest_update_post_with_empty_date() { 2566 // Create a new test post. 2567 $post_id = $this->factory->post->create(); 2568 wp_set_current_user( self::$editor_id ); 2569 2570 // Set the post date to the future. 2571 $future_date = '2919-07-29T18:00:00'; 2572 $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', $post_id ) ); 2573 $request->add_header( 'content-type', 'application/json' ); 2574 $params = $this->set_post_data( 2575 array( 2576 'date_gmt' => $future_date, 2577 'date' => $future_date, 2578 'title' => 'update', 2579 'status' => 'draft', 2580 ) 2581 ); 2582 $request->set_body( wp_json_encode( $params ) ); 2583 $response = rest_get_server()->dispatch( $request ); 2584 $this->check_update_post_response( $response ); 2585 $new_data = $response->get_data(); 2586 2587 // Verify the post is set to the future date. 2588 $this->assertEquals( $new_data['date_gmt'], $future_date ); 2589 $this->assertEquals( $new_data['date'], $future_date ); 2590 $this->assertNotEquals( $new_data['date_gmt'], $new_data['modified_gmt'] ); 2591 $this->assertNotEquals( $new_data['date'], $new_data['modified'] ); 2592 2593 // Update post with a blank field (date or date_gmt). 2594 $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', $post_id ) ); 2595 $request->add_header( 'content-type', 'application/json' ); 2596 $params = $this->set_post_data( 2597 array( 2598 'date_gmt' => null, 2599 'title' => 'test', 2600 'status' => 'draft', 2601 ) 2602 ); 2603 $request->set_body( wp_json_encode( $params ) ); 2604 $response = rest_get_server()->dispatch( $request ); 2605 2606 // Verify the date field values are reset in the API response. 2607 $this->check_update_post_response( $response ); 2608 $new_data = $response->get_data(); 2609 $this->assertEquals( $new_data['date_gmt'], $new_data['date'] ); 2610 $this->assertNotEquals( $new_data['date_gmt'], $future_date ); 2611 2612 $post = get_post( $post_id, 'ARRAY_A' ); 2613 $this->assertEquals( $post['post_date_gmt'], '0000-00-00 00:00:00' ); 2614 $this->assertNotEquals( $new_data['date_gmt'], $future_date ); 2615 $this->assertNotEquals( $new_data['date'], $future_date ); 2616 } 2617 2558 2618 public function test_rest_update_post_raw() { 2559 2619 wp_set_current_user( self::$editor_id ); 2560 2620 -
tests/phpunit/tests/rest-api/rest-schema-sanitization.php
diff --git a/tests/phpunit/tests/rest-api/rest-schema-sanitization.php b/tests/phpunit/tests/rest-api/rest-schema-sanitization.php index 4f9f2c9242..1b2fd1d90c 100644
a b class WP_Test_REST_Schema_Sanitization extends WP_UnitTestCase { 292 292 $this->assertEquals( 1.10, rest_sanitize_value_from_schema( 1.10, $schema ) ); 293 293 $this->assertEquals( 1, rest_sanitize_value_from_schema( 1, $schema ) ); 294 294 } 295 296 public function test_nullable_date() { 297 $schema = array( 298 'type' => array( 'string', 'null' ), 299 'format' => 'date-time', 300 ); 301 302 $this->assertNull( rest_sanitize_value_from_schema( null, $schema ) ); 303 $this->assertEquals( '2019-09-19T18:00:00', rest_sanitize_value_from_schema( '2019-09-19T18:00:00', $schema ) ); 304 $this->assertNull( rest_sanitize_value_from_schema( 'lalala', $schema ) ); 305 } 306 307 public function test_object_or_string() { 308 $schema = array( 309 'type' => array( 'object', 'string' ), 310 'properties' => array( 311 'raw' => array( 312 'type' => 'string', 313 ), 314 ), 315 ); 316 317 $this->assertEquals( 'My Value', rest_sanitize_value_from_schema( 'My Value', $schema ) ); 318 $this->assertEquals( array( 'raw' => 'My Value' ), rest_sanitize_value_from_schema( array( 'raw' => 'My Value' ), $schema ) ); 319 $this->assertNull( rest_sanitize_value_from_schema( array( 'raw' => 1 ), $schema ) ); 320 } 321 322 public function test_object_or_bool() { 323 $schema = array( 324 'type' => array( 'object', 'boolean' ), 325 'properties' => array( 326 'raw' => array( 327 'type' => 'boolean', 328 ), 329 ), 330 ); 331 332 $this->assertTrue( rest_sanitize_value_from_schema( true, $schema ) ); 333 $this->assertTrue( rest_sanitize_value_from_schema( '1', $schema ) ); 334 $this->assertTrue( rest_sanitize_value_from_schema( 1, $schema ) ); 335 336 $this->assertFalse( rest_sanitize_value_from_schema( false, $schema ) ); 337 $this->assertFalse( rest_sanitize_value_from_schema( '0', $schema ) ); 338 $this->assertFalse( rest_sanitize_value_from_schema( 0, $schema ) ); 339 340 $this->assertEquals( array( 'raw' => true ), rest_sanitize_value_from_schema( array( 'raw' => true ), $schema ) ); 341 $this->assertEquals( array( 'raw' => true ), rest_sanitize_value_from_schema( array( 'raw' => '1' ), $schema ) ); 342 $this->assertEquals( array( 'raw' => true ), rest_sanitize_value_from_schema( array( 'raw' => 1 ), $schema ) ); 343 344 $this->assertEquals( array( 'raw' => false ), rest_sanitize_value_from_schema( array( 'raw' => false ), $schema ) ); 345 $this->assertEquals( array( 'raw' => false ), rest_sanitize_value_from_schema( array( 'raw' => '0' ), $schema ) ); 346 $this->assertEquals( array( 'raw' => false ), rest_sanitize_value_from_schema( array( 'raw' => 0 ), $schema ) ); 347 348 $this->assertNull( rest_sanitize_value_from_schema( array( 'raw' => 'something non boolean' ), $schema ) ); 349 } 295 350 } -
tests/phpunit/tests/rest-api/rest-schema-validation.php
diff --git a/tests/phpunit/tests/rest-api/rest-schema-validation.php b/tests/phpunit/tests/rest-api/rest-schema-validation.php index df16a894ad..9c18a846dc 100644
a b class WP_Test_REST_Schema_Validation extends WP_UnitTestCase { 296 296 $this->assertTrue( rest_validate_value_from_schema( 1, $schema ) ); 297 297 $this->assertTrue( rest_validate_value_from_schema( array(), $schema ) ); 298 298 } 299 300 public function test_type_null() { 301 $this->assertTrue( rest_validate_value_from_schema( null, array( 'type' => 'null' ) ) ); 302 $this->assertWPError( rest_validate_value_from_schema( '', array( 'type' => 'null' ) ) ); 303 $this->assertWPError( rest_validate_value_from_schema( 'null', array( 'type' => 'null' ) ) ); 304 } 305 306 public function test_nullable_date() { 307 $schema = array( 308 'type' => array( 'string', 'null' ), 309 'format' => 'date-time', 310 ); 311 312 $this->assertTrue( rest_validate_value_from_schema( null, $schema ) ); 313 $this->assertTrue( rest_validate_value_from_schema( '2019-09-19T18:00:00', $schema ) ); 314 $this->assertWPError( rest_validate_value_from_schema( 'some random string', $schema ) ); 315 } 316 317 public function test_object_or_string() { 318 $schema = array( 319 'type' => array( 'object', 'string' ), 320 'properties' => array( 321 'raw' => array( 322 'type' => 'string', 323 ), 324 ), 325 ); 326 327 $this->assertTrue( rest_validate_value_from_schema( 'My Value', $schema ) ); 328 $this->assertTrue( rest_validate_value_from_schema( array( 'raw' => 'My Value' ), $schema ) ); 329 $this->assertWPError( rest_validate_value_from_schema( array( 'raw' => array( 'a list' ) ), $schema ) ); 330 } 299 331 } -
tests/qunit/fixtures/wp-api-generated.js
diff --git a/tests/qunit/fixtures/wp-api-generated.js b/tests/qunit/fixtures/wp-api-generated.js index 47f925fe44..fdbf056589 100644
a b mockedApiResponse.Schema = { 373 373 "date": { 374 374 "required": false, 375 375 "description": "The date the object was published, in the site's timezone.", 376 "type": "string" 376 "type": [ 377 "string", 378 "null" 379 ] 377 380 }, 378 381 "date_gmt": { 379 382 "required": false, 380 383 "description": "The date the object was published, as GMT.", 381 "type": "string" 384 "type": [ 385 "string", 386 "null" 387 ] 382 388 }, 383 389 "slug": { 384 390 "required": false, … … mockedApiResponse.Schema = { 553 559 "date": { 554 560 "required": false, 555 561 "description": "The date the object was published, in the site's timezone.", 556 "type": "string" 562 "type": [ 563 "string", 564 "null" 565 ] 557 566 }, 558 567 "date_gmt": { 559 568 "required": false, 560 569 "description": "The date the object was published, as GMT.", 561 "type": "string" 570 "type": [ 571 "string", 572 "null" 573 ] 562 574 }, 563 575 "slug": { 564 576 "required": false, … … mockedApiResponse.Schema = { 893 905 "date": { 894 906 "required": false, 895 907 "description": "The date the object was published, in the site's timezone.", 896 "type": "string" 908 "type": [ 909 "string", 910 "null" 911 ] 897 912 }, 898 913 "date_gmt": { 899 914 "required": false, 900 915 "description": "The date the object was published, as GMT.", 901 "type": "string" 916 "type": [ 917 "string", 918 "null" 919 ] 902 920 }, 903 921 "slug": { 904 922 "required": false, … … mockedApiResponse.Schema = { 1238 1256 "date": { 1239 1257 "required": false, 1240 1258 "description": "The date the object was published, in the site's timezone.", 1241 "type": "string" 1259 "type": [ 1260 "string", 1261 "null" 1262 ] 1242 1263 }, 1243 1264 "date_gmt": { 1244 1265 "required": false, 1245 1266 "description": "The date the object was published, as GMT.", 1246 "type": "string" 1267 "type": [ 1268 "string", 1269 "null" 1270 ] 1247 1271 }, 1248 1272 "slug": { 1249 1273 "required": false, … … mockedApiResponse.Schema = { 1390 1414 "date": { 1391 1415 "required": false, 1392 1416 "description": "The date the object was published, in the site's timezone.", 1393 "type": "string" 1417 "type": [ 1418 "string", 1419 "null" 1420 ] 1394 1421 }, 1395 1422 "date_gmt": { 1396 1423 "required": false, 1397 1424 "description": "The date the object was published, as GMT.", 1398 "type": "string" 1425 "type": [ 1426 "string", 1427 "null" 1428 ] 1399 1429 }, 1400 1430 "slug": { 1401 1431 "required": false, … … mockedApiResponse.Schema = { 1702 1732 "date": { 1703 1733 "required": false, 1704 1734 "description": "The date the object was published, in the site's timezone.", 1705 "type": "string" 1735 "type": [ 1736 "string", 1737 "null" 1738 ] 1706 1739 }, 1707 1740 "date_gmt": { 1708 1741 "required": false, 1709 1742 "description": "The date the object was published, as GMT.", 1710 "type": "string" 1743 "type": [ 1744 "string", 1745 "null" 1746 ] 1711 1747 }, 1712 1748 "slug": { 1713 1749 "required": false, … … mockedApiResponse.Schema = { 2015 2051 "date": { 2016 2052 "required": false, 2017 2053 "description": "The date the object was published, in the site's timezone.", 2018 "type": "string" 2054 "type": [ 2055 "string", 2056 "null" 2057 ] 2019 2058 }, 2020 2059 "date_gmt": { 2021 2060 "required": false, 2022 2061 "description": "The date the object was published, as GMT.", 2023 "type": "string" 2062 "type": [ 2063 "string", 2064 "null" 2065 ] 2024 2066 }, 2025 2067 "slug": { 2026 2068 "required": false, … … mockedApiResponse.Schema = { 2152 2194 "date": { 2153 2195 "required": false, 2154 2196 "description": "The date the object was published, in the site's timezone.", 2155 "type": "string" 2197 "type": [ 2198 "string", 2199 "null" 2200 ] 2156 2201 }, 2157 2202 "date_gmt": { 2158 2203 "required": false, 2159 2204 "description": "The date the object was published, as GMT.", 2160 "type": "string" 2205 "type": [ 2206 "string", 2207 "null" 2208 ] 2161 2209 }, 2162 2210 "slug": { 2163 2211 "required": false, … … mockedApiResponse.Schema = { 2399 2447 "date": { 2400 2448 "required": false, 2401 2449 "description": "The date the object was published, in the site's timezone.", 2402 "type": "string" 2450 "type": [ 2451 "string", 2452 "null" 2453 ] 2403 2454 }, 2404 2455 "date_gmt": { 2405 2456 "required": false, 2406 2457 "description": "The date the object was published, as GMT.", 2407 "type": "string" 2458 "type": [ 2459 "string", 2460 "null" 2461 ] 2408 2462 }, 2409 2463 "slug": { 2410 2464 "required": false, … … mockedApiResponse.Schema = { 2503 2557 "date": { 2504 2558 "required": false, 2505 2559 "description": "The date the object was published, in the site's timezone.", 2506 "type": "string" 2560 "type": [ 2561 "string", 2562 "null" 2563 ] 2507 2564 }, 2508 2565 "date_gmt": { 2509 2566 "required": false, 2510 2567 "description": "The date the object was published, as GMT.", 2511 "type": "string" 2568 "type": [ 2569 "string", 2570 "null" 2571 ] 2512 2572 }, 2513 2573 "slug": { 2514 2574 "required": false, … … mockedApiResponse.Schema = { 2612 2672 "date": { 2613 2673 "required": false, 2614 2674 "description": "The date the object was published, in the site's timezone.", 2615 "type": "string" 2675 "type": [ 2676 "string", 2677 "null" 2678 ] 2616 2679 }, 2617 2680 "date_gmt": { 2618 2681 "required": false, 2619 2682 "description": "The date the object was published, as GMT.", 2620 "type": "string" 2683 "type": [ 2684 "string", 2685 "null" 2686 ] 2621 2687 }, 2622 2688 "slug": { 2623 2689 "required": false,