Projet

Général

Profil

Paste
Télécharger (39,3 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / file_entity / file_entity.pages.inc @ 59ae487e

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;
89
  switch ($step) {
90
    case 1:
91
      return file_entity_add_upload_step_upload($form, $form_state, $options);
92

    
93
    case 2:
94
      return file_entity_add_upload_step_filetype($form, $form_state, $options);
95

    
96
    case 3:
97
      return file_entity_add_upload_step_scheme($form, $form_state, $options);
98

    
99
    case 4:
100
      return file_entity_add_upload_step_fields($form, $form_state, $options);
101

    
102
  }
103
}
104

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

    
120
  $form['actions'] = array('#type' => 'actions');
121
  $form['actions']['next'] = array(
122
    '#type' => 'submit',
123
    '#value' => t('Next'),
124
  );
125

    
126
  form_load_include($form_state, 'inc', 'file_entity', 'file_entity.pages');
127

    
128
  return $form;
129
}
130

    
131
/**
132
 * Generate form fields for the second step in the add file wizard.
133
 */
134
function file_entity_add_upload_step_filetype($form, &$form_state, array $options = array()) {
135
  $file = file_load($form_state['storage']['upload']);
136

    
137
  $form['type'] = array(
138
    '#type' => 'radios',
139
    '#title' => t('File type'),
140
    '#options' => file_entity_get_filetype_candidates($file),
141
    '#default_value' => isset($form_state['storage']['type']) ? $form_state['storage']['type'] : NULL,
142
    '#required' => TRUE,
143
  );
144

    
145
  $form['actions'] = array('#type' => 'actions');
146
  $form['actions']['previous'] = array(
147
    '#type' => 'submit',
148
    '#value' => t('Previous'),
149
    '#limit_validation_errors' => array(),
150
    '#submit' => array('file_entity_add_upload_submit'),
151
  );
152
  $form['actions']['next'] = array(
153
    '#type' => 'submit',
154
    '#value' => t('Next'),
155
  );
156

    
157
  return $form;
158
}
159

    
160
/**
161
 * Generate form fields for the third step in the add file wizard.
162
 */
163
function file_entity_add_upload_step_scheme($form, &$form_state, array $options = array()) {
164
  $file = file_load($form_state['storage']['upload']);
165

    
166
  $schemes = array();
167
  foreach (file_get_stream_wrappers(STREAM_WRAPPERS_WRITE_VISIBLE) as $scheme => $info) {
168
    $schemes[$scheme] = check_plain($info['description']);
169
  }
170

    
171
  // Remove any schemes not found in the instance settings.
172
  if (!empty($options['schemes'])) {
173
    $schemes = array_intersect_key($schemes, array_flip($options['schemes']));
174
  }
175

    
176
  // Determine which scheme to use as the default value.
177
  if (isset($form_state['storage']['scheme'])) {
178
    $fallback_scheme = $form_state['storage']['scheme'];
179
  }
180
  elseif (!empty($options['uri_scheme'])) {
181
    $fallback_scheme = $options['uri_scheme'];
182
  }
183
  else {
184
    $fallback_scheme = file_default_scheme();
185
  }
186

    
187
  $form['scheme'] = array(
188
    '#type' => 'radios',
189
    '#title' => t('Destination'),
190
    '#options' => $schemes,
191
    '#default_value' => $fallback_scheme,
192
    '#required' => TRUE,
193
  );
194

    
195
  $form['actions'] = array('#type' => 'actions');
196
  $form['actions']['previous'] = array(
197
    '#type' => 'submit',
198
    '#value' => t('Previous'),
199
    '#limit_validation_errors' => array(),
200
    '#submit' => array('file_entity_add_upload_submit'),
201
  );
202
  $form['actions']['next'] = array(
203
    '#type' => 'submit',
204
    '#value' => t('Next'),
205
  );
206

    
207
  return $form;
208
}
209

    
210
/**
211
 * Generate form fields for the fourth step in the add file wizard.
212
 */
213
function file_entity_add_upload_step_fields($form, &$form_state, array $options = array()) {
214
  // Load the file and overwrite the filetype set on the previous screen.
215
  $file = file_load($form_state['storage']['upload']);
216
  $file->type = $form_state['storage']['type'];
217

    
218
  // Let users modify the filename here.
219
  $form['filename'] = array(
220
    '#type' => 'textfield',
221
    '#title' => t('Name'),
222
    '#default_value' => $file->filename,
223
    '#required' => TRUE,
224
    '#maxlength' => 255,
225
    '#weight' => -10,
226
  );
227

    
228
  // Add fields.
229
  field_attach_form('file', $file, $form, $form_state);
230

    
231
  $form['actions'] = array('#type' => 'actions');
232
  $form['actions']['previous'] = array(
233
    '#type' => 'submit',
234
    '#value' => t('Previous'),
235
    '#limit_validation_errors' => array(),
236
    '#submit' => array('file_entity_add_upload_submit'),
237
  );
238
  $form['actions']['submit'] = array(
239
    '#type' => 'submit',
240
    '#value' => t('Save'),
241
  );
242

    
243
  return $form;
244
}
245

    
246
/**
247
 * Page callback to show file usage information.
248
 */
