| Commit | Line | Data |
|---|---|---|
| dc19d48f YC |
1 | <?php |
| 2 | // $Id$ | |
| 3 | ||
| 4 | /** | |
| 64cb8552 | 5 | * @file |
| dc19d48f YC |
6 | * Create fields' form for a content type. |
| 7 | * | |
| 8 | * Each field defines its own component of the content entry form, via its | |
| 9 | * chosen widget. | |
| 10 | */ | |
| 11 | function content_form(&$form, &$form_state) { | |
| 12 | $type = content_types($form['type']['#value']); | |
| 13 | foreach ($type['fields'] as $field_name => $field) { | |
| 14 | $form['#field_info'][$field['field_name']] = $field; | |
| 15 | $form += (array) content_field_form($form, $form_state, $field); | |
| 16 | } | |
| 17 | return $form; | |
| 18 | } | |
| 19 | ||
| 20 | /** | |
| 21 | * Create a separate form element for each field. | |
| 22 | * | |
| 3a2d35f5 | 23 | * // TODO: $count param ? not used anymore ? |
| dc19d48f YC |
24 | * Hook_widget() picks up two new values, $count and $delta, to help |
| 25 | * widgets know what information to return since multiple values are | |
| 26 | * sometimes controlled by the content module. | |
| 27 | * | |
| 28 | * @param $form | |
| 29 | * the form to add this field element to | |
| 30 | * @param $form_state | |
| 31 | * the form_state for the above form | |
| 32 | * @param $field | |
| 33 | * the field array to use to create the form element | |
| 34 | * @param $get_delta | |
| 35 | * use to get only a specific delta value of a multiple value field, otherwise | |
| 36 | * function will return the entire $field form element. | |
| 37 | */ | |
| 38 | function content_field_form(&$form, &$form_state, $field, $get_delta = NULL) { | |
| 39 | $form['#cache'] = FALSE; | |
| 40 | $node = $form['#node']; | |
| 41 | $addition = array(); | |
| 42 | $form_element = array(); | |
| 53a93046 | 43 | $field_name = $field['field_name']; |
| 9fbc360b | 44 | |
| d0144209 | 45 | $items = array(); |
| 9fbc360b | 46 | |
| 3a2d35f5 | 47 | // TODO: is the "if (function_exists($function)) {" needed ? |
| dc19d48f YC |
48 | // defining the $function here makes it unclear where it is actually called |
| 49 | $function = $field['widget']['module'] .'_widget'; | |
| dc19d48f YC |
50 | if (function_exists($function)) { |
| 51 | // Prepare the values to be filled in the widget. | |
| 3a2d35f5 | 52 | // We look in the following places: |
| dc19d48f YC |
53 | // - Form submitted values |
| 54 | // - Node values (when editing an existing node), or pre-filled values (when | |
| 55 | // creating a new node translation) | |
| 56 | // - Default values set for the field (when creating a new node). | |
| dc19d48f YC |
57 | if (!empty($form_state['values'][$field['field_name']])) { |
| 58 | $items = $form_state['values'][$field['field_name']]; | |
| 9372a36c YC |
59 | // If there was an AHAH add more button in this field, don't save it. |
| 60 | unset($items[$field['field_name'] .'_add_more']); | |
| dc19d48f YC |
61 | } |
| 62 | elseif (!empty($node->$field['field_name'])) { | |
| 63 | $items = $node->$field['field_name']; | |
| 64 | } | |
| 65 | elseif (empty($node->nid)) { | |
| 66 | if (content_callback('widget', 'default value', $field) != CONTENT_CALLBACK_NONE) { | |
| 5561e976 KS |
67 | // If a module wants to insert custom default values here, |
| 68 | // it should provide a hook_default_value() function to call, | |
| 69 | // otherwise the content module's content_default_value() function | |
| 70 | // will be used. | |
| 71 | $callback = content_callback('widget', 'default value', $field) == CONTENT_CALLBACK_CUSTOM ? $field['widget']['module'] .'_default_value' : 'content_default_value'; | |
| dc19d48f YC |
72 | if (function_exists($callback)) { |
| 73 | $items = $callback($form, $form_state, $field, 0); | |
| 74 | } | |
| 75 | } | |
| 76 | } | |
| 77 | ||
| d0144209 MFM |
78 | // See if access to this form element is restricted, |
| 79 | // if so, skip widget processing and just set the value. | |
| c7d4ceea | 80 | $access = content_access('edit', $field, NULL, $node); |
| d0144209 MFM |
81 | if (!$access) { |
| 82 | $addition[$field_name] = array( | |
| 83 | '#access' => $access, | |
| 84 | '#type' => 'value', | |
| 85 | '#value' => $items, | |
| 86 | ); | |
| 87 | return $addition; | |
| 88 | } | |
| 89 | ||
| dc19d48f YC |
90 | // If content module handles multiple values for this form element, |
| 91 | // and not selecting an individual $delta, process the multiple value form. | |
| 92 | if (!isset($get_delta) && content_handle('widget', 'multiple values', $field) == CONTENT_HANDLE_CORE) { | |
| 93 | $form_element = content_multiple_value_form($form, $form_state, $field, $items); | |
| 94 | } | |
| 95 | // If the widget is handling multiple values (e.g optionwidgets), | |
| 96 | // or selecting an individual element, just get a single form | |
| 97 | // element and make it the $delta value. | |
| 98 | else { | |
| 99 | $delta = isset($get_delta) ? $get_delta : 0; | |
| 100 | if ($element = $function($form, $form_state, $field, $items, $delta)) { | |
| 9fbc360b YC |
101 | $title = check_plain(t($field['widget']['label'])); |
| 102 | $description = content_filter_xss(t($field['widget']['description'])); | |
| dc19d48f | 103 | $defaults = array( |
| dc19d48f | 104 | '#required' => $get_delta > 0 ? FALSE : $field['required'], |
| dc19d48f | 105 | '#columns' => array_keys($field['columns']), |
| 07c92176 | 106 | '#title' => $title, |
| 9fbc360b | 107 | '#description' => $description, |
| dc19d48f | 108 | '#delta' => $delta, |
| 07c92176 YC |
109 | '#field_name' => $field['field_name'], |
| 110 | '#type_name' => $field['type_name'], | |
| dc19d48f YC |
111 | ); |
| 112 | // If we're processing a specific delta value for a field where the | |
| 113 | // content module handles multiples, set the delta in the result. | |
| 114 | // For fields that handle their own processing, we can't make assumptions | |
| 115 | // about how the field is structured, just merge in the returned value. | |
| 116 | if (content_handle('widget', 'multiple values', $field) == CONTENT_HANDLE_CORE) { | |
| 117 | $form_element[$delta] = array_merge($element, $defaults); | |
| 118 | } | |
| 119 | else { | |
| 120 | $form_element = array_merge($element, $defaults); | |
| 121 | } | |
| 122 | } | |
| 123 | } | |
| 124 | ||
| 125 | // Field name is needed at top level as well as the individual elements | |
| 126 | // so the multiple values or other field level theme or processing can find it. | |
| 127 | if ($form_element) { | |
| 128 | $defaults = array( | |
| 129 | '#field_name' => $field['field_name'], | |
| 130 | '#tree' => TRUE, | |
| 131 | '#weight' => $field['widget']['weight'], | |
| 53a93046 | 132 | '#access' => $access, |
| 3a2d35f5 | 133 | // TODO: what's the need for #count ? does not seem to be used anywhere ? |
| dc19d48f YC |
134 | '#count' => count($form_element), |
| 135 | ); | |
| 136 | $addition[$field['field_name']] = array_merge($form_element, $defaults); | |
| 137 | } | |
| 138 | } | |
| 139 | return $addition; | |
| 140 | } | |
| 141 | ||
| 142 | /** | |
| 143 | * Special handling to create form elements for multiple values. | |
| 144 | * | |
| 3a2d35f5 | 145 | * Handles generic features for multiple fields: |
| dc19d48f YC |
146 | * - number of widgets |
| 147 | * - AHAH-'add more' button | |
| 148 | * - drag-n-drop value reordering | |
| 149 | */ | |
| 150 | function content_multiple_value_form(&$form, &$form_state, $field, $items) { | |
| 151 | $field_name = $field['field_name']; | |
| 152 | ||
| 153 | switch ($field['multiple']) { | |
| 154 | case 0: | |
| cb68e6f6 | 155 | $deltas = array(0); |
| dc19d48f YC |
156 | $max = 0; |
| 157 | break; | |
| 9372a36c | 158 | |
| cb68e6f6 MFM |
159 | case 1: |
| 160 | $deltas = array_keys($items); | |
| 0328c552 | 161 | $current_item_count = max(1, (isset($form_state['item_count'][$field_name]) ? $form_state['item_count'][$field_name] : count($deltas))); |
| cb68e6f6 MFM |
162 | $max = (!empty($deltas) ? max($deltas) : -1); |
| 163 | while (count($deltas) < $current_item_count) { | |
| 164 | $max++; | |
| 165 | $deltas[] = $max; | |
| 166 | } | |
| dc19d48f | 167 | break; |
| cb68e6f6 | 168 | |
| dc19d48f YC |
169 | default: |
| 170 | $max = $field['multiple'] - 1; | |
| cb68e6f6 | 171 | $deltas = range(0, $max); |
| dc19d48f YC |
172 | break; |
| 173 | } | |
| 174 | ||
| eb0954b8 YC |
175 | $title = check_plain(t($field['widget']['label'])); |
| 176 | $description = content_filter_xss(t($field['widget']['description'])); | |
| 177 | ||
| dc19d48f | 178 | $form_element = array( |
| dc19d48f | 179 | '#theme' => 'content_multiple_values', |
| eb0954b8 | 180 | '#title' => $title, |
| 3ed89d6e | 181 | '#required' => $field['required'], |
| eb0954b8 | 182 | '#description' => $description, |
| dc19d48f | 183 | ); |
| 6c4e453d | 184 | $function = $field['widget']['module'] .'_widget'; |
| dc19d48f | 185 | |
| cb68e6f6 | 186 | foreach ($deltas as $delta) { |
| dc19d48f YC |
187 | if ($element = $function($form, $form_state, $field, $items, $delta)) { |
| 188 | $defaults = array( | |
| eb0954b8 YC |
189 | '#title' => ($field['multiple'] >= 1) ? '' : $title, |
| 190 | '#description' => ($field['multiple'] >= 1) ? '' : $description, | |
| cb68e6f6 | 191 | '#required' => ($field['multiple'] == 0 ? $field['required'] : FALSE), |
| dc19d48f YC |
192 | '#weight' => $delta, |
| 193 | '#delta' => $delta, | |
| 194 | '#columns' => array_keys($field['columns']), | |
| 07c92176 YC |
195 | '#field_name' => $field_name, |
| 196 | '#type_name' => $field['type_name'], | |
| dc19d48f YC |
197 | ); |
| 198 | ||
| 199 | // Add an input field for the delta (drag-n-drop reordering), which will | |
| 200 | // be hidden by tabledrag js behavior. | |
| 201 | if ($field['multiple'] >= 1) { | |
| 202 | // We name the element '_weight' to avoid clashing with column names | |
| 203 | // defined by field modules. | |
| 204 | $element['_weight'] = array( | |
| 169430ce YC |
205 | '#type' => 'weight', |
| 206 | '#delta' => $max, // this 'delta' is the 'weight' element's property | |
| 98fd13df | 207 | '#default_value' => isset($items[$delta]['_weight']) ? $items[$delta]['_weight'] : $delta, |
| dc19d48f YC |
208 | '#weight' => 100, |
| 209 | ); | |
| 210 | } | |
| 211 | ||
| cb68e6f6 MFM |
212 | // Add a checkbox to allow users remove a single delta item. |
| 213 | // See content_set_empty() and theme_content_multiple_values(). | |
| 214 | if ($field['multiple'] == 1) { | |
| 215 | // We name the element '_remove' to avoid clashing with column names | |
| 216 | // defined by field modules. | |
| 217 | $element['_remove'] = array( | |
| 218 | '#type' => 'checkbox', | |
| 219 | '#attributes' => array('class' => 'content-multiple-remove-checkbox'), | |
| 220 | '#default_value' => isset($items[$delta]['_remove']) ? $items[$delta]['_remove'] : 0, | |
| 221 | ); | |
| 222 | } | |
| 223 | ||
| dc19d48f YC |
224 | $form_element[$delta] = array_merge($element, $defaults); |
| 225 | } | |
| 226 | } | |
| 227 | ||
| cb68e6f6 MFM |
228 | // Add an #after_build callback to prevent validation of fields that are |
| 229 | // flagged for removal and enforce field requirement settings. | |
| 230 | if ($field['multiple'] >= 1) { | |
| 231 | $form_element['#after_build'] = array('content_multiple_value_after_build_proxy'); | |
| 232 | } | |
| 233 | ||
| dc19d48f YC |
234 | // Add AHAH add more button, if not working with a programmed form. |
| 235 | if ($field['multiple'] == 1 && empty($form['#programmed'])) { | |
| 236 | // Make sure the form is cached so ahah can work. | |
| 237 | $form['#cache'] = TRUE; | |
| 07c92176 | 238 | $content_type = content_types($field['type_name']); |
| dc19d48f YC |
239 | $field_name_css = str_replace('_', '-', $field_name); |
| 240 | ||
| 241 | $form_element[$field_name .'_add_more'] = array( | |
| 242 | '#type' => 'submit', | |
| 9372a36c | 243 | '#name' => $field_name .'_add_more', |
| 169430ce | 244 | '#value' => t('Add another item'), |
| dc19d48f | 245 | '#weight' => $field['widget']['weight'] + $max + 1, |
| 9372a36c YC |
246 | // Submit callback for disabled JavaScript. drupal_get_form() might get |
| 247 | // the form from the cache, so we can't rely on content_form_alter() | |
| 248 | // including this file. Therefore, call a proxy function to do this. | |
| 249 | '#submit' => array('content_add_more_submit_proxy'), | |
| dc19d48f YC |
250 | '#ahah' => array( |
| 251 | 'path' => 'content/js_add_more/'. $content_type['url_str'] .'/'. $field_name, | |
| 252 | 'wrapper' => $field_name_css .'-items', | |
| 253 | 'method' => 'replace', | |
| 254 | 'effect' => 'fade', | |
| 255 | ), | |
| 256 | // When JS is disabled, the content_add_more_submit handler will find | |
| 257 | // the relevant field using these entries. | |
| 9372a36c | 258 | '#field_name' => $field_name, |
| 07c92176 | 259 | '#type_name' => $field['type_name'], |
| dc19d48f YC |
260 | ); |
| 261 | ||
| 262 | // Add wrappers for the fields and 'more' button. | |
| a95bdc23 YC |
263 | $form_element['#prefix'] = '<div id="'. $field_name_css .'-items">'; |
| 264 | $form_element['#suffix'] = '</div>'; | |
| 265 | $form_element[$field_name .'_add_more']['#prefix'] = '<div class="content-add-more clear-block">'; | |
| cb68e6f6 | 266 | $form_element[$field_name .'_add_more']['#suffix'] = '</div>'; |
| dc19d48f | 267 | } |
| 9318808e | 268 | |
| dc19d48f YC |
269 | return $form_element; |
| 270 | } | |
| 271 | ||
| 272 | /** | |
| cb68e6f6 MFM |
273 | * After build callback for multiple value fields. |
| 274 | */ | |
| 275 | function content_multiple_value_after_build($elements, &$form_state) { | |
| 276 | $items_map = array(); | |
| 277 | ||
| 278 | foreach (element_children($elements) as $delta) { | |
| 279 | // Find delta items for this field when the form if being processed for validation. | |
| 280 | if (isset($elements[$delta]) && $elements[$delta] && is_numeric($delta) && !empty($elements[$delta]['#needs_validation'])) { | |
| 281 | ||
| 282 | // Find items that have been flagged for removal. | |
| 283 | if (isset($elements[$delta]['_remove']) && !empty($elements[$delta]['_remove']['#value'])) { | |
| 284 | ||
| 285 | // Update the value in the #post attribute of the elements. | |
| 286 | $post = &$elements[$delta]['#post']; | |
| 287 | foreach ($elements[$delta]['#parents'] as $name) { | |
| 288 | $post = &$post[$name]; | |
| 289 | } | |
| 290 | $post = array('_weight' => $elements[$delta]['_weight']['#value'], '_remove' => 1); | |
| 291 | ||
| 292 | // Alter the value of this element and children recursively. | |
| 293 | content_multiple_value_after_build_recursive($elements[$delta], $elements[$delta]['#post']); | |
| 294 | ||
| 295 | $items_map[$delta] = TRUE; | |
| 296 | } | |
| 297 | else { | |
| 298 | $items_map[$delta] = FALSE; | |
| 299 | } | |
| 300 | } | |
| 301 | } | |
| 302 | ||
| 303 | // If the multiple values field is required, then make sure there's at | |
| 304 | // least one item not flagged for removal. This is necessary to point | |
| 305 | // the user to the correct form element when the validation error is | |
| 306 | // issued from content_multiple_value_nodeapi_validate(). | |
| 307 | $items_count = count($items_map); | |
| 308 | if (!empty($elements['#required']) && $items_count > 0) { | |
| 309 | // If the number of removed items is equal to the total number of | |
| 310 | // items, then we'll reset the '_remove' flag of the first item, and | |
| 311 | // that will be used to point the user when the required field error | |
| 312 | // is issued by content_multiple_value_nodeapi_validate(). | |
| 313 | if ($items_count == count(array_filter($items_map))) { | |
| 314 | $delta = key($items_map); | |
| 315 | if (isset($elements[$delta]['_remove'])) { | |
| 316 | $elements[$delta]['_remove']['#value'] = 0; | |
| 317 | } | |
| 318 | } | |
| 319 | } | |
| 320 | ||
| 321 | return $elements; | |
| 322 | } | |
| 323 | ||
| 324 | /** | |
| 325 | * Helper function to deal with items flagged for removal recursively. | |
| 326 | */ | |
| 327 | function content_multiple_value_after_build_recursive(&$elements, $post) { | |
| 328 | foreach (element_children($elements) as $key) { | |
| 329 | if (isset($elements[$key]) && $elements[$key] && !in_array($key, array('_weight', '_remove', '_error_element'))) { | |
| 330 | // Recurse through all children elements. | |
| 331 | content_multiple_value_after_build_recursive($elements[$key], $post); | |
| 332 | } | |
| 333 | } | |
| 334 | ||
| 335 | // Remove values for items flagged for removal. | |
| 336 | if (isset($elements['#value'])) { | |
| 337 | $elements['#value'] = NULL; | |
| 338 | form_set_value($elements, NULL, $form_state); | |
| 339 | $elements['#post'] = $post; | |
| 340 | } | |
| 341 | } | |
| 342 | ||
| 343 | /** | |
| 344 | * Implementation of nodeapi('validate') for multiple value fields | |
| 345 | * managed by content module itself. | |
| 346 | */ | |
| 347 | function content_multiple_value_nodeapi_validate(&$node, $field, &$items, $form) { | |
| 348 | $field_name = $field['field_name']; | |
| 349 | ||
| 350 | // Getting the field structure from the form allows other modules alter | |
| 351 | // field properties such as the required attribute. | |
| 352 | $field = $form['#field_info'][$field_name]; | |
| 353 | ||
| 354 | // Get rid of the add more items element. | |
| 355 | unset($items[$field_name .'_add_more']); | |
| 356 | ||
| 357 | // Reorder items to account for drag-n-drop reordering. | |
| 358 | $items = _content_sort_items($field, $items); | |
| 359 | ||
| 360 | // Create a copy of the items before filtering those that are flagged | |
| 361 | // for removal. We need this copy later to obtain the error element. | |
| 362 | $items_copy = $items; | |
| 363 | ||
| 364 | // Filter out items flagged for removal. | |
| 365 | $items = content_set_empty($field, $items); | |
| 366 | ||
| 367 | // Enforce field requirement settings. | |
| ffa4a96a | 368 | if ($field['required'] && empty($node->_content_ignore_required_fields[$field_name]) && content_access('edit', $field, NULL, $node)) { |
| cb68e6f6 MFM |
369 | // Count non-empty items. |
| 370 | $count = 0; | |
| 371 | $function = $field['module'] .'_content_is_empty'; | |
| 372 | foreach ($items as $item) { | |
| 373 | if (!$function($item, $field)) { | |
| 374 | $count++; | |
| 375 | } | |
| 376 | } | |
| 377 | // The field is required so we expect at least one non-empty item. | |
| 378 | if ($count == 0) { | |
| 379 | // Try to guess the element path in the form from the first item that | |
| 380 | // is not flagged for removal. Defaults to first item. | |
| 381 | $error_element_index = 0; | |
| 382 | foreach ($items_copy as $index => $item) { | |
| 383 | if (empty($item['_remove'])) { | |
| 384 | $error_element_index = $index; | |
| 385 | break; | |
| 386 | } | |
| 387 | } | |
| 388 | $error_element = isset($items_copy[$error_element_index]) && is_array($items_copy[$error_element_index]) && isset($items_copy[$error_element_index]['_error_element']) ? $items_copy[$error_element_index]['_error_element'] : ''; | |
| 389 | form_set_error($error_element, t('%name field is required.', array('%name' => t($field['widget']['label'])))); | |
| 390 | } | |
| 391 | } | |
| 392 | } | |
| 393 | ||
| 394 | /** | |
| dc19d48f YC |
395 | * Submit handler to add more choices to a content form. This handler is used when |
| 396 | * JavaScript is not available. It makes changes to the form state and the | |
| 397 | * entire form is rebuilt during the page reload. | |
| 398 | */ | |
| 399 | function content_add_more_submit($form, &$form_state) { | |
| 400 | // Set the form to rebuild and run submit handlers. | |
| 401 | node_form_submit_build_node($form, $form_state); | |
| 402 | $field_name = $form_state['clicked_button']['#field_name']; | |
| 403 | $type_name = $form_state['clicked_button']['#type_name']; | |
| 404 | ||
| 9372a36c YC |
405 | // Make the changes we want to the form state. |
| 406 | if ($form_state['values'][$field_name][$field_name .'_add_more']) { | |
| 407 | $form_state['item_count'][$field_name] = count($form_state['values'][$field_name]); | |
| dc19d48f YC |
408 | } |
| 409 | } | |
| 410 | ||
| dc19d48f YC |
411 | /** |
| 412 | * Menu callback for AHAH addition of new empty widgets. | |
| 413 | */ | |
| 414 | function content_add_more_js($type_name_url, $field_name) { | |
| 9318808e | 415 | $content_type = content_types($type_name_url); |
| dc19d48f YC |
416 | $type = content_types($type_name_url); |
| 417 | $field = content_fields($field_name, $type['type']); | |
| 880fb507 | 418 | |
| 5dd9ad1b YC |
419 | if (($field['multiple'] != 1) || empty($_POST['form_build_id'])) { |
| 420 | // Invalid request. | |
| 421 | drupal_json(array('data' => '')); | |
| 422 | exit; | |
| dc19d48f YC |
423 | } |
| 424 | ||
| dc19d48f | 425 | // Retrieve the cached form. |
| 880fb507 | 426 | $form_state = array('submitted' => FALSE); |
| 5dd9ad1b YC |
427 | $form_build_id = $_POST['form_build_id']; |
| 428 | $form = form_get_cache($form_build_id, $form_state); | |
| 429 | if (!$form) { | |
| 430 | // Invalid form_build_id. | |
| 431 | drupal_json(array('data' => '')); | |
| 432 | exit; | |
| 433 | } | |
| dc19d48f | 434 | |
| 880fb507 YC |
435 | // We don't simply return a new empty widget to append to existing ones, because |
| 436 | // - ahah.js won't simply let us add a new row to a table | |
| 437 | // - attaching the 'draggable' behavior won't be easy | |
| 438 | // So we resort to rebuilding the whole table of widgets including the existing ones, | |
| 439 | // which makes us jump through a few hoops. | |
| 440 | ||
| 441 | // The form that we get from the cache is unbuilt. We need to build it so that | |
| 442 | // _value callbacks can be executed and $form_state['values'] populated. | |
| 443 | // We only want to affect $form_state['values'], not the $form itself | |
| 444 | // (built forms aren't supposed to enter the cache) nor the rest of $form_data, | |
| 445 | // so we use copies of $form and $form_data. | |
| 446 | $form_copy = $form; | |
| 447 | $form_state_copy = $form_state; | |
| 448 | $form_copy['#post'] = array(); | |
| 449 | form_builder($_POST['form_id'], $form_copy, $form_state_copy); | |
| 450 | // Just grab the data we need. | |
| 451 | $form_state['values'] = $form_state_copy['values']; | |
| 452 | // Reset cached ids, so that they don't affect the actual form we output. | |
| 453 | form_clean_id(NULL, TRUE); | |
| 454 | ||
| 455 | // Sort the $form_state['values'] we just built *and* the incoming $_POST data | |
| 456 | // according to d-n-d reordering. | |
| 457 | unset($form_state['values'][$field_name][$field['field_name'] .'_add_more']); | |
| 458 | foreach ($_POST[$field_name] as $delta => $item) { | |
| 459 | $form_state['values'][$field_name][$delta]['_weight'] = $item['_weight']; | |
| cb68e6f6 | 460 | $form_state['values'][$field_name][$delta]['_remove'] = isset($item['_remove']) ? $item['_remove'] : 0; |
| 880fb507 YC |
461 | } |
| 462 | $form_state['values'][$field_name] = _content_sort_items($field, $form_state['values'][$field_name]); | |
| 463 | $_POST[$field_name] = _content_sort_items($field, $_POST[$field_name]); | |
| 464 | ||
| 465 | // Build our new form element for the whole field, asking for one more element. | |
| cb68e6f6 | 466 | $delta = max(array_keys($_POST[$field_name])) + 1; |
| 880fb507 | 467 | $form_state['item_count'] = array($field_name => count($_POST[$field_name]) + 1); |
| dc19d48f | 468 | $form_element = content_field_form($form, $form_state, $field); |
| 880fb507 | 469 | // Let other modules alter it. |
| 165c7a0c YC |
470 | // We pass an empty array as hook_form_alter's usual 'form_state' parameter, |
| 471 | // instead of $form_atate (for reasons we may never remember). | |
| 472 | // However, this argument is still expected to be passed by-reference | |
| 473 | // (and PHP5.3 will throw an error if it isn't.) This leads to: | |
| 474 | $data = &$form_element; | |
| 475 | $empty_form_state = array(); | |
| 476 | $data['__drupal_alter_by_ref'] = array(&$empty_form_state); | |
| 477 | drupal_alter('form', $data, 'content_add_more_js'); | |
| dc19d48f | 478 | |
| 880fb507 | 479 | // Add the new element at the right place in the (original, unbuilt) form. |
| 9318808e | 480 | content_set_form_element($field_name, $type['type'], $form, $form_element[$field_name]); |
| dc19d48f YC |
481 | |
| 482 | // Save the new definition of the form. | |
| 5dd9ad1b YC |
483 | $form_state['values'] = array(); |
| 484 | form_set_cache($form_build_id, $form, $form_state); | |
| dc19d48f | 485 | |
| 880fb507 YC |
486 | // Build the new form against the incoming $_POST values so that we can |
| 487 | // render the new element. | |
| 880fb507 | 488 | $_POST[$field_name][$delta]['_weight'] = $delta; |
| dc19d48f YC |
489 | $form_state = array('submitted' => FALSE); |
| 490 | $form += array( | |
| 491 | '#post' => $_POST, | |
| 492 | '#programmed' => FALSE, | |
| 493 | ); | |
| 494 | $form = form_builder($_POST['form_id'], $form, $form_state); | |
| 495 | ||
| dc19d48f | 496 | // Render the new output. |
| 9318808e KS |
497 | $field_form = content_get_form_element($field_name, $type['type'], $form); |
| 498 | ||
| dc19d48f | 499 | // We add a div around the new content to receive the ahah effect. |
| dc19d48f YC |
500 | $field_form[$delta]['#prefix'] = '<div class="ahah-new-content">'. (isset($field_form[$delta]['#prefix']) ? $field_form[$delta]['#prefix'] : ''); |
| 501 | $field_form[$delta]['#suffix'] = (isset($field_form[$delta]['#suffix']) ? $field_form[$delta]['#suffix'] : '') .'</div>'; | |
| ede4e955 YC |
502 | // Prevent duplicate wrapper. |
| 503 | unset($field_form['#prefix'], $field_form['#suffix']); | |
| b7ab4f3e YC |
504 | |
| 505 | // If a newly inserted widget contains AHAH behaviors, they normally won't | |
| 506 | // work because AHAH doesn't know about those - it just attaches to the exact | |
| 507 | // form elements that were initially specified in the Drupal.settings object. | |
| 508 | // The new ones didn't exist then, so we need to update Drupal.settings | |
| 509 | // by ourselves in order to let AHAH know about those new form elements. | |
| 510 | $javascript = drupal_add_js(NULL, NULL); | |
| 9bca71e9 | 511 | $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>' : ''; |
| b7ab4f3e | 512 | |
| 9bca71e9 | 513 | $output = theme('status_messages') . drupal_render($field_form) . $output_js; |
| 4b78f6df YC |
514 | |
| 515 | // Using drupal_json() breaks filefield's file upload, because the jQuery | |
| 516 | // Form plugin handles file uploads in a way that is not compatible with | |
| 517 | // 'text/javascript' response type. | |
| f1a5ee1d | 518 | $GLOBALS['devel_shutdown'] = FALSE; |
| 4b78f6df | 519 | print drupal_to_js(array('status' => TRUE, 'data' => $output)); |
| 5dd9ad1b | 520 | exit; |
| dc19d48f | 521 | } |
| 9318808e KS |
522 | |
| 523 | /** | |
| 524 | * Store an element into a form. | |
| 525 | * | |
| 526 | * @param $name | |
| 527 | * The field name. | |
| 528 | * @param $type | |
| 529 | * The content type where the field instance belongs to. | |
| 530 | * @param $form | |
| 531 | * The form to store this field element into. | |
| 532 | * @param $element | |
| 533 | * The form element to store. | |
| 534 | */ | |
| 535 | function content_set_form_element($name, $type, &$form, $element) { | |
| 536 | $is_group = substr($name, 0, 6) == 'group_'; | |
| 537 | if (module_exists('fieldgroup') && ($parents = fieldgroup_get_parents($type, $name))) { | |
| 538 | $reference = &$form; | |
| 539 | if ($is_group) { | |
| 540 | array_shift($parents); | |
| 541 | $parents = array_reverse($parents); | |
| 542 | } | |
| 543 | else { | |
| 544 | $parents = array_reverse($parents); | |
| 545 | } | |
| 546 | ||
| 547 | foreach (array_values($parents) as $group_name) { | |
| 548 | $reference = &$reference[$group_name]; | |
| 549 | } | |
| 550 | ||
| 551 | $reference[$name] = $element; | |
| 552 | ||
| 553 | } | |
| 554 | else { | |
| 555 | $form[$name] = $element; | |
| 556 | } | |
| 557 | } | |
| 558 | ||
| 559 | /** | |
| 560 | * Retrieve an element from a form. | |
| 561 | * | |
| 562 | * @param $name | |
| 563 | * The field name. | |
| 564 | * @param $type | |
| 565 | * The content type where the field instance belongs to. | |
| 566 | * @param $form | |
| 567 | * The form to retrieve this field element from. | |
| 568 | */ | |
| 569 | function content_get_form_element($name, $type, $form) { | |
| 570 | $is_group = substr($name, 0, 6) == 'group_'; | |
| 571 | if (module_exists('fieldgroup') && ($parents = fieldgroup_get_parents($type, $name))) { | |
| 572 | $reference = &$form; | |
| 573 | if ($is_group) { | |
| 574 | array_shift($parents); | |
| 575 | $parents = array_reverse($parents); | |
| 576 | } | |
| 577 | else { | |
| 578 | $parents = array_reverse($parents); | |
| 579 | } | |
| 580 | ||
| 581 | foreach (array_values($parents) as $group_name) { | |
| 582 | $reference = &$reference[$group_name]; | |
| 583 | } | |
| 584 | ||
| 585 | return $reference[$name]; | |
| 586 | } | |
| 587 | else { | |
| 588 | return $form[$name]; | |
| 589 | } | |
| 590 | } | |
| 591 |