=== modified file 'includes/common.inc' --- includes/common.inc 2009-03-30 05:13:45 +0000 +++ includes/common.inc 2009-04-04 14:46:09 +0000 @@ -3368,6 +3368,15 @@ } } + // Add additional CSS and JavaScript files associated with this element. + foreach (array('css', 'js') as $kind) { + if (!empty($elements['#attached_' . $kind]) && is_array($elements['#attached_' . $kind])) { + foreach ($elements['#attached_' . $kind] as $arguments) { + call_user_func_array('drupal_add_' . $kind, is_array($arguments) ? $arguments : array($arguments)); + } + } + } + // Get the children of the element, sorted by weight. $children = element_children($elements, TRUE); @@ -3750,6 +3759,9 @@ 'text_format_wrapper' => array( 'arguments' => array('element' => NULL), ), + 'vertical_tabs' => array( + 'arguments' => array('element' => NULL), + ), ); } === modified file 'includes/form.inc' --- includes/form.inc 2009-03-30 03:15:40 +0000 +++ includes/form.inc 2009-04-04 17:48:42 +0000 @@ -227,6 +227,7 @@ 'method' => 'post', 'rerender' => TRUE, 'programmed' => FALSE, + 'groups' => array(), ); } @@ -939,7 +940,8 @@ /** * Walk through the structured form array, adding any required * properties to each element and mapping the incoming $_POST - * data to the proper elements. + * data to the proper elements. Also, execute any #process handlers + * attached to a specific element. * * @param $form_id * A unique string identifying the form for validation, submission, @@ -972,9 +974,22 @@ } } + if (!isset($form['#id'])) { + $form['#id'] = form_clean_id('edit-' . implode('-', $form['#parents'])); + } if (isset($form['#input']) && $form['#input']) { _form_builder_handle_input_element($form_id, $form, $form_state, $complete_form); } + // Allow for elements to expand to multiple elements, e.g., radios, + // checkboxes and files. + if (isset($form['#process']) && !$form['#processed']) { + foreach ($form['#process'] as $process) { + if (drupal_function_exists($process)) { + $form = $process($form, $form_state, $complete_form); + } + } + $form['#processed'] = TRUE; + } $form['#defaults_loaded'] = TRUE; // We start off assuming all form elements are in the correct order. @@ -1062,8 +1077,7 @@ /** * Populate the #value and #name properties of input elements so they - * can be processed and rendered. Also, execute any #process handlers - * attached to a specific element. + * can be processed and rendered. */ function _form_builder_handle_input_element($form_id, &$form, &$form_state, $complete_form) { if (!isset($form['#name'])) { @@ -1080,9 +1094,6 @@ } array_unshift($form['#parents'], $name); } - if (!isset($form['#id'])) { - $form['#id'] = form_clean_id('edit-' . implode('-', $form['#parents'])); - } if (!empty($form['#disabled'])) { $form['#attributes']['disabled'] = 'disabled'; @@ -1151,16 +1162,6 @@ } } } - // Allow for elements to expand to multiple elements, e.g., radios, - // checkboxes and files. - if (isset($form['#process']) && !$form['#processed']) { - foreach ($form['#process'] as $process) { - if (drupal_function_exists($process)) { - $form = $process($form, isset($edit) ? $edit : NULL, $form_state, $complete_form); - } - } - $form['#processed'] = TRUE; - } form_set_value($form, $form['#value'], $form_state); } @@ -1597,6 +1598,7 @@ $element['#attributes']['class'] .= ' collapsed'; } } + $element['#attributes']['id'] = $element['#id']; return '
\n"; } @@ -2248,6 +2250,121 @@ } /** + * Adds fieldsets to the specified group or adds group members to this + * fieldset. + * + * @param $element + * An associative array containing the properties and children of the + * fieldset. + * @param $form_state + * The $form_state array for the form this fieldset belongs to. + * + * @return + * The processed element. + */ +function form_process_fieldset(&$element, &$form_state) { + $parents = implode('][', $element['#parents']); + + // Add this fieldset to a group if one is set and if it's not being + // added to itself. + if (isset($element['#group']) && $element['#group'] != $parents) { + if (isset($form_state['groups'][$element['#group']]) && !empty($form_state['groups'][$element['#group']]['#group_exists'])) { + // Trick drupal_render() into believing this has already been output. + // The group widget will rerender this later. It's only set to + // #printed if the group already exists. That way, we can be sure + // that the fieldset /will/ be rendered later. + $element['#printed'] = TRUE; + } + + // Store a reference to this fieldset for the vertical tabs processing function. + $form_state['groups'][$element['#group']][] = &$element; + } + + // Each fieldset can be a group itself and gets a reference to all + // elements in its group. + $form_state['groups'][$parents]['#group_exists'] = TRUE; + foreach (element_children($form_state['groups'][$parents]) as $key) { + $form_state['groups'][$parents][$key]['#printed'] = TRUE; + } + $element['#group_members'] = &$form_state['groups'][$parents]; + + // Contains form element summary functionalities. + drupal_add_js('misc/form.js', array('weight' => JS_LIBRARY + 1)); + + return $element; +} + +/** + * Adds members of this group as actual elements for rendering. + * + * @param $element + * An associative array containing the properties and children of the + * fieldset. + * + * @return + * The modified element with all group members. + */ +function form_pre_render_fieldset($element) { + if (!empty($element['#group_members'])) { + // Add the group members to this fieldset for rendering purposes only. + foreach (element_children($element['#group_members']) as $key) { + // This was set in form_process_fieldset so that fieldsets which are + // added to groups are not rendered at their original location. + // drupal_render_children() will set this back to TRUE. + unset($element['#group_members'][$key]['#printed']); + $element[] = &$element['#group_members'][$key]; + } + + // Resort the element's children after the group members have been added. + $element['#sorted'] = FALSE; + } + + return $element; +} + +/** + * Creates a group formatted as vertical tabs. + * + * @param $element + * An associative array containing the properties and children of the + * fieldset. + * + * @return + * The processed element. + */ +function form_process_vertical_tabs($element) { + $element['group'] = array( + '#type' => 'fieldset', + '#theme_wrapper' => '', + '#parents' => $element['#parents'], + ); + + // The JavaScript stores the currently selected tab in this hidden + // field so that the active tab can be restored the next time the + // form is rendered, e.g. on preview pages or when form validation + // fails. + $name = implode('__', $element['#parents']); + $element[$name . '__active_tab'] = array( + '#type' => 'hidden', + '#default_value' => isset($element['#default_tab']) ? $element['#default_tab'] : '', + '#attributes' => array('class' => 'vertical-tabs-active-tab'), + ); + + return $element; +} + +/** + * Makes the element's children fieldsets be vertical tabs. + */ +function theme_vertical_tabs(&$element) { + // Add required JavaScript and Stylesheet. + drupal_add_js('misc/vertical-tabs.js', array('weight' => JS_DEFAULT - 1)); + drupal_add_css('misc/vertical-tabs.css'); + + return '