== Changelog == = 2.2.32 = * Security: the WordPress dashboard widget is now restricted to users with the `manage_options` capability, the same capability required by all plugin admin pages. Previously it was registered for anyone with `edit_posts` (editors, authors and contributors as well as administrators), exposing submission statistics to roles that cannot access the plugin. The render callback also returns silently when the capability check fails instead of calling `wp_die()`, which killed the whole dashboard page. * Performance: the widget statistics (active forms, pages, submissions, distinct emails) are now computed by the new `accua_contact_forms_dashboard_get_stats()` helper and cached in the `accua_forms_dashboard_stats` transient for 5 minutes, so repeat dashboard loads run zero COUNT queries against the submissions tables (the distinct-emails COUNT scanned the whole `accua_forms_submissions_values` table on every dashboard load). A short TTL was chosen over invalidation hooks because submissions are written, trashed, restored and anonymized from several independent code paths. `get_plugin_data()` is now called with `$markup = false, $translate = false`, skipping markup filtering and header translation on each render. * Coding conventions in the widget: `afsv_type LIKE 'autoreply_email'` replaced with `=` (no wildcard was involved), counts cast to int and displayed through `number_format_i18n()`, the Dashboard link built with `admin_url()` and escaped with `esc_url()` instead of a hardcoded relative `admin.php?page=...` URL, redundant untranslated `title` attribute removed, data fetching moved out of the table markup. The widget id (`accua_contact_forms_dashboard_widget_news`) and function names are unchanged so existing screen-option preferences and any `remove_action()` calls keep working. * Code organization: the widget code (stats helper and render callback) moved from `contact-forms.php` to the new `admin/dashboard-widget.php`, included on demand from the `wp_dashboard_setup` callback after the capability check. Since `wp_dashboard_setup` only fires on the dashboard screen, the widget code is never loaded on the frontend, on other admin pages, or for users who cannot see the widget. Only the registration hook remains in the main plugin file. = 2.2.31 = * Fix: the sidebar admin menu icon no longer briefly flashes the brand blue (#15caff) before WordPress recolors it grey on load. `add_menu_page()` shipped the icon as a base64 SVG with the brand fill baked in; WordPress core `wp-admin/js/svg-painter.js` repaints admin-menu SVG icons to the active color scheme's icon color only after JavaScript runs, so the first server paint showed blue and the repaint caused a visible flash. The icon is now rendered server-side in the scheme's base icon color, so the first paint already matches svg-painter's result and there is no flash. Because color schemes register on `admin_init` (after `admin_menu`), the exact color is applied in an `admin_init` (priority 20) pass that rewrites the icon in the `$menu` global: `accua_forms_paint_menu_icon()` calls `accua_forms_admin_menu_icon()` / `accua_forms_admin_menu_icon_color()`, which resolve per active scheme (Default/modern -> #f3f1f1, Classic/fresh -> #a7aaad) and fall back to #a7aaad. The standalone brand icon (`assets/img/accua-contacts-forms.svg`) and the inline colored SVGs in the plugin's page headers are unchanged, so those contexts keep the colored logo. Also removed a stale rule in `assets/css/admin.css` that tried to force the menu icon to stay colored by targeting `.wp-menu-image img` (dead code: base64 SVG menu icons render as a `div` background-image, not an `img`). CSS version 192. = 2.2.30 = * Fix: restored the original frontend form DOM id format `accua-form___accua-form__{id}_{uniqid}`. Version 2.2.21 silently changed it to `accua-form_{id}_{uniqid}` (the internal `__accua-form__` prefix was stripped in `AccuaForm::__construct()` to match the then-new E2E selectors), breaking custom CSS/JS, analytics triggers and integrations that target the form element by id. The E2E selector helpers (`tests/helpers/selectors.ts`, now via a shared `formIdPrefix()`) were updated to the restored format instead. Anchor ids (`formSubmitSuccess-{id}` etc.) are unaffected: they are derived from the internal form id by `get_anchor_id()`, which strips the prefix independently of the DOM id. = 2.2.29 = * Fix: the reCAPTCHA v2 lazy-load trigger in `classes/Element/Captcha2.php` now uses a delegated `change` handler on `document` instead of binding directly to the elements matched when the first captcha's inline script executes during HTML parsing. With multiple forms on a page, the trigger script only runs once (guarded by `accuaform_recaptcha2_ajax_loaded`), so fields rendered after the first captcha (e.g. a second form in the footer) never got the handler: interacting with them did not load the reCAPTCHA API and the captcha stayed hidden until the first form was touched. The delegated binding also covers fields placed below the captcha within a single form and forms injected after page load. = 2.2.28 = * Feature: Multiple post checkboxes now honors a `post_type=` override in its "Additional query parameters" (validated against public post types), matching post-select; previously the render path set the query post_type unconditionally to the field dropdown, discarding the parameter. The value is admin-supplied (saved config, not a client request), so there is no tampering surface. The rest of the post-multicheckbox privacy model already matched post-select (server-rendered opt-in via `post_status=publish,private`, drafts never rendered, submit validated by the options allowlist) and needed no change. * Tests: new E2E spec `tests/e2e/frontend/post-multicheckbox.spec.ts` covering private/draft hidden by default, private rendered+stored only with `post_status=publish,private`, the `post_type=` override, and forged-private-checkbox submit rejection. = 2.2.27 = * Feature: post-select / post-multicheckbox "Additional query parameters" now honor `post_status`, limited to publish/private via `accua_forms_filter_field_post_status()`. On the anonymous post-select AJAX endpoint (where extra_args is client-controlled), `post_status=private` is only honored when the received string matches a saved field configuration AND the request's resolved post type matches that configuration's post type (`accua_forms_extra_args_is_saved_config()`), or the user has `read_private_posts` (form editor preview). Binding the post type prevents lifting a private-enabled query string from one post type onto another. The selected-option prefetch, the PostSelect element render (`getAllowedPostStatuses()`), and submit-time validation accept the configured statuses accordingly. Draft/pending/future are never exposed. * Fix: Post select / Multiple post checkboxes fields no longer drop child posts. `accua_get_pages()` applied `get_page_children()` to an already-paginated, publish-only result batch (because `hierarchical` defaults to 1), so any post whose parent was not in the same batch was silently removed: children of draft/private parents never appeared, searching for a child post returned nothing, `parent=`/`include=` filters could return empty lists, and AJAX "load more" pagination ended prematurely (`has_more` was computed on the filtered count). * Fix: `child_of=` now returns all descendants (like core `get_pages()`) via the new `accua_forms_get_post_descendant_ids()` helper, instead of direct children only; `exclude_tree=` excludes the full subtree computed against the whole tree rather than the current batch. Both are resolved to explicit ID lists before querying so they compose correctly with pagination, search and `exclude=`. * Fix: hierarchical (tree) ordering is now applied only when the result set is complete and title-sorted, and posts whose ancestors are unavailable are appended instead of dropped. * Security: the `accua_forms_get_posts` AJAX endpoint (`selected=` prefetch) and the PostSelect element's selected-option rendering no longer disclose titles of draft/private/pending posts; both now require a publicly viewable post of the field's effective post type. * Fix: post-select submitted values are validated against the field's configured post type (honoring the `post_type=` override in extra args, via `AccuaForm_Element_PostSelect::getEffectivePostType()`). * Fix: post-multicheckbox `{__post_id_*}` / `{__post_url_*}` tokens used the whole value array instead of each item, producing "Array"/empty output. * Fix: `AccuaForm_Element_PostSelect` lost its `post_type`/`extra_args` properties when the form instance was serialized into the submission transient (parent `OptionElement::__sleep()` whitelist), so submit-time validation compared against the defaults. Added a `__sleep()` override preserving the element's own properties. * Fix: post-select.js dropped a search typed while the initial option load was still in flight (`isLoading` early return), leaving the unfiltered list; a stale slow response could also overwrite newer search results. Requests now carry a sequence token so the latest request always wins (JS version 118). = 2.2.26 = * Plugin Check (PCP) compliance fixes: `esc_sql()` applied to all `$wpdb->prefix` table name assignments in `includes/privacy.php`, with `phpcs:disable/enable` blocks for multi-line prepared queries; dashboard page `orderby`/`order` sort parameters sanitized with `wp_unslash()`/`sanitize_key()`, plus `phpcs:ignore` annotations for nonce-safe GET reads, an underscore-prefixed internal function name, and the block editor render variable. * Removed stale build artifact zip files from the plugin directory. = 2.2.25 = * Fix: Translations no longer trigger a `_load_textdomain_just_in_time` notice on WordPress 6.7+. Form processing and the database-version check are now hooked to `init` (priorities 5 and 1 respectively) instead of `plugins_loaded`. This ensures translations are loaded after `after_setup_theme`, as WordPress requires since 6.7. * Translations IT/ES: 49 previously untranslated strings added (lead statuses, border/title style options, form-editor labels, submission detail strings, notes UI, filter dropdowns). = 2.2.24 = * Fix: Trashing a submission from the single-submission page now redirects correctly. The previous helper function echoed HTML output before returning, causing `wp_safe_redirect()` to fail (headers already sent). The redirect path now calls the echo-free `accua_forms_trash_submission()` directly. = 2.2.23 = * Fix: Validation summary links now point to the field they reference (`href="#fieldId"` instead of `href="#"`), enabling native browser focus-on-click behaviour and fixing keyboard/screen-reader navigation. * Fix: Email format inline error no longer appears twice when the field loses focus after a prior format error was already displayed. The blur handler now checks for an existing non-removing error element instead of the parent's CSS class, preventing a race condition with the required-blur handler. = 2.2.22 = * Fix: Non-AJAX form submission now sets the correct URL hash (#formSubmitSuccess-{id}) after page reload. Previously the form action defaulted to "#", causing the URL to show a bare "#". The form action is now set to the current page URL at construction time, and the post-submit DOMContentLoaded script always calls history.replaceState regardless of whether the anchor element exists. = 2.2.21 = * Fix: AJAX form submission no longer changes the page URL. The smooth-scroll helper previously called history.replaceState after each successful/invalid submit, updating the URL hash. This was unintended for AJAX mode where the page must not navigate. = 2.2.20 = * Admin: consolidated tab component into a single JS file; removed redundant script dependency. = 2.2.19 = * Translations IT: fixed "Submissions" noun → "Compilazioni" (was "Compilati"); updated "Forms submissions" and "Contact Forms - Submissions" accordingly. * Translations ES: fixed missing accent "Envíos" (was "Envios"); unified "Unique submissions" / "Total submissions" / "Submissions from all forms" to use "envíos" consistently (was "presentaciones"). = 2.2.18 = * PCP 2.0.0 compliance: raised minimum WordPress version to 5.9; fixed stable tag mismatch; added translators comments to printf calls; replaced parse_url() with wp_parse_url(); added phpcs:ignore for already-escaped helper output; removed redundant load_plugin_textdomain() call (WordPress auto-loads since 4.6). = 2.2.17 = * Fieldset group field: New "Border and Title" dropdown in the form editor with 6 style options: no border/no title, border only, border + inline legend, border + title above, border + title inside, no border + title inside. * Fieldset group field: Group Title input is now always visible and appears first in the settings panel. * Fieldset group field: Widget title bar displays the group title for both the Fieldset Begin and Fieldset End widgets; Fieldset End mirrors the preceding Fieldset Begin label. * Frontend CSS: Fieldset border is now opt-in (removed unconditional `1px solid #ccc`). Existing fieldsets lose their border — users can re-enable it via the new dropdown. Resolves unexpected border appearing after upgrading from v1.9.x. * New CSS helpers: `.accua-fieldset-border`, `.accua-fieldset-title-outer`, `.accua-fieldset-title-inner`. = 2.2.16 = * Dashboard: replaced ID column with an Actions column (Open / Move to Trash links); default sort changed to Submitted. * Dashboard: Actions column is always non-wrapping; Move to Trash link is red; translations added (IT: Azioni / Sposta nel cestino, ES: Acciones / Mover a la papelera). * Dashboard & Submissions list: URL columns (Page, Referrer) now use CSS-driven overflow detection instead of a fixed 80-character threshold. Only cells that truly overflow the rendered column width get the [+] expandable widget; short URLs are shown as plain links with no clutter. * Submissions list: Referrer column no longer wraps for medium-length URLs. * New JS: `assets/js/admin/expandable-cells.js` — measures natural link width via `Range.getBoundingClientRect()` and builds `
/` only when needed. = 2.2.15 = * Hidden field: the "CSS ID" setting now correctly sets the `id` attribute on the hidden `` element itself (since hidden fields have no wrapper div). = 2.2.14 = * Frontend CSS: scoped `button:focus`, `input[type="checkbox"]:focus`, and `input[type="radio"]:focus` focus styles inside `.accua-form` to avoid polluting page-level button styles. = 2.2.13 = * **Date field**: Fixed validation error for custom date fields - now correctly extracts minDate/maxDate properties from form configuration. = 2.2.12 = * Submissions list: renamed "Review" column to "Open"; updated "Essential Columns" preset (ID, Open, Form, IP, Page, Referrer, Language, Submitted, Email). * Submissions list: new users now see essential columns visible by default. * Form editor: fixed widget Save button alignment (Cancella/Chiudi left, Salva right). = 2.2.11 = * Form editor: Added "Default Submit button" informational widget permanently at the bottom of the form area (non-draggable, non-sortable). * Form editor: The widget auto-hides when a custom submit-type field is in the form, and reappears when removed. = 2.2.10 = * Single submission page: refactored to the WordPress-standard two-column postbox layout (`#poststuff`, `#post-body.columns-2`). Submitted fields and Details tables on one side; Submission, Lead status and Notes postboxes on the other. Form name shown as a link. * Notes: author display name shown instead of email address. * Removed toggletip, progress icons and custom submitbox styles; simplified `set-lead-status.js` and `single-submission.js`. CSS 185, JS 111. = 2.2.9 = * Submissions list: long page URLs and referrers collapse to a 2-line clamp with a `
` toggle that expands to the full clickable link; Page and Referrer columns are now clickable links. * Dashboard last 10 submissions: same expandable pattern; column names aligned with the submissions list. * Submissions list: export buttons moved below the description for better discoverability. * Translation fix (IT): page title "Compilazioni" -> "Compilati". = 2.2.8 = * Single submission page: replaced the custom table with WordPress `widefat fixed striped` styling; human-readable field labels resolved from field definitions; internal structural fields hidden; line breaks preserved in text values; columns stack responsively at 782px. * Dashboard last 10 submissions: rewritten with sortable columns, ID as primary column with row actions (View | Trash), and a single JOIN query replacing the previous N+1 pattern (30 queries per page load reduced to 1). * Forms list: fixed the misaligned sort indicator on the Submissions column; removed stale inline CSS and float-based sorting hacks. * Cleanup: removed dead `.accua_forms_trash` CSS and dead `del_sub_form` handler. = 2.2.7 = * **Refactor**: Extracted settings page into `admin/settings-page.php` (enqueue + render). * **Refactor**: Extracted form list/add/edit pages into `admin/form-editor.php`. * **Refactor**: Extracted privacy/GDPR/data-retention into `includes/privacy.php`. = 2.2.6 = * **Refactor**: Extracted fields page into `admin/fields-page.php` with separate JS file. * **Refactor**: Moved submissions pages to `admin/` with separate JS files (`submissions-list.js`, `single-submission.js`). * **Refactor**: Replaced all form editor inline `