Add number new posts (post_status = pending) to administration menu

What I have:
A custom post type. A front end posting form which creates new posts with post_status = pending until they are manually reviewed and status changed to published.

What I need:
While I can go to the listing of posts and see if there are any with “Pending” to authorize; what I want is to add a round icon with a number inside just like what WordPress does when there are new comments. I can write the code to extract the number of pending posts but I have no idea how to insert it in the admin menu.

Read More

This is the code created by wp when a new comment is added:

<span class="awaiting-mod count-6"><span class="pending-count">1</span></span>

A screenshot which shows one new comment, i need the same at Items.

I have been looking for a solution to this for a while now and cannot find it anywhere; my next stop if no one can answer me here is to take apart the WP core and see how they do it there.

Related posts

1 comment

  1. Normally everything is contained in $labels['menu_name'] argument on cpt registration is printed in the menu, so the right place where put the count span is there, but there are 2 problems:

    1. You have to dinamically count posts, so you can’t just write there the html
    2. Even if you insert the html in the $labels['menu_name'] option or registration, the span is stripped out, because before printing the menu the value of the param is wrapped in esc_attr

    The second ‘problem’, is not a real problem, it’s the solution!

    In fact, the esc_attr function fires a filter hook attribute_escape, that we can use to calculate the pending posts count and then output the right html unescaped.

    We we must be very careful if we remove the sanitize functionality from esc_attr because this is a function used a lot times in WP and is a function related to secutirity.
    So, we have to isolate the behaviour only to admin menu and only in specific case.

    Moreover we can add filter again when it has done its work, in this way it’s removed only once.

    The only problem now is how to understand which is the right menu item for which remove the filter and add the count.

    The only chance we have, it’s insert a sort of placeholder that let us recognize the menu item. In other words, when you register your cpt you have to add an uncommon string to $labels['menu_name']:

    $labels = array(
      /** This do the magic ;) ! */
      'menu_name' => _x('My CPT %%PENDING_COUNT%%', 'My CPT post type label menu_name', 'mytextdomain'),
    
      'name' => _x('My CPT','My CPT label name','mytextdomain'),
      'singular_name' => _x('My CPT','My CPT label singular_name','mytextdomain'),
      'all_items' => _x('All My CPT','My CPT label all_items','mytextdomain'),
      'add_new' => _x('Add New','My CPT label add_new','mytextdomain'),
      'add_new_item' => _x('Add New My CPT','My CPT label add_new_item','mytextdomain'),
      'edit_item' => _x('Edit My CPT','My CPT label edit_item','mytextdomain'),
      'new_item' => _x('New My CPT','My CPT label new_item','mytextdomain'),
      'view_item' => _x('View My CPT','My CPTlabel view_item','mytextdomain'),
      'search_items' => _x('Search My CPT','My CPT label search_items','mytextdomain'),
      'not_found' => _x('My CPT not found','My CPT label not_found','mytextdomain'),
      'not_found_in_trash' => _x('My CPT not found in trash','My CPT label not_found_in_trash','mytextdomain'),
      'parent_item_colon' => _x('Parent:','My CPT label parent_item_colon','mytextdomain')
    );
    $args = array(
      'labels' => $labels,
      ... other args here
    )
    register_post_type( 'my_cpt', $args );
    

    Once the %%PENDING_COUNT%% is added, when the esc_attr is called for a string containing that text, we run our work:

    add_action('auth_redirect', 'add_pending_count_filter'); // modify esc_attr on auth_redirect
    add_action('admin_menu', 'esc_attr_restore'); // restore on admin_menu (very soon)
    
    function add_pending_count_filter() {
      add_filter('attribute_escape', 'remove_esc_attr_and_count', 20, 2);
    }
    
    function esc_attr_restore() {
      remove_filter('attribute_escape', 'remove_esc_attr_and_count', 20, 2);
    }
    
    function remove_esc_attr_and_count( $safe_text = '', $text = '' ) {
      if ( substr_count($text, '%%PENDING_COUNT%%') ) {
        $text = trim( str_replace('%%PENDING_COUNT%%', '', $text) );
        // run only once!
        remove_filter('attribute_escape', 'remove_esc_attr_and_count', 20, 2);
        $safe_text = esc_attr($text);
        // remember to set the right cpt name below
        $count = (int)wp_count_posts( 'my_cpt',  'readable' )->pending;
        if ( $count > 0 ) {
          // we have pending, add the count
          $text = esc_attr($text) . '<span class="awaiting-mod count-' . $count . '"><span class="pending-count">' . $count . '</span></span>';
          return $text;
        } 
      }
      return $safe_text;
    }
    

    That’s all, and it works!

    enter image description here

    There is only a little issue: if a pending post is published via the quick edit the pending count is not updated, because quick edit use ajax, and to update the numebr you need to use some js.
    Work on it, if you want.

Comments are closed.