Wie kann ich ein Webformular programmgesteuert mit Ajax senden?

8

Ich arbeite an einer Ajax-Implementierung für das Webform-Submit auf Drupal 7. Ich konnte nichts Gutes finden hook, um den Webform-Submit-Button zu ändern und '#ajax' in das Formular einzufügen, also habe ich mir ein Drupal 6-Modul angesehen , das implementiert diese Funktionalität aus einem externen Skript.

Deshalb habe ich mich für mein eigenes Modul und JavaScript-Code entschieden, um eine Ajax-Post-Anfrage an einen benutzerdefinierten Menü-Rückruf zu senden, den ich hook_menu()in Drupal 7 definiert habe.

Der JavaScript-Teil funktioniert einwandfrei, aber ich habe Probleme beim programmgesteuerten Senden des Webformulars.

Hier ist mein JavaScript-Code:

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

Und mein Modulcode (basierend auf dem webform_ajax-Modul):

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

Wenn ich mein Formular abschicke, erhalte ich 500 Serverfehler.

Ich denke, die D6- und D7-Formular-APIs sind sehr unterschiedlich und ich bin mir nicht sicher, wo ich anfangen soll, damit dieser Code funktioniert. Ich habe versucht, es zu debuggen, aber ich kann nicht herausfinden, was die 500 Fehler erzeugt.

Ich verwende Webform 3 und das Modul, für das ich den Code verwendet habe, basiert ebenfalls auf Version 3 von Webform, jedoch für Drupal 6. Beide Module sollten jedoch dieselben Funktionen und Funktionen bieten. Erste Problemumgehung: Es kann von den Werten kommen, die ich übergebe und die nicht mit D7 form api kompatibel wären.

In meinem Log habe ich:

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

- BEARBEITEN -

Ich debugge jetzt Zeile für Zeile, am Ende könnte dieser Code es wert sein, ein D7-Modul zu werden;)

Ich habe in der D7-Dokumentation festgestellt , dass sich die Argumente von drupal_rebuild_form () von D6 geändert haben und dass die Argumente $form_statezu diesem Zeitpunkt nicht mehr leer sein können. Deshalb habe ich meinen Code folgendermaßen aktualisiert:

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

Jetzt versuche ich, das Äquivalent von drupal_execute () zu finden, das in D7 nicht mehr existiert.

- Bearbeiten (2) -

Ich habe es vor ein paar Tagen zum Laufen gebracht und komme zurück, um die Lösung zu teilen und vielleicht einige Ratschläge und Verbesserungsvorschläge zu erhalten.

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

Um noch einen Schritt weiter zu gehen, möchte ich jetzt die Fehler aus dem verarbeiteten Formular abrufen, damit ich sie mit dem json-Objekt zurücksenden kann. Irgendwelche Ideen ?

E. de Saint Chamas
quelle

Antworten:

4

Ich machte etwas Ähnliches und fand die Lösung von E. de Saint Chamas, hauptsächlich für mich zu arbeiten. Es gab jedoch einige Dinge, die ich hinzufügen musste:

Zuerst musste ich dies dem Array form_state hinzufügen, bevor ich das Formular verarbeitete

'method' => 'post',

Dann unten einige Anpassungen, um das Formular zu verarbeiten und ggf. Fehlermeldungen zurückzugeben:

  // 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'],
  ));

Ich bin mir nicht sicher, ob dies der beste Weg ist, aber ich fand, dass es für mich funktioniert hat. Natürlich möchten Sie möglicherweise einfach die Fehlermeldungen rendern und ein vollständig gerendertes Fehlermeldungsfeld zurückgeben. Außerdem können Sie die "Bestätigungsnachricht" aus dem Array $ form_state abrufen, um die Erfolgsmeldung über das zu steuern Webform-Benutzeroberfläche.

wesnick
quelle
Das ist großartig, aber ich bekomme immer wieder einen Fehler ($ form_state ['ausgeführt'] = False). Und drupal_get_messages enthält nichts ('Fehler'). Ich frage mich, wie ich das debuggen kann.
Cybertoast
Ich sollte klarstellen, dass ich versuche, über curl zu senden, wie curl -vvv -X POST -H "X-Requested-With: XMLHttpRequest" -d 'submit [contact_fullname] = mein% 20name & submit [contact_email] = test% 40example. com & submit [contact_message] = test% 20message '" localhost / fubar / 31 ". Der Inhalt wird gesendet und der form_state wird ausgefüllt, aber drupal_form_build () wird nicht ausgeführt / gesendet.
Cybertoast
-1

Sagen Sie mir, wenn ich falsch liege, aber da eine Webform-Übermittlung ein Knoten ist, erstellen Sie den Knoten nicht direkt programmgesteuert in Ihrem page callback(mit Feldvalidierung (oder vor der Übermittlung mit Javascript).

Es könnte so etwas sein

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
quelle
3
Tatsächlich sind Webform-Übermittlungen keine Knoten. Webform speichert Übermittlungen in eigenen Tabellen. Wir können also keinen neuen Knoten erstellen, um die Übermittlung hinzuzufügen. Außerdem möchte ich, dass der gesamte Webform-Validierungs-Workflow ausgelöst wird, sobald das Formular ausgeführt wird, damit die erforderlichen Felder usw. überprüft werden.
E. de Saint Chamas