upload.module 24.8 KB
Newer Older
Dries's avatar
   
Dries committed
1
<?php
Dries's avatar
Dries committed
2
// $Id$
Dries's avatar
   
Dries committed
3

Dries's avatar
   
Dries committed
4
5
6
/**
 * @file
 * File-handling and attaching files to nodes.
7
 *
Dries's avatar
   
Dries committed
8
9
 */

10
11
12
/**
 * Implementation of hook_help().
 */
Dries's avatar
   
Dries committed
13
14
function upload_help($section) {
  switch ($section) {
15
16
17
18
19
20
21
22
23
24
    case 'admin/help#upload':
      $output = '<p>'. t('The upload module allows users to upload files to the site.  The ability to upload files to a site 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 a node or page.') .'</p>';
      $output .= '<p>'. t('Users with the upload files permission can upload attachments. You can choose which post types can take attachments on the content types settings page.  Each user role can be customized for the file size of uploads, and the dimension of image files.') .'</p>';
      $output .= t('<p>You can</p>
<ul>
<li>administer user permissions at <a href="%admin-user-configure">administer &gt;&gt; user &gt;&gt; configure &gt;&gt; permissions</a>.</li>
<li>administer content at <a href="%admin-node-configure">administer &gt;&gt; content types</a>.</li>
<li>administer upload at <a href="%admin-settings">administer &gt;&gt; settings</a>.</li>
</ul>
', array('%admin-user-configure' => url('admin/user/configure'), '%admin-node-configure' => url('admin/node/configure'), '%admin-settings' => url('admin/settings')));
25
      $output .= '<p>'. t('For more information please read the configuration and customization handbook <a href="%upload">Upload page</a>.', array('%upload' => 'http://drupal.org/handbook/modules/upload/')) .'</p>';
26
      return $output;
Dries's avatar
   
Dries committed
27
    case 'admin/modules#description':
28
      return t('Allows users to upload and attach files to content.');
29
    case 'admin/settings/upload':
30
      return t('<p>Users with the <a href="%permissions">upload files permission</a> can upload attachments. You can choose which post types can take attachments on the <a href="%types">content types settings</a> page.</p>', array('%permissions' => url('admin/access'), '%types' => url('admin/settings/content-types')));
Dries's avatar
   
Dries committed
31
32
33
  }
}

34
35
36
/**
 * Implementation of hook_perm().
 */
Dries's avatar
   
Dries committed
37
function upload_perm() {
38
  return array('upload files', 'view uploaded files');
Dries's avatar
   
Dries committed
39
40
}

41
42
43
44
45
46
47
/**
 * Implementation of hook_link().
 */
function upload_link($type, $node = 0, $main = 0) {
  $links = array();

  // Display a link with the number of attachments
48
  if ($main && $type == 'node' && is_array($node->files) && user_access('view uploaded files')) {
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
    $num_files = 0;
    foreach ($node->files as $file) {
      if ($file->list) {
        $num_files++;
      }
    }
    if ($num_files) {
      $links[] = l(format_plural($num_files, '1 attachment', '%count attachments'), "node/$node->nid", array('title' => t('Read full article to view attachments.')), NULL, 'attachments');
    }
  }

  return $links;
}

/**
 * Implementation of hook_menu().
 */
Dries's avatar
   
Dries committed
66
67
68
69
function upload_menu($may_cache) {
  $items = array();

  if ($may_cache) {
70
71
72
73
74
75
    $items[] = array(
      'path' => 'upload/js',
      'callback' => 'upload_js',
      'access' => user_access('upload files'),
      'type' => MENU_CALLBACK
    );
76
77
  }
  else {
Dries's avatar
   
Dries committed
78
    // Add handlers for previewing new uploads.
79
80
    if ($_SESSION['file_previews']) {
      foreach ($_SESSION['file_previews'] as $fid => $file) {
Dries's avatar
   
Dries committed
81
        $filename = file_create_filename($file->filename, file_create_path());
82
        if (variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC) ==  FILE_DOWNLOADS_PRIVATE) {
83
            // strip file_directory_path() from filename. @see file_create_url 
84
85
86
87
88
89
            if (strpos($filename, file_directory_path()) !== false) {
              $filename = trim(substr($filename, strlen(file_directory_path())), '\\/');
            }
            $filename = 'system/files/' . $filename;
        }

Dries's avatar
   
Dries committed
90
91
92
        $items[] = array(
          'path' => $filename, 'title' => t('file download'),
          'callback' => 'upload_download',
93
          'access' => user_access('view uploaded files'),
94
          'type' => MENU_CALLBACK
Dries's avatar
   
Dries committed
95
        );
96
        $_SESSION['file_previews'][$fid]->_filename = $filename;
Dries's avatar
   
Dries committed
97
      }
Dries's avatar
   
Dries committed
98
99
    }
  }
Dries's avatar
   
Dries committed
100

Dries's avatar
   
Dries committed
101
102
103
  return $items;
}

