Removing translation directories
[project/filefield.git] / filefield.widget.inc
1 <?php
2 /**
3 * @file
4 * FileField: Defines a CCK file field type.
5 *
6 * Uses content.module to store the fid and field specific metadata,
7 * and Drupal's {files} table to store the actual file data.
8 *
9 * This file contains CCK widget related functionality.
10 */
11
12 /**
13 * Implementation of hook_widget() - the one in filefield.module
14 * is just there to include this one on demand.
15 */
16 function filefield_widget(&$form, &$form_state, $field, $items, $delta = 0) {
17 // JavaScript might reload the form element but not the CSS that has been
18 // defined inside the process hook of mimetype specific widgets. As we don't
19 // know which widget will show up, just include all of their CSS files.
20 $file_widget_info = _filefield_file_widget_info($field['widget']);
21 _filefield_add_css($file_widget_info);
22 drupal_add_css(drupal_get_path('module', 'filefield') .'/filefield.css');
23
24 if (!empty($items[$delta]) && $file = field_file_load($items[$delta]['fid'])) {
25 $file = array_merge($items[$delta], $file);
26 return filefield_file_edit_form($form, $form_state, $field, $delta, $file);
27 }
28 return filefield_file_upload_form($form, $form_state, $field, $delta, $items);
29 }
30
31 /**
32 * The filefield widget for not (yet) existing files.
33 */
34 function filefield_file_upload_form(&$form, &$form_state, $field, $delta, $items) {
35 $form['#attributes']['enctype'] = 'multipart/form-data';
36
37 // Include JavaScript for client-side file validation.
38 drupal_add_js(drupal_get_path('module', 'filefield') .'/filefield.js');
39
40 $field_name_css = str_replace('_', '-', $field['field_name']);
41 $id = 'filefield-'. $field_name_css .'-'. $delta .'-form';
42
43 $replaced_file = (isset($items[$delta]) && isset($items[$delta]['replaced_file']))
44 ? $items[$delta]['replaced_file'] : NULL;
45
46 $requirements = _filefield_upload_requirements($field, $field['widget'], $items);
47
48 $widget = array(
49 '#type' => 'filefield_file_upload',
50 '#field' => $field,
51 '#delta' => $delta,
52 '#replaced_file' => $replaced_file,
53 '#prefix' => '<div id="'. $id .'" class="filefield-file-form"><div class="filefield-file-upload">',
54 '#suffix' => '</div></div>',
55 '#upload_requirements' => $requirements,
56 );
57
58 if ($requirements['upload possible']) {
59 // Buttons inside custom form elements are not registered by the Forms API,
60 // so we make the "Upload" button a regular child element and not a part
61 // of the filefield_file_upload widget.
62 $widget[$field['field_name'] .'_'. $delta .'_upload'] = array(
63 '#name' => $field['field_name'] .'_'. $delta .'_upload',
64 '#type' => 'submit',
65 '#value' => t('Upload'),
66 '#submit' => array('filefield_file_upload_submit'), // without JavaScript
67 '#ahah' => array( // with JavaScript
68 'path' => 'filefield/js/upload/'. $field['field_name'] .'/'. $field['type_name'] .'/'. $delta,
69 'wrapper' => $id,
70 'method' => 'replace',
71 'effect' => 'fade',
72 ),
73 '#weight' => 10,
74 '#field' => $field,
75 '#delta' => $delta,
76 );
77 }
78 return $widget;
79 }
80
81 /**
82 * The 'process' callback for 'filefield_file_upload' form elements.
83 * Called after defining the form and while building it, transforms the
84 * barebone element array into a file selection widget.
85 */
86 function filefield_file_upload_process($element, $edit, &$form_state, $form) {
87 // Before the element user gets to do his validation, make sure we do ours.
88 array_unshift($element['#element_validate'], 'filefield_file_upload_validate');
89
90 $field = $element['#field'];
91 $field_name = $field['field_name'];
92 $upload_name = $field_name .'_'. $element['#delta'];
93
94 $requirements = $element['#upload_requirements'];
95
96 // Construct the upload description out of user supplied text,
97 // maximum upload file size, and (optionally) allowed extensions.
98 if ($requirements['upload possible'] == FALSE) {
99 $element[$upload_name] = array(
100 '#type' => 'markup',
101 '#value' => t('!errors No new files can be uploaded anymore.', array(
102 '!errors' => implode(' ', $requirements['messages']),
103 )),
104 );
105 return $element;
106 }
107
108 // Make a list out of the messages if there are too many restrictions.
109 // Looks better than a concatenated sequence of sentences.
110 $upload_description = (count($requirements['messages']) > 2)
111 ? '<ul><li>'. implode('</li><li>', $requirements['messages']) .'</li></ul>'
112 : implode(' ', $requirements['messages']);
113
114 $element[$upload_name] = array(
115 '#type' => 'file',
116 '#title' => t('Attach new file'),
117 '#description' => $upload_description,
118 '#weight' => -1,
119 // Emulate how FAPI normalizes the _FILES array since this won't go through form_builder
120 '#name' => 'files['. $upload_name .']',
121 );
122
123 // User 1 may even upload files with extensions that are not allowed.
124 // (At least, that's how core's file_validate_extensions() thinks about it.)
125 // So only add the JavaScript extension check for other users.
126 global $user;
127 if ($user->uid != 1) {
128 $element[$upload_name]['#attributes'] = array(
129 'accept' => str_replace(' ', '|', trim($field['widget']['file_extensions']))
130 );
131 }
132
133 return $element;
134 }
135
136 /**
137 * Theme function for the file upload container element.
138 */
139 function theme_filefield_file_upload($element) {
140 return theme('filefield_container_item', $element);
141 }
142
143 /**
144 * Value callback for 'filefield_upload' form elements.
145 * Uploads and validates a file if one has been specified,
146 * and returns the fid of that file as result value.
147 */
148 function filefield_file_upload_value($element, $edit = FALSE) {
149 return empty($element['#value'])
150 ? array('fid' => 0, 'replaced_file' => $element['#replaced_file'])
151 : $element['#value'];
152 }
153
154 /**
155 * The 'validate' callback for 'duration_combo' form elements.
156 * Called after values are assigned, before form validate and submit are called.
157 */
158 function filefield_file_upload_validate($element, &$form_state) {
159 if (!empty($element['#required']) && empty($element['#value']['fid'])) {
160 form_error($element, t('You need to upload a file to the %field field.', array(
161 '%field' => $element['#field']['widget']['label']
162 )));
163 }
164 }
165
166 /**
167 * Submit callback for the "Upload" button next to each file upload field.
168 */
169 function filefield_file_upload_submit($form, &$form_state) {
170 $field = $form_state['clicked_button']['#field'];
171 $delta = $form_state['clicked_button']['#delta'];
172 _filefield_file_upload($form_state, $field, $delta);
173
174 // Rebuild the form with the new uploaded-file state (hopefully).
175 node_form_submit_build_node($form, $form_state);
176 }
177
178 /**
179 * Form callback for the "Upload" button with JavaScript enabled,
180 * invoked by filefield_js(). Uploads a file to the given field and delta.
181 */
182 function filefield_file_upload_js(&$form, &$form_state, $field, $delta) {
183 _filefield_file_upload($form_state, $field, $delta);
184 }
185
186 /**
187 * Upload a file to the given field and delta (or try to, at least), and
188 * update the corresponding part of the form state with the new file data.
189 */
190 function _filefield_file_upload(&$form_state, $field, $delta) {
191 $field_name = $field['field_name'];
192
193 if (module_exists('token')) {
194 global $user;
195 $widget_file_path = token_replace($field['widget']['file_path'], 'user', $user);
196 }
197 else {
198 $widget_file_path = $field['widget']['file_path'];
199 }
200
201 // Let modules provide their own validators.
202 $validators = _filefield_upload_validators(
203 $field, $field['widget'], $form_state['values'][$field_name]
204 );
205 $upload_name = $field_name .'_'. $delta;
206 $complete_file_path = file_directory_path() .'/'. $widget_file_path;
207
208 $file = &$form_state['values'][$field_name][$delta];
209 $replaced_file = $file['replaced_file'];
210
211 if (!filefield_check_directory($widget_file_path, $upload_name)) {
212 watchdog('file', 'The upload directory %directory for the file field %field (content type %type) could not be created or is not accessible. A newly uploaded file could not be saved in this directory as a consequence, and the upload was canceled.', array('%directory' => $widget_file_path, '%field' => $field_name, '%type' => $field['type_name']));
213 $file = array('fid' => 0, 'replaced_file' => $replaced_file);
214 return $file;
215 }
216 if (!$file = field_file_save_upload($upload_name, $validators, $complete_file_path)) {
217 watchdog('file', 'The file %file could not be saved as addition to the file field %field (content type %type). This can be a consequence of the file failing validation, or if it can\'t be moved to the file directory, or whatever reason the file framework comes up with. No further information is available to the filefield module, but if you\'re lucky then that function left one or more hints in the log as well (directly before this log entry).', array('%file' => $complete_file_path, '%field' => $field_name, '%type' => $field['type_name']));
218 $file = array('fid' => 0, 'replaced_file' => $replaced_file);
219 return $file;
220 }
221
222 $file_default_properties = array(
223 'list' => 1,
224 'description' => $file['filename'],
225 );
226 $file = array_merge($file_default_properties, $file);
227 $file['replaced_file'] = $replaced_file;
228 return $file;
229 }
230
231 /**
232 * Retrieve a list of file validator functions (and arguments) that can be
233 * passed to file_save_upload() (or field_file_save_upload()) as is.
234 */
235 function _filefield_upload_validators($field, $widget, $items) {
236 $validator_info = _filefield_validator_info($field, $widget, $items);
237 $validators = array();
238
239 foreach ($validator_info as $validator_function => $info) {
240 $validators[$validator_function] = $info['validator arguments'];
241 }
242 return $validators;
243 }
244
245 /**
246 * Retrieve a list of upload requirement strings for the various upload
247 * restrictions that this module and possible extensions provide.
248 */
249 function _filefield_upload_requirements($field, $widget, $items) {
250 $validator_info = _filefield_validator_info($field, $widget, $items);
251 $messages = array();
252 $errors = array();
253
254 foreach ($validator_info as $validator_function => $info) {
255 $messages[] = $info['requirement message'];
256
257 if (isset($info['upload possible']) && $info['upload possible'] == FALSE) {
258 $errors[] = $info['requirement message'];
259 }
260 }
261 return array(
262 'messages' => empty($errors) ? $messages : $errors,
263 'upload possible' => empty($errors),
264 );
265 }
266
267 /**
268 * Retrieve an array of file validators and their associated requirement
269 * messages (placing filefield's own validators first in the result array).
270 */
271 function _filefield_validator_info($field, $widget, $items) {
272 // Clean out empty items, so that they're not taken into account by
273 // implementations of hook_filefield_validators() - they're irrelevant here.
274 // Also, objectify items - because we're leaving the pure filefield realm.
275 $existing_files = array();
276 foreach ($items as $delta => $item) {
277 if (is_array($item) && !empty($item['fid'])) {
278 $existing_files[] = (object) $item;
279 }
280 }
281 return array_merge(
282 _filefield_filefield_validators($field, $widget, $existing_files),
283 module_invoke_all('filefield_validators', $field, $widget, $existing_files)
284 );
285 }
286
287 /**
288 * Implementation of hook_filefield_validators():
289 * Upload restrictions for file size, file extension and supported file widgets.
290 * Implemented as private function instead of as a real hook, because we want
291 * to make an exception so that these requirements appear first in any list.
292 */
293 function _filefield_filefield_validators($field, $widget, $existing_files) {
294 $validators = array();
295
296 // Thanks to the php.ini restrictions, there is always a maximum file size.
297 // Therefore we can rely on at least one restriction always being in force.
298 $max_filesize = _filefield_maximum_filesize(
299 $field, $widget, $existing_files
300 );
301 $filesize_message = ($max_filesize > 0)
302 ? t('Maximum file size: !size.', array('!size' => format_size($max_filesize)))
303 : t('The allowed maximum file size total has been exceeded.');
304
305 $validators['file_validate_size'] = array(
306 'validator arguments' => array($max_filesize),
307 'requirement message' => $filesize_message,
308 'upload possible' => ($max_filesize > 0),
309 );
310
311 if (!empty($widget['file_extensions'])) {
312 $validators['file_validate_extensions'] = array(
313 'validator arguments' => array($widget['file_extensions']),
314 'requirement message' => t('Allowed extensions: %ext.', array(
315 '%ext' => $widget['file_extensions'],
316 )),
317 );
318 }
319
320 $supported_file_widgets = _filefield_supported_file_widgets($widget);
321 if (!empty($supported_file_widgets)) {
322 $validators['filefield_validate_file_widget_support'] = array(
323 'validator arguments' => array($field, $widget, $supported_file_widgets),
324 'requirement message' => t('Uploads are restricted to the following categories: !widgets.', array(
325 '!widgets' => implode(', ', $supported_file_widgets),
326 )),
327 );
328 }
329 return $validators;
330 }
331
332 /**
333 * Check that a file is supported by at least one of the widgets that are
334 * enabled for the field instance in question.
335 *
336 * @return
337 * An array. If the file is not allowed, it will contain an error message.
338 */
339 function filefield_validate_file_widget_support($file, $field, $field_widget, $supported_file_widgets) {
340 $errors = array();
341
342 // No widgets at all means the widget settings db entry does not exist,
343 // so we fall back to "accept this file and use the generic edit widget".
344 if (empty($field_widget['file_widgets'])) {
345 return $errors;
346 }
347 // In the common case, we only accept a file if an enabled widget
348 // wants to handle it.
349 $edit_widget_info = filefield_widget_for_file($file, $field, $field_widget);
350 if (empty($edit_widget_info)) {
351 $errors[] = t('Uploaded files are restricted to the following categories: !widgets.', array(
352 '!widgets' => implode(', ', $supported_file_widgets),
353 ));
354 }
355 return $errors;
356 }
357
358 /**
359 * If not all file types might be handled by the enabled set of file widgets,
360 * return an array of widget titles specifying which ones are allowed for the
361 * given field. If a widget is enabled which handles all files, return an empty array.
362 */
363 function _filefield_supported_file_widgets($field_widget) {
364 if (empty($field_widget['file_widgets'])) {
365 return array();
366 }
367 $titles = array();
368 $file_widget_info = _filefield_file_widget_info($field_widget);
369
370 foreach ($file_widget_info as $widget_name => $info) {
371 if (!$info['enabled']) {
372 continue;
373 }
374 if ($info['suitability callback'] === TRUE) {
375 // Handles all kinds of files, no need for requiring any other widget.
376 return array();
377 }
378 $titles[] = $info['title'];
379 }
380 return $titles;
381 }
382
383 /**
384 * Get the maximum file size that is allowed for a new upload.
385 *
386 * @return
387 * -1 for "no more files allowed", or any positive value as the number
388 * of bytes that may still be uploaded. A result of 0 ("unlimited") will
389 * never happen because of PHP's upload limits.)
390 */
391 function _filefield_maximum_filesize($field, $widget, $existing_files) {
392 // Calculate the maximum file size - the least of all returned values.
393 $max_filesize = FALSE;
394 $restrictions = module_invoke_all(
395 'filefield_filesize_restrictions', $field, $widget, $existing_files
396 );
397 foreach ($restrictions as $value) {
398 if ($max_filesize === FALSE || $value < $max_filesize) {
399 $max_filesize = $value;
400 }
401 }
402
403 // Return -1 if any restriction value was not a positive number.
404 if ($max_filesize === FALSE || $max_filesize <= 0) {
405 return -1;
406 }
407 return $max_filesize;
408 }
409
410 /**
411 * Implementation of hook_filefield_filesize_restrictions():
412 * Specify how large a newly uploaded file may be, in bytes.
413 * (The smallest size of all hook implementations will be applied in the end).
414 */
415 function filefield_filefield_filesize_restrictions($field, $widget, $existing_files) {
416 $filesize_restrictions = array(file_upload_max_size());
417
418 // Maximum file size for each file separately.
419 if (!empty($widget['max_filesize_per_file'])) {
420 $filesize_restrictions[] = parse_size($widget['max_filesize_per_file']);
421 }
422
423 // Maximum file size for all files in the node (for this field).
424 if (!empty($widget['max_filesize_per_node'])) {
425 $allowed_total_size = parse_size($widget['max_filesize_per_node']);
426 $total_size = 0;
427
428 foreach ($existing_files as $delta => $file) {
429 if (!empty($file->filesize)) {
430 $total_size += $file->filesize;
431 }
432 }
433 if (!empty($total_size)) {
434 $filesize_restrictions[] = $allowed_total_size - $total_size;
435 }
436 }
437 return $filesize_restrictions;
438 }
439
440 /**
441 * Create the file directory relative to the 'files' dir recursively for every
442 * directory in the path.
443 *
444 * @param $directory
445 * The directory path under files to check, such as 'photo/path/here'.
446 * @param $form_item
447 * An optional string containing the name of a form item that any errors
448 * will be attached to. (See field_file_check_directory() for more details.)
449 */
450 function filefield_check_directory($directory, $form_item = NULL) {
451 $directory = field_file_strip_path($directory);
452
453 foreach (explode('/', $directory) as $dir) {
454 $dirs[] = $dir;
455 $path = file_create_path(implode($dirs, '/'));
456 if (!field_file_check_directory($path, FILE_CREATE_DIRECTORY, $form_item)) {
457 watchdog('filefield', t('FileField failed to create directory (%d) at (%p).', array('%d' => $directory, '%p' => $path)), WATCHDOG_ERROR);
458 return FALSE;
459 }
460 }
461 return TRUE;
462 }
463
464
465 /**
466 * The filefield widget for previously uploaded files.
467 */
468 function filefield_file_edit_form(&$form, &$form_state, $field, $delta, $file) {
469 $field_name_css = str_replace('_', '-', $field['field_name']);
470 $id = 'filefield-'. $field_name_css .'-'. $delta .'-form';
471
472 $classes = array(
473 'filefield-'. $field_name_css .'-form',
474 'filefield-file-form',
475 );
476 $widget = array(
477 '#type' => 'filefield_file_edit',
478 '#default_value' => $file,
479 '#field' => $field,
480 '#prefix' => '<div id="'. $id .'" class="'. implode(' ', $classes) .'"><div class="filefield-file-edit">',
481 '#suffix' => '</div></div>',
482 );
483
484 // Buttons inside custom form elements are not registered by the Forms API,
485 // so we make the "Delete" button a regular child element and not a part
486 // of the filefield_file_upload widget.
487 $widget['flags'] = array(
488 '#type' => 'markup',
489 '#value' => '',
490 '#prefix' => '<div class="filefield-file-edit-flags">',
491 '#suffix' => '</div>',
492 );
493 $widget['flags'][$field['field_name'] .'_'. $delta .'_delete'] = array(
494 '#name' => $field['field_name'] .'_'. $delta .'_delete',
495 '#type' => 'submit',
496 '#value' => t('Delete'),
497 '#submit' => array('filefield_file_edit_delete_submit'), // without JavaScript
498 '#ahah' => array( // with JavaScript
499 'path' => 'filefield/js/delete/'. $field['field_name'] .'/'. $field['type_name'] .'/'. $delta,
500 'wrapper' => $id,
501 'method' => 'replace',
502 'effect' => 'fade',
503 ),
504 '#field' => $field,
505 '#delta' => $delta,
506 '#file' => $file,
507 );
508 // Only show the list checkbox if files are not forced to be listed.
509 if (!$field['force_list']) {
510 $widget['flags']['list'] = array(
511 '#type' => 'checkbox',
512 '#title' => t('List'),
513 '#default_value' => $file['list'],
514 );
515 }
516
517 $edit_widget_info = filefield_widget_for_file($file, $field, $field['widget']);
518 $widget['edit'] = array(
519 '#type' => empty($edit_widget_info)
520 ? 'filefield_generic_edit' // as last-resort fallback
521 : $edit_widget_info['form element'],
522 '#field' => $field,
523 '#delta' => $delta,
524 '#file' => (object) $file,
525 '#default_value' => $file,
526 '#prefix' => '<div class="filefield-file-edit-widget">',
527 '#suffix' => '</div>',
528 );
529
530 return $widget;
531 }
532
533 /**
534 * Theme function for the file edit container element.
535 */
536 function theme_filefield_file_edit($element) {
537 return theme('filefield_container_item', $element);
538 }
539
540 /**
541 * Custom value callback for file edit widgets, so that we don't need to rely
542 * on a tree structure but can assemble the file to our likings.
543 */
544 function filefield_file_edit_value($element, $edit = FALSE) {
545 $file = $element['#default_value'];
546
547 if (!is_array($edit)) {
548 return $file;
549 }
550
551 // Form API is being mean to us and doesn't include the 'list' value
552 // for checkboxes if the checkbox is disabled (0), only if it's enabled (1).
553 // That means we need to go the extra mile.
554 $list = isset($element['flags']['list'])
555 ? !empty($edit['flags']['list'])
556 : $file['list'];
557
558 $file_fixed_properties = array(
559 'list' => $list,
560 'delete' => 0,
561 'fid' => $file['fid'],
562 'uid' => $file['uid'],
563 'status' => $file['status'],
564 'filename' => $file['filename'],
565 'filepath' => $file['filepath'],
566 'filemime' => $file['filemime'],
567 'filesize' => $file['filesize'],
568 'timestamp' => $file['timestamp'],
569 );
570
571 if (is_array($edit['edit'])) {
572 $file = array_merge($file, $edit['edit']);
573 }
574 $file = array_merge($file, $file_fixed_properties);
575
576 // Also merge in other values that might come from other child form elements,
577 // like the '_weight' property that CCK adds to this field.
578 unset($edit['flags']);
579 unset($edit['edit']);
580 $file = array_merge($edit, $file);
581
582 return $file;
583 }
584
585 /**
586 * Submit callback for the "Delete" button next to each file item.
587 */
588 function filefield_file_edit_delete_submit($form, &$form_state) {
589 $field = $form_state['clicked_button']['#field'];
590 $delta = $form_state['clicked_button']['#delta'];
591 filefield_file_edit_delete($form_state, $field, $delta);
592
593 // Rebuild the form with the new deleted-file state.
594 node_form_submit_build_node($form, $form_state);
595 }
596
597 /**
598 * Form callback for the "Delete" button with JavaScript enabled,
599 * invoked by filefield_js(). Marks the file in the given field and delta
600 * as deleted, or deletes it right away (depending on the context).
601 */
602 function filefield_file_edit_delete_js(&$form, &$form_state, $field, $delta) {
603 filefield_file_edit_delete($form_state, $field, $delta);
604 }
605
606 /**
607 * Update the form state so that the file for the given field and delta
608 * is marked as deleted.
609 */
610 function filefield_file_edit_delete(&$form_state, $field, $delta) {
611 $field_name = $field['field_name'];
612 $file = &$form_state['values'][$field_name][$delta];
613
614 if (isset($file['status']) && $file['status'] == FILE_STATUS_PERMANENT) {
615 $file['delete'] = 1;
616 $file = array(
617 'fid' => 0,
618 'replaced_file' => $file,
619 );
620 }
621 else { // temporary file, get rid of it before it's even saved
622 $empty_file = array(
623 'fid' => 0,
624 'replaced_file' => $file['replaced_file'], // remember permanent files from before
625 );
626 field_file_delete($file);
627 $file = $empty_file;
628 }
629 return $file;
630 }
631
632 /**
633 * Shared AHAH callback for uploads and deletions. It just differs in a few
634 * unimportant details (what happens to the file, and which form is used as
635 * a replacement) so these details are taken care of by a form callback.
636 */
637 function filefield_js($field_name, $type_name, $delta, $form_callback) {
638 $field = content_fields($field_name, $type_name);
639
640 if (empty($field) || empty($_POST['form_build_id'])) {
641 // Invalid request.
642 print drupal_to_js(array('data' => ''));
643 exit;
644 }
645
646 // Build the new form.
647 $form_state = array('submitted' => FALSE);
648 $form_build_id = $_POST['form_build_id'];
649 $form = form_get_cache($form_build_id, $form_state);
650
651 if (!$form) {
652 // Invalid form_build_id.
653 print drupal_to_js(array('data' => ''));
654 exit;
655 }
656 // form_get_cache() doesn't yield the original $form_state,
657 // but form_builder() does. Needed for retrieving the file array.
658 $built_form = $form;
659 $built_form_state = $form_state;
660 $built_form += array('#post' => $_POST);
661 $built_form = form_builder($_POST['form_id'], $built_form, $built_form_state);
662
663 // Clean ids, so that the same element doesn't get a different element id
664 // when rendered once more further down.
665 form_clean_id(NULL, TRUE);
666
667 // Perform the action for this AHAH callback.
668 $form_callback($built_form, $built_form_state, $field, $delta);
669
670 // Ask CCK for the replacement form element. Going through CCK gets us
671 // the benefit of nice stuff like '#required' merged in correctly.
672 module_load_include('inc', 'content', 'includes/content.node_form');
673 $field_element = content_field_form($form, $built_form_state, $field, $delta);
674 $delta_element = $field_element[$field_name][0]; // there's only one element in there
675
676 // Add the new element at the right place in the form.
677 if (module_exists('fieldgroup') && ($group_name = _fieldgroup_field_get_group($type_name, $field_name))) {
678 $form[$group_name][$field_name][$delta] = $delta_element;
679 }
680 else {
681 $form[$field_name][$delta] = $delta_element;
682 }
683
684 // Write the (unbuilt, updated) form back to the form cache.
685 form_set_cache($form_build_id, $form, $form_state);
686
687 // Render the form for output.
688 $form += array(
689 '#post' => $_POST,
690 '#programmed' => FALSE,
691 );
692 drupal_alter('form', $form, array(), 'filefield_js');
693 $form_state = array('submitted' => FALSE);
694 $form = form_builder('filefield_js', $form, $form_state);
695 $field_form = empty($group_name) ? $form[$field_name] : $form[$group_name][$field_name];
696
697 // We add a div around the new content to tell AHAH to let this fade in.
698 $field_form[$delta]['#prefix'] = '<div class="ahah-new-content">'. (isset($field_form[$delta]['#prefix']) ? $field_form[$delta]['#prefix'] : '');
699 $field_form[$delta]['#suffix'] = (isset($field_form[$delta]['#suffix']) ? $field_form[$delta]['#suffix'] : '') .'</div>';
700
701 $output = theme('status_messages') . drupal_render($field_form[$delta]);
702
703 // AHAH is not being nice to us and doesn't know the "other" button (that is,
704 // either "Upload" or "Delete") yet. Which in turn causes it not to attach
705 // AHAH behaviours after replacing the element. So we need to tell it first.
706 $javascript = drupal_add_js(NULL, NULL);
707 if (isset($javascript['setting'])) {
708 $output .= '<script type="text/javascript">jQuery.extend(Drupal.settings, '. drupal_to_js(call_user_func_array('array_merge_recursive', $javascript['setting'])) .');</script>';
709 }
710
711 // For some reason, file uploads don't like drupal_json() with its manual
712 // setting of the text/javascript HTTP header. So use this one instead.
713 print drupal_to_js(array('status' => TRUE, 'data' => $output));
714 exit;
715 }