All plugin versions and changes at a glance — new features, bugfixes and improvements.
License::api_call() extracted only key and site_url from params and silently dropped email. Server never received it → no strict check. Now properly forwarded. required attribute. visitor-lang → server-fallback-lang → EN → DE → source — English kicks in before German becomes the fallback (better lingua franca for non-DE visitors). On NL sites, the placeholder box now reliably appears in Dutch ("Deze inhoud wordt geleverd door YouTube. Om te laden is uw toestemming vereist."). cookielawinfo-*, viewed_cookie_policy, wf_loginalerted_*, wfvt_*, wfwaf-authcookie-*. Now with hand-curated translations in all 7 native languages (EN, FR, ES, IT, NL, DA, SV) — previously they fell through to raw German. A Dutch visitor now sees "Slaat de cookie-toestemming op voor elke cookie-categorie." instead of the original German description. 35 new native-dict entries. consenta-btn-accept, consenta-btn-reject and consenta-btn-details alongside consenta-btn-primary/-secondary — site owners can style each button separately via CSS (e.g. green accept, red reject). The dialog already had these classes; the banner was missing them. Backward-compatible: existing -primary/-secondary selectors keep working. localized-texts were lost. The import path used array_map('sanitize_text_field', $value), which only fits flat arrays. localized-texts is 2-level nested ([lang => [key => html]]) — the sub-arrays were passed to sanitize_text_field as non-string values → PHP warning + all translations became empty strings. Now with detection logic: nested structures are sanitized via array_walk_recursive, flat ones via array_map. Custom texts survive the JSON round-trip cleanly. Bug report bachfestivaldordrecht.nl 2026-04-29. auth_required, site_not_registered, license_inactive, rate_limit etc.), the plugin maps them via __() to local strings — automatic language selection based on admin locale. 26 new strings × 33 languages = 858 new entries in the translation database. License.php, Plugin.php, Frontend/Consent.php, WhiteLabel.php) now route through __() — so the existing 34-language system covers the newer code paths too. Plugin admin in Polish sees Polish errors, in Japanese Japanese, etc. WordPress/X.X; https://... — IONOS and some WAF providers classify this as a "bot" and respond with 401 Unauthorized, even when the Bearer token is correct. The Multi-Site Dashboard therefore showed "Invalid token" despite token regeneration. Fix: fetch_site_stats now sends Mozilla/5.0 (compatible; Consenta-MultiSite/<ver>; +https://consenta.io) — passes typical UA filters and still honestly identifies us in site owners' logs. Tokens do not need to be regenerated, just update the plugin and re-run the sync. wl_config_save / wl_config_get was comparing site URLs too strictly — if the site was originally activated as https://example.com but home_url() now returns https://www.example.com (or vice versa after a www / SSL migration), the auth check failed with "Authentication required (signed cookie or registered site_url)". The comparison now ignores scheme (http/https), www prefix, trailing slash, default ports and case — foreign domains are still rejected, but the same site under notation drift is recognized. Just retry the Sync button on affected sites. consenta_customer session cookie on consenta.io now carries an HMAC-SHA256 signature via wp_salt('auth') + per-install pepper. Previously it was base64-encoded JSON without integrity protection — a known customer email could have allowed session spoofing. 30-day migration window: existing sessions remain valid, new ones are signed immediately. /dkey) and the White-Label config sync (wl_config_save/wl_config_get) now additionally verify that the requesting site_url is in the license activation list, on top of license-key authentication. Plus per-license rate limiting (5–60 requests/hour depending on endpoint). Mitigates the risk of license keys leaking via support emails, screenshots or backups. amount=0.00. Also closed: the public ?action=addon_activate URL handler (free White-Label addon activation given any active license key); addon activation now happens exclusively from signature-verified webhook events. consenta_check_order now responds only with status + associated email address. License keys are delivered exclusively in the confirmation email to the order address. Closes a data exposure path that would have surfaced the license key given a known order_ref. hash_equals() against timing-oracle attacks. $wpdb->esc_like() on user searches closes LIKE-wildcard tricks. /stats aggregate queries (total consents, accept rate, daily/geo charts) are now cached for 5 minutes. Dashboards with 10+ connected remote sites load noticeably faster, and the license server is hit less often. uninstall hook now also removes warmup queue options and clears the license validate cron. consenta_options key via Settings::set/save, in-memory cache is updated alongside. consenta_save_paddle_txn only sets a transaction_id when the field is still empty (no override of an already-set value). Prevents audit trail corruption and silent order laundering in the edge case where multiple calls come in for the same order. crete-round, roboto-flex, playfair-display-sc …). Consenta derives the family name from the slug and downloads it on next save. Non-existent fonts produce a concrete error message. localStorage.consenta_services) was stored granularly — but invisible to users and only revocable by deleting cookies. Now clearly surfaced and revocable in one click (iframes get re-replaced with the placeholder). languages/ folder from 10 MB to 4.4 MB. We now only ship compiled .mo files (what WordPress reads at runtime) plus the .pot template. The .po source files (~6 MB, editor format for translators) are no longer in the distribution ZIP — functionality is 100 % preserved. Noticeably faster updates and smaller hosting footprint. cURL error 60: SSL certificate problem (typical on MAMP/XAMPP/Local-by-Flywheel or behind corporate proxies with broken CA bundles) the download is retried once with sslverify=false. Risk is bounded: only static CSS and woff2 files, woff2 URLs are whitelist-checked against fonts.gstatic.com, no credentials in flight. Function is_feed was called incorrectly. Conditional query tags do not work before the query is run. fixed: is_feed() calls in Blocker and TagManager are now gated with did_action('parse_query'). Resolves conflicts with IDE plugins like WPCodeBox that hook into the same actions and surface those warnings. label{display:inline} from forms.css beat our inline style="display:flex"; now hardened with dedicated CSS classes plus !important. At the same time the filter (search + category) is functional again — the !important hardening had nullified the previous style.display='none' hiding, a new .is-hidden class fixes that cleanly. str_replace " "→"+" followed by rawurlencode() turned + into %2B — Google interpreted family names literally and returned HTTP 400 → no @font-face definitions → everything fell back to system-ui. wp-content/uploads/consenta-fonts/{slug}/. The frontend then serves the font exclusively from your own domain — zero requests to fonts.googleapis.com / fonts.gstatic.com. This automatically satisfies the LG München 2022 ruling on Google Fonts (3 EO 1361/21): no IP-address transfer to Google, no consent requirement, no liability trap. License file (OFL/Apache-2.0/Ubuntu) is stored alongside the font. admin-texts.json overriding the shipped po/mo via gettext filter — previously many po/mo strings were Google-Translate output with grossly wrong terms (e.g. "Toggle anzeigen" instead of "Impressum anzeigen" in Ukrainian). TypeError fixed: Helper::localize_lifespan() now also accepts multi-language lifespan arrays ({de: "6 Monate", en: "6 months", …}). Previously the dialog crashed on sites with cookie database entries whose lifespan field was stored as an array instead of a string — the whole frontend rendering blocked with a white screen. consenta_force_frontend_output as an escape hatch for edge cases: forces frontend assets to load even when the automatic maintenance/staging detection would normally block them. Useful in exotic deployment setups. wp_footer() on some templates (Elementor Canvas, FSE Blank Templates, some landing page builders): additional fallback hooks on wp_body_open and shutdown guarantee reliable consent dialog delivery — double injection is prevented via static flags. WhiteLabel::is_licensed() now explicitly requires the white_label addon in license state — Agency tier alone no longer unlocks the branding override. Closes a bypass where Agency licenses could use the addon without purchase. /en/ subpages (where the theme short-circuits template_redirect with exit to map URL slugs). Output hooks (wp_head, wp_footer, wp_body_open, shutdown) are now attached at plugin init instead of inside template_redirect — survives aggressive theme routes. Gate checks (preview mode, URL exclusions) unified into a central is_suppressed() method. wp_remote_post now also sends the key as a query parameter — defensive against 301/302 redirects that turn POST into GET and drop the body (previously resulted in "license not found"). redirection => 0 to redirection => 3 so canonical WordPress redirects (trailing slash, http→https, WAF rewrites) stop silently killing stats fetches. HTTP error codes now map to actionable German messages (401/403/404/429/5xx). consent_type — now runs independently of DB_VERSION gate, prevents silent INSERT failures when version tracking drifts html[lang] selector hides wrong-language blocks even if server-side stripping fails consenta_auth) for server-authenticated consent verification — replaces weak djb2 hash title attribute tooltips style attribute removed from consent text sanitization allowlist wp_safe_redirect() used instead of wp_redirect() for dismiss-rescan flow url()