Comment soumettre un formulaire Web par programme avec Ajax?

8

Je travaille sur une implémentation Ajax pour la soumission Webform sur Drupal 7. Je n'ai pas pu trouver de bon hookpour modifier le bouton de soumission Webform et ajouter '#ajax' dans le formulaire, j'ai donc jeté un œil à un module Drupal 6 qui implémente cette fonctionnalité à partir d'un script externe.

J'ai donc décidé d'aller avec mon propre module et mon code JavaScript pour lancer une demande de publication Ajax vers un rappel de menu personnalisé que j'ai défini dans hook_menu(), dans Drupal 7.

La partie JavaScript fonctionne bien, mais je rencontre des problèmes pour envoyer le formulaire Web par programme.

Voici mon code JavaScript:

function formSubmit(event, formId) {

  event.preventDefault();

  var form = jQuery("#" + formId);
  var postData = form.serialize();
  var nodeId = formId.substring(20);
  var msg = '';

  msg += form.find('#edit-submitted-name').attr('value') ? '' : 'Please enter your name';
  console.log(form.find('#edit-submitted-name').attr('value'));
  console.log(form.find('#edit-submitted-e-mail').attr('value'));

  if(msg) {
    alert(msg);
  } else {
    jQuery.ajax({
      url: Drupal.settings.basePath + 'webform_ajax/' + nodeId,
      fid:formId,
      type: 'POST',
      data: postData,
      success: function(ajaxData) {
        console.log(ajaxData);
        console.log('Hello world');
        // can't get here
      }
    });
  }
}

Et mon code de module (basé sur le module webform_ajax):

function custom_menu() {
  $items = array();
  $items['webform_ajax/%'] = array(
    'page callback' => '_custom_webform_ajax',
    'page arguments' => array(1,2),
    'access callback' => '_custom_webform_ajax_access',
  );
  return $items;
}

function _custom_webform_ajax($nid, $data) {
  //$sid = $_POST['details']['sid'];

  $local_POST = $_POST;
  $form_build_id = $_POST['form_build_id'];

  $form_id = 'webform_client_form_' . $nid;

  $node = node_load($nid);

  $submission = array();
  $form_state = array();

  $form = form_get_cache($form_build_id, $form_state);
  $form_array = drupal_rebuild_form($form_id, $form_state, array($form_state, $node, $submission), $form_build_id);
  $form_state['clicked_button'] = $form_array['actions']['submit'];

  if (is_array($local_POST['submitted'])) {
    foreach ($local_POST['submitted'] as $submit_index => $submit) {
      $form_state['storage']['submitted'][$submit_index] = $submit;
      $form_state['values']['submitted'][$submit_index] = $submit;
    }
  }

  // Clearing empty values from $form_state
  if (is_array($form_state['values']['submitted'])) {
    foreach ($form_state['values']['submitted'] as $value_index => $value) {
      if (!$value) {
        unset($form_state['values']['submitted'][$value_index]);
      }
    }
  }

  // Executing the pressed button action
  drupal_execute($form_id, $form_state, $node, array());

  // Get the HTML for the error messages
  $error_html = theme('status_messages', 'error');

  // Building the resulting form after the processing of the button
  $form_array = drupal_rebuild_form($form_id, $form_state, array($form_state, $node, $submission), $form_build_id);
  $form = drupal_render_form($form_id, $form_array);

  return drupal_json_output(array(
    'message' => $error_html,
    'status' => 'sent',
  ));

}

function _custom_webform_ajax_access() {
  // Todo: Add webform access conditions
  return true;
}

Lorsque je soumets mon formulaire, j'obtiens 500 erreurs de serveur.

Je suppose que les API de formulaire D6 et D7 sont assez différentes et je ne sais pas par où commencer pour que ce morceau de code fonctionne. J'ai essayé de le déboguer mais je n'arrive pas à comprendre ce qui génère les 500 erreurs.

J'utilise webform 3 et le module dont j'ai pris le code repose également sur la version 3 de webform mais pour Drupal 6. Mais les deux modules devraient fournir les mêmes fonctions et le même type de fonctionnalités derrière. Première solution de contournement: cela peut provenir des valeurs que je passe qui ne seraient pas compatibles avec l'api de forme D7.

Dans mon journal, j'ai:

Argument 1 passed to drupal_array_nested_key_exists() must be an array, null given, called in D:\wamp\www\productionsite\includes\form.inc on line 1986 and defined in drupal_array_nested_key_exists() (line 6296 of D:\wamp\www\productionsite\includes\common.inc).

-- ÉDITER --

Je débogue ligne par ligne maintenant, à la fin ce morceau de code pourrait valoir la peine de devenir un module D7;)

J'ai trouvé dans la documentation D7 que les arguments drupal_rebuild_form () ont changé par rapport à D6, et que le $form_statene peut plus être vide à ce stade, j'ai donc mis à jour mon code de cette manière:

$form_state = array('submitted' => false, 'values' => array());
$form = form_get_cache($form_build_id, $form_state);
$form_array = drupal_rebuild_form($form_id, $form_state, $form);

Maintenant, j'essaie de trouver l'équivalent de drupal_execute (), qui n'existe plus en D7.

- Modifier (2) -

Je l'ai fait fonctionner il y a quelques jours et je reviens pour partager la solution, et peut-être obtenir des conseils et des suggestions d'améliorations.

<?php

