Code coverage for /20080809/modules/upload/upload.module

Line #Times calledCode
1
<?php
2
// $Id: upload.module,v 1.205 2008/07/24 16:25:19 dries Exp $
3
4
/**
5
 * @file
6
 * File-handling and attaching files to nodes.
7
 *
8
 */
9
10
/**
11
 * Implementation of hook_help().
12
 */
1362
function upload_help($path, $arg) {
14
  switch ($path) {
1542
    case 'admin/help#upload':
160
      $output = '<p>' . t('The upload module allows users to upload files
to the site. The ability to upload files is important for members of a
community who want to share work. It is also useful to administrators who
want to keep uploaded files connected to posts.') . '</p>';
170
      $output .= '<p>' . t('Users with the upload files permission can
upload attachments to posts. Uploads may be enabled for specific content
types on the content types settings page. Each user role can be customized
to limit or control the file size of uploads, or the maximum dimension of
image files.') . '</p>';
180
      $output .= '<p>' . t('For more information, see the online handbook
entry for <a href="@upload">Upload module</a>.', array('@upload' =>
'http://drupal.org/handbook/modules/upload/')) . '</p>';
190
      return $output;
2042
    case 'admin/settings/upload':
210
      return '<p>' . t('Users with the <a href="@permissions">upload files
permission</a> can upload attachments. Users with the <a
href="@permissions">view uploaded files permission</a> can view uploaded
attachments. You can choose which post types can take attachments on the <a
href="@types">content types settings</a> page.', array('@permissions' =>
url('admin/user/permissions'), '@types' => url('admin/settings/types'))) .
'</p>';
220
  }
2342
}
24
25
/**
26
 * Implementation of hook_theme().
27
 */
2862
function upload_theme() {
29
  return array(
30
    'upload_attachments' => array(
316
      'arguments' => array('files' => NULL),
326
    ),
33
    'upload_form_current' => array(
346
      'arguments' => array('form' => NULL),
356
    ),
36
    'upload_form_new' => array(
376
      'arguments' => array('form' => NULL),
386
    ),
396
  );
400
}
41
42
/**
43
 * Implementation of hook_perm().
44
 */
