Grow Enquiries
Enquiry forms for Grow Creative's WordPress sites: private log, cache-safe spam protection, shadow-accept quarantine, no address ever printed.
Install
This is the repository archive. It installs, but the folder name will carry the branch suffix and updates will not flow.
Install straight from GitHub with WP-CLI:
wp plugin install https://github.com/aconitewill/grow-enquiries/archive/refs/heads/main.zipEnquiry forms for Grow Creative's WordPress sites. One plugin, every site, so a fix goes out once.
- A private Enquiries log in wp-admin: Inbox and Spam views, unread counts, a read-only view with Reply, Release and send, Mark as spam, Block sender.
- Cache-safe spam protection: honeypot, JavaScript token and time on page, content scoring with per-site extra and ignored terms and sector packs, blocked senders, a per-visitor rate limit, optional Cloudflare Turnstile.
- Shadow-accept: anything caught is logged, not emailed, and the sender still sees the thank-you.
- The recipient's address is never printed on any page, including on errors.
- Email to the recipients with Reply-To set to the visitor; HTML or plain text. A failed email is retried hourly, up to 3 times, then flagged for a reply by hand.
- A digest of held spam (monthly, weekly or off), which also lists any enquiry whose email failed, so nothing is deleted unseen.
- Daily clean-up: held spam after 60 days, genuine enquiries after 24 months (both settings).
- CSV export of the view on screen, with a date range. WordPress's Export and Erase Personal Data tools find enquiries by email address, and the privacy policy guide gets matching wording.
- A small Dashboard widget: unread, held as spam this month, not emailed.
- Updates from GitHub releases, like any other plugin.
The plan this implements: sites/enquiry-module-plan.md (19th October, 2026). v0.1 was phase 1 of that plan; v0.2 is phase 2.
Putting a form on a site
Template tag, in a theme:
<?php if ( function_exists( 'grow_enquiry_form' ) ) { grow_enquiry_form( array( 'form_id' => 'contact' ) ); } ?>
Shortcode, in page content:
[enquiry_form form_id="contact"]
Arguments: form_id (when there are several on a page), about (a post ID; locks an "Enquiring about" field and names the subject), heading, intro, button.
Extra fields
add_filter( 'grow_enquiry_fields', function ( $fields, $form_id ) {
if ( 'contact' === $form_id ) {
$fields['topic'] = array(
'label' => "What's it about?",
'type' => 'select', // text | select | date
'options' => array( 'hosting' => 'Hosting', 'other' => 'Something else' ),
'about' => true, // the choice names the enquiry
'prefill' => 'topic', // ?topic=hosting pre-selects it, in the browser
);
}
return $fields;
}, 10, 2 );
Styling
- The plugin stylesheet is all
:where(), so any theme rule wins. Set--gre-accent,--gre-ink,--gre-muted,--gre-bg,--gre-border,--gre-radius,--gre-font,--gre-label-fonton.gre. - Or switch the stylesheet off (Settings, Form) and style the stable classes (
.gre,.gre__field,.gre__button,.gre__notice--successand so on). - Or copy
templates/form.phpto<theme>/enquiries/form.phpand change the markup. Keep the field names and callgrow_enquiry_hidden_fields( $gre )inside the form.
Filters and actions
grow_enquiry_fields( $fields, $form_id )grow_enquiry_about_label( $label, $post_id )grow_enquiry_spam_terms( $terms ): label => array( pattern, weight )grow_enquiry_settings( $settings )grow_enquiry_roles( $roles ): who getsmanage_enquiries(Administrator and Editor)grow_enquiry_receivedaction ( $id, $reasons )grow_enquiry_flaggedaction ( $id ): an email has failed every retrygrow_enquiry_update_checker( $on ): from a must-use plugin only, as it runs when the plugin loads
Turnstile keys can be set as GRE_TURNSTILE_SITE_KEY and GRE_TURNSTILE_SECRET_KEY in wp-config.php instead of the database.
WP-CLI
wp grow-enquiries list [--view=inbox|spam|failed] [--format=table|csv|json|ids]
wp grow-enquiries release <id>... # also spam, read, resend, bin
wp grow-enquiries retry # the hourly retry, now
wp grow-enquiries digest [--preview] [--if-due]
gre_do_action( $do, $id ) works from scripts too (wp eval), with no need to load the admin files.
Releases and updates
Sites update from GitHub releases through the bundled Plugin Update Checker (MIT, in lib/). The repo is public, so no token is needed anywhere.
To release:
- Bump the version in the plugin header and
GRE_VERSION(they must match), and add a CHANGELOG.md entry headed## x.y.z (date). - Commit, then tag and push:
git tag vX.Y.Z && git push origin main vX.Y.Z. - The Release workflow (
.github/workflows/release.yml) checks the versions match the tag, buildsgrow-enquiries.zipwithgit archive(.gitattributesleaves out.githuband the git files), and attaches it to a release whose notes are that CHANGELOG entry. - Sites show the update in Dashboard, Updates within 12 hours (or straight away after "Check for updates"). Update Grow Creative's site first, check it, then the rest;
wp plugin update grow-enquiriesover SSH works too.
The update checker is off in a git checkout (a folder with .git), so a development copy is never overwritten, and off wherever GRE_DISABLE_UPDATES is true in wp-config.php. A must-use plugin can force it either way with the grow_enquiry_update_checker filter.
Not in v0.2 yet
From phase 3 of the plan: the migration tool (wp grow-enquiries migrate) and legacy form names for Adrian Paye and Seven Dunes.