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.

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″.

To create the validation, we add WordPress filter code into Tools -> Add Actions and Filters provided by the “Add 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
  • Optionally change the error message
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
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 = 'email_form'; // Name of the form containing this field
    $fieldName = 'email_123'; // 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;
}
 
add_filter('wpcf7_validate_email*', 'my_validate_email', 10, 2);

 

If you field is not an email field, use ‘wpcf7_validate_text‘ instead of ‘wpcf7_validate_email‘, e.g.:

29
add_filter('wpcf7_validate_text*', 'my_validate_email', 10, 2);

(Thanks to a user for pointing this out)

  1. March 1st, 2013 at 12:51 | #1

    I am trying to check 2 fields: email and licenseplate. I there a way to do that?

  2. Michael Simpson
    March 1st, 2013 at 15:20 | #2

    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.

  3. March 1st, 2013 at 16:06 | #3

    Would you be able to give me a quick lesson on how to do this? I can get one or the other to work but not both.

    Thanks for you time.

    I will post this as a feature in the cf7 forum

  4. Michael Simpson
    March 1st, 2013 at 17:58 | #4

    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))

  5. March 1st, 2013 at 18:11 | #5

    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]

  6. March 1st, 2013 at 19:19 | #6

    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

  7. March 18th, 2013 at 18:45 | #7

    Another Question on this script.
    Is there away to ignore the case when comparing values in this script?

    Thanks

    Jeff

  8. Michael Simpson
    March 18th, 2013 at 21:39 | #8

    @Jeff Frey
    What case? I don’t follow.

  9. March 19th, 2013 at 10:12 | #9

    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.

    • Michael Simpson
      March 19th, 2013 at 12:56 | #10

      Try using regex in the filter:

      6
      
      $atts['filter'] = "$fieldName~~/$fieldValue/i";
  10. March 19th, 2013 at 13:24 | #11

    Thanks Michael,
    It works perfect.

    I appreciate all your help!

    Hope this helps others as well.

    Thanks again.

  11. Alberto
    April 1st, 2013 at 18:29 | #12

    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!!!

    • Michael Simpson
      April 5th, 2013 at 20:07 | #13

      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.

  12. jasnon
    September 13th, 2013 at 12:19 | #14

    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.

    • Michael Simpson
      September 14th, 2013 at 15:12 | #15

      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";
      }

  13. jasnon
    September 16th, 2013 at 19:45 | #16

    Worked perfect!

    Thanks again for the help, your support is always appreciated.

  14. kevinharter
    December 4th, 2013 at 12:47 | #17

    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!

    • Michael Simpson
      December 4th, 2013 at 14:32 | #18

      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”

  15. kevinharter
    December 4th, 2013 at 14:58 | #19

    Thank you for the “Holy Quick!” reply, Michael! This indeed gets me close to my goal. I appreciate your time.

  16. londonxn
    February 25th, 2014 at 12:34 | #20

    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

  17. Isidoros
    April 4th, 2014 at 06:15 | #21

    @Michael Simpson
    Hello Michael, is it possible to check two or more fields as a set?
    Thks for this great plugin

    • Michael Simpson
      April 4th, 2014 at 08:18 | #22

      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.

  18. Isidoros
    April 4th, 2014 at 11:07 | #23

    Accept my apologizes for not identifying the answer but your proposed link gave me some ideas. Thanks for the immediate response!

  1. No trackbacks yet.
You must be logged in to post a comment.