Skip to content

Fix plugin update refresh, dock badge, and stuck-row failures (#202)#211

Merged
epeicher merged 2 commits into
trunkfrom
worktree-issue-202-plugin-update-refresh
May 14, 2026
Merged

Fix plugin update refresh, dock badge, and stuck-row failures (#202)#211
epeicher merged 2 commits into
trunkfrom
worktree-issue-202-plugin-update-refresh

Conversation

@epeicher
Copy link
Copy Markdown
Collaborator

@epeicher epeicher commented May 14, 2026

Closes #202.

Three real bugs the reporter described, fixed surgically:

What was broken

  1. Refresh button was a no-op within 12h. The in-window Refresh button hit /wp/v2/plugins with no special signal, so the server-side prime helper honoured Core's 12h update_plugins throttle and returned the same stale "no updates" snapshot it had already cached. The classic Plugins screen fixes this with wp_clean_plugins_cache( true ) on every load — we had no equivalent escape hatch.

  2. Dock badge drifted out of sync with the window. The Refresh handler only re-fetched row data; it never called refreshFrameworkMenu(). After any successful update, the row cleared but the dock-icon "1" badge could persist.

  3. Failed / silent-success updates left the row stuck on "Update available." Two sub-cases:

    • When Core's wp_ajax_update_plugin returns the up-to-date branch (line 4685 of ajax-actions.php), it only ships errorMessage — not errorCode. Our previous code only matched on errorCode === 'up_to_date', so we always fell through to the failure path.
    • The failure path never re-fetched the row, so a server-success / client-error race (timeout, parse failure) left the UI showing "Update available" even though disk was already at the new version.

What changed

Server (includes/plugins-window/rest-fields.php)

  • desktop_mode_plugins_window_maybe_refresh_update_transient( $force = false ) — new $force flag. On force, calls wp_clean_plugins_cache( true ) (or delete_site_transient on the fallback path) and then wp_update_plugins() so the transient is repopulated with a fresh wp.org snapshot before the field callback reads it.
  • New helper desktop_mode_plugins_window_force_refresh_requested() reads ?desktop_mode_force_refresh=1 from the REST request's query string.
  • The desktop_mode_plugins_window_refresh_updates filter gains a second $force argument so hosts that block wp.org calls outright can stay opted out even on user-initiated refreshes.

Client (src/plugins-window/)

  • fetchInstalledPlugins({ force?: boolean }) appends the new query arg when force is set.
  • The Refresh button handler now awaits reload({ force: true }) and then calls refreshFrameworkMenu() so the dock badge repaints from the same fresh state.
  • runUpdate() catch path:
    • Detects Core's up-to-date signal via either errorCode === 'up_to_date' (future-proof) or errorMessage === wp.i18n.__('The plugin is at the latest version.') (the actual current shape). On match, clears the row's update marker and shows a calm "X is already up to date." toast instead of "Update of X failed: …".
    • Other failures: shows the failure toast and kicks void reload() to reconcile from the server.
    • Both branches now always call refreshFrameworkMenu() at the end so the dock badge stays consistent.
  • extractAjaxError reads either code or errorCode from the response (Core uses both depending on the handler).

Tests

  • New JS tests covering fetchInstalledPlugins({ force }) URL building.
  • New PHPUnit tests for the force-refresh query-string detector, the throttle-bypass behaviour, and the $force filter contract.
  • 1211 JS tests + 674 PHP tests pass post-merge with trunk.

Manual test coverage

Verified end-to-end against the reporter's exact sequence: empty cache → Refresh → Update succeeds, dock badge clears. Also covered: non-force path still throttles (regression coverage); up-to-date silent-success path; hard-failure path (download 404) leaves row + badge intact and surfaces a clear error toast.

Test plan

  • Pull this branch, run npm run build && npm run test:js && npm run test:php.
  • In the desktop Plugins window, click Refresh on a stale state — pending update should appear.
  • Update a plugin that's already at latest version externally — calm "already up to date" toast, no scary "failed" wording.
  • Simulate a download failure (bad package URL) — clear failure toast, row stays on "Update available", dock badge stays correct.
Open WordPress Playground Preview

epeicher added 2 commits May 14, 2026 12:03
…reconcile (#202)

Three bugs from GH#202, each with a small surgical fix:

1. Refresh button: bypass the 12h `update_plugins` throttle so a user-
   initiated refresh actually re-queries wp.org. The button now sends
   `?desktop_mode_force_refresh=1`; the REST field callback calls
   `wp_clean_plugins_cache( true )` + `wp_update_plugins()`. The
   `desktop_mode_plugins_window_refresh_updates` filter gains a `$force`
   arg so hosts that block wp.org calls can opt out.

2. Dock badge: the Refresh handler now also calls
   `refreshFrameworkMenu()` so the plugin-icon update count stays in
   sync with the in-window list. `runUpdate()` does the same after both
   success and failure so the badge never lags behind the row state.

3. Update-failure: detect Core's `up_to_date` signal by matching the
   translated error message (Core only ships `errorCode` for WP_Error
   results, not for the bulk-upgrade "already at latest version"
   branch). Treat it as a calm soft-success instead of a scary failure
   toast. Hard failures now also kick a `reload()` so a server-success /
   client-error race self-heals.

Adds JS + PHP unit tests for the new helper, query-string detection,
and filter contract. Docs updated for the new filter signature.
@epeicher epeicher mentioned this pull request May 14, 2026
@github-actions
Copy link
Copy Markdown

✅ WordPress Plugin Check Report

✅ Status: Passed

📊 Report

All checks passed! No errors or warnings found.


🤖 Generated by WordPress Plugin Check Action • Learn more about Plugin Check

@epeicher epeicher merged commit f4945a5 into trunk May 14, 2026
5 checks passed
@epeicher epeicher deleted the worktree-issue-202-plugin-update-refresh branch May 14, 2026 10:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Plugin update issues

1 participant