Fix plugin update refresh, dock badge, and stuck-row failures (#202)#211
Merged
Conversation
…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.
…lugin-update-refresh
Closed
✅ WordPress Plugin Check Report
📊 ReportAll checks passed! No errors or warnings found. 🤖 Generated by WordPress Plugin Check Action • Learn more about Plugin Check |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #202.
Three real bugs the reporter described, fixed surgically:
What was broken
Refresh button was a no-op within 12h. The in-window Refresh button hit
/wp/v2/pluginswith no special signal, so the server-side prime helper honoured Core's 12hupdate_pluginsthrottle and returned the same stale "no updates" snapshot it had already cached. The classic Plugins screen fixes this withwp_clean_plugins_cache( true )on every load — we had no equivalent escape hatch.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.Failed / silent-success updates left the row stuck on "Update available." Two sub-cases:
wp_ajax_update_pluginreturns the up-to-date branch (line 4685 ofajax-actions.php), it only shipserrorMessage— noterrorCode. Our previous code only matched onerrorCode === 'up_to_date', so we always fell through to the failure path.What changed
Server (
includes/plugins-window/rest-fields.php)desktop_mode_plugins_window_maybe_refresh_update_transient( $force = false )— new$forceflag. On force, callswp_clean_plugins_cache( true )(ordelete_site_transienton the fallback path) and thenwp_update_plugins()so the transient is repopulated with a fresh wp.org snapshot before the field callback reads it.desktop_mode_plugins_window_force_refresh_requested()reads?desktop_mode_force_refresh=1from the REST request's query string.desktop_mode_plugins_window_refresh_updatesfilter gains a second$forceargument 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.reload({ force: true })and then callsrefreshFrameworkMenu()so the dock badge repaints from the same fresh state.runUpdate()catch path:errorCode === 'up_to_date'(future-proof) orerrorMessage === 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: …".void reload()to reconcile from the server.refreshFrameworkMenu()at the end so the dock badge stays consistent.extractAjaxErrorreads eithercodeorerrorCodefrom the response (Core uses both depending on the handler).Tests
fetchInstalledPlugins({ force })URL building.$forcefilter contract.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
npm run build && npm run test:js && npm run test:php.