function custom_menu() {
  $items = array();
  $items['webform_ajax/%'] = array(
    'page callback' => '_custom_webform_ajax',
    'page arguments' => array(1,2),
    'access callback' => '_custom_webform_ajax_access',
  );
  return $items;
}

function _custom_webform_ajax($nid, $data) {

  $local_POST = $_POST;
  $form_build_id = $_POST['form_build_id'];

  $form_id = 'webform_client_form_' . $nid;

  $node = node_load($nid);

  $submission = array();
  $form_state = array(
    'submitted' => false, 
    'values' => array(),
    'build_info' => array(
      'args' => array(
        $node,
        array(),
        FALSE
      )
    )
  );

  $form = form_get_cache($form_build_id, $form_state);
  $form_array = drupal_rebuild_form($form_id, $form_state);

  // Add the clicked button before processing the form
  $form_state['clicked_button'] = $form_array['actions']['submit'];

  if (is_array($local_POST['submitted'])) {
    foreach ($local_POST['submitted'] as $submit_index => $submit) {
      $form_state['values']['submitted'][$submit_index] = $submit;
    }
  }

  // Clearing empty values from $form_state
  if (is_array($form_state['values']['submitted'])) {
    foreach ($form_state['values']['submitted'] as $value_index => $value) {
      if (!$value) {
        unset($form_state['values']['submitted'][$value_index]);
      }
    }
  }

  $form_state['values']['details']['nid'] = $nid;

  // Executing the pressed button action
  drupal_build_form($form_id, $form_state);

  return drupal_json_output(array(
    'message' => t('Your submission has been received. Thank you for contacting us.'),
    'status' => 'sent',
  ));  

}

function _custom_webform_ajax_access() {
  // TODO: Add user role / perm check
  return true;
}

Pour aller plus loin, je voudrais maintenant obtenir les erreurs du formulaire traité afin que je puisse les renvoyer avec l'objet json. Des idées ?

E. de Saint Chamas
la source

Réponses:

4

Je faisais quelque chose de similaire et j'ai trouvé la solution d'E. De Saint Chamas pour travailler principalement pour moi. Cependant, je devais ajouter quelques éléments:

Tout d'abord, j'ai dû ajouter ceci au tableau form_state avant de traiter le formulaire

'method' => 'post',

Ensuite, vers le bas, quelques ajustements pour traiter le formulaire et renvoyer les messages d'erreur le cas échéant:

  // Prevent the form from redirecting the request
  $form_state['no_redirect'] = TRUE;
  // Executing the pressed button action
  drupal_build_form($form_id, $form_state);
  // See if the form submitted successfully
  if (!$form_state['executed']) {
    // If the form didn't submit successfully, get the errors
    // which are set bu drupal_set_message
    $messages = drupal_get_messages('error');
    $messages = implode('<br />', $messages['error']);
  }
  else {
    // If form submitted successfully, create a nice message.
    $messages = "Thanks for contacting us! We will let you know when the Beta is live!";
  }
  // drupal_json_output seems to confuse some browsers, who want to save as a file 
  print drupal_json_encode(array(
    'message' => $messages,
    'status' => $form_state['executed'],
  ));

Je ne sais pas si c'est la meilleure façon de le faire, mais j'ai trouvé que cela fonctionnait pour moi. Bien sûr, vous voudrez peut-être simplement continuer et afficher les messages d'erreur et renvoyer une boîte de message d'erreur entièrement rendue.En outre, vous pouvez extraire le "message de confirmation" du tableau $ form_state afin de pouvoir contrôler le message de réussite à partir de la interface utilisateur du formulaire Web.

Wesnick
la source
C'est super, mais j'obtiens toujours un échec ($ form_state ['execute'] = False). Et il n'y a rien dans drupal_get_messages ('erreur'). Je me demande comment je peux déboguer cela.
cybertoast
Je dois préciser que j'essaie de soumettre via curl, comme curl -vvv -X POST -H "X-Requested-With: XMLHttpRequest" -d 'soumis [contact_fullname] = mon% 20name & soumis [contact_email] = test% 40example. com & soumis [contact_message] = test% 20message '" localhost / fubar / 31 ". Le contenu est envoyé et le form_state est rempli, mais drupal_form_build () n'est pas exécuté / soumis.
cybertoast
-1

Dites-moi si je me trompe, mais comme une soumission de formulaire Web est un nœud, pourquoi ne pas créer directement le nœud par programmation dans votre page callback(avec validation de champ (ou pourrait le faire avant de soumettre en utilisant javascript))

Cela pourrait être quelque chose comme

if(!function_exists("node_object_prepare"))
{
  include_once(drupal_get_path('module', 'node') . '/node.pages.inc');
}
$node = new stdClass();                                                         
$node->is_new = TRUE;
$node->type = 'YOUR_NODE_TYPE_HERE';                                
node_object_prepare($node);

// then all the fields you need

node_validate($node);
$node = node_submit($node);
node_save($node);
$nid = $node->nid;

Et voilà ! :)

Gueno
la source
3
En fait, les soumissions de formulaires Web ne sont pas des nœuds. Webform stocke les soumissions dans ses propres tableaux. Nous ne pouvons donc pas créer un nouveau nœud pour ajouter la soumission. De plus je souhaiterais que l'ensemble du workflow de validation du formulaire web soit déclenché une fois le formulaire exécuté afin qu'il vérifie les champs obligatoires, etc ...
E. de Saint Chamas