Skip to content

v6.18.0

Choose a tag to compare

@github-actions github-actions released this 08 Aug 03:13
· 8 commits to main since this release
cffec5f

Fixed

  • Short URLs of posts/pages now follow the current permalink (#888): a post's short URL stored the permalink captured at creation, so changing the slug (or the site's permalink structure) stranded it; the redirect, the admin Destination column and the CSV export now resolve a post-linked short URL from the post's current permalink (falling back to the stored target only if the post is gone). Manual links keep their stored destination. Self-healing, no migration.
  • Certificate layout toolbar polish (#865): the Import HTML / Background Image / Load buttons now share one size, border and dashicon alignment, and the duplicated inline-dashicon + leftover emoji rules were removed.
  • Default certificate templates no longer copy shipped images into the Media Library (#865): a default with a stale html/ image ref made the Rewrite html/ Image References migration side-load it into every site's uploads; the migration now skips shipped defaults and a seeder bump refreshes their bodies to the assets/ source.
  • Internal (#849) — the GitHub-updater's SHA-256 mismatch error is now an English source string (was hard-coded pt_BR), so the translation flows through the catalog.
  • User-edit FFC role assignments survive the native "Update user" save (#826): FFC roles are snapshotted and re-applied on profile_update (core's single-role <select> was collapsing the user back and dropping the FFC role); the role chips now also surface the real failure reason and disable the non-Administrator chips for admin targets (per the #739 policy).
  • Internal (#822) — the datetime→unix safety-net migration now targets the real ffc_self_scheduling_appointments table (it pointed at a non-existent name and silently no-op'd); behaviour-preserving.

Added

  • URL Shortener enhancements (#886, #888, #889): a per-post-type Expose column publishes a page's short URL as the WordPress canonical shortlink — the rel="shortlink" <head> tag + HTTP Link header, via pre_get_shortlink (opt-in, a server-enforced subset of the shortened types); an Edit action changes a manual link's destination/title (post-linked ones follow the page); a Generate missing short URLs backfill creates links for already-published posts; and GET /ffc/v1/short-urls/{code}/qr returns a link's QR as base64 (cap-gated by ffc_view_url_shortener via an Application Password, for external automations like N8N). All reuse the existing qr_cache/generation and add no module coupling.
  • Certificate template pool (#865): a database-backed ffc_cert_template pool (gated by ffc_manage_forms / ffc_view_forms) with a Templates management screen — list table, Show/Hide visibility toggle, HTML-body editor, Duplicate / Export (.html) / Add New / Restore defaults, and a sample-data Preview iframe — plus a non-destructive versioned seeder for the shipped defaults. The form editor's layout box wires to it: Load reads the pool server-side (replacing the unauthenticated html/ fetch), Save as model stores the current HTML, and Insert Image / Background Image use wp.media. Shipped defaults are read-only (Duplicate to customize). The first step toward retiring the update-fragile html/ folder.
  • Certificate branding tokens {{logo_gov}} / {{logo_org}} (#865): two Settings → General → Branding logo URLs resolve into the certificate, ficha and appointment-receipt templates (Media Library picker, shipped fallback), de-hardcoding the instance-specific logos.
  • Retire the html/ drop-folder — migration cards (#865): two Settings → Migrations cards (gated by ffc_manage_settings_dangerzone) import any user certificate layouts left in html/ into the template pool and rewrite html/ image references (form layouts, backgrounds and pool bodies) into the Media Library — each distinct file side-loaded once, idempotent, missing files reported; plus an admin notice flagging any remaining html/ reference.
  • Authenticated operator REST API (#858, #840): POST /ffc/v1/operator/certificates issues a certificate (reusing the public form's SubmissionHandler + validation) and GET …/{id}/pdf returns its pdf_data, both via WordPress Application Passwords. Issuance is gated by ffc_manage_certificates; the PDF route by that cap or certificate ownership. Omits the public path's anti-anonymous defenses (a per-user_id rate-limit guards instead) and audits each issuance; additive — the existing handlers are untouched.
  • Encryption Key Health — one-click key/salt suggestions (#851, #846): while FFC's encryption key still derives from the WordPress salts, the Advanced-tab card offers ready-to-paste FFC_ENCRYPTION_KEY / FFC_HASH_SALT snippets (64 chars, generated client-side over a PHP-literal-safe charset, never saved or sent) with Generate another / Copy, each carrying a self-documenting phpdoc comment and the mandatory rotation caveat. Hidden once decoupled.
  • Auto-update via GitHub Releases (#820): WordPress's update check now sees the plugin's GitHub Releases — the update appears in Dashboard → Updates with the release changelog and the per-plugin auto-update toggle (opt-in). Points at the built ffcertificate-X.Y.Z.zip, caches 12h with an ETag request, verifies the package SHA-256 before install, and adds an Update URI: header so wp.org can't hijack the slug.
  • Internal (#809) — added a tests/Integration/ layer: a PluginBootstrapSmokeTest boots the real plugin object graph over a stubbed WordPress boundary and asserts it composes and lands its hooks / crons / roles (the wiring the unit LoaderTest deliberately excludes). Tests only; the coverage floor is untouched.

Changed

  • Internal (#890) — plugin-wide inline-style consolidation: lifted static inline style="…" across the admin/settings, audience, recruitment and admin-template views into reusable classes — spacing/layout utilities (ffc-mt-8/12/16, ffc-mb-2/4, ffc-ml-18, ffc-inline, ffc-list-disc) plus component rules — and moved the colour swatches/dots onto the --ffc-color custom property. No behaviour change; email templates, the PDF-rendered certificate/ficha/receipt HTML, JS-toggled display states and documentation examples were left inline by necessity.
  • ⚠ Internal (#883) — CSS class-name consistency pass: aligned shorthand admin classes to the spelled-out ffc- kebab convention (ffc-em-*ffc-email-model-*, ffc-sched-exc-*ffc-schedule-exception-*, the CSV info-table ffc-j*ffc-info-table-*, and the margin utilities ffc-mt15/ffc-mt20/ffc-mb5ffc-mt-15/ffc-mt-20/ffc-mb-5). No behaviour change; breaking only for custom CSS that overrode the old class names.
  • Certificate editor UX (#865): the template edit screen gained the form editor's CodeMirror (read-only for defaults); shipped defaults are read-only except an AJAX-autosaving Show in Load list sidebar toggle (now a .ffc-toggle switch); and the default templates' full-page background moved from a baked <img> into the dedicated Background Image field (seed v3, so Load carries it into the form like any user template).
  • Shipped default assets moved out of html/ (#865): the default background/signature images now live in assets/img/certificate-defaults/ and the ficha/receipt templates in templates/documents/, both versioned/update-safe; only the legacy html/default_certificate_* glob remains there as a deprecated fallback.
  • Internal (#882) — the develop → testes deploy fails fast instead of hanging on a stalled SSH connection (BatchMode=yes, a timeout 300 around rsync, a 25-min job backstop). Deploy/CI-only.
  • Internal (#850, #849) — swept the ~50 hand-built <div class="notice …"> admin notices onto WordPress's canonical wp_admin_notice() (WP 6.4+) and extracted a shared AbstractDismissibleNotice base for the persistent-dismiss notices; bumped Requires at least 6.2 → 6.4 to use it. Behaviour-preserving.
  • Encryption Key Rotation now requires both decoupling constants (#857): the rotation card and its gate stay hidden until both FFC_ENCRYPTION_KEY and FFC_HASH_SALT are defined (rotating with only the key set would rebuild the search hashes under the shared salt and force a second rotation), and the Advanced panel surfaces a pending-record count linking to it.
  • Internal (#809, #822) — updated the vendored thumbmarkjs 1.9.1 → 1.10.1 (drop-in; the privacy logging:false beacon-disable is preserved), flag-guarded the user_id → wp_users foreign-key migration so it stops re-writing an all-skipped audit line on every activation, and pushed audience test coverage (tests only, no product change).
  • Docs (#822, #800, #802, #820, #858, #711) — recorded the reregistration accepted-orphan user-deletion decision and rewrote CLAUDE.md §4 with the "retain records, drop relationships" principle; and closed documentation-tab gaps for Modules, the Activity Log + its CSV export, Privacy & LGPD, Updates, the operator REST API and the Short URLs export cap. Docs only.

Deprecated

  • Legacy html/ certificate-layout glob (#865): the pre-pool fallback that discovered certificate layouts by globbing the plugin's html/ folder is deprecated in favour of the database template pool. On the project's 3-release cycle it is scheduled for removal at N+3, after which the (emptied) html/ folder is retired; sites relying on files dropped there should import them (Settings → Migrations) first.

Removed

  • Internal (#849) — removed the now-dead ffc-device-threshold-notice.js (the device-threshold notice uses the shared ffc-dismissible-notice.js after the AbstractDismissibleNotice extraction).
  • Internal (#809) — retired the pre-4.6.15 orphan-cron cleanup calls (inert on any install newer than 4.6.15); behaviour-preserving.
  • ⚠ (#809) — removed the pre-6.2.0 certificate-cap rename migration. Dormant on every install that passed through 6.2.0–6.17.0. Edge case: a site upgrading directly from pre-6.2.0 to 6.18.0 skips the rename — first update to any 6.2.0–6.17.0 release, then to 6.18.0.
  • ⚠ Breaking (#739) — CptEditorCompat shim removed. WordPress Editors (holders of edit_others_posts) no longer receive ffc_manage_forms / ffc_manage_calendars — completing the 6.16.0 CPT-capability decoupling (its two-release window has closed). Action: grant the FFC Administrator role — or the management capability — to the users who need it.

Security

  • ⚠ REST PII masking (#838): the GET /submissions[/{id}] and public POST /verify endpoints no longer leak unmasked PII — /verify now masks cpf/rf/rg/email like the /valid page, and /submissions returns plaintext only to the ffc_view_certificates_pii cap (audited). Per-key masking now lives in the shared DocumentFormatter::mask_field_value(). Breaking for external REST consumers on a view/operator role: grant ffc_view_certificates_pii to keep plaintext.
  • Public certificate CSV minimized (#836): dropped the magic-link Token column (a live access credential leaking in bulk) plus the User IP / Consent / Status columns; the admin export is unchanged and /valid now emits noindex. Public CSV column layout changed.
  • Encryption key rotation + FFC→FFC re-keying (#857, #863, #846, #840): a new Encryption Key Rotation migration (Settings → Migrations, gated by ffc_manage_settings_dangerzone) re-encrypts submissions + appointments PII under a strong FFC_ENCRYPTION_KEY and rebuilds the CPF/RF/email search hashes under FFC_HASH_SALT. Encryption::decrypt() gains a fallback chain (active → FFC-previous → WP-derived) so rows stay readable during rotation — define FFC_ENCRYPTION_KEY_PREVIOUS for a zero-downtime FFC→FFC re-key (finish one rotation before starting the next). A key/salt health surface (Encryption::key_health()ok/weak/placeholder/empty) flags predictable/placeholder secrets via a dismissible notice + an Advanced-tab panel (source, fingerprint, decoupled?), never disabling encryption.
  • Rate-limit + input-hardening batch (#839): the ffc_rate_limits counter table now hashes non-IP identifiers (email/CPF) at rest; the recruitment classifications CSV import validates extension + a 10 MB cap before reading; the no-JS [ffc_verification] fallback applies the same throttle as the AJAX path (closing an un-throttled auth-code probing hole); and the nginx deny rule for the CSV-export temp dir is documented.
  • User-deletion integrity — attribution + promoted-candidate coverage (#834): the deleted_user hook now also SET-NULLs the nullable actor-attribution columns (edited_by, approved_by/cancelled_by, created_by/updated_by, reviewed_by, …) and the promoted-candidate link, so a deleted admin/operator's id no longer dangles as stale attribution. The NOT NULL created_by columns stay accepted orphans on retained records; row-body encrypted PII is scrubbed only by the manual PrivacyErasers (LGPD erasure) path, by design.
  • Fixed two JS attribute-context escaping gaps (#837, #564): the admin submission-edit user search and the dashboard profile edit now encode "/' in data-* / value / style attributes, closing a stored-XSS via a crafted display_name (a privilege-boundary crossing) and a self-XSS. Bundles rebuilt.
  • Dev/CI dependency security bumps (#884): squizlabs/php_codesniffer 3.13.5 → 3.13.6 (CVE-2026-67434, OS command injection) plus the npm build-chain advisories (brace-expansion, js-yaml, postcss) via lockfile. Dev/build tooling only — not shipped in the plugin runtime.