Project

General

Profile

Paste
Download (40.9 KB) Statistics
| Branch: | Revision:

root / drupal7 / sites / all / modules / file_entity / file_entity.pages.inc @ 082b75eb

1
<?php
2

    
3
/**
4
 * @file
5
 * Supports file operations including View, Edit, and Delete.
6
 */
7

    
8
/**
9
 * Menu callback; view a single file entity.
10
 */
11
function file_entity_view_page($file) {
12
  drupal_set_title($file->filename);
13

    
14
  $uri = entity_uri('file', $file);
15
  // Set the file path as the canonical URL to prevent duplicate content.
16
  drupal_add_html_head_link(array('rel' => 'canonical', 'href' => url($uri['path'], $uri['options'])), TRUE);
17
  // Set the non-aliased path as a default shortlink.
18
  drupal_add_html_head_link(array('rel' => 'shortlink', 'href' => url($uri['path'], array_merge($uri['options'], array('alias' => TRUE)))), TRUE);
19

    
20
  return file_view($file, 'full');
21
}
22

    
23
/**
24
 * Menu callback; download a single file entity.
25
 */
26
function file_entity_download_page($file) {
27
  // Ensure there is a valid token to download this file.
28
  if (!variable_get('file_entity_allow_insecure_download', FALSE)) {
29
    if (!isset($_GET['token']) || $_GET['token'] !== file_entity_get_download_token($file)) {
30
      return MENU_ACCESS_DENIED;
31
    }
32
  }
33

    
34
  // If the file does not exist it can cause problems with file_transfer().
35
  if (!is_file($file->uri)) {
36
    return MENU_NOT_FOUND;
37
  }
38
  // @todo Remove this once drupal_basename is fixed for PHP versions greater
39
  //  than '5.3.29'.
40
  $basename_function = version_compare(PHP_VERSION,'5.3.29','>') ? 'basename' : 'drupal_basename';
41
  $headers = array(
42
    'Content-Type' => mime_header_encode($file->filemime),
43
    'Content-Disposition' => 'attachment; filename="' . mime_header_encode($basename_function($file->uri)) . '"',
44
    'Content-Length' => $file->filesize,
45
    'Content-Transfer-Encoding' => 'binary',
46
    'Pragma' => 'no-cache',
47
    'Cache-Control' => 'must-revalidate, post-check=0, pre-check=0',
48
    'Expires' => '0',
49
  );
50

    
51
  // Let other modules alter the download headers.
52
  drupal_alter('file_download_headers', $headers, $file);
53

    
54
  // Let other modules know the file is being downloaded.
55
  module_invoke_all('file_transfer', $file->uri, $headers);
56

    
57
  if (file_entity_file_is_local($file)) {
58
    // For local files, transfer the file and do not reveal the actual URL.
59
    file_transfer($file->uri, $headers);
60
  }
61
  else {
62
    // For remote files, just redirect the user to that file's actual URL.
63
    $headers['Location'] = file_create_url($file->uri);
64
    foreach ($headers as $name => $value) {
65
      drupal_add_http_header($name, $value);
66
    }
67
    drupal_send_headers();
68
    drupal_exit();
69
  }
70
}
71

    
72
/**
73
 * Form callback for adding a file via an upload form.
74
 *
75
 * This is a multi step form which has 1-3 pages:
76
 * - Upload file
77
 * - Choose filetype
78
 *   If there is only one candidate (based on mimetype) we will skip this step.
79
 * - Edit fields
80
 *   Skip this step if there are no fields on this entity type.
81
 */
82
function file_entity_add_upload($form, &$form_state, $options = array()) {
83
  if (!is_array($options)) {
84
    $options = array($options);
85
  }
86
  $step = (isset($form_state['step']) && in_array($form_state['step'], array(1, 2, 3, 4))) ? $form_state['step'] : 1;
87
  $form['#step'] = $step;
88
  $form['#options'] = $options + array(
89
    'types' => array(),
90
    'enabledPlugins' => array(),
91
    'schemes' => array(),
92
    'max_filesize' => '',
93
    'uri_scheme' => file_default_scheme(),
94
    'plugins' => ''
95
  );
96

    
97
  switch ($step) {
98
    case 1:
99
      return file_entity_add_upload_step_upload($form, $form_state, $options);
100

    
101
    case 2:
102
      return file_entity_add_upload_step_filetype($form, $form_state, $options);
103

    
104
    case 3:
105
      return file_entity_add_upload_step_scheme($form, $form_state, $options);
106

    
107
    case 4:
108
      return file_entity_add_upload_step_fields($form, $form_state, $options);
109

    
110
  }
111
}
112

    
113
/**
114
 * Generate form fields for the first step in the add file wizard.
115
 */
116
function file_entity_add_upload_step_upload($form, &$form_state, array $options = array()) {
117
  $form['upload'] = array(
118
    '#type' => 'managed_file',
119
    '#title' => t('Upload a new file'),
120
    '#upload_location' => file_entity_upload_destination_uri($options),
121
    '#upload_validators' => file_entity_get_upload_validators($options),
122
    '#progress_indicator' => 'bar',
123
    '#required' => TRUE,
124
    '#pre_render' => array('file_managed_file_pre_render', 'file_entity_upload_validators_pre_render'),
125
    '#default_value' => isset($form_state['storage']['upload']) ? $form_state['storage']['upload'] : NULL,
126
  );
127

    
128
  $form['actions'] = array('#type' => 'actions');
129
  $form['actions']['next'] = array(
130
    '#type' => 'submit',
131
    '#value' => t('Next'),
132
  );
133

    
134
  form_load_include($form_state, 'inc', 'file_entity', 'file_entity.pages');
135

    
136
  return $form;
137
}
138

    
139
/**
140
 * Generate form fields for the second step in the add file wizard.
141
 */
