A WordPress plugin by Tech Matters that provides a contact/newsletter subscription form with Airtable integration.
Website: https://bd4d.org/ Version: 1.0.3 License: GPL-2.0-or-later
- Contact/newsletter subscription form (shortcode:
[bd4d-contact-form]) - Airtable integration for storing user data
- Google reCAPTCHA v3 for bot protection
- Auto-reply confirmation emails
- WordPress admin settings page for configuration
bd4d-plugin/ # Repo root (build tools & config)
├── Gruntfile.js # Build task configuration
├── package.json # NPM dependencies
├── composer.json # PHP dependencies
├── phpcs.xml # PHP CodeSniffer config
├── .github/ # CI/CD workflows (not deployed)
│ └── workflows/
│ ├── wpcs.yml # WordPress coding standards check
│ └── check-commits.yml # Conventional commits validation
│
└── wp-content/ # ← DEPLOYED TO PRESSABLE
└── plugins/
└── bd4d/ # The WordPress plugin
├── bd4d.php # Main plugin entry point
├── includes/
│ ├── class-bd4d.php # Core form & Airtable integration
│ ├── class-google-recaptcha.php # ReCAPTCHA verification
│ └── settings/
│ ├── class-settings.php # Base settings class
│ └── class-contact-form-settings.php # Admin settings
├── assets/
│ ├── css/src/main.scss # Styles (→ main.min.css)
│ └── js/src/main.js # Form JS (→ main.min.js)
└── template-parts/
├── form-email.php # Newsletter form HTML
└── auto-reply.php # Email template
- PHP with WordPress Plugin Architecture (requires WP 6.2+)
- JavaScript/jQuery for AJAX form handling
- SCSS for styling
- Grunt for build tasks (linting, compilation, minification)
- Composer for PHP dependencies
- Follows WordPress-VIP-Go coding standards
npm install && composer install # Install dependencies
npx grunt # Run all build tasks
npx grunt watch # Watch for changes
npx grunt css-js # Build CSS and JS only
npx grunt php # Run PHP linting/standardsSettings are managed in WordPress Admin under the BD4D settings page:
- Airtable Base ID, Table ID, API Token
- Google reCAPTCHA Site Key and Secret Key
The plugin writes form submissions to Airtable via the REST API (https://api.airtable.com/v0).
| Airtable Field | Form Input | Type | Required |
|---|---|---|---|
Email Address |
Email input | No | |
First Name |
First name input | Text | Yes |
Last Name |
Last name input | Text | Yes |
Affiliation |
Affiliation input | Text | No |
Form Comments |
Message textarea | Text | No |
Email-Opted In? |
Newsletter checkbox | Boolean | Always sent |
CotW-Opted In? |
Supporter checkbox | Boolean | Always sent |
Adoption? |
Adoption checkbox | Boolean | Always sent |
Note: At least one of Email Address or Form Comments must be provided for submission.
class-bd4d.php- Containsadd()method that writes to Airtable (line ~153)form-email.php- Form HTML templateclass-contact-form-settings.php- Admin settings for API credentials
- Add the field/column directly in Airtable (no staging Airtable exists)
- Update
class-bd4d.php:- Add parameter to
add()method - Add field to
$data['fields']array - Update
send_message()to read from$_POSTand pass toadd()
- Add parameter to
- If field comes from form:
- Update
form-email.phpto add the HTML input - Update
assets/js/src/main.jsto read the input and include it in the AJAXdataobject
- Update
- Run
npx gruntto build assets - Commit (including built assets), push, deploy
- No staging Airtable environment - all environments write to the same Airtable base
- Plugin creates new records (POST), does not update existing ones
- Auto-reply email is sent after successful Airtable write
The plugin sends a confirmation email after successful form submission. The email content varies based on which checkboxes were selected.
| Case | Newsletter | Supporter | Adoption |
|---|---|---|---|
| A | ✓ | ✗ | ✗ |
| B | ✗ | ✓ | ✗ |
| C | ✓ | ✓ | ✗ |
| D | any | any | ✓ |
| E | ✗ | ✗ | ✗ |
- Case A (Newsletter only): User subscribes to email updates. Gets unsubscribe instructions.
- Case B (Supporter only): User agrees to be listed as public supporter. Gets display permission confirmation.
- Case C (Newsletter + Supporter): User wants both. Gets combined confirmation with bullet points for both permissions plus unsubscribe instructions.
- Case D (Adoption): Takes priority. User wants to learn about adopting BD4D Standard. Gets personalized follow-up promise ("We will contact you personally within the next two business days").
- Case E (No checkboxes): User submits without selecting any options. Gets generic welcome message with no confirmation section.
auto-reply.php- Email template with conditional logic
| Environment | URL | Branch | Deploy Method |
|---|---|---|---|
| Production | bd4d.org | main |
Auto-deploy on push to main |
| Staging | bd4d-staging.mystagingwebsite.com | main |
Unknown (may be auto or manual) |
| Sandbox | bd4dsandbox.mystagingwebsite.com | N/A | Static clone from staging (Oct 2025) |
Please double-check that this information is still correct before relying on these notes.
These deployment notes are listed here for convenience, but they are not specific to this plugin. This may change based on modifications completely unrelated to this plugin.
Pressable has GitHub Integration configured:
- Source: UI shows
wp-contentfolder in repo - Destination: UI shows
htdocs/wp-contenton Pressable server - Trigger: Production auto-deploys on push to
mainbranch - Result: See observations below
GitHub (main branch) Pressable
───────────────────── ────────────────────
wp-content/plugins/bd4d ──────► htdocs/wp-content/plugins/bd4d
Observations: Despite the Pressable UI showing wp-content → htdocs/wp-content, we observed that only bd4d/ plugin files were updated during deployments. Other plugins, themes, and uploads were not deleted.
This was verified by:
- Comparing Pressable daily backups before/after deployments
- Checking server file timestamps (
find /srv/htdocs -newermt ...) - Confirming only
bd4d/files changed
Why this happens is unclear. Possible explanations:
- Pressable may implicitly only sync directories that exist in the repo
- There may be hidden settings from when staging was cloned to production
- The behavior could change if the GitHub integration is deleted and recreated
We cannot confirm the exact behavior without deleting and recreating the integration, which carries risk.
The Pressable UI shows broader paths than intended. The recommended settings are:
Repository Directory to Deploy From: wp-content/plugins/bd4d
Deployment Path: htdocs/wp-content/plugins/bd4d
Note: The Pressable UI may show different values and changes may revert when navigating away. The "Set and Deploy" button appears to be required to save settings (which also triggers a deploy).
Server architecture:
htdocs/- Your site files (wp-content, wp-config.php) - GitHub deploys herewordpress/(symlink) - Pressable's shared WordPress core (managed by Pressable, read-only)
Production auto-deploys when you push to main:
GitHub main ──► production (auto-deploy)
- Make changes locally
- Run
npx gruntto build assets (CSS/JS) - Commit changes (including built assets in
wp-content/plugins/bd4d/assets/) - Push to
mainbranch - Production auto-deploys within minutes
- Verify changes on production
Recommended: Always backup before deploying (see backup.sh in repo root)
Best for: When staging and production should be identical mirrors (content, settings, everything).
- Make and test changes on staging
- When satisfied → Clone staging to production in Pressable dashboard
Pros: Single source of truth, staging exactly matches what goes to production Cons: Copies EVERYTHING (database, uploads, settings) - overwrites any production-only content
| Scenario | Recommended |
|---|---|
| Plugin changes only | Option A (GitHub deploy) |
| Staging/production are identical mirrors | Option B (Clone) |
| Production has unique content or settings | Option A (GitHub deploy) |
| Unsure | Option A (safer) |
Note: Form submissions go to Airtable, not WordPress, so no form data is lost either way.
The staging branch in the GitHub repo is not used and is stale:
| Branch | Last Commit | Date |
|---|---|---|
main |
64aee4e | Jan 6, 2026 |
staging |
a8bcbbe "fix: fix submit button handling" | Mar 27, 2025 |
204 commits behind main:
- 202 are Dependabot dependency updates
- 2 are actual code changes (
fix: disable mobile logo fix,build: update dependencies)
The staging workflow happens at the Pressable level (deploy to staging site first, then production), not via git branches. The staging branch could be deleted or kept for historical reference.
- wpcs.yml - Validates WordPress-VIP-Go coding standards on PRs
- check-commits.yml - Enforces conventional commit messages
- Build tools (Grunt, npm, composer configs) live at repo root but are NOT deployed
- Only the
wp-content/plugins/bd4d/folder is deployed to Pressable - Built assets must be committed (they're not built on the server)
- Never deploy the entire
wp-content/folder - it will delete themes and other plugins not in this repo
Last updated: Jan 2026