104
function upload_settings() {
105
  $form['settings_general'] = array('#type' => 'fieldset', '#title' => t('General settings'));
106
  $form['settings_general']['upload_max_resolution'] = array(
107
108
    '#type' => 'textfield', '#title' => t('Maximum resolution for uploaded images'), '#default_value' => variable_get('upload_max_resolution', 0),
    '#size' => 15, '#maxlength' => 10, '#description' => t('The maximum allowed image size expressed as WIDTHxHEIGHT (e.g. 640x480). Set to 0 for no restriction.')
109
  );
Dries's avatar
   
Dries committed
110

111
  $form['settings_general']['upload_list_default'] = array('#type' => 'select', '#title' => t('List files by default'),
112
113
114
115
    '#default_value' => variable_get('upload_list_default',1),
    '#options' => array( 0 => t('No'), 1 => t('Yes') ),
    '#description' => t('Set whether files attached to nodes are listed or not in the node view by default.'),
  );
116

Dries's avatar
   
Dries committed
117
118
119
  $roles = user_roles(0, 'upload files');

  foreach ($roles as $rid => $role) {
120
    $form["settings_role_$rid"] = array('#type' => 'fieldset', '#title' => t('Settings for %role', array('%role' => theme('placeholder', $role))), '#collapsible' => TRUE, '#collapsed' => TRUE);
121
    $form["settings_role_$rid"]["upload_extensions_$rid"] = array(
122
      '#type' => 'textfield', '#title' => t('Permitted file extensions'), '#default_value' => variable_get("upload_extensions_$rid", "jpg jpeg gif png txt html doc xls pdf ppt pps"),
123
      '#maxlength' => 255, '#description' => t('Extensions that users in this role can upload. Separate extensions with a space and do not include the leading dot.')
124
125
    );
    $form["settings_role_$rid"]["upload_uploadsize_$rid"] = array(
126
127
      '#type' => 'textfield', '#title' => t('Maximum file size per upload'), '#default_value' => variable_get("upload_uploadsize_$rid", 1),
      '#size' => 5, '#maxlength' => 5, '#description' => t('The maximum size of a file a user can upload (in megabytes).')
128
129
    );
    $form["settings_role_$rid"]["upload_usersize_$rid"] = array(
130
131
      '#type' => 'textfield', '#title' => t('Total file size per user'), '#default_value' => variable_get("upload_usersize_$rid", 10),
      '#size' => 5, '#maxlength' => 5, '#description' => t('The maximum size of all files a user can have on the site (in megabytes).')
132
    );
Dries's avatar
   
Dries committed
133
134
  }

135
  return $form;
Dries's avatar
   
Dries committed
136
137
138
}

function upload_download() {
139
  foreach ($_SESSION['file_previews'] as $file) {
Dries's avatar
   
Dries committed
140
    if ($file->_filename == $_GET['q']) {
Dries's avatar
Dries committed
141
      file_transfer($file->filepath, array('Content-Type: '. mime_header_encode($file->filemime), 'Content-Length: '. $file->filesize));
Dries's avatar
   
Dries committed
142
143
144
145
146
    }
  }
}

function upload_file_download($file) {
147
148
  if (user_access('view uploaded files')) {
    $file = file_create_path($file);
149
    $result = db_query("SELECT f.* FROM {files} f WHERE filepath = '%s'", $file);
150
    if ($file = db_fetch_object($result)) {
151
152
153
154
155
156
157
158
159
160
      $node = node_load($file->nid);
      if (node_access('view', $node)) {
        $name = mime_header_encode($file->filename);
        $type = mime_header_encode($file->filemime);
        // Serve images and text inline for the browser to display rather than download.
        $disposition = ereg('^(text/|image/)', $file->filemime) ? 'inline' : 'attachment';
        return array('Content-Type: '. $type .'; name='. $name,
                     'Content-Length: '. $file->filesize,
                     'Content-Disposition: '. $disposition .'; filename='. $name);
      }
161
162
163
      else {
        return -1;
      }
164
    }
Dries's avatar
   
Dries committed
165
  }
166
167
168
  else {
    return -1;
  }
Dries's avatar
   
Dries committed
169
170
}