142
function file_entity_add_upload_step_filetype($form, &$form_state, array $options = array()) {
143
  $file = file_load($form_state['storage']['upload']);
144
  $selected_files = $form['#options']['types'];
145

    
146
  $form['type'] = array(
147
    '#type' => 'radios',
148
    '#title' => t('File type'),
149
    '#options' => file_entity_get_filetype_candidates($file, $selected_files),
150
    '#default_value' => isset($form_state['storage']['type']) ? $form_state['storage']['type'] : NULL,
151
    '#required' => TRUE,
152
  );
153

    
154
  $form['actions'] = array('#type' => 'actions');
155
  $form['actions']['previous'] = array(
156
    '#type' => 'submit',
157
    '#value' => t('Previous'),
158
    '#limit_validation_errors' => array(),
159
    '#submit' => array('file_entity_add_upload_submit'),
160
  );
161
  $form['actions']['next'] = array(
162
    '#type' => 'submit',
163
    '#value' => t('Next'),
164
  );
165

    
166
  return $form;
167
}
168

    
169
/**
170
 * Generate form fields for the third step in the add file wizard.
171
 */
172
function file_entity_add_upload_step_scheme($form, &$form_state, array $options = array()) {
173
  $file = file_load($form_state['storage']['upload']);
174

    
175
  $schemes = array();
176
  foreach (file_get_stream_wrappers(STREAM_WRAPPERS_WRITE_VISIBLE) as $scheme => $info) {
177
    $schemes[$scheme] = check_plain($info['description']);
178
  }
179

    
180
  // Remove any schemes not found in the instance settings.
181
  if (!empty($options['schemes'])) {
182
    $schemes = array_intersect_key($schemes, array_flip($options['schemes']));
183
  }
184

    
185
  // Determine which scheme to use as the default value.
186
  if (isset($form_state['storage']['scheme'])) {
187
    $fallback_scheme = $form_state['storage']['scheme'];
188
  }
189
  elseif (!empty($options['uri_scheme'])) {
190
    $fallback_scheme = $options['uri_scheme'];
191
  }
192
  else {
193
    $fallback_scheme = file_default_scheme();
194
  }
195

    
196
  $form['scheme'] = array(
197
    '#type' => 'radios',
198
    '#title' => t('Destination'),
199
    '#options' => $schemes,
200
    '#default_value' => $fallback_scheme,
201
    '#required' => TRUE,
202
  );
203

    
204
  $form['actions'] = array('#type' => 'actions');
205
  $form['actions']['previous'] = array(
206
    '#type' => 'submit',
207
    '#value' => t('Previous'),
208
    '#limit_validation_errors' => array(),
209
    '#submit' => array('file_entity_add_upload_submit'),
210
  );
211
  $form['actions']['next'] = array(
212
    '#type' => 'submit',
213
    '#value' => t('Next'),
214
  );
215

    
216
  return $form;
217
}
218

    
219
/**
220
 * Generate form fields for the fourth step in the add file wizard.
221
 */
222
function file_entity_add_upload_step_fields($form, &$form_state, array $options = array()) {
223
  // Load the file and overwrite the filetype set on the previous screen.
224
  $file = file_load($form_state['storage']['upload']);
225
  $file->type = $form_state['storage']['type'];
226

    
227
  // Let users modify the filename here.
228
  $form['filename'] = array(
229
    '#type' => 'textfield',
230
    '#title' => t('Name'),
231
    '#default_value' => $file->filename,
232
    '#required' => TRUE,
233
    '#maxlength' => 255,
234
    '#weight' => -10,
235
  );
236

    
237
  // Add fields.
238
  field_attach_form('file', $file, $form, $form_state);
239

    
240
  $form['actions'] = array('#type' => 'actions');
241
  $form['actions']['previous'] = array(
242
    '#type' => 'submit',
243
    '#value' => t('Previous'),
244
    '#limit_validation_errors' => array(),
245
    '#submit' => array('file_entity_add_upload_submit'),
246
  );
247
  $form['actions']['submit'] = array(
248
    '#type' => 'submit',
249
    '#value' => t('Save'),
250
  );
251

    
252
  return $form;
253
}
254

    
255
/**
256
 * Page callback to show file usage information.
257
 */
258
function file_entity_usage_page($file) {
259
  $rows = array();
260
  $occured_entities = array();
261

    
262
  // Determine all of the locations where a file is used, then loop through the
263
  // occurrences and filter out any duplicates.
264
  foreach (file_usage_list($file) as $module => $type) {
265
    foreach ($type as $entity_type => $entity_ids) {
266
      // There are cases where the actual entity doesn't exist.
267
      // We have to handle this.
268
      $entity_info = entity_get_info($entity_type);
269
      $entities = empty($entity_info) ? NULL : entity_load($entity_type, array_keys($entity_ids));
270

    
271
      foreach ($entity_ids as $entity_id => $count) {
272
        // If this entity has already been listed in the table, just add any
273
        // additional usage to the total count column in the table row and
274
        // continue on to the next iteration of the loop.
275
        if (isset($occured_entities[$entity_type][$entity_id])) {
276
          $rows[$occured_entities[$entity_type][$entity_id]][2] += $count;
277
          continue;
278
        }
279

    
280
        // Retrieve the label and the URI of the entity.
281
        $label = empty($entities[$entity_id]) ? $module : entity_label($entity_type, $entities[$entity_id]);
282
        $entity_uri = empty($entities[$entity_id]) ? NULL : entity_uri($entity_type, $entities[$entity_id]);
283

    
284
        // Link the label to the URI when possible.
285
        if (!empty($entity_uri['path'])) {
286
          $entity_label = l($label, $entity_uri['path']);
287
        }
288
        else {
289
          $entity_label = check_plain($label);
290
        }
291

    
292
        $rows[] = array($entity_label, $entity_type, $count);
293

    
294
        // Record the occurrence of the entity to ensure that it isn't listed in
295
        // the table again.
296
        $occured_entities[$entity_type][$entity_id] = count($rows) - 1;
297
      }
298
    }
299
  }
300

    
301
  $header = array(t('Entity'), t('Entity type'), t('Use count'));
302
  $build['usage_table'] = array(
303
    '#theme' => 'table',
304
    '#header' => $header,
305
    '#rows' => $rows,
306
    '#caption' => t('This table lists all of the places where @filename is used.', array('@filename' => $file->filename)),
307
    '#empty' => t('This file is not currently used.'),
308
  );
309

    
310
  return $build;
311
}
312

    
313
/**
314
 * Get the candidate filetypes for a given file.
315
 *
316
 * Only filetypes for which the user has access to create entities are returned.
317
 *
318
 * @param array $file
319
 *   An upload file array from form_state.
320
 *
321
 * @return array
322
 *   An array of file type bundles that support the file's mime type.
323
 */
