Skip to content

feat(tools): add one-click Reset Internal State troubleshooting tool#1085

Merged
Miraeld merged 6 commits into
developfrom
fix/1012-add-one-click-reset
Jun 17, 2026
Merged

feat(tools): add one-click Reset Internal State troubleshooting tool#1085
Miraeld merged 6 commits into
developfrom
fix/1012-add-one-click-reset

Conversation

@Miraeld

@Miraeld Miraeld commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Note

🤖 This PR was generated by the AI delivery pipeline (orchestrator · Claude Sonnet 4.6). All code was reviewed by the lead-reviewer and QA agents before this PR was marked ready.

Description

Closes #1012

Adds a Troubleshooting section to the Imagify Settings page with a "Reset Internal State" button. Clicking the button POSTs to a new AJAX endpoint that clears stale optimization transients, process locks, and pending ActionScheduler jobs — the same cleanup that occurs on plugin deactivation, without touching user settings or media optimization records.

This resolves the most common "optimization stuck" support case with a single click, without requiring deactivate/reactivate.

Type of change

  • New feature (non-breaking change which adds functionality).
  • Bug fix (non-breaking change which fixes an issue).
  • Enhancement (non-breaking change which improves an existing functionality).
  • Breaking change (fix or feature that would cause existing functionality to not work as before).
  • Sub-task of #(issue number)
  • Chore
  • Release

Detailed scenario

What was tested

Manual scenario 1 — Troubleshooting section visible with valid API key:

  • Navigated to Settings page with a valid API key → Troubleshooting section with "Reset Internal State" button is visible below the Our Plugins section and above the submit button.

Manual scenario 2 — Section absent without API key:

  • Navigated to Settings page without an API key configured → Troubleshooting section does not render (intentionally inside is_api_key_valid() block).

Manual scenario 3 — JS handler wires up correctly:

  • Verified #imagify-reset-internal-state button has a non-empty data-nonce attribute via browser DevTools.
  • Verified imagifyOptions.resetInternalState is available in the page JS via browser console.

Manual scenario 4 — Nonce key is _wpnonce:

  • Inspected POST payload in Network tab when clicking the button → _wpnonce key present (not bare nonce).

Manual scenario 5 — Build passes:

  • npm run build succeeds with no errors. assets/js/options.min.js rebuilt from source.

Manual scenario 6 — PHPCS passes:

  • composer phpcs -- views/part-settings-tools.php inc/functions/i18n.php views/page-settings.php returns 0 errors.

How to test

  1. Activate the plugin and ensure a valid API key is configured.
  2. Go to Settings → Imagify.
  3. Scroll to the bottom — a Troubleshooting section should be visible above the footer buttons.
  4. Click Reset Internal State.
  5. A SweetAlert confirmation dialog should appear.
  6. Confirm → the button disables briefly, then a success message appears next to the button.
  7. Verify in the browser Network tab that the POST goes to admin-ajax.php with action=imagify_reset_internal_state and _wpnonce=<token>.
  8. Verify transients are cleared: with WP_DEBUG on, running a forced bulk optimization then resetting should unblock it.

Affected Features & Quality Assurance Scope

  • Settings page — new section added; existing settings sections and save button are unaffected.
  • AJAX handler — new imagify_reset_internal_state endpoint (capability-checked, nonce-verified).
  • options.js — new click handler added; existing handlers (API key check, backup alert, files tree) are unchanged.
  • uninstall.php — already updated by backend agent to use InternalStateList; no frontend impact.

Technical description

Documentation

The full technical documentation is in docs/api/tools.md:

  • AJAX action imagify_reset_internal_state with nonce action imagify_reset_internal_state.
  • Nonce delivered via data-nonce attribute on the button (same pattern as backup-dir-is-writable).
  • Nonce posted as _wpnonce in the request body — required by imagify_check_nonce() / check_ajax_referer().
  • JS localization in imagifyOptions.resetInternalState (action, confirm text, success/error labels).

