Make WordPress Core

Changeset 40101


Ignore:
Timestamp:
02/21/2017 06:17:32 PM (8 years ago)
Author:
jnylen0
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.

Fixes #39256.

Location:
trunk
Files:
5 edited

Legend:

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

    r40047 r40101  
    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 );
  • trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php

    r40080 r40101  
    10051005            if ( ! empty( $date_data ) ) {
    10061006                list( $prepared_post->post_date, $prepared_post->post_date_gmt ) = $date_data;
     1007                $prepared_post->edit_date = true;
    10071008            }
    10081009        } elseif ( ! empty( $schema['properties']['date_gmt'] ) && ! empty( $request['date_gmt'] ) ) {
     
    10111012            if ( ! empty( $date_data ) ) {
    10121013                list( $prepared_post->post_date, $prepared_post->post_date_gmt ) = $date_data;
     1014                $prepared_post->edit_date = true;
    10131015            }
    10141016        }
  • trunk/tests/phpunit/tests/rest-api.php

    r38832 r40101  
    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}
  • trunk/tests/phpunit/tests/rest-api/rest-comments-controller.php

    r39954 r40101  
    961961    }
    962962
     963    public function comment_dates_provider() {
     964        return array(
     965            'set date without timezone' => array(
     966                'params'   => array(
     967                    'timezone_string' => 'America/New_York',
     968                    'date'            => '2016-12-12T14:00:00',
     969                ),
     970                'results' => array(
     971                    'date'            => '2016-12-12T14:00:00',
     972                    'date_gmt'        => '2016-12-12T19:00:00',
     973                ),
     974            ),
     975            'set date_gmt without timezone' => array(
     976                'params'   => array(
     977                    'timezone_string' => 'America/New_York',
     978                    'date_gmt'        => '2016-12-12T19:00:00',
     979                ),
     980                'results' => array(
     981                    'date'            => '2016-12-12T14:00:00',
     982                    'date_gmt'        => '2016-12-12T19:00:00',
     983                ),
     984            ),
     985            'set date with timezone' => array(
     986                'params'   => array(
     987                    'timezone_string' => 'America/New_York',
     988                    'date'            => '2016-12-12T18:00:00-01:00',
     989                ),
     990                'results' => array(
     991                    'date'            => '2016-12-12T14:00:00',
     992                    'date_gmt'        => '2016-12-12T19:00:00',
     993                ),
     994            ),
     995            'set date_gmt with timezone' => array(
     996                'params'   => array(
     997                    'timezone_string' => 'America/New_York',
     998                    'date_gmt'        => '2016-12-12T18:00:00-01:00',
     999                ),
     1000                'results' => array(
     1001                    'date'            => '2016-12-12T14:00:00',
     1002                    'date_gmt'        => '2016-12-12T19:00:00',
     1003                ),
     1004            ),
     1005        );
     1006    }
     1007
     1008    /**
     1009     * @dataProvider comment_dates_provider
     1010     */
     1011    public function test_create_comment_date( $params, $results ) {
     1012        wp_set_current_user( self::$admin_id );
     1013        update_option( 'timezone_string', $params['timezone_string'] );
     1014
     1015        $request = new WP_REST_Request( 'POST', '/wp/v2/comments' );
     1016        $request->set_param( 'content', 'not empty' );
     1017        $request->set_param( 'post', self::$post_id );
     1018        if ( isset( $params['date'] ) ) {
     1019            $request->set_param( 'date', $params['date'] );
     1020        }
     1021        if ( isset( $params['date_gmt'] ) ) {
     1022            $request->set_param( 'date_gmt', $params['date_gmt'] );
     1023        }
     1024        $response = $this->server->dispatch( $request );
     1025
     1026        update_option( 'timezone_string', '' );
     1027
     1028        $this->assertEquals( 201, $response->get_status() );
     1029        $data = $response->get_data();
     1030        $comment = get_comment( $data['id'] );
     1031
     1032        $this->assertEquals( $results['date'], $data['date'] );
     1033        $comment_date = str_replace( 'T', ' ', $results['date'] );
     1034        $this->assertEquals( $comment_date, $comment->comment_date );
     1035
     1036        $this->assertEquals( $results['date_gmt'], $data['date_gmt'] );
     1037        $comment_date_gmt = str_replace( 'T', ' ', $results['date_gmt'] );
     1038        $this->assertEquals( $comment_date_gmt, $comment->comment_date_gmt );
     1039    }
     1040
    9631041    public function test_create_item_using_accepted_content_raw_value() {
    9641042        wp_set_current_user( self::$admin_id );
     
    19692047        $this->assertEquals( mysql_to_rfc3339( $updated->comment_date ), $comment['date'] );
    19702048        $this->assertEquals( '2014-11-07T10:14:25', $comment['date'] );
     2049    }
     2050
     2051    /**
     2052     * @dataProvider comment_dates_provider
     2053     */
     2054    public function test_update_comment_date( $params, $results ) {
     2055        wp_set_current_user( self::$editor_id );
     2056        update_option( 'timezone_string', $params['timezone_string'] );
     2057
     2058        $comment_id = $this->factory->comment->create();
     2059
     2060        $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/comments/%d', $comment_id ) );
     2061        if ( isset( $params['date'] ) ) {
     2062            $request->set_param( 'date', $params['date'] );
     2063        }
     2064        if ( isset( $params['date_gmt'] ) ) {
     2065            $request->set_param( 'date_gmt', $params['date_gmt'] );
     2066        }
     2067        $response = $this->server->dispatch( $request );
     2068
     2069        update_option( 'timezone_string', '' );
     2070
     2071        $this->assertEquals( 200, $response->get_status() );
     2072        $data = $response->get_data();
     2073        $comment = get_comment( $data['id'] );
     2074
     2075        $this->assertEquals( $results['date'], $data['date'] );
     2076        $comment_date = str_replace( 'T', ' ', $results['date'] );
     2077        $this->assertEquals( $comment_date, $comment->comment_date );
     2078
     2079        $this->assertEquals( $results['date_gmt'], $data['date_gmt'] );
     2080        $comment_date_gmt = str_replace( 'T', ' ', $results['date_gmt'] );
     2081        $this->assertEquals( $comment_date_gmt, $comment->comment_date_gmt );
    19712082    }
    19722083
  • trunk/tests/phpunit/tests/rest-api/rest-posts-controller.php

    r40037 r40101  
    11531153    }
    11541154
     1155    public function post_dates_provider() {
     1156        $all_statuses = array(
     1157            'draft',
     1158            'publish',
     1159            'future',
     1160            'pending',
     1161            'private',
     1162        );
     1163
     1164        $cases_short = array(
     1165            'set date without timezone' => array(
     1166                'statuses' => $all_statuses,
     1167                'params'   => array(
     1168                    'timezone_string' => 'America/New_York',
     1169                    'date'            => '2016-12-12T14:00:00',
     1170                ),
     1171                'results' => array(
     1172                    'date'            => '2016-12-12T14:00:00',
     1173                    'date_gmt'        => '2016-12-12T19:00:00',
     1174                ),
     1175            ),
     1176            'set date_gmt without timezone' => array(
     1177                'statuses' => $all_statuses,
     1178                'params'   => array(
     1179                    'timezone_string' => 'America/New_York',
     1180                    'date_gmt'        => '2016-12-12T19:00:00',
     1181                ),
     1182                'results' => array(
     1183                    'date'            => '2016-12-12T14:00:00',
     1184                    'date_gmt'        => '2016-12-12T19:00:00',
     1185                ),
     1186            ),
     1187            'set date with timezone' => array(
     1188                'statuses' => array( 'draft', 'publish' ),
     1189                'params'   => array(
     1190                    'timezone_string' => 'America/New_York',
     1191                    'date'            => '2016-12-12T18:00:00-01:00',
     1192                ),
     1193                'results' => array(
     1194                    'date'            => '2016-12-12T14:00:00',
     1195                    'date_gmt'        => '2016-12-12T19:00:00',
     1196                ),
     1197            ),
     1198            'set date_gmt with timezone' => array(
     1199                'statuses' => array( 'draft', 'publish' ),
     1200                'params'   => array(
     1201                    'timezone_string' => 'America/New_York',
     1202                    'date_gmt'        => '2016-12-12T18:00:00-01:00',
     1203                ),
     1204                'results' => array(
     1205                    'date'            => '2016-12-12T14:00:00',
     1206                    'date_gmt'        => '2016-12-12T19:00:00',
     1207                ),
     1208            ),
     1209        );
     1210
     1211        $cases = array();
     1212        foreach ( $cases_short as $description => $case ) {
     1213            foreach ( $case['statuses'] as $status ) {
     1214                $cases[ $description . ', status=' . $status ] = array(
     1215                    $status,
     1216                    $case['params'],
     1217                    $case['results'],
     1218                );
     1219            }
     1220        }
     1221
     1222        return $cases;
     1223    }
     1224
     1225    /**
     1226     * @dataProvider post_dates_provider
     1227     */
     1228    public function test_create_post_date( $status, $params, $results ) {
     1229        wp_set_current_user( self::$editor_id );
     1230        update_option( 'timezone_string', $params['timezone_string'] );
     1231
     1232        $request = new WP_REST_Request( 'POST', '/wp/v2/posts' );
     1233        $request->set_param( 'status', $status );
     1234        $request->set_param( 'title', 'not empty' );
     1235        if ( isset( $params['date'] ) ) {
     1236            $request->set_param( 'date', $params['date'] );
     1237        }
     1238        if ( isset( $params['date_gmt'] ) ) {
     1239            $request->set_param( 'date_gmt', $params['date_gmt'] );
     1240        }
     1241        $response = $this->server->dispatch( $request );
     1242
     1243        update_option( 'timezone_string', '' );
     1244
     1245        $this->assertEquals( 201, $response->get_status() );
     1246        $data = $response->get_data();
     1247        $post = get_post( $data['id'] );
     1248
     1249        $this->assertEquals( $results['date'], $data['date'] );
     1250        $post_date = str_replace( 'T', ' ', $results['date'] );
     1251        $this->assertEquals( $post_date, $post->post_date );
     1252
     1253        $this->assertEquals( $results['date_gmt'], $data['date_gmt'] );
     1254        // TODO expect null here for drafts (see https://core.trac.wordpress.org/ticket/5698#comment:14)
     1255        $post_date_gmt = str_replace( 'T', ' ', $results['date_gmt'] );
     1256        $this->assertEquals( $post_date_gmt, $post->post_date_gmt );
     1257    }
     1258
    11551259    /**
    11561260     * @ticket 38698
     
    19842088        $this->assertEquals( date( 'Y-m-d', strtotime( mysql_to_rfc3339( $expected_modified ) ) ), date( 'Y-m-d', strtotime( $data['modified'] ) ) );
    19852089        $this->assertEquals( date( 'Y-m-d', strtotime( $expected_modified ) ), date( 'Y-m-d', strtotime( $new_post->post_modified ) ) );
     2090    }
     2091
     2092    /**
     2093     * @dataProvider post_dates_provider
     2094     */
     2095    public function test_update_post_date( $status, $params, $results ) {
     2096        wp_set_current_user( self::$editor_id );
     2097        update_option( 'timezone_string', $params['timezone_string'] );
     2098
     2099        $post_id = $this->factory->post->create( array( 'post_status' => $status ) );
     2100
     2101        $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', $post_id ) );
     2102        if ( isset( $params['date'] ) ) {
     2103            $request->set_param( 'date', $params['date'] );
     2104        }
     2105        if ( isset( $params['date_gmt'] ) ) {
     2106            $request->set_param( 'date_gmt', $params['date_gmt'] );
     2107        }
     2108        $response = $this->server->dispatch( $request );
     2109
     2110        update_option( 'timezone_string', '' );
     2111
     2112        $this->assertEquals( 200, $response->get_status() );
     2113        $data = $response->get_data();
     2114        $post = get_post( $data['id'] );
     2115
     2116        $this->assertEquals( $results['date'], $data['date'] );
     2117        $post_date = str_replace( 'T', ' ', $results['date'] );
     2118        $this->assertEquals( $post_date, $post->post_date );
     2119
     2120        $this->assertEquals( $results['date_gmt'], $data['date_gmt'] );
     2121        // TODO expect null here for drafts (see https://core.trac.wordpress.org/ticket/5698#comment:14)
     2122        $post_date_gmt = str_replace( 'T', ' ', $results['date_gmt'] );
     2123        $this->assertEquals( $post_date_gmt, $post->post_date_gmt );
    19862124    }
    19872125
Note: See TracChangeset for help on using the changeset viewer.