171
/**
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
 * Save new uploads and attach them to the node object.
 * append file_previews to the node object as well.
 */
function _upload_prepare(&$node) {

  // Clean up old file previews if a post didn't get the user to this page.
  // i.e. the user left the edit page, because they didn't want to upload anything.
  if(count($_POST) == 0) {
    if (is_array($_SESSION['file_previews']) && count($_SESSION['file_previews'])) {
      foreach($_SESSION['file_previews'] as $fid => $file) {
         file_delete($file->filepath);
      }
      unset($_SESSION['file_previews']);
    }
  }

  // $_SESSION['file_submitted'] tracks the fid of the file submitted this page request.
  // form_builder sets the value of file->list to 0 for checkboxes added to a form after
  // it has been submitted. Since unchecked checkboxes have no return value and do not
  // get a key in _POST form_builder has no way of knowing the difference between a check
  // box that wasn't present on the last form build, and a checkbox that is unchecked.

  unset($_SESSION['file_submitted']);

  // Save new file uploads to tmp dir.
  if (($file = file_check_upload()) && user_access('upload files')) {
    global $user;

    // Scale image uploads.
    $file = _upload_image($file);

    $key = 'upload_'. count($_SESSION['file_previews']);
    $file->fid = $key;
    $file->source = $key;
    $file->list = variable_get('upload_list_default',1);
    $_SESSION['file_previews'][$key] = $file;

    // Store the uploaded fid for this page request in case of submit without
    // preview or attach. See earlier notes.
    $_SESSION['file_submitted'] = $key;
  }

  // Attach file previews to node object.
  if (is_array($_SESSION['file_previews']) && count($_SESSION['file_previews'])) {
    foreach($_SESSION['file_previews'] as $fid => $file) {
      $node->files[$fid] = $file;
    }
  }
}

222
function upload_form_alter($form_id, &$form) {
223
224
225
226
227
228
229
  if (isset($form['type'])) {
    if ($form['type']['#value'] .'_node_settings' == $form_id) {
      $form['workflow']['upload_'. $form['type']['#value']] = array(
        '#type' => 'radios', '#title' => t('Attachments'), '#default_value' => variable_get('upload_'. $form['type']['#value'], 1),
        '#options' => array(t('Disabled'), t('Enabled')),
      );
    }
230

231
232
    $node = $form['#node'];
    if ($form['type']['#value'] .'_node_form' == $form_id && variable_get("upload_$node->type", TRUE) && user_access('upload files')) {
233
234
      drupal_add_js('misc/progress.js');
      drupal_add_js('misc/upload.js');
235

236
237
238
239
240
241
242
243
      $form['attachments'] = array(
        '#type' => 'fieldset',
        '#title' => t('File attachments'),
        '#collapsible' => TRUE,
        '#collapsed' => empty($node->files),
        '#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.'),
        '#prefix' => '<div class="attachments">',
        '#suffix' => '</div>',
244
        '#weight' => 30,
245
      );
246
      $form['attachments'] += _upload_form($node);
247
      $form['#attributes']['enctype'] = 'multipart/form-data';
248
    }
249
250
251
  }
}

