Project

General

Profile

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

root / drupal7 / sites / all / modules / file_entity / file_entity.pages.inc @ 1f142f4f

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

    
39
  $headers = array(
40
    'Content-Type' => mime_header_encode($file->filemime),
41
    'Content-Disposition' => 'attachment; filename="' . mime_header_encode(drupal_basename($file->uri)) . '"',
42
    'Content-Length' => $file->filesize,
43
    'Content-Transfer-Encoding' => 'binary',
44
    'Pragma' => 'no-cache',
45
    'Cache-Control' => 'must-revalidate, post-check=0, pre-check=0',
46
    'Expires' => '0',
47
  );
48

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

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

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

    
70
/**
71
 * Form callback for adding a file via an upload form.
72
 *
73
 * This is a multi step form which has 1-3 pages:
74
 * - Upload file
75
 * - Choose filetype
76
 *   If there is only one candidate (based on mimetype) we will skip this step.
77
 * - Edit fields
78
 *   Skip this step if there are no fields on this entity type.
79
 */
80
function file_entity_add_upload($form, &$form_state, array $options = array()) {
81
  $step = (isset($form_state['step']) && in_array($form_state['step'], array(1, 2, 3, 4))) ? $form_state['step'] : 1;
82
  $form['#step'] = $step;
83
  $form['#options'] = $options;
84
  switch ($step) {
85
    case 1:
86
      return file_entity_add_upload_step_upload($form, $form_state, $options);
87

    
88
    case 2:
89
      return file_entity_add_upload_step_filetype($form, $form_state, $options);
90

    
91
    case 3:
92
      return file_entity_add_upload_step_scheme($form, $form_state, $options);
93

    
94
    case 4:
95
      return file_entity_add_upload_step_fields($form, $form_state, $options);
96

    
97
  }
98
}
99

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

    
115
  $form['actions'] = array('#type' => 'actions');
116
  $form['actions']['next'] = array(
117
    '#type' => 'submit',
118
    '#value' => t('Next'),
119
  );
120

    
121
  form_load_include($form_state, 'inc', 'file_entity', 'file_entity.pages');
122

    
123
  return $form;
124
}
125

    
126
/**
127
 * Generate form fields for the second step in the add file wizard.
128
 */
129
function file_entity_add_upload_step_filetype($form, &$form_state, array $options = array()) {
130
  $file = file_load($form_state['storage']['upload']);
131

    
132
  $form['type'] = array(
133
    '#type' => 'radios',
134
    '#title' => t('File type'),
135
    '#options' => file_entity_get_filetype_candidates($file),
136
    '#default_value' => isset($form_state['storage']['type']) ? $form_state['storage']['type'] : NULL,
137
    '#required' => TRUE,
138
  );
139

    
140
  $form['actions'] = array('#type' => 'actions');
141
  $form['actions']['previous'] = array(
142
    '#type' => 'submit',
143
    '#value' => t('Previous'),
144
    '#limit_validation_errors' => array(),
145
    '#submit' => array('file_entity_add_upload_submit'),
146
  );
147
  $form['actions']['next'] = array(
148
    '#type' => 'submit',
149
    '#value' => t('Next'),
150
  );
151

    
152
  return $form;
153
}
154

    
155
/**
156
 * Generate form fields for the third step in the add file wizard.
157
 */
