register_post_status – show_in_admin_all_list & show_in_admin_status_list does not affect query

TL;DR: It’s not a bug (as we generally understand it), rather it’s a feature that was never fully implemented in WordPress.


Status of register_post_status()

register_post_status() function was never fully implemented in WordPress. If you check WordPress Codex entry for register_post_status() function, you’ll see it’s clearly mentioned in a notice:

NOTICE:

This function does NOT add the registered post status to the admin panel. This functionality is pending future development. Please refer to Trac Ticket #12706 . Consider the action hook post_submitbox_misc_actions for adding this parameter.

Also, if you visit the Related Ticket, you’ll see that the discussion to implement it fully is going on for over 8 years now (starting from March 2010)! However, since WordPress is open source community driven software & there weren’t a lot of volunteers willing to work on this feature, it’s still pending proper implementation.

The Trac Ticket #12706 states the following in the description:

A developer should be able to register a custom post status using register_post_status(). The admin UI (including post submit box and quick edit) should reflect this new custom post status. Furthermore, there are many hard-coded references to ‘draft’ and ‘pending’ statuses in core that should properly use the post status API.

All existing arguments to register_post_status() should be fully implemented, should also support per-post-type arguments. As things get implemented across core, there will likely be a need for supporting capabilities and bits of API.

Now if you look at the WordPress core CODE, you’ll see that this description is still valid. That means:

  • The admin UI (including post submit box and quick edit) doesn’t reflect custom post status. Check this part of the CODE in core:

    <?php _e( 'Status:' ); ?> <span id="post-status-display">
    <?php
        switch ( $post->post_status ) {
            case 'private':
                _e( 'Privately Published' );
                break;
            case 'publish':
                _e( 'Published' );
                break;
            case 'future':
                _e( 'Scheduled' );
                break;
            case 'pending':
                _e( 'Pending Review' );
                break;
            case 'draft':
            case 'auto-draft':
                _e( 'Draft' );
                break;
        }
    ?>
    </span>
    

    The CODE contains no action / filter hook for any custom post status or for any modification whatsoever. So the only logical way at the moment is to use some JavaScript to alter the UI implementation until this feature is implemented in WordPress core.

  • All existing arguments to register_post_status() is not yet fully implemented & hence may look buggy.

  • And a lot more discussion & work is needed for the full implementation of the Post Status API.

What to do about this Lack of Feature or Bug?

Call it a lack of feature or call it a bug, the fact remains: there’s a lot more work that needs to be done to complete the Post Status API.
So if this is too important for you, it’s better to join in the discussion in the Support Ticket mentioned above to expedite the development process.

Although the entire development process will need a lot of voluntary work, you can still comment on it about the argument show_in_admin_all_list not affecting the query, so that at least this part of the anomaly can be improved earlier than the rest of the Status API.

What to do until core doesn’t implement the features properly?

As it is now, show_in_admin_all_list argument only affects the All(*) status header part of the All Posts Listing, for example:

All Posts Listing Status Header (All Posts Selected)

Image-1: All Posts Listing Status Header (All Posts Selected)

However, it doesn’t affect the posts that appear in the list (although it should).

To make sure that posts with your custom status my_hidden_status doesn’t appear in the list as well, you need to set the public argument to false.

This will make the register_post_status() function call look like the following:

register_post_status( 'my_hidden_status', array(
    'label'                     => _x( 'My Hidden Status', 'post' ),
    'public'                    => false,
    'show_in_admin_all_list'    => false, 
    'show_in_admin_status_list' => false,
    'label_count'               => _n_noop( 'My Hidden Status <span class="count">(%s)</span>', 'My Hidden Status <span class="count">(%s)</span>' )
) );

This will show the correct count in All(*) status header and also remove the posts with my_hidden_status from the All Posts or All {custom_post_type} listing.

Of course, this will essentially make posts with my_hidden_status inaccessible from WordPress Admin Panel. If that was the intent, then fine, otherwise, you may use the show_in_admin_status_list to show the posts with my_hidden_status in a separate listing header:

register_post_status( 'my_hidden_status', array(
    'label'                     => _x( 'My Hidden Status', 'post' ),
    'public'                    => false,
    'show_in_admin_all_list'    => false, 
    'show_in_admin_status_list' => true,
    'label_count'               => _n_noop( 'My Hidden Status <span class="count">(%s)</span>', 'My Hidden Status <span class="count">(%s)</span>' )
) );

This will show the Post Lists with the status my_hidden_status in a separate header like the following:

All Posts Listing Status Header (Custom Status Selected)

Image-2: All Posts Listing Status Header (Custom Status Selected)

DB Query Optimization Issue:

The related DB Query comes from wp_count_posts() function in wp-includes/post.php file. If you take a look at the related CODE, you’ll see that the arguments from register_post_status() function are not implemented there. Also, there’s no way to filter or extend this part of the core CODE either:

$query = "SELECT post_status, COUNT( * ) AS num_posts FROM {$wpdb->posts} WHERE post_type = %s";
if ( 'readable' == $perm && is_user_logged_in() ) {
    $post_type_object = get_post_type_object( $type );
    if ( ! current_user_can( $post_type_object->cap->read_private_posts ) ) {
        $query .= $wpdb->prepare(
            " AND (post_status != 'private' OR ( post_author = %d AND post_status="private" ))",
            get_current_user_id()
        );
    }
}
$query .= ' GROUP BY post_status';

Unless both show_in_admin_all_list and show_in_admin_status_list are set to false for a particular status, related posts shouldn’t be removed from the query anyway. Otherwise WordPress will not be able to generate the proper status header (as shown in image-2).

However, in the case where both show_in_admin_all_list and show_in_admin_status_list are set to false, WordPress doesn’t need the count information for that custom status. So logically in this case you’ll have a little query performance improvement with AND post_status != 'my_hidden_status' part, if you have a large number of posts (like Millions) and most of your posts (as you said 90%) are from that custom status.

Still, there are two reasons why core may not update this part of the CODE even though there may be a little performance gain in your particular case:

  1. WordPress Database uses an index named type_status_date that indexes wp-posts table entries based on post_type, post_status, post_date and ID fields. So queries are fast enough even without the AND post_status != 'my_hidden_status' part.

    I’ve tested with 15K+ posts and it only takes a few milliseconds. Check this MySQL Document for more information on GROUP BY Optimization with index. With some tweaks to MySQL index, you may even get better results without even changing WP Core. For example, adding another index with post_type and post_status column may give you a few milliseconds gain.

  2. Even though adding AND post_status != 'my_hidden_status' may make the query faster (merely by milliseconds) in your particular case, WordPress still may not update it, because WordPress will have to consider the possibility where a hidden custom status has only a few posts. In those cases, adding AND post_status != 'my_hidden_status' will actually make the query results a bit slower (only by a few milliseconds).

So considering everything, WordPress may or may not update the query with AND post_status != 'my_hidden_status' when both show_in_admin_all_list and show_in_admin_status_list are set to false for a custom status. However, in my opinion, at least making the related query filterable will improve the flexibility for the developers.

Leave a Comment