Prevenir envíos múltiples de formularios (lado del servidor)

9

Me encuentro con un problema en el que un usuario puede enviar cualquier formulario creado por la API del formulario varias veces (hacer clic rápidamente dando como resultado múltiples solicitudes).

He puesto la solución básica del lado del cliente (javascript) de deshabilitar el botón, pero tengo curiosidad por saber cuál es el mejor enfoque para prevenir esta situación en el lado del servidor.

¿Hay alguna forma recomendada de usar el sistema de token de formulario de Drupal para manejar esto? Especialmente una solución de formulario global (es decir, agregar un validador personalizado a cada formulario usando hook_form_alter ()).

Mi enfoque hasta ahora ha sido algo como esto:

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

Me encuentro con problemas donde el form_token no es exclusivo del formulario, parece que permanece igual pase lo que pase. Probablemente estoy malinterpretando cuál es el token en el gran esquema de la API de formulario.

Cualquier idea es apreciada!

PraderaHippo
fuente
Como seguimiento, comencé a usar $ form_state ['form_build_id'] en lugar del token. Si presento el mismo id. De compilación del formulario dos veces, en algún punto del camino el formulario se reconstruye y procesa de todos modos.
PrairieHippo

Respuestas:

8

Tuve exactamente el mismo problema y logré solucionarlo usando los mecanismos de bloqueo de Drupal

En la función de validación que utilicé:

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

Y en la función de envío, liberé el bloqueo:

function mymodule_custom_form_submit($form, &$form_state){
  // submit code
  lock_release('your_custom_lock_name');
}
Marius Ilie
fuente
1

Aquí debe considerar el peso del módulo:

  1. Un módulo (let first_module) que debe tener el peso negativo máximo del módulo (puede ser -2000) aquí debe implementar hook_form_alter () con el siguiente código. Ahora debe verificar si el formulario ya ha sido enviado o no por su código.
   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. El segundo módulo que tiene un peso de valor positivo más alto. Aquí debe desarmar la sesión agregando la devolución de llamada de envío a un módulo

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

}
sathishkumar
fuente
1

Si desea esta funcionalidad en todos los formularios y más control sin codificación, eche un vistazo al módulo Ocultar botón de envío .

caracteristicas:

  1. Ocultar o deshabilitar el botón Enviar después de hacer clic
  2. Mostrar un mensaje y / o imagen mientras espera
Gokul NK
fuente
55
El módulo Ocultar botón Submt no es una solución del lado del servidor. De la descripción del módulo: "Para los navegadores con Javascript deshabilitado, este módulo no tendrá ningún efecto". drupal.org/project/hide_submit
Blake Frederick
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);',
  ),
);

Espero que esto ayude.

o puede referirse Evitar múltiples clics del botón Enviar y Drupal tiene un módulo Ocultar botón Enviar

Algunos usuarios hacen clic accidentalmente en el botón Enviar más de una vez mientras esperan que se guarde su publicación. En algunos casos, esto puede generar publicaciones duplicadas u órdenes de comercio electrónico duplicadas.

madhurjya
fuente
-1

Este era mi problema también antes. Mi solución a esto es desactivar el botón a través de JS.

.módulo:

/**
 * 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();
    });
  }
});
}
ninjascorner
fuente