158
function file_entity_add_upload_step_scheme($form, &$form_state, array $options = array()) {
159
  $file = file_load($form_state['storage']['upload']);
160

    
161
  $schemes = array();
162
  foreach (file_get_stream_wrappers(STREAM_WRAPPERS_WRITE_VISIBLE) as $scheme => $info) {
163
    $schemes[$scheme] = check_plain($info['description']);
164
  }
165

    
166
  // Remove any schemes not found in the instance settings.
167
  if (!empty($options['schemes'])) {
168
    $schemes = array_intersect_key($schemes, array_flip($options['schemes']));
169
  }
170

    
171
  // Determine which scheme to use as the default value.
172
  if (isset($form_state['storage']['scheme'])) {
173
    $fallback_scheme = $form_state['storage']['scheme'];
174
  }
175
  elseif (!empty($options['uri_scheme'])) {
176
    $fallback_scheme = $options['uri_scheme'];
177
  }
178
  else {
179
    $fallback_scheme = file_default_scheme();
180
  }
181

    
182
  $form['scheme'] = array(
183
    '#type' => 'radios',
184
    '#title' => t('Destination'),
185
    '#options' => $schemes,
186
    '#default_value' => $fallback_scheme,
187
    '#required' => TRUE,
188
  );
189

    
190
  $form['actions'] = array('#type' => 'actions');
191
  $form['actions']['previous'] = array(
192
    '#type' => 'submit',
193
    '#value' => t('Previous'),
194
    '#limit_validation_errors' => array(),
195
    '#submit' => array('file_entity_add_upload_submit'),
196
  );
197
  $form['actions']['next'] = array(
198
    '#type' => 'submit',
199
    '#value' => t('Next'),
200
  );
201

    
202
  return $form;
203
}
204

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

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

    
223
  // Add fields.
224
  field_attach_form('file', $file, $form, $form_state);
225

    
226
  $form['actions'] = array('#type' => 'actions');
227
  $form['actions']['previous'] = array(
228
    '#type' => 'submit',
229
    '#value' => t('Previous'),
230
    '#limit_validation_errors' => array(),
231
    '#submit' => array('file_entity_add_upload_submit'),
232
  );
233
  $form['actions']['submit'] = array(
234
    '#type' => 'submit',
235
    '#value' => t('Save'),
236
  );
237

    
238
  return $form;
239
}
240

    
241
/**
242
 * Page callback to show file usage information.
243
 */
244
function file_entity_usage_page($file) {
245
  $rows = array();
246
  $occured_entities = array();
247

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

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

    
266
        // Retrieve the label and the URI of the entity.
267
        $label = empty($entities[$entity_id]) ? $module : entity_label($entity_type, $entities[$entity_id]);
268
        $entity_uri = empty($entities[$entity_id]) ? NULL : entity_uri($entity_type, $entities[$entity_id]);
269

    
270
        // Link the label to the URI when possible.
271
        if (empty($entity_uri)) {
272
          $entity = check_plain($label);
273
        }
274
        else {
275
          $entity = l($label, $entity_uri['path']);
276
        }
277

    
278
        $rows[] = array($entity, $entity_type, $count);
279

    
280
        // Record the occurrence of the entity to ensure that it isn't listed in
281
        // the table again.
282
        $occured_entities[$entity_type][$entity_id] = count($rows) - 1;
283
      }
284
    }
285
  }
286

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

    
296
  return $build;
297
}
298

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

    
323
/**
324
 * Submit handler for the add file form.
325
 */
326
function file_entity_add_upload_submit($form, &$form_state) {
327
  $form_state['storage'] = isset($form_state['storage']) ? $form_state['storage'] : array();
328
  $form_state['storage'] = array_merge($form_state['storage'], $form_state['values']);
329

    
330
  // This var is set to TRUE when we are ready to save the file.
331
  $save = FALSE;
332
  $trigger = $form_state['triggering_element']['#id'];
333

    
334
  $steps_to_check = array(2, 3);
335
  if ($trigger == 'edit-previous') {
336
    // If the previous button was hit,
337
    // the step checking order should be reversed 3, 2.
338
    $steps_to_check = array_reverse($steps_to_check);
339
  }
340

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

    
366
        // Remove any schemes not found in the instance settings.
367
        if (!empty($options['schemes'])) {
368
          $schemes = array_intersect_key($schemes, $options['schemes']);
369
        }
370

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

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

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

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

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

    
437
      // Change the file from temporary to permanent.
438
      $file->status = FILE_STATUS_PERMANENT;
439

    
440
      // Save the form fields.
441
      // Keep in mind that the values for the Field API fields must be in
442
      // $form_state['values'] and not in ['storage']. This is true as long as
443
      // the fields are on the last page of the multi step form.
444
      entity_form_submit_build_entity('file', $file, $form, $form_state);
445

    
446
      file_save($file);
447
      $form_state['file'] = $file;
448
      drupal_set_message(t('@type %name was uploaded.', array('@type' => file_entity_type_get_name($file), '%name' => $file->filename)));
449
    }