249
function file_entity_usage_page($file) {
250
  $rows = array();
251
  $occured_entities = array();
252

    
253
  // Determine all of the locations where a file is used, then loop through the
254
  // occurrences and filter out any duplicates.
255
  foreach (file_usage_list($file) as $module => $type) {
256
    foreach ($type as $entity_type => $entity_ids) {
257
      // There are cases where the actual entity doesn't exist.
258
      // We have to handle this.
259
      $entity_info = entity_get_info($entity_type);
260
      $entities = empty($entity_info) ? NULL : entity_load($entity_type, array_keys($entity_ids));
261

    
262
      foreach ($entity_ids as $entity_id => $count) {
263
        // If this entity has already been listed in the table, just add any
264
        // additional usage to the total count column in the table row and
265
        // continue on to the next iteration of the loop.
266
        if (isset($occured_entities[$entity_type][$entity_id])) {
267
          $rows[$occured_entities[$entity_type][$entity_id]][2] += $count;
268
          continue;
269
        }
270

    
271
        // Retrieve the label and the URI of the entity.
272
        $label = empty($entities[$entity_id]) ? $module : entity_label($entity_type, $entities[$entity_id]);
273
        $entity_uri = empty($entities[$entity_id]) ? NULL : entity_uri($entity_type, $entities[$entity_id]);
274

    
275
        // Link the label to the URI when possible.
276
        if (!empty($entity_uri['path'])) {
277
          $entity_label = l($label, $entity_uri['path']);
278
        }
279
        else {
280
          $entity_label = check_plain($label);
281
        }
282

    
283
        $rows[] = array($entity_label, $entity_type, $count);
284

    
285
        // Record the occurrence of the entity to ensure that it isn't listed in
286
        // the table again.
287
        $occured_entities[$entity_type][$entity_id] = count($rows) - 1;
288
      }
289
    }
290
  }
291

    
292
  $header = array(t('Entity'), t('Entity type'), t('Use count'));
293
  $build['usage_table'] = array(
294
    '#theme' => 'table',
295
    '#header' => $header,
296
    '#rows' => $rows,
297
    '#caption' => t('This table lists all of the places where @filename is used.', array('@filename' => $file->filename)),
298
    '#empty' => t('This file is not currently used.'),
299
  );
300

    
301
  return $build;
302
}
303

    
304
/**
305
 * Get the candidate filetypes for a given file.
306
 *
307
 * Only filetypes for which the user has access to create entities are returned.
308
 *
309
 * @param array $file
310
 *   An upload file array from form_state.
311
 *
312
 * @return array
313
 *   An array of file type bundles that support the file's mime type.
314
 */
315
function file_entity_get_filetype_candidates($file) {
316
  $types = module_invoke_all('file_type', $file);
317
  drupal_alter('file_type', $types, $file);
318
  $candidates = array();
319
  foreach ($types as $type) {
320
    $file->type = $type;
321
    if (file_entity_access('create', $file)) {
322
      $candidates[$type] = file_entity_type_get_name($file);
323
    }
324
  }
325
  return $candidates;
326
}
327

    
328
/**
329
 * Submit handler for the add file form.
330
 */