New dependencies

None. No new PHP or JS dependencies. SweetAlert and jQuery already loaded with the options script handle.

Risks

  • Security: Nonce-verified + capability-checked (imagify_get_context('wp')->current_user_can('manage')). No unauthenticated access possible.
  • Data loss: Explicitly excludes user settings, API key, and all media optimization records. Only transients and scheduled-action rows are affected.
  • Double-click: Button is disabled in-flight; server-side is idempotent (deleting absent transients is a no-op).
  • Multisite: Sitemeta DELETE is guarded by is_multisite(); reset is scoped to current site only. Documented in PR and docs/api/tools.md.

Acceptance Criteria

  1. A "Troubleshooting" section with a "Reset Internal State" button appears on the Settings page for users with a valid API key
  2. Clicking the button triggers an AJAX call that clears optimization-state transients, cancels pending ActionScheduler jobs, and removes process locks
  3. User settings, API key, and media optimization records are NOT affected by the reset
  4. Only logged-in admins with manage_options capability can trigger the reset
  5. Nonce-protected AJAX endpoint validates properly
  6. Button shows confirmation dialog before resetting, and displays success/error feedback
  7. uninstall.php uses the shared InternalStateList static methods (single source of truth)
  8. No inline CSS or inline JS in views

Mandatory Checklist

Code validation

  • I validated all the Acceptance Criteria. If possible, provide screenshots or videos.
  • I triggered all changed lines of code at least once without new errors/warnings/notices.
  • I implemented built-in tests to cover the new/changed code.

Code style

  • I wrote a self-explanatory code about what it does.
  • I protected entry points against unexpected inputs.
  • I did not introduce unnecessary complexity.
  • Output messages (errors, notices, logs) are explicit enough for users to understand the issue and are actionnable.

Unticked items justification

  • Output messages — The AJAX endpoint returns wp_send_json_success() on success and wp_send_json_error() on failure. User-facing feedback is in the JS layer (success/error strings in imagifyOptions.resetInternalState). No raw server-side log messages are exposed to end users; the feedback strings are translatable and human-readable.

Additional Checks

  • In the case of complex code, I wrote comments to explain it.
  • When possible, I prepared ways to observe the implemented system (logs, data, etc.)
  • I added error handling logic when using functions that could throw errors (HTTP/API request, filesystem, etc.)

Miraeld and others added 2 commits June 12, 2026 17:04
Adds a 'Reset Internal State' button to the Imagify Settings page that
clears stale optimization transients, process locks, and pending
ActionScheduler jobs — replicating the deactivation cleanup without
touching user settings or media optimization records.

New module: classes/Tools/ (InternalStateList, ResetInternalState,
Subscriber, ServiceProvider). InternalStateList is autoloader-free so
uninstall.php can require_once it before the container is bootstrapped.
Multisite: also clears sitemeta process locks via a separate DELETE.
All DB queries use $wpdb->prepare + $wpdb->esc_like (no raw SQL).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…te tool

Adds the settings page UI for the one-click internal state reset:
views/part-settings-tools.php template with data-nonce button, JS click
handler in options.js posting _wpnonce to the AJAX endpoint, i18n
strings in the options localization, and a Playwright E2E spec.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@codacy-production

codacy-production Bot commented Jun 12, 2026

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 10 complexity · 0 duplication

Metric Results
Complexity 10
Duplication 0

View in Codacy

🟢 Coverage 76.19% diff coverage

Metric Results
Coverage variation Report missing for 35156381
Diff coverage 76.19% diff coverage (50.00%)

View coverage diff in Codacy

Coverage variation details
Coverable lines Covered lines Coverage
Common ancestor commit (3515638) Report Missing Report Missing Report Missing
Head commit (da413e6) 18536 246 1.33%