450
    else {
451
      drupal_set_message(t('An error occurred and no file was uploaded.'), 'error');
452
      return;
453
    }
454

    
455
    // Figure out destination.
456
    if (user_access('administer files')) {
457
      $path = 'admin/content/file';
458
    }
459
    else {
460
      $path = 'file/' . $file->fid;
461
    }
462
    $form_state['redirect'] = $path;
463
  }
464
  else {
465
    $form_state['rebuild'] = TRUE;
466
  }
467

    
468
  // Clear the page and block caches.
469
  cache_clear_all();
470
}
471

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

    
491
  $destination = trim($params['file_directory'], '/');
492

    
493
  // Replace tokens.
494
  $destination = token_replace($destination, $data);
495

    
496
  return $params['uri_scheme'] . '://' . $destination;
497
}
498

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

    
509
  $form['upload']['#type'] = 'plupload';
510

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

    
525
  $form['submit']['#value'] = t('Start upload');
526
  return $form;
527
}
528

    
529
/**
530
 * Submit handler for the multiple upload form.
531
 */
532
function file_entity_add_upload_multiple_submit($form, &$form_state) {
533
  $upload_location = !empty($form['upload']['#upload_location']) ?
534
    $form['upload']['#upload_location'] . '/' :
535
    variable_get('file_default_scheme', 'public') . '://';
536

    
537
  // Ensure writable destination directory for the files.
538
  file_prepare_directory($upload_location, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
539

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

    
551
      $file = file_uri_to_object($destination);
552
      $file->status = FILE_STATUS_PERMANENT;
553
      file_save($file);
554

    
555
      $form_state['files'][$file->fid] = $file;
556
    }
557
    else {
558
      // @todo: move this to element validate or something.
559
      form_set_error('pud', t('The specified file %name could not be uploaded.', array('%name' => $uploaded_file['name'])));
560
    }
561
  }
562

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

    
579
  // Clear the page and block caches.
580
  cache_clear_all();
581
}
582

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

    
598
  $form_state['file'] = $file;
599

    
600
  $form['#attributes']['class'][] = 'file-form';
601
  if (!empty($file->type)) {
602
    $form['#attributes']['class'][] = 'file-' . $file->type . '-form';
603
  }
604

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

    
614
  $form['filename'] = array(
615
    '#type' => 'textfield',
616
    '#title' => t('Name'),
617
    '#default_value' => $file->filename,
618
    '#required' => TRUE,
619
    '#maxlength' => 255,
620
    '#weight' => -10,
621
  );
622

    
623
  // Add a 'replace this file' upload field if the file is writeable.
624
  if (file_entity_file_is_writeable($file)) {
625
    // Set up replacement file validation.
626
    $replacement_options = array();
627

    
628
    // The replacement file must have an extension valid for the original type.
629
    $file_extensions = array();
630
    $file_type_name = isset($file->type) ? $file->type : file_get_type($file);
631
    if ($file_type_name && ($file_type = file_type_load($file_type_name))) {
632
      $file_extensions = file_type_get_valid_extensions($file_type);
633
    }
634

    
635
    // Set allowed file extensions.
636
    if (!empty($file_extensions)) {
637
      // Set to type based file extensions.
638
      $replacement_options['file_extensions'] = implode(' ', $file_extensions);
639
    }
640
    else {
641
      // Fallback to the extension of the current file.
642
      $replacement_options['file_extensions'] = pathinfo($file->uri, PATHINFO_EXTENSION);
643
    }
644

    
645
    $form['replace_upload'] = array(
646
      '#type' => 'file',
647
      '#title' => t('Replace file'),
648
      '#description' => t('This file will replace the existing file. This action cannot be undone.'),
649
      '#upload_validators' => file_entity_get_upload_validators($replacement_options),
650
      '#pre_render' => array('file_entity_upload_validators_pre_render'),
651
    );
652
  }
653

    
654
  $form['preview'] = file_view_file($file, 'preview');
655

    
656
  $form['additional_settings'] = array(
657
    '#type' => 'vertical_tabs',
658
    '#weight' => 99,
659
  );
