=== EffortLess Multisite Form Creator ===
Contributors: domclic
Tags: form, contact form, gutenberg, ajax, multisite
Requires at least: 5.8
Tested up to: 7.1
Requires PHP: 7.4
Stable tag: 7.5.69
License: GPL-2.0-or-later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
Donate link: https://id7.dev/donate/

AJAX-powered Gutenberg contact forms. Display inline or as popups. Supports multisite, SMTP, captcha, file uploads, and CPT submission storage.

== Description ==

**EffortLess Multisite Form Creator** lets you build forms in the block editor, as a "Form" post whose content is your form's HTML. Each form is placed on a page with a shortcode or the ELMFC Form block, inline or as a popup. Submissions are sent by AJAX, emailed to the addresses you choose, and can optionally be stored as posts of a custom post type.

= Key Features =

* **Gutenberg-Ready** - Build forms directly in the block editor with HTML
* **AJAX Submission** - No page refresh needed
* **Popup or Inline** - Display forms as popups or embedded in your content
* **Fieldset Support** - Full support for fieldset and legend elements with default styling
* **Flexible Width Options** - Container width (default), narrow, default, or wide layouts
* **Email Notifications** - Send to the site admin and/or a chosen user; optionally a copy to the submitter
* **SMTP Support** - Optional SMTP configuration for reliable email delivery
* **Per-Form Customization** - Each form can have unique messages and settings
* **Theme Button Support** - Uses your theme's button styles by default (optional custom styling)
* **Fully Translatable** - All messages configurable per form (no hardcoded text)
* **Multisite Compatible** - Forms can be rendered and submitted across the sites of a multisite network
* **Responsive Design** - Mobile-friendly popups and forms with optimized spacing
* **Simple Math Captcha** - Optional spam protection; the question is loaded when the form is shown, so it keeps working on cached pages, and forms sent less than 3 seconds after loading are rejected
* **Enhanced Radio & Checkbox** - Card-style radio buttons and highlighted terms checkbox
* **Save Submissions** - Optional storage as custom post type with custom slug
* **Configurable Retention** - Optionally auto-delete stored submissions after a set number of days
* **Privacy Tools Integration** - Submissions are included in WordPress's built-in personal data export/erase tools
* **Page Builder Compatible** - Works with Elementor, Beaver Builder, Divi, and Gutenberg blocks
* **No Dependencies** - Uses native WordPress functions and jQuery

= Use Cases =

* Contact forms
* Support request forms
* Quote request forms
* Newsletter signup forms (with consent & storage)
* Subscription forms with plan selection
* Feedback forms
* Registration forms

= Form Width Options =

Choose how your inline forms should display:

* **Container Width (Default)** - Uses 100% of the parent container width
* **Narrow** - Maximum 450px width, centered
* **Default** - Maximum 720px width, centered  
* **Wide** - Maximum 1080px width, centered

= Email Configuration =

* Send to site admin email
* Send to specific WordPress user
* Send a copy to the form submitter (off by default; requires an email field)
* Optional SMTP configuration for better deliverability
* Customizable email subjects and messages per form

= Styling Features =

* Card-style radio button options with hover effects
* Highlighted terms and conditions checkbox container
* Proper fieldset and legend styling with nested support
* Consistent spacing throughout forms
* Custom or theme button styling options
* Visual width preview in admin settings

== Installation ==

1. Upload the plugin files to `/wp-content/plugins/effortless-multisite-form-creator/`
2. Activate the plugin through the 'Plugins' menu in WordPress
3. Go to Forms > Add New Form
4. Build your form using HTML in the Gutenberg editor
5. Configure settings in the sidebar meta boxes
6. Use shortcode `[elmfc id="123"]` or `[elmfc name="your-form-slug"]`
7. Add `popup="true"` to display as popup: `[elmfc id="123" popup="true"]`

== Frequently Asked Questions ==

= How do I create a form? =

1. Go to Forms > Add New Form
2. Add your form HTML in the editor (use fieldset, input, textarea, select, etc.)
3. Configure email settings, captcha, and appearance in the sidebar
4. Publish and copy the shortcode

= Who can create and edit forms? =