252
253
254
255
256
function _upload_validate(&$node) {
  // Accumulator for disk space quotas.
  $filesize = 0;

  // Check if node->files exists, and if it contains something.
257
  if (is_array($node->files)) {
258
259
    // Update existing files with form data.
    foreach($node->files as $fid => $file) {
260
261
      // Convert file to object for compatability
      $file = (object)$file;
262

263
264
      // Validate new uploads.
      if (strpos($fid, 'upload') !== false && !$file->remove) {
Dries's avatar
   
Dries committed
265
266
        global $user;

267
        // Bypass validation for uid  = 1.
Steven Wittens's avatar
Steven Wittens committed
268
        if ($user->uid != 1) {
269
270
271
272
273
274
          //Update filesize accumulator.
          $filesize += $file->filesize;

          // Validate file against all users roles.
          // Only denies an upload when all roles prevent it.

275
          $total_usersize = upload_space_used($user->uid) + $filesize;
276
          $error = array();
Steven Wittens's avatar
Steven Wittens committed
277
278
          foreach ($user->roles as $rid => $name) {
            $extensions = variable_get("upload_extensions_$rid", 'jpg jpeg gif png txt html doc xls pdf ppt pps');
279
280
            $uploadsize = variable_get("upload_uploadsize_$rid", 1) * 1024 * 1024;
            $usersize = variable_get("upload_usersize_$rid", 1) * 1024 * 1024;
Steven Wittens's avatar
Steven Wittens committed
281
282
283
284
285
286

            $regex = '/\.('. ereg_replace(' +', '|', preg_quote($extensions)) .')$/i';

            if (!preg_match($regex, $file->filename)) {
              $error['extension']++;
            }
Dries's avatar
   
Dries committed
287

288
            if ($uploadsize && $file->filesize > $uploadsize) {
Steven Wittens's avatar
Steven Wittens committed
289
290
              $error['uploadsize']++;
            }
Dries's avatar
   
Dries committed
291

292
            if ($usersize && $total_usersize + $file->filesize > $usersize) {
Steven Wittens's avatar
Steven Wittens committed
293
294
              $error['usersize']++;
            }
Dries's avatar
   
Dries committed
295
          }
296
297
298
299

          $user_roles = count($user->roles);
          $valid = TRUE;
          if ($error['extension'] == $user_roles) {
300
            form_set_error('upload', t('The selected file %name can not be attached to this post, because it is only possible to attach files with the following extensions: %files-allowed.', array('%name' => theme('placeholder', $file->filename), '%files-allowed' => theme('placeholder', $extensions))));
301
            $valid = FALSE;
302
          }
303
          elseif ($error['uploadsize'] == $user_roles) {
304
            form_set_error('upload', t('The selected file %name can not be attached to this post, because it exceeded the maximum filesize of %maxsize.', array('%name' => theme('placeholder', $file->filename), '%maxsize' => theme('placeholder', format_size($uploadsize)))));
305
            $valid = FALSE;
306
          }
307
          elseif ($error['usersize'] == $user_roles) {
308
            form_set_error('upload', t('The selected file %name can not be attached to this post, because the disk quota of %quota has been reached.', array('%name' => theme('placeholder', $file->filename), '%quota' => theme('placeholder', format_size($usersize)))));
309
            $valid = FALSE;
310
          }
311
          if (!$valid) {
312
313
314
            unset($node->files[$fid], $_SESSION['file_previews'][$fid]);
            file_delete($file->filepath);
          }
315
        }
316
      }
317
318
319
320
321
322
323
324
325
    }
  }
}

/**
 * Implementation of hook_nodeapi().
 */
