Make WordPress Core

Changeset 40114 for branches/4.7


Ignore:
Timestamp:
02/24/2017 09:58:07 PM (10 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.