331
function file_entity_add_upload_submit($form, &$form_state) {
332
  $form_state['storage'] = isset($form_state['storage']) ? $form_state['storage'] : array();
333
  $form_state['storage'] = array_merge($form_state['storage'], $form_state['values']);
334

    
335
  // This var is set to TRUE when we are ready to save the file.
336
  $save = FALSE;
337
  $trigger = $form_state['triggering_element']['#id'];
338

    
339
  $steps_to_check = array(2, 3);
340
  if ($trigger == 'edit-previous') {
341
    // If the previous button was hit,
342
    // the step checking order should be reversed 3, 2.
343
    $steps_to_check = array_reverse($steps_to_check);
344
  }
345

    
346
  foreach ($steps_to_check as $step) {
347
    // Check if we can skip step 2 and 3.
348
    if (($form['#step'] == $step - 1 && $trigger == 'edit-next') || ($form['#step'] == $step + 1 && $trigger == 'edit-previous')) {
349
      $file = file_load($form_state['storage']['upload']);
350
      if ($step == 2) {
351
        // Check if we can skip step 2.
352
        $candidates = file_entity_get_filetype_candidates($file);
353
        if (count($candidates) == 1) {
354
          $candidates_keys = array_keys($candidates);
355
          // There is only one possible filetype for this file.
356
          // Skip the second page.
357
          $form['#step'] += ($trigger == 'edit-previous') ? -1 : 1;
358
          $form_state['storage']['type'] = reset($candidates_keys);
359
        }
360
        elseif (!$candidates || variable_get('file_entity_file_upload_wizard_skip_file_type', FALSE)) {
361
          // Do not assign the file a file type.
362
          $form['#step'] += ($trigger == 'edit-previous') ? -1 : 1;
363
          $form_state['storage']['type'] = FILE_TYPE_NONE;
364
        }
365
      }
366
      else {
367
        // Check if we can skip step 3.
368
        $options = $form['#options'];
369
        $schemes = file_get_stream_wrappers(STREAM_WRAPPERS_WRITE_VISIBLE);
370

    
371
        // Remove any schemes not found in the instance settings.
372
        if (!empty($options['schemes'])) {
373
          $schemes = array_intersect_key($schemes, $options['schemes']);
374
        }
375

    
376
        if (!file_entity_file_is_writeable($file)) {
377
          // The file is read-only (remote) and must use its provided scheme.
378
          $form['#step'] += ($trigger == 'edit-previous') ? -1 : 1;
379
          $form_state['storage']['scheme'] = file_uri_scheme($file->uri);
380
        }
381
        elseif (count($schemes) == 1) {
382
          // There is only one possible stream wrapper for this file.
383
          // Skip the third page.
384
          $form['#step'] += ($trigger == 'edit-previous') ? -1 : 1;
385
          $form_state['storage']['scheme'] = key($schemes);
386
        }
387
        elseif (variable_get('file_entity_file_upload_wizard_skip_scheme', FALSE)) {
388
          $form['#step'] += ($trigger == 'edit-previous') ? -1 : 1;
389

    
390
          // Fallback to the URI scheme specified in the field settings
391
          // otherwise use the default file scheme.
392
          if (!empty($options['uri_scheme'])) {
393
            $form_state['storage']['scheme'] = $options['uri_scheme'];
394
          }
395
          else {
396
            $form_state['storage']['scheme'] = file_default_scheme();
397
          }
398
        }
399
      }
400
    }
401
  }
402

    
403
  // We have the filetype, check if we can skip step 4.
404
  if (($form['#step'] == 3 && $trigger == 'edit-next')) {
405
    $file = file_load($form_state['storage']['upload']);
406
    $form_state['file'] = $file;
407
    if (!field_info_instances('file', $form_state['storage']['type'])) {
408
      // This filetype doesn't have fields, save the file.
409
      $save = TRUE;
410
    }
411
    elseif (variable_get('file_entity_file_upload_wizard_skip_fields', FALSE)) {
412
      // Save the file with blanks fields.
413
      $save = TRUE;
414
    }
415
  }
416

    
417
  // Form id's can vary depending on how many other forms are displayed, so we
418
  // need to do string comparissons. e.g edit-submit--2.
419
  if (strpos($trigger, 'edit-next') !== FALSE) {
420
    $form_state['step'] = $form['#step'] + 1;
421
  }
422
  elseif (strpos($trigger, 'edit-previous') !== FALSE) {
423
    $form_state['step'] = $form['#step'] - 1;
424
  }
425
  elseif (strpos($trigger, 'edit-submit') !== FALSE) {
426
    $save = TRUE;
427
  }
428

    
429
  if ($save) {
430
    $file = file_load($form_state['storage']['upload']);
431
    if ($file) {
432
      if (file_uri_scheme($file->uri) != $form_state['storage']['scheme']) {
433
        $file_destination = $form_state['storage']['scheme'] . '://' . file_uri_target($file->uri);
434
        $file_destination = file_stream_wrapper_uri_normalize($file_destination);
435
        if ($moved_file = file_move($file, $file_destination, FILE_EXISTS_RENAME)) {
436
          // Only re-assign the file object if file_move() did not fail.
437
          $file = $moved_file;
438
        }
439
      }
440
      $file->type = $form_state['storage']['type'];
441
      $file->display = TRUE;
442

    
443
      // Change the file from temporary to permanent.
444
      $file->status = FILE_STATUS_PERMANENT;
445

    
446
      // Save the form fields.
447
      // Keep in mind that the values for the Field API fields must be in
448
      // $form_state['values'] and not in ['storage']. This is true as long as
449
      // the fields are on the last page of the multi step form.
450
      entity_form_submit_build_entity('file', $file, $form, $form_state);
451

    
452
      file_save($file);
453
      $form_state['file'] = $file;
454
      drupal_set_message(t('@type %name was uploaded.', array('@type' => file_entity_type_get_name($file), '%name' => $file->filename)));
455
    }
456
    else {
457
      drupal_set_message(t('An error occurred and no file was uploaded.'), 'error');
458
      return;
459
    }
460

    
461
    // Figure out destination.
462
    if (user_access('administer files')) {
463
      $path = 'admin/content/file';
464
    }
465
    else {
466
      $path = 'file/' . $file->fid;
467
    }
468
    $form_state['redirect'] = $path;
469
  }
470
  else {
471
    $form_state['rebuild'] = TRUE;
472
  }
473

    
474
  // Clear the page and block caches.
475
  cache_clear_all();
476
}
477

    
478
/**
479
 * Determines the upload location for the file add upload form.
480
 *
481
 * @param array $params
482
 *   An array of parameters from the media browser.
483
 * @param array $data
484
 *   (optional) An array of token objects to pass to token_replace().
485
 *
486
 * @return string
487
 *   A file directory URI with tokens replaced.
488
 *
489
 * @see token_replace()
490
 */
491
function file_entity_upload_destination_uri(array $params, array $data = array()) {
492
  $params += array(
493
    'uri_scheme' => file_default_scheme(),
494
    'file_directory' => '',
495
  );
496

    
497
  $destination = trim($params['file_directory'], '/');
498

    
499
  // Replace tokens.
500
  $destination = token_replace($destination, $data);
501

    
502
  return $params['uri_scheme'] . '://' . $destination;
503
}
504

    
505
/**
506
 * Form for uploading multiple files.
507
 */