function upload_nodeapi(&$node, $op, $arg) {
  switch ($op) {
326

Dries's avatar
   
Dries committed
327
    case 'load':
328
      if (variable_get("upload_$node->type", 1) == 1) {
Steven Wittens's avatar
Steven Wittens committed
329
        $output['files'] = upload_load($node);
Dries's avatar
   
Dries committed
330
      }
331
332
333
334
335
336
337
338
339
      return $output;
      break;

    case 'prepare':
      _upload_prepare($node);
      break;

    case 'validate':
      _upload_validate($node);
Dries's avatar
   
Dries committed
340
      break;
341

Dries's avatar
   
Dries committed
342
    case 'view':
343
      if (is_array($node->files) && user_access('view uploaded files')) {
Dries's avatar
   
Dries committed
344
        $header = array(t('Attachment'), t('Size'));
Dries's avatar
   
Dries committed
345
346
347
348
        $rows = array();
        $previews = array();

        // Build list of attached files
349
        foreach ($node->files as $key => $file) {
350
          if ($file->list) {
Dries's avatar
   
Dries committed
351
            $rows[] = array(
352
              '<a href="'. check_url(($file->fid ? file_create_url($file->filepath) : url(file_create_filename($file->filename, file_create_path())))) .'">'. check_plain($file->description ? $file->description : $file->filename) .'</a>',
Dries's avatar
   
Dries committed
353
354
355
              format_size($file->filesize)
            );
            // We save the list of files still in preview for later
356
            if (strpos($file->fid, 'upload') !== false) {
Dries's avatar
   
Dries committed
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
              $previews[] = $file;
            }
          }
        }

        // URLs to files being previewed are actually Drupal paths. When Clean
        // URLs are disabled, the two do not match. We perform an automatic
        // replacement from temporary to permanent URLs. That way, the author
        // can use the final URL in the body before having actually saved (to
        // place inline images for example).
        if (!variable_get('clean_url', 0)) {
          foreach ($previews as $file) {
            $old = file_create_filename($file->filename, file_create_path());
            $new = url($old);
            $node->body = str_replace($old, $new, $node->body);
            $node->teaser = str_replace($old, $new, $node->teaser);
          }
        }

        $teaser = $arg;
        // Add the attachments list
        if (count($rows) && !$teaser) {
379
          $node->body .= theme('table', $header, $rows, array('id' => 'attachments'));
Dries's avatar
   
Dries committed
380
381
382
        }
      }
      break;
383

Dries's avatar
   
Dries committed
384
385
    case 'insert':
    case 'update':
386
387
388
      if (user_access('upload files')) {
        upload_save($node);
      }
Dries's avatar
   
Dries committed
389
      break;
390

Dries's avatar
   
Dries committed
391
392
393
    case 'delete':
      upload_delete($node);
      break;
394

395
396
397
    case 'delete revision':
      upload_delete_revision($node);
      break;
398

Dries's avatar
Dries committed
399
    case 'search result':
400
      return is_array($node->files) ? format_plural(count($node->files), '1 attachment', '%count attachments') : null;
401

Dries's avatar
   
Dries committed
402
    case 'rss item':
403
      if (is_array($node->files)) {
Dries's avatar
   
Dries committed
404
405
406
407
408
409
410
411
412
413
414
415
416
        $files = array();
        foreach ($node->files as $file) {
          if ($file->list) {
            $files[] = $file;
          }
        }
        if (count($files) > 0) {
          // RSS only allows one enclosure per item
          $file = array_shift($files);
          return array(array('key' => 'enclosure',
                'attributes' => array('url' => file_create_url($file->filepath),
                  'length' => $file->filesize,
                  'type' => $file->filemime)));
Dries's avatar
   
Dries committed
417
418
        }
      }
419
      return array();
Dries's avatar
   
Dries committed
420

421
  }
Dries's avatar
   
Dries committed
422
423
}

424
425
426
427
428
429
/**
 * Determine how much disk space is occupied by a user's uploaded files.
 *
 * @param $uid
 *   The integer user id of a user.
 * @return
430
 *   The amount of disk space used by the user in bytes.
431
432
 */
function upload_space_used($uid) {
433
  return db_result(db_query('SELECT SUM(filesize) FROM {files} f INNER JOIN {node} n ON f.nid = n.nid WHERE n.uid = %d', $uid));
434
}
Dries's avatar
   
Dries committed
435

436
437
438
439
/**
 * Determine how much disk space is occupied by uploaded files.
 *
 * @return
440
 *   The amount of disk space used by uploaded files in bytes.
441
442
 */
function upload_total_space_used() {
443
  return db_result(db_query('SELECT SUM(filesize) FROM {files}'));
Dries's avatar
   
Dries committed
444
445
446
}

