=== VLink Broken Links Scout ===
Contributors:      vlinkasia
Tags:              broken links, link checker, redirect, 404, seo
Requires at least: 6.2
Tested up to:      7.0
Stable tag:        1.17.3
Requires PHP:      7.4
License:           GPLv2 or later
License URI:       https://www.gnu.org/licenses/gpl-2.0.html

Scan the WordPress database for internal links that return 404, 410, or 301/302 redirects, and fix them in bulk. RFC 3986 and RFC 9110 compliant.

== Description ==

**Find every broken or redirecting internal link in your WordPress content, and fix them in bulk.**

VLink Broken Links Scout reads your content straight from the database, so the front-end never gets crawled and visitors are never slowed down. Every fix is preceded by a one-click-restorable snapshot, so you can replace links across hundreds of posts without fearing a typo.

= How it works =

1. **Scan** your content for internal links.
2. **Check** each URL with a real HTTP request and classify the response.
3. **Fix** broken URLs and permanent redirects (301 / 308) one at a time or in bulk.

= What gets flagged =

* **Broken**: 404 Not Found, 410 Gone.
* **Safe to auto-replace**: 301 Moved Permanently, 308 Permanent Redirect.
* **Informational only**: 302 / 303 / 307 (the original URL is still authoritative, so the plugin will not auto-replace them).

= How you fix things =

* **One link at a time**: open the Change Link modal, see the suggested target, replace across every post that uses it.
* **In bulk**: filter by status, preview the diff, then apply.
* **Always reversible**: every fix creates a snapshot you can restore in one click.
* **Audit trail**: every change is logged with old URL, new URL, posts touched, user, and timestamp.

= Built for safety =

* Snapshot before every change. Restore is one click away.
* Background work runs in small bounded batches with retry on timeout, so visitor requests are never blocked.
* Optional scheduled scan with a clean HTML email summary.
* RFC 3986 and RFC 9110 compliant URL handling, so http vs https, www vs non-www, and trailing slash variants are all matched correctly.

== Installation ==

1. Open **Plugins &rarr; Add New &rarr; Upload Plugin** in your WordPress admin, choose the zip, and Activate. (Or drop the `vlink-broken-links-scout` folder into `wp-content/plugins/`.)
2. Open **VLink Links Scout** from the WordPress admin menu.
3. On the **Overview** tab, click **Run scan** to collect every internal URL from your content.
4. Click **Check all links** to fetch the HTTP status for each unique URL.
5. Open the **Links** tab to fix items one by one, or use the **Bulk Update** wizard for batches.

== Frequently Asked Questions ==

= Will this plugin slow my site down? =

**No.** Every long-running operation runs in the background through AJAX or WP-Cron. No plugin code executes during a visitor request.

= Does it work with custom post types? =

**Yes.** Open **VLink Links Scout &rarr; Settings** and tick the post types you want included.

= What if I replace a link by mistake? =

You can undo it. Every fix, single or bulk, creates a snapshot of the affected content before the replacement. Open the **Backups** tab and click **Restore** on the session you want to roll back.

= Why are 302 and 307 redirects not auto-fixed? =

Because RFC 9110 defines them as **temporary** redirects: the original URL is still the canonical destination. The plugin shows them as informational only. You can replace them manually if you know they have become permanent.

= Where is plugin data stored? =

In three dedicated tables inside your own WordPress database. By default these are removed when you uninstall the plugin. If you want them to persist across a delete + reinstall, turn on **Keep my data when the plugin is deleted** in **Settings &rarr; Data retention**.

= Does the plugin call any external service? =

**No.** The only outbound traffic is HTTP requests to the URLs already inside your own content, in order to read their response codes. No telemetry, no analytics, no call-home.

== Privacy ==

The plugin does not collect, store, or transmit personal data about visitors or administrators. No third-party API calls. No telemetry. No analytics. No call-home.

The only outbound traffic is HTTP requests to the URLs already present in your own content, in order to read their response codes. No cookies, no Referer header, and no request body are sent.

The optional scheduled-scan email report sends a plain summary to the WordPress admin address you configure; nothing leaves your server otherwise.

== Screenshots ==