508
function file_entity_add_upload_multiple($form, &$form_state, $params = array()) {
509
  $form = file_entity_add_upload($form, $form_state, $params);
510
  unset($form['upload']['#title']);
511
  // The validators will be set from plupload anyway. This isn't pretty,
512
  // but don't allow it to show up twice.
513
  unset($form['upload']['#description']);
514

    
515
  $form['upload']['#type'] = 'plupload';
516

    
517
  // Ensure that we call the plupload_element_pre_render function.
518
  // If it isn't called, it doesn't set the JS settings that transfers the
519
  // list of allowed file extensions to the PLUpload widget.
520
  // We override the 'file_entity_upload_validators_pre_render' setting if it
521
  // exists, because both pre-render hooks adds the upload-help with list of
522
  // allowed file extensions.
523
  $index = array_search('file_entity_upload_validators_pre_render', $form['upload']['#pre_render']);
524
  if ($index !== FALSE) {
525
    $form['upload']['#pre_render'][$index] = 'plupload_element_pre_render';
526
  }
527
  else {
528
    $form['upload']['#pre_render'][] = 'plupload_element_pre_render';
529
  }
530

    
531
  $form['submit']['#value'] = t('Start upload');
532
  return $form;
533
}
534

    
535
/**
536
 * Submit handler for the multiple upload form.
537
 */