Administrators (users with the `manage_options` capability; on multisite, each site's administrators and the super admins). A form's settings decide where its submissions go (recipients, SMTP server, webhook URL), so Editors, Authors and Contributors cannot create or edit forms. They can still place a published form on a page with the shortcode or the ELMFC Form block. Developers can grant another capability with the `elmfc_manage_forms_capability` filter.

On multisite, a site administrator who is not a super admin may save form fields (`<input>`, `<select>`, `<textarea>`, `<button>`) in a form; WordPress still removes `<script>`, `<style>` and event attributes for them.

= Where can the webhook send submissions? =

To a public `http` or `https` URL. Addresses on the local or private network (`localhost`, `127.0.0.1`, `10.x`, `192.168.x`, and so on) are refused by WordPress's safe HTTP API and nothing is sent.

= How do I add fieldsets? =

Use standard HTML fieldset and legend tags:
```html
<fieldset>
    <legend>Section Title</legend>
    <label>Field Name</label>
    <input type="text" name="field_name" required>
</fieldset>
```

= How do I write my own label/text around the captcha? =

The "Enable simple captcha" checkbox auto-inserts a boxed question with fixed
label text. If you'd rather write that text yourself — and have it translate
like the rest of your form content instead of through a separate settings
field — place the `[elmfc_captcha]` shortcode directly in the form's HTML
instead. It renders only the question (e.g. "5 + 6 = ?"), the answer input,
and a hidden verification token; everything around it is plain HTML you
control:

```html
<label>Security check: [elmfc_captcha]</label>
```

The shortcode already includes its own answer input and hidden token, so
nothing else is needed — just wrap it in whatever label/wording you want.
When `[elmfc_captcha]` is present in the content, the auto-inserted box is
skipped automatically so you never get two captchas on the same form.

= How do I build a form with a Custom HTML block? =

1. Go to Forms > Add New Form and give the form a title (it is shown above the form).
2. Add a Custom HTML block and write the fields. Every field needs a `name` attribute; that name is the label used in the email and in stored submissions.
3. Add a submit button: `<button type="submit">Send</button>`.
4. Optionally put `[elmfc_captcha]` where the captcha should appear; a form containing the tag always shows and checks the captcha, even with "Enable simple captcha" unticked. Without the tag, and with "Enable simple captcha" ticked, the captcha is placed just before the submit button.
5. Publish, then add the form to a page with `[elmfc id="123"]` or the ELMFC Form block.

Example:

```html
<p><label>Name <input type="text" name="name" required></label></p>
<p><label>Email <input type="email" name="email" required></label></p>
<p><label>Message <textarea name="message" rows="5" required></textarea></label></p>
<p><label>Security check: [elmfc_captcha]</label></p>
<button type="submit">Send</button>
```

Line breaks typed in a textarea are kept in the email and the stored submission.

= Can I put my own CSS and JavaScript in a form? =

Yes. In the same Custom HTML block, add a `<style>` element and a `<script>` element. This works when the form's author has the `unfiltered_html` capability: administrators on a single-site install, and super admins on multisite. For authors without that capability, `<script>` and `<style>` are removed from the form when it is displayed.

= How do I style the form myself, without fighting the plugin's default CSS? =

Go to Forms > Settings and enable "Disable the plugin's built-in CSS". This
stops `assets/css/elmfc.css` from loading site-wide, so every form (fields,
buttons, the popup, the CAPTCHA) falls back to your theme's default
appearance and you write 100% of the styling yourself. The plugin's
JavaScript — AJAX submission, popup open/close, validation, multi-step
navigation — keeps working normally either way; only the visual styling is
affected.

= How do I display the form as a popup? =

Add `popup="true"` to your shortcode:
`[elmfc id="123" popup="true"]`

= How do I make the form full width? =

By default, forms use their container's full width. To explicitly control width:

1. Edit your form
2. Go to Appearance Settings sidebar
3. Select "Container" for full width, or choose Narrow/Default/Wide for specific max-widths

= Can I change the button texts and messages of one form? =

Yes. Add hidden fields to the form's HTML; each one overrides the default text for that form only:

* `elmfc_submit_text` - not needed for a `<button>` written in the form: it keeps its own text and icons, which are put back after sending
* `elmfc_sent_text` - button text after a successful submission
* `elmfc_sending_message` - button text while the form is being sent
* `elmfc_success_message` - message shown after a successful submission
* `elmfc_error_message` - message shown when the email could not be sent
* `elmfc_validation_message` - message shown when a required field is empty
* `elmfc_captcha_error` - message shown after a wrong captcha answer
* `elmfc_captcha_question` - label of the automatically inserted captcha

```html
<input type="hidden" name="elmfc_submit_text" value="Send Message">
<input type="hidden" name="elmfc_success_message" value="Thanks for contacting us!">
```

The notification email's subject and introduction (`_elmfc_admin_subject`, `_elmfc_admin_message`, `_elmfc_user_subject`, `_elmfc_user_message`) cannot be set by hidden fields, so a visitor cannot change them. Set them through Export / Import Settings on the form. The redirect after submission is set in the Appearance Settings box.

= Which CSS classes give fields the built-in styling? =

* `radio-label` on a `<label>` wrapping a radio button: card-style option.
* `checkbox-label` on a `<label>` wrapping a checkbox, inside a `<div class="terms-container">`: highlighted terms checkbox.
* `elmfc-buttons` on the `<div>` around the buttons; `elmfc-btn` on a button uses the plugin's button style when "custom buttons" is enabled in Appearance Settings, otherwise the theme's button style.
* `elmfc-cancel` on a `type="button"` button: resets the form.
* `<span class="required">*</span>` in a label: required-field marker.

```html
<label class="radio-label"><input type="radio" name="source" value="search" required> <span>Search engine</span></label>
<div class="terms-container">
    <label class="checkbox-label"><input type="checkbox" name="terms" required> <span>I agree to the terms <span class="required">*</span></span></label>
</div>
<div class="elmfc-buttons">
    <button type="submit" class="elmfc-btn">Send Message</button>
    <button type="button" class="elmfc-btn elmfc-cancel">Reset</button>
</div>
```

= How do I accept file uploads? =

1. Edit the form and tick "Allow file uploads" in its settings. Set "Max size (MB)" (per file, 1 to 50, default 5) and "Allowed extensions" (comma-separated, default `jpg,jpeg,png,gif,pdf,doc,docx,xls,xlsx,zip`).
2. Add a file field to the form: `<input type="file" name="attachment">`, or `<input type="file" name="attachments[]" multiple>` for several files.

Accepted files are attached to the admin email and deleted from the server afterwards; the email and the stored submission list their names and sizes. A file that is too large or has another extension is left out. Executable extensions (such as `php`, `exe`, `sh`) are always refused.

= How do I enable SMTP? =

1. Edit your form
2. In Email Settings sidebar, check "Use SMTP"
3. Enter your SMTP host, port, encryption, username, and password
4. Save the form

= Does it work with page builders? =

Yes! The plugin includes CSS overrides for:
* Elementor
* Beaver Builder
* Divi
* Gutenberg blocks
* Generic row/column layouts

= How do I save form submissions? =

1. Edit your form
2. In Storage Settings sidebar, check "Save submissions to CPT"
3. Enter a CPT slug (e.g., "newsletter", "contact-requests")
4. Submissions will appear as a submenu under Forms

= How is stored submission data handled for privacy (GDPR)? =

If you enable "Save submissions to CPT" for a form, submitted data (name, email, message, and any other field) is stored in your site's database indefinitely by default. You can set "Auto-delete submissions after (days)" in Storage Settings to have the plugin permanently delete submissions older than that automatically (checked once daily); leave it at 0 to keep them forever.

Stored submissions are also included in WordPress's built-in personal data tools (Tools → Export Personal Data / Erase Personal Data), so you can fulfil a visitor's data request without manually searching the database.

= How are forms translated on a multisite network? =

With the EffortLess Multisite Auto Translate plugin active, a form saved on the network's source site is copied and translated to the other sites like a post:

* The text in the form's Custom HTML blocks is translated, and nothing else changes. With EffortLess Multisite Auto Translate 3.3.8 or later, only the visible text and the `placeholder`, `title`, `alt`, `aria-label` and button-label attributes are sent for translation; field names, IDs, classes, types, inline styles and hidden values stay exactly as on the source. The form fields are kept when the translated copy is saved.
* CSS and JavaScript written in the form's Custom HTML block (`<style>`, `<script>`) are kept on every site: the translated copy is stored with the source form's code, in the same place. This requires the source form's author to be allowed unfiltered HTML (a super admin on multisite). Edit the code on the source site; a translated copy should not be edited by a user without that capability, because WordPress would then remove its code on save (the form keeps running the source form's code).
* The form keeps the same slug on every site, so `[elmfc name="..."]` works everywhere. On a page that uses the ELMFC Form block, the block is pointed at the translated form of that site.
* Per-form texts that you set (button labels, messages, email subjects) are translated; the plugin's default texts follow each site's language.
* Form statistics are not copied.