324
function file_entity_get_filetype_candidates($file, $selected_files = array()) {
325
  $types = module_invoke_all('file_type', $file);
326
  drupal_alter('file_type', $types, $file);
327

    
328
  // If no file types are selected in field instance settings, allow all
329
  // available types.
330
  if (!empty($selected_files)) {
331
    // Limit file type candidates to field allowed types.
332
    $types = array_intersect($types, $selected_files);
333
  }
334

    
335
  $candidates = array();
336
  foreach ($types as $type) {
337
    $file->type = $type;
338
    if (file_entity_access('create', $file)) {
339
      $candidates[$type] = file_entity_type_get_name($file);
340
    }
341
  }
342
  return $candidates;
343
}
344

    
345
/**
346
 * Submit handler for the add file form.
347
 */
348
function file_entity_add_upload_submit($form, &$form_state) {
349
  $form_state['storage'] = isset($form_state['storage']) ? $form_state['storage'] : array();
350
  $form_state['storage'] = array_merge($form_state['storage'], $form_state['values']);
351

    
352
  // Field selected allowed types.
353
  $selected_files = $form['#options']['types'];
354

    
355
  // This var is set to TRUE when we are ready to save the file.
356
  $save = FALSE;
357
  $trigger = $form_state['triggering_element']['#id'];
358

    
359
  $steps_to_check = array(2, 3);
360
  if ($trigger == 'edit-previous') {
361
    // If the previous button was hit,
362
    // the step checking order should be reversed 3, 2.
363
    $steps_to_check = array_reverse($steps_to_check);
364
  }
365

    
366
  foreach ($steps_to_check as $step) {
367
    // Check if we can skip step 2 and 3.
368
    if (($form['#step'] == $step - 1 && $trigger == 'edit-next') || ($form['#step'] == $step + 1 && $trigger == 'edit-previous')) {
369
      $file = file_load($form_state['storage']['upload']);
370
      if ($step == 2) {
371
        // Check if we can skip step 2.
372
        $candidates = file_entity_get_filetype_candidates($file, $selected_files);
373
        if (count($candidates) == 1) {
374
          $candidates_keys = array_keys($candidates);
375
          // There is only one possible filetype for this file.
376
          // Skip the second page.
377
          $form['#step'] += ($trigger == 'edit-previous') ? -1 : 1;
378
          $form_state['storage']['type'] = reset($candidates_keys);
379
        }
380
        elseif (!$candidates || variable_get('file_entity_file_upload_wizard_skip_file_type', FALSE)) {
381
          // Do not assign the file a file type.
382
          $form['#step'] += ($trigger == 'edit-previous') ? -1 : 1;
383
          $form_state['storage']['type'] = FILE_TYPE_NONE;
384
        }
385
      }
386
      else {
387
        // Check if we can skip step 3.
388
        $options = $form['#options'];
389
        $schemes = file_get_stream_wrappers(STREAM_WRAPPERS_WRITE_VISIBLE);
390

    
391
        // Remove any schemes not found in the instance settings.
392
        if (!empty($options['schemes'])) {
393
          $schemes = array_intersect_key($schemes, $options['schemes']);
394
        }
395

    
396
        if (!file_entity_file_is_writeable($file)) {
397
          // The file is read-only (remote) and must use its provided scheme.
398
          $form['#step'] += ($trigger == 'edit-previous') ? -1 : 1;
399
          $form_state['storage']['scheme'] = file_uri_scheme($file->uri);
400
        }
401
        elseif (count($schemes) == 1) {
402
          // There is only one possible stream wrapper for this file.
403
          // Skip the third page.
404
          $form['#step'] += ($trigger == 'edit-previous') ? -1 : 1;
405
          $form_state['storage']['scheme'] = key($schemes);
406
        }
407
        elseif (variable_get('file_entity_file_upload_wizard_skip_scheme', FALSE)) {
408
          $form['#step'] += ($trigger == 'edit-previous') ? -1 : 1;
409

    
410
          // Fallback to the URI scheme specified in the field settings
411
          // otherwise use the default file scheme.
412
          if (!empty($options['uri_scheme'])) {
413
            $form_state['storage']['scheme'] = $options['uri_scheme'];
414
          }
415
          else {
416
            $form_state['storage']['scheme'] = file_default_scheme();
417
          }
418
        }
419
      }
420
    }
421
  }
422

    
423
  // We have the filetype, check if we can skip step 4.
424
  if (($form['#step'] == 3 && $trigger == 'edit-next')) {
425
    $file = file_load($form_state['storage']['upload']);
426
    $form_state['file'] = $file;
427
    if (!field_info_instances('file', $form_state['storage']['type'])) {
428
      // This filetype doesn't have fields, save the file.
429
      $save = TRUE;
430
    }
431
    elseif (variable_get('file_entity_file_upload_wizard_skip_fields', FALSE)) {
432
      // Save the file with blanks fields.
433
      $save = TRUE;
434
    }
435
  }
436

    
437
  // Form id's can vary depending on how many other forms are displayed, so we
438
  // need to do string comparissons. e.g edit-submit--2.
439
  if (strpos($trigger, 'edit-next') !== FALSE) {
440
    $form_state['step'] = $form['#step'] + 1;
441
  }
442
  elseif (strpos($trigger, 'edit-previous') !== FALSE) {
443
    $form_state['step'] = $form['#step'] - 1;
444
  }
445
  elseif (strpos($trigger, 'edit-submit') !== FALSE) {
446
    $save = TRUE;
447
  }
448

    
449
  if ($save) {
450
    $file = file_load($form_state['storage']['upload']);
451
    if ($file) {
452
      if (file_uri_scheme($file->uri) != $form_state['storage']['scheme']) {
453
        $file_destination = $form_state['storage']['scheme'] . '://' . file_uri_target($file->uri);
454
        $file_destination = file_stream_wrapper_uri_normalize($file_destination);
455
        if ($moved_file = file_move($file, $file_destination, FILE_EXISTS_RENAME)) {
456
          // Only re-assign the file object if file_move() did not fail.
457
          $file = $moved_file;
458
        }
459
      }
460
      $file->type = $form_state['storage']['type'];
461
      $file->display = TRUE;
462

    
463
      // Change the file from temporary to permanent.
464
      $file->status = FILE_STATUS_PERMANENT;
465

    
466
      // Save the form fields.
467
      // Keep in mind that the values for the Field API fields must be in
468
      // $form_state['values'] and not in ['storage']. This is true as long as
469
      // the fields are on the last page of the multi step form.
470
      entity_form_submit_build_entity('file', $file, $form, $form_state);
471

    
472
      file_save($file);
473
      $form_state['file'] = $file;
474
      drupal_set_message(t('@type %name was uploaded.', array('@type' => file_entity_type_get_name($file), '%name' => $file->filename)));
475
    }
476
    else {
477
      drupal_set_message(t('An error occurred and no file was uploaded.'), 'error');
478
      return;
479
    }
480

    
481
    // Figure out destination.
482
    if (user_access('administer files')) {
483
      $path = 'admin/content/file';
484
    }
485
    else {
486
      $path = 'file/' . $file->fid;
487
    }
488
    $form_state['redirect'] = $path;
489
  }
490
  else {
491
    $form_state['rebuild'] = TRUE;
492
  }
493

    
494
  // Clear the page and block caches.
495
  cache_clear_all();
496
}
497

    
498
/**
499
 * Determines the upload location for the file add upload form.
500
 *
501
 * @param array $params
502
 *   An array of parameters from the media browser.
503
 * @param array $data
504
 *   (optional) An array of token objects to pass to token_replace().
505
 *
506
 * @return string
507
 *   A file directory URI with tokens replaced.
508
 *
509
 * @see token_replace()
510
 */