4562
function upload_perm() {
46
  return array(
471
    'upload files' => t('Attach images and other files to content.'),
481
    'view uploaded files' => t('View and download files attached to
content.'),
491
  );
500
}
51
52
/**
53
 * Implementation of hook_link().
54
 */
5562
function upload_link($type, $node = NULL, $teaser = FALSE) {
567
  $links = array();
57
58
  // Display a link with the number of attachments
597
  if ($teaser && $type == 'node' && isset($node->files) &&
user_access('view uploaded files')) {
600
    $num_files = 0;
610
    foreach ($node->files as $file) {
620
      if ($file->list) {
630
        $num_files++;
640
      }
650
    }
660
    if ($num_files) {
670
      $links['upload_attachments'] = array(
680
        'title' => format_plural($num_files, '1 attachment', '@count
attachments'),
690
        'href' => "node/$node->nid",
700
        'attributes' => array('title' => t('Read full article to view
attachments.')),
71
        'fragment' => 'attachments'
720
      );
730
    }
740
  }
75
767
  return $links;
770
}
78
79
/**
80
 * Implementation of hook_menu().
81
 */
8262
function upload_menu() {
831
  $items['upload/js'] = array(
841
    'page callback' => 'upload_js',
851
    'access arguments' => array('upload files'),
861
    'type' => MENU_CALLBACK,
87
  );
881
  $items['admin/settings/uploads'] = array(
891
    'title' => 'File uploads',
901
    'description' => 'Control how files may be attached to content.',
911
    'page callback' => 'drupal_get_form',
921
    'page arguments' => array('upload_admin_settings'),
931
    'access arguments' => array('administer site configuration'),
941
    'type' => MENU_NORMAL_ITEM,
95
  );
961
  return $items;
970
}
98
9962
function upload_menu_alter(&$items) {
1001
  $items['system/files']['access arguments'] = array('view uploaded
files');
1011
}
102
103
/**
104
 * Determine the limitations on files that a given user may upload. The
user
105
 * may be in multiple roles so we select the most permissive limitations
from
106
 * all of their roles.
107
 *
108
 * @param $user
109
 *   A Drupal user object.
110
 * @return
111
 *   An associative array with the following keys:
112
 *     'extensions'
113
 *       A white space separated string containing all the file extensions
this
114
 *       user may upload.
115
 *     'file_size'
116
 *       The maximum size of a file upload in bytes.
117
 *     'user_size'
118
 *       The total number of bytes for all for a user's files.
119
 *     'resolution'
120
 *       A string specifying the maximum resolution of images.
121
 */
12262
function _upload_file_limits($user) {
12314
  $file_limit = variable_get('upload_uploadsize_default', 1);
12414
  $user_limit = variable_get('upload_usersize_default', 1);
12514
  $all_extensions = explode(' ', variable_get('upload_extensions_default',
'jpg jpeg gif png txt doc xls pdf ppt pps odt ods odp'));
12614
  foreach ($user->roles as $rid => $name) {
12714
    $extensions = variable_get("upload_extensions_$rid",
variable_get('upload_extensions_default', 'jpg jpeg gif png txt doc xls pdf
ppt pps odt ods odp'));
12814
    $all_extensions = array_merge($all_extensions, explode(' ',
$extensions));
129
130
    // A zero value indicates no limit, take the least restrictive limit.
13114
    $file_size = variable_get("upload_uploadsize_$rid",
variable_get('upload_uploadsize_default', 1)) * 1024 * 1024;
13214
    $file_limit = ($file_limit && $file_size) ? max($file_limit,
$file_size) : 0;
133
13414
    $user_size = variable_get("upload_usersize_$rid",
variable_get('upload_usersize_default', 1)) * 1024 * 1024;
13514
    $user_limit = ($user_limit && $user_size) ? max($user_limit,
$user_size) : 0;
13614
  }
13714
  $all_extensions = implode(' ', array_unique($all_extensions));
138
  return array(
13914
    'extensions' => $all_extensions,
14014
    'file_size' => $file_limit,
14114
    'user_size' => $user_limit,
14214
    'resolution' => variable_get('upload_max_resolution', 0),
14314
  );
1440
}
145
146
/**
147
 * Implementation of hook_file_download().
148
 */
14962
function upload_file_download($filepath) {
1500
  $filepath = file_create_path($filepath);
1510
  $result = db_query("SELECT f.* FROM {files} f INNER JOIN {upload} u ON
f.fid = u.fid WHERE filepath = '%s'", $filepath);
1520
  if ($file = db_fetch_object($result)) {
1530
    if (!user_access('view uploaded files')) {
1540
      return -1;
1550
    }
156
    return array(
1570
      'Content-Type: ' . $file->filemime,
1580
      'Content-Length: ' . $file->filesize,
1590
    );
1600
  }
1610
}
162
163
/**
164
 * Save new uploads and store them in the session to be associated to the
node
165
 * on upload_save.
166
 *
167
 * @param $node
168
 *   A node object to associate with uploaded files.
169
 */
17062
function upload_node_form_submit($form, &$form_state) {
1717
  global $user;
172
1737
  $limits = _upload_file_limits($user);
174
  $validators = array(
1757
    'file_validate_extensions' => array($limits['extensions']),
1767
    'file_validate_image_resolution' => array($limits['resolution']),
1777
    'file_validate_size' => array($limits['file_size'],
$limits['user_size']),
1787
  );
179
180
  // Save new file uploads.
1817
  if (($user->uid != 1 || user_access('upload files')) && ($file =
file_save_upload('upload', $validators, file_directory_path()))) {
1823
    $file->list = variable_get('upload_list_default', 1);
1833
    $file->description = $file->filename;
1843
    $file->weight = 0;
1853
    $_SESSION['upload_files'][$file->fid] = $file;
1863
  }
187
188
  // Attach session files to node.
1897
  if (!empty($_SESSION['upload_files'])) {
1903
    foreach ($_SESSION['upload_files'] as $fid => $file) {
1913
      if (!isset($form_state['values']['files'][$fid]['filepath'])) {
1923
        $form_state['values']['files'][$fid] = (array)$file;
1933
      }
1943
    }
1953
  }
196
197
  // Order the form according to the set file weight values.
1987
  if (!empty($form_state['values']['files'])) {
1995
    $microweight = 0.001;
2005
    foreach ($form_state['values']['files'] as $fid => $file) {
2015
      if (is_numeric($fid)) {
2025
        $form_state['values']['files'][$fid]['#weight'] = $file['weight'] +
$microweight;
2035
        $microweight += 0.001;
2045
      }
2055
    }
2065
    uasort($form_state['values']['files'], 'element_sort');
2075
  }
2087
}
209
21062
function upload_form_alter(&$form, $form_state, $form_id) {
21137
  if ($form_id == 'node_type_form' && isset($form['identity']['type'])) {
2120
    $form['workflow']['upload'] = array(
2130
      '#type' => 'radios',
2140
      '#title' => t('Attachments'),
2150
      '#default_value' => variable_get('upload_' .
$form['#node_type']->type, 1),
2160
      '#options' => array(t('Disabled'), t('Enabled')),
217
    );
2180
  }
219
22037
  if (isset($form['type']) && isset($form['#node'])) {
2217
    $node = $form['#node'];
2227
    if ($form['type']['#value'] . '_node_form' == $form_id &&
variable_get("upload_$node->type", TRUE)) {
223
      // Attachments fieldset
2247
      $form['attachments'] = array(
2257
        '#type' => 'fieldset',
2267
        '#access' => user_access('upload files'),
2277
        '#title' => t('File attachments'),
2287
        '#collapsible' => TRUE,
2297
        '#collapsed' => empty($node->files),
2307
        '#description' => t('Changes made to the attachments are not
permanent until you save this post. The first "listed" file will be
included in RSS feeds.'),
2317
        '#prefix' => '<div class="attachments">',
2327
        '#suffix' => '</div>',
2337
        '#weight' => 30,
234
      );
235
236
      // Wrapper for fieldset contents (used by ahah.js).
2377
      $form['attachments']['wrapper'] = array(
2387
        '#prefix' => '<div id="attach-wrapper">',
2397
        '#suffix' => '</div>',
240
      );
241
242
      // Make sure necessary directories for upload.module exist and are
243
      // writable before displaying the attachment form.
2447
      $path = file_directory_path();
2457
      $temp = file_directory_temp();
246
      // Note: pass by reference
2477
      if (!file_check_directory($path, FILE_CREATE_DIRECTORY) ||
!file_check_directory($temp, FILE_CREATE_DIRECTORY)) {
2480
        $form['attachments']['#description'] =  t('File attachments are
disabled. The file directories have not been properly configured.');
2490
        if (user_access('administer site configuration')) {
2500
          $form['attachments']['#description'] .= ' ' . t('Please visit the
<a href="@admin-file-system">file system configuration page</a>.',
array('@admin-file-system' => url('admin/settings/file-system')));
2510
        }
252
        else {
2530
          $form['attachments']['#description'] .= ' ' . t('Please contact
the site administrator.');
254
        }
2550
      }
256
      else {
2577
        $form['attachments']['wrapper'] += _upload_form($node);
2587
        $form['#attributes']['enctype'] = 'multipart/form-data';
259
      }
2607
    }
2617
    $form['#submit'][] = 'upload_node_form_submit';
2627
  }
26337
}
264
265
/**
266
 * Implementation of hook_nodeapi().
267
 */
26862
function upload_nodeapi(&$node, $op, $teaser) {
269
  switch ($op) {
270
27122
    case 'load':
27221
      $output = '';
27321
      if (variable_get("upload_$node->type", 1) == 1) {
27421
        $output['files'] = upload_load($node);
27521
        return $output;
2760
      }
2770
      break;
278
27922
    case 'view':
2807
      if (isset($node->files) && user_access('view uploaded files')) {
281
        // Add the attachments list to node body with a heavy
282
        // weight to ensure they're below other elements
2837
        if (count($node->files)) {
2845
          if (!$teaser && user_access('view uploaded files')) {
2855
            $node->content['files'] = array(
2865
              '#markup' => theme('upload_attachments', $node->files),
2875
              '#weight' => 50,
288
            );
2895
          }
2905
        }
2917
      }
2927
      break;
293
29422
    case 'prepare':
295
      // Initialize $_SESSION['upload_files'] if no post occurred.
296
      // This clears the variable from old forms and makes sure it
297
      // is an array to prevent notices and errors in other parts
298
      // of upload.module.
2997
      if (!$_POST) {
3007
        $_SESSION['upload_files'] = array();
3017
      }
3027
      break;
303
30415
    case 'insert':
30515
    case 'update':
3068
      if (user_access('upload files')) {
3078
        upload_save($node);
3088
      }
3098
      break;
310
31115
    case 'delete':
3120
      upload_delete($node);
3130
      break;
314
31515
    case 'delete revision':
3160
      upload_delete_revision($node);
3170
      break;
318
31915
    case 'search result':
3200
      return isset($node->files) && is_array($node->files) ?
format_plural(count($node->files), '1 attachment', '@count attachments') :
NULL;
321
32215
    case 'rss item':
3230
      if (is_array($node->files)) {
3240
        $files = array();
3250
        foreach ($node->files as $file) {
3260
          if ($file->list) {
3270
            $files[] = $file;
3280
          }
3290
        }
3300
        if (count($files) > 0) {
331
          // RSS only allows one enclosure per item
3320
          $file = array_shift($files);
333
          return array(
334
            array(
3350
              'key' => 'enclosure',
336
              'attributes' => array(
3370
                'url' => file_create_url($file->filepath),
3380
                'length' => $file->filesize,
3390
                'type' => $file->filemime
3400
              )
3410
            )
3420
          );
3430
        }
3440
      }
3450
      return array();
3460
  }
34722
}
348
349
/**
350
 * Displays file attachments in table
351
 *
352
 * @ingroup themeable
353
 */
35462
function theme_upload_attachments($files) {
3555
  $header = array(t('Attachment'), t('Size'));
3565
  $rows = array();
3575
  foreach ($files as $file) {
3585
    $file = (object)$file;
3595
    if ($file->list && empty($file->remove)) {
3605
      $href = file_create_url($file->filepath);
3615
      $text = $file->description ? $file->description : $file->filename;
3625
      $rows[] = array(l($text, $href), format_size($file->filesize));
3635
    }
3645
  }
3655
  if (count($rows)) {
3665
    return theme('table', $header, $rows, array('id' => 'attachments'));
3670
  }
3680
}
369
370
/**
371
 * Determine how much disk space is occupied by a user's uploaded files.
372
 *
373
 * @param $uid
374
 *   The integer user id of a user.
375
 * @return
376
 *   The amount of disk space used by the user in bytes.
377
 */
37862
function upload_space_used($uid) {
3790
  return file_space_used($uid);
3800
}
381
382
/**
383
 * Determine how much disk space is occupied by uploaded files.
384
 *
385
 * @return
386
 *   The amount of disk space used by uploaded files in bytes.
387
 */
38862
function upload_total_space_used() {
3890
  return db_result(db_query('SELECT SUM(f.filesize) FROM {files} f INNER
JOIN {upload} u ON f.fid = u.fid'));
3900
}
391
39262
function upload_save(&$node) {
3938
  if (empty($node->files) || !is_array($node->files)) {
3943
    return;
3950
  }
396
3975
  foreach ($node->files as $fid => $file) {
398
    // Convert file to object for compatibility
3995
    $file = (object)$file;
400
401
    // Remove file. Process removals first since no further processing
402
    // will be required.
4035
    if (!empty($file->remove)) {
4041
      db_query('DELETE FROM {upload} WHERE fid = %d AND vid = %d', $fid,
$node->vid);
405
406
      // If the file isn't used by any other revisions delete it.
4071
      $count = db_result(db_query('SELECT COUNT(fid) FROM {upload} WHERE
fid = %d', $fid));
4081
      if ($count < 1) {
4091
        file_delete($file->filepath);
4101
        db_query('DELETE FROM {files} WHERE fid = %d', $fid);
4111
      }
412
413
      // Remove it from the session in the case of new uploads,
414
      // that you want to disassociate before node submission.
4151
      unset($_SESSION['upload_files'][$fid]);
416
      // Move on, so the removed file won't be added to new revisions.
4171
      continue;
4180
    }
419
420
    // Create a new revision, or associate a new file needed.
4215
    if (!empty($node->old_vid) || isset($_SESSION['upload_files'][$fid]))
{
4223
      db_query("INSERT INTO {upload} (fid, nid, vid, list, description,
weight) VALUES (%d, %d, %d, %d, '%s', %d)", $file->fid, $node->nid,
$node->vid, $file->list, $file->description, $file->weight);
4233
      file_set_status($file, FILE_STATUS_PERMANENT);
4243
    }
425
    // Update existing revision.
426
    else {
4273
      db_query("UPDATE {upload} SET list = %d, description = '%s', weight =
%d WHERE fid = %d AND vid = %d", $file->list, $file->description,
$file->weight, $file->fid, $node->vid);
4283
      file_set_status($file, FILE_STATUS_PERMANENT);
429
    }
4305
  }
431
  // Empty the session storage after save. We use this variable to track
files
432
  // that haven't been related to the node yet.
4335
  unset($_SESSION['upload_files']);
4345
}
435
43662
function upload_delete($node) {
4370
  $files = array();
4380
  $result = db_query('SELECT DISTINCT f.* FROM {upload} u INNER JOIN
{files} f ON u.fid = f.fid WHERE u.nid = %d', $node->nid);
4390
  while ($file = db_fetch_object($result)) {
4400
    $files[$file->fid] = $file;
4410
  }
442
4430
  foreach ($files as $fid => $file) {
444
    // Delete all files associated with the node
4450
    db_query('DELETE FROM {files} WHERE fid = %d', $fid);
4460
    file_delete($file->filepath);
4470
  }
448
449
  // Delete all file revision information associated with the node
4500
  db_query('DELETE FROM {upload} WHERE nid = %d', $node->nid);
4510
}
452
45362
function upload_delete_revision($node) {
4540
  if (is_array($node->files)) {
4550
    foreach ($node->files as $file) {
456
      // Check if the file will be used after this revision is deleted
4570
      $count = db_result(db_query('SELECT COUNT(fid) FROM {upload} WHERE
fid = %d', $file->fid));
458
459
      // if the file won't be used, delete it
4600
      if ($count < 2) {
4610
        db_query('DELETE FROM {files} WHERE fid = %d', $file->fid);
4620
        file_delete($file->filepath);
4630
      }
4640
    }
4650
  }
466
467
  // delete the revision
4680
  db_query('DELETE FROM {upload} WHERE vid = %d', $node->vid);
4690
}
470
47162
function _upload_form($node) {
4727
  global $user;
473
474
  $form = array(
4757
    '#theme' => 'upload_form_new',
4767
    '#cache' => TRUE,
4777
  );
478
4797
  if (!empty($node->files) && is_array($node->files)) {
4803
    $form['files']['#theme'] = 'upload_form_current';
4813
    $form['files']['#tree'] = TRUE;
4823
    foreach ($node->files as $key => $file) {
4833
      $file = (object)$file;
4843
      $description = file_create_url($file->filepath);
4853
      $description = "<small>" . check_plain($description) . "</small>";
4863
      $form['files'][$key]['description'] = array('#type' => 'textfield',
'#default_value' => !empty($file->description) ? $file->description :
$file->filename, '#maxlength' => 256, '#description' => $description );
4873
      $form['files'][$key]['size'] = array('#markup' =>
format_size($file->filesize));
4883
      $form['files'][$key]['remove'] = array('#type' => 'checkbox',
'#default_value' => !empty($file->remove));
4893
      $form['files'][$key]['list'] = array('#type' => 'checkbox', 
'#default_value' => $file->list);
4903
      $form['files'][$key]['weight'] = array('#type' => 'weight', '#delta'
=> count($node->files), '#default_value' => $file->weight);
4913
      $form['files'][$key]['filename'] = array('#type' => 'value', 
'#value' => $file->filename);
4923
      $form['files'][$key]['filepath'] = array('#type' => 'value', 
'#value' => $file->filepath);
4933
      $form['files'][$key]['filemime'] = array('#type' => 'value', 
'#value' => $file->filemime);
4943
      $form['files'][$key]['filesize'] = array('#type' => 'value', 
'#value' => $file->filesize);
4953
      $form['files'][$key]['fid'] = array('#type' => 'value',  '#value' =>
$file->fid);
4963
    }
4973
  }
498
4997
  if (user_access('upload files')) {
5007
    $limits = _upload_file_limits($user);
5017
    $form['new']['#weight'] = 10;
5027
    $form['new']['upload'] = array(
5037
      '#type' => 'file',
5047
      '#title' => t('Attach new file'),
5057
      '#size' => 40,
5067
      '#description' => ($limits['resolution'] ? t('Images are larger than
%resolution will be resized. ', array('%resolution' =>
$limits['resolution'])) : '') . t('The maximum upload size is %filesize.
Only files with the following extensions may be uploaded: %extensions. ',
array('%extensions' => $limits['extensions'], '%filesize' =>
format_size($limits['file_size']))),
507
    );
5087
    $form['new']['attach'] = array(
5097
      '#type' => 'submit',
5107
      '#value' => t('Attach'),
5117
      '#name' => 'attach',
512
      '#ahah' => array(
5137
        'path' => 'upload/js',
5147
        'wrapper' => 'attach-wrapper',
5157
        'progress' => array('type' => 'bar', 'message' => t('Please
wait...')),
5167
      ),
5177
      '#submit' => array('node_form_submit_build_node'),
518
    );
5197
  }
520
521
  // This value is used in upload_js().
5227
  $form['current']['vid'] = array('#type' => 'hidden', '#value' =>
isset($node->vid) ? $node->vid : 0);
5237
  return $form;
5240
}
525
526
/**
527
 * Theme the attachments list.
528
 *
529
 * @ingroup themeable
530
 */
53162
function theme_upload_form_current(&$form) {
5323
  $header = array('', t('Delete'), t('List'), t('Description'),
t('Weight'), t('Size'));
5333
  drupal_add_tabledrag('upload-attachments', 'order', 'sibling',
'upload-weight');
534
5353
  foreach (element_children($form) as $key) {
536
    // Add class to group weight fields for drag and drop.
5373
    $form[$key]['weight']['#attributes']['class'] = 'upload-weight';
538
5393
    $row = array('');
5403
    $row[] = drupal_render($form[$key]['remove']);
5413
    $row[] = drupal_render($form[$key]['list']);
5423
    $row[] = drupal_render($form[$key]['description']);
5433
    $row[] = drupal_render($form[$key]['weight']);
5443
    $row[] = drupal_render($form[$key]['size']);
5453
    $rows[] = array('data' => $row, 'class' => 'draggable');
5463
  }
5473
  $output = theme('table', $header, $rows, array('id' =>
'upload-attachments'));
5483
  $output .= drupal_render($form);
5493
  return $output;
5500
}
551
552
/**
553
 * Theme the attachment form.
554
 * Note: required to output prefix/suffix.
555
 *
556
 * @ingroup themeable
557
 */
55862
function theme_upload_form_new($form) {
5597
  drupal_add_tabledrag('upload-attachments', 'order', 'sibling',
'upload-weight');
5607
  $output = drupal_render($form);
5617
  return $output;
5620
}
563
56462
function upload_load($node) {
56521
  $files = array();
566
56721
  if ($node->vid) {
56821
    $result = db_query('SELECT * FROM {files} f INNER JOIN {upload} r ON
f.fid = r.fid WHERE r.vid = %d ORDER BY r.weight, f.fid', $node->vid);
56921
    while ($file = db_fetch_object($result)) {
57011
      $files[$file->fid] = $file;
57111
    }
57221
  }
573
57421
  return $files;
5750
}
576
577
/**
578
 * Menu-callback for JavaScript-based uploads.
579
 */
58062
function upload_js() {
581
  // Load the form from the Form API cache.
5820
  $cache = cache_get('form_' . $_POST['form_build_id'], 'cache_form');
583
584
  // We only do the upload.module part of the node validation process.
5850
  $node = (object)$_POST;
5860
  unset($node->files['upload']);
5870
  $form = $cache->data;
5880
  $form_state = array('values' => $_POST);
589
590
  // Handle new uploads, and merge tmp files into node-files.
5910
  upload_node_form_submit($form, $form_state);
5920
  $node_files = upload_load($node);
5930
  if (!empty($form_state['values']['files'])) {
5940
    foreach ($form_state['values']['files'] as $fid => $file) {
5950
      if (is_numeric($fid)) {
5960
        $node->files[$fid] = $file;
5970
        if (!isset($file['filepath'])) {
5980
          $node->files[$fid] = $node_files[$fid];
5990
        }
6000
      }
6010
    }
6020
  }
6030
  $form = _upload_form($node);
604
605
  // Update the default values changed in the $_POST array.
6060
  $files = isset($_POST['files']) ? $_POST['files'] : array();
6070
  foreach ($files as $fid => $file) {
6080
    if (is_numeric($fid)) {
6090
      $form['files'][$fid]['description']['#default_value'] =
$file['description'];
6100
      $form['files'][$fid]['list']['#default_value'] = isset($file['list'])
? 1 : 0;
6110
      $form['files'][$fid]['remove']['#default_value'] =
isset($file['remove']) ? 1 : 0;
6120
      $form['files'][$fid]['weight']['#default_value'] = $file['weight'];
6130
    }
6140
  }
615
616
  // Add the new element to the stored form state and resave.
6170
  $cache->data['attachments']['wrapper'] =
array_merge($cache->data['attachments']['wrapper'], $form);
6180
  cache_set('form_' . $_POST['form_build_id'], $cache->data, 'cache_form',
$cache->expire);
619
620
  // Render the form for output.
621
  $form += array(
6220
    '#post' => $_POST,
6230
    '#programmed' => FALSE,
6240
    '#tree' => FALSE,
6250
    '#parents' => array(),
6260
  );
6270
  drupal_alter('form', $form, array(), 'upload_js');
6280
  $form_state = array('submitted' => FALSE);
6290
  $form = form_builder('upload_js', $form, $form_state);
6300
  $output = theme('status_messages') . drupal_render($form);
631
632
  // We send the updated file attachments form.
633
  // Don't call drupal_json(). ahah.js uses an iframe and
634
  // the header output by drupal_json() causes problems in some browsers.
6350
  print drupal_to_js(array('status' => TRUE, 'data' => $output));
6360
  exit;
6370
}
63862