660

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

    
679
  $options = array();
680
  foreach (file_get_stream_wrappers(STREAM_WRAPPERS_WRITE_VISIBLE) as $scheme => $info) {
681
    $options[$scheme] = check_plain($info['name']);
682
  }
683

    
684
  $form['destination']['scheme'] = array(
685
    '#type' => 'radios',
686
    '#title' => t('Destination'),
687
    '#options' => $options,
688
    '#default_value' => file_uri_scheme($file->uri),
689
  );
690

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

    
723
  // Add the buttons.
724
  $form['actions'] = array('#type' => 'actions');
725
  $form['actions']['submit'] = array(
726
    '#type' => 'submit',
727
    '#value' => t('Save'),
728
    '#weight' => 5,
729
    '#submit' => array('file_entity_edit_submit'),
730
    '#validate' => array('file_entity_edit_validate'),
731
  );
732
  $form['actions']['delete'] = array(
733
    '#type' => 'submit',
734
    '#value' => t('Delete'),
735
    '#weight' => 10,
736
    '#submit' => array('file_entity_edit_delete_submit'),
737
    '#access' => file_entity_access('delete', $file),
738
  );
739

    
740
  // Build the URL for the cancel button taking into account that there might be
741
  // a "destination" that includes query string variables.
742
  $parameters = drupal_get_query_parameters();
743
  $destination = isset($parameters['destination']) ? $parameters['destination'] : 'file/' . $file->fid;
744
  $url = drupal_parse_url($destination);
745

    
746
  $form['actions']['cancel'] = array(
747
    '#type' => 'link',
748
    '#title' => t('Cancel'),
749
    '#href' => $url['path'],
750
    '#options' => array('query' => $url['query']),
751
    '#weight' => 15,
752
  );
753

    
754
  $langcode = function_exists('entity_language') ? entity_language('file', $file) : NULL;
755
  field_attach_form('file', $file, $form, $form_state, $langcode);
756

    
757
  return $form;
758
}
759

    
760
/**
761
 * Form validation handler for file_entity_edit().
762
 */
763
function file_entity_edit_validate($form, &$form_state) {
764
  $file = (object) $form_state['values'];
765

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

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

    
788
  // Run entity form validation.
789
  entity_form_field_validate('file', $form, $form_state);
790
}
791

    
792
/**
793
 * Form submission handler for the 'Save' button for file_entity_edit().
794
 */
795
function file_entity_edit_submit($form, &$form_state) {
796
  $file = $form_state['file'];
797
  $orphaned_uri = '';
798

    
799
  // Check if a replacement file has been uploaded.
800
  if (!empty($form_state['values']['replace_upload'])) {
801
    $replacement = $form_state['values']['replace_upload'];
802
    // Move file from temp to permanent home.
803
    $destination_uri = rtrim($file->uri, drupal_basename($file->uri)) . drupal_basename($replacement->uri);
804
    $replace_mode = $destination_uri == $file->uri ? FILE_EXISTS_REPLACE : FILE_EXISTS_RENAME;
805
    if ($new_file_uri = file_unmanaged_copy($replacement->uri, $destination_uri, $replace_mode)) {
806
      // @todo Add watchdog() about replaced file here?
807

    
808
      // Remove temporary file.
809
      file_delete($replacement);
810

    
811
      // Update if the uri target has changed.
812
      if ($new_file_uri != $file->uri) {
813
       // Store the original file uri to delete if save is sucessful.
814
       $orphaned_uri = $file->uri;
815

    
816
        // Update file entity uri.
817
        $file->uri = $new_file_uri;
818
      }
819
    }
820
  }
821

    
822
  // Run entity form submit handling and save the file.
823
  entity_form_submit_build_entity('file', $file, $form, $form_state);
824

    
825
  // A user might assign the associated user by entering a user name in the file
826
  // edit form, which we then need to translate to a user ID.
827
  if (isset($file->name)) {
828
    // The use of isset() is mandatory in the context of user IDs, because
829
    // user ID 0 denotes the anonymous user.
830
    if ($user = user_load_by_name($file->name)) {
831
      $file->uid = $user->uid;
832
    }
833
    else {
834
      // Anonymous user.
835
      $file->uid = 0;
836
    }
837
  }
838
  elseif ($file->uid) {
839
    $user = user_load($file->uid);
840
    $file->name = $user->name;
841
  }
842

    
843
  if (file_uri_scheme($file->uri) != $form_state['values']['scheme']) {
844
    $file_destination = $form_state['storage']['scheme'] . '://' . file_uri_target($file->uri);
845
    $file_destination = file_stream_wrapper_uri_normalize($file_destination);
846
    if ($moved_file = file_move($file, $file_destination, FILE_EXISTS_RENAME)) {
847
      // Only re-assign the file object if file_move() did not fail.
848
      $file = $moved_file;
849
    }
850
  }
851

    
852
  file_save($file);
853

    
854
  $args = array(
855
    '@type' => file_entity_type_get_name($file),
856
    '%title' => entity_label('file', $file),
857
  );
858
  watchdog('file', '@type: updated %title.', $args);
859
  drupal_set_message(t('@type %title has been updated.', $args));
860

    
861
  // Clean up orphaned file.
862
  if (!empty($orphaned_uri)) {
863
    file_unmanaged_delete($orphaned_uri);
864

    
865
    $args['@orphaned'] = file_uri_target($orphaned_uri);
866
    watchdog('file', '@type: deleted orphaned file @orphaned for %title.', $args);
867
    drupal_set_message(t('The replaced @type @orphaned has been deleted.', $args));
868
  }
869

    
870
  $form_state['redirect'] = 'file/' . $file->fid;
871

    
872
  // Clear the page and block caches.
873
  cache_clear_all();
874
}
875

    
876
/**
877
 * Form submission handler for the 'Delete' button for file_entity_edit().
878
 */
