Beskrivelse
Adds an admin page with three tabs:
- Dashboard — overview of all collected hit data (totals, hits per day, top redirects, device / browser / OS / language breakdown, recent hits).
- List — table of redirects. A modal lets you add a new redirect (unique 6-character lowercase alpha code, target URL — internal or external — with automatic
https://prefix when omitted, and an optional expiry date). After creation the modal shows the final redirect URL, a copy button, and a QR code that can be downloaded or copied. - Settings — configure the URL path prefix used before the code (for example
rorredirect).
When someone visits https://yourdomain/{prefix}/{code}, the plugin logs the hit (timestamp, IP, user agent, referer, parsed browser / OS / device type, language) and 302-redirects the visitor to the configured target URL.
Text domain: wa-url-redirect-premium
Ændringslog
1.1.7
- Landing page redirect script moved from the end of up into . On desktop and other non-WebView clients the script runs before the body is parsed, sets html visibility to hidden immediately, then triggers location.replace(), so the landing card no longer flashes visibly for 1–2 seconds before the browser navigates away. Meta WebView and Android tablet clients are unchanged: the script no-ops for them, and the body renders normally with the button visible.
= 1.1.6 =* Top redirects table on the Dashboard now puts the target URL on its own full-width sub-row underneath the Code + Hits row, so long target URLs no longer force the panel wider than the dashboard grid. Switched the brand logo asset to assets/images/webalive_blueduck.svg at 80×80 pixels (previously blueduck.png at 100×100).
= 1.1.5 =* Fixed Dashboard tables (Top redirects, Recent hits) overflowing their panel width when target URLs or referers were long. All widefat tables inside .wurp-panel now use table-layout: fixed with overflow-wrap: anywhere on cells, so long URLs wrap instead of stretching the layout.
= 1.1.4 =* Landing page content split into two plain-text fields: a single-line Heading (rendered as an
) and a multi-line Body (rendered as text with line breaks preserved). HTML is no longer accepted; both fields are sanitized with sanitize_text_field / sanitize_textarea_field and escaped with esc_html on output. The old landing_content column is left in place by dbDelta; on upgrade its value is copied into landing_body when the new column is empty, so existing configurations keep working. Rounded corners are now applied to all admin buttons — including the Generate button next to the code input in the modal and the primary Add redirect button in the list toolbar, both of which slipped through in 1.1.3.
= 1.1.3 =* Modernized the admin CSS that 1.1.1 promised but did not ship. Rounded corners (12–16px) on panels, cards, modal and inputs, softer box-shadows, refined nav-tabs, larger click targets on action icon buttons, cleaner list toolbar, updated dashboard cards and breakdown bars, and positioning styles for the top-right blueduck brand logo (assets/images/blueduck.png).
= 1.1.2 =* Removed the fb_landing_url feature (introduced in 1.0.11). The client-side landing-page mode (1.0.20) makes it obsolete: it handles Meta WebView / Android tablet / native browser routing purely client-side without needing a separate fallback URL. Removed the modal field, the "FB landing" column in the List table, the REST args, the RedirectRepository parameters, and the Router branch. The underlying database column is left intact by dbDelta (harmless) and simply ignored. Log rows previously served with served_type='js_redirect_fb' remain in the log; new hits log served_type='js_redirect' or 'js_redirect_fb_landing' where the landing-page mode is active.
= 1.1.1 =* Modernized the admin UI. Rounded corners (12–16px), softer box-shadows, cleaner panels/cards/tables/modal, refined nav tabs, and consistent button styling with hover/active states across the plugin’s Dashboard, List, Log and Settings tabs. Added a 100×100 pixel Webalive brand logo (blueduck) anchored to the top-right corner of the plugin's admin pages. The image file is loaded from assets/images/blueduck.png inside the plugin folder; if the file is not present the logo is silently omitted so nothing breaks. Drop blueduck.png into that folder to activate the logo.
= 1.1.0 =* Version bump for translation-readiness. Fixed a leftover Danish source string in the landing-content textarea placeholder — it was hard-coded and not wrapped in an i18n call. Now uses esc_attr__() with an English source string so Poedit's "Update from source" picks it up cleanly.
= 1.0.21 =* Landing page client-side JS also skips auto-redirect for Android tablets (Android without "Mobile" in the UA). Android tablets often lack the MobilePay app configured, and qr.mobilepay.dk on a tablet tends to attempt an app handoff instead of showing the QR fallback, so it is better to leave the button visible and let the user tap. Android phones (with "Mobile" in UA), iOS, and desktop are unchanged.
= 1.0.20 =* New optional landing-page mode per redirect. Three new columns on the redirects table (landing_enabled BOOL, landing_content TEXT, landing_button_label VARCHAR) added via dbDelta migration (DB_VERSION 4 -> 5). When enabled, the router serves a small HTML landing page with the admin-authored content and a tap-through button pointing at target_url. A tiny JS on the landing page runs client-side and auto-invokes location.replace(target_url) UNLESS it detects a Meta in-app browser (FBAN / FBAV / FB_IAB / FBIOS / Instagram in navigator.userAgent). This bypasses the server-side cache problem: even if the landing HTML is cached and served to every visitor, the JS runs per client and picks the right behavior. Meta WebView users see the button (their tap triggers Facebook's own "leaving app" dialog and hands off to the system browser, so universal links to apps like MobilePay resolve correctly), everyone else is redirected transparently. The modal in the List tab has a new "Landing page" section with the checkbox + textarea + button label input.
= 1.0.19 =* Log Target column no longer falls back to the redirect's current target_url for rows without served_url. Pre-1.0.18 hits have no per-hit snapshot, so showing the current target_url would misrepresent what was actually served at that time. Those rows now display "—" honestly. Post-1.0.18 hits continue to display the exact URL that was served.
= 1.0.18 =* Store per-hit served_url on the hits table (new TEXT column via dbDelta migration, DB_VERSION 3 -> 4) and display it in the Log tab's Target column. Previously the Target column showed the redirect's current target_url via JOIN, so every hit on the same redirect displayed identical values and did not reflect fb_landing_url routing decisions. Old rows without served_url fall back to the joined target_url.
1.0.17
- Removed the visible "Redirecting to the target URL…" fallback paragraph from the redirect page. It was intended as a no-JS accessibility fallback but flashed briefly for real users when the meta refresh / location.replace fired. The response body is now empty apart from the meta refresh and JS redirect; noscript / bot fallback is provided by the meta refresh alone.
1.0.16
- Fixed a modal bug where creating a new redirect after editing an existing one would silently PATCH the previously edited row instead of creating a new one. Reason: HTMLFormElement.reset() does not reliably clear programmatically-set values on hidden inputs, so the hidden id field kept the previous row's id and the submit handler saw it as an edit. openFormForCreate() now explicitly clears every input (id, code, target, fb_landing, expires) before opening the modal, and openFormForEdit() explicitly sets each value.
1.0.15
- Added a diagnostic raw-request logger hooked on plugins_loaded priority 1 (earlier than the normal init routing). Every incoming request whose path starts with the configured redirect prefix (e.g. /r/) is written to wp_wurp_hits with redirect_id=0 and served_type='raw_request', before any validation runs. This lets admins see IF a request reaches PHP at all, regardless of whether the code is valid, expired, or subsequently intercepted. If FB Android taps do not appear in the raw log, PHP is never invoked for them (upstream cache / CDN / dropin / FB itself) and no plugin-side code can affect the flow.
1.0.14
- Meta detection now also matches Facebook's server-side crawlers (facebookexternalhit, Facebot, meta-externalagent), not just the in-app browsers (FBAN / FBAV / FB_IAB / FBIOS / Instagram). This is the cache-bypass trick that actually works: when Facebook shares a link, its link-preview crawler is the very first request — usually before any real user taps the link. By serving the fb_landing_url to the crawler, page-cache plugins store the fb_landing response as THE cached copy for /r/{code}. Every subsequent visitor — including the FB Android in-app browser — is then served that cached fb_landing response even if their request never reaches PHP because of the cache. No manual cache purging or plugin-side exclusion is required.
1.0.13
- Registered explicit cache exclusions for the redirect path prefix (e.g. /r/{code}) with the major WordPress page-cache plugins: WP Rocket (rocket_cache_reject_uri), WP Super Cache (wpsc_add_noncached_url), W3 Total Cache (w3tc_pgcache_reject_uri) and LiteSpeed Cache (litespeed_api_purge_post plus litespeed_esi_status filter). Without these registrations, cache plugins may serve an already-cached HTML response to real visitors before our PHP router runs, which silently skips both hit logging AND the fb_landing_url routing for Meta in-app browsers. After deploying 1.0.13, admins must purge the cache once so any stale cached entries under the prefix are removed.
1.0.12
- Added Edit action per row in the List tab. Clicking Edit opens the redirect modal in edit mode with the code shown as read-only and target URL, FB landing URL (new in 1.0.11) and expiry pre-populated. Submitting the modal now issues a REST PATCH to /wurp/v1/redirects/{id} (create still uses POST). A new column "FB landing" is shown in the list table so it is visible at a glance whether a given redirect has a Meta WebView landing page configured.
1.0.11
- New optional "FB landing URL" per redirect. When a hit comes from a Facebook / Instagram / Messenger in-app browser (Meta WebView detected via UA), the router serves the FB landing URL instead of the primary target URL. This lets admins point Meta traffic to a dedicated landing page that hosts a normal link the user can tap — which triggers FB's own "leaving app" dialog and hands the URL off to the system browser, so universal links to apps like MobilePay open correctly. Non-Meta visitors continue to be redirected to the primary target. The new column is added via a dbDelta migration (DB_VERSION 2 → 3). The list modal now has the extra optional field, an Edit action on each row lets you set / change the FB landing URL on existing redirects (including the code itself and expiry), and the Log tab records served_type=js_redirect_fb whenever the FB landing path fires.
= 1.0.10 =* Removed the Meta in-app browser interstitial ("Continue / Open in MobilePay / Copy link" tap-through page introduced in 1.0.3 and extended in 1.0.4). End users would not follow the copy-link workaround, so all visitors — including those inside the Facebook / Instagram / Messenger WebViews — now go straight through the standard JS redirect page. The Meta detection helpers and the mobilepaydk:// URL builder are retained for diagnostic use by the Log tab, and mobilepaydk:// is still accepted as a direct target URL (from 1.0.8) for admins who prefer the custom scheme.
1.0.9
- Redirect responses are now protected against page caching. Whenever the router matches a code and is about to serve either the JS redirect or the Meta interstitial, it now emits Cache-Control: no-store, no-cache, must-revalidate, private, max-age=0 plus Pragma: no-cache and defines the DONOTCACHEPAGE constant. This prevents WP page cache plugins (WP Rocket, WP Super Cache, LiteSpeed Cache, W3 Total Cache, Comet Cache, etc.) as well as reverse proxies / CDNs (Cloudflare, Varnish) from caching the first response and then serving it to all subsequent visitors, which would silently skip both hit logging and the FB WebView / MobilePay rewrite.
1.0.8
- Fixed a fatal error on the Settings tab caused by leftover code from the old file-based logger (removed in 1.0.7) still calling Router::log_file_path(). The Settings tab now only shows redirect prefix configuration; the Log tab remains the sole location for hit diagnostics.
- Custom-scheme targets like mobilepaydk://send?phone=115107&comment=... are now accepted as direct target URLs. The plugin registers mobilepaydk as an allowed protocol via the wp_allowed_protocols filter and updates the REST create endpoint's whitelist. When the target is already a custom scheme, the plugin passes it through unchanged (no rewrite needed).
1.0.7
- Replaced file-based diagnostic logging with a dedicated Log tab in WP-admin. Every routed hit is stored directly in the wp_wurp_hits table with two new columns (served_type, native_url) via a dbDelta migration, and the new "Log" tab (between List and Settings) shows the most recent 200 hits with time, code, IP, device/browser/OS, whether the visitor was in a Meta in-app browser, the target URL, the generated mobilepaydk:// URL (if the MobilePay rewrite kicked in), and which response path was served. No files are written to disk.
1.0.5
- Added diagnostic logging. Every routed hit is now appended to wp-content/wurp-debug.log with timestamp, request URI, code, IP, user agent (truncated), is_meta_inapp flag, target URL, the generated mobilepaydk:// URL (if the MobilePay rewrite kicked in), and which response path was served (js_redirect / meta_interstitial / meta_interstitial+native). A read-only viewer showing the last 200 log lines plus a "Clear log" button have been added to the Settings tab so the log can be inspected without SSH access.
1.0.4
- When the target is a MobilePay payment link (https://qr.mobilepay.dk/paymentlink?phone=…&comment=…&lock=…) and the visitor is in a Meta in-app browser, the interstitial's primary button now invokes MobilePay's native custom scheme (mobilepaydk://send?…) instead of the https URL. The custom scheme bypasses the universal-link handoff that Facebook's WebView breaks, so the MobilePay app receives the payload intact. The original https URL is kept as a secondary fallback link.
1.0.3
- Detect Facebook / Instagram / Messenger in-app browsers (FBAN / FBAV / FB_IAB / FBIOS / Instagram) and serve a tap-through interstitial instead of an auto redirect. Programmatic navigations inside those WebViews block universal links (so apps such as MobilePay never receive a valid payload); a user-initiated tap on the interstitial's button lets the system hand off correctly. Includes a "Copy link" fallback and a hint to open the page in the system browser. All other browsers still get the instant redirect.
1.0.2
- Redirect now delivers a lightweight HTML page (meta refresh + JS location.replace) with HTTP 200 instead of a 302 Location header. This prevents Facebook (and other link crawlers) from flagging the short link as "invalid link" when the target URL is on the same or another platform that rejects redirect chains. Real users still experience an immediate redirect and hit tracking is unchanged.
1.0.1
- Redirect codes may now be 2 to 10 lowercase letters (a–z) when entered manually. The Generate button still produces a 6-character code by default.
1.0.0
- Initial release.