WordPress.org

Make WordPress Core

Changeset 40114


Ignore:
Timestamp:
02/24/2017 09:58:07 PM (2 years ago)
Author:
SergeyBiryukov
Message:

REST API: Fix multiple issues with setting dates of posts and comments.

This commit modifies the rest_get_date_with_gmt function to correctly parse local and UTC timestamps with or without timezone information.

It also ensures that the REST API can edit the dates of draft posts by setting the edit_date flag to wp_update_post.

Overall this commit ensures that post and comment dates can be set and updated as expected.

Props jnylen0.
Merges [40101] to the 4.7 branch.
Fixes #39256.

Location:
branches/4.7
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • branches/4.7

  • branches/4.7/src/wp-includes/rest-api.php

    r40079 r40114  
    781781
    782782/**
    783  * Retrieves a local date with its GMT equivalent, in MySQL datetime format.
     783 * Parses a date into both its local and UTC equivalent, in MySQL datetime format.
    784784 *
    785785 * @since 4.4.0
     
    787787 * @see rest_parse_date()
    788788 *
    789  * @param string $date      RFC3339 timestamp.
    790  * @param bool   $force_utc Whether a UTC timestamp should be forced. Default false.
     789 * @param string $date   RFC3339 timestamp.
     790 * @param bool   $is_utc Whether the provided date should be interpreted as UTC. Default false.
    791791 * @return array|null Local and UTC datetime strings, in MySQL datetime format (Y-m-d H:i:s),
    792792 *                    null on failure.
    793793 */
    794 function rest_get_date_with_gmt( $date, $force_utc = false ) {
    795     $date = rest_parse_date( $date, $force_utc );
     794function rest_get_date_with_gmt( $date, $is_utc = false ) {
     795    // Whether or not the original date actually has a timezone string
     796    // changes the way we need to do timezone conversion.  Store this info
     797    // before parsing the date, and use it later.
     798    $has_timezone = preg_match( '#(Z|[+-]\d{2}(:\d{2})?)$#', $date );
     799
     800    $date = rest_parse_date( $date );
    796801
    797802    if ( empty( $date ) ) {
     
    799804    }
    800805
    801     $utc = date( 'Y-m-d H:i:s', $date );
    802     $local = get_date_from_gmt( $utc );
     806    // At this point $date could either be a local date (if we were passed a
     807    // *local* date without a timezone offset) or a UTC date (otherwise).
     808    // Timezone conversion needs to be handled differently between these two
     809    // cases.
     810    if ( ! $is_utc && ! $has_timezone ) {
     811        $local = date( 'Y-m-d H:i:s', $date );
     812        $utc = get_gmt_from_date( $local );
     813    } else {
     814        $utc = date( 'Y-m-d H:i:s', $date );
     815        $local = get_date_from_gmt( $utc );
     816    }
    803817
    804818    return array( $local, $utc );
  • branches/4.7/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php

    r40081 r40114  
    10001000            if ( ! empty( $date_data ) ) {
    10011001                list( $prepared_post->post_date, $prepared_post->post_date_gmt ) = $date_data;
     1002                $prepared_post->edit_date = true;
    10021003            }
    10031004        } elseif ( ! empty( $schema['properties']['date_gmt'] ) && ! empty( $request['date_gmt'] ) ) {
     
    10061007            if ( ! empty( $date_data ) ) {
    10071008                list( $prepared_post->post_date, $prepared_post->post_date_gmt ) = $date_data;
     1009                $prepared_post->edit_date = true;
    10081010            }
    10091011        }
  • branches/4.7/tests/phpunit/tests/rest-api.php

    r38832 r40114  
    384384    }
    385385
     386    public function rest_date_provider() {
     387        return array(
     388            // Valid dates with timezones
     389            array( '2017-01-16T11:30:00-05:00', gmmktime( 11, 30,  0,  1, 16, 2017 ) + 5 * HOUR_IN_SECONDS ),
     390            array( '2017-01-16T11:30:00-05:30', gmmktime( 11, 30,  0,  1, 16, 2017 ) + 5.5 * HOUR_IN_SECONDS ),
     391            array( '2017-01-16T11:30:00-05'   , gmmktime( 11, 30,  0,  1, 16, 2017 ) + 5 * HOUR_IN_SECONDS ),
     392            array( '2017-01-16T11:30:00+05'   , gmmktime( 11, 30,  0,  1, 16, 2017 ) - 5 * HOUR_IN_SECONDS ),
     393            array( '2017-01-16T11:30:00-00'   , gmmktime( 11, 30,  0,  1, 16, 2017 ) ),
     394            array( '2017-01-16T11:30:00+00'   , gmmktime( 11, 30,  0,  1, 16, 2017 ) ),
     395            array( '2017-01-16T11:30:00Z'     , gmmktime( 11, 30,  0,  1, 16, 2017 ) ),
     396
     397            // Valid dates without timezones
     398            array( '2017-01-16T11:30:00'      , gmmktime( 11, 30,  0,  1, 16, 2017 ) ),
     399
     400            // Invalid dates (TODO: support parsing partial dates as ranges, see #38641)
     401            array( '2017-01-16T11:30:00-5', false ),
     402            array( '2017-01-16T11:30', false ),
     403            array( '2017-01-16T11', false ),
     404            array( '2017-01-16T', false ),
     405            array( '2017-01-16', false ),
     406            array( '2017-01', false ),
     407            array( '2017', false ),
     408        );
     409    }
     410
     411    /**
     412     * @dataProvider rest_date_provider
     413     */
     414    public function test_rest_parse_date( $string, $value ) {
     415        $this->assertEquals( $value, rest_parse_date( $string ) );
     416    }
     417
     418    public function rest_date_force_utc_provider() {
     419        return array(
     420            // Valid dates with timezones
     421            array( '2017-01-16T11:30:00-05:00', gmmktime( 11, 30,  0,  1, 16, 2017 ) ),
     422            array( '2017-01-16T11:30:00-05:30', gmmktime( 11, 30,  0,  1, 16, 2017 ) ),
     423            array( '2017-01-16T11:30:00-05'   , gmmktime( 11, 30,  0,  1, 16, 2017 ) ),
     424            array( '2017-01-16T11:30:00+05'   , gmmktime( 11, 30,  0,  1, 16, 2017 ) ),
     425            array( '2017-01-16T11:30:00-00'   , gmmktime( 11, 30,  0,  1, 16, 2017 ) ),
     426            array( '2017-01-16T11:30:00+00'   , gmmktime( 11, 30,  0,  1, 16, 2017 ) ),
     427            array( '2017-01-16T11:30:00Z'     , gmmktime( 11, 30,  0,  1, 16, 2017 ) ),
     428
     429            // Valid dates without timezones
     430            array( '2017-01-16T11:30:00'      , gmmktime( 11, 30,  0,  1, 16, 2017 ) ),
     431
     432            // Invalid dates (TODO: support parsing partial dates as ranges, see #38641)
     433            array( '2017-01-16T11:30:00-5', false ),
     434            array( '2017-01-16T11:30', false ),
     435            array( '2017-01-16T11', false ),
     436            array( '2017-01-16T', false ),
     437            array( '2017-01-16', false ),
     438            array( '2017-01', false ),
     439            array( '2017', false ),
     440        );
     441    }
     442
     443    /**
     444     * @dataProvider rest_date_force_utc_provider
     445     */
     446    public function test_rest_parse_date_force_utc( $string, $value ) {
     447        $this->assertEquals( $value, rest_parse_date( $string, true ) );
     448    }
    386449}
  • branches/4.7/tests/phpunit/tests/rest-api/rest-comments-controller.php

    r39957 r40114  
    959959    }
    960960
     961    public function comment_dates_provider() {
     962        return array(
     963            'set date without timezone' => array(
     964                'params'   => array(
     965                    'timezone_string' => 'America/New_York',
     966                    'date'            => '2016-12-12T14:00:00',
     967                ),
     968                'results' => array(
     969                    'date'            => '2016-12-12T14:00:00',
     970                    'date_gmt'        => '2016-12-12T19:00:00',
     971                ),
     972            ),
     973            'set date_gmt without timezone' => array(
     974                'params'   => array(
     975                    'timezone_string' => 'America/New_York',
     976                    'date_gmt'        => '2016-12-12T19:00:00',
     977                ),
     978                'results' => array(
     979                    'date'            => '2016-12-12T14:00:00',
     980                    'date_gmt'        => '2016-12-12T19:00:00',
     981                ),
     982            ),
     983            'set date with timezone' => array(
     984                'params'   => array(
     985                    'timezone_string' => 'America/New_York',
     986                    'date'            => '2016-12-12T18:00:00-01:00',
     987                ),
     988                'results' => array(
     989                    'date'            => '2016-12-12T14:00:00',
     990                    'date_gmt'        => '2016-12-12T19:00:00',
     991                ),
     992            ),
     993            'set date_gmt with timezone' => array(
     994                'params'   => array(
     995                    'timezone_string' => 'America/New_York',
     996                    'date_gmt'        => '2016-12-12T18:00:00-01:00',
     997                ),
     998                'results' => array(
     999                    'date'            => '2016-12-12T14:00:00',
     1000                    'date_gmt'        => '2016-12-12T19:00:00',
     1001                ),
     1002            ),
     1003        );
     1004    }
     1005
     1006    /**
     1007     * @dataProvider comment_dates_provider
     1008     */
     1009    public function test_create_comment_date( $params, $results ) {
     1010        wp_set_current_user( self::$admin_id );
     1011        update_option( 'timezone_string', $params['timezone_string'] );
     1012
     1013        $request = new WP_REST_Request( 'POST', '/wp/v2/comments' );
     1014        $request->set_param( 'content', 'not empty' );
     1015        $request->set_param( 'post', self::$post_id );
     1016        if ( isset( $params['date'] ) ) {
     1017            $request->set_param( 'date', $params['date'] );
     1018        }
     1019        if ( isset( $params['date_gmt'] ) ) {
     1020            $request->set_param( 'date_gmt', $params['date_gmt'] );
     1021        }
     1022        $response = $this->server->dispatch( $request );
     1023
     1024        update_option( 'timezone_string', '' );
     1025
     1026        $this->assertEquals( 201, $response->get_status() );
     1027        $data = $response->get_data();
     1028        $comment = get_comment( $data['id'] );
     1029
     1030        $this->assertEquals( $results['date'], $data['date'] );
     1031        $comment_date = str_replace( 'T', ' ', $results['date'] );
     1032        $this->assertEquals( $comment_date, $comment->comment_date );
     1033
     1034        $this->assertEquals( $results['date_gmt'], $data['date_gmt'] );
     1035        $comment_date_gmt = str_replace( 'T', ' ', $results['date_gmt'] );
     1036        $this->assertEquals( $comment_date_gmt, $comment->comment_date_gmt );
     1037    }
     1038
    9611039    public function test_create_item_using_accepted_content_raw_value() {
    9621040        wp_set_current_user( self::$admin_id );
     
    19412019        $this->assertEquals( mysql_to_rfc3339( $updated->comment_date ), $comment['date'] );
    19422020        $this->assertEquals( '2014-11-07T10:14:25', $comment['date'] );
     2021    }
     2022
     2023    /**
     2024     * @dataProvider comment_dates_provider
     2025     */
     2026    public function test_update_comment_date( $params, $results ) {
     2027        wp_set_current_user( self::$editor_id );
     2028        update_option( 'timezone_string', $params['timezone_string'] );
     2029
     2030        $comment_id = $this->factory->comment->create();
     2031
     2032        $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/comments/%d', $comment_id ) );
     2033        if ( isset( $params['date'] ) ) {
     2034            $request->set_param( 'date', $params['date'] );
     2035        }
     2036        if ( isset( $params['date_gmt'] ) ) {
     2037            $request->set_param( 'date_gmt', $params['date_gmt'] );
     2038        }
     2039        $response = $this->server->dispatch( $request );
     2040
     2041        update_option( 'timezone_string', '' );
     2042
     2043        $this->assertEquals( 200, $response->get_status() );
     2044        $data = $response->get_data();
     2045        $comment = get_comment( $data['id'] );
     2046
     2047        $this->assertEquals( $results['date'], $data['date'] );
     2048        $comment_date = str_replace( 'T', ' ', $results['date'] );
     2049        $this->assertEquals( $comment_date, $comment->comment_date );
     2050
     2051        $this->assertEquals( $results['date_gmt'], $data['date_gmt'] );
     2052        $comment_date_gmt = str_replace( 'T', ' ', $results['date_gmt'] );
     2053        $this->assertEquals( $comment_date_gmt, $comment->comment_date_gmt );
    19432054    }
    19442055
  • branches/4.7/tests/phpunit/tests/rest-api/rest-posts-controller.php

    r39957 r40114  
    10971097    }
    10981098
     1099    public function post_dates_provider() {
     1100        $all_statuses = array(
     1101            'draft',
     1102            'publish',
     1103            'future',
     1104            'pending',
     1105            'private',
     1106        );
     1107
     1108        $cases_short = array(
     1109            'set date without timezone' => array(
     1110                'statuses' => $all_statuses,
     1111                'params'   => array(
     1112                    'timezone_string' => 'America/New_York',
     1113                    'date'            => '2016-12-12T14:00:00',
     1114                ),
     1115                'results' => array(
     1116                    'date'            => '2016-12-12T14:00:00',
     1117                    'date_gmt'        => '2016-12-12T19:00:00',
     1118                ),
     1119            ),
     1120            'set date_gmt without timezone' => array(
     1121                'statuses' => $all_statuses,
     1122                'params'   => array(
     1123                    'timezone_string' => 'America/New_York',
     1124                    'date_gmt'        => '2016-12-12T19:00:00',
     1125                ),
     1126                'results' => array(
     1127                    'date'            => '2016-12-12T14:00:00',
     1128                    'date_gmt'        => '2016-12-12T19:00:00',
     1129                ),
     1130            ),
     1131            'set date with timezone' => array(
     1132                'statuses' => array( 'draft', 'publish' ),
     1133                'params'   => array(
     1134                    'timezone_string' => 'America/New_York',
     1135                    'date'            => '2016-12-12T18:00:00-01:00',
     1136                ),
     1137                'results' => array(
     1138                    'date'            => '2016-12-12T14:00:00',
     1139                    'date_gmt'        => '2016-12-12T19:00:00',
     1140                ),
     1141            ),
     1142            'set date_gmt with timezone' => array(
     1143                'statuses' => array( 'draft', 'publish' ),
     1144                'params'   => array(
     1145                    'timezone_string' => 'America/New_York',
     1146                    'date_gmt'        => '2016-12-12T18:00:00-01:00',
     1147                ),
     1148                'results' => array(
     1149                    'date'            => '2016-12-12T14:00:00',
     1150                    'date_gmt'        => '2016-12-12T19:00:00',
     1151                ),
     1152            ),
     1153        );
     1154
     1155        $cases = array();
     1156        foreach ( $cases_short as $description => $case ) {
     1157            foreach ( $case['statuses'] as $status ) {
     1158                $cases[ $description . ', status=' . $status ] = array(
     1159                    $status,
     1160                    $case['params'],
     1161                    $case['results'],
     1162                );
     1163            }
     1164        }
     1165
     1166        return $cases;
     1167    }
     1168
     1169    /**
     1170     * @dataProvider post_dates_provider
     1171     */
     1172    public function test_create_post_date( $status, $params, $results ) {
     1173        wp_set_current_user( self::$editor_id );
     1174        update_option( 'timezone_string', $params['timezone_string'] );
     1175
     1176        $request = new WP_REST_Request( 'POST', '/wp/v2/posts' );
     1177        $request->set_param( 'status', $status );
     1178        $request->set_param( 'title', 'not empty' );
     1179        if ( isset( $params['date'] ) ) {
     1180            $request->set_param( 'date', $params['date'] );
     1181        }
     1182        if ( isset( $params['date_gmt'] ) ) {
     1183            $request->set_param( 'date_gmt', $params['date_gmt'] );
     1184        }
     1185        $response = $this->server->dispatch( $request );
     1186
     1187        update_option( 'timezone_string', '' );
     1188
     1189        $this->assertEquals( 201, $response->get_status() );
     1190        $data = $response->get_data();
     1191        $post = get_post( $data['id'] );
     1192
     1193        $this->assertEquals( $results['date'], $data['date'] );
     1194        $post_date = str_replace( 'T', ' ', $results['date'] );
     1195        $this->assertEquals( $post_date, $post->post_date );
     1196
     1197        $this->assertEquals( $results['date_gmt'], $data['date_gmt'] );
     1198        // TODO expect null here for drafts (see https://core.trac.wordpress.org/ticket/5698#comment:14)
     1199        $post_date_gmt = str_replace( 'T', ' ', $results['date_gmt'] );
     1200        $this->assertEquals( $post_date_gmt, $post->post_date_gmt );
     1201    }
     1202
    10991203    /**
    11001204     * @ticket 38698
     
    19282032        $this->assertEquals( date( 'Y-m-d', strtotime( mysql_to_rfc3339( $expected_modified ) ) ), date( 'Y-m-d', strtotime( $data['modified'] ) ) );
    19292033        $this->assertEquals( date( 'Y-m-d', strtotime( $expected_modified ) ), date( 'Y-m-d', strtotime( $new_post->post_modified ) ) );
     2034    }
     2035
     2036    /**
     2037     * @dataProvider post_dates_provider
     2038     */
     2039    public function test_update_post_date( $status, $params, $results ) {
     2040        wp_set_current_user( self::$editor_id );
     2041        update_option( 'timezone_string', $params['timezone_string'] );
     2042
     2043        $post_id = $this->factory->post->create( array( 'post_status' => $status ) );
     2044
     2045        $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', $post_id ) );
     2046        if ( isset( $params['date'] ) ) {
     2047            $request->set_param( 'date', $params['date'] );
     2048        }
     2049        if ( isset( $params['date_gmt'] ) ) {
     2050            $request->set_param( 'date_gmt', $params['date_gmt'] );
     2051        }
     2052        $response = $this->server->dispatch( $request );
     2053
     2054        update_option( 'timezone_string', '' );
     2055
     2056        $this->assertEquals( 200, $response->get_status() );
     2057        $data = $response->get_data();
     2058        $post = get_post( $data['id'] );
     2059
     2060        $this->assertEquals( $results['date'], $data['date'] );
     2061        $post_date = str_replace( 'T', ' ', $results['date'] );
     2062        $this->assertEquals( $post_date, $post->post_date );
     2063
     2064        $this->assertEquals( $results['date_gmt'], $data['date_gmt'] );
     2065        // TODO expect null here for drafts (see https://core.trac.wordpress.org/ticket/5698#comment:14)
     2066        $post_date_gmt = str_replace( 'T', ' ', $results['date_gmt'] );
     2067        $this->assertEquals( $post_date_gmt, $post->post_date_gmt );
    19302068    }
    19312069
Note: See TracChangeset for help on using the changeset viewer.