538
function file_entity_add_upload_multiple_submit($form, &$form_state) {
539
  $upload_location = !empty($form['upload']['#upload_location']) ?
540
    $form['upload']['#upload_location'] . '/' :
541
    variable_get('file_default_scheme', 'public') . '://';
542

    
543
  // Ensure writable destination directory for the files.
544
  file_prepare_directory($upload_location, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
545

    
546
  // We can't use file_save_upload() because of
547
  // http://www.jacobsingh.name/content/tight-coupling-no-not.
548
  foreach ($form_state['values']['upload'] as $uploaded_file) {
549
    if ($uploaded_file['status'] == 'done') {
550
      $source = $uploaded_file['tmppath'];
551
      $destination = file_stream_wrapper_uri_normalize($upload_location . $uploaded_file['name']);
552
      // Rename it to its original name, and put it in its final home.
553
      // Note - not using file_move here because if we call file_get_mime
554
      // (in file_uri_to_object) while it has a .tmp extension, it horks.
555
      $destination = file_unmanaged_move($source, $destination, FILE_EXISTS_RENAME);
556

    
557
      $file = file_uri_to_object($destination);
558
      $file->status = FILE_STATUS_PERMANENT;
559
      file_save($file);
560

    
561
      $form_state['files'][$file->fid] = $file;
562
    }
563
    else {
564
      // @todo: move this to element validate or something.
565
      form_set_error('pud', t('The specified file %name could not be uploaded.', array('%name' => $uploaded_file['name'])));
566
    }
567
  }
568

    
569
  // Redirect to the file edit page.
570
  if (file_entity_access('update', $file) && module_exists('media_bulk_upload')) {
571
    $destination = array();
572
    if (isset($_GET['destination'])) {
573
      $destination = drupal_get_destination();
574
      unset($_GET['destination']);
575
    }
576
    elseif (user_access('administer files')) {
577
      $destination = array('destination' => 'admin/content/file');
578
    }
579
    $form_state['redirect'] = array('admin/content/file/edit-multiple/' . implode(' ', array_keys($form_state['files'])), array('query' => $destination));
580
  }
581
  else {
582
    $form_state['redirect'] = user_access('administer files') ? 'admin/content/file' : '<front>';
583
  }
584

    
585
  // Clear the page and block caches.
586
  cache_clear_all();
587
}
588

    
589
/**
590
 * Page callback: Form constructor for the file edit form.
591
 *
592
 * Path: file/%file/edit
593
 *
594
 * @param object $file
595
 *   A file object from file_load().
596
 *
597
 * @see file_entity_menu()
598
 *
599
 * @todo Rename this form to file_edit_form to ease into core.
600
 */
601
function file_entity_edit($form, &$form_state, $file) {
602
  drupal_set_title(t('<em>Edit @type</em> @title', array('@type' => $file->type, '@title' => $file->filename)), PASS_THROUGH);
603

    
604
  $form_state['file'] = $file;
605

    
606
  $form['#attributes']['class'][] = 'file-form';
607
  if (!empty($file->type)) {
608
    $form['#attributes']['class'][] = 'file-' . $file->type . '-form';
609
  }
610

    
611
  // Basic file information.
612
  // These elements are just values so they are not even sent to the client.
613
  foreach (array('fid', 'type', 'uid', 'timestamp') as $key) {
614
    $form[$key] = array(
615
      '#type' => 'value',
616
      '#value' => isset($file->$key) ? $file->$key : NULL,
617
    );
618
  }
619

    
620
  $form['filename'] = array(
621
    '#type' => 'textfield',
622
    '#title' => t('Name'),
623
    '#default_value' => $file->filename,
624
    '#required' => TRUE,
625
    '#maxlength' => 255,
626
    '#weight' => -10,
627
  );
628

    
629
  // Add a 'replace this file' upload field if the file is writeable.
630
  if (file_entity_file_is_writeable($file)) {
631
    // Set up replacement file validation.
632
    $replacement_options = array();
633

    
634
    // The replacement file must have an extension valid for the original type.
635
    $file_extensions = array();
636
    $file_type_name = isset($file->type) ? $file->type : file_get_type($file);
637
    if ($file_type_name && ($file_type = file_type_load($file_type_name))) {
638
      $file_extensions = file_type_get_valid_extensions($file_type);
639
    }
640

    
641
    // Set allowed file extensions.
642
    if (!empty($file_extensions)) {
643
      // Set to type based file extensions.
644
      $replacement_options['file_extensions'] = implode(' ', $file_extensions);
645
    }
646
    else {
647
      // Fallback to the extension of the current file.
648
      $replacement_options['file_extensions'] = pathinfo($file->uri, PATHINFO_EXTENSION);
649
    }
650

    
651
    $form['replace_upload'] = array(
652
      '#type' => 'file',
653
      '#title' => t('Replace file'),
654
      '#description' => t('This file will replace the existing file. This action cannot be undone.'),
655
      '#upload_validators' => file_entity_get_upload_validators($replacement_options),
656
      '#pre_render' => array('file_entity_upload_validators_pre_render'),
657
    );
658
  }
659

    
660
  $form['preview'] = file_view_file($file, 'preview');
661

    
662
  $form['additional_settings'] = array(
663
    '#type' => 'vertical_tabs',
664
    '#weight' => 99,
665
  );
666

    
667
  // File destination information for administrators.
668
  $form['destination'] = array(
669
    '#type' => 'fieldset',
670
    '#access' => user_access('administer files') && file_entity_file_is_writeable($file),
671
    '#title' => t('Destination'),
672
    '#collapsible' => TRUE,
673
    '#collapsed' => TRUE,
674
    '#group' => 'additional_settings',
675
    '#attributes' => array(
676
      'class' => array('file-form-destination'),
677
    ),
678
    '#attached' => array(
679
      'js' => array(
680
        drupal_get_path('module', 'file_entity') . '/file_entity.js',
681
      ),
682
    ),
683
  );
684

    
685
  $options = array();
686
  foreach (file_get_stream_wrappers(STREAM_WRAPPERS_WRITE_VISIBLE) as $scheme => $info) {
687
    $options[$scheme] = check_plain($info['name']);
688
  }
689

    
690
  $form['destination']['scheme'] = array(
691
    '#type' => 'radios',
692
    '#title' => t('Destination'),
693
    '#options' => $options,
694
    '#default_value' => file_uri_scheme($file->uri),
695
  );
696

    
697
  // File user information for administrators.
698
  $form['user'] = array(
699
    '#type' => 'fieldset',
700
    '#access' => user_access('administer files'),
701
    '#title' => t('User information'),
702
    '#collapsible' => TRUE,
703
    '#collapsed' => TRUE,
704
    '#group' => 'additional_settings',
705
    '#attributes' => array(
706
      'class' => array('file-form-user'),
707
    ),
708
    '#attached' => array(
709
      'js' => array(
710
        drupal_get_path('module', 'file_entity') . '/file_entity.js',
711
        array(
712
          'type' => 'setting',
713
          'data' => array('anonymous' => variable_get('anonymous', t('Anonymous'))),
714
        ),
715
      ),
716
    ),
717
    '#weight' => 90,
718
  );
719
  $form['user']['name'] = array(
720
    '#type' => 'textfield',
721
    '#title' => t('Associated with'),
722
    '#maxlength' => 60,
723
    '#autocomplete_path' => 'user/autocomplete',
724
    '#default_value' => !empty($file->uid) ? user_load($file->uid)->name : '',
725
    '#weight' => -1,
726
    '#description' => t('Leave blank for %anonymous.', array('%anonymous' => variable_get('anonymous', t('Anonymous')))),
727
  );
728

    
729
  // Add the buttons.
730
  $form['actions'] = array('#type' => 'actions');
731
  $form['actions']['submit'] = array(
732
    '#type' => 'submit',
733
    '#value' => t('Save'),
734
    '#weight' => 5,
735
    '#submit' => array('file_entity_edit_submit'),
736
    '#validate' => array('file_entity_edit_validate'),
737
  );
738
  $form['actions']['delete'] = array(
739
    '#type' => 'submit',
740
    '#value' => t('Delete'),
741
    '#weight' => 10,
742
    '#submit' => array('file_entity_edit_delete_submit'),
743
    '#access' => file_entity_access('delete', $file),
744
  );
745

    
746
  // Build the URL for the cancel button taking into account that there might be
747
  // a "destination" that includes query string variables.
748
  $parameters = drupal_get_query_parameters();
749
  $destination = isset($parameters['destination']) ? $parameters['destination'] : 'file/' . $file->fid;
750
  $url = drupal_parse_url($destination);
751

    
752
  $form['actions']['cancel'] = array(
753
    '#type' => 'link',
754
    '#title' => t('Cancel'),
755
    '#href' => $url['path'],
756
    '#options' => array('query' => $url['query']),
757
    '#weight' => 15,
758
  );
759

    
760
  $langcode = function_exists('entity_language') ? entity_language('file', $file) : NULL;
761
  field_attach_form('file', $file, $form, $form_state, $langcode);
762

    
763
  return $form;
764
}
765

    
766
/**
767
 * Form validation handler for file_entity_edit().
768
 */
769
function file_entity_edit_validate($form, &$form_state) {
770
  $file = (object) $form_state['values'];
771

    
772
  // Validate the "associated user" field.
773
  if (!empty($file->name) && !($account = user_load_by_name($file->name))) {
774
    // The use of empty() is mandatory in the context of usernames
775
    // as the empty string denotes the anonymous user. In case we
776
    // are dealing with an anonymous user we set the user ID to 0.
777
    form_set_error('name', t('The username %name does not exist.', array('%name' => $file->name)));
778
  }
779

    
780
  // Handle the replacement file if uploaded.
781
  if (isset($form_state['values']['replace_upload'])) {
782
    // Save the file as a temporary file.
783
    $file = file_save_upload('replace_upload', $form['replace_upload']['#upload_validators']);
784
    if (!empty($file)) {
785
      // Put the temporary file in form_values so we can save it on submit.
786
      $form_state['values']['replace_upload'] = $file;
787
    }
788
    elseif ($file === FALSE) {
789
      // File uploaded failed.
790
      form_set_error('replace_upload', t('The replacement file could not be uploaded.'));
791
    }
792
  }
793

    
794
  // Run entity form validation.
795
  entity_form_field_validate('file', $form, $form_state);
796
}
797

    
798
/**
799
 * Form submission handler for the 'Save' button for file_entity_edit().
800
 */
801
function file_entity_edit_submit($form, &$form_state) {
802
  $file = $form_state['file'];
803
  $orphaned_uri = '';
804

    
805
  // Check if a replacement file has been uploaded.
806
  if (!empty($form_state['values']['replace_upload'])) {
807
    $replacement = $form_state['values']['replace_upload'];
808
    // Move file from temp to permanent home.
809
    if (pathinfo($replacement->uri, PATHINFO_EXTENSION) == pathinfo($file->uri, PATHINFO_EXTENSION)) {
810
        file_unmanaged_copy($replacement->uri, $file->uri, FILE_EXISTS_REPLACE);
811
    } else {
812
      $destination_uri = rtrim($file->uri, drupal_basename($file->uri)) . drupal_basename($replacement->uri);
813
      $replace_mode = $destination_uri == $file->uri ? FILE_EXISTS_REPLACE : FILE_EXISTS_RENAME;
814
      if ($new_file_uri = file_unmanaged_copy($replacement->uri, $destination_uri, $replace_mode)) {
815
        // @todo Add watchdog() about replaced file here?
816

    
817
        // Remove temporary file.
818
        file_delete($replacement);
819

    
820
        // Update if the uri target has changed.
821
        if ($new_file_uri != $file->uri) {
822
         // Store the original file uri to delete if save is successful.
823
         $orphaned_uri = $file->uri;
824

    
825
          // Update file entity uri.
826
          $file->uri = $new_file_uri;
827
        }
828
      }
829
    }
830
  }
831

    
832
  // Run entity form submit handling and save the file.
833
  entity_form_submit_build_entity('file', $file, $form, $form_state);
834

    
835
  // A user might assign the associated user by entering a user name in the file
836
  // edit form, which we then need to translate to a user ID.
837
  if (isset($file->name)) {
838
    // The use of isset() is mandatory in the context of user IDs, because
839
    // user ID 0 denotes the anonymous user.
840
    if ($user = user_load_by_name($file->name)) {
841
      $file->uid = $user->uid;
842
    }
843
    else {
844
      // Anonymous user.
845
      $file->uid = 0;
846
    }
847
  }
848
  elseif ($file->uid) {
849
    $user = user_load($file->uid);
850
    $file->name = $user->name;
851
  }
852

    
853
  if (file_uri_scheme($file->uri) != $form_state['values']['scheme']) {
854
    $file_destination = $form_state['values']['scheme'] . '://' . file_uri_target($file->uri);
855
    $file_destination = file_stream_wrapper_uri_normalize($file_destination);
856
    if ($moved_file = file_move($file, $file_destination, FILE_EXISTS_RENAME)) {
857
      // Only re-assign the file object if file_move() did not fail.
858
      $file = $moved_file;
859
    }
860
  }
861

    
862
  file_save($file);
863

    
864
  $args = array(
865
    '@type' => file_entity_type_get_name($file),
866
    '%title' => entity_label('file', $file),
867
  );
868
  watchdog('file', '@type: updated %title.', $args);
869
  drupal_set_message(t('@type %title has been updated.', $args));
870

    
871
  // Clean up orphaned file.
872
  if (!empty($orphaned_uri)) {
873
    file_unmanaged_delete($orphaned_uri);
874

    
875
    $args['@orphaned'] = file_uri_target($orphaned_uri);
876
    watchdog('file', '@type: deleted orphaned file @orphaned for %title.', $args);
877
    drupal_set_message(t('The replaced @type @orphaned has been deleted.', $args));
878
  }
879

    
880
  $form_state['redirect'] = 'file/' . $file->fid;
881

    
882
  // Clear the page and block caches.
883
  cache_clear_all();
884
}
885

    
886
/**
887
 * Form submission handler for the 'Delete' button for file_entity_edit().
888
 */
889
function file_entity_edit_delete_submit($form, &$form_state) {
890
  $fid = $form_state['values']['fid'];
891
  $destination = array();
892
  if (isset($_GET['destination'])) {
893
    $destination = drupal_get_destination();
894
    unset($_GET['destination']);
895
  }
896
  $form_state['redirect'] = array('file/' . $fid . '/delete', array('query' => $destination));
897

    
898
  // Clear the page and block caches.
899
  cache_clear_all();
900
}
901

    
902
/**
903
 * Page callback: Form constructor for the file deletion confirmation form.
904
 *
905
 * Path: file/%file/delete
906
 *
907
 * @param object $file
908
 *   A file object from file_load().
909
 *
910
 * @see file_entity_menu()
911
 */
912
function file_entity_delete_form($form, &$form_state, $file) {
913
  $form_state['file'] = $file;
914

    
915
  $form['fid'] = array(
916
    '#type' => 'value',
917
    '#value' => $file->fid,
918
  );
919

    
920
  $description = t('This action cannot be undone.');
921
  if ($references = file_usage_list($file)) {
922
    $description .= ' ' . t('This file is currently in use and may cause problems if deleted.');
923
  }
924

    
925
  return confirm_form($form,
926
    t('Are you sure you want to delete the file %title?', array(
927
      '%title' => entity_label('file', $file),
928
    )),
929
    'file/' . $file->fid,
930
    $description,
931
    t('Delete')
932
  );
933
}
934

    
935
/**
936
 * Form submission handler for file_entity_delete_form().
937
 */
938
function file_entity_delete_form_submit($form, &$form_state) {
939
  if ($form_state['values']['confirm'] && $file = file_load($form_state['values']['fid'])) {
940
    // Use file_delete_multiple() rather than file_delete() since we want to
941
    // avoid unwanted validation and usage checking.
942
    file_delete_multiple(array($file->fid));
943

    
944
    $args = array(
945
      '@type' => file_entity_type_get_name($file),
946
      '%title' => entity_label('file', $file),
947
    );
948
    watchdog('file', '@type: deleted %title.', $args);
949
    drupal_set_message(t('@type %title has been deleted.', $args));
950
  }
951

    
952
  $form_state['redirect'] = '<front>';
953

    
954
  // Clear the page and block caches.
955
  cache_clear_all();
956
}
957

    
958
/**
959
 * Form constructor for file deletion confirmation form.
960
 *
961
 * @param array $files
962
 *   An array of file objects.
963
 */
964
function file_entity_multiple_delete_form($form, &$form_state, array $files) {
965
  $form['files'] = array(
966
    '#prefix' => '<ul>',
967
    '#suffix' => '</ul>',
968
    '#tree' => TRUE,
969
  );
970

    
971
  $files_have_usage = FALSE;
972
  foreach ($files as $fid => $file) {
973
    $title = entity_label('file', $file);
974
    $usage = file_usage_list($file);
975
    if (!empty($usage)) {
976
      $files_have_usage = TRUE;
977
      $title = t('@title (in use)', array('@title' => $title));
978
    }
979
    else {
980
      $title = check_plain($title);
981
    }
982
    $form['files'][$fid] = array(
983
      '#type' => 'hidden',
984
      '#value' => $fid,
985
      '#prefix' => '<li>',
986
      '#suffix' => $title . "</li>\n",
987
    );
988
  }
989

    
990
  $form['operation'] = array(
991
    '#type' => 'hidden',
992
    '#value' => 'delete',
993
  );
994

    
995
  $description = t('This action cannot be undone.');
996
  if ($files_have_usage) {
997
    $description .= ' ' . t('Some of the files are currently in use and may cause problems if deleted.');
998
  }
999

    
1000
  return confirm_form(
1001
    $form,
1002
    format_plural(count($files), 'Are you sure you want to delete this file?', 'Are you sure you want to delete these files?'),
1003
    'admin/content/file',
1004
    $description,
1005
    t('Delete')
1006
  );
1007
}
1008

    
1009
/**
1010
 * Form submission handler for file_entity_multiple_delete_form().
1011
 */
1012
function file_entity_multiple_delete_form_submit($form, &$form_state) {
1013
  if ($form_state['values']['confirm'] && $fids = array_keys($form_state['values']['files'])) {
1014
    file_delete_multiple($fids);
1015
    $count = count($fids);
1016
    watchdog('file', 'Deleted @count files.', array('@count' => $count));
1017
    drupal_set_message(format_plural($count, 'Deleted one file.', 'Deleted @count files.'));
1018
  }
1019
  $form_state['redirect'] = 'admin/content/file';
1020

    
1021
  // Clear the page and block caches.
1022
  cache_clear_all();
1023
}
1024

    
1025
/**
1026
 * Page callback for the file edit form.
1027
 *
1028
 * @deprecated
1029
 *   Use drupal_get_form('file_entity_edit')
1030
 */
1031
function file_entity_page_edit($file) {
1032
  return drupal_get_form('file_entity_edit', $file);
1033
}
1034

    
1035
/**
1036
 * Page callback for the file deletion confirmation form.
1037
 *
1038
 * @deprecated
1039
 *   Use drupal_get_form('file_entity_delete_form')
1040
 */
1041
function file_entity_page_delete($file) {
1042
  return drupal_get_form('file_entity_delete_form');
1043
}
1044

    
1045
/**
1046
 * Retrieves the upload validators for a file.
1047
 *
1048
 * @param array $options
1049
 *   (optional) An array of options for file validation.
1050
 *
1051
 * @return array
1052
 *   An array suitable for passing to file_save_upload() or for a managed_file
1053
 *   or upload element's '#upload_validators' property.
1054
 */
1055
function file_entity_get_upload_validators(array $options = array()) {
1056
  // Set up file upload validators.
1057
  $validators = array();
1058

    
1059
  // Validate file extensions. If there are no file extensions in $params and
1060
  // there are no Media defaults, there is no file extension validation.
1061
  if (!empty($options['file_extensions'])) {
1062
    $validators['file_validate_extensions'] = array($options['file_extensions']);
1063
  }
1064
  else {
1065
    $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'));
1066
  }
1067

    
1068
  // Cap the upload size according to the system or user defined limit.
1069
  $max_filesize = parse_size(file_upload_max_size());
1070
  $file_entity_max_filesize = parse_size(variable_get('file_entity_max_filesize', ''));
1071

    
1072
  // If the user defined a size limit, use the smaller of the two.
1073
  if (!empty($file_entity_max_filesize)) {
1074
    $max_filesize = min($max_filesize, $file_entity_max_filesize);
1075
  }
1076

    
1077
  if (!empty($options['max_filesize']) && $options['max_filesize'] < $max_filesize) {
1078
    $max_filesize = parse_size($options['max_filesize']);
1079
  }
1080

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

    
1084
  // Add image validators.
1085
  $options += array('min_resolution' => 0, 'max_resolution' => 0);
1086
  if ($options['min_resolution'] || $options['max_resolution']) {
1087
    $validators['file_validate_image_resolution'] = array($options['max_resolution'], $options['min_resolution']);
1088
  }
1089

    
1090
  // Add other custom upload validators from options.
1091
  if (!empty($options['upload_validators'])) {
1092
    $validators += $options['upload_validators'];
1093
  }
1094

    
1095
  return $validators;
1096
}
1097

    
1098
function file_entity_upload_archive_form($form, &$form_state) {
1099
  $options = array(
1100
    'file_extensions' => archiver_get_extensions(),
1101
  );
1102

    
1103
  $form['upload'] = array(
1104
    '#type' => 'managed_file',
1105
    '#title' => t('Upload an archive file'),
1106
    '#upload_location' => NULL, // Upload to the temporary directory.
1107
    '#upload_validators' => file_entity_get_upload_validators($options),
1108
    '#progress_indicator' => 'bar',
1109
    '#required' => TRUE,
1110
    '#pre_render' => array('file_managed_file_pre_render', 'file_entity_upload_validators_pre_render'),
1111
  );
1112

    
1113
  $form['pattern'] = array(
1114
    '#type' => 'textfield',
1115
    '#title' => t('Pattern'),
1116
    '#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.'),
1117
    '#default_value' => '.*',
1118
    '#required' => TRUE,
1119
  );
1120

    
1121
  $form['actions'] = array('#type' => 'actions');
1122
  $form['actions']['submit'] = array(
1123
    '#type' => 'submit',
1124
    '#value' => t('Submit'),
1125
  );
1126

    
1127
  form_load_include($form_state, 'inc', 'file_entity', 'file_entity.pages');
1128

    
1129
  return $form;
1130
}
1131

    
1132
/**
1133
 * Upload a file.
1134
 */