If a site has no translated copy of a form yet, `[elmfc name="..."]` shows the source site's form until the translation exists.

= Can I translate form messages? =

Yes. Each form can override its messages with hidden fields (see "Can I change the button texts and messages of one form?" above).

The plugin's own texts (submit and sending labels, success and error messages, the captcha label and placeholder) follow the site language. The plugin ships translations for German, Spanish, French, Italian, Japanese, Korean, Brazilian Portuguese and Traditional Chinese; they are used until a language pack for that language is available from translate.wordpress.org, which then takes priority. Text you write yourself in the form content, such as field labels or a submit button in a Custom HTML block, is not translated automatically: write it in the site's language.

== External services ==

This plugin can optionally connect to a third-party service to reduce spam. It is off by default and only activates when Akismet is installed and configured.

**Akismet**

If the Akismet plugin is installed, active, and configured with an API key on your site, this plugin sends each form submission's author name, email, message text, IP address, user agent, and referrer to Akismet's comment-check endpoint (`https://REST.akismet.com/1.1/comment-check`) to determine whether the submission is spam. This only happens if Akismet is already active on your site; if it is not, no data is sent to Akismet.

This service is provided by Automattic: [Terms of Service](https://akismet.com/tos/), [Privacy Policy](https://automattic.com/privacy/).

== Screenshots ==

1. Form builder in Gutenberg editor with fieldset support
2. Popup form
3. Admin settings - Email configuration with SMTP
4. Admin settings - Width options with visual preview
5. Admin settings - Security and appearance options
6. Mobile-responsive form with card-style radio buttons
7. Form with fieldsets and legend styling
8. Submissions stored as custom post type

== Changelog ==

= 7.5.69 =
* Change: a form translated by EffortLess Multisite Auto Translate is rebuilt from its source form (source markup and code, translated text) and saved with `wp_update_post()` on behalf of the source form's author, instead of being written directly to the database. WordPress applies the same rules as when that person saves the form: the copy is saved only if they may publish unfiltered HTML on the destination site.
* Change: showing a translated form never writes to the database any more. A copy saved before the rebuild existed is shown rebuilt from its source, and is saved at its next translation.

= 7.5.68 =
* Change: Akismet receives the form's real fields. It previously only read fields named `name`, `email` and `message`, so forms with other field names were checked with empty content. The email is the form's configured email field (or the first email address), the author the first field whose name contains "name", and the content every other text field.
* Change: when a submission is stored but its email cannot be sent, the visitor sees the success message instead of an error, so they do not send it again and create a duplicate. The stored submission shows "Failed" and can be resent from Forms > All Submissions.
* Fix: a form placed outside the page content (a template, template part, widget or page builder) now always loads its script and styles. With a block theme such as Twenty Twenty-Five, the styles are in the page head, so the form no longer appears unstyled first.
* Fix: fields written as `disabled` in the form stay disabled after an error, a reset or a show-if rule.
* Fix: the same form shown twice on a page no longer repeats the captcha field's HTML id.
* Fix: the search on a form's submissions screen is kept when moving to another page of results or sorting a column.
* Fix: the "Settings imported" message is shown in the block editor.
* Change: the redirect after submission may point to another domain, for example another site of the network with its own domain. It is read from the form's settings, never from the visitor's request, and must be an http or https address.

= 7.5.67 =
* Fix: a submit button written in the form kept its own text only until the first send. While sending, after an error (for example a wrong captcha answer) and when a popup was closed, it was replaced by the plugin's text ("Submit"), losing any icon or arrow inside it. The button's own markup is now put back.
* Fix: after a wrong captcha answer, the cursor is now placed in the captcha field (it was still disabled when the form tried to focus it).
* Fix: settings import from the "Export / Import Settings" box did nothing in the block editor, because the box's form sat inside the editor's own form. The file is now sent in the background and the editor reloads with the imported settings. The upload settings (allow uploads, maximum size, allowed extensions) are now imported too.
* Fix: fields hidden by a `data-show-if-field` rule were sent with the form (a hidden `<select>` always has a value), and became active again after an error. Disabled fields are no longer sent, and the rules are applied again after an error.
* Security: the submission identifier shown in the admin and the CSV export was the raw value of the email field. It is now used only when it is a valid email address, and the CSV export protects that column against spreadsheet formulas like the other columns.
* Fix: backslashes typed by a visitor were removed from stored submissions.
* Fix: the personal data eraser (Tools > Erase Personal Data) skipped part of the submissions of a person who had more than 50. The personal data export now lists checkbox choices instead of their stored form.
* Fix: bulk "Delete" on the submissions screen acts only on submissions of the form shown.
* Fix: on uninstall, the captcha entries of a single-site install and the folder used for email attachments are removed too.
* Change: the plain-text part of notification emails ("Form", "Submitted from", "Submitted on") follows the site language.
* Change: the page no longer contains the form's email subjects and introductions as hidden fields; they are read from the form's settings when the email is sent. A hidden field written in the form itself (for example `elmfc_sending_message`) now also overrides the texts shown by the form's script.
* Fix: the note shown in the Email Settings box when a site has more than 500 users now points to the CC field.

= 7.5.66 =
* Added: file fields with `multiple` send all the selected files. Previously only one file reached the server, or none when the field name ended with `[]`, without any message.
* Fix: with "Disable the plugin's built-in CSS" enabled, the hidden spam-trap field ("Leave this field empty") was visible in every form. It is now hidden by its own markup. Password managers are also told not to fill it, which would have made a real submission look like spam.
* Fix: a button with the `elmfc-cancel` class resets the form in both button modes. It previously only worked with "custom buttons" enabled.
* Change: a translated form reads its source form once per page view instead of twice.

= 7.5.65 =
* Change: creating, editing and deleting forms requires the `manage_options` capability (administrators) instead of the capabilities used for posts. A form's settings decide where submissions are sent, including the webhook URL and SMTP server, which Authors could previously set. Editors, Authors and Contributors can still place published forms on pages. Developers can change the capability with the `elmfc_manage_forms_capability` filter.
* Change: the webhook is sent with `wp_safe_remote_post()`, which refuses local and private network addresses.
* Change: the form's content is rendered with the block, paragraph and shortcode functions only, instead of the full `the_content` filter, so content that other plugins add to posts (share buttons, related posts, ads) no longer appears inside forms.
* Fix: on multisite, a site administrator who is not a super admin lost every form field when saving a form in the block editor or duplicating it. Form fields are now kept for users who manage forms; `<script>`, `<style>` and event attributes are still removed for them.
* Fix: Duplicate and Copy to site copied the original form's statistics, edit lock and EffortLess Multisite Auto Translate links. A duplicated translated form was then taken for a second translation of the source form. These are no longer copied. Backslashes in the form's content and settings (for example in its JavaScript) are also kept.
* Fix: after a wrong captcha answer, the replacement captcha had no minimum fill time, so a script could send one wrong answer and then the right one at once. The replacement now keeps the time of the first captcha.
* Change: a form's refresh request (security token, captcha and view count when the form is shown) is limited to 60 per form and IP address in 10 minutes. Developers can change it with the `elmfc_refresh_limit` filter.

= 7.5.64 =
* Fix: translated forms whose copy was saved before 7.5.63 kept missing their `<style>`/`<script>`, because EffortLess Multisite Auto Translate only reports a full translation, not a meta-only update of an unchanged form. A translated form whose stored copy differs from its source (apart from the translated text) is now rebuilt from the source once, the next time it is shown, under the same conditions as the rebuild after a translation.

= 7.5.63 =
* Change: a form translated by EffortLess Multisite Auto Translate is now stored identical to its source form except for the translated text, including the `<style>` and `<script>` of its Custom HTML block. Translations are saved in the background, where WordPress's content filter cannot keep that code intact, so right after the save the copy is rebuilt from the source markup with the translated text, the translated `placeholder`, `title`, `alt`, `aria-label` and button labels. Requires the source form's author to be allowed unfiltered HTML. If the copy does not line up piece by piece with the source, it is left as saved and the form still runs the source form's code.
* Change: when a translated form is shown, code identical to its source form's stays where it is in the form; any other `<style>`/`<script>` is removed whole, so code is never printed as text.

= 7.5.62 =
* Fix: opening a popup made the page shift sideways, because hiding the page's overflow removed its scrollbar. On a page that has a scrollbar, the scrollbar now stays visible and the page is held in place while the popup is open, then returned to the same scroll position when it closes. Pages without a scrollbar are unchanged.

= 7.5.61 =
* Change: the popup opens faster and more smoothly. The blur applied to the whole page behind an open popup is removed (it was repainted on every animation frame, which made the popup appear slowly and stutter on phones and image-heavy pages); the dark overlay is unchanged. The open animation is shorter (0.2 s instead of 0.3 s).

= 7.5.60 =
* Change: the Forms list shows only each form's title. In "Extended view" it also printed an excerpt of the form's content (its CSS code and field texts); forms now have no excerpt in the admin, and the Compact/Extended view switch is no longer offered for forms.

= 7.5.59 =
* Change: the Shortcode box and the Slug column of the forms list show a form's non-Latin slug as written (for example `[elmfc name="預約品嚐體驗"]`) instead of WordPress's percent-encoded storage form (`%e9%a0%90...`). Both forms work in the shortcode.

= 7.5.58 =
* Docs: translated forms keep the source form's markup exactly when EffortLess Multisite Auto Translate 3.3.8 or later is used, which translates a form's Custom HTML block text-only. No code change in this plugin; the FAQ explains what is translated.

= 7.5.56 =
* Fix: forms translated by EffortLess Multisite Auto Translate lost their fields. Translations are saved in the background, where WordPress's content filter removes `<input>`, `<select>`, `<textarea>` and `<button>`. While a form translation is being saved, these form tags are now added to the filter's allowed tags (`<script>`, `<style>` and event attributes are still removed).
* Fix: CSS and JavaScript written in a form's Custom HTML block were missing on translated sites, for the same reason. A translated form now runs the `<style>` and `<script>` of its source form, provided the source form's author may publish unfiltered HTML. Keep the code in the native Custom HTML block on the source site.
* Change: a form's Custom HTML block is translated even when it contains `<style>` or `<script>` (reverting the 7.5.55 behaviour): the translated copy's code is not used, so its text can be translated safely.

= 7.5.55 =
* Fix: forms were no longer translated by EffortLess Multisite Auto Translate. That plugin renamed its hooks and settings from `ms_gpt_*` to `elmat_*`, so none of this plugin's translation hooks ran any more: forms were not selected for translation, their HTML was not translated, slugs were not kept and the form block was not pointed at the translated form. The hooks are now registered under both names, and the source site is read from the new setting (with the old one as fallback).
* Change: a form's Custom HTML block that contains `<script>` or `<style>` is copied to other sites without automatic translation, so its code is not altered. Blocks without code are translated as before.

= 7.5.54 =
* Fix: the browser's own validation messages ("Please fill out this field.") follow the browser's language, so they stayed in English on a site in another language. Forms now show the plugin's translated messages for empty required fields, unticked required checkboxes, unselected options and invalid email, web address and number values. A field's own `data-error` message is used when set.

= 7.5.53 =
* Removed: the `bin/smoke-test.php` developer test script is no longer included in the plugin. It was never used by the plugin itself.

= 7.5.52 =
* Fix: searching the submissions list did nothing; the search term was read from the wrong place. Search now also looks inside every submitted field, not only the identifier (email).
* Fix: answers from a group of checkboxes showed as encoded text (`a:2:{…}`) on the submission detail page and in a resent email.
* Fix: "Resend email" added two internal rows ("Elmfc Form", "Elmfc Url") to the email, and sent the copy to the visitor again. It now sends the notifications only, without those rows.
* Fix: line breaks in a submitted message are shown on the submission detail page.
* Fix: the submissions conversion column always showed 0 submissions; the submission counter was never increased.
* Fix: forms in the trash, and draft, pending or scheduled forms, were shown to visitors when a page used their shortcode or block. Visitors now see published forms (and private forms when logged in); drafts are visible only to users who can edit them, for previewing.
* Change: viewing, searching, exporting, resending and deleting stored submissions now requires the `manage_options` capability (administrators) instead of `edit_posts`, because submissions contain visitors' personal data. Developers can change it with the `elmfc_submissions_capability` filter.
* Added: new submissions are marked "New" until opened, the All Submissions menu badge counts new submissions, and the list has "Mark as read" / "Mark as new" bulk actions. Submissions stored before this version are not counted as new.
* Change: CSV export reads submissions 100 at a time, so large exports use less memory.
* Change: the ELMFC Form block is registered from a `block.json` file (block API version 3). Requires WordPress 5.8 or later.
* Change: after a form's emails are sent through its own SMTP server, the shared mailer's keep-alive and timeout settings are restored, so later emails in the same request are not affected.
* Change: the delete confirmations no longer use inline `onclick` attributes, and the admin pages use even side margins.
* Removed: the `[elmfc_submissions]` shortcode, which showed stored submissions on the website. Pages that still contain it display nothing in its place; submissions are under Forms > All Submissions.
* Readme: the "Additional Information" section was moved into the FAQ and corrected (email subjects cannot be set with hidden fields; there is no `popup_width` setting).

= 7.5.51 =
* Fix: the bundled translations were never used, so the plugin's texts (submit and sending labels, success message, captcha label and placeholder) stayed in English on a site set to another language. The bundled translation for the site language is now loaded when no translate.wordpress.org language pack is installed for it; an installed language pack still takes priority.

= 7.5.50 =
* Fix: the ELMFC Form block's labels in the editor ("ELMFC Form", "Select a form", "Open as popup", and so on) used the wrong text domain (`elmfc`), so they could never be translated. They now use the plugin's text domain and are included in the translation template and the bundled translations.
* Added: bundled editor translations for the block in German, Spanish, French, Italian, Japanese, Korean, Brazilian Portuguese and Traditional Chinese, loaded with `wp_set_script_translations()`.

= 7.5.49 =
* Translations: added the "Please take a moment to check your answers, then send the form again." message (shown when a form is sent too quickly) to the translation template and to the bundled German, Spanish, French, Italian, Japanese, Korean, Brazilian Portuguese and Traditional Chinese translations.

= 7.5.48 =
* Fix: after a successful submission the success message could stay hidden. On forms whose submit button sits inside a paragraph or `<div>` (common in a Custom HTML block), hiding the form also hid the message placed next to the button. The message is now moved out of the form fields before they are hidden.

= 7.5.47 =
* Fix: the captcha stayed as "… = ?" when the form contained the `[elmfc_captcha]` tag but "Enable simple captcha" was unticked. A form containing the tag now always shows and checks the captcha. Before 7.5.46 such a form showed a question but did not check the answer.
* Fix: the captcha stayed as "… = ?" on forms that were still drafts. The question is now loaded for every form the shortcode can display.
* Change: when the captcha cannot be loaded, the reason is written to the browser console.

= 7.5.46 =
* Change: the captcha question and the form's security token are now loaded when the form is shown, instead of being written into the page. Forms keep working on pages served from a full-page cache, where they previously failed with "Incorrect captcha" or "Security check failed" after an hour or a day. Rendering a page no longer stores a captcha in the database.
* Change: form views ("impressions") are counted when a browser shows the form, not on every page render, so crawlers and cache warm-ups are no longer counted.
* Added: a form sent less than 3 seconds after its captcha was loaded is rejected with a message asking the visitor to check the answers and send again.
* Change: "Send copy to user" is now off by default for new forms. Anyone can type another person's address into a form, so the copy is only sent when you enable it. Forms that already have the setting saved are unchanged.
* Change: when a form uses its own SMTP server, the admin email and the copies of one submission share one connection, and an unresponsive server is abandoned after 10 seconds instead of 5 minutes.
* Change: SMTP passwords and webhook secrets are encrypted with a key generated for the plugin, instead of one derived from WordPress's security keys, so changing those keys no longer makes the stored passwords unreadable. Existing values are re-encrypted the first time an administrator opens the dashboard of each site.
* Change: the REST route (`/wp-json/elmfc/v1/submit`) now uses the same code as the AJAX submission, so it also runs the Akismet check and accepts file uploads.
* Change: the `[elmfc_submissions]` shortcode is now shown to administrators only (`manage_options`); it previously showed stored submissions to any user who could edit posts, including Contributors.
* Change: stored submissions are no longer registered in the REST API.

= 7.5.45 =
* Fix: when a form has no `[elmfc_captcha]` tag, the captcha is now placed just before the submit button also for a plain `<button type="submit">` or `<input type="submit">` written in a Custom HTML block. Previously it was only placed correctly when the button sat in a Buttons block or an `elmfc-buttons` wrapper, and otherwise ended up after the button.
* Fix: line breaks in multi-line fields (for example a message textarea) are kept in the notification email, the stored submission and the webhook payload. They were previously merged into a single line.
* Fix: the form's CC and BCC addresses are only added to the admin notification, no longer to the copy sent to the person who filled in the form.
* Change: submission storage types are registered from a cached list, removing one database query per storing form on every page load.

= 7.5.44 =
* Fix: the popup overlay's background blur is now applied only while a popup is open. It was always present on the page, which could make scrolling sluggish on some browsers.
* Fix: pages that use the form block now load the plugin's stylesheet in the page head, so the form no longer appears unstyled and then shifts when the page loads.

= 7.5.43 =
* Change: after a successful submission the form fields are hidden and only the success message is shown, in a larger green box with a check mark, and scrolled into view.

= 7.5.42 =
* Added: a Custom HTML block inside a form can now contain its own `<style>` and `<script>` code, when the form's author has the `unfiltered_html` capability (administrators on single-site installs, super admins on multisite). For other authors the previous filtering still applies.

= 7.5.41 =
* Dev: bin/smoke-test.php now escapes its console output, reads the .htaccess file through WP_Filesystem, uses strict comparison, and no longer reuses the names of WordPress globals ($id, $path) for its cleanup loop variables.

= 7.5.40 =
* Added bundled translation files: a template (.pot) and translations for Spanish, Traditional Chinese, Korean, French, Japanese, German, Italian and Brazilian Portuguese.

= 7.5.39 - 2026-10-10 =
* Change: the form submission REST route no longer uses the generic `__return_true` permission callback. A named permission function documents why the route is public and where spam protection (honeypot, math captcha, validation) is applied. No change in behaviour.

= 7.5.38 - 2026-09-12 =
* Fix: the form's outer container (popup and inline display modes alike) had no explicit text color of its own, so fieldset labels, radio options, and the terms-and-conditions checkbox text silently inherited the active theme's body text color — on a theme with light/white body text, they could render nearly invisible against the form's own light backgrounds. The container now sets its own explicit text color instead of relying on the theme.

= 7.5.37 - 2026-09-05 =
* Change: the bundled donate-notice class is renamed from the shared `Effortless_Donate_Notice` to `ELMFC_Donate_Notice`, so the class name carries this plugin's own prefix and cannot collide with another EffortLess plugin loading its own copy.
* Fix: the PHPCS configuration file is no longer included in the released plugin. It is a development-only file and did not belong in a distributed build.

= 7.5.36 - 2026-09-05 =
* Change: tested against WordPress 7.1 and the "Tested up to" header updated to 7.1. No functional changes.
* Change: the donate notice's outbound link no longer includes the site address as a query parameter. It still identifies the plugin and version so support requests can be matched to a release, but not which installation was viewing it.

= 7.5.35 - 2026-08-18 =
* Added: Forms > Settings page with a "Disable the plugin's built-in CSS" option. When enabled, forms render with no plugin styling at all — fields, buttons, the popup, and the CAPTCHA fall back to the active theme's default appearance, for site owners who want to write their own CSS from scratch. Applies site-wide; the plugin's JavaScript (AJAX submission, popup, validation, multi-step navigation) keeps working either way.

= 7.5.34 - 2026-08-18 =
* Fix: form field labels could still inherit an underline from the active theme's default label styling. The previous fix only covered the legacy auto-inserted CAPTCHA box's own label; the general `.elmfc-form label` rule (which also covers any label an author writes by hand around the new `[elmfc_captcha]` shortcode) never reset text-decoration. Now set to none, with !important to reliably win against theme styles.

= 7.5.33 - 2026-08-18 =
* Added: `[elmfc_captcha]` shortcode — a bare arithmetic CAPTCHA (question, answer input, and verification token only, no label or box) that can be placed directly in a form's HTML. Lets you write the surrounding label/instructions as ordinary form text — so it translates through the same pipeline as the rest of the form instead of a separate settings field. When present, the auto-inserted CAPTCHA box from "Enable simple captcha" is skipped automatically.

= 7.5.32 - 2026-08-18 =
* Fix: the CAPTCHA question label and answer placeholder were rendered from the plugin's hardcoded default text instead of the per-form (and, on translated sites, GPT-translated) value stored in postmeta, so a translated form's CAPTCHA always displayed in the source site's default language.
* Fix: "send copy to user" and the submitter Reply-To header were matched by the admin-configured field name only. On a translated copy of a form, if translation ever alters the underlying HTML field name, the match would silently fail with no submitter email sent. Now falls back to the first submitted value that is itself a valid email address.
* Fix: the CAPTCHA label could pick up an underline from the active theme's default label styling, since the plugin's own CSS never set text-decoration on it. Explicitly set to none.

= 7.5.31 - 2026-08-18 =
* Fix: forms are registered as a non-public post type, which excluded them from the EffortLess Multisite Auto Translate plugin's default translatable post types and silently prevented any form text from being translated to destination sites. Forms now register themselves for translation via the `ms_gpt_translatable_post_types` filter.

= 7.5.30 - 2026-07-11 =
* Fix: corrected plugin name casing to EffortLess (was Effortless) in the plugin header, readme, and privacy exporter/eraser labels.

= 7.5.29 - 2026-07-10 =
* Added: Dismissible "Support the development" donate notice on the form and submissions admin screens, plus the standard Donate link plugin header.

= 7.5.28 - 2026-07-04 =
* Dev: Added bin/smoke-test.php, a WP-CLI script (`wp eval-file bin/smoke-test.php`) that exercises file-upload handling, the privacy exporter/eraser, and the daily cleanup cron directly against a live install, cleaning up all test data it creates. Not part of the plugin's runtime; excluded from PHPCS scanning in phpcs.xml since it's CLI-only output, not web-facing.

= 7.5.27 - 2026-07-04 =
* Added: GDPR/Privacy Tools integration — form submissions are now included in WordPress's built-in Export Personal Data / Erase Personal Data tools (Tools → Export/Erase Personal Data), matched by the requester's email address across every submission CPT. Suggested privacy policy language is also registered via wp_add_privacy_policy_content().
* Added: Configurable submission retention — a new "Auto-delete submissions after (days)" field in Storage Settings, enforced by a new daily cron job. Leave at 0 (default) to keep submissions forever, matching prior behavior.
* Security: Form file attachments are now uploaded into a dedicated `wp-content/uploads/elmfc-attachments/` directory with a deny-all `.htaccess`/`index.php`, instead of the standard public year/month uploads folder, since they may contain sensitive user-submitted documents. Cleanup of these files (and the multisite blog-switch restore) is now guaranteed via try/finally even if a hook throws mid-request.
* Security: Rate-limit rows written directly to wp_options (for atomicity) are now purged once expired by the new daily cron, instead of accumulating indefinitely.
* Security: Email subject lines are now run through sanitize_text_field() before being handed to wp_mail(), as defense-in-depth against control-character/header injection regardless of how the underlying setting was populated.
* Fix: `wp_die( 'Unauthorized' )` in the CSV export handler now uses `esc_html__()` like every other message in the plugin.
* Fix: `uninstall.php` now also removes the `elmfc_recaptcha_captcha_fixed` migration-tracking option and the daily cleanup cron event.
* Cleanup: The shortcode's KSES allowlist is now memoized (built once per request) instead of being rebuilt on every form render.
* Docs: Clarified the security trade-offs of the `ELMFC_TRUST_PROXY` constant in its doc comment.

= 7.5.26 - 2026-07-03 =
* Security: The submissions admin page now validates the `cpt` query parameter against the plugin's own list of managed submission post types before rendering or listing anything, closing a gap that let any user with the broad `edit_posts` capability browse/export posts of an arbitrary post type through this screen.
* Security: Deleting a submission (single or bulk) now also checks `current_user_can( 'delete_post', $id )` for that specific post, not just the broad `edit_posts` capability.
* Fix: The honeypot field label now uses `esc_html__()` instead of an unescaped `__()` call.
* Fix: The one-time "my_form" → "elmfc_form" CPT migration now clears the WordPress post cache for every renamed post, so sites using a persistent object cache (Redis/Memcached) don't keep serving stale cached posts under the old post type after the rename.

== Upgrade Notice ==

= 7.5.14 =
Security release: rate limiter fixed for Redis/Memcached setups, captcha cross-form replay patched, nonce scoped to form_id, email subject injection closed, form_title/URL CPT storage fixed. Strongly recommended.

= 7.5.13 =
Code quality release: shared helpers for reCAPTCHA and rate-limit config, webhook skip-list fix, elmfc_default_texts() memoization, and documentation corrections.

= 7.5.12 =
Security: reCAPTCHA bypass patched — omitting the token no longer skips verification. Rate-limit DB-error bypass fixed. Cross-site redirect URL fixed. Recommended update.

= 7.5.11 =
Security: redirect URL no longer sent as a hidden form field — read server-side from post meta instead.

= 7.5.10 =
Security hardening: REST endpoint now enforces captcha, secrets are encrypted at rest, capability checks tightened. Recommended update.

= 7.5.9 =
Form field labels and HTML content now fully translated. Requires effortless-multisite-auto-translate 3.2.31+.

= 7.5.8 =
Fixes multisite auto-translate integration: forms now translate correctly and the Gutenberg block remaps form IDs across sites.

= 7.5.7 =
Code quality: resolves all remaining PHPCS warnings. No functional changes.

= 7.4.9 =
Adds file upload support, a native Gutenberg block, and fixes required checkbox validation. Cancel button now fully resets the form after submission.

= 7.4.8 =
Security: SMTP passwords are now encrypted at rest. HTML emails, submission detail view, REST API, and settings export/import added.

= 7.4.7 =
Security: proxy IP headers now opt-in. Adds test email button and email delivery log in Submissions table.

= 7.4.6 =
Rate limiter fixes: real IP detection, per-form counters, runs after captcha. Per-form limit now configurable in Security Settings.

= 7.4.5 =
Bug fixes: shortcode copy buttons now work, submission counts batched correctly, rate limiting added (5/IP/10min).

= 7.4.4 =
Bug fixes: redirect URL / CC / BCC no longer appear in emails, duplicate form resets CPT storage, elmfc_before_send cancellation works correctly.

= 7.4.3 =
Adds redirect-after-submit, duplicate form, CC/BCC, developer filters, and form status columns on the list.

= 7.4.2 =
Performance: CSS/JS now loads only on pages with a form shortcode. Adds shortcode sidebar, submission badge, honeypot spam protection, and clean uninstall.

= 7.4.0 =
New dedicated Submissions admin page with full field listing, sortable table, bulk delete, and CSV export.

= 7.3.8 =
Admin refactor: consolidated asset loading, extracted JS/CSS to files, form styles now visible in Gutenberg editor.

= 7.3.7 =
Forms list now shows clickable copy buttons for ID and Slug columns — one click copies the ready-to-paste shortcode.

= 7.3.6 =
Multisite: [elmfc name="slug"] now resolves forms from the main site automatically when copied to destination sites via another plugin.

= 7.3.5 =
Security fix: captcha solution is no longer exposed in page source. Update recommended for all sites using captcha.

= 7.3.3 =
Major update with full container width support, fieldset styling, improved spacing, and enhanced admin interface. Forms now adapt to their container by default with optional width controls.

= 7.3.1 =
CPT submissions now properly organized as submenu under Forms with descriptive titles.

= 7.2.4 =
Full email system with SMTP support. Update recommended for better email deliverability.
