When creating a contact form in WordPress using Contact Form 7, they can be configured to automatically send an email upon user submission to anyone you want. Out-of-the-box, Contact Form 7 even includes the special mail tag [_site_admin_email] so you can send the email to the administration email address in the General Settings. That’s somewhat dynamic in that you aren’t typing in a specific email address in the To field of the mail template.
However, a common use of my plugin, Contact Form 7 – Dynamic Text Extension, is used to dynamically pre-fill a hidden field with some value, perhaps a custom field made with ACF, or the event organizer of a calendar event, or even just the author of the post itself.
This is great, but in this context, those email addresses are visible to anyone sneaky enough to look at the page’s source code, which means they’re also vulnerable to any bots scraping the HTML for sensitive data. So how can we keep their email addresses private but still get them sent to the right people? The answer: use the wpcf7_before_send_mail action hook!
Base function for wpcf7_before_send_mail action hook
First, we’ll start off with adding the action and defining our function like this:
/**
* Custom Email Behavior on Contact Form 7 Form Submission
*
* @param WPCF7_ContactForm $contact_form The current contact form.
* @param bool $abort If the submission is being aborted.
* @param WPCF7_Submission $submission The current submission data.
*
* @return void
*/
function au_wpcf7_before_send_mail($contact_form, &$abort, $submission)
{
// We'll do something cool here
}
add_action('wpcf7_before_send_mail', 'au_wpcf7_before_send_mail', 10, 3);
This code runs when a form is submitted by a user successfully and it is about to send the email(s), so this is where we want to intercept it and modify our recipients.
Determine the scope/source of the data
Where is your dynamic email address saved? If you want to use the email address that belongs to the author of the post that the form was submitted on, or if the email address is saved in a custom field on the post that the form was submitted on, then the scope for this is the post and its post id is automatically passed with the form submission.
Alternatively, perhaps this email address is associated with a specific post, not the one that the form is on, or an option page created with Advanced Custom Fields. In this case, the scope is the form and we’ll need to assign that post id or option page to the form’s attributes.
Get the post id from the page the form is submitted on
If you need anything associated with the post or page the form was submitted from, like its author, metadata, etc., then we’ll need the post id. We also want to sanitize the value and ensure the user submitting the form has proper access to the post in the first place. So with that in mind, we can get the post id like this:
// Get the post id & sanitize it
$post_id = intval(sanitize_text_field($submission->get_meta('container_post_id')));
// Validate the post id & user permissions
$post_id = $post_id > 0 && // Is a positive integer
(is_post_publicly_viewable($_post_id) && !post_password_required($_post_id)) || // Allows publicly published posts of post types and status that are publicly visible that have no password or user can access the password protected post
current_user_can('edit_post', $_post_id) || // Allows anyone with edit access to this specific post (author, editor, admin, etc.)
(get_post_status($_post_id) == 'private' && current_user_can('read_private_posts')) // Allows anyone with private capability to read private post
? $post_id : 0;
// Bail if disallowed
if(!$post_id) return;
Get the post id from the form’s additional settings
Let’s say you have a single post or page with the metadata and want all forms to pull from that same post. To do that, using the additional settings is my personally preferred method since it does not disclose the post id on the frontend like a hidden field does. Instead of using a form tag in the form template, create a custom setting in the form using the fourth tab and add a line like this:
post_id: 123
Now let’s access the post id saved in the form’s setting from inside our function:
// Get the post id & sanitize it
$post_id = intval(sanitize_text_field(($contact_form->pref('post_id')));
// Validate the post id (see above)
// Bail if disallowed (see above)
Get email address
Just as we had to decide where we’d get the post id, we also have to decide where to get the email address. These examples show when an email address is saved in a post’s metadata or when you want to use that post author’s email address from their user account.
When the email address is saved as metadata to post
Now that we have the post id, let us look up the metadata associated with it. Perhaps you used Advanced Custom Fields to create an email address field with the field name email_address and each post as its own different value. Let’s grab the one this form was submitted on using this:
// Get & sanitize the email address
$email = sanitize_email(get_post_meta(
$post_id, // post id where the metadata exists
'email_address', // meta key or ACF field name/key
true
));
// Validate
if(!$email) return; // Bail if empty
When you want the post author’s email address from their user account
If we want to get the author’s email address, we’ll first need to get the author of the post and then their email address and can do it with this code snippet:
// Get author's user id associated with the post & sanitize
$author_id = intval(sanitize_text_field(get_post_field(
'post_author, / post field that holds author id
$post_id // post id where you want the author
)));
// Validate author id
if(!$author_id) return; // Bail on failure
// Get email address saved to author's user account & sanitize
$email = sanitize_email(get_the_author_meta('user_email', $author_id));
// Validate
if(!$email) return; // Bail if empty
Setting the email recipient
Now that we have a value for $email, it’s time to change out the recipient so they get it. If you want to completely overwrite the to field, just set it, otherwise we’ll need to play with merging the values if you want to simply add this new email address to the list of recipients already configured in the backend.
Contact Form 7 allows you to send up to two (2) different kinds of emails out-of-the-box. The first email is typically used for administration purposes, such as notifying the person who owns the form, while the second email is typically used as a thank-you email to send to the person who submitted the form. We can use the code snippet below to configure them.
$mail = $contact_form->prop('mail'); // Get the first mail property
$mail['recipient'] = $email; // Replace the recipient with our dynamic email address
$mail2 = $contact_form->prop('mail_2'); // Get the second mail property
if ($mail2['active']) {
// If the second email is active, set the "Reply To" to our dynamic email address
$mail2['additional_headers'] .= "Reply-To: {$email}\n";
}
Once you have it, you’re nearly done! Just set the properties back onto the contact form and you’ll be done!
// Set the mail properties with the updated values
$contact_form->set_properties(array(
'mail' => $mail,
'mail_2' => $mail2
));
Complete Code Snippet
I year ya, sometimes you don’t care for me praddlin’ on and just want to copy/paste a full code snippet. Well here you go, this will get the author’s email address of the post the form was submitted on and send it to them while also setting them as the submitter’s reply-to. Also less comments because you should know what your code does already 😉
/**
* Custom Email Behavior on Contact Form 7 Form Submission
*
* @param WPCF7_ContactForm $contact_form The current contact form.
* @param bool $abort If the submission is being aborted.
* @param WPCF7_Submission $submission The current submission data.
*
* @return void
*/
function au_wpcf7_before_send_mail($contact_form, &$abort, $submission)
{
// Post id
$post_id = intval(sanitize_text_field($submission->get_meta('container_post_id')));
$post_id = $post_id > 0 && // Is a positive integer
(is_post_publicly_viewable($_post_id) && !post_password_required($_post_id)) ||
current_user_can('edit_post', $_post_id) ||
(get_post_status($_post_id) == 'private' && current_user_can('read_private_posts')) ? $post_id : 0;
if(!$post_id) return; // Bail if disallowed
// Post author id
$author_id = intval(sanitize_text_field(get_post_field('post_author', $post_id)));
if(!$author_id) return; // Bail on failure
// Post author email address
$email = sanitize_email(get_the_author_meta('user_email', $author_id));
if(!$email) return; // Bail if empty
// Update email recipient and reply-to
$mail = $contact_form->prop('mail');
$mail['recipient'] = $email;
$mail2 = $contact_form->prop('mail_2'); // Get the second mail property
if ($mail2['active']) {
$mail2['additional_headers'] .= "Reply-To: {$email}\n";
}
$contact_form->set_properties(array('mail' => $mail, 'mail_2' => $mail2));
}
add_action('wpcf7_before_send_mail', 'au_wpcf7_before_send_mail', 10, 3);
