EffortLess Multisite Form Creator

Description

EffortLess Multisite Form Creator is a lightweight, powerful WordPress plugin that lets you create unlimited contact forms using the Gutenberg block editor. Each form can be displayed inline or as a popup modal with smooth animations.

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 beautiful styling
  • Flexible Width Options – Container width (default), narrow, default, or wide layouts
  • Dual Email System – Send to admin and user copy automatically
  • 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 – Works seamlessly on WordPress multisite networks
  • Responsive Design – Mobile-friendly popups and forms with optimized spacing
  • Simple Math Captcha – Optional spam protection with automatic renewal
  • 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
  • Any custom form you need!

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 copy to form submitter (if email field exists)
  • 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

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, Privacy Policy.

Additional Information

Configuration Keys

You can customize these messages per form using hidden input fields:

  • elmfc_submit_text – Submit button text
  • elmfc_sent_text – Button text after successful submission
  • elmfc_cancel_message – Cancel/Reset button text
  • elmfc_success_message – Success message after submission
  • elmfc_error_message – Error message on failure
  • elmfc_validation_message – Validation error message
  • elmfc_sending_message – Text shown while submitting
  • elmfc_captcha_error – Captcha error message
  • elmfc_captcha_message – Captcha question label
  • elmfc_captcha_answer – Captcha answer placeholder
  • elmfc_admin_subject – Admin email subject
  • elmfc_admin_message – Admin email intro text
  • elmfc_user_subject – User copy email subject
  • elmfc_user_message – User copy email intro text
    Note: The redirect URL is configured in the Appearance Settings sidebar for each form (stored as post meta). It cannot be set via a hidden input field — any client-submitted value is ignored.
  • popup_width – Popup width in pixels (default: 500)

Example Form HTML

`html

Contact Information

Your Name *

Your Email *

Message *

How did you hear about us?

Search Engine

Social Media

I agree to the Terms and Conditions *

Send Message
Reset

`

Support

For support, feature requests, or bug reports, please visit the plugin’s support forum on WordPress.org or contact the developer.

Credits

Developed by domclic with ❤️ for the WordPress community.

Blocks

This plugin provides 1 block.

  • ELMFC Form

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"]

FAQ

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

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