function upload_save($node) {
447
448
449
450
  if (!is_array($node->files)) {
    return;
  }

451
452
453
454
455
456
457
458
459
460
  foreach ($node->files as $fid => $file) {
    // Convert file to object for compatability
    $file = (object)$file;

    // Remove file. Process removals first since no further processing
    // will be required.
    if ($file->remove) {
      // Remove file previews...
      if (strpos($file->fid, 'upload') !== false) {
          file_delete($file->filepath);
Dries's avatar
   
Dries committed
461
      }
462

463
464
465
      // Remove managed files.
      else {
        db_query('DELETE FROM {file_revisions} WHERE fid = %d AND vid = %d', $fid, $node->vid);
466
        // Only delete a file if it isn't used by any revision
467
        $count = db_result(db_query('SELECT COUNT(fid) FROM {file_revisions} WHERE fid = %d', $fid));
468
        if ($count < 1) {
469
          db_query('DELETE FROM {files} WHERE fid = %d', $fid);
470
471
          file_delete($file->filepath);
        }
Dries's avatar
   
Dries committed
472
      }
473
    }
474

475
476
477
478
479
480
481
    // New file upload
    elseif (strpos($file->fid, 'upload') !== false) {
      if ($file = file_save_upload($file, $file->filename)) {
        // Track the file which was submitted last, in case of a direct submission
        // without preview or attach. See notes in upload_prepare.
        if ($_SESSION['file_submitted'] == $file->fid) {
          $file->list = variable_get('upload_list_default',1);
482
483
        }

484
485
486
        $file->fid = db_next_id('{files}_fid');
        db_query("INSERT INTO {files} (fid, nid, filename, filepath, filemime, filesize) VALUES (%d, %d, '%s', '%s', '%s', %d)", $file->fid, $node->nid, $file->filename, $file->filepath, $file->filemime, $file->filesize);
        db_query("INSERT INTO {file_revisions} (fid, vid, list, description) VALUES (%d, %d, %d, '%s')", $file->fid, $node->vid, $file->list, $file->description);
487
      }
488
489
490
491
492
493
494
495
496
497
498
      unset($_SESSION['file_previews'][$fid]);
    }

    // Create a new revision, as needed
    elseif ($node->old_vid && is_numeric($fid)) {
      db_query("INSERT INTO {file_revisions} (fid, vid, list, description) VALUES (%d, %d, %d, '%s')", $file->fid, $node->vid, $file->list, $file->description);
    }

    // Update existing revision
    else {
      db_query("UPDATE {file_revisions} SET list = %d, description = '%s' WHERE fid = %d AND vid = %d", $file->list, $file->description, $file->fid, $node->vid);
499
500
    }
  }
Dries's avatar
   
Dries committed
501
502
503
}

function upload_delete($node) {
504
505
506
507
508
509
510
  $files = array();
  $result = db_query('SELECT * FROM {files} WHERE nid = %d', $node->nid);
  while ($file = db_fetch_object($result)) {
    $files[$file->fid] = $file;
  }

  foreach ($files as $fid => $file) {
511
    // Delete all file revision information associated with the node
512
    db_query('DELETE FROM {file_revisions} WHERE fid = %d', $fid);
Dries's avatar
   
Dries committed
513
514
    file_delete($file->filepath);
  }
515

516
  // Delete all files associated with the node
517
518
519
520
  db_query('DELETE FROM {files} WHERE nid = %d', $node->nid);
}

function upload_delete_revision($node) {
521
522
523
524
525
526
527
528
529
530
  if (is_array($node->files)) {
    foreach ($node->files as $file) {
      // Check if the file will be used after this revision is deleted
      $count = db_result(db_query('SELECT COUNT(fid) FROM {file_revisions} WHERE fid = %d', $file->fid));

      // if the file won't be used, delete it
      if ($count < 2) {
        db_query('DELETE FROM {files} WHERE fid = %d', $file->fid);
        file_delete($file->filepath);
      }
531
532
533
534
535
    }
  }

  // delete the revision
  db_query('DELETE FROM {file_revisions} WHERE vid = %d', $node->vid);
Dries's avatar
   
Dries committed
536
537
}

