Preventing Duplicate Submissions from CF7
Situation: You have users who submit forms but you don’t want them to submit more than once.
Solution: add a custom validation to a form field that uniquely identifies the submission.
This solution is for Contact Form 7. Contact Form 7 has a hook into which you can inject custom validation code. It is somewhat limited, so read carefully. (This solution is based on: Contact Form 7 Custom Validation)
Let’s assume that essentially what you need to do is to prevent users from submitting a form more than once with the same email address in it. In this example, we will put a validation on one particular form field and check if that email was submitted previously. We will ignore all other fields in this example.
The limitation to the Contact Form 7 field validation is that although we can put in code to validate a particular field, in that code we can’t know what form the submission is coming from. This means that if you set up a validation on a field named “email” then that validation will be run against all form submissions that have an “email” field. If you have more than one form with a field named “email” but you only want this validation run for one particular form, then you have a problem.
The best idea is to give a unique name to the email field in your form. Use this name only on this form and none other. In this example, we use “email_123”. The Contact Form 7 form definition for this example is:
Form Name: email_form
1 2 3 4 5 6 7 | <p>Your Name (required)<br /> [text* name] </p> <p>Your Email (required)<br /> [email* email_123] </p> <p>[submit "Send"]</p> |
To create the validation, we add WordPress filter code into Tools -> Shortcodes Actions and Filters using the Shortcodes Actions and Filters plugin. This is the same technique used in Changing Form Data Before it is Saved.
The following code is an example. You will need to make some changes to make it work for you. In the my_validate_email function:
- Change $formName to the name of your form
- Change $fieldName to the name of your email field
- Change $errorMessage an error message you like
NOTE: this code is now updated to work with changes made in CF7 version 4.1.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | /** * @param $formName string * @param $fieldName string * @param $fieldValue string * @return bool */ function is_already_submitted($formName, $fieldName, $fieldValue) { require_once(ABSPATH . 'wp-content/plugins/contact-form-7-to-database-extension/CFDBFormIterator.php'); $exp = new CFDBFormIterator(); $atts = array(); $atts['show'] = $fieldName; $atts['filter'] = "$fieldName=$fieldValue"; $atts['unbuffered'] = 'true'; $exp->export($formName, $atts); $found = false; while ($row = $exp->nextRow()) { $found = true; } return $found; } /** * @param $result WPCF7_Validation * @param $tag array * @return WPCF7_Validation */ function my_validate_email($result, $tag) { $formName = 'email_form'; // Change to name of the form containing this field $fieldName = 'email_123'; // Change to your form's unique field name $errorMessage = 'Email has already been submitted'; // Change to your error message $name = $tag['name']; if ($name == $fieldName) { if (is_already_submitted($formName, $fieldName, $_POST[$name])) { $result->invalidate($tag, $errorMessage); } } return $result; } // use the next line if your field is a **required email** field on your form add_filter('wpcf7_validate_email*', 'my_validate_email', 10, 2); // use the next line if your field is an **email** field not required on your form add_filter('wpcf7_validate_email', 'my_validate_email', 10, 2); // use the next line if your field is a **required text** field add_filter('wpcf7_validate_text*', 'my_validate_email', 10, 2); // use the next line if your field is a **text** field field not required on your form add_filter('wpcf7_validate_text', 'my_validate_email', 10, 2); |
I am trying to check 2 fields: email and licenseplate. I there a way to do that?
You can check them individually but not as a a set.
The CF7 validation is done independently for each individual field. This is “field level validation” and there is no available “form level validation.” I encourage you to post on the CF7 forum to request this feature.
To check both fields (independently)
1. make two copies of the my_validate_email function, giving each a different name.
In each:
1.1. Give each one a different $fieldName.
1.2. Update the error message
2. Provide 2 add_filter function calls, one to to register each of those functions (change the 2nd argument to function names in (1))
Thanks for the response. I tried it and it didn’t work, I think number 2 I am confused about.
Here is what I have:
[code]function is_already_submitted($formName, $fieldName, $fieldValue){
require_once(ABSPATH . ‘wp-content/plugins/contact-form-7-to-database-extension/CFDBFormIterator.php’);
$exp = new CFDBFormIterator();
$atts = array();
$atts[‘show’] = $fieldName;
$atts[‘filter’] = “$fieldName=$fieldValue”;
$exp->export($formName, $atts);
$found = false;
while ($row = $exp->nextRow()) {
$found = true;
}
return $found;
}
function my_validate_email($result, $tag) {
$formName = ‘Free Wash’; // Name of the form containing this field
$fieldName = ’email’; // Set to your form’s unique field name
$name = $tag[‘name’];
if($name == $fieldName){
$valueToValidate = $_POST[$name];
if (is_already_submitted($formName, $fieldName, $valueToValidate)) {
$result[‘valid’] = false;
$result[‘reason’][$name] = ‘Email has already been submitted’; // error message
}
}
return $result;
}
function my_validate_license($result, $tag) {
$formName = ‘Free Wash’; // Name of the form containing this field
$fieldName = ‘licenseplate’; // Set to your form’s unique field name
$name = $tag[‘name’];
if($name == $fieldName){
$valueToValidate = $_POST[$name];
if (is_already_submitted($formName, $fieldName, $valueToValidate)) {
$result[‘valid’] = false;
$result[‘reason’][$name] = ‘License Plate has already been submitted’; // error message
}
}
return $result;
}
add_filter(‘wpcf7_validate_email’, ‘my_validate_email’, 10, 2);
add_filter(‘wpcf7_validate_email’, ‘my_validate_license’, 10, 2);
[/code]
Figured it out, the above code is right except this line
add_filter(‘wpcf7_validate_email’, ‘my_validate_license’, 10, 2);
should be
add_filter(‘wpcf7_validate_text’, ‘my_validate_license’, 10, 2);
Thanks for the help
Another Question on this script.
Is there away to ignore the case when comparing values in this script?
Thanks
Jeff
@Jeff Frey
What case? I don’t follow.
One of the fields we are checking is a license plate number. We need when checked it doesn’t look at upper letter as different form lower case letters.
Sorry could have explained that better the first time.
Thanks for your help.
Try using regex in the filter:
Thanks Michael,
It works perfect.
I appreciate all your help!
Hope this helps others as well.
Thanks again.
Hi;
I want to validate the submission of only one national document number in the form.
I try to submit same document but the functions do not detect it.
I try with the default WP theme.
It is a required field, and I try with the following code too:
add_filter(‘wpcf7_validate_text*’, ‘my_validate_email’, 10, 2);
Anything else to try?
Thanks!!!
A thought: the “wpcf7_validate_text” is hooked into CF7 text fields. If you are trying to validate a different kind of field, then you might need a different hook name. Use the debug technique to print out something to see if your function is even called.
Hi Michael,
I’m glad I came across this post, this is exactly what I was looking for. I’m trying to take it a step further though and validate previous submissions for specific users only (preferrably the currently logged in user). I’m guessing this is something I need to add to the filter but I can’t figure out exactly what to add. I tried using the normal syntax from shortcodes but it didn’t seem to work. This is what I have currently:
$atts[‘filter’] = “$fieldName=$fieldValue&&Username=$user_login”;
Thanks as always for the help.
Try this:
if (is_user_logged_in()) {
$current_user = wp_get_current_user(); // WP_User object
$user = $current_user->user_login;
$atts['filter'] = "$fieldName=$fieldValue&&Submitted Login=$user";
}
Worked perfect!
Thanks again for the help, your support is always appreciated.
Thanks for the post, this helps tremendously! I have it working on my site, but I need to only limit the email being used once DURING THE CURRENT MONTH. Currently, it won’t let that email address to be used again EVER.
I’m trying to integrate a contest into CFDB. Essentially, I want to restrict entries to once a month, with the “reset” occurring at midnight on the first day of the month.
Any help is appreciated!
Try using something like this for the filter in is_already_submitted:
$atts['filter'] = "$fieldName=$fieldValue&&submit_time>last month";
That “submit_time>last month” identifies submissions from the last month (but not just in the current month) to exclude. I don’t think that is exactly what you want but should point you in the direction. See also: Filtering Submit Time and PHP strtotime which interprets English text like “last month”
Thank you for the “Holy Quick!” reply, Michael! This indeed gets me close to my goal. I appreciate your time.
Hello Michael, I am having issue with duplicate entries in Contact Form DB database. I am using this extension along side “Contact Form 7 email verification” which it is to allow for email addresses to be verified.
Each time when a form submitted, it writes to the database and then it duplicate it after person verified her/his email. Basically the submit button is triggered twice.
I have contacted with the developer of “Contact Form 7 email verification” but he confirmed that this is something that can be done from your side.
Would you please kindly help me with this and if there is a function to stop this double entry.
thanks
@Michael Simpson
Hello Michael, is it possible to check two or more fields as a set?
Thks for this great plugin
No because the CF7 validation hook is called independently for each individual field. This is called “field level validation”. As far as I know, CF7 lacks a “form level validation” hook that we could use to check more than one field together. For example, if you had “City”, “Zip” fields you can use field level validation to check that the zip was valid and the city was valid but not that the city and zip matched.
I posted this on the CF7 forum some time ago but it has not be addressed to my knowledge.
That being said, you can try editing the CF7 code to try to make this happen.
Accept my apologizes for not identifying the answer but your proposed link gave me some ideas. Thanks for the immediate response!
Hello Michael!
For long time ago I´ve been using CFDB making my programming easier, with this post I could add the functionality that my client wanted, and because of that, I´ve already donate to you and want to thank you for all the hours you´ve been working so hard, I just came here to say THANK YOU Michael, thanks not only for the time you spent programming but also for the time you take to sit and answer each message for helping all of us.
Warm regards from Mexico!
@Michael Simpson Ny chance to get the full code to use here? I tried replacing line 6 from your code with this, but it doesn’t seem to work. What should I use for `$fieldname` on line 17?
Lines 16 & 17 are the only one you should need to change.
I meant to use the filter on the logged in user and I’ve used the code
if (is_user_logged_in()) {
$current_user = wp_get_current_user(); // WP_User object
$user = $current_user->user_login;
$atts[‘filter’] = “$fieldName=$fieldValue&&Submitted Login=$user”;
}
from a previous comment of yours.
@reddo
If you have any further difficulty, please post on the support forum: http://wordpress.org/support/plugin/contact-form-7-to-database-extension
I think we misunderstood each other :(. I still need help with using the above code. I tried placing it in the place of row 6 of your original code, but I still can submit the same info twice from the form. I changed $formName to my form’s name, but I’m not sure what should I set for $fieldName. If you would be so kind to point me in the right direction, I would be very grateful.
Hi again. First of all, sorry for the amount of comment. I just wanted to say that I got it to work, it was my mistake, I used wpcf7_validate_text when I actually had a select field. switched to wpcf7_validate_select and all is well. Thank you for this great plugin. Rating it 5* right now!