511
function file_entity_upload_destination_uri(array $params, array $data = array()) {
512
  $params += array(
513
    'uri_scheme' => file_default_scheme(),
514
    'file_directory' => variable_get('file_entity_default_file_directory', ''),
515
  );
516

    
517
  $destination = trim($params['file_directory'], '/');
518

    
519
  // Replace tokens.
520
  $destination = token_replace($destination, $data);
521

    
522
  return $params['uri_scheme'] . '://' . $destination;
523
}
524

    
525
/**
526
 * Form for uploading multiple files.
527
 */
528
function file_entity_add_upload_multiple($form, &$form_state, $params = array()) {
529
  $form = file_entity_add_upload($form, $form_state, $params);
530
  unset($form['upload']['#title']);
531
  // The validators will be set from plupload anyway. This isn't pretty,
532
  // but don't allow it to show up twice.
533
  unset($form['upload']['#description']);
534

    
535
  $form['upload']['#type'] = 'plupload';
536

    
537
  // Ensure that we call the plupload_element_pre_render function.
538
  // If it isn't called, it doesn't set the JS settings that transfers the
539
  // list of allowed file extensions to the PLUpload widget.
540
  // We override the 'file_entity_upload_validators_pre_render' setting if it
541
  // exists, because both pre-render hooks adds the upload-help with list of
542
  // allowed file extensions.
543
  $index = array_search('file_entity_upload_validators_pre_render', $form['upload']['#pre_render']);
544
  if ($index !== FALSE) {
545
    $form['upload']['#pre_render'][$index] = 'plupload_element_pre_render';
546
  }
547
  else {
548
    $form['upload']['#pre_render'][] = 'plupload_element_pre_render';
549
  }
550

    
551
  $form['submit']['#value'] = t('Start upload');
552
  return $form;
553
}
554

    
555
/**
556
 * Submit handler for the multiple upload form.
557
 */