538
function _upload_form($node) {
539
  $form['#theme'] = 'upload_form_new';
540
  if (is_array($node->files) && count($node->files)) {
541
542
    $form['files']['#theme'] = 'upload_form_current';
    $form['files']['#tree'] = TRUE;
Dries's avatar
   
Dries committed
543
    foreach ($node->files as $key => $file) {
544
545
546
547
548
549
550
551
552
553
      $description = "<small>". file_create_url((strpos($file->fid,'upload') === false ? $file->filepath : file_create_filename($file->filename, file_create_path()))) ."</small>";
      $form['files'][$key]['description'] = array('#type' => 'textfield', '#default_value' => (strlen($file->description)) ? $file->description : $file->filename, '#maxlength' => 256, '#description' => $description );
      $form['files'][$key]['size'] = array('#type' => 'markup', '#value' => format_size($file->filesize));
      $form['files'][$key]['remove'] = array('#type' => 'checkbox', '#default_value' => $file->remove);
      $form['files'][$key]['list'] = array('#type' => 'checkbox',  '#default_value' => $file->list);
      $form['files'][$key]['filename'] = array('#type' => 'value',  '#value' => $file->filename);
      $form['files'][$key]['filepath'] = array('#type' => 'value',  '#value' => $file->filepath);
      $form['files'][$key]['filemime'] = array('#type' => 'value',  '#value' => $file->filemime);
      $form['files'][$key]['filesize'] = array('#type' => 'value',  '#value' => $file->filesize);
      $form['files'][$key]['fid'] = array('#type' => 'value',  '#value' => $file->fid);
Dries's avatar
   
Dries committed
554
555
    }
  }
556

557
  if (user_access('upload files')) {
558
559
    $form['new']['upload'] = array('#type' => 'file', '#title' => t('Attach new file'), '#size' => 40);
    $form['new']['fileop'] = array('#type' => 'button', '#value' => t('Attach'), '#name'=> 'fileop', '#attributes' => array('id' => 'fileop'));
560
    // The class triggers the js upload behaviour.
561
    $form['fileop'] = array('#type' => 'hidden', '#value' => url('upload/js', NULL, NULL, TRUE), '#attributes' => array('class' => 'upload'));
562
  }
Dries's avatar
   
Dries committed
563

564
565
  // Needed for JS
  $form['current']['vid'] = array('#type' => 'hidden', '#value' => $node->vid);
566
567
568
569
570
571
572
573
574
575
576
577
578
579
  return $form;
}

function theme_upload_form_new($form) {
  $output .= '<div id="fileop-wrapper">' . "\n";
  $output .= '<div id="fileop-hide">' . "\n";
  $output .= form_render($form) . "\n";
  $output .= "</div>\n";
  $output .= "</div>\n";
  return $output;
}

function theme_upload_form_current(&$form) {
  $header = array(t('Delete'), t('List'), t('Description'), t('Size'));
580
581

  foreach (element_children($form) as $key) {
582
    $row = array();
583
584
585
586
    $row[] = form_render($form[$key]['remove']);
    $row[] = form_render($form[$key]['list']);
    $row[] = form_render($form[$key]['description']);
    $row[] = form_render($form[$key]['size']);
587
588
589
590
    $rows[] = $row;
  }
  $output = theme('table', $header, $rows);
  $output .= form_render($form);
591
  return $output;
Dries's avatar
   
Dries committed
592
593
594
595
596
}

function upload_load($node) {
  $files = array();

597
  if ($node->vid) {
598
    $result = db_query('SELECT * FROM {files} f INNER JOIN {file_revisions} r ON f.fid = r.fid WHERE r.vid = %d', $node->vid);
Dries's avatar
   
Dries committed
599
600
601
602
603
604
605
606
    while ($file = db_fetch_object($result)) {
      $files[$file->fid] = $file;
    }
  }

  return $files;
}

607
608
609
610
611
612
613
614
615
616
617
618
619
/**
 * Check an upload, if it is an image, make sure it fits within the
 * maximum dimensions allowed.
 */
function _upload_image($file) {
  $info = image_get_info($file->filepath);

  if ($info) {
    list($width, $height) = explode('x', variable_get('upload_max_resolution', 0));
    if ($width && $height) {
      $result = image_scale($file->filepath, $file->filepath, $width, $height);
      if ($result) {
        $file->filesize = filesize($file->filepath);
620
        drupal_set_message(t('The image was resized to fit within the maximum allowed resolution of %resolution pixels.', array('%resolution' => theme('placeholder', variable_get('upload_max_resolution', 0)))));
621
622
623
624
625
626
627
      }
    }
  }

  return $file;
}

628
629
630
631
632
/**
 * Menu-callback for JavaScript-based uploads.
 */
function upload_js() {
  // We only do the upload.module part of the node validation process.
633
  $node = (object)$_POST['edit'];
634
635
636
637
638
639
640
641

  // Load existing node files.
  $node->files = upload_load($node);

  // Handle new uploads, and merge tmp files into node-files.
  _upload_prepare($node);
  _upload_validate($node);

642
  $form = _upload_form($node);
643
  $form = form_builder('upload_js', $form);
644
  $output = theme('status_messages') . form_render($form);
645
646

  // We send the updated file attachments form.
647
  print drupal_to_js(array('status' => TRUE, 'data' => $output));
648
649
  exit;
}