WordPress.org

Make WordPress Core

Changeset 46249


Ignore:
Timestamp:
09/23/2019 05:24:58 PM (3 weeks ago)
Author:
kadamwhite
Message:

REST API: Pass "null" as the post date property to reset post to initial "floating" date value.

Props TimothyBlynJacobs, adamsilverstein, jnylen0, mnelson4.
Fixes #44975.

Location:
trunk
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/rest-api.php

    r46233 r46249  
    12101210 */
    12111211function rest_validate_value_from_schema( $value, $args, $param = '' ) {
     1212    if ( is_array( $args['type'] ) ) {
     1213        foreach ( $args['type'] as $type ) {
     1214            $type_args         = $args;
     1215            $type_args['type'] = $type;
     1216
     1217            if ( true === rest_validate_value_from_schema( $value, $type_args, $param ) ) {
     1218                return true;
     1219            }
     1220        }
     1221
     1222        /* translators: 1: Parameter, 2: List of types. */
     1223        return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s is not of type %2$s' ), $param, implode( ',', $args['type'] ) ) );
     1224    }
     1225
    12121226    if ( 'array' === $args['type'] ) {
    12131227        if ( ! is_null( $value ) ) {
     
    12601274            }
    12611275        }
     1276    }
     1277
     1278    if ( 'null' === $args['type'] ) {
     1279        if ( null !== $value ) {
     1280            /* translators: 1: Parameter, 2: Type name. */
     1281            return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s is not of type %2$s.' ), $param, 'null' ) );
     1282        }
     1283
     1284        return true;
    12621285    }
    12631286
     
    13661389 */
    13671390function rest_sanitize_value_from_schema( $value, $args ) {
     1391    if ( is_array( $args['type'] ) ) {
     1392        // Determine which type the value was validated against, and use that type when performing sanitization
     1393        $validated_type = '';
     1394
     1395        foreach ( $args['type'] as $type ) {
     1396            $type_args         = $args;
     1397            $type_args['type'] = $type;
     1398
     1399            if ( ! is_wp_error( rest_validate_value_from_schema( $value, $type_args ) ) ) {
     1400                $validated_type = $type;
     1401                break;
     1402            }
     1403        }
     1404
     1405        if ( ! $validated_type ) {
     1406            return null;
     1407        }
     1408
     1409        $args['type'] = $validated_type;
     1410    }
     1411
    13681412    if ( 'array' === $args['type'] ) {
    13691413        if ( empty( $args['items'] ) ) {
     
    14081452    }
    14091453
     1454    if ( 'null' === $args['type'] ) {
     1455        return null;
     1456    }
     1457
    14101458    if ( 'integer' === $args['type'] ) {
    14111459        return (int) $value;
  • trunk/src/wp-includes/rest-api/class-wp-rest-request.php

    r46206 r46249  
    399399
    400400    /**
     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
     424    /**
    401425     * Sets a parameter on the request.
    402426     *
  • trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php

    r46184 r46249  
    10371037        }
    10381038
     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
    10391049        // Post slug.
    10401050        if ( ! empty( $schema['properties']['slug'] ) && isset( $request['slug'] ) ) {
     
    18921902                'date'         => array(
    18931903                    'description' => __( "The date the object was published, in the site's timezone." ),
    1894                     'type'        => 'string',
     1904                    'type'        => array( 'string', 'null' ),
    18951905                    'format'      => 'date-time',
    18961906                    'context'     => array( 'view', 'edit', 'embed' ),
     
    18981908                'date_gmt'     => array(
    18991909                    'description' => __( 'The date the object was published, as GMT.' ),
    1900                     'type'        => 'string',
     1910                    'type'        => array( 'string', 'null' ),
    19011911                    'format'      => 'date-time',
    19021912                    'context'     => array( 'view', 'edit' ),
  • trunk/tests/phpunit/tests/rest-api/rest-posts-controller.php

    r46184 r46249  
    26242624        $this->assertEquals( $params['content'], $post->post_content );
    26252625        $this->assertEquals( $params['excerpt'], $post->post_excerpt );
     2626    }
     2627
     2628    /**
     2629     * Verify that updating a post with a `null` date or date_gmt results in a reset post, where all
     2630     * date values are equal (date, date_gmt, date_modified and date_modofied_gmt) in the API response.
     2631     * In the database, the post_date_gmt field is reset to the default `0000-00-00 00:00:00`.
     2632     *
     2633     * @ticket 44975
     2634     */
     2635    public function test_rest_update_post_with_empty_date() {
     2636        // Create a new test post.
     2637        $post_id = $this->factory->post->create();
     2638        wp_set_current_user( self::$editor_id );
     2639
     2640        // Set the post date to the future.
     2641        $future_date = '2919-07-29T18:00:00';
     2642        $request     = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', $post_id ) );
     2643        $request->add_header( 'content-type', 'application/json' );
     2644        $params = $this->set_post_data(
     2645            array(
     2646                'date_gmt' => $future_date,
     2647                'date'     => $future_date,
     2648                'title'    => 'update',
     2649                'status'   => 'draft',
     2650            )
     2651        );
     2652        $request->set_body( wp_json_encode( $params ) );
     2653        $response = rest_get_server()->dispatch( $request );
     2654        $this->check_update_post_response( $response );
     2655        $new_data = $response->get_data();
     2656
     2657        // Verify the post is set to the future date.
     2658        $this->assertEquals( $new_data['date_gmt'], $future_date );
     2659        $this->assertEquals( $new_data['date'], $future_date );
     2660        $this->assertNotEquals( $new_data['date_gmt'], $new_data['modified_gmt'] );
     2661        $this->assertNotEquals( $new_data['date'], $new_data['modified'] );
     2662
     2663        // Update post with a blank field (date or date_gmt).
     2664        $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', $post_id ) );
     2665        $request->add_header( 'content-type', 'application/json' );
     2666        $params = $this->set_post_data(
     2667            array(
     2668                'date_gmt' => null,
     2669                'title'    => 'test',
     2670                'status'   => 'draft',
     2671            )
     2672        );
     2673        $request->set_body( wp_json_encode( $params ) );
     2674        $response = rest_get_server()->dispatch( $request );
     2675
     2676        // Verify the date field values are reset in the API response.
     2677        $this->check_update_post_response( $response );
     2678        $new_data = $response->get_data();
     2679        $this->assertEquals( $new_data['date_gmt'], $new_data['date'] );
     2680        $this->assertNotEquals( $new_data['date_gmt'], $future_date );
     2681
     2682        $post = get_post( $post_id, 'ARRAY_A' );
     2683        $this->assertEquals( $post['post_date_gmt'], '0000-00-00 00:00:00' );
     2684        $this->assertNotEquals( $new_data['date_gmt'], $future_date );
     2685        $this->assertNotEquals( $new_data['date'], $future_date );
    26262686    }
    26272687
  • trunk/tests/phpunit/tests/rest-api/rest-schema-sanitization.php

    r44703 r46249  
    293293        $this->assertEquals( 1, rest_sanitize_value_from_schema( 1, $schema ) );
    294294    }
     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    }
    295350}
  • trunk/tests/phpunit/tests/rest-api/rest-schema-validation.php

    r44546 r46249  
    297297        $this->assertTrue( rest_validate_value_from_schema( array(), $schema ) );
    298298    }
     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    }
    299331}
  • trunk/tests/qunit/fixtures/wp-api-generated.js

    r46190 r46249  
    374374                            "required": false,
    375375                            "description": "The date the object was published, in the site's timezone.",
    376                             "type": "string"
     376                            "type": [
     377                                "string",
     378                                "null"
     379                            ]
    377380                        },
    378381                        "date_gmt": {
    379382                            "required": false,
    380383                            "description": "The date the object was published, as GMT.",
    381                             "type": "string"
     384                            "type": [
     385                                "string",
     386                                "null"
     387                            ]
    382388                        },
    383389                        "slug": {
     
    554560                            "required": false,
    555561                            "description": "The date the object was published, in the site's timezone.",
    556                             "type": "string"
     562                            "type": [
     563                                "string",
     564                                "null"
     565                            ]
    557566                        },
    558567                        "date_gmt": {
    559568                            "required": false,
    560569                            "description": "The date the object was published, as GMT.",
    561                             "type": "string"
     570                            "type": [
     571                                "string",
     572                                "null"
     573                            ]
    562574                        },
    563575                        "slug": {
     
    894906                            "required": false,
    895907                            "description": "The date the object was published, in the site's timezone.",
    896                             "type": "string"
     908                            "type": [
     909                                "string",
     910                                "null"
     911                            ]
    897912                        },
    898913                        "date_gmt": {
    899914                            "required": false,
    900915                            "description": "The date the object was published, as GMT.",
    901                             "type": "string"
     916                            "type": [
     917                                "string",
     918                                "null"
     919                            ]
    902920                        },
    903921                        "slug": {
     
    12391257                            "required": false,
    12401258                            "description": "The date the object was published, in the site's timezone.",
    1241                             "type": "string"
     1259                            "type": [
     1260                                "string",
     1261                                "null"
     1262                            ]
    12421263                        },
    12431264                        "date_gmt": {
    12441265                            "required": false,
    12451266                            "description": "The date the object was published, as GMT.",
    1246                             "type": "string"
     1267                            "type": [
     1268                                "string",
     1269                                "null"
     1270                            ]
    12471271                        },
    12481272                        "slug": {
     
    13911415                            "required": false,
    13921416                            "description": "The date the object was published, in the site's timezone.",
    1393                             "type": "string"
     1417                            "type": [
     1418                                "string",
     1419                                "null"
     1420                            ]
    13941421                        },
    13951422                        "date_gmt": {
    13961423                            "required": false,
    13971424                            "description": "The date the object was published, as GMT.",
    1398                             "type": "string"
     1425                            "type": [
     1426                                "string",
     1427                                "null"
     1428                            ]
    13991429                        },
    14001430                        "slug": {
     
    17031733                            "required": false,
    17041734                            "description": "The date the object was published, in the site's timezone.",
    1705                             "type": "string"
     1735                            "type": [
     1736                                "string",
     1737                                "null"
     1738                            ]
    17061739                        },
    17071740                        "date_gmt": {
    17081741                            "required": false,
    17091742                            "description": "The date the object was published, as GMT.",
    1710                             "type": "string"
     1743                            "type": [
     1744                                "string",
     1745                                "null"
     1746                            ]
    17111747                        },
    17121748                        "slug": {
     
    20162052                            "required": false,
    20172053                            "description": "The date the object was published, in the site's timezone.",
    2018                             "type": "string"
     2054                            "type": [
     2055                                "string",
     2056                                "null"
     2057                            ]
    20192058                        },
    20202059                        "date_gmt": {
    20212060                            "required": false,
    20222061                            "description": "The date the object was published, as GMT.",
    2023                             "type": "string"
     2062                            "type": [
     2063                                "string",
     2064                                "null"
     2065                            ]
    20242066                        },
    20252067                        "slug": {
     
    21532195                            "required": false,
    21542196                            "description": "The date the object was published, in the site's timezone.",
    2155                             "type": "string"
     2197                            "type": [
     2198                                "string",
     2199                                "null"
     2200                            ]
    21562201                        },
    21572202                        "date_gmt": {
    21582203                            "required": false,
    21592204                            "description": "The date the object was published, as GMT.",
    2160                             "type": "string"
     2205                            "type": [
     2206                                "string",
     2207                                "null"
     2208                            ]
    21612209                        },
    21622210                        "slug": {
     
    24002448                            "required": false,
    24012449                            "description": "The date the object was published, in the site's timezone.",
    2402                             "type": "string"
     2450                            "type": [
     2451                                "string",
     2452                                "null"
     2453                            ]
    24032454                        },
    24042455                        "date_gmt": {
    24052456                            "required": false,
    24062457                            "description": "The date the object was published, as GMT.",
    2407                             "type": "string"
     2458                            "type": [
     2459                                "string",
     2460                                "null"
     2461                            ]
    24082462                        },
    24092463                        "slug": {
     
    25042558                            "required": false,
    25052559                            "description": "The date the object was published, in the site's timezone.",
    2506                             "type": "string"
     2560                            "type": [
     2561                                "string",
     2562                                "null"
     2563                            ]
    25072564                        },
    25082565                        "date_gmt": {
    25092566                            "required": false,
    25102567                            "description": "The date the object was published, as GMT.",
    2511                             "type": "string"
     2568                            "type": [
     2569                                "string",
     2570                                "null"
     2571                            ]
    25122572                        },
    25132573                        "slug": {
     
    26132673                            "required": false,
    26142674                            "description": "The date the object was published, in the site's timezone.",
    2615                             "type": "string"
     2675                            "type": [
     2676                                "string",
     2677                                "null"
     2678                            ]
    26162679                        },
    26172680                        "date_gmt": {
    26182681                            "required": false,
    26192682                            "description": "The date the object was published, as GMT.",
    2620                             "type": "string"
     2683                            "type": [
     2684                                "string",
     2685                                "null"
     2686                            ]
    26212687                        },
    26222688                        "slug": {
Note: See TracChangeset for help on using the changeset viewer.