10

I'm running into a problem where a user can submit any form built by the Form API multiple times (fast clicking resulting in multiple requests).

I've put in the basic client side (javascript) solution of disabling the button, but I'm curious what the best approach to preventing this situation is on the server side.

Is there a recommended way to use Drupal's form token system to handle this? Especially a global form solution (ie, adding a custom validator to every form using hook_form_alter()).

My approach so far has been something like this:

function mymodule_form_alter(&$form, &$form_state, $form_id) {
  $form['#validate'][] = 'mymodule_form_validate';
}

function mymodule_form_validate(&$form, &$form_state){
  //initialize form array
  if (!isset($_SESSION['submitted_forms'])){
    $_SESSION['submitted_forms'] = array();
  }

  $form_token = $form_state['values']['form_token'];
  if ( isset($_SESSION['submitted_forms'][$form_token]) && $_SESSION['submitted_forms'][$form_token] = TRUE ){
    form_set_error('name]', 'This form has already been submitted');
  }
  else{
    $_SESSION['submitted_forms'][$form_token] = TRUE;
  }
}

I'm running into trouble where the form_token isn't unique to the form - it seems to stay the same no matter what happens. I'm probably misunderstanding what the token is in the grand scheme of the form api.

Any insight is appreciated!

1
  • As a follow up, I started using $form_state['form_build_id'] instead of the token. If I submit the same form build id twice, somewhere along the way the form get's rebuilt and processed anyways. Commented Feb 10, 2012 at 17:22

6 Answers 6

8

I had the exact same problem and managed to fix it using the Locking mechanisms from Drupal

In the validate function I used:

function mymodule_custom_form_validate($form, &$form_state){
  if (lock_acquire('your_custom_lock_name')) {
    // long operations here
  } else {
    form_set_error("", t("You submitted this form already."));
  }
}

And in the submit function I released the lock:

function mymodule_custom_form_submit($form, &$form_state){
  // submit code
  lock_release('your_custom_lock_name');
}
1
  • This blocks multiple threads from simultaneously handling form submits. But it is possible for the user who spams heavily on a submit button to still successfully create subsequent form submits even with this lock logic. In my case I had to use this lock as well as a database query to check if the form submit had already happened previously. Commented Sep 19 at 21:26
1

Here you should consider the module weight:

  1. One module (let first_module) which should have the module weight negative maximum_value (may be -2000) here it should implement hook_form_alter() with the following code.Now you should verify whether the form is already submitted or not by your code.
   function first_module_form_alter(&$form, &$form_state, $form_id)
    {
      $form['#validate'][] = 'mymodule_form_validate';
    }
function mymodule_form_validate(&$form, &$form_state){
  //a($form_state);
  //initialize form array
  if (!isset($_SESSION['submitted_forms'])){
    $_SESSION['submitted_forms'] = array();
  }

  $form_token = $form_state['values']['form_id'];
  if ( isset($_SESSION['submitted_forms'][$form_token]) && $_SESSION['submitted_forms'][$form_token] = TRUE ){
    form_set_error('name]', 'This form has already been submitted');
  }
  else{
    $_SESSION['submitted_forms'][$form_token] = TRUE;
  }
}
  1. The second_module which the have weight of possitive higher value.Here you should unset the session by adding the submit callback to a module

function second_module_form_alter(&$form, &$form_state, $form_id) { $form['#submit'][] = 'mymodule_form_submit'; }

function mymodule_form_submit(&$form, &$form_state){

  $form_token = $form_state['values']['form_id'];
  unset($_SESSION['submitted_forms'][$form_token]);

}
1

If you want this functionality on all the forms and more control without coding take a look at Hide Submit Button module.

Features:

  1. Hide or disable the submit button after it has been clicked
  2. Display a message and/or image while waiting
1
  • 5
    The Hide Submt Button module is not a server side solution. From the module's description: "For browsers with Javascript disabled this module will have no effect at all." drupal.org/project/hide_submit Commented Dec 8, 2015 at 1:06
0
$form['submit'] = array(
  '#type' => 'submit',
  '#value' => t('Save'),
  '#attributes' => array(
    'onclick' => 'javascript:var s=this;setTimeout(function(){s.value="Saving...";s.disabled=true;},1);',
  ),
);

hope this will help..

or you can refer Preventing multiple clicks of submit button & drupal has one module Hide submit button

Some users accidentally click the submit button more than once while waiting for their post to be saved. In some cases this may result duplicate postings or duplicate e-commerce orders.

0

Here is server-side multiple submit validation implementation available in the module for the Drupal 8 and Drupal 9 https://www.drupal.org/project/pmfs

-1

This was my problem also before. My solution to this is to disable the button via JS.

.module:

/**
 * Implementation of hook_init().
 */
function myModule_init(){
if (arg(0) == 'node' && (arg(2) == 'edit' || arg(1) == 'add')) {
    //hide btn when clicked on article nodes
    drupal_add_js(drupal_get_path('module', myModule') . '/js/disable-submit.js');
}

JS:

Drupal.behaviors.module_disable_submit = function (context) {

/* 
 * Disable keypress on form fields.
 * Prevent browser to reload when pressing enter in input fields 
 */


$('.buttons input:submit').click(function() {
  $('.buttons input:submit').hide();
  $('#node-form .buttons').prepend('<input type="submit" style="margin:1px 0; box-shadow:0 1px 1px #DDDDDD; border-radius:3px 3px 3px 3px; background:url(/sites/all/themes/rubik/images/bleeds.png) repeat-x scroll 0 -41px #F4F4F4; border-color:#DDDDDD #DDDDDD #CCCCCC; border-style:solid; border-width:1px; color:#B8A98F; cursor:default; font-weight:normal; padding:2px 10px; text-align:center;" value="Saving..." name="op" onclick="return false;" />');
  if ('.buttons input:submit') {
    $('.buttons input:submit').keypress(function() {
      $('.buttons input:submit').parents("form").submit();
      $('.buttons input:submit').hide();
    });
  }
});
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.