diff --git a/src/wp-includes/post.php b/src/wp-includes/post.php index 2043f932ed..dde54c510d 100644 --- a/src/wp-includes/post.php +++ b/src/wp-includes/post.php @@ -337,14 +337,15 @@ function create_initial_post_types() { register_post_status( 'draft', array( - 'label' => _x( 'Draft', 'post status' ), - 'protected' => true, - '_builtin' => true, /* internal use only. */ + 'label' => _x( 'Draft', 'post status' ), + 'protected' => true, + '_builtin' => true, /* internal use only. */ /* translators: %s: Number of draft posts. */ - 'label_count' => _n_noop( + 'label_count' => _n_noop( 'Draft (%s)', 'Drafts (%s)' ), + 'date_floating' => true, ) ); @@ -394,9 +395,10 @@ function create_initial_post_types() { register_post_status( 'auto-draft', array( - 'label' => 'auto-draft', - 'internal' => true, - '_builtin' => true, /* internal use only. */ + 'label' => 'auto-draft', + 'internal' => true, + '_builtin' => true, /* internal use only. */ + 'date_floating' => true, ) ); @@ -1018,6 +1020,8 @@ function _wp_privacy_statuses() { * the top of the edit listings, * e.g. All (12) | Published (9) | My Custom Status (2) * Default is value of $internal. + * @type bool $date_floating Whether the post has a floating creation date. + * Default to false. * } * @return object */ @@ -1041,6 +1045,7 @@ function register_post_status( $post_status, $args = array() ) { 'publicly_queryable' => null, 'show_in_admin_status_list' => null, 'show_in_admin_all_list' => null, + 'date_floating' => null, ); $args = wp_parse_args( $args, $defaults ); $args = (object) $args; @@ -1085,6 +1090,10 @@ function register_post_status( $post_status, $args = array() ) { $args->show_in_admin_status_list = ! $args->internal; } + if ( null === $args->date_floating ) { + $args->date_floating = false; + } + if ( false === $args->label ) { $args->label = $post_status; } diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-post-statuses-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-post-statuses-controller.php index 155c8aaa96..c0cce245ba 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-post-statuses-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-post-statuses-controller.php @@ -234,6 +234,10 @@ public function prepare_item_for_response( $status, $request ) { $data['slug'] = $status->name; } + if ( in_array( 'date_floating', $fields, true ) ) { + $data['date_floating'] = $status->date_floating; + } + $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; $data = $this->add_additional_fields_to_object( $data, $request ); $data = $this->filter_response_by_context( $data, $context ); @@ -277,48 +281,54 @@ public function get_item_schema() { 'title' => 'status', 'type' => 'object', 'properties' => array( - 'name' => array( + 'name' => array( 'description' => __( 'The title for the status.' ), 'type' => 'string', 'context' => array( 'embed', 'view', 'edit' ), 'readonly' => true, ), - 'private' => array( + 'private' => array( 'description' => __( 'Whether posts with this status should be private.' ), 'type' => 'boolean', 'context' => array( 'edit' ), 'readonly' => true, ), - 'protected' => array( + 'protected' => array( 'description' => __( 'Whether posts with this status should be protected.' ), 'type' => 'boolean', 'context' => array( 'edit' ), 'readonly' => true, ), - 'public' => array( + 'public' => array( 'description' => __( 'Whether posts of this status should be shown in the front end of the site.' ), 'type' => 'boolean', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), - 'queryable' => array( + 'queryable' => array( 'description' => __( 'Whether posts with this status should be publicly-queryable.' ), 'type' => 'boolean', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), - 'show_in_list' => array( + 'show_in_list' => array( 'description' => __( 'Whether to include posts in the edit listing for their post type.' ), 'type' => 'boolean', 'context' => array( 'edit' ), 'readonly' => true, ), - 'slug' => array( + 'slug' => array( 'description' => __( 'An alphanumeric identifier for the status.' ), 'type' => 'string', 'context' => array( 'embed', 'view', 'edit' ), 'readonly' => true, ), + 'date_floating' => array( + 'description' => __( 'Whether posts of this status may have floating published dates.' ), + 'type' => 'boolean', + 'context' => array( 'view', 'edit' ), + 'readonly' => true, + ), ), ); 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 b1e2836af2..d355c3d076 100644 --- 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 @@ -1021,16 +1021,18 @@ protected function prepare_item_for_database( $request ) { // Post date. if ( ! empty( $schema['properties']['date'] ) && ! empty( $request['date'] ) ) { - $date_data = rest_get_date_with_gmt( $request['date'] ); + $current_date = isset( $prepared_post->ID ) ? get_post( $prepared_post->ID )->post_date : false; + $date_data = rest_get_date_with_gmt( $request['date'] ); - if ( ! empty( $date_data ) ) { + if ( ! empty( $date_data ) && $current_date !== $date_data[0] ) { list( $prepared_post->post_date, $prepared_post->post_date_gmt ) = $date_data; $prepared_post->edit_date = true; } } elseif ( ! empty( $schema['properties']['date_gmt'] ) && ! empty( $request['date_gmt'] ) ) { - $date_data = rest_get_date_with_gmt( $request['date_gmt'], true ); + $current_date = isset( $prepared_post->ID ) ? get_post( $prepared_post->ID )->post_date_gmt : false; + $date_data = rest_get_date_with_gmt( $request['date_gmt'], true ); - if ( ! empty( $date_data ) ) { + if ( ! empty( $date_data ) && $current_date !== $date_data[1] ) { list( $prepared_post->post_date, $prepared_post->post_date_gmt ) = $date_data; $prepared_post->edit_date = true; } diff --git a/tests/phpunit/tests/rest-api/rest-post-statuses-controller.php b/tests/phpunit/tests/rest-api/rest-post-statuses-controller.php index 1fe933b008..bcf9475240 100644 --- a/tests/phpunit/tests/rest-api/rest-post-statuses-controller.php +++ b/tests/phpunit/tests/rest-api/rest-post-statuses-controller.php @@ -153,7 +153,7 @@ public function test_get_item_schema() { $response = rest_get_server()->dispatch( $request ); $data = $response->get_data(); $properties = $data['schema']['properties']; - $this->assertEquals( 7, count( $properties ) ); + $this->assertEquals( 8, count( $properties ) ); $this->assertArrayHasKey( 'name', $properties ); $this->assertArrayHasKey( 'private', $properties ); $this->assertArrayHasKey( 'protected', $properties ); @@ -161,6 +161,7 @@ public function test_get_item_schema() { $this->assertArrayHasKey( 'queryable', $properties ); $this->assertArrayHasKey( 'show_in_list', $properties ); $this->assertArrayHasKey( 'slug', $properties ); + $this->assertArrayhasKey( 'date_floating', $properties ); } public function test_get_additional_field_registration() { @@ -217,6 +218,7 @@ protected function check_post_status_obj( $status_obj, $data, $links ) { ), array_keys( $links ) ); + $this->assertEquals( $status_obj->date_floating, $data['date_floating'] ); } protected function check_post_status_object_response( $response ) { diff --git a/tests/phpunit/tests/rest-api/rest-posts-controller.php b/tests/phpunit/tests/rest-api/rest-posts-controller.php index 9596dd3ccd..10d5a1e3a2 100644 --- a/tests/phpunit/tests/rest-api/rest-posts-controller.php +++ b/tests/phpunit/tests/rest-api/rest-posts-controller.php @@ -4413,6 +4413,119 @@ public function test_generated_permalink_template_generated_slug_for_posts() { } + public function test_putting_same_publish_date_does_not_remove_floating_date() { + + wp_set_current_user( self::$superadmin_id ); + + $time = date( 'Y-m-d H:i:s' ); + + $post = self::factory()->post->create_and_get( + array( + 'post_status' => 'draft', + 'post_date' => $time, + ) + ); + + $this->assertEquals( '0000-00-00 00:00:00', $post->post_date_gmt ); + + $get = new WP_REST_Request( 'GET', "/wp/v2/posts/{$post->ID}" ); + $get->set_query_params( array( 'context' => 'edit' ) ); + + $get = rest_get_server()->dispatch( $get ); + $get_body = $get->get_data(); + + $put = new WP_REST_Request( 'PUT', "/wp/v2/posts/{$post->ID}" ); + $put->set_body_params( $get_body ); + + $response = rest_get_server()->dispatch( $put ); + $body = $response->get_data(); + + $this->assertEquals( $get_body['date'], $body['date'] ); + $this->assertEquals( $get_body['date_gmt'], $body['date_gmt'] ); + + $this->assertEquals( '0000-00-00 00:00:00', get_post( $post->ID )->post_date_gmt ); + } + + public function test_putting_different_publish_date_removes_floating_date() { + + wp_set_current_user( self::$superadmin_id ); + + $time = date( 'Y-m-d H:i:s' ); + $new_time = date( 'Y-m-d H:i:s', strtotime( '+1 week' ) ); + + $post = self::factory()->post->create_and_get( + array( + 'post_status' => 'draft', + 'post_date' => $time, + ) + ); + + $this->assertEquals( '0000-00-00 00:00:00', $post->post_date_gmt ); + + $get = new WP_REST_Request( 'GET', "/wp/v2/posts/{$post->ID}" ); + $get->set_query_params( array( 'context' => 'edit' ) ); + + $get = rest_get_server()->dispatch( $get ); + $get_body = $get->get_data(); + + $put = new WP_REST_Request( 'PUT', "/wp/v2/posts/{$post->ID}" ); + $put->set_body_params( + array_merge( + $get_body, + array( + 'date' => mysql_to_rfc3339( $new_time ), + ) + ) + ); + + $response = rest_get_server()->dispatch( $put ); + $body = $response->get_data(); + + $this->assertEquals( mysql_to_rfc3339( $new_time ), $body['date'] ); + + $this->assertNotEquals( '0000-00-00 00:00:00', get_post( $post->ID )->post_date_gmt ); + } + + public function test_publishing_post_with_same_date_removes_floating_date() { + + wp_set_current_user( self::$superadmin_id ); + + $time = date( 'Y-m-d H:i:s' ); + + $post = self::factory()->post->create_and_get( + array( + 'post_status' => 'draft', + 'post_date' => $time, + ) + ); + + $this->assertEquals( '0000-00-00 00:00:00', $post->post_date_gmt ); + + $get = new WP_REST_Request( 'GET', "/wp/v2/posts/{$post->ID}" ); + $get->set_query_params( array( 'context' => 'edit' ) ); + + $get = rest_get_server()->dispatch( $get ); + $get_body = $get->get_data(); + + $put = new WP_REST_Request( 'PUT', "/wp/v2/posts/{$post->ID}" ); + $put->set_body_params( + array_merge( + $get_body, + array( + 'status' => 'publish', + ) + ) + ); + + $response = rest_get_server()->dispatch( $put ); + $body = $response->get_data(); + + $this->assertEquals( $get_body['date'], $body['date'] ); + $this->assertEquals( $get_body['date_gmt'], $body['date_gmt'] ); + + $this->assertNotEquals( '0000-00-00 00:00:00', get_post( $post->ID )->post_date_gmt ); + } + public function tearDown() { _unregister_post_type( 'private-post' ); _unregister_post_type( 'youseeme' ); diff --git a/tests/qunit/fixtures/wp-api-generated.js b/tests/qunit/fixtures/wp-api-generated.js index 57e92cb885..f9ce077692 100644 --- a/tests/qunit/fixtures/wp-api-generated.js +++ b/tests/qunit/fixtures/wp-api-generated.js @@ -7392,6 +7392,7 @@ mockedApiResponse.StatusesCollection = { "public": true, "queryable": true, "slug": "publish", + "date_floating": false, "_links": { "archives": [ { @@ -7405,6 +7406,7 @@ mockedApiResponse.StatusesCollection = { "public": false, "queryable": false, "slug": "future", + "date_floating": false, "_links": { "archives": [ { @@ -7418,6 +7420,7 @@ mockedApiResponse.StatusesCollection = { "public": false, "queryable": false, "slug": "draft", + "date_floating": true, "_links": { "archives": [ { @@ -7431,6 +7434,7 @@ mockedApiResponse.StatusesCollection = { "public": false, "queryable": false, "slug": "pending", + "date_floating": false, "_links": { "archives": [ { @@ -7444,6 +7448,7 @@ mockedApiResponse.StatusesCollection = { "public": false, "queryable": false, "slug": "private", + "date_floating": false, "_links": { "archives": [ { @@ -7457,6 +7462,7 @@ mockedApiResponse.StatusesCollection = { "public": false, "queryable": false, "slug": "trash", + "date_floating": false, "_links": { "archives": [ { @@ -7471,7 +7477,8 @@ mockedApiResponse.StatusModel = { "name": "Published", "public": true, "queryable": true, - "slug": "publish" + "slug": "publish", + "date_floating": false }; mockedApiResponse.TaxonomiesCollection = {