Coverage variation is the difference between the coverage for the head and common ancestor commits of the pull request branch: <coverage of head commit> - <coverage of common ancestor commit>

Diff coverage details
Coverable lines Covered lines Diff coverage
Pull request (#1085) 63 48 76.19%

Diff coverage is the percentage of lines that are covered by tests out of the coverable lines that the pull request added or modified: <covered lines added or modified>/<coverable lines added or modified> * 100%

1 Codacy didn't receive coverage data for the commit, or there was an error processing the received data. Check your integration for errors and validate that your coverage setup is correct.

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

…I docs

- Add capital-letter short descriptions to all test method doc comments
- Add @inheritdoc short descriptions to setUp()/tearDown() doc comments
- Add phpcs:ignore for intentional WordPress.WP.GlobalVariablesOverride in test setup
- Add docs/api/tools.md documenting the AJAX action, what gets cleared, and multisite notes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Comment thread classes/Tools/ResetInternalState.php
Comment thread classes/Tools/Subscriber.php Outdated
@Miraeld

Miraeld commented Jun 12, 2026

Copy link
Copy Markdown
Contributor Author

Note

Generated by the AI delivery pipeline (lead-reviewer · claude-sonnet-4-6).

Review: ❌ CHANGES REQUESTED

Blockers:

  • [MEDIUM] classes/Tools/Subscriber.php:40 — AJAX callback method is named handle_reset / mapped as 'handle_reset', but the spec (Implementation Plan step 3) requires the method and mapping key to be reset_internal_state. Fix: rename handle_reset() to reset_internal_state(), update get_subscribed_events() return value to 'wp_ajax_imagify_reset_internal_state' => 'reset_internal_state', and update the Subscriber unit test assertion on line 26 accordingly. Naming must match the spec so the implementation is auditable and consistent with the hook name convention used in the rest of the codebase.

Nice-to-haves:

  • docs/api/tools.md — file is not listed in the spec's Affected Files, but its content is accurate and useful; no action required unless the team prefers to track documentation changes in spec.

Spec step 3 mandates the AJAX callback method name is `reset_internal_state`;
`handle_reset` was used by mistake. Pure rename — no logic changes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

@Miraeld Miraeld left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed: renamed handle_reset()reset_internal_state() in classes/Tools/Subscriber.php and updated get_subscribed_events() mapping + unit test assertion to match. Commit: 497c2fc. All 12 unit tests pass locally.

…sses/

- InternalStateList::get_locked_transient_patterns() now returns plain
  (unescaped) strings; ResetInternalState::reset() assembles safe LIKE
  patterns via esc_like() on each segment, fixing the pre-escaped pattern
  issue raised in the lead-reviewer HIGH finding.
- phpunit.xml.dist coverage include now covers classes/ alongside inc/,
  so Codacy diff coverage reports on new PSR-4 code.
- Add Test_ResetInternalState test for Subscriber::reset_internal_state()
  covering the authorized and unauthorized AJAX handler paths.
- Update existing reset.php tests to mock esc_like() on $wpdb.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@Miraeld Miraeld requested review from Honemo and MathieuLamiot June 15, 2026 07:10
@Honemo

Honemo commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

@Miraeld
image

The text is very compact in the popup. It might be worth spacing it out a bit by adding something like:

line-height: 1;

to the CSS.

@Miraeld

Miraeld commented Jun 15, 2026

Copy link
Copy Markdown
Contributor Author
Screenshot 2026-06-15 at 16 24 02

@Miraeld Miraeld added this pull request to the merge queue Jun 17, 2026
Merged via the queue into develop with commit 2e57c5f Jun 17, 2026
12 of 13 checks passed
@Miraeld Miraeld deleted the fix/1012-add-one-click-reset branch June 17, 2026 14:18
@Miraeld Miraeld mentioned this pull request Jun 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature Request: One-Click Tool to Reset Imagify Internal State (Clear Transients and Stuck Jobs)

2 participants