summaryrefslogtreecommitdiff
path: root/modules/field/field.form.inc
diff options
context:
space:
mode:
authorAngie Byron <webchick@24967.no-reply.drupal.org>2009-02-05 03:42:58 +0000
committerAngie Byron <webchick@24967.no-reply.drupal.org>2009-02-05 03:42:58 +0000
commitf3ed3283db182dfbbf35f5db077ebce2bc368bfe (patch)
treef2581841cbca28f3aebce26ee331ac3c72dd20de /modules/field/field.form.inc
parent8bac2dd319a42cade43b06218cbef31bf8f4867d (diff)
downloadbrdo-f3ed3283db182dfbbf35f5db077ebce2bc368bfe.tar.gz
brdo-f3ed3283db182dfbbf35f5db077ebce2bc368bfe.tar.bz2
#361683 follow-up by sun: Remove more windows line endings.
Diffstat (limited to 'modules/field/field.form.inc')
-rw-r--r--modules/field/field.form.inc817
1 files changed, 409 insertions, 408 deletions
diff --git a/modules/field/field.form.inc b/modules/field/field.form.inc
index 041fbabbe..dff18fb2c 100644
--- a/modules/field/field.form.inc
+++ b/modules/field/field.form.inc
@@ -1,408 +1,409 @@
-<?php
-// $Id$
-// TODO : merge with field.default.inc ?
-
-/**
- * Create a separate form element for each field.
- */
-function field_default_form($obj_type, $object, $field, $instance, $items, &$form, &$form_state, $get_delta = NULL) {
- // This could be called with no object, as when a UI module creates a
- // dummy form to set default values.
- if ($object) {
- list($id, ,) = field_attach_extract_ids($obj_type, $object);
- }
- $addition = array();
-
- $field_name = $field['field_name'];
-
- // If the field is not accessible, don't add anything. The field value will
- // be left unchanged on update, or considered empty on insert.
- // TODO : if/when field_attach_insert() takes care of default values,
- // unaccessible fields will automatically get the default value on insert.
- if (!field_access('edit', $field)) {
- return $addition;
- }
-
- // Put field information at the top of the form, so that it can be easily
- // retrieved.
- // Note : widgets and other form handling code should *always* fetch
- // field and instance information from $form['#fields'] rather than from
- // field_info_field(). This lets us build forms for 'variants' of a field,
- // for instance on admin screens.
- $form['#fields'][$field_name] = array(
- 'field' => $field,
- 'instance' => $instance,
- );
- // TODO : why do we need this ?
- $form['#cache'] = FALSE;
-
- // Populate widgets with default values if we're creating a new object.
- if (empty($items) && empty($id) && !empty($instance['default_value_function'])) {
- $items = array();
- $function = $instance['default_value_function'];
- if (drupal_function_exists($function)) {
- $items = $function($obj_type, $object, $field, $instance);
- }
- }
-
- $form_element = array();
-
- // If field module handles multiple values for this form element,
- // and we are displaying an individual element, process the multiple value
- // form.
- if (!isset($get_delta) && field_behaviors_widget('multiple values', $instance) == FIELD_BEHAVIOR_DEFAULT) {
- $form_element = field_multiple_value_form($field, $instance, $items, $form, $form_state);
- }
- // If the widget is handling multiple values (e.g Options),
- // or if we are displaying an individual element, just get a single form
- // element and make it the $delta value.
- else {
- $delta = isset($get_delta) ? $get_delta : 0;
- $function = $instance['widget']['module'] . '_field_widget';
- if (drupal_function_exists($function)) {
- if ($element = $function($form, $form_state, $field, $instance, $items, $delta)) {
- $defaults = array(
- '#required' => $get_delta > 0 ? FALSE : $instance['required'],
- '#columns' => array_keys($field['columns']),
- '#title' => check_plain(t($instance['label'])),
- '#description' => field_filter_xss($instance['description']),
- '#delta' => $delta,
- '#field_name' => $field['field_name'],
- '#bundle' => $instance['bundle'],
- );
- $element = array_merge($element, $defaults);
- // If we're processing a specific delta value for a field where the
- // field module handles multiples, set the delta in the result.
- // For fields that handle their own processing, we can't make assumptions
- // about how the field is structured, just merge in the returned value.
- if (field_behaviors_widget('multiple values', $instance) == FIELD_BEHAVIOR_DEFAULT) {
- $form_element[$delta] = $element;
- }
- else {
- $form_element = $element;
- }
- }
- }
- }
-
- if ($form_element) {
- $defaults = array(
- '#field_name' => $field['field_name'],
- '#tree' => TRUE,
- '#weight' => $instance['weight'],
- );
-
- $addition[$field['field_name']] = array_merge($form_element, $defaults);
- $form['#fields'][$field['field_name']]['form_path'] = array($field['field_name']);
- }
-
- return $addition;
-}
-
-/**
- * Special handling to create form elements for multiple values.
- *
- * Handles generic features for multiple fields:
- * - number of widgets
- * - AHAH-'add more' button
- * - drag-n-drop value reordering
- */
-function field_multiple_value_form($field, $instance, $items, &$form, &$form_state) {
- $field = field_info_field($instance['field_name']);
- $field_name = $field['field_name'];
-
- switch ($field['cardinality']) {
- case FIELD_CARDINALITY_UNLIMITED:
- $filled_items = field_set_empty($field, $items);
- $current_item_count = isset($form_state['field_item_count'][$field_name])
- ? $form_state['field_item_count'][$field_name]
- : count($items);
- // We always want at least one empty icon for the user to fill in.
- $max = ($current_item_count > count($filled_items))
- ? $current_item_count - 1
- : $current_item_count;
-
- break;
- default:
- $max = $field['cardinality'] - 1;
- break;
- }
-
- $title = check_plain(t($instance['label']));
- $description = field_filter_xss(t($instance['description']));
-
- $form_element = array(
- '#theme' => 'field_multiple_value_form',
- '#multiple' => $field['cardinality'],
- '#title' => $title,
- '#required' => $instance['required'],
- '#description' => $description,
- );
-
- $function = $instance['widget']['module'] . '_field_widget';
- if (drupal_function_exists($function)) {
- for ($delta = 0; $delta <= $max; $delta++) {
- if ($element = $function($form, $form_state, $field, $instance, $items, $delta)) {
- $multiple = $field['cardinality'] > 1 || $field['cardinality'] == FIELD_CARDINALITY_UNLIMITED;
- $defaults = array(
- '#title' => $multiple ? '' : $title,
- '#description' => $multiple ? '' : $description,
- '#required' => $delta == 0 && $instance['required'],
- '#weight' => $delta,
- '#delta' => $delta,
- '#columns' => array_keys($field['columns']),
- '#field_name' => $field_name,
- '#bundle' => $instance['bundle'],
- );
-
- // Add an input field for the delta (drag-n-drop reordering), which will
- // be hidden by tabledrag js behavior.
- if ($multiple) {
- // We name the element '_weight' to avoid clashing with column names
- // defined by field modules.
- $element['_weight'] = array(
- '#type' => 'weight',
- '#delta' => $max, // this 'delta' is the 'weight' element's property
- '#default_value' => isset($items[$delta]['_weight']) ? $items[$delta]['_weight'] : $delta,
- '#weight' => 100,
- );
- }
-
- $form_element[$delta] = array_merge($element, $defaults);
- }
- }
-
- // Add AHAH add more button, if not working with a programmed form.
- if ($field['cardinality'] == FIELD_CARDINALITY_UNLIMITED && empty($form['#programmed'])) {
- // Make sure the form is cached so ahah can work.
- $form['#cache'] = TRUE;
- $bundle_name_url_str = str_replace('_', '-', $instance['bundle']);
- $field_name_url_str = str_replace('_', '-', $field_name);
-
- $form_element[$field_name . '_add_more'] = array(
- '#type' => 'submit',
- '#name' => $field_name . '_add_more',
- '#value' => t('Add another item'),
- '#weight' => $instance['weight'] + $max + 1,
- // Submit callback for disabled JavaScript.
- '#submit' => array('field_add_more_submit'),
- '#ahah' => array(
- 'path' => 'field/js_add_more/' . $bundle_name_url_str . '/' . $field_name_url_str,
- 'wrapper' => $field_name_url_str . '-items',
- 'method' => 'replace',
- 'effect' => 'fade',
- ),
- // When JS is disabled, the field_add_more_submit handler will find
- // the relevant field using these entries.
- '#field_name' => $field_name,
- '#bundle' => $instance['bundle'],
- );
-
- // Add wrappers for the fields and 'more' button.
- $form_element['#prefix'] = '<div class="clear-block" id="' . $field_name_url_str . '-add-more-wrapper"><div id="' . $field_name_url_str . '-items">';
- $form_element[$field_name . '_add_more']['#prefix'] = '<div class="field-add-more">';
- $form_element[$field_name . '_add_more']['#suffix'] = '</div></div></div>';
- }
- }
- return $form_element;
-}
-
-/**
- * Theme an individual form element.
- *
- * Combine multiple values into a table with drag-n-drop reordering.
- * TODO : convert to a template.
- */
-function theme_field_multiple_value_form($element) {
- $output = '';
-
- if ($element['#multiple'] > 1 || $element['#multiple'] == FIELD_CARDINALITY_UNLIMITED) {
- $table_id = $element['#field_name'] . '_values';
- $order_class = $element['#field_name'] . '-delta-order';
- $required = !empty($element['#required']) ? '<span class="form-required" title="' . t('This field is required. ') . '">*</span>' : '';
-
- $header = array(
- array(
- 'data' => t('!title: !required', array('!title' => $element['#title'], '!required' => $required)),
- 'colspan' => 2
- ),
- t('Order'),
- );
- $rows = array();
-
- // Sort items according to '_weight' (needed when the form comes back after
- // preview or failed validation)
- $items = array();
- foreach (element_children($element) as $key) {
- if ($key !== $element['#field_name'] . '_add_more') {
- $items[] = &$element[$key];
- }
- }
- usort($items, '_field_sort_items_value_helper');
-
- // Add the items as table rows.
- foreach ($items as $key => $item) {
- $item['_weight']['#attributes']['class'] = $order_class;
- $delta_element = drupal_render($item['_weight']);
- $cells = array(
- array('data' => '', 'class' => 'field-multiple-drag'),
- drupal_render($item),
- array('data' => $delta_element, 'class' => 'delta-order'),
- );
- $rows[] = array(
- 'data' => $cells,
- 'class' => 'draggable',
- );
- }
-
- $output .= theme('table', $header, $rows, array('id' => $table_id, 'class' => 'field-multiple-table'));
- $output .= $element['#description'] ? '<div class="description">' . $element['#description'] . '</div>' : '';
- $output .= drupal_render($element[$element['#field_name'] . '_add_more']);
-
- drupal_add_tabledrag($table_id, 'order', 'sibling', $order_class);
- }
- else {
- foreach (element_children($element) as $key) {
- $output .= drupal_render($element[$key]);
- }
- }
-
- return $output;
-}
-
-/**
- * Submit handler to add more choices to a field form. This handler is used when
- * JavaScript is not available. It makes changes to the form state and the
- * entire form is rebuilt during the page reload.
- */
-function field_add_more_submit($form, &$form_state) {
- // Set the form to rebuild and run submit handlers.
- if (isset($form['#builder_function']) && drupal_function_exists($form['#builder_function'])) {
- $form['#builder_function']($form, $form_state);
-
- // Make the changes we want to the form state.
- $field_name = $form_state['clicked_button']['#field_name'];
- if ($form_state['values'][$field_name][$field_name . '_add_more']) {
- $form_state['field_item_count'][$field_name] = count($form_state['values'][$field_name]);
- }
- }
-}
-
-/**
- * Menu callback for AHAH addition of new empty widgets.
- */
-function field_add_more_js($bundle_name, $field_name) {
- // Arguments are coming from the url, so we translate back dashes.
- $field_name = str_replace('-', '_', $field_name);
-
- $invalid = FALSE;
- if (empty($_POST['form_build_id'])) {
- // Invalid request.
- $invalid = TRUE;
- }
-
- // Retrieve the cached form.
- $form_state = array('submitted' => FALSE);
- $form_build_id = $_POST['form_build_id'];
- $form = form_get_cache($form_build_id, $form_state);
- if (!$form) {
- // Invalid form_build_id.
- $invalid = TRUE;
- }
-
- // Retrieve field information.
- $field = $form['#fields'][$field_name]['field'];
- $instance = $form['#fields'][$field_name]['instance'];
- $form_path = $form['#fields'][$field_name]['form_path'];
- if ($field['cardinality'] != FIELD_CARDINALITY_UNLIMITED) {
- // Ivnalid
- $invalid = TRUE;
- }
-
- if ($invalid) {
- drupal_json(array('data' => ''));
- exit;
- }
-
- // We don't simply return a new empty widget row to append to existing ones,
- // because:
- // - ahah.js won't simply let us add a new row to a table
- // - attaching the 'draggable' behavior won't be easy
- // So we resort to rebuilding the whole table of widgets including the
- // existing ones, which makes us jump through a few hoops.
-
- // The form that we get from the cache is unbuilt. We need to build it so
- // that _value callbacks can be executed and $form_state['values'] populated.
- // We only want to affect $form_state['values'], not the $form itself
- // (built forms aren't supposed to enter the cache) nor the rest of
- // $form_state, so we use copies of $form and $form_state.
- $form_copy = $form;
- $form_state_copy = $form_state;
- $form_copy['#post'] = array();
- form_builder($_POST['form_id'], $form_copy, $form_state_copy);
- // Just grab the data we need.
- $form_state['values'] = $form_state_copy['values'];
- // Reset cached ids, so that they don't affect the actual form we output.
- form_clean_id(NULL, TRUE);
-
- // Sort the $form_state['values'] we just built *and* the incoming $_POST data
- // according to d-n-d reordering.
- unset($form_state['values'][$field_name][$field['field_name'] . '_add_more']);
- foreach ($_POST[$field_name] as $delta => $item) {
- $form_state['values'][$field_name][$delta]['_weight'] = $item['_weight'];
- }
- $form_state['values'][$field_name] = _field_sort_items($field, $form_state['values'][$field_name]);
- $_POST[$field_name] = _field_sort_items($field, $_POST[$field_name]);
-
- // Build our new form element for the whole field, asking for one more element.
- $form_state['field_item_count'] = array($field_name => count($_POST[$field_name]) + 1);
- $items = $form_state['values'][$field_name];
- $form_element = field_default_form(NULL, NULL, $field, $instance, $items, $form, $form_state);
- // Let other modules alter it.
- drupal_alter('form', $form_element, array(), 'field_add_more_js');
-
- // Add the new element at the right location in the (original, unbuilt) form.
- $target = &$form;
- foreach ($form_path as $key) {
- $target = &$target[$key];
- }
- $target = $form_element[$field_name];
-
- // Save the new definition of the form.
- $form_state['values'] = array();
- form_set_cache($form_build_id, $form, $form_state);
-
- // Build the new form against the incoming $_POST values so that we can
- // render the new element.
- $delta = max(array_keys($_POST[$field_name])) + 1;
- $_POST[$field_name][$delta]['_weight'] = $delta;
- $form_state = array('submitted' => FALSE);
- $form += array(
- '#post' => $_POST,
- '#programmed' => FALSE,
- );
- $form = form_builder($_POST['form_id'], $form, $form_state);
-
- // Render the new output.
- // We get fetch the form element from the built $form.
- $field_form = $form;
- foreach ($form_path as $key) {
- $field_form = $field_form[$key];
- }
- // We add a div around the new field to receive the ahah effect.
- $field_form[$delta]['#prefix'] = '<div class="ahah-new-field">' . (isset($field_form[$delta]['#prefix']) ? $field_form[$delta]['#prefix'] : '');
- $field_form[$delta]['#suffix'] = (isset($field_form[$delta]['#suffix']) ? $field_form[$delta]['#suffix'] : '') . '</div>';
- // TODO : this causes duplication of the wrapping divs
-
- // If a newly inserted widget contains AHAH behaviors, they normally won't
- // work because AHAH doesn't know about those - it just attaches to the exact
- // form elements that were initially specified in the Drupal.settings object.
- // The new ones didn't exist then, so we need to update Drupal.settings
- // by ourselves in order to let AHAH know about those new form elements.
- $javascript = drupal_add_js(NULL, NULL);
- $output_js = isset($javascript['setting']) ? '<script type="text/javascript">jQuery.extend(Drupal.settings, ' . drupal_to_js(call_user_func_array('array_merge_recursive', $javascript['setting'])) . ');</script>' : '';
-
- $output = theme('status_messages') . drupal_render($field_form) . $output_js;
- drupal_json(array('status' => TRUE, 'data' => $output));
- exit;
-}
+<?php
+// $Id$
+
+// TODO : merge with field.default.inc ?
+
+/**
+ * Create a separate form element for each field.
+ */
+function field_default_form($obj_type, $object, $field, $instance, $items, &$form, &$form_state, $get_delta = NULL) {
+ // This could be called with no object, as when a UI module creates a
+ // dummy form to set default values.
+ if ($object) {
+ list($id, ,) = field_attach_extract_ids($obj_type, $object);
+ }
+ $addition = array();
+
+ $field_name = $field['field_name'];
+
+ // If the field is not accessible, don't add anything. The field value will
+ // be left unchanged on update, or considered empty on insert.
+ // TODO : if/when field_attach_insert() takes care of default values,
+ // unaccessible fields will automatically get the default value on insert.
+ if (!field_access('edit', $field)) {
+ return $addition;
+ }
+
+ // Put field information at the top of the form, so that it can be easily
+ // retrieved.
+ // Note : widgets and other form handling code should *always* fetch
+ // field and instance information from $form['#fields'] rather than from
+ // field_info_field(). This lets us build forms for 'variants' of a field,
+ // for instance on admin screens.
+ $form['#fields'][$field_name] = array(
+ 'field' => $field,
+ 'instance' => $instance,
+ );
+ // TODO : why do we need this ?
+ $form['#cache'] = FALSE;
+
+ // Populate widgets with default values if we're creating a new object.
+ if (empty($items) && empty($id) && !empty($instance['default_value_function'])) {
+ $items = array();
+ $function = $instance['default_value_function'];
+ if (drupal_function_exists($function)) {
+ $items = $function($obj_type, $object, $field, $instance);
+ }
+ }
+
+ $form_element = array();
+
+ // If field module handles multiple values for this form element,
+ // and we are displaying an individual element, process the multiple value
+ // form.
+ if (!isset($get_delta) && field_behaviors_widget('multiple values', $instance) == FIELD_BEHAVIOR_DEFAULT) {
+ $form_element = field_multiple_value_form($field, $instance, $items, $form, $form_state);
+ }
+ // If the widget is handling multiple values (e.g Options),
+ // or if we are displaying an individual element, just get a single form
+ // element and make it the $delta value.
+ else {
+ $delta = isset($get_delta) ? $get_delta : 0;
+ $function = $instance['widget']['module'] . '_field_widget';
+ if (drupal_function_exists($function)) {
+ if ($element = $function($form, $form_state, $field, $instance, $items, $delta)) {
+ $defaults = array(
+ '#required' => $get_delta > 0 ? FALSE : $instance['required'],
+ '#columns' => array_keys($field['columns']),
+ '#title' => check_plain(t($instance['label'])),
+ '#description' => field_filter_xss($instance['description']),
+ '#delta' => $delta,
+ '#field_name' => $field['field_name'],
+ '#bundle' => $instance['bundle'],
+ );
+ $element = array_merge($element, $defaults);
+ // If we're processing a specific delta value for a field where the
+ // field module handles multiples, set the delta in the result.
+ // For fields that handle their own processing, we can't make assumptions
+ // about how the field is structured, just merge in the returned value.
+ if (field_behaviors_widget('multiple values', $instance) == FIELD_BEHAVIOR_DEFAULT) {
+ $form_element[$delta] = $element;
+ }
+ else {
+ $form_element = $element;
+ }
+ }
+ }
+ }
+
+ if ($form_element) {
+ $defaults = array(
+ '#field_name' => $field['field_name'],
+ '#tree' => TRUE,
+ '#weight' => $instance['weight'],
+ );
+
+ $addition[$field['field_name']] = array_merge($form_element, $defaults);
+ $form['#fields'][$field['field_name']]['form_path'] = array($field['field_name']);
+ }
+
+ return $addition;
+}
+
+/**
+ * Special handling to create form elements for multiple values.
+ *
+ * Handles generic features for multiple fields:
+ * - number of widgets
+ * - AHAH-'add more' button
+ * - drag-n-drop value reordering
+ */
+function field_multiple_value_form($field, $instance, $items, &$form, &$form_state) {
+ $field = field_info_field($instance['field_name']);
+ $field_name = $field['field_name'];
+
+ switch ($field['cardinality']) {
+ case FIELD_CARDINALITY_UNLIMITED:
+ $filled_items = field_set_empty($field, $items);
+ $current_item_count = isset($form_state['field_item_count'][$field_name])
+ ? $form_state['field_item_count'][$field_name]
+ : count($items);
+ // We always want at least one empty icon for the user to fill in.
+ $max = ($current_item_count > count($filled_items))
+ ? $current_item_count - 1
+ : $current_item_count;
+
+ break;
+ default:
+ $max = $field['cardinality'] - 1;
+ break;
+ }
+
+ $title = check_plain(t($instance['label']));
+ $description = field_filter_xss(t($instance['description']));
+
+ $form_element = array(
+ '#theme' => 'field_multiple_value_form',
+ '#multiple' => $field['cardinality'],
+ '#title' => $title,
+ '#required' => $instance['required'],
+ '#description' => $description,
+ );
+
+ $function = $instance['widget']['module'] . '_field_widget';
+ if (drupal_function_exists($function)) {
+ for ($delta = 0; $delta <= $max; $delta++) {
+ if ($element = $function($form, $form_state, $field, $instance, $items, $delta)) {
+ $multiple = $field['cardinality'] > 1 || $field['cardinality'] == FIELD_CARDINALITY_UNLIMITED;
+ $defaults = array(
+ '#title' => $multiple ? '' : $title,
+ '#description' => $multiple ? '' : $description,
+ '#required' => $delta == 0 && $instance['required'],
+ '#weight' => $delta,
+ '#delta' => $delta,
+ '#columns' => array_keys($field['columns']),
+ '#field_name' => $field_name,
+ '#bundle' => $instance['bundle'],
+ );
+
+ // Add an input field for the delta (drag-n-drop reordering), which will
+ // be hidden by tabledrag js behavior.
+ if ($multiple) {
+ // We name the element '_weight' to avoid clashing with column names
+ // defined by field modules.
+ $element['_weight'] = array(
+ '#type' => 'weight',
+ '#delta' => $max, // this 'delta' is the 'weight' element's property
+ '#default_value' => isset($items[$delta]['_weight']) ? $items[$delta]['_weight'] : $delta,
+ '#weight' => 100,
+ );
+ }
+
+ $form_element[$delta] = array_merge($element, $defaults);
+ }
+ }
+
+ // Add AHAH add more button, if not working with a programmed form.
+ if ($field['cardinality'] == FIELD_CARDINALITY_UNLIMITED && empty($form['#programmed'])) {
+ // Make sure the form is cached so ahah can work.
+ $form['#cache'] = TRUE;
+ $bundle_name_url_str = str_replace('_', '-', $instance['bundle']);
+ $field_name_url_str = str_replace('_', '-', $field_name);
+
+ $form_element[$field_name . '_add_more'] = array(
+ '#type' => 'submit',
+ '#name' => $field_name . '_add_more',
+ '#value' => t('Add another item'),
+ '#weight' => $instance['weight'] + $max + 1,
+ // Submit callback for disabled JavaScript.
+ '#submit' => array('field_add_more_submit'),
+ '#ahah' => array(
+ 'path' => 'field/js_add_more/' . $bundle_name_url_str . '/' . $field_name_url_str,
+ 'wrapper' => $field_name_url_str . '-items',
+ 'method' => 'replace',
+ 'effect' => 'fade',
+ ),
+ // When JS is disabled, the field_add_more_submit handler will find
+ // the relevant field using these entries.
+ '#field_name' => $field_name,
+ '#bundle' => $instance['bundle'],
+ );
+
+ // Add wrappers for the fields and 'more' button.
+ $form_element['#prefix'] = '<div class="clear-block" id="' . $field_name_url_str . '-add-more-wrapper"><div id="' . $field_name_url_str . '-items">';
+ $form_element[$field_name . '_add_more']['#prefix'] = '<div class="field-add-more">';
+ $form_element[$field_name . '_add_more']['#suffix'] = '</div></div></div>';
+ }
+ }
+ return $form_element;
+}
+
+/**
+ * Theme an individual form element.
+ *
+ * Combine multiple values into a table with drag-n-drop reordering.
+ * TODO : convert to a template.
+ */
+function theme_field_multiple_value_form($element) {
+ $output = '';
+
+ if ($element['#multiple'] > 1 || $element['#multiple'] == FIELD_CARDINALITY_UNLIMITED) {
+ $table_id = $element['#field_name'] . '_values';
+ $order_class = $element['#field_name'] . '-delta-order';
+ $required = !empty($element['#required']) ? '<span class="form-required" title="' . t('This field is required. ') . '">*</span>' : '';
+
+ $header = array(
+ array(
+ 'data' => t('!title: !required', array('!title' => $element['#title'], '!required' => $required)),
+ 'colspan' => 2
+ ),
+ t('Order'),
+ );
+ $rows = array();
+
+ // Sort items according to '_weight' (needed when the form comes back after
+ // preview or failed validation)
+ $items = array();
+ foreach (element_children($element) as $key) {
+ if ($key !== $element['#field_name'] . '_add_more') {
+ $items[] = &$element[$key];
+ }
+ }
+ usort($items, '_field_sort_items_value_helper');
+
+ // Add the items as table rows.
+ foreach ($items as $key => $item) {
+ $item['_weight']['#attributes']['class'] = $order_class;
+ $delta_element = drupal_render($item['_weight']);
+ $cells = array(
+ array('data' => '', 'class' => 'field-multiple-drag'),
+ drupal_render($item),
+ array('data' => $delta_element, 'class' => 'delta-order'),
+ );
+ $rows[] = array(
+ 'data' => $cells,
+ 'class' => 'draggable',
+ );
+ }
+
+ $output .= theme('table', $header, $rows, array('id' => $table_id, 'class' => 'field-multiple-table'));
+ $output .= $element['#description'] ? '<div class="description">' . $element['#description'] . '</div>' : '';
+ $output .= drupal_render($element[$element['#field_name'] . '_add_more']);
+
+ drupal_add_tabledrag($table_id, 'order', 'sibling', $order_class);
+ }
+ else {
+ foreach (element_children($element) as $key) {
+ $output .= drupal_render($element[$key]);
+ }
+ }
+
+ return $output;
+}
+
+/**
+ * Submit handler to add more choices to a field form. This handler is used when
+ * JavaScript is not available. It makes changes to the form state and the
+ * entire form is rebuilt during the page reload.
+ */
+function field_add_more_submit($form, &$form_state) {
+ // Set the form to rebuild and run submit handlers.
+ if (isset($form['#builder_function']) && drupal_function_exists($form['#builder_function'])) {
+ $form['#builder_function']($form, $form_state);
+
+ // Make the changes we want to the form state.
+ $field_name = $form_state['clicked_button']['#field_name'];
+ if ($form_state['values'][$field_name][$field_name . '_add_more']) {
+ $form_state['field_item_count'][$field_name] = count($form_state['values'][$field_name]);
+ }
+ }
+}
+
+/**
+ * Menu callback for AHAH addition of new empty widgets.
+ */
+function field_add_more_js($bundle_name, $field_name) {
+ // Arguments are coming from the url, so we translate back dashes.
+ $field_name = str_replace('-', '_', $field_name);
+
+ $invalid = FALSE;
+ if (empty($_POST['form_build_id'])) {
+ // Invalid request.
+ $invalid = TRUE;
+ }
+
+ // Retrieve the cached form.
+ $form_state = array('submitted' => FALSE);
+ $form_build_id = $_POST['form_build_id'];
+ $form = form_get_cache($form_build_id, $form_state);
+ if (!$form) {
+ // Invalid form_build_id.
+ $invalid = TRUE;
+ }
+
+ // Retrieve field information.
+ $field = $form['#fields'][$field_name]['field'];
+ $instance = $form['#fields'][$field_name]['instance'];
+ $form_path = $form['#fields'][$field_name]['form_path'];
+ if ($field['cardinality'] != FIELD_CARDINALITY_UNLIMITED) {
+ // Ivnalid
+ $invalid = TRUE;
+ }
+
+ if ($invalid) {
+ drupal_json(array('data' => ''));
+ exit;
+ }
+
+ // We don't simply return a new empty widget row to append to existing ones,
+ // because:
+ // - ahah.js won't simply let us add a new row to a table
+ // - attaching the 'draggable' behavior won't be easy
+ // So we resort to rebuilding the whole table of widgets including the
+ // existing ones, which makes us jump through a few hoops.
+
+ // The form that we get from the cache is unbuilt. We need to build it so
+ // that _value callbacks can be executed and $form_state['values'] populated.
+ // We only want to affect $form_state['values'], not the $form itself
+ // (built forms aren't supposed to enter the cache) nor the rest of
+ // $form_state, so we use copies of $form and $form_state.
+ $form_copy = $form;
+ $form_state_copy = $form_state;
+ $form_copy['#post'] = array();
+ form_builder($_POST['form_id'], $form_copy, $form_state_copy);
+ // Just grab the data we need.
+ $form_state['values'] = $form_state_copy['values'];
+ // Reset cached ids, so that they don't affect the actual form we output.
+ form_clean_id(NULL, TRUE);
+
+ // Sort the $form_state['values'] we just built *and* the incoming $_POST data
+ // according to d-n-d reordering.
+ unset($form_state['values'][$field_name][$field['field_name'] . '_add_more']);
+ foreach ($_POST[$field_name] as $delta => $item) {
+ $form_state['values'][$field_name][$delta]['_weight'] = $item['_weight'];
+ }
+ $form_state['values'][$field_name] = _field_sort_items($field, $form_state['values'][$field_name]);
+ $_POST[$field_name] = _field_sort_items($field, $_POST[$field_name]);
+
+ // Build our new form element for the whole field, asking for one more element.
+ $form_state['field_item_count'] = array($field_name => count($_POST[$field_name]) + 1);
+ $items = $form_state['values'][$field_name];
+ $form_element = field_default_form(NULL, NULL, $field, $instance, $items, $form, $form_state);
+ // Let other modules alter it.
+ drupal_alter('form', $form_element, array(), 'field_add_more_js');
+
+ // Add the new element at the right location in the (original, unbuilt) form.
+ $target = &$form;
+ foreach ($form_path as $key) {
+ $target = &$target[$key];
+ }
+ $target = $form_element[$field_name];
+
+ // Save the new definition of the form.
+ $form_state['values'] = array();
+ form_set_cache($form_build_id, $form, $form_state);
+
+ // Build the new form against the incoming $_POST values so that we can
+ // render the new element.
+ $delta = max(array_keys($_POST[$field_name])) + 1;
+ $_POST[$field_name][$delta]['_weight'] = $delta;
+ $form_state = array('submitted' => FALSE);
+ $form += array(
+ '#post' => $_POST,
+ '#programmed' => FALSE,
+ );
+ $form = form_builder($_POST['form_id'], $form, $form_state);
+
+ // Render the new output.
+ // We get fetch the form element from the built $form.
+ $field_form = $form;
+ foreach ($form_path as $key) {
+ $field_form = $field_form[$key];
+ }
+ // We add a div around the new field to receive the ahah effect.
+ $field_form[$delta]['#prefix'] = '<div class="ahah-new-field">' . (isset($field_form[$delta]['#prefix']) ? $field_form[$delta]['#prefix'] : '');
+ $field_form[$delta]['#suffix'] = (isset($field_form[$delta]['#suffix']) ? $field_form[$delta]['#suffix'] : '') . '</div>';
+ // TODO : this causes duplication of the wrapping divs
+
+ // If a newly inserted widget contains AHAH behaviors, they normally won't
+ // work because AHAH doesn't know about those - it just attaches to the exact
+ // form elements that were initially specified in the Drupal.settings object.
+ // The new ones didn't exist then, so we need to update Drupal.settings
+ // by ourselves in order to let AHAH know about those new form elements.
+ $javascript = drupal_add_js(NULL, NULL);
+ $output_js = isset($javascript['setting']) ? '<script type="text/javascript">jQuery.extend(Drupal.settings, ' . drupal_to_js(call_user_func_array('array_merge_recursive', $javascript['setting'])) . ');</script>' : '';
+
+ $output = theme('status_messages') . drupal_render($field_form) . $output_js;
+ drupal_json(array('status' => TRUE, 'data' => $output));
+ exit;
+}