summaryrefslogtreecommitdiff
path: root/sites/all/modules/multiform/multiform.module
blob: 1d3bb5031b95201c8c62fe876cf20cf3bd8eec8f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
<?php

/**
 * Returns a form containing a number of other forms.
 *
 * When the forms are submitted their first button is pressed in array order.
 * Make sure you rearrange the buttons in your form accordingly in a form_alter
 * when using this function.
 *
 * @param ...
 *   Every argument is a list of arguments to be passed to drupal_get_form.
 *   For example, if the first form is called as
 *   drupal_get_form($form_id1, $arg1, $arg2); and
 *   the second as drupal_get_form($form_id2, $arg3, $arg4) call
 *   multiform_get_form(array($form_id1, $arg1, $arg2), array($form_id2, $arg3, $arg4)).
 */
function multiform_get_form() {
  $all_args = func_get_args();
  $redirect = NULL;
  $form = element_info('form');
  $form['#id'] = 'multiform';
  $form['#attributes'] = array();
  // We need a $form_id so that the submission of other forms (like search) do
  // not disturb us.
  $form['form_id'] = array(
    '#type' => 'hidden',
    '#value' => 'multiform',
    '#id' => drupal_html_id("edit-multiform"),
    '#name' => 'form_id',
    '#attributes' => array(),
  );
  $build_id = 'form-' . drupal_random_key();
  // We need a $form_build_id because the buttons are cached and the values
  // belonging to them in $_POST are handed to each form so those can recognize
  // the buttons pressed.
  $form['form_build_id'] = array(
    '#type' => 'hidden',
    '#value' => $build_id,
    '#id' => $build_id,
    '#name' => 'form_build_id',
    '#attributes' => array(),
  );
  // This is where buttons will be collected.
  $form['buttons'] = array();
  $form['buttons']['#weight'] = 1000;
  $form['buttons']['#build_id'] = $build_id;
  $form_state_save = array();
  $button_names = array();
  // The only way to support $_GET would be to accept $form_state. Maybe later.
  if ($_POST && isset($_POST['form_id']) && $_POST['form_id'] == 'multiform' && !empty($_POST['form_build_id'])) {
    $form_state_save['input'] = $_POST;
    $_files_save = $_FILES;
    // Retrieve buttons.
    if ($button_elements = form_get_cache($_POST['form_build_id'], $form_state_save)) {
      foreach ($button_elements as $button) {
        // For each button, save it's name. Later on we will need the button
        // names because the buttons are used in the multiform but their values
        // in $_POST (if it exists) needs to be handed down to each form so
        // those can recognize the button press.
        $name = isset($button_elements['#name']) ? $button_elements['#name'] : 'op';
        $button_names[$name] = $name;
      }
    }
  }
  foreach ($all_args as $key => $args) {
    $form_id = array_shift($args);
    // Reset $form_state and disable redirection.
    $form_state = array('no_redirect' => TRUE);
    // This line is copied literally from drupal_get_form().
    $form_state['build_info']['args'] = $args;
    $index = $form_id . '_' . $key;
    if (isset($form_state_save['input']['multiform'][$index])) {
      // drupal_build_form() honors our $form_state['input'] setup.
      $form_state['input'] = $form_state_save['input']['multiform'][$index];
      // Pass in the information about pressed buttons too.
      foreach ($button_names as $name) {
        if (isset($form_state_save['input'][$name])) {
          $form_state['input'][$name] = $form_state_save['input'][$name];
        }
      }
    }
    if (isset($_files_save['multiform']['name'][$index])) {
      $_FILES = array();
      foreach (array('name', 'type', 'tmp_name', 'error', 'size') as $files_key) {
        // PHP is seriously messed up, dude.
        foreach ($_files_save['multiform'][$files_key][$index] as $running_out_of_indexes => $data) {
          $_FILES[$running_out_of_indexes][$files_key] = $data;
        }
      }
    }
    // Build and process this form.
    $current_form = drupal_build_form($form_id, $form_state);
    // Do not render the <form> tags. Instead we render the <form> as a <div>.
    $current_form['#theme_wrappers'] = array('container');
    _multiform_get_form($current_form, $form['buttons'], $index);
    // Unset any attributes specifics to form tags.
    $disallowed_attributes = array('enctype', 'action', 'method');
    $current_form['#attributes'] = array_diff_key($current_form['#attributes'], array_flip($disallowed_attributes));
    $form['multiform'][$index] = $current_form;
    if (isset($form_state['has_file_element'])) {
      $form['#attributes']['enctype'] = 'multipart/form-data';
    }
    // Keep the redirect from the first form.
    if (!$key) {
      $redirect = isset($form_state['redirect']) ? $form_state : array();
    }
  }
  form_set_cache($build_id, $form['buttons'], $form_state_save);
  if (!empty($form_state_save['input'])) {
    // We forced $form_state['no_redirect'] to TRUE above, so unset it in order
    // to allow the redirection to proceed.
    unset($redirect['no_redirect']);
    drupal_redirect_form($redirect);
  }
  return $form;
}

/**
 * Recursive helper for multiform_get_form().
 */
function _multiform_get_form(&$element, &$buttons, $form_id) {
  // Recurse.
  foreach (element_children($element) as $key) {
    _multiform_get_form($element[$key], $buttons, $form_id);
  }
  // Save but do not display buttons. Note that this is done before the #name
  // is changed. This way the buttons belong to the top form and their values
  // can be handed to each form.
  if (isset($element['#button_type'])) {
    $buttons[$element['#value']] = $element;
    $element['#access'] = FALSE;
  }
  // By only changing $element['#name'] form API is not affected but the
  // browser will put the element values into _POST where multiform_get_form
  // expects them.
  elseif (isset($element['#name'])) {
    // If the name was op then we want multiform[$form_id][op]. If it was
    // foo[bar] then we want multiform[$form_id][foo][bar].
    $element['#name'] = "multiform[$form_id]" . preg_replace('/^[^[]+/', '[\0]', $element['#name']);
  }
}