1135
function file_entity_upload_archive_form_submit($form, &$form_state) {
1136
  $form_state['files'] = array();
1137

    
1138
  if ($archive = file_load($form_state['values']['upload'])) {
1139
    if ($archiver = archiver_get_archiver($archive->uri)) {
1140
      $files = $archiver->listContents();
1141

    
1142
      $extract_dir = file_default_scheme() . '://' . pathinfo($archive->filename, PATHINFO_FILENAME);
1143
      $extract_dir = file_destination($extract_dir, FILE_EXISTS_RENAME);
1144
      if (!file_prepare_directory($extract_dir, FILE_MODIFY_PERMISSIONS | FILE_CREATE_DIRECTORY)) {
1145
        throw new Exception(t('Unable to prepar, e directory %dir for extraction.', array('%dir' => $extract_dir)));
1146
      }
1147

    
1148
      $archiver->extract($extract_dir);
1149
      $pattern = '/' . $form_state['values']['pattern'] . '/';
1150
      if ($files = file_scan_directory($extract_dir, $pattern)) {
1151
        foreach ($files as $file) {
1152
          $file->status = FILE_STATUS_PERMANENT;
1153
          $file->uid = $archive->uid;
1154
          file_save($file);
1155
          $form_state['files'][$file->fid] = $file;
1156
        }
1157
      }
1158
      drupal_set_message(t('Extracted %file and added @count new files.', array('%file' => $archive->filename, '@count' => count($files))));
1159
    }
1160
    else {
1161
      throw new Exception(t('Cannot extract %file, not a valid archive.', array('%file' => $archive->uri)));
1162
    }
1163
  }
1164

    
1165
  // Redirect to the file edit page.
1166
  if (file_entity_access('edit') && module_exists('multiform')) {
1167
    $destination = array('destination' => 'admin/content/file');
1168
    if (isset($_GET['destination'])) {
1169
      $destination = drupal_get_destination();
1170
      unset($_GET['destination']);
1171
    }
1172
    $form_state['redirect'] = array('admin/content/file/edit-multiple/' . implode(' ', array_keys($form_state['files'])), array('query' => $destination));
1173
  }
1174
  else {
1175
    $form_state['redirect'] = 'admin/content/file';
1176
  }
1177
}