1. Overview dashboard - four summary cards plus the three-step scan workflow (Step 1 done, Step 2 checking links in real time).
2. Foundation Audit - six priority cards (P1 Index, P2 Crawl, P3 Redirect, P4 Canonical, P5 Duplicate, Pass) with a filterable priority list of affected URLs.
3. Links table with filter pills (All, Success, Broken, Permanent / Temporary Redirects, Marked Safe, Unchecked, External, Comments, Generic anchor), search, and inline row actions.
4. Change link URL modal - auto-suggests the final destination from the redirect chain so one click replaces every variant across affected posts.
5. Bulk Update wizard, Step 1: filter by link type and status before running a batch operation.
6. Bulk Update wizard, Step 3: preview every replacement against its source post before applying.
7. Bulk Fix Progress modal - one-at-a-time replacement so each change can be audited live, with the full queue visible.
8. Backups & Restore - every bulk fix creates a snapshot of the original `post_content` so any change can be reverted with one click.

== Changelog ==

= 1.17.3 =
**Critical fix.** The Change Link replacement could leave a stray slash behind when the source URL contained accidental double slashes (`example.com//path//`), producing malformed links like `example.com/path///`. It could also rewrite the wrong URL when the source was a prefix of a longer child link (replacing `example.com/blog` would corrupt `example.com/blog/article`). Replacement is now URL-boundary aware and consumes any trailing slashes cleanly.
Step 2 is faster: per-round batch raised to 4 URLs and per-URL timeout lowered to 6 seconds, so a 1000-link scan finishes in roughly half the time on most hosts.
The Change Link modal now shows the full variant list by default, so you can see exactly which forms of the URL will be touched before clicking Replace.
After a Replace, the new URL is re-checked immediately so the row in the Links table shows its real status instead of staying as "Unchecked".

= 1.17.2 =
Step 2 (Check links) responsiveness pass: each round of the link check now finishes faster and the status line shows live elapsed seconds, so slow batches no longer read as frozen. Foundation Audit redirect-chain detection fixed: redirect counts are now tracked correctly. No schema change.

= 1.17.0 - 1.17.1 =
New Foundation Audit tab: an in-dashboard technical SEO audit that categorises issues into five priority buckets (Index, Crawl, Redirect, Canonical, Duplicate). Hardened against oversized response bodies and pathological HTML.

= 1.16.x =
Re-check button now always shows a clear status pulse and toast. Bulk Update wizard spans the full dashboard width. Plugin Check (PCP) compliance pass.

= 1.16.0 =
History tab gains drill-down detail: click the Posts count to expand an inline list of the posts touched, with anchor text parsed from the backup snapshot. Each row links straight to Edit post or Change URL again.

= 1.15.x =
Dashboard rebuild on a light surface: four summary cards (Unchecked, Passed, Warnings, Failed) with click-through into the Links tab pre-filtered. Refresh and Export CSV controls in the top bar. Roadmap tab.

= 1.13.0 - 1.14.0 =
Full-screen Bulk Fix Progress modal: replacement one link at a time with FROM and TO diff, source post and anchor text visible, instant Cancel, and fail-fast after three consecutive timeouts. Mark-safe and Ignore batches fan out concurrently.

= 1.12.0 - 1.12.7 =
Returned to zero third-party API calls. Change-link modal gains a pre-flight preview block (posts affected + variant list). SQL hardening pass.

= 1.10.0 - 1.11.x =
External link checking, comment-content scanning, auto-fix permanent redirects after scheduled scans, REST endpoints, and a UX polish pass (toasts, focus traps, empty states).

= 1.0.0 - 1.9.x =
Initial public release: RFC 3986 URI normalisation, RFC 9110 redirect classification, automatic snapshot before every fix, audit history, ignore patterns, CSV export.

== Upgrade Notice ==

= 1.17.3 =
**Critical fix.** Change Link is now URL-boundary aware: double slashes no longer leave `///` artifacts, and a source URL that is a prefix of a longer URL no longer rewrites child links. Step 2 is faster, the new URL is re-checked after Replace, and the variant list is visible upfront.

= 1.17.2 =
Step 2 (Check links) responsiveness pass: smaller rounds finish faster and a live status ticker stops slow batches from reading as frozen. Foundation Audit redirect-chain detection fixed. No schema change.

= 1.17.1 =
Hardening pass on the Foundation Audit module: bounded response handling, safer regex on canonical and noindex parsing, and Unicode-aware Soft-404 detection for non-Latin keywords.

= 1.17.0 =
New "Foundation Audit" module - a technical-SEO audit baked into the dashboard. Records HTTP code, response time, canonical and redirect depth per URL, then categorises findings into 5 priority buckets (P1 Index · P2 Crawl · P3 Redirect · P4 Canonical · P5 Duplicate).

= 1.16.3 =
The Bulk Update wizard no longer caps at ~780 px wide - every step spans the full dashboard width, matching the other tabs.