879
function file_entity_edit_delete_submit($form, &$form_state) {
880
  $fid = $form_state['values']['fid'];
881
  $destination = array();
882
  if (isset($_GET['destination'])) {
883
    $destination = drupal_get_destination();
884
    unset($_GET['destination']);
885
  }
886
  $form_state['redirect'] = array('file/' . $fid . '/delete', array('query' => $destination));
887

    
888
  // Clear the page and block caches.
889
  cache_clear_all();
890
}
891

    
892
/**
893
 * Page callback: Form constructor for the file deletion confirmation form.
894
 *
895
 * Path: file/%file/delete
896
 *
897
 * @param object $file
898
 *   A file object from file_load().
899
 *
900
 * @see file_entity_menu()
901
 */
902
function file_entity_delete_form($form, &$form_state, $file) {
903
  $form_state['file'] = $file;
904

    
905
  $form['fid'] = array(
906
    '#type' => 'value',
907
    '#value' => $file->fid,
908
  );
909

    
910
  $description = t('This action cannot be undone.');
911
  if ($references = file_usage_list($file)) {
912
    $description .= ' ' . t('This file is currently in use and may cause problems if deleted.');
913
  }
914

    
915
  return confirm_form($form,
916
    t('Are you sure you want to delete the file %title?', array(
917
      '%title' => entity_label('file', $file),
918
    )),
919
    'file/' . $file->fid,
920
    $description,
921
    t('Delete')
922
  );
923
}
924

    
925
/**
926
 * Form submission handler for file_entity_delete_form().
927
 */
928
function file_entity_delete_form_submit($form, &$form_state) {
929
  if ($form_state['values']['confirm'] && $file = file_load($form_state['values']['fid'])) {
930
    // Use file_delete_multiple() rather than file_delete() since we want to
931
    // avoid unwanted validation and usage checking.
932
    file_delete_multiple(array($file->fid));
933

    
934
    $args = array(
935
      '@type' => file_entity_type_get_name($file),
936
      '%title' => entity_label('file', $file),
937
    );
938
    watchdog('file', '@type: deleted %title.', $args);
939
    drupal_set_message(t('@type %title has been deleted.', $args));
940
  }
941

    
942
  $form_state['redirect'] = '<front>';
943

    
944
  // Clear the page and block caches.
945
  cache_clear_all();
946
}
947

    
948
/**
949
 * Form constructor for file deletion confirmation form.
950
 *
951
 * @param array $files
952
 *   An array of file objects.
953
 */