Security check: [elmfc_captcha]
`

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 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 customize the button text?

Yes! Add hidden input fields in your form:
html
<input type="hidden" name="elmfc_submit_text" value="Send Message">
<input type="hidden" name="elmfc_success_message" value="Thanks for contacting us!">

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.

Can I translate form messages?

Absolutely! All messages can be customized per form using hidden input fields. Check the documentation for the full list of available configuration keys.

Reviews

There are no reviews for this plugin.

Contributors & Developers

“EffortLess Multisite Form Creator” is open source software. The following people have contributed to this plugin.

Contributors

Changelog

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.

7.5.25 – 2026-07-03

  • Fix: The one-time « my_form » « elmfc_form » CPT migration now also runs immediately after switch_to_blog() in cross-site rendering/submission code paths, not just on init. Previously, a source site reached only via switch_to_blog() (never visited directly since upgrading) would keep rejecting valid cross-site submissions as « Invalid form ».
  • Fix: Forms that had Google reCAPTCHA enabled with the math captcha explicitly turned off now automatically get the math captcha re-enabled on upgrade, instead of silently ending up with no CAPTCHA at all after reCAPTCHA was removed. Stale reCAPTCHA postmeta is cleaned up at the same time.
  • Fix: Submission detail view no longer shows the literal text « Array » for multi-value fields (e.g. checkbox groups); values are now joined into a readable comma-separated list.
  • Fix: Submission detail view field ordering now matches the previous case-insensitive sort instead of PHP’s case-sensitive default.
  • Fix: Admin asset loading now matches the submissions page by its exact registered hook suffix instead of a substring match on the screen ID.
  • Cleanup: Removed a dead, unreachable branch left over from the reCAPTCHA removal in the settings import handler, and a redundant duplicate merge pass in the shortcode’s KSES allowlist builder.

7.5.24 – 2026-07-03

  • Code quality: Removed ~45 unnecessary phpcs:ignore/disable suppressions across the codebase (verified empirically that the underlying code already satisfied WordPress Coding Standards without them). Replaced a raw SQL query with get_post_meta() in the submission detail view, used $wpdb->esc_like() instead of a hand-escaped LIKE wildcard, and sanitized a couple of previously-unwrapped $_SERVER/$_FILES reads directly instead of suppressing the warning. No functional changes.

7.5.23 – 2026-07-03

  • Removed: Google reCAPTCHA v3 integration entirely (settings UI, verification, footer script). Forms now rely solely on the built-in, self-hosted math captcha plus honeypot and rate limiting — no external CAPTCHA service or data leaves your site.

7.5.22 – 2026-07-03

  • Fix: Renamed the « my_form » custom post type to the prefixed « elmfc_form » to avoid naming collisions with other plugins/themes. Existing forms are migrated automatically (one-time rename on the first admin request per site); no action needed.
  • Cleanup: Removed AI-assistant planning/spec files from the shipped plugin (docs/superpowers/); added them to .gitignore.

7.5.21 – 2026-07-02

  • Fix: Enqueue Google reCAPTCHA and admin/submissions meta-box JS via wp_enqueue_script()/wp_add_inline_script() instead of printing inline <script> tags.
  • Fix: Use wp_handle_upload() instead of move_uploaded_file() for form file attachments.
  • Fix: Apply a form-aware KSES allowlist to shortcode-rendered form content as defense-in-depth escaping.
  • Docs: Document Google reCAPTCHA and Akismet as external services in the readme.

7.5.20 – 2026-06-30

  • Update: Confirmed compatibility with WordPress 7.0.

7.5.19 – 2026-06-30

  • Update: Tested up to WordPress 7.0.

7.5.18 – 2026-06-30

  • FEATURE: Shortcode pre-fill — pass any field’s name= attribute directly in the shortcode to pre-populate text inputs, textarea, radio buttons, checkboxes, and selects. Example: [elmfc id= »123″ plan= »premium » firstname= »John »]. Comma-separated values pre-check multiple checkboxes in a group (name= »field[] »). Reserved attributes (id, name, popup) are not treated as field names.

7.5.17 – 2026-06-30

  • FIXED: User copy email never sent when the form’s email input had any name other than « email » — the handler hardcoded $data[’email’] with no way to configure it. Added « Email field name » setting (defaults to « email ») in Email Settings so any field name works. Also fixes Reply-To on admin emails for the same reason.

7.5.16 – 2026-06-30

  • UX: Fixed popup close animation — overlay now fades out correctly instead of snapping off instantly; replaced display:none toggle with pointer-events/visibility so CSS transitions run on both open and close.
  • UX: Scroll to first invalid field on validation failure — page now smoothly scrolls to and focuses the first field that failed validation, not just the error banner.
  • UX: Submit button loading spinner — button shows a CSS spinner while the AJAX request is in flight; text goes transparent so button size stays stable.
  • UX: SMTP password field show/hide toggle — added a reveal button (dashicons-visibility) next to the password input in the email settings meta box.
  • UX: Stats column now shows impressions submits (conversion %) instead of two separate raw-number columns; tooltip gives full label text.

7.5.15 – 2026-06-30

  • REFACTOR: elmfc_get_config_keys() and elmfc_get_skip_fields() now use static caching — arrays are built once per request instead of on every call.
  • FIXED: Removed duplicate elmfc_captcha_answer and elmfc_redirect_url entries from elmfc_get_skip_fields() — both keys are already included via elmfc_get_config_keys().
  • REFACTOR: Removed trivial get_skip_fields() proxy method from ELMFC_Email_Handler — extract_form_fields() calls elmfc_get_skip_fields() directly.
  • REFACTOR: Removed dead $plain parameter from ELMFC_Email_Handler::format_html_email() — the plain-text body was passed in but never used inside the method.
  • REFACTOR: Extracted duplicated main-site ID resolution in the shortcode into a single pre-computed variable — the ms_gpt_source_site / get_main_site_id() lookup was previously copy-pasted in both the id= and name= fallback paths.
  • DOCS: Added return type hint :array to elmfc_default_texts() and elmfc_get_config_keys(); improved docblock for elmfc_get_config_keys().

7.5.14 – 2026-06-29

  • SECURITY: Rate limiter no longer bypassed on sites running a persistent object cache (Redis/Memcached) — counter rows are now written directly to the options table via $wpdb->replace() instead of set_transient(), so the atomic SQL UPDATE always finds the rows it needs.
  • SECURITY: Math captcha tokens now include the form_id in the transient key (‘elmfc_cap_{form_id}_{token}’) — a token solved on one form can no longer be replayed against a different form in the network.
  • SECURITY: AJAX nonce is now scoped to the specific form_id (‘elmfc_form_submit_{id}’) — a nonce obtained from form A can no longer be used to target form B on any site in the network.
  • SECURITY: Email subject and intro text are now read from post meta, not from $_POST — an attacker can no longer spoof the admin notification email’s subject line or opening sentence.
  • FIXED: form_title and form_url are now saved to CPT submissions as ‘elmfc_form’ and ‘elmfc_url’ meta keys — the rename block was previously dead code because it ran after array_diff_key() had already stripped those keys.
  • FIXED: Akismet API key now read with get_site_option() before switch_to_blog() — cross-site form submissions on multisite no longer bypass the spam check when Akismet is network-activated (key stored in sitemeta, not per-site options).
  • FIXED: elmfc_rl_limit and elmfc_rl_window added to the canonical elmfc_get_skip_fields() — these internal rate-limit config keys are no longer forwarded to external webhook payloads; local duplicates removed from email handler and CPT storage.

7.5.13 – 2026-06-29

  • REFACTOR: reCAPTCHA v3 verification extracted to shared elmfc_verify_recaptcha() helper — AJAX and REST handlers no longer duplicate the secret-fetch, HTTP call, and score-check logic.
  • REFACTOR: Rate-limit config reading extracted to shared elmfc_get_rate_limit_config() helper — eliminates duplicated get_post_meta() calls in both submission handlers.
  • REFACTOR: DISTINCT postmeta scan for submission meta keys extracted to shared elmfc_get_submission_meta_keys() helper — list table and CSV export now share the same cached query; fetch_meta_keys() delegates to it.
  • FIXED: Webhook payload builder now uses elmfc_get_skip_fields() instead of a partial hand-rolled list, ensuring elmfc_cc, elmfc_bcc, and any future skip-list additions are excluded from outgoing webhook payloads.
  • IMPROVED: elmfc_default_texts() now memoizes its return value with a static variable, avoiding array reconstruction on every call.
  • IMPROVED: elmfc_is_valid_blog() now returns true for blog ID 1 on single-site installations instead of always returning false.
  • DOCS: readme.txt Additional Information section corrected — elmfc_redirect_url is configured in the Appearance Settings sidebar (post meta), not via a hidden form field.
  • DOCS: REST endpoint now has a clear note that file uploads are not supported; the AJAX handler should be used for forms with file fields.
  • DOCS: docs/ directory added to .gitignore and excluded from distributable zip.

7.5.12 – 2026-06-29

  • SECURITY: reCAPTCHA v3 — omitting the token or leaving the secret unconfigured now rejects the submission instead of silently skipping verification (previously bypassed both reCAPTCHA and math captcha simultaneously).
  • SECURITY: Rate-limit guard now distinguishes a DB error (false) from zero rows affected (0) — a transient DB failure no longer resets the window and bypasses the limit.
  • FIXED: Cross-site multisite — redirect URL after submit now read from source blog postmeta before restore_current_blog(); previously always returned empty on cross-site forms (AJAX and REST).
  • FIXED: Email handler get_skip_fields() now delegates to the global elmfc_get_skip_fields() so future additions to the skip list are automatically inherited.
  • FIXED: send_emails() now returns true when no email routes are configured, preventing a false « Error sending email » response after a successful CPT save.
  • FIXED: Impression and submit stat counters — on the very first submission a concurrent-insert race could leave the counter at 1 instead of N; retry logic ensures no increment is lost.
  • IMPROVED: fetch_meta_keys() in the submissions list table is now cached in a 60-second transient (invalidated on new submission), eliminating a full postmeta DISTINCT scan on every page load.
  • IMPROVED: « Send to specific user » dropdown raised from 200 to 500 users; sites with more than 500 users now see an inline notice with the total count.

7.5.11 – 2026-04-28

  • SECURITY: elmfc_redirect_url is no longer emitted as a hidden form field — it is now read directly from post meta by the AJAX and REST handlers, eliminating any possibility of client-side tampering with the post-submission redirect destination.

7.5.10 – 2026-04-27

  • SECURITY: REST endpoint now enforces the same captcha stack as the AJAX handler — math captcha (token/transient) and reCAPTCHA v3 are both checked before processing REST submissions.
  • SECURITY: reCAPTCHA v3 secret key and webhook signing secret are now encrypted at rest using AES-256-CBC (same scheme as SMTP password). Existing plain-text values are decrypted transparently via the enc: prefix detection.
  • SECURITY: Duplicate-form action now uses object-level capability check (edit_post + post ID) instead of the broad edit_posts capability.
  • SECURITY: Single-submission delete action now validates the post type with elmfc_is_plugin_cpt() before calling wp_delete_post().
  • FIXED: JSON form import no longer allows overwriting the CPT slug (_elmfc_cpt_slug) — changing the slug via import could silently rename the submission table.
  • FIXED: wp_die() strings in admin actions are now translatable.

7.5.9 – 2026-04-27

  • FIXED: Form field labels, placeholder text, and other text inside core/html blocks is now translated. Registers the ms_gpt_translate_html_block filter (added in auto-translate 3.2.31) for my_form posts so GPT receives and translates the raw HTML content.

7.5.8 – 2026-04-27

  • FIXED: Forms now translate correctly — removed the filter that was telling the auto-translate plugin to skip all block content for my_form posts.
  • FIXED: The elmfc/form Gutenberg block now registers ms_gpt_remap_block_attrs so its formId attribute is remapped to the destination site’s translated form when a page is translated.
  • FIXED: my_form slugs are now preserved on translated sites (ms_gpt_preserve_post_slug) so [elmfc name= »… »] shortcodes continue to work.
  • FIXED: [elmfc id=…] shortcode now falls back to the source site when the translated form doesn’t exist yet on the destination site.

7.5.7 – 2026-04-26

  • FIXED: Resolved final two PHPCS warnings — reworded inline comment in email handler that was flagged as commented-out code; corrected operator alignment in shortcode file.

7.5.6 – 2026-04-27

  • SECURITY: REST endpoint redirect URL now validated with wp_validate_redirect() (parity with AJAX handler).
  • SECURITY: [elmfc_submissions] shortcode now validates the cpt attribute against plugin-owned slugs via elmfc_is_plugin_cpt() — prevents listing arbitrary CPT content.
  • FIXED: CPT slug save in meta boxes now rejects slugs that belong to built-in or third-party post types (post, page, my_form, etc.) to prevent form submissions being inserted as regular WordPress posts.

7.5.5 – 2026-04-26

  • SECURITY: Rate limit counter is now atomic (SQL UPDATE + expiry JOIN) — eliminates TOCTOU race that allowed bypass under concurrent requests.
  • SECURITY: REST endpoint now uses elmfc_get_client_ip() (proxy-aware) instead of bare $_SERVER[‘REMOTE_ADDR’] for rate limiting.
  • SECURITY: Copy-to-site handler validates target blog ID with elmfc_is_valid_blog() before switch_to_blog().
  • SECURITY: Resend-email AJAX handler verifies the submission belongs to a plugin-owned CPT via new elmfc_is_plugin_cpt() helper.
  • SECURITY: Bulk delete and CSV export now validate the ?cpt= slug is a plugin-owned CPT before acting.
  • SECURITY: Bulk delete verifies each post ID belongs to the expected CPT before deleting.
  • SECURITY: Redirect URL after submission is validated with wp_validate_redirect() to enforce same-origin policy.
  • SECURITY: File upload rename() return value is now checked — failed rename discards the temp file instead of attaching a phantom path.
  • SECURITY: CPT submission meta keys are sanitized with sanitize_key() and private-meta prefix (_) is rejected in elmfc_save_to_cpt().
  • SECURITY: Settings import rejects files larger than 512 KB before calling file_get_contents().

7.5.4 – 2026-04-26

  • SECURITY: Fixed DOM-based XSS in showMessage() — replaced .html() with safe DOM construction; captcha question update now uses .text() via new .elmfc-captcha-question span.
  • SECURITY: REST endpoint (/wp-json/elmfc/v1/submit) now enforces the same per-form, per-IP rate limiting as the AJAX handler.
  • SECURITY: SMTP password no longer passed through sanitize_text_field() which was silently corrupting passwords containing <, >, or &.
  • SECURITY: Server-side blocklist of executable extensions (php, phar, asp, sh, etc.) applied to file uploads regardless of admin-configured allowlist.
  • SECURITY: elmfc_source_site POST/REST parameter is now validated against real network blog IDs before switch_to_blog() is called.
  • SECURITY: reCAPTCHA v3 verification is now fail-closed — network errors reject the submission instead of silently passing it.
  • FIXED: elmfc_submissions_shortcode now escapes get_the_title() output and requires edit_posts capability.
  • FIXED: Cross-site block in main shortcode wrapped in try/finally so restore_current_blog() always runs on exception.
  • FIXED: Resend email AJAX handler now checks current_user_can(‘edit_post’, $form_id) to prevent cross-form access.
  • FIXED: Copy-to-site handler now verifies nonce before capability check (correct security gate order).
  • FIXED: Duplicate admin notices now only display on my_form screens.
  • FIXED: Settings import now applies type-appropriate sanitizers (URL, int, float, email) instead of blanket sanitize_text_field for all values.
  • FIXED: CPT registration (elmfc_register_all_dynamic_cpts) now caches results in a transient, eliminating the per-request DB query.
  • FIXED: Submissions badge no longer runs N+1 DB queries on every admin page load — uses elmfc_sub_counts transient.
  • FIXED: get_users() in email settings now limited to 200 results to prevent memory issues on large networks.
  • IMPROVED: Centralized skip-field list via elmfc_get_skip_fields() used across CPT storage.
  • IMPROVED: Uninstall transient cleanup now uses $wpdb->prepare() consistently.

7.5.2 – 2026-03-04

  • FIXED: Remaining PHP Deprecated warnings — str_replace(null) caused by get_edit_post_link() returning null for non-public CPT in duplicate handler; replaced with admin_url('post.php?...').
  • FIXED: preg_replace() null return (on PCRE error) propagating as null $form_content in shortcode; cast result to string.
  • FIXED: Forms not rendering for visitors — deprecation warnings output before form HTML disrupted page structure; null guards now applied before $post is accessed in asset enqueue.

7.5.1 – 2026-03-04

  • FIXED: PHP Deprecated warnings — strpos(null) and str_replace(null) — caused by $post->post_content being null on some post types; cast to string before passing to has_shortcode() and apply_filters('the_content', ...)

Earlier versions

Entries for 7.5.0 and earlier (back to the 7.0.0 initial release) have been
removed from this file to stay under WordPress.org’s Changelog length limit.
They remain viewable in earlier revisions of this same readme.txt in the
plugin’s SVN repository history.