= 1.16.2 =
The row-level Re-check button now always shows a toast + brief row flash so a no-change re-check (still 404, still 200) is clearly confirmed instead of looking silent.

= 1.16.1 =
PCP report fixes. Tested-up-to bumped from 6.9 to 7.0. Upgrade Notice entries trimmed under 300 chars, Changelog under 5000 chars. Four `phpcs:disable` blocks around dynamic-WHERE prepare() calls now also list PCP's `PluginCheck.Security.DirectDB.UnescapedDBParameter` sniff.

= 1.16.0 =
History tab gains drill-down detail. Posts count is now a clickable navy pill that expands an inline list of the actual posts touched, with the visible anchor text parsed live from the backup snapshot. Each row has Edit post and Change URL again buttons.

= 1.15.9 =
Third-party admin notices are now suppressed on the plugin's own admin screens (page-scoped). The scan-time pill moves out of the top bar to a small status line directly under the tabs.

= 1.15.8 =
Summary cards re-ordered to follow workflow direction (UNCHECKED → PASSED → WARNINGS → FAILED). UNCHECKED switches from info-blue to neutral gray. Links-tab pill counters now share the colour of the parent Overview card.

= 1.15.7 =
Removes the legacy auto dark-mode CSS layer that was still inverting Backups cards and row buttons. Light VLink palette regardless of OS preference. History table column widths redistributed.

= 1.15.6 =
Redundant meta strip removed (Step 1 already reports the same numbers). Last scan / Next scheduled moves into a soft pill on the top bar. Step 3 fits above the fold on a 900 px viewport.

= 1.15.5 =
Step 2 bar no longer keeps a stale 100 %% on rescan. Base font upscaled to 14.5 px with Inter-style font features. Step cards compressed vertically.

= 1.15.4 =
Pre-resubmission audit pass. The Pro features tab is renamed to Roadmap with a "nothing here is required" disclaimer and its external CTA removed. Inline rationale added to two `reject_unsafe_urls => false` sites.

= 1.15.3 =
Brand palette aligned with the VLink Asia logo. Every accent and primary surface now uses VLink navy with a 3-stop gradient. Pure CSS swap.

= 1.15.2 =
Refresh and Export CSV icons in the top right are now wired (correct dedicated nonce). Every primary step button shares the same 180×42 px dimensions.

= 1.15.1 =
Upgrade-safety patch: admin JS scrubs every plugin-owned sessionStorage flag on plugin upgrade, so the stale "Resume check" banner no longer carries over.

= 1.15.0 =
Light VLink-style dashboard. Single row of four big summary cards with icon badges, headline counts and sub-items. Cards and sub-items click through to the Links tab pre-filtered. Step 3 buttons side-by-side on one row.

= 1.14.0 =
Premium admin UI: dark theme, gradient header, Health Score hero, icon tabs, and a Roadmap tab listing upcoming optional add-on capabilities. (Light theme reinstated in 1.15.0.)

= 1.13.3 =
Hardening pass on the 1.13 Bulk Fix Progress modal: removes silent auto-resume, adds fail-fast after 3 consecutive timeouts, tightens per-link timeout to 15 s.

= 1.13.2 =
Step 2 HTTP check now survives a page reload via a sessionStorage flag. Step 3 card redesigned as a 2-column grid. Top bar refined.

= 1.13.1 =
Stability fix on the Bulk Fix Progress modal: Close button is always live, Cancel uses AbortController. Mark-safe and Ignore bulk batches now fan out 8-at-a-time.

= 1.13.0 =
New full-screen Bulk Fix Progress modal shows every link as it gets rewritten (FROM → TO diff + source post + anchor + per-row status + Cancel). Security pass: every `$wpdb->prepare()` first argument is a literal SQL template; PCP-equivalent scan zero errors zero warnings.

= 1.12.7 =
Plugin Check (PCP) clean pass under the WordPress + PHPCompatibilityWP rulesets. Five remaining findings silenced with scoped `phpcs:disable` / `phpcs:enable` blocks that name the exact sniff being suppressed.

= 1.12.0 =
Optional Featured Image module removed. The plugin is back to its original mission with zero third-party API calls. Upgrades from any 1.11.x release automatically clean up legacy options and the scratch directory.

= 1.10.0 =
External link checking, comment-content scanning, auto-fix permanent redirects after scheduled scans, backup TTL auto-cleanup, plain-text email fallback, a read-only REST API, and a generic-anchor-text filter pill.