558
function file_entity_add_upload_multiple_submit($form, &$form_state) {
559
  $upload_location = !empty($form['upload']['#upload_location']) ?
560
    $form['upload']['#upload_location'] . '/' :
561
    variable_get('file_default_scheme', 'public') . '://';
562

    
563
  // Ensure writable destination directory for the files.
564
  file_prepare_directory($upload_location, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
565

    
566
  // We can't use file_save_upload() because of
567
  // http://www.jacobsingh.name/content/tight-coupling-no-not.
568
  foreach ($form_state['values']['upload'] as $uploaded_file) {
569
    if ($uploaded_file['status'] == 'done') {
570
      $source = $uploaded_file['tmppath'];
571
      $destination = file_stream_wrapper_uri_normalize($upload_location . $uploaded_file['name']);
572
      // Rename it to its original name, and put it in its final home.
573
      // Note - not using file_move here because if we call file_get_mime
574
      // (in file_uri_to_object) while it has a .tmp extension, it horks.
575
      $destination = file_unmanaged_move($source, $destination, FILE_EXISTS_RENAME);
576

    
577
      $file = file_uri_to_object($destination);
578
      $file->status = FILE_STATUS_PERMANENT;
579
      file_save($file);
580

    
581
      $form_state['files'][$file->fid] = $file;
582
    }
583
    else {
584
      // @todo: move this to element validate or something.
585
      form_set_error('pud', t('The specified file %name could not be uploaded.', array('%name' => $uploaded_file['name'])));
586
    }
587
  }
588

    
589
  // Redirect to the file edit page.
590
  if (file_entity_access('update', $file) && module_exists('media_bulk_upload')) {
591
    $destination = array();
592
    if (isset($_GET['destination'])) {
593
      $destination = drupal_get_destination();
594
      unset($_GET['destination']);
595
    }
596
    elseif (user_access('administer files')) {
597
      $destination = array('destination' => 'admin/content/file');
598
    }
599
    $form_state['redirect'] = array('admin/content/file/edit-multiple/' . implode(' ', array_keys($form_state['files'])), array('query' => $destination));
600
  }
601
  else {
602
    $form_state['redirect'] = user_access('administer files') ? 'admin/content/file' : '<front>';
603
  }
604

    
605
  // Clear the page and block caches.
606
  cache_clear_all();
607
}
608

    
609
/**
610
 * Page callback: Form constructor for the file edit form.
611
 *
612
 * Path: file/%file/edit
613
 *
614
 * @param object $file
615
 *   A file object from file_load().
616
 *
617
 * @see file_entity_menu()
618
 *
619
 * @todo Rename this form to file_edit_form to ease into core.
620
 */
621
function file_entity_edit($form, &$form_state, $file) {
622
  drupal_set_title(t('<em>Edit @type</em> @title', array('@type' => $file->type, '@title' => $file->filename)), PASS_THROUGH);
623

    
624
  $form_state['file'] = $file;
625

    
626
  $form['#attributes']['class'][] = 'file-form';
627
  if (!empty($file->type)) {
628
    $form['#attributes']['class'][] = 'file-' . $file->type . '-form';
629
  }
630

    
631
  // Basic file information.
632
  // These elements are just values so they are not even sent to the client.
633
  foreach (array('fid', 'type', 'uid', 'timestamp') as $key) {
634
    $form[$key] = array(
635
      '#type' => 'value',
636
      '#value' => isset($file->$key) ? $file->$key : NULL,
637
    );
638
  }
639

    
640
  $form['filename'] = array(
641
    '#type' => 'textfield',
642
    '#title' => t('Name'),
643
    '#default_value' => $file->filename,
644
    '#required' => TRUE,
645
    '#maxlength' => 255,
646
    '#weight' => -10,
647
  );
648

    
649
  // Add a 'replace this file' upload field if the file is writeable.
650
  if (file_entity_file_is_writeable($file)) {
651
    // Set up replacement file validation.
652
    $replacement_options = array();
653

    
654
    // The replacement file must have an extension valid for the original type.
655
    $file_extensions = array();
656
    $file_type_name = isset($file->type) ? $file->type : file_get_type($file);
657
    if ($file_type_name && ($file_type = file_type_load($file_type_name))) {
658
      $file_extensions = file_type_get_valid_extensions($file_type);
659
    }
660

    
661
    // Set allowed file extensions.
662
    if (!empty($file_extensions)) {
663
      // Set to type based file extensions.
664
      $replacement_options['file_extensions'] = implode(' ', $file_extensions);
665
    }
666
    else {
667
      // Fallback to the extension of the current file.
668
      $replacement_options['file_extensions'] = pathinfo($file->uri, PATHINFO_EXTENSION);
669
    }
670

    
671
    $form['replace_upload'] = array(
672
      '#type' => 'file',
673
      '#title' => t('Replace file'),
674
      '#description' => t('This file will replace the existing file. This action cannot be undone.'),
675
      '#upload_validators' => file_entity_get_upload_validators($replacement_options),
676
      '#pre_render' => array('file_entity_upload_validators_pre_render'),
677
    );
678
    $form['replace_keep_original_filename'] = array(
679
      '#type' => 'checkbox',
680
      '#title' => t('Keep original filename'),
681
      '#default_value' => variable_get('file_entity_file_replace_options_keep_original_filename', FALSE),
682
      '#description' => t('Rename the newly uploaded file to the name of the original file. This action cannot be undone.'),
683
    );
684
  }
685

    
686
  $form['preview'] = file_view_file($file, 'preview');
687

    
688
  $form['additional_settings'] = array(
689
    '#type' => 'vertical_tabs',
690
    '#weight' => 99,
691
  );
692

    
693
  // File destination information for administrators.
694
  $form['destination'] = array(
695
    '#type' => 'fieldset',
696
    '#access' => user_access('administer files') && file_entity_file_is_writeable($file),
697
    '#title' => t('Destination'),
698
    '#collapsible' => TRUE,
699
    '#collapsed' => TRUE,
700
    '#group' => 'additional_settings',
701
    '#attributes' => array(
702
      'class' => array('file-form-destination'),
703
    ),
704
    '#attached' => array(
705
      'js' => array(
706
        drupal_get_path('module', 'file_entity') . '/file_entity.js',
707
      ),
708
    ),
709
  );
710

    
711
  $options = array();
712
  foreach (file_get_stream_wrappers(STREAM_WRAPPERS_WRITE_VISIBLE) as $scheme => $info) {
713
    $options[$scheme] = check_plain($info['name']);
714
  }
715

    
716
  $form['destination']['scheme'] = array(
717
    '#type' => 'radios',
718
    '#title' => t('Destination'),
719
    '#options' => $options,
720
    '#default_value' => file_uri_scheme($file->uri),
721
  );
722

    
723
  // File user information for administrators.
724
  $form['user'] = array(
725
    '#type' => 'fieldset',
726
    '#access' => user_access('administer files'),
727
    '#title' => t('User information'),
728
    '#collapsible' => TRUE,
729
    '#collapsed' => TRUE,
730
    '#group' => 'additional_settings',
731
    '#attributes' => array(
732
      'class' => array('file-form-user'),
733
    ),
734
    '#attached' => array(
735
      'js' => array(
736
        drupal_get_path('module', 'file_entity') . '/file_entity.js',
737
        array(
738
          'type' => 'setting',
739
          'data' => array('anonymous' => variable_get('anonymous', t('Anonymous'))),
740
        ),
741
      ),
742
    ),
743
    '#weight' => 90,
744
  );
745
  $form['user']['name'] = array(
746
    '#type' => 'textfield',
747
    '#title' => t('Associated with'),
748
    '#maxlength' => 60,
749
    '#autocomplete_path' => 'user/autocomplete',
750
    '#default_value' => !empty($file->uid) ? user_load($file->uid)->name : '',
751
    '#weight' => -1,
752
    '#description' => t('Leave blank for %anonymous.', array('%anonymous' => variable_get('anonymous', t('Anonymous')))),
753
  );
754

    
755
  // Add the buttons.
756
  $form['actions'] = array('#type' => 'actions');
757
  $form['actions']['submit'] = array(
758
    '#type' => 'submit',
759
    '#value' => t('Save'),
760
    '#weight' => 5,
761
    '#submit' => array('file_entity_edit_submit'),
762
    '#validate' => array('file_entity_edit_validate'),
763
  );
764
  $form['actions']['delete'] = array(
765
    '#type' => 'submit',
766
    '#value' => t('Delete'),
767
    '#weight' => 10,
768
    '#submit' => array('file_entity_edit_delete_submit'),
769
    '#access' => file_entity_access('delete', $file),
770
  );
771

    
772
  // Build the URL for the cancel button taking into account that there might be
773
  // a "destination" that includes query string variables.
774
  $parameters = drupal_get_query_parameters();
775
  $destination = isset($parameters['destination']) ? $parameters['destination'] : 'file/' . $file->fid;
776
  $url = drupal_parse_url($destination);
777

    
778
  $form['actions']['cancel'] = array(
779
    '#type' => 'link',
780
    '#title' => t('Cancel'),
781
    '#href' => $url['path'],
782
    '#options' => array('query' => $url['query']),
783
    '#weight' => 15,
784
  );
785

    
786
  $langcode = function_exists('entity_language') ? entity_language('file', $file) : NULL;
787
  field_attach_form('file', $file, $form, $form_state, $langcode);
788

    
789
  return $form;
790
}
791

    
792
/**
793
 * Form validation handler for file_entity_edit().
794
 */
795
function file_entity_edit_validate($form, &$form_state) {
796
  $file = (object) $form_state['values'];
797

    
798
  // Validate the "associated user" field.
799
  if (!empty($file->name) && !($account = user_load_by_name($file->name))) {
800
    // The use of empty() is mandatory in the context of usernames
801
    // as the empty string denotes the anonymous user. In case we
802
    // are dealing with an anonymous user we set the user ID to 0.
803
    form_set_error('name', t('The username %name does not exist.', array('%name' => $file->name)));
804
  }
805

    
806
  // Handle the replacement file if uploaded.
807
  if (isset($form_state['values']['replace_upload'])) {
808
    // Save the file as a temporary file.
809
    $file = file_save_upload('replace_upload', $form['replace_upload']['#upload_validators']);
810
    if (!empty($file)) {
811
      // Put the temporary file in form_values so we can save it on submit.
812
      $form_state['values']['replace_upload'] = $file;
813
    }
814
    elseif ($file === FALSE) {
815
      // File uploaded failed.
816
      form_set_error('replace_upload', t('The replacement file could not be uploaded.'));
817
    }
818
  }
819

    
820
  // Run entity form validation.
821
  entity_form_field_validate('file', $form, $form_state);
822
}
823

    
824
/**
825
 * Form submission handler for the 'Save' button for file_entity_edit().
826
 */
827
function file_entity_edit_submit($form, &$form_state) {
828
  $file = $form_state['file'];
829
  $orphaned_uri = '';
830

    
831
  // Check if a replacement file has been uploaded.
832
  if (!empty($form_state['values']['replace_upload'])) {
833
    $replacement = $form_state['values']['replace_upload'];
834
    // Move file from temp to permanent home.
835
    if (pathinfo($replacement->uri, PATHINFO_EXTENSION) == pathinfo($file->uri, PATHINFO_EXTENSION)) {
836
      if ($new_file_uri = file_unmanaged_copy($replacement->uri, $file->uri, FILE_EXISTS_REPLACE)) {
837
        // Remove temporary file.
838
        file_delete($replacement);
839
      }
840
    } else {
841
      if (!empty($form_state['values']['replace_keep_original_filename'])
842
      && $form_state['values']['replace_keep_original_filename']) {
843
        $destination_uri = rtrim($file->uri, drupal_basename($file->uri)) . drupal_basename($file->uri);
844
      }
845
      else {
846
        $destination_uri = rtrim($file->uri, drupal_basename($file->uri)) . drupal_basename($replacement->uri);
847
      }
848
      $replace_mode = $destination_uri == $file->uri ? FILE_EXISTS_REPLACE : FILE_EXISTS_RENAME;
849
      if ($new_file_uri = file_unmanaged_copy($replacement->uri, $destination_uri, $replace_mode)) {
850
        // @todo Add watchdog() about replaced file here?
851

    
852
        // Remove temporary file.
853
        file_delete($replacement);
854

    
855
        // Update if the uri target has changed.
856
        if ($new_file_uri != $file->uri) {
857
          // Store the original file uri to delete if save is successful.
858
          $orphaned_uri = $file->uri;
859

    
860
          // Update file entity uri.
861
          $file->uri = $new_file_uri;
862
        }
863
      }
864
    }
865
  }
866

    
867
  // Run entity form submit handling and save the file.
868
  entity_form_submit_build_entity('file', $file, $form, $form_state);
869

    
870
  // A user might assign the associated user by entering a user name in the file
871
  // edit form, which we then need to translate to a user ID.
872
  if (isset($file->name)) {
873
    // The use of isset() is mandatory in the context of user IDs, because
874
    // user ID 0 denotes the anonymous user.
875
    if ($user = user_load_by_name($file->name)) {
876
      $file->uid = $user->uid;
877
    }
878
    else {
879
      // Anonymous user.
880
      $file->uid = 0;
881
    }
882
  }
883
  elseif ($file->uid) {
884
    $user = user_load($file->uid);
885
    $file->name = $user->name;
886
  }
887

    
888
  if (file_uri_scheme($file->uri) != $form_state['values']['scheme']) {
889
    $file_destination = $form_state['values']['scheme'] . '://' . file_uri_target($file->uri);
890
    $file_destination = file_stream_wrapper_uri_normalize($file_destination);
891
    $file_destination_dirname = drupal_dirname($file_destination);
892
    // Create the directory in case it doesn't exist.
893
    file_prepare_directory($file_destination_dirname, FILE_CREATE_DIRECTORY);
894
    if ($moved_file = file_move($file, $file_destination, FILE_EXISTS_RENAME)) {
895
      // Only re-assign the file object if file_move() did not fail.
896
      $file = $moved_file;
897
    }
898
  }
899

    
900
  file_save($file);
901

    
902
  $args = array(
903
    '@type' => file_entity_type_get_name($file),
904
    '%title' => entity_label('file', $file),
905
  );
906
  watchdog('file', '@type: updated %title.', $args);
907
  drupal_set_message(t('@type %title has been updated.', $args));
908

    
909
  // Clean up orphaned file.
910
  if (!empty($orphaned_uri)) {
911
    file_unmanaged_delete($orphaned_uri);
912

    
913
    $args['@orphaned'] = file_uri_target($orphaned_uri);
914
    watchdog('file', '@type: deleted orphaned file @orphaned for %title.', $args);
915
    drupal_set_message(t('The replaced @type @orphaned has been deleted.', $args));
916
  }
917

    
918
  $form_state['redirect'] = 'file/' . $file->fid;
919

    
920
  // Clear the page and block caches.
921
  cache_clear_all();
922
}
923

    
924
/**
925
 * Form submission handler for the 'Delete' button for file_entity_edit().
926
 */
927
function file_entity_edit_delete_submit($form, &$form_state) {
928
  $fid = $form_state['values']['fid'];
929
  $destination = array();
930
  if (isset($_GET['destination'])) {
931
    $destination = drupal_get_destination();
932
    unset($_GET['destination']);
933
  }
934
  $form_state['redirect'] = array('file/' . $fid . '/delete', array('query' => $destination));
935

    
936
  // Clear the page and block caches.
937
  cache_clear_all();
938
}
939

    
940
/**
941
 * Page callback: Form constructor for the file deletion confirmation form.
942
 *
943
 * Path: file/%file/delete
944
 *
945
 * @param object $file
946
 *   A file object from file_load().
947
 *
948
 * @see file_entity_menu()
949
 */
950
function file_entity_delete_form($form, &$form_state, $file) {
951
  $form_state['file'] = $file;
952

    
953
  $form['fid'] = array(
954
    '#type' => 'value',
955
    '#value' => $file->fid,
956
  );
957

    
958
  $description = t('This action cannot be undone.');
959
  if ($references = file_usage_list($file)) {
960
    $description .= ' ' . t('This file is currently in use and may cause problems if deleted.');
961
  }
962

    
963
  return confirm_form($form,
964
    t('Are you sure you want to delete the file %title?', array(
965
      '%title' => entity_label('file', $file),
966
    )),
967
    'file/' . $file->fid,
968
    $description,
969
    t('Delete')
970
  );
971
}
972

    
973
/**
974
 * Form submission handler for file_entity_delete_form().
975
 */
976
function file_entity_delete_form_submit($form, &$form_state) {
977
  if ($form_state['values']['confirm'] && $file = file_load($form_state['values']['fid'])) {
978
    // Use file_delete_multiple() rather than file_delete() since we want to
979
    // avoid unwanted validation and usage checking.
980
    file_delete_multiple(array($file->fid));
981

    
982
    $args = array(
983
      '@type' => file_entity_type_get_name($file),
984
      '%title' => entity_label('file', $file),
985
    );
986
    watchdog('file', '@type: deleted %title.', $args);
987
    drupal_set_message(t('@type %title has been deleted.', $args));
988
  }
989

    
990
  $form_state['redirect'] = '<front>';
991

    
992
  // Clear the page and block caches.
993
  cache_clear_all();
994
}
995

    
996
/**
997
 * Form constructor for file deletion confirmation form.
998
 *
999
 * @param array $files
1000
 *   An array of file objects.
1001
 */
1002
function file_entity_multiple_delete_form($form, &$form_state, array $files) {
1003
  $form['files'] = array(
1004
    '#prefix' => '<ul>',
1005
    '#suffix' => '</ul>',
1006
    '#tree' => TRUE,
1007
  );
1008

    
1009
  $files_have_usage = FALSE;
1010
  foreach ($files as $fid => $file) {
1011
    $title = entity_label('file', $file);
1012
    $usage = file_usage_list($file);
1013
    if (!empty($usage)) {
1014
      $files_have_usage = TRUE;
1015
      $title = t('@title (in use)', array('@title' => $title));
1016
    }
1017
    else {
1018
      $title = check_plain($title);
1019
    }
1020
    $form['files'][$fid] = array(
1021
      '#type' => 'hidden',
1022
      '#value' => $fid,
1023
      '#prefix' => '<li>',
1024
      '#suffix' => $title . "</li>\n",
1025
    );
1026
  }
1027

    
1028
  $form['operation'] = array(
1029
    '#type' => 'hidden',
1030
    '#value' => 'delete',
1031
  );
1032

    
1033
  $description = t('This action cannot be undone.');
1034
  if ($files_have_usage) {
1035
    $description .= ' ' . t('Some of the files are currently in use and may cause problems if deleted.');
1036
  }
1037

    
1038
  return confirm_form(
1039
    $form,
1040
    format_plural(count($files), 'Are you sure you want to delete this file?', 'Are you sure you want to delete these files?'),
1041
    'admin/content/file',
1042
    $description,
1043
    t('Delete')
1044
  );
1045
}
1046

    
1047
/**
1048
 * Form submission handler for file_entity_multiple_delete_form().
1049
 */
1050
function file_entity_multiple_delete_form_submit($form, &$form_state) {
1051
  if ($form_state['values']['confirm'] && $fids = array_keys($form_state['values']['files'])) {
1052
    file_delete_multiple($fids);
1053
    $count = count($fids);
1054
    watchdog('file', 'Deleted @count files.', array('@count' => $count));
1055
    drupal_set_message(format_plural($count, 'Deleted one file.', 'Deleted @count files.'));
1056
  }
1057
  $form_state['redirect'] = 'admin/content/file';
1058

    
1059
  // Clear the page and block caches.
1060
  cache_clear_all();
1061
}
1062

    
1063
/**
1064
 * Page callback for the file edit form.
1065
 *
1066
 * @deprecated
1067
 *   Use drupal_get_form('file_entity_edit')
1068
 */
1069
function file_entity_page_edit($file) {
1070
  return drupal_get_form('file_entity_edit', $file);
1071
}
1072

    
1073
/**
1074
 * Page callback for the file deletion confirmation form.
1075
 *
1076
 * @deprecated
1077
 *   Use drupal_get_form('file_entity_delete_form')
1078
 */
1079
function file_entity_page_delete($file) {
1080
  return drupal_get_form('file_entity_delete_form');
1081
}
1082

    
1083
/**
1084
 * Retrieves the upload validators for a file.
1085
 *
1086
 * @param array $options
1087
 *   (optional) An array of options for file validation.
1088
 *
1089
 * @return array
1090
 *   An array suitable for passing to file_save_upload() or for a managed_file
1091
 *   or upload element's '#upload_validators' property.
1092
 */
1093
function file_entity_get_upload_validators(array $options = array()) {
1094
  // Set up file upload validators.
1095
  $validators = array();
1096

    
1097
  // Validate file extensions. If there are no file extensions in $params and
1098
  // there are no Media defaults, there is no file extension validation.
1099
  if (!empty($options['file_extensions'])) {
1100
    $validators['file_validate_extensions'] = array($options['file_extensions']);
1101
  }
1102
  else {
1103
    $validators['file_validate_extensions'] = array(variable_get('file_entity_default_allowed_extensions', 'jpg jpeg gif png txt doc docx xls xlsx pdf ppt pptx pps ppsx odt ods odp mp3 mov mp4 m4a m4v mpeg avi ogg oga ogv weba webp webm'));
1104
  }
1105

    
1106
  // Cap the upload size according to the system or user defined limit.
1107
  $max_filesize = parse_size(file_upload_max_size());
1108
  $file_entity_max_filesize = parse_size(variable_get('file_entity_max_filesize', ''));
1109

    
1110
  // If the user defined a size limit, use the smaller of the two.
1111
  if (!empty($file_entity_max_filesize)) {
1112
    $max_filesize = min($max_filesize, $file_entity_max_filesize);
1113
  }
1114

    
1115
  if (!empty($options['max_filesize']) && $options['max_filesize'] < $max_filesize) {
1116
    $max_filesize = parse_size($options['max_filesize']);
1117
  }
1118

    
1119
  // There is always a file size limit due to the PHP server limit.
1120
  $validators['file_validate_size'] = array($max_filesize);
1121

    
1122
  // Add image validators.
1123
  $options += array('min_resolution' => 0, 'max_resolution' => 0);
1124
  if ($options['min_resolution'] || $options['max_resolution']) {
1125
    $validators['file_validate_image_resolution'] = array($options['max_resolution'], $options['min_resolution']);
1126
  }
1127

    
1128
  // Add other custom upload validators from options.
1129
  if (!empty($options['upload_validators'])) {
1130
    $validators += $options['upload_validators'];
1131
  }
1132

    
1133
  return $validators;
1134
}
1135

    
1136
function file_entity_upload_archive_form($form, &$form_state) {
1137
  $options = array(
1138
    'file_extensions' => archiver_get_extensions(),
1139
  );
1140

    
1141
  $form['upload'] = array(
1142
    '#type' => 'managed_file',
1143
    '#title' => t('Upload an archive file'),
1144
    '#upload_location' => NULL, // Upload to the temporary directory.
1145
    '#upload_validators' => file_entity_get_upload_validators($options),
1146
    '#progress_indicator' => 'bar',
1147
    '#required' => TRUE,
1148
    '#pre_render' => array('file_managed_file_pre_render', 'file_entity_upload_validators_pre_render'),
1149
  );
1150

    
1151
  $form['pattern'] = array(
1152
    '#type' => 'textfield',
1153
    '#title' => t('Pattern'),
1154
    '#description' => t('Only files matching this pattern will be imported. For example, to import all jpg and gif files, the pattern would be <em>*.jpg|*.gif</em>. Use <em>.*</em> to extract all files in the archive.'),
1155
    '#default_value' => '.*',
1156
    '#required' => TRUE,
1157
  );
1158

    
1159
  $form['actions'] = array('#type' => 'actions');
1160
  $form['actions']['submit'] = array(
1161
    '#type' => 'submit',
1162
    '#value' => t('Submit'),
1163
  );
1164

    
1165
  form_load_include($form_state, 'inc', 'file_entity', 'file_entity.pages');
1166

    
1167
  return $form;
1168
}
1169

    
1170
/**
1171
 * Upload a file.
1172
 */
1173
function file_entity_upload_archive_form_submit($form, &$form_state) {
1174
  $form_state['files'] = array();
1175

    
1176
  if ($archive = file_load($form_state['values']['upload'])) {
1177
    if ($archiver = archiver_get_archiver($archive->uri)) {
1178
      $files = $archiver->listContents();
1179

    
1180
      $extract_dir = file_default_scheme() . '://' . pathinfo($archive->filename, PATHINFO_FILENAME);
1181
      $extract_dir = file_destination($extract_dir, FILE_EXISTS_RENAME);
1182
      if (!file_prepare_directory($extract_dir, FILE_MODIFY_PERMISSIONS | FILE_CREATE_DIRECTORY)) {
1183
        throw new Exception(t('Unable to prepar, e directory %dir for extraction.', array('%dir' => $extract_dir)));
1184
      }
1185

    
1186
      $archiver->extract($extract_dir);
1187
      $pattern = '/' . $form_state['values']['pattern'] . '/';
1188
      if ($files = file_scan_directory($extract_dir, $pattern)) {
1189
        foreach ($files as $file) {
1190
          $file->status = FILE_STATUS_PERMANENT;
1191
          $file->uid = $archive->uid;
1192
          file_save($file);
1193
          $form_state['files'][$file->fid] = $file;
1194
        }
1195
      }
1196
      drupal_set_message(t('Extracted %file and added @count new files.', array('%file' => $archive->filename, '@count' => count($files))));
1197
    }
1198
    else {
1199
      throw new Exception(t('Cannot extract %file, not a valid archive.', array('%file' => $archive->uri)));
1200
    }
1201
  }
1202

    
1203
  // Redirect to the file edit page.
1204
  if (file_entity_access('edit') && module_exists('multiform')) {
1205
    $destination = array('destination' => 'admin/content/file');
1206
    if (isset($_GET['destination'])) {
1207
      $destination = drupal_get_destination();
1208
      unset($_GET['destination']);
1209
    }
1210
    $form_state['redirect'] = array('admin/content/file/edit-multiple/' . implode(' ', array_keys($form_state['files'])), array('query' => $destination));
1211
  }
1212
  else {
1213
    $form_state['redirect'] = 'admin/content/file';
1214
  }
1215
}