Update post counts (published, draft, unattached) in admin interface

I am working on a multi-user WordPress setup and have made it so a particular type of user can only see and interact with posts, images, pages etc that they have authored. The code to make this happen looks like this:

add_filter('pre_get_posts', 'current_author_posts');

function current_author_posts($query) {

    if ($query->is_admin && current_user_can('artist')) {
        global $user_ID;
        $query->set('author', $user_ID);
    }

    return $query;
}

This works well within the admin interface but leaves all of the post counts displaying incorrectly:

Read More

image that shows incorrect post counts within WordPress

Are you aware of any filters or hooks to manipulate these figures and be correct across posts, pages, media and custom post types?

Many thanks.

Related posts

Leave a Reply

4 comments

  1. I got this almost working, but refinements are needed to fit the specifics of the question and to deal with Attachments and Post-Types differently (see comments in code)…


    First, I think it’s worth noting how I found the filter:
    apply_filters( 'views_' . $screen->id, $views )

    • inspect element
      subsubsub

    • do a global search in /wp-admin and /wp-includes for subsubsub (funny class name, btw)…

    • and here it is: /wp-admin/includes/class-wp-list-table.php

    foreach( array( 'edit-post', 'edit-page', 'edit-movie', 'upload' ) as $hook )
        add_filter( "views_$hook" , 'wpse_30331_custom_view_count', 10, 1);
    
    function wpse_30331_custom_view_count( $views ) 
    {
        global $current_screen;
        switch( $current_screen->id ) 
        {
            case 'edit-post':
                $views = wpse_30331_manipulate_views( 'post', $views );
                break;
            case 'edit-page':
                $views = wpse_30331_manipulate_views( 'page', $views );
                break;
            case 'edit-movie':
                $views = wpse_30331_manipulate_views( 'movie', $views );
                break;
            case 'upload':
                $views = wpse_30331_manipulate_views( 'attachment', $views );
                break;
        }
        return $views;
    }
    
    function wpse_30331_manipulate_views( $what, $views )
    {
        global $user_ID, $wpdb;
    
        /*
         * This is not working for me, 'artist' and 'administrator' are passing this condition (?)
         */
        if ( !current_user_can('artist') ) 
            return $views;
    
        /*
         * This needs refining, and maybe a better method
         * e.g. Attachments have completely different counts 
         */
        $total = $wpdb->get_var("SELECT COUNT(*) FROM $wpdb->posts WHERE (post_status = 'publish' OR post_status = 'draft' OR post_status = 'pending') AND (post_author = '$user_ID'  AND post_type = '$what' ) ");
        $publish = $wpdb->get_var("SELECT COUNT(*) FROM $wpdb->posts WHERE post_status = 'publish' AND post_author = '$user_ID' AND post_type = '$what' ");
        $draft = $wpdb->get_var("SELECT COUNT(*) FROM $wpdb->posts WHERE post_status = 'draft' AND post_author = '$user_ID' AND post_type = '$what' ");
        $pending = $wpdb->get_var("SELECT COUNT(*) FROM $wpdb->posts WHERE post_status = 'pending' AND post_author = '$user_ID' AND post_type = '$what' ");
    
        /*
         * Only tested with Posts/Pages
         * - there are moments where Draft and Pending shouldn't return any value
         */
        $views['all'] = preg_replace( '/(.+)/U', '('.$total.')', $views['all'] ); 
        $views['publish'] = preg_replace( '/(.+)/U', '('.$publish.')', $views['publish'] ); 
        $views['draft'] = preg_replace( '/(.+)/U', '('.$draft.')', $views['draft'] ); 
        $views['pending'] = preg_replace( '/(.+)/U', '('.$pending.')', $views['pending'] ); 
    
        // Debug info
        //echo 'Default counts: <pre>'.print_r($views,true).'</pre>';
        //echo '<hr><hr>';
        //echo 'Query for this screen of this post_type: <b>'.$what.'</b><pre>'.print_r($wp_query,true).'</pre>';
    
        return $views;
    }
    
  2. As of writing, this functionality now resides in the class WP_List_Table, in the method “views()”.

    The filter now looks like this:

    $views = apply_filters( "views_{$this->screen->id}", $views );
    

    $views will contain an array of each list element:

    [19-Feb-2016 11:43:44 UTC] Array
    (
      [all] => <a href="link_to_view" class="current">Alle <span class="count">(1)</span></a>
      [trash] => <a href="link_to_view">Trash <span class="count">(94)</span></a>
      [confirmed] => <a href="link_to_view">Confirmed <span class="count">(1)</span></a>
    )
    

    You can hook it up at the current_screen hook with a priority >10:

    add_action( 'current_screen', function ( $current_screen ) {
            if ($current_screen->id === 'edit-my_page')
                add_filter( "views_{$current_screen->id}", 'list_table_views_filter' );
        }, 20);
    
    function list_table_views_filter( array $view ) {
        error_log(print_r($view, true));
        return $view;
    }
    

    You can then add/change/remove upon elements in the list.

  3. I know this is an old post, but I have run across it several times looking for a better solution, so I figured I’d add a little something to the mix. Until today, I have been parsing out each individual number from the strings filtered by view_{$screen->id}. But, I just learned these numbers are parsed in at line 248 of class-wp-posts-list-table.php. This view_{$screen->id} filter is supposed to be used to add and remove views, not change the numbers (stupid me).

    The function that does all the calculating of the numbers in the parenthesis of the view string is wp_count_posts(). And, its got a filter with the same name that’s been around since version 3.7.0 (released on October 24, 2013—two years after this question was asked).

  4. There are 2 options. Let’s assume that your custom post type key is “arts-and-crafts-movement”

    OPTION 1

    Using the hook views_{$this->screen->id} here:

    https://developer.wordpress.org/reference/hooks/views_this-screen-id/

    You should hook like this :

    add_filter( 'views_edit-arts-and-crafts-movement', 'filter_list_table_views', 10 );
    

    Then you should add somewhere a function called filter_list_table_views to filter the views:

    function filter_list_table_views( $views ){
    
       // your counting logic here
    
       return $views;
    }
    

    OPTION 2

    Using the hook wp_count_posts here:

    https://developer.wordpress.org/reference/functions/wp_count_posts/

    add_action( 'current_screen', function ( $screen ) {
                
        if( $screen->id == 'edit-arts-and-crafts-movement' ){
                
            add_filter( 'wp_count_posts', 'filter_list_table_counts' );
    
        }
    });
    
    function filter_list_table_counts( $counts ){
    
       // your counting logic here
    
       return $counts;
    }