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 @@ class WP_REST_Post_Statuses_Controller extends WP_REST_Controller {
$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 @@ class WP_REST_Post_Statuses_Controller extends WP_REST_Controller {
'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 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
// 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 @@ class WP_Test_REST_Post_Statuses_Controller extends WP_Test_REST_Controller_Test
$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 @@ class WP_Test_REST_Post_Statuses_Controller extends WP_Test_REST_Controller_Test
$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 @@ class WP_Test_REST_Post_Statuses_Controller extends WP_Test_REST_Controller_Test
),
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 @@ class WP_Test_REST_Posts_Controller extends WP_Test_REST_Post_Type_Controller_Te
}
+ 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 = {