954
function file_entity_multiple_delete_form($form, &$form_state, array $files) {
955
  $form['files'] = array(
956
    '#prefix' => '<ul>',
957
    '#suffix' => '</ul>',
958
    '#tree' => TRUE,
959
  );
960

    
961
  $files_have_usage = FALSE;
962
  foreach ($files as $fid => $file) {
963
    $title = entity_label('file', $file);
964
    $usage = file_usage_list($file);
965
    if (!empty($usage)) {
966
      $files_have_usage = TRUE;
967
      $title = t('@title (in use)', array('@title' => $title));
968
    }
969
    else {
970
      $title = check_plain($title);
971
    }
972
    $form['files'][$fid] = array(
973
      '#type' => 'hidden',
974
      '#value' => $fid,
975
      '#prefix' => '<li>',
976
      '#suffix' => $title . "</li>\n",
977
    );
978
  }
979

    
980
  $form['operation'] = array(
981
    '#type' => 'hidden',
982
    '#value' => 'delete',
983
  );
984

    
985
  $description = t('This action cannot be undone.');
986
  if ($files_have_usage) {
987
    $description .= ' ' . t('Some of the files are currently in use and may cause problems if deleted.');
988
  }
989

    
990
  return confirm_form(
991
    $form,
992
    format_plural(count($files), 'Are you sure you want to delete this file?', 'Are you sure you want to delete these files?'),
993
    'admin/content/file',
994
    $description,
995
    t('Delete')
996
  );
997
}
998

    
999
/**
1000
 * Form submission handler for file_entity_multiple_delete_form().
1001
 */
1002
function file_entity_multiple_delete_form_submit($form, &$form_state) {
1003
  if ($form_state['values']['confirm'] && $fids = array_keys($form_state['values']['files'])) {
1004
    file_delete_multiple($fids);
1005
    $count = count($fids);
1006
    watchdog('file', 'Deleted @count files.', array('@count' => $count));
1007
    drupal_set_message(format_plural($count, 'Deleted one file.', 'Deleted @count files.'));
1008
  }
1009
  $form_state['redirect'] = 'admin/content/file';
1010

    
1011
  // Clear the page and block caches.
1012
  cache_clear_all();
1013
}
1014

    
1015
/**
1016
 * Page callback for the file edit form.
1017
 *
1018
 * @deprecated
1019
 *   Use drupal_get_form('file_entity_edit')
1020
 */
1021
function file_entity_page_edit($file) {
1022
  return drupal_get_form('file_entity_edit', $file);
1023
}
1024

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

    
1035
/**
1036
 * Retrieves the upload validators for a file.
1037
 *
1038
 * @param array $options
1039
 *   (optional) An array of options for file validation.
1040
 *
1041
 * @return array
1042
 *   An array suitable for passing to file_save_upload() or for a managed_file
1043
 *   or upload element's '#upload_validators' property.
1044
 */
1045
function file_entity_get_upload_validators(array $options = array()) {
1046
  // Set up file upload validators.
1047
  $validators = array();
1048

    
1049
  // Validate file extensions. If there are no file extensions in $params and
1050
  // there are no Media defaults, there is no file extension validation.
1051
  if (!empty($options['file_extensions'])) {
1052
    $validators['file_validate_extensions'] = array($options['file_extensions']);
1053
  }
1054
  else {
1055
    $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'));
1056
  }
1057

    
1058
  // Cap the upload size according to the system or user defined limit.
1059
  $max_filesize = parse_size(file_upload_max_size());
1060
  $file_entity_max_filesize = parse_size(variable_get('file_entity_max_filesize', ''));
1061

    
1062
  // If the user defined a size limit, use the smaller of the two.
1063
  if (!empty($file_entity_max_filesize)) {
1064
    $max_filesize = min($max_filesize, $file_entity_max_filesize);
1065
  }
1066

    
1067
  if (!empty($options['max_filesize']) && $options['max_filesize'] < $max_filesize) {
1068
    $max_filesize = parse_size($options['max_filesize']);
1069
  }
1070

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

    
1074
  // Add image validators.
1075
  $options += array('min_resolution' => 0, 'max_resolution' => 0);
1076
  if ($options['min_resolution'] || $options['max_resolution']) {
1077
    $validators['file_validate_image_resolution'] = array($options['max_resolution'], $options['min_resolution']);
1078
  }
1079

    
1080
  // Add other custom upload validators from options.
1081
  if (!empty($options['upload_validators'])) {
1082
    $validators += $options['upload_validators'];
1083
  }
1084

    
1085
  return $validators;
1086
}
1087

    
1088
function file_entity_upload_archive_form($form, &$form_state) {
1089
  $options = array(
1090
    'file_extensions' => archiver_get_extensions(),
1091
  );
1092

    
1093
  $form['upload'] = array(
1094
    '#type' => 'managed_file',
1095
    '#title' => t('Upload an archive file'),
1096
    '#upload_location' => NULL, // Upload to the temporary directory.
1097
    '#upload_validators' => file_entity_get_upload_validators($options),
1098
    '#progress_indicator' => 'bar',
1099
    '#required' => TRUE,
1100
    '#pre_render' => array('file_managed_file_pre_render', 'file_entity_upload_validators_pre_render'),
1101
  );
1102

    
1103
  $form['pattern'] = array(
1104
    '#type' => 'textfield',
1105
    '#title' => t('Pattern'),
1106
    '#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.'),
1107
    '#default_value' => '.*',
1108
    '#required' => TRUE,
1109
  );
1110

    
1111
  $form['actions'] = array('#type' => 'actions');
1112
  $form['actions']['submit'] = array(
1113
    '#type' => 'submit',
1114
    '#value' => t('Submit'),
1115
  );
1116

    
1117
  form_load_include($form_state, 'inc', 'file_entity', 'file_entity.pages');
1118

    
1119
  return $form;
1120
}
1121

    
1122
/**
1123
 * Upload a file.
1124
 */
1125
function file_entity_upload_archive_form_submit($form, &$form_state) {
1126
  $form_state['files'] = array();
1127

    
1128
  if ($archive = file_load($form_state['values']['upload'])) {
1129
    if ($archiver = archiver_get_archiver($archive->uri)) {
1130
      $files = $archiver->listContents();
1131

    
1132
      $extract_dir = file_default_scheme() . '://' . pathinfo($archive->filename, PATHINFO_FILENAME);
1133
      $extract_dir = file_destination($extract_dir, FILE_EXISTS_RENAME);
1134
      if (!file_prepare_directory($extract_dir, FILE_MODIFY_PERMISSIONS | FILE_CREATE_DIRECTORY)) {
1135
        throw new Exception(t('Unable to prepar, e directory %dir for extraction.', array('%dir' => $extract_dir)));
1136
      }
1137

    
1138
      $archiver->extract($extract_dir);
1139
      $pattern = '/' . $form_state['values']['pattern'] . '/';
1140
      if ($files = file_scan_directory($extract_dir, $pattern)) {
1141
        foreach ($files as $file) {
1142
          $file->status = FILE_STATUS_PERMANENT;
1143
          $file->uid = $archive->uid;
1144
          file_save($file);
1145
          $form_state['files'][$file->fid] = $file;
1146
        }
1147
      }
1148
      drupal_set_message(t('Extracted %file and added @count new files.', array('%file' => $archive->filename, '@count' => count($files))));
1149
    }
1150
    else {
1151
      throw new Exception(t('Cannot extract %file, not a valid archive.', array('%file' => $archive->uri)));
1152
    }
1153
  }
1154

    
1155
  // Redirect to the file edit page.
1156
  if (file_entity_access('edit') && module_exists('multiform')) {
1157
    $destination = array('destination' => 'admin/content/file');
1158
    if (isset($_GET['destination'])) {
1159
      $destination = drupal_get_destination();
1160
      unset($_GET['destination']);
1161
    }
1162
    $form_state['redirect'] = array('admin/content/file/edit-multiple/' . implode(' ', array_keys($form_state['files'])), array('query' => $destination));
1163
  }
1164
  else {
1165
    $form_state['redirect'] = 'admin/content/file';
1166
  }
1167
}