From f5f503d73c851d4622b8efd5fe08a77e51c0a39f Mon Sep 17 00:00:00 2001 From: Sukhendu Sekhar Guria Date: Tue, 4 Aug 2026 15:35:37 +0530 Subject: [PATCH] fix(admin): support custom post statuses --- src/js/_enqueues/admin/post.js | 27 +++-- src/wp-admin/includes/meta-boxes.php | 54 ++++++++-- tests/phpunit/tests/admin/metaBoxes.php | 138 ++++++++++++++++++++++++ tests/qunit/index.html | 1 + tests/qunit/wp-admin/js/post.js | 121 +++++++++++++++++++++ 5 files changed, 328 insertions(+), 13 deletions(-) create mode 100644 tests/phpunit/tests/admin/metaBoxes.php create mode 100644 tests/qunit/wp-admin/js/post.js diff --git a/src/js/_enqueues/admin/post.js b/src/js/_enqueues/admin/post.js index 445e79d8a6f05..f812b1b5e57b7 100644 --- a/src/js/_enqueues/admin/post.js +++ b/src/js/_enqueues/admin/post.js @@ -308,6 +308,7 @@ jQuery( function($) { postId = $('#post_ID').val() || 0, $submitpost = $('#submitpost'), releaseLock = true, + statusBeforePrivate = null, $postVisibilitySelect = $('#post-visibility-select'), $timestampdiv = $('#timestampdiv'), $postStatusSelect = $('#post-status-select'), @@ -757,7 +758,7 @@ jQuery( function($) { if ( ! $timestampdiv.length ) return true; - var attemptedDate, originalDate, currentDate, publishOn, postStatus = $('#post_status'), + var attemptedDate, originalDate, currentDate, publishOn, selectedPostStatus, postStatus = $('#post_status'), optPublish = $('option[value="publish"]', postStatus), aa = $('#aa').val(), mm = $('#mm').val(), jj = $('#jj').val(), hh = $('#hh').val(), mn = $('#mn').val(); @@ -823,15 +824,23 @@ jQuery( function($) { // Add "privately published" to post status when applies. if ( $postVisibilitySelect.find('input:radio:checked').val() == 'private' ) { $('#publish').val( __( 'Update' ) ); + if ( postStatus.val() !== 'publish' ) { + statusBeforePrivate = postStatus.val(); + } if ( 0 === optPublish.length ) { - postStatus.append(''); + postStatus.append(''); } else { optPublish.html( __( 'Privately Published' ) ); } $('option[value="publish"]', postStatus).prop('selected', true); $('#misc-publishing-actions .edit-post-status').hide(); } else { - if ( $('#original_post_status').val() == 'future' || $('#original_post_status').val() == 'draft' ) { + if ( null !== statusBeforePrivate ) { + optPublish.html( __( 'Published' ) ); + optPublish.filter('[data-private-status-option]').remove(); + postStatus.val( statusBeforePrivate ); + statusBeforePrivate = null; + } else if ( $('#original_post_status').val() == 'future' || $('#original_post_status').val() == 'draft' ) { if ( optPublish.length ) { optPublish.remove(); postStatus.val($('#hidden_post_status').val()); @@ -843,22 +852,26 @@ jQuery( function($) { $('#misc-publishing-actions .edit-post-status').show(); } + selectedPostStatus = $('option:selected', postStatus); + // Update "Status:" to currently selected status. $('#post-status-display').text( // Remove any potential tags from post status text. - wp.sanitize.stripTagsAndEncodeText( $('option:selected', postStatus).text() ) + wp.sanitize.stripTagsAndEncodeText( selectedPostStatus.text() ) ); // Show or hide the "Save Draft" button. if ( - $('option:selected', postStatus).val() == 'private' || - $('option:selected', postStatus).val() == 'publish' + selectedPostStatus.val() == 'private' || + selectedPostStatus.val() == 'publish' ) { $('#save-post').hide(); } else { $('#save-post').show(); - if ( $('option:selected', postStatus).val() == 'pending' ) { + if ( selectedPostStatus.val() == 'pending' ) { $('#save-post').show().val( __( 'Save as Pending' ) ); + } else if ( selectedPostStatus.attr( 'data-save-text' ) ) { + $('#save-post').show().val( selectedPostStatus.attr( 'data-save-text' ) ); } else { $('#save-post').show().val( __( 'Save Draft' ) ); } diff --git a/src/wp-admin/includes/meta-boxes.php b/src/wp-admin/includes/meta-boxes.php index 535a00cd3fe94..1c4e8aabc0916 100644 --- a/src/wp-admin/includes/meta-boxes.php +++ b/src/wp-admin/includes/meta-boxes.php @@ -30,10 +30,11 @@ function post_submit_meta_box( $post, $args = array() ) { global $action; - $post_id = (int) $post->ID; - $post_type = $post->post_type; - $post_type_object = get_post_type_object( $post_type ); - $can_publish = current_user_can( $post_type_object->cap->publish_posts ); + $post_id = (int) $post->ID; + $post_type = $post->post_type; + $post_type_object = get_post_type_object( $post_type ); + $post_status_object = get_post_status_object( $post->post_status ); + $can_publish = current_user_can( $post_type_object->cap->publish_posts ); ?>
@@ -48,12 +49,16 @@ function post_submit_meta_box( $post, $args = array() ) {
post_status, array( 'publish', 'future', 'pending' ), true ) ) { - $private_style = ''; + $private_style = ''; + $save_button_text = __( 'Save Draft' ); if ( 'private' === $post->post_status ) { $private_style = 'style="display:none"'; + } elseif ( $post_status_object && ! $post_status_object->_builtin ) { + /* translators: %s: Post status label. */ + $save_button_text = sprintf( __( 'Save as %s' ), $post_status_object->label ); } ?> - type="submit" name="save" id="save-post" value="" class="button" /> + type="submit" name="save" id="save-post" value="" class="button" /> post_status && $can_publish ) { ?> @@ -121,6 +126,11 @@ function post_submit_meta_box( $post, $args = array() ) { case 'auto-draft': _e( 'Draft' ); break; + default: + if ( $post_status_object ) { + echo esc_html( $post_status_object->label ); + } + break; } ?> @@ -161,6 +171,38 @@ function post_submit_meta_box( $post, $args = array() ) { post_status, 'draft' ); ?> value='draft'> + _builtin && ! in_array( $post->post_status, $custom_post_statuses, true ) ) { + $custom_post_statuses[] = $post->post_status; + } + + foreach ( $custom_post_statuses as $custom_post_status_name ) { + $custom_post_status = get_post_status_object( $custom_post_status_name ); + if ( ! $custom_post_status || $custom_post_status->_builtin ) { + continue; + } + + /* translators: %s: Post status label. */ + $save_button_text = sprintf( __( 'Save as %s' ), $custom_post_status->label ); + ?> + post_status, $custom_post_status->name ); ?> value="name ); ?>" data-save-text="">label ); ?> + diff --git a/tests/phpunit/tests/admin/metaBoxes.php b/tests/phpunit/tests/admin/metaBoxes.php new file mode 100644 index 0000000000000..eaf6c4e463fd9 --- /dev/null +++ b/tests/phpunit/tests/admin/metaBoxes.php @@ -0,0 +1,138 @@ +user->create( array( 'role' => 'editor' ) ); + } + + public function tear_down() { + remove_filter( 'post_submitbox_custom_statuses', array( $this, 'filter_post_submitbox_custom_statuses' ) ); + _unregister_post_status( 'reviewed' ); + _unregister_post_status( 'hidden-status' ); + unset( $GLOBALS['post'], $GLOBALS['action'] ); + + parent::tear_down(); + } + + /** + * Adds the reviewed status to the post Publish meta box. + * + * @param string[] $statuses Post status names. + * @param WP_Post $post Current post object. + * @return string[] Post status names. + */ + public function filter_post_submitbox_custom_statuses( $statuses, $post ) { + if ( 'post' === $post->post_type ) { + $statuses[] = 'reviewed'; + } + + return $statuses; + } + + /** + * @ticket 12706 + * + * @covers ::post_submit_meta_box + */ + public function test_post_submit_meta_box_includes_filtered_custom_post_statuses() { + require_once ABSPATH . 'wp-admin/includes/meta-boxes.php'; + + register_post_status( + 'reviewed', + array( + 'label' => 'Reviewed & approved', + 'protected' => true, + 'show_in_admin_status_list' => false, + ) + ); + register_post_status( + 'hidden-status', + array( + 'label' => 'Hidden status', + 'protected' => true, + 'show_in_admin_status_list' => true, + ) + ); + add_filter( 'post_submitbox_custom_statuses', array( $this, 'filter_post_submitbox_custom_statuses' ), 10, 2 ); + + $post = self::factory()->post->create_and_get(); + + wp_set_current_user( self::$editor_id ); + $GLOBALS['post'] = $post; + $GLOBALS['action'] = 'edit'; + + ob_start(); + post_submit_meta_box( $post ); + $output = ob_get_clean(); + + $this->assertStringContainsString( 'value="reviewed" data-save-text="Save as Reviewed & approved">Reviewed & approved', $output ); + $this->assertStringNotContainsString( 'Hidden status', $output ); + + $page = self::factory()->post->create_and_get( array( 'post_type' => 'page' ) ); + $GLOBALS['post'] = $page; + + ob_start(); + post_submit_meta_box( $page ); + $page_output = ob_get_clean(); + + $this->assertStringNotContainsString( 'Reviewed & approved', $page_output ); + } + + /** + * @ticket 12706 + * + * @covers ::post_submit_meta_box + * @covers ::_wp_translate_postdata + */ + public function test_post_submit_meta_box_includes_and_preserves_current_custom_post_status() { + require_once ABSPATH . 'wp-admin/includes/meta-boxes.php'; + + register_post_status( + 'hidden-status', + array( + 'label' => 'Hidden status', + 'protected' => true, + ) + ); + + wp_set_current_user( self::$editor_id ); + $post = self::factory()->post->create_and_get( + array( + 'post_author' => self::$editor_id, + 'post_status' => 'hidden-status', + ) + ); + + $GLOBALS['post'] = $post; + $GLOBALS['action'] = 'edit'; + + ob_start(); + post_submit_meta_box( $post ); + $output = ob_get_clean(); + + $this->assertMatchesRegularExpression( '/\s*Hidden status\s*<\/span>/', $output ); + $this->assertMatchesRegularExpression( '/]+selected=[\'\"]selected[\'\"][^>]+value=[\'\"]hidden-status[\'\"][^>]*>Hidden status<\/option>/', $output ); + $this->assertStringContainsString( 'value="Save as Hidden status"', $output ); + + $translated_post_data = _wp_translate_postdata( + true, + array( + 'post_ID' => $post->ID, + 'post_type' => 'post', + 'post_status' => 'hidden-status', + 'save' => 'Save as Hidden status', + ) + ); + + $this->assertSame( 'hidden-status', $translated_post_data['post_status'] ); + } +} diff --git a/tests/qunit/index.html b/tests/qunit/index.html index a6b6177014586..eca9a57224dc6 100644 --- a/tests/qunit/index.html +++ b/tests/qunit/index.html @@ -167,6 +167,7 @@ +