From 87927e56d36485e2e0201b617d8db3f874107568 Mon Sep 17 00:00:00 2001 From: Shameem Reza Date: Tue, 1 Sep 2026 17:41:43 +0600 Subject: [PATCH] Fix add-meta AJAX side effects on auto-drafts Adding a custom field to an auto-draft via the Custom Fields meta box runs wp_ajax_add_meta(), which promoted the post through edit_post(). That call added the meta a first time from the $_POST data before wp_ajax_add_meta() added it again, and _wp_translate_postdata() defaulted the missing comment and ping status to 'closed'. The block editor never resends comment_status over the REST API, so the closed status stuck after publishing. Promote the auto-draft with wp_update_post() instead. The meta is added exactly once and the post keeps its comment and ping status. See https://core.trac.wordpress.org/ticket/66016 --- src/wp-admin/includes/ajax-actions.php | 32 ++++++---- tests/phpunit/tests/ajax/wpAjaxAddMeta.php | 71 ++++++++++++++++++++++ 2 files changed, 90 insertions(+), 13 deletions(-) diff --git a/src/wp-admin/includes/ajax-actions.php b/src/wp-admin/includes/ajax-actions.php index 747bcc75e53eb..fb50754696dc8 100644 --- a/src/wp-admin/includes/ajax-actions.php +++ b/src/wp-admin/includes/ajax-actions.php @@ -1649,21 +1649,25 @@ function wp_ajax_add_meta() { // If the post is an autodraft, save the post as a draft and then attempt to save the meta. if ( 'auto-draft' === $post->post_status ) { - $post_data = array(); - $post_data['action'] = 'draft'; // Warning fix. - $post_data['post_ID'] = $post_id; - $post_data['post_type'] = $post->post_type; - $post_data['post_status'] = 'draft'; - $now = time(); - - $post_data['post_title'] = sprintf( - /* translators: 1: Post creation date, 2: Post creation time. */ - __( 'Draft created on %1$s at %2$s' ), - gmdate( __( 'F j, Y' ), $now ), - gmdate( __( 'g:i a' ), $now ) + $now = time(); + + /* + * Update the post directly instead of via edit_post(), which would + * add the meta a second time from the $_POST data and reset the + * comment and ping status of the post to 'closed'. See #66016. + */ + $post_data = array( + 'ID' => $post_id, + 'post_status' => 'draft', + 'post_title' => sprintf( + /* translators: 1: Post creation date, 2: Post creation time. */ + __( 'Draft created on %1$s at %2$s' ), + gmdate( __( 'F j, Y' ), $now ), + gmdate( __( 'g:i a' ), $now ) + ), ); - $post_id = edit_post( $post_data ); + $post_id = wp_update_post( $post_data, true ); if ( $post_id ) { if ( is_wp_error( $post_id ) ) { @@ -1676,6 +1680,8 @@ function wp_ajax_add_meta() { $response->send(); } + update_post_meta( $post_id, '_edit_last', get_current_user_id() ); + $meta_id = add_meta( $post_id ); if ( ! $meta_id ) { diff --git a/tests/phpunit/tests/ajax/wpAjaxAddMeta.php b/tests/phpunit/tests/ajax/wpAjaxAddMeta.php index dbaee104baea5..4f759c7da72a9 100644 --- a/tests/phpunit/tests/ajax/wpAjaxAddMeta.php +++ b/tests/phpunit/tests/ajax/wpAjaxAddMeta.php @@ -75,4 +75,75 @@ public function test_wp_ajax_add_meta_allows_empty_values_on_updating() { $this->assertSame( '', get_post_meta( $post, 'testkey', true ) ); } + + /** + * Adding meta to an auto-draft should only create the meta once. + * + * @ticket 66016 + */ + public function test_adding_meta_to_an_auto_draft_should_not_duplicate_the_meta() { + $post = self::factory()->post->create( + array( + 'post_status' => 'auto-draft', + ) + ); + + // Become an administrator. + $this->_setRole( 'administrator' ); + + $_POST = array( + 'post_id' => $post, + 'metakeyinput' => 'testkey', + 'metavalue' => 'testvalue', + '_ajax_nonce-add-meta' => wp_create_nonce( 'add-meta' ), + ); + + // Make the request. + try { + $this->_handleAjax( 'add-meta' ); + } catch ( WPAjaxDieContinueException $e ) { + unset( $e ); + } + + $this->assertSame( array( 'testvalue' ), get_post_meta( $post, 'testkey' ) ); + } + + /** + * Adding meta to an auto-draft should save the post as a draft + * without changing its comment or ping status. + * + * @ticket 66016 + */ + public function test_adding_meta_to_an_auto_draft_should_not_change_the_comment_and_ping_status() { + $post = self::factory()->post->create( + array( + 'post_status' => 'auto-draft', + 'comment_status' => 'open', + 'ping_status' => 'open', + ) + ); + + // Become an administrator. + $this->_setRole( 'administrator' ); + + $_POST = array( + 'post_id' => $post, + 'metakeyinput' => 'testkey', + 'metavalue' => 'testvalue', + '_ajax_nonce-add-meta' => wp_create_nonce( 'add-meta' ), + ); + + // Make the request. + try { + $this->_handleAjax( 'add-meta' ); + } catch ( WPAjaxDieContinueException $e ) { + unset( $e ); + } + + $post = get_post( $post ); + + $this->assertSame( 'draft', $post->post_status, 'The auto-draft should have been saved as a draft.' ); + $this->assertSame( 'open', $post->comment_status, 'The comment status of the post should not have changed.' ); + $this->assertSame( 'open', $post->ping_status, 'The ping status of the post should not have changed.' ); + } }