Skip to content

Forms ​

Contact forms in the Contact Form 7 format, with Flamingo messages and mail.

kitloom/forms · admins: kitloom/forms-filament, kitloom/forms-nova

Video lessons ​

Recorded in the Filament admin; the Nova admin works the same way.

Contact forms
Contact form messages
Contact form settings

Contact Form 7 forms on a kitloom site: the shortcodes that draw them, taking a submission, mailing it and keeping it. Forms stay where the plugin keeps them and messages where Flamingo keeps them, so a site moved from WordPress brings both along, and nothing here needs a table of its own.

The admins are kitloom/forms-filament and kitloom/forms-nova.

The data ​

WhatWhere
A forma wpcf7_contact_form post
Its template, [text* your-name] and the rest_form meta (the content keeps a copy)
Its mails_mail and _mail_2 meta: recipient, sender, subject, body, additional headers, use_html, exclude_blank, active
The texts a visitor sees_messages meta, by the plugin's keys (mail_sent_ok, invalid_required, …)
Additional settings_additional_settings meta, name: value lines
What a shortcode finds it by_hash meta, given on the first save
A messagea flamingo_inbound post: _field_<name> meta per field, _fields, _from, _subject, _meta (IP, user agent, page, date)
The form a message came fromits flamingo_inbound_channel term, under "Contact Form 7", with the form's slug

Arrays are written as JSON and read in WordPress's serialized format too.

On the site ​

[contact-form-7 id="1a2b3c4"] (or the older [contact-form id="12"]) finds the published form by the start of its hash or by its post ID. Its data for the theme is the template with each form-tag turned into a field shortcode — [text], [email], [url], [tel], [number], [date], [textarea], [select], [checkbox], [radio], [acceptance], [submit] — whose arguments say what to draw: name, value, required, placeholder, options, multiple, and the rest of the tag's options. Other tags ([file], [quiz]) stay as text.

The shortcode's data also has action: the address the theme posts the fields to, POST /wp-json/contact-form-7/v1/contact-forms/{id}/feedback — the plugin's own route, under the site's REST root (kitloom/site). The fields go as the form posts them, form-encoded or JSON, a checkbox group as a list; no session and no CSRF token, kitloom-forms.routes.throttle submissions a minute from one address (10). The answer is the plugin's:

json
{ "contact_form_id": 12, "status": "validation_failed", "message": "One or more fields have an error…",
  "invalid_fields": [{ "field": "your-email", "message": "The field is required.", "into": "span.wpcf7-form-control-wrap[data-name=\"your-email\"]" }] }

status is mail_sent, validation_failed, spam or mail_failed, always with 200; an unknown form is wpcf7_not_found (404). routes.register turns the route off.

In another language ​

With kitloom/languages a form follows the page's language: its placeholders, the choices of a list, the acceptance sentence, the buttons, the text around the fields and the messages after sending come from the form's Translations tab — one per language, with Translate with AI. A submission is checked against the words the visitor saw, and a translated choice still says in mail what the original says, so the mail you get is the same in every language. The messages a form keeps as they came — the defaults — are this package's words, translated for the visitor (lang/fr.json) until the tab says otherwise.

A form offers its words through Kitloom\WpSchema\Contracts\TranslatableTexts (FormTexts, keys like form.your-name.placeholder, form.submit.0, messages.mail_sent_ok) and reads them through TextTranslator: without a translation module the form is as written.

A submission ​

  1. Spam protection decides whether to take it at all (below). A submission made in code — Submissions::submit() without a request — is not asked.
  2. Each field is checked against its tag — required, email, URL, telephone, number and date ranges, length, a choice the form offers — and only the form's own fields are kept.
  3. The message is stored as Flamingo's, unless do_not_store: true.
  4. The mails are queued through the application's mailer (or kitloom-forms.mailer), mail-tags filled — [your-name], [_raw_topic], [_site_title], [_site_admin_email], [_date], [_remote_ip], [_url] — unless skip_mail: on. demo_mode: on skips both.

The pieces are public for a site that draws or takes forms its own way: Forms finds a form, Submissions takes a submission, FormProperties reads and writes a form as an admin edits it.

Spam protection ​

Two checks, cheapest first; a submission that fails one gets the form's "spam" text (Your message could not be sent. unless the form changes it) and leaves no message and no mail. How often one address may post is the route's limit (10 a minute).

Everything here is set on the Settings page of the forms section in either admin (Contact in Filament, Contact Forms in Nova): the honeypot, the submissions a minute from one address, and the Turnstile keys. What is saved there is used; until then the config below is, so a site that keeps its keys in .env goes on as it was. The Turnstile keys go as a pair — the admin's once a site key is saved there, else .env's; empty the site key on the page to go back to .env. The secret is stored encrypted with the application key and never shown again; leave its field empty to keep it. Only administrators open the page (the manage_contact_form_settings permission), and agents cannot read or change the secret.

CheckWhat it doesOn
Honeypotspatie/laravel-honeypot: a field people do not see and bots fill in, and the time the form was drawn — sent sooner than HONEYPOT_SECONDS (1) after it, or with the fields missing, it is a botby default; off on the Settings page or with KITLOOM_FORMS_HONEYPOT=false
Cloudflare TurnstileCloudflare's widget checks the visitor is a person, mostly without a puzzleonce both keys are set

Turnstile: in the Cloudflare dashboard, Turnstile → Add widget, with the site's domain; then paste both keys on the Settings page, or put them in .env:

dotenv
KITLOOM_FORMS_TURNSTILE_SITE_KEY=0x4AAAAAAA...
KITLOOM_FORMS_TURNSTILE_SECRET=0x4AAAAAAA...

When Cloudflare cannot be reached, submissions are refused and the error is reported, rather than let through.

The theme draws what the form's data says under protection: honeypot (nameFieldName, validFromFieldName, encryptedValidFrom — two inputs hidden with CSS, not type="hidden", the first left empty) and turnstile (siteKey — a .cf-turnstile element, which the widget fills with cf-turnstile-response); each is null when off. They are posted with the fields. Cloudflare's script lands in @siteFooter of a page that drew such a form; a Vue theme loads it itself. The themes that draw the form — Journal, Bureau, Portfolio and Pantry (Vue) and Blade — do all of it; Marketplace and Game Reviews don't draw [contact-form-7] and leave nothing in its place. The honeypot gets along with the page cache: a cached form was only drawn earlier.

The same two checks guard the site's other forms — the comments' — through kitloom/site's FormProtection, with the same switches and keys; a module that asks needs no forms package, and gets them once this one is installed.

An application with its own idea binds Contracts\SubmissionGuard: its guard replaces both checks for the contact forms, and protection stays empty. AllowAllSubmissions takes everything, for a site that stops bots elsewhere.

AI agents ​

With kitloom/ai installed, an agent reads the forms with their shortcodes and fields (forms_list) and the messages they received (submissions_list, the inbox or the spam box) with read_contact_messages; with manage_contact_forms it reads a form's template (form_get), creates a form (form_create, mailing the site's admin email as a new form in the admin does — where messages go is a person's choice) and changes its title or template through Forms (form_update) and moves a message to the spam box or back (submission_spam). Nothing is deleted. kitloom-forms.ai.register false — no form tools.

Personal data ​

With kitloom/privacy installed, Tools → Export Personal Data hands a person every message sent from their address — the inbox, the spam and the trash: the form, the subject, when it was sent, each field by its label and where it was sent from. Erase Personal Data deletes those messages for good, as Flamingo does — a message is only what its sender wrote — and the forms' inbox counts follow. Other people's messages that mention the address are left alone. kitloom-forms.privacy.register false — the module does not take part.

Configuration ​

config/kitloom-forms.php: mailer, spam (honeypot, turnstile.site_key, turnstile.secret, turnstile.timeout), routes (register, throttle), and the switches shortcodes.register, filament.register, nova.register, ai.register, privacy.register.

kitloom — modules and themes for Laravel.
Pricing · Services · License · Refunds · Privacy · Community · [email protected]