Index: modules/field/field.test =================================================================== RCS file: /cvs/drupal/drupal/modules/field/field.test,v retrieving revision 1.42 diff -u -r1.42 field.test --- modules/field/field.test 22 Aug 2009 00:58:52 -0000 1.42 +++ modules/field/field.test 22 Aug 2009 23:15:56 -0000 @@ -1331,8 +1331,7 @@ $pattern[$weight] = "]*value=\"$value\" [^>]*"; } // Press 'add more' button through AHAH. - $path = 'field/js_add_more/' . str_replace('_', '-', $this->instance['bundle']) . '/' . str_replace('_', '-', $this->instance['field_name']); - $this->_fieldPostAhah($path, $edit, t('Add another item')); + $this->_fieldPostAhah($edit, t('Add another item')); ksort($values); $values = array_values($values); @@ -1357,11 +1356,11 @@ * Since the result is generally not a full-fledged form, this cannot be * called iteratively. */ - function _fieldPostAhah($path, $edit, $submit, array $options = array(), array $headers = array()) { + function _fieldPostAhah($edit, $submit, array $options = array(), array $headers = array()) { // @TODO: the framework should make it possible to submit a form to a // different URL than its action or the current. For now, we can just force // it. - $this->additionalCurlOptions[CURLOPT_URL] = url($path, array('absolute' => TRUE)); + $this->additionalCurlOptions[CURLOPT_URL] = url('system/ajax', array('absolute' => TRUE)); $this->drupalPost(NULL, $edit, $submit); unset($this->additionalCurlOptions[CURLOPT_URL]); Index: modules/field/field.module =================================================================== RCS file: /cvs/drupal/drupal/modules/field/field.module,v retrieving revision 1.25 diff -u -r1.25 field.module --- modules/field/field.module 22 Aug 2009 00:58:52 -0000 1.25 +++ modules/field/field.module 22 Aug 2009 23:15:55 -0000 @@ -154,22 +154,6 @@ } /** - * Implement hook_menu(). - */ -function field_menu() { - $items = array(); - - // Callback for AHAH add more buttons. - $items['field/js_add_more'] = array( - 'page callback' => 'field_add_more_js', - 'access arguments' => array('access content'), - 'type' => MENU_CALLBACK, - ); - - return $items; -} - -/** * Implement hook_theme(). */ function field_theme() { Index: modules/field/field.form.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/field/field.form.inc,v retrieving revision 1.17 diff -u -r1.17 field.form.inc --- modules/field/field.form.inc 22 Aug 2009 14:34:19 -0000 1.17 +++ modules/field/field.form.inc 22 Aug 2009 23:15:54 -0000 @@ -141,8 +141,7 @@ $title = check_plain(t($instance['label'])); $description = field_filter_xss(t($instance['description'])); - $bundle_name_url_css = str_replace('_', '-', $instance['bundle']); - $field_name_url_css = str_replace('_', '-', $field_name); + $wrapper_id = str_replace('_', '-', $field_name) . '-wrapper'; $form_element = array( '#theme' => 'field_multiple_value_form', @@ -150,8 +149,9 @@ '#title' => $title, '#required' => $instance['required'], '#description' => $description, - '#prefix' => '
', + '#prefix' => '
', '#suffix' => '
', + '#max_delta' => $max, ); $function = $instance['widget']['module'] . '_field_widget'; @@ -188,28 +188,25 @@ } } - // Add AHAH add more button, if not working with a programmed form. + // Add 'add more' button, if not working with a programmed form. if ($field['cardinality'] == FIELD_CARDINALITY_UNLIMITED && empty($form_state['programmed'])) { - $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'), + '#attributes' => array('class' => array('field-add-more-submit')), // Submit callback for disabled JavaScript. '#submit' => array('field_add_more_submit'), '#ajax' => array( - 'path' => 'field/js_add_more/' . $bundle_name_url_css . '/' . $field_name_url_css, - 'wrapper' => $field_name_url_css . '-wrapper', + 'callback' => 'field_add_more_js', + 'wrapper' => $wrapper_id, 'method' => 'replace', 'effect' => 'fade', ), - // When JS is disabled, the field_add_more_submit handler will find - // the relevant field using these entries. + // The field_add_more_submit() and field_add_more_js() handlers will + // find the relevant field using those entries. '#field_name' => $field_name, - '#bundle' => $instance['bundle'], - '#attributes' => array('class' => array('field-add-more-submit')), '#language' => $langcode, ); } @@ -343,114 +340,25 @@ } /** - * Menu callback for AHAH addition of new empty widgets. + * Ajax callback for 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 = form_state_defaults(); - $form_build_id = $_POST['form_build_id']; - $form = form_get_cache($form_build_id, $form_state); - if (!$form) { - // Invalid form_build_id. - $invalid = TRUE; - } - +function field_add_more_js($form, $form_state) { // Retrieve field information. - $field = $form['#fields'][$field_name]['field']; - $instance = $form['#fields'][$field_name]['instance']; $form_path = $form['#fields'][$field_name]['form_path']; + $field = $form['#fields'][$field_name]['field']; if ($field['cardinality'] != FIELD_CARDINALITY_UNLIMITED) { - // Ivnalid - $invalid = TRUE; - } - - if ($invalid) { ajax_render(array()); } - - // 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 - // @todo ajax.js lets you. :) - // - 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_state_copy['input'] = 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. - drupal_static_reset('form_clean_id'); - - // Ensure that a valid language is provided. - $langcode = key($_POST[$field_name]); - if ($langcode != FIELD_LANGUAGE_NONE) { - $langcode = field_multilingual_valid_language($langcode); - } - - // 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][$langcode][$field['field_name'] . '_add_more']); - foreach ($_POST[$field_name][$langcode] as $delta => $item) { - $form_state['values'][$field_name][$langcode][$delta]['_weight'] = $item['_weight']; - } - $form_state['values'][$field_name][$langcode] = _field_sort_items($field, $form_state['values'][$field_name][$langcode]); - $_POST[$field_name][$langcode] = _field_sort_items($field, $_POST[$field_name][$langcode]); - - // 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][$langcode]) + 1); - $items = $form_state['values'][$field_name][$langcode]; - $form_element = field_default_form(NULL, NULL, $field, $instance, $langcode, $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][$langcode])) + 1; - $_POST[$field_name][$langcode][$delta]['_weight'] = $delta; - $form_state = form_state_defaults(); - $form_state['input'] = $_POST; - $form = form_builder($_POST['form_id'], $form, $form_state); - - // Render the new output. - // We fetch the form element from the built $form. + // Navigate to the right part of the form. $field_form = $form; foreach ($form_path as $key) { $field_form = $field_form[$key]; } + // Add a DIV around the new field to receive the AJAX effect. + $delta = $field_form['#max_delta']; $field_form[$delta]['#prefix'] = '
' . (isset($field_form[$delta]['#prefix']) ? $field_form[$delta]['#prefix'] : ''); $field_form[$delta]['#suffix'] = (isset($field_form[$delta]['#suffix']) ? $field_form[$delta]['#suffix'] : '') . '
'; - // Prevent duplicate wrapper. - unset($field_form['#prefix'], $field_form['#suffix']); $output = theme('status_messages') . drupal_render($field_form);