WordPress.org

Make WordPress Core

Opened 11 months ago

Last modified 2 months ago

#24415 new defect (bug)

The 'show_in_admin_all_list' argument for the 'register_post_status' function is ignored when the argument 'public' is set to 'false'

Reported by: XyntaMan Owned by:
Milestone: Awaiting Review Priority: normal
Severity: normal Version: 3.5.1
Component: Posts, Post Types Keywords: needs-patch
Focuses: administration Cc:

Description

Hello,

I stumbled upon a bug in the admin section of WordPress. I'm currently running the latest release (3.5.1) without any third-party plugins.

After creating some custom post statuses via the 'register_post_status' function, I noticed that posts with them do not appear in the default (all) post listing in the admin section, despite me setting the 'show_in_admin_all_list' argument to 'true'.

I narrowed this problem down only to the 'public' argument of the same ('register_post_status') function: if the 'public' argument of custom post status is set to 'true', then everything works as expected and the posts with a custom post status appear in the default (all) post listing in the admin section — but this also makes posts with that custom post status appear to the regular users, making them public, hence the name of the argument.

It's worth noting that the 'public' argument has no such buggy effect on the 'show_in_admin_status_list' argument of the same ('register_post_status') function: it doesn't matter to what the 'public' argument is set — the links to the appropriate post statuses are showed at the top of the post listing only based on the 'show_in_admin_status_list' argument, just like it should.

Change History (3)

comment:1 jeremyfelt3 months ago

  • Component changed from Administration to Posts, Post Types
  • Focuses admin added

comment:2 follow-up: jessepollak2 months ago

  • Keywords needs-patch added

Thanks for the bug report XyntaMan (and congrats on this being your first one!), sorry you never got a response!

I just confirmed that this bug is still around in trunk at revision 27076.

To recreate:

  1. Register a new post status with show_in_admin_all_list = true and public = false like:
register_post_status( 'test_status', array(
    'label'                     => _x( 'Test Status', 'post' ),
    'public'                    => false,
    'exclude_from_search'       => false,
    'show_in_admin_all_list'    => true,
    'show_in_admin_status_list' => true,
    'label_count'               => _n_noop( 'Unread <span class="count">(%s)</span>', 'Unread <span class="count">(%s)</span>' ),
));
  1. Give a Post that status.

Expected:

Post is not public, but it should show up in admin all list.

Actual

Post is not public and it does not show up in admin all list.

Why

So, when we look in query.php at the section that filters by post status, we see three cases:

$public_states = get_post_stati( array('public' => true) );
foreach ( (array) $public_states as $state ) {
    if ( 'publish' == $state ) // Publish is hard-coded above.
        continue;
    $where .= " OR $wpdb->posts.post_status = '$state'";
}

if ( $this->is_admin ) {
    // Add protected states that should show in the admin all list.
    $admin_all_states = get_post_stati( array('protected' => true, 'show_in_admin_all_list' => true) );

    foreach ( (array) $admin_all_states as $state )
        $where .= " OR $wpdb->posts.post_status = '$state'";
}

if ( is_user_logged_in() ) {
    // Add private states that are limited to viewing by the author of a post or someone who has caps to read private states.
    $private_states = get_post_stati( array('private' => true) );
    foreach ( (array) $private_states as $state )
        $where .= current_user_can( $read_private_cap ) ? " OR $wpdb->posts.post_status = '$state'" : " OR $wpdb->posts.post_author = $user_id AND $wpdb->posts.post_status = '$state'";
}

First, we look for all public posts. There, the custom status is obviously excluded because public is false. Then, in the admin section, we look for show_in_admin_all_list posts, but we also filter by 'protectect' => true. Since our custom post type doesn't have the protected option set (it's not even described as an option in the docs), it gets excluded here — causing the bug.

Fix

For a temporary fix, users can add protected => true to their custom post status.

For a long term fix, I recommend that we make that an OR query, so we get posts with protected statuses and all posts with show_in_admin_all_list statuses. EDIT: this fix won't work because it will add duplicate values and add private posts if they are in categories that have show_in_admin_all_list.

For a real fix, it'd be awesome if someone who knows the public/protected/private categories better could weigh in on whether they are mutually exclusive or not. For instance, would it be OK to add an additional case where array('public' => false, 'show_in_admin_all_list' => true) or could that also unearth private posts? I guess we could add a 'private' => false to that query, but then we're targeting very specifically, which seems not great. Ideally, we wouldn't expand from these three cases, just make them a little looser.

Thanks again XyntaMan!

Last edited 2 months ago by jessepollak (previous) (diff)

comment:3 in reply to: ↑ 2 landakram2 months ago

Thanks XyntaMan!

Replying to jessepollak:

It looks like the difference between protected and private is that post statuses with protected => true require edit_post capabilities to display, while post statuses with private => true require only view_post capabilities (from query.php).

Looking at post.php, it also seems that public, protected, and private, are not mutually exclusive, at least in the code. In other words, it's possible to specify

register_post_status( 'test_status', array(
    'label'                     => _x( 'Test Status', 'post' ),
    'public'                    => true,
    'private'                   => true,
    'protected'                 => true,
    'exclude_from_search'       => false,
    'show_in_admin_all_list'    => true,
    'show_in_admin_status_list' => true,
    'label_count'               => _n_noop( 'Unread <span class="count">(%s)</span>', 'Unread <span class="count">(%s)</span>' ),
));

and the custom post status will be saved just fine. When a post is queried for, public is checked for first, then protected, then private, so if public => true, then the other fields are irrelevant (and if protected => true, then private is irrelevant).

It seems to me that if public is null or false, then at the very least private should be set to true by default. This way, If someone wants to require edit_post capabilities to display a post, then they can explicitly specify protected => true. I also think protected and private should be added to the codex :)

This would also allow the posts to be shown in the default post listing, due to the third block of code in jessepollak's comment above (from query.php).

Note: See TracTickets for help on using tickets.