Skip to content

Recycle Bin 'Empty bin' silently stops after 200 items #97

@epeicher

Description

@epeicher

Summary

desktop_mode_recycle_bin_empty() fetches one page of 200 items, deletes them, and returns success, leaving everything beyond the 200th item in the bin. Users with a >200-item trash see a "success" message and a still-non-empty bin.

The code comment promises "Loop in chunks" but the loop is missing.

Evidence

includes/recycle-bin/store.php:803-838:

function desktop_mode_recycle_bin_empty() {
    $purged  = 0;
    $skipped = 0;

    // Loop in chunks. `wp_delete_post()` is cheap individually but
    // hammering it on a 10k-item bin without yielding back to PHP can
    // still time out. 200 is plenty for a single REST roundtrip.
    $batch = desktop_mode_recycle_bin_get_items( array( 'per_page' => 200, 'page' => 1 ) );
    foreach ( $batch['items'] as $item ) {
        // purge…
    }

    return array(
        'purged'    => $purged,
        'skipped'   => $skipped,
        'remaining' => max( 0, $batch['total'] - $purged ),
    );
}

Client (src/recycle-bin/index.ts:606-644) calls emptyBin() once, ignores the remaining field, shows success, and re-renders. So a 500-item bin → 200 purged, 300 silently remain, UI claims success.

Repro

  1. Trash 250+ posts.
  2. Open Recycle Bin in the shell.
  3. Click "Empty bin".
  4. Observe: success toast, bin still contains 50+ items.

Independently reproduced by an automated gray-box run with 250 trashed items.

Suggested fix

Two-part:

  1. Server: loop until $batch['total'] is exhausted, OR keep the 200-cap-per-call but have the response signal remaining > 0 so the client can keep going. (The cap exists to avoid PHP timeouts on huge bins, which is a legitimate concern, keeping it and letting the client iterate is the safer call.)
  2. Client (src/recycle-bin/index.ts:606-644): if remaining > 0, auto-call the endpoint again until it hits 0 (or until skipped matches the remaining set, indicating capability-blocked items). Surface progress (e.g. "Emptying… 200 of 500").

Add a >200-item case to tests/phpunit/tests/recycleBinStore.php (and the equivalent vitest) to lock this down.

Severity

High. Silent data-state mismatch, user trusts a success message that isn't true.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions