Description
Template-Oriented Form Utilities (TOFU) is designed to streamline the process of creating and managing forms within WordPress themes and plugins. By adopting a template-oriented approach, TOFU allows developers to define form structures and behaviors using reusable templates, making it easier to manage/maintain with team collaboration through version control systems.
Forms are configured in the theme’s PHP code and nothing about them is stored in the database. A read-only screen under Tools shows each registered form’s pages, mail, fields and features, and points out pages and mail templates that don’t exist.
GitHub and documentation for this plugin can be found at:
External services
This plugin relies on third-party services to protect your website from spam and automated attacks. Depending on your configuration, this plugin connects to the following services:
Google reCAPTCHA
- Purpose: Protecting forms from spam and bot abuse.
- When data is sent: When a page containing a reCAPTCHA-protected form is loaded or submitted.
- Data sent: IP address, mouse movements, browser/device information, and duration of stay.
- Service Provider: Google LLC.
- Links: Google Privacy Policy, Google Terms of Service.
Cloudflare Turnstile
- Purpose: Privacy-focused alternative for bot protection and spam prevention.
- When data is sent: When a user interacts with a form protected by Turnstile.
- Data sent: Browser/device characteristics and interaction data (privacy-friendly, does not use cookies for tracking).
- Service Provider: Cloudflare, Inc.
- Links: Cloudflare Privacy Policy, Cloudflare Website Terms.
Installation
- From the WP admin panel, click “Plugins” -> “Add new”.
- In the browser input box, type “Template-Oriented Form Utilities”.
- Select the “Template-Oriented Form Utilities” plugin and click “Install”.
- Activate the plugin.
OR…
- Download the plugin from this page.
- Save the .zip file to a location on your computer.
- Open the WP admin panel, and click “Plugins” -> “Add new”.
- Click “upload”.. then browse to the .zip file downloaded from this page.
- Click “Install”.. and then “Activate plugin”.
FAQ
-
Where are the form settings?
-
In your theme’s code. Tools -> Forms (TOFU) shows them, read-only.
Reviews
There are no reviews for this plugin.
Contributors & Developers
“TOFU (Template-Oriented Form Utilities)” is open source software. The following people have contributed to this plugin.
ContributorsTranslate “TOFU (Template-Oriented Form Utilities)” into your language.
Interested in development?
Browse the code, check out the SVN repository, or subscribe to the development log by RSS.
Changelog
- v0.0.1 – Initial release.
- v0.0.2 – Arranged required PHP version to 8.1, added external services section to the readme.
- v0.0.3
- Implemented Ajax form submission and validation with reCAPTCHA and Turnstile support.
- Fixed recaptcha issue when embedded multiple forms on the same page.
- Replaced validation library from wixel/gump to somnambulist/validation.
- v0.0.4
- Added optional database recording of form submissions (
saveToDatabase), encrypted at
rest, with an admin screen to view recorded data. - Added
Form::setTemplate()to override a form’s input/confirm/result URLs per visitor
session, so a single registered form can be embedded on multiple pages (e.g. post
templates); addedFormConfig::$dynamicTemplatefor forms configured this way.
- Added optional database recording of form submissions (
- v0.0.5
- The AJAX nonce endpoint (
GET /wp-json/tofu/v1/forms/{key}/nonce) now also returns the
reCAPTCHA/Turnstile site key when enabled, so cross-origin/headless clients no longer need
to hardcode it separately from the server-side configuration.
- The AJAX nonce endpoint (
- v0.0.6
- Fixed: AJAX form submissions with a field literally named
keycould fail with a
“form not found” error, because the REST handlers read the form key from the merged
request parameters instead of strictly from the URL route.
- Fixed: AJAX form submissions with a field literally named
- v0.0.7
- Replaced the bundled validation library with an in-house engine. The plugin now has no
runtime dependencies at all. Every rule name still resolves, so existingrules:
configuration keeps working — see the upgrade notice for the behavioural differences. - Added:
required_fileas the name of the required-file rule.custom_required_file,
its name since 0.0.3, still works and runs the same code. - Fixed: a full-width space (U+3000) no longer satisfies
required. It is what a Japanese
IME emits for the space bar, so a field the visitor sees as empty could pass. - Fixed:
requirednow recognises an empty file input as empty. - Fixed:
after,before,extensionanduuidno longer raise a fatal error on
ordinary input — a blank date field was enough to return a 500. - Fixed:
uploaded_file,mimesandextensionnow work. They depended on a check that
is never true in this plugin’s request flow. - Fixed: a file carried over to the confirm page is verified against the server’s own
session record, so a tampered form cannot claim an upload that is not there. - Fixed: the session cookie is issued only when a session is actually saved. It was
previously sent on every request, including pages with no form and the admin screens,
which is enough to stop most full-page caches serving anything cached. - Fixed: the plugin never called
load_plugin_textdomain(), so its bundled Japanese
translations were not loaded and validation, reCAPTCHA and Turnstile messages rendered
in English on Japanese sites. - Fixed: “reCAPTCHA token is missing.” and “Turnstile token is missing.” were the only
bot-protection messages not passed through__(), so they stayed English even on a
translated site. - Fixed: none of the recorded-submissions admin screen’s text had been extracted for
translation since it was added in 0.0.4, so translators never received it. It is in the
.pot now, with Japanese supplied. - Tested against WordPress 7.1.
- Replaced the bundled validation library with an in-house engine. The plugin now has no
- v0.1.0
- Added: seven actions and filters, so code outside a form’s own configuration can react to
submissions and extend validation —tofu_form_submitted,
tofu_register_validation_rules,tofu_pre_send_mail,tofu_validation_failed,
tofu_redirect_url,tofu_record_valuesandtofu_admin_page_capability. They fire for
the redirect and AJAX flows alike. See docs/hooks/index.md. - Added: a named custom validation rule can now be registered site-wide, via the
tofu_register_validation_rules action. The validator factory was previously unreachable,
so this was not possible without editing the plugin. - Fixed:
Form::setTemplate()no longer writes to the session, and so no longer sends a
Set-Cookie, when a page is merely rendered. A theme calling it for each of its registered
forms on every page load was issuing one cookie per form on every response — including
pages with no form on them — which stops server and CDN page caches serving anything
cached. The override is now carried to the following POST in a hidden field and persisted
only when the visitor actually submits. - Fixed: at most one session cookie is issued per response. A single response can save the
session more than once, and each save sent its own redundantSet-Cookie. - Fixed: database migrations now run when the plugin is updated. The plugin listened for
upgrade_process_complete, which WordPress does not define — the hook is
upgrader_process_complete — so migrations had only ever run on activation. A site that
installed before a migration was added and updated since never received it; updating to
this version applies anything outstanding. Migrations are tracked inwp_tofu_migrateand
already-applied ones are skipped, so there is nothing to do by hand. - Fixed: a submission nonce is now bound to the form it was issued for. The redirect flow
minted its nonce against a bareinput/confirmaction, so a nonce issued for one form
verified against every other one; only the field name distinguished them, and a field name
is a label the sender controls. The REST flow already did this. - Changed: the plugin’s own hidden field names now all use a
__tofu_prefix, and a form may
no longer declare a field name starting with_tofu_or__tofu_—FormConfigthrows at
registration instead of letting the collision silently drop the form’s value. Breaking for
AJAX/headless clients that hardcode those names; see the upgrade notice.
- Added: seven actions and filters, so code outside a form’s own configuration can react to
- v0.1.1
- Added:
MailConfig::$returnPathsets the Return-Path (envelope sender) of the plugin’s
emails, so bounces go to a chosen address instead of the server default. WordPress
ignores aReturn-Path:header passed towp_mail(), so it is applied to PHPMailer’s
Sender and only for the plugin’s own messages.Mail::setReturnPath()lets a
tofu_pre_send_mail callback set it per recipient. See docs/settings/mailconfig.md.
- Added:
- v0.1.2
- Added: a read-only screen under Tools -> Forms (TOFU) that shows each registered form’s
pages, mail, fields and features, one tab per form. It points out pages and mail
templates that don’t exist, bot protection that is enabled but not registered, and
placeholders that are not allowed fields. Secret keys are never shown. The screen uses
the sametofu_admin_page_capabilityas the records screen. See docs/admin/index.md. - Fixed: the migration that adds the
dataandsubmitted_atcolumns compares column
names exactly. WithLIKE, the_insubmitted_atmatched any character, so a
similarly named column could make the migration skip adding it. - Fixed: running the migrations a second time in the same request, after one had failed,
stopped with a fatal error instead of retrying it. - Declared
Requires at least: 6.0, so translate.wordpress.org imports the plugin’s strings. - Escaped and sanitized request values flagged by Plugin Check.
- Added: a read-only screen under Tools -> Forms (TOFU) that shows each registered form’s
- v0.1.3
- Fixed: database migrations now also run when the plugin’s files are replaced without
WordPress’s updater (FTP, deployments). The plugin compares the storedtofu_db_version
with its own version oninitand runs any outstanding migration once; a failed one is
retried on the next request. - Requires WordPress 6.5.
- Fixed: database migrations now also run when the plugin’s files are replaced without
