Impedir vários envios de formulário (lado do servidor)

9

Estou com um problema no qual um usuário pode enviar qualquer formulário criado pela API do formulário várias vezes (clique rápido resultando em várias solicitações).

Eu coloquei a solução básica do cliente (javascript) para desativar o botão, mas estou curioso para saber qual é a melhor abordagem para evitar essa situação no lado do servidor.

Existe uma maneira recomendada de usar o sistema de token de formulário do Drupal para lidar com isso? Especialmente uma solução global de formulários (por exemplo, adicionando um validador personalizado a todos os formulários usando hook_form_alter ()).

Minha abordagem até agora tem sido algo assim:

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

Estou com problemas em que o form_token não é exclusivo do formulário - parece permanecer o mesmo, aconteça o que acontecer. Provavelmente estou entendendo mal qual é o token no grande esquema da API de formulário.

Qualquer insight é apreciado!

PrairieHippo
fonte
Como acompanhamento, comecei a usar $ form_state ['form_build_id'] em vez do token. Se eu enviar o mesmo ID de construção do formulário duas vezes, em algum lugar ao longo do caminho, o formulário será reconstruído e processado de qualquer maneira.
PrairieHippo

Respostas:

8

Eu tive exatamente o mesmo problema e consegui corrigi-lo usando os mecanismos de bloqueio do Drupal

Na função validate, usei:

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

E na função de envio, soltei o bloqueio:

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

Aqui você deve considerar o peso do módulo:

  1. Um módulo (deixe first_module) que deve ter o valor máximo negativo do peso do módulo (pode ser -2000) aqui deve implementar hook_form_alter () com o código a seguir. Agora você deve verificar se o formulário já foi enviado ou não pelo seu 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. O second_module com peso potencial mais alto.Aqui você deve desmarcar a sessão adicionando o retorno de chamada de envio a um módulo

função 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
fonte
1

Se você deseja essa funcionalidade em todos os formulários e mais controle sem codificação, dê uma olhada no módulo Hide Submit Button .

Recursos:

  1. Oculte ou desative o botão enviar depois que ele foi clicado
  2. Exibir uma mensagem e / ou imagem enquanto aguarda
Gokul NK
fonte
5
O módulo Ocultar botão de envio não é uma solução do lado do servidor. Na descrição do módulo: "Para navegadores com Javascript desativado, este módulo não terá nenhum efeito." 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 isso ajude ..

ou você pode consultar Impedindo vários cliques no botão enviar e o drupal possui um módulo Ocultar botão enviar

Alguns usuários clicam acidentalmente no botão enviar mais de uma vez enquanto esperam que a postagem seja salva. Em alguns casos, isso pode resultar em lançamentos duplicados ou pedidos duplicados de comércio eletrônico.

madhurjya
fonte
-1

Este foi o meu problema também antes. Minha solução para isso é desativar o botão via 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
fonte