[wp-trac] [WordPress Trac] #65191: deletePluginSuccess does not update counts for custom plugin_status groups

WordPress Trac noreply at wordpress.org
Thu May 7 15:05:13 UTC 2026


#65191: deletePluginSuccess does not update counts for custom plugin_status groups
--------------------------+---------------------------------------
 Reporter:  andrew.p      |      Owner:  (none)
     Type:  defect (bug)  |     Status:  new
 Priority:  normal        |  Milestone:  Awaiting Review
Component:  Plugins       |    Version:
 Severity:  minor         |   Keywords:  needs-testing needs-patch
  Focuses:                |
--------------------------+---------------------------------------
 #60495 added the `plugins_list_status_text` filter, so plugins can
 register custom group tabs on `plugins.php` by adding entries to the array
 passed through `plugins_list` and supplying a label through the new
 filter.

 The server side works. The count badge is right on initial load, and
 `?plugin_status=<custom>` filters the list as expected.

 The AJAX delete flow does not keep up. `wp.updates.deletePluginSuccess` in
 `wp-admin/js/updates.js` only decrements counts for the hardcoded buckets
 (`upgrade`, `inactive`, `active`, `recently_activated`). Anything added
 through `plugins_list` is skipped, so the badge on the custom tab stays
 stale until the page reloads.

 == Steps to reproduce ==

 1. Drop the snippet below into a must-use plugin to register a sample
 group.

 {{{
 #!php
 <?php
 add_filter( 'plugins_list', function( $plugins ) {
         if ( empty( $plugins['all'] ) ) {
                 return $plugins;
         }

         $plugins['demo_group'] = array_filter(
                 $plugins['all'],
                 static function ( $data, $file ) {
                         return str_starts_with( $file, 'a' );
                 },
                 ARRAY_FILTER_USE_BOTH
         );

         return $plugins;
 } );

 add_filter( 'plugins_list_status_text', function( $text, $count, $type ) {
         return $type === 'demo_group' ? 'Demo Group' : $text;
 }, 10, 3 );
 }}}

 2. Install two plugins whose folder starts with `a`, plus one that does
 not.
 3. Visit `wp-admin/plugins.php`. The "Demo Group (2)" tab shows the right
 count.
 4. Click the tab. Delete one of the listed plugins from the row actions.
 5. The row fades out, but the tab still reads "(2)".
 6. Reload the page. The tab now reads "(1)", matching what the server
 returns.

 The hardcoded tabs (`Active`, `Inactive`) decrement correctly in the same
 flow.

 == Cause ==

 In `wp-admin/js/updates.js`, `deletePluginSuccess` checks four buckets and
 stops:

 {{{
 #!js
 if ( -1 !== _.indexOf( plugins.upgrade, response.plugin ) ) { ... }
 if ( -1 !== _.indexOf( plugins.inactive, response.plugin ) ) { ... }
 if ( -1 !== _.indexOf( plugins.active, response.plugin ) ) { ... }
 if ( -1 !== _.indexOf( plugins.recently_activated, response.plugin ) ) {
 ... }
 }}}

 Other keys present in `plugins` are not iterated.

 == Suggested approach ==

 After the existing blocks, walk any remaining keys and apply the same
 pattern. Hardcoded keys keep their explicit path, so existing sites see no
 behavior change.

 {{{
 #!js
 var knownKeys = [ 'all', 'search', 'upgrade', 'active', 'inactive',
         'recently_activated', 'mustuse', 'dropins', 'paused',
         'auto-update-enabled', 'auto-update-disabled' ];

 _.each( _.keys( plugins ), function( key ) {
         if ( -1 !== _.indexOf( knownKeys, key ) ) {
                 return;
         }
         if ( ! _.isArray( plugins[ key ] ) ) {
                 return;
         }
         if ( -1 === _.indexOf( plugins[ key ], response.plugin ) ) {
                 return;
         }

         plugins[ key ] = _.without( plugins[ key ], response.plugin );

         var $tab = $views.find( '.' + key );

         if ( plugins[ key ].length ) {
                 $tab.find( '.count' ).text( '(' + plugins[ key ].length +
 ')' );
         } else {
                 $tab.remove();
         }
 } );
 }}}

 The activate, deactivate, and update success callbacks likely have the
 same gap. Happy to roll those into one patch if reviewers want a single
 sweep, otherwise this ticket can stay scoped to delete.

 == Notes ==

 `wp-plugin-delete-success` is already triggered as a jQuery event, so
 individual plugins can shim this on the client. With #60495 turning custom
 groups into a documented extension point, asking every consumer to ship
 the same workaround does not feel right.

 == Test plan ==

 - Hardcoded tabs: delete still decrements `Active`, `Inactive`, and
 `Update available` exactly as before.
 - Custom group registered via `plugins_list`: the count drops on delete,
 and the tab disappears when it reaches zero.
 - Sites with no custom group: no change, no extra DOM lookups.

-- 
Ticket URL: <https://core.trac.wordpress.org/ticket/65191>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform


More information about the wp-trac mailing list