multiform.module 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. <?php
  2. /**
  3. * Returns a form containing a number of other forms.
  4. *
  5. * When the forms are submitted their first button is pressed in array order.
  6. * Make sure you rearrange the buttons in your form accordingly in a form_alter
  7. * when using this function.
  8. *
  9. * @param ...
  10. * Every argument is a list of arguments to be passed to drupal_get_form.
  11. * For example, if the first form is called as
  12. * drupal_get_form($form_id1, $arg1, $arg2); and
  13. * the second as drupal_get_form($form_id2, $arg3, $arg4) call
  14. * multiform_get_form(array($form_id1, $arg1, $arg2), array($form_id2, $arg3, $arg4)).
  15. */
  16. function multiform_get_form() {
  17. $all_args = func_get_args();
  18. $redirect = NULL;
  19. $form = element_info('form');
  20. $form['#id'] = 'multiform';
  21. $form['#attributes'] = array();
  22. // We need a $form_id so that the submission of other forms (like search) do
  23. // not disturb us.
  24. $form['form_id'] = array(
  25. '#type' => 'hidden',
  26. '#value' => 'multiform',
  27. '#id' => drupal_html_id("edit-multiform"),
  28. '#name' => 'form_id',
  29. '#attributes' => array(),
  30. );
  31. $build_id = 'form-' . md5(uniqid(mt_rand(), TRUE));
  32. // We need a $form_build_id because the buttons are cached and the values
  33. // belonging to them in $_POST are handed to each form so those can recognize
  34. // the buttons pressed.
  35. $form['form_build_id'] = array(
  36. '#type' => 'hidden',
  37. '#value' => $build_id,
  38. '#id' => $build_id,
  39. '#name' => 'form_build_id',
  40. '#attributes' => array(),
  41. );
  42. // This is where buttons will be collected.
  43. $form['buttons'] = array();
  44. $form['buttons']['#weight'] = 1000;
  45. $form_state_save = array();
  46. $button_names = array();
  47. // The only way to support $_GET would be to accept $form_state. Maybe later.
  48. if ($_POST && $_POST['form_id'] == 'multiform' && !empty($_POST['form_build_id'])) {
  49. $form_state_save['input'] = $_POST;
  50. $_files_save = $_FILES;
  51. // Retrieve buttons.
  52. if ($button_elements = form_get_cache($_POST['form_build_id'], $form_state_save)) {
  53. foreach ($button_elements as $button) {
  54. // For each button, save it's name. Later on we will need the button
  55. // names because the buttons are used in the multiform but their values
  56. // in $_POST (if it exists) needs to be handed down to each form so
  57. // those can recognize the button press.
  58. $name = isset($button_elements['#name']) ? $button_elements['#name'] : 'op';
  59. $button_names[$name] = $name;
  60. }
  61. }
  62. }
  63. foreach ($all_args as $key => $args) {
  64. $form_id = array_shift($args);
  65. // Reset $form_state and disable redirection.
  66. $form_state = array('no_redirect' => TRUE);
  67. // This line is copied literally from drupal_get_form().
  68. $form_state['build_info']['args'] = $args;
  69. $index = $form_id . '_' . $key;
  70. if (isset($form_state_save['input']['multiform'][$index])) {
  71. // drupal_build_form() honors our $form_state['input'] setup.
  72. $form_state['input'] = $form_state_save['input']['multiform'][$index];
  73. // Pass in the information about pressed buttons too.
  74. foreach ($button_names as $name) {
  75. if (isset($form_state_save['input'][$name])) {
  76. $form_state['input'][$name] = $form_state_save['input'][$name];
  77. }
  78. }
  79. }
  80. if (isset($_files_save['multiform']['name'][$index])) {
  81. $_FILES = array();
  82. foreach (array('name', 'type', 'tmp_name', 'error', 'size') as $files_key) {
  83. // PHP is seriously messed up, dude.
  84. foreach ($_files_save['multiform'][$files_key][$index] as $running_out_of_indexes => $data) {
  85. $_FILES[$running_out_of_indexes][$files_key] = $data;
  86. }
  87. }
  88. }
  89. // Build and process this form.
  90. $current_form = drupal_build_form($form_id, $form_state);
  91. // Do not render the <form> tags. Instead we render the <form> as a <div>.
  92. $current_form['#theme_wrappers'] = array('container');
  93. _multiform_get_form($current_form, $form['buttons'], $index);
  94. // Unset any attributes specifics to form tags.
  95. $disallowed_attributes = array('enctype', 'action', 'method');
  96. $current_form['#attributes'] = array_diff_key($current_form['#attributes'], array_flip($disallowed_attributes));
  97. $form['multiform'][$index] = $current_form;
  98. if (isset($form_state['has_file_element'])) {
  99. $form['#attributes']['enctype'] = 'multipart/form-data';
  100. }
  101. // Keep the redirect from the first form.
  102. if (!$key) {
  103. $redirect = isset($form_state['redirect']) ? $form_state : array();
  104. }
  105. }
  106. form_set_cache($build_id, $form['buttons'], $form_state_save);
  107. if (!empty($form_state_save['input'])) {
  108. // We forced $form_state['no_redirect'] to TRUE above, so unset it in order
  109. // to allow the redirection to proceed.
  110. unset($redirect['no_redirect']);
  111. drupal_redirect_form($redirect);
  112. }
  113. return $form;
  114. }
  115. /**
  116. * Recursive helper for multiform_get_form().
  117. */
  118. function _multiform_get_form(&$element, &$buttons, $form_id) {
  119. // Recurse.
  120. foreach (element_children($element) as $key) {
  121. _multiform_get_form($element[$key], $buttons, $form_id);
  122. }
  123. // Save but do not display buttons. Note that this is done before the #name
  124. // is changed. This way the buttons belong to the top form and their values
  125. // can be handed to each form.
  126. if (isset($element['#button_type'])) {
  127. $buttons[$element['#value']] = $element;
  128. $element['#access'] = FALSE;
  129. }
  130. // By only changing $element['#name'] form API is not affected but the
  131. // browser will put the element values into _POST where multiform_get_form
  132. // expects them.
  133. elseif (isset($element['#name'])) {
  134. // If the name was op then we want multiform[$form_id][op]. If it was
  135. // foo[bar] then we want multiform[$form_id][foo][bar].
  136. $element['#name'] = "multiform[$form_id]" . preg_replace('/^[^[]+/', '[\0]', $element['#name']);
  137. }
  138. }