Projet

Général

Profil

Paste
Télécharger (38,6 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / file_entity / file_entity.pages.inc @ 87dbc3bf

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, $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
  foreach (file_usage_list($file) as $module => $usage) {
249
    $info = system_get_info('module', $module);
250

    
251
    // There are cases where the actual entity doesn't exist.
252
    // We have to handle this.
253
    foreach ($usage as $entity_type => $entity_ids) {
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 some other module already added this entity just sum all counts.
259
        if (isset($occured_entities[$entity_type][$entity_id])) {
260
          $rows[$occured_entities[$entity_type][$entity_id]][2] += $count;
261
          continue;
262
        }
263

    
264
        $label = empty($entities[$entity_id]) ? $module : entity_label($entity_type, $entities[$entity_id]);
265
        $entity_uri = empty($entities[$entity_id]) ? NULL : entity_uri($entity_type, $entities[$entity_id]);
266

    
267
        // Some entities do not have URL.
268
        if (empty($entity_uri)) {
269
          $rows[] = array(check_plain($label), $entity_type, $module, $count);
270
        }
271
        else {
272
          $uri = $entity_uri['path'];
273
          $rows[] = array(l($label, $uri), $entity_type, $module, $count);
274
        }
275

    
276
        $occured_entities[$entity_type][$entity_id] = count($rows) - 1;
277
      }
278
    }
279
  }
280
  $header[] = array(
281
    'data' => t('Entity'),
282
  );
283
  $header[] = array(
284
    'data' => t('Entity type'),
285
  );
286
  $header[] = array(
287
    'data' => t('Registering module'),
288
  );
289
  $header[] = array(
290
    'data' => t('Use count'),
291
  );
292
  $build['usage_table'] = array(
293
    '#theme' => 'table',
294
    '#header' => $header,
295
    '#rows' => $rows,
296
    '#caption' => t('This table lists all of the places where @filename is used.',
297
    array('@filename' => $file->filename)),
298
    '#empty' => t('This file is not currently used.'),
299
  );
300
  return $build;
301
}
302

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
457
    // Figure out destination.
458
    if (isset($_GET['destination'])) {
459
      $destination = drupal_get_destination();
460
      unset($_GET['destination']);
461
    }
462
    elseif (user_access('administer files')) {
463
      $destination = array('destination' => 'admin/content/file');
464
    }
465
    else {
466
      $destination = array('destination' => 'file/' . $file->fid);
467
    }
468
    $form_state['redirect'] = $destination['destination'];
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 extentions 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
      $saved_files[] = $file;
562
      $form_state['files'][$file->fid] = $file;
563
    }
564
    else {
565
      // @todo: move this to element validate or something.
566
      form_set_error('pud', t('The specified file %name could not be uploaded.', array('%name' => $uploaded_file['name'])));
567
    }
568
  }
569

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
764
  return $form;
765
}
766

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

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

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

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

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

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

    
815
      // Remove temporary file.
816
      file_delete($replacement);
817

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

    
823
        // Update file entity uri.
824
        $file->uri = $new_file_uri;
825
      }
826
    }
827
  }
828

    
829
  // Run entity form submit handling and save the file.
830
  entity_form_submit_build_entity('file', $file, $form, $form_state);
831

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

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

    
857
  file_save($file);
858

    
859
  $args = array(
860
    '@type' => file_entity_type_get_name($file),
861
    '%title' => entity_label('file', $file),
862
  );
863
  watchdog('file', '@type: updated %title.', $args);
864
  drupal_set_message(t('@type %title has been updated.', $args));
865

    
866
  // Clean up orphaned file.
867
  if (!empty($orphaned_uri)) {
868
    file_unmanaged_delete($orphaned_uri);
869

    
870
    $args['@orphaned'] = file_uri_target($orphaned_uri);
871
    watchdog('file', '@type: deleted orphaned file @orphaned for %title.', $args);
872
    drupal_set_message(t('The replaced @type @orphaned has been deleted.', $args));
873
  }
874

    
875
  $form_state['redirect'] = 'file/' . $file->fid;
876

    
877
  // Clear the page and block caches.
878
  cache_clear_all();
879
}
880

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

    
893
  // Clear the page and block caches.
894
  cache_clear_all();
895
}
896

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

    
910
  $form['fid'] = array(
911
    '#type' => 'value',
912
    '#value' => $file->fid,
913
  );
914

    
915
  $description = t('This action cannot be undone.');
916
  if ($references = file_usage_list($file)) {
917
    $description .= ' ' . t('This file is currently in use and may cause problems if deleted.');
918
  }
919

    
920
  return confirm_form($form,
921
    t('Are you sure you want to delete the file %title?', array(
922
      '%title' => entity_label('file', $file),
923
    )),
924
    'file/' . $file->fid,
925
    $description,
926
    t('Delete')
927
  );
928
}
929

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

    
939
    $args = array(
940
      '@type' => file_entity_type_get_name($file),
941
      '%title' => entity_label('file', $file),
942
    );
943
    watchdog('file', '@type: deleted %title.', $args);
944
    drupal_set_message(t('@type %title has been deleted.', $args));
945
  }
946

    
947
  $form_state['redirect'] = '<front>';
948

    
949
  // Clear the page and block caches.
950
  cache_clear_all();
951
}
952

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

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

    
985
  $form['operation'] = array(
986
    '#type' => 'hidden',
987
    '#value' => 'delete',
988
  );
989

    
990
  $description = t('This action cannot be undone.');
991
  if ($files_have_usage) {
992
    $description .= ' ' . t('Some of the files are currently in use and may cause problems if deleted.');
993
  }
994

    
995
  return confirm_form(
996
    $form,
997
    format_plural(count($files), 'Are you sure you want to delete this file?', 'Are you sure you want to delete these files?'),
998
    'admin/content/file',
999
    $description,
1000
    t('Delete')
1001
  );
1002
}
1003

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

    
1016
  // Clear the page and block caches.
1017
  cache_clear_all();
1018
}
1019

    
1020
/**
1021
 * Page callback for the file edit form.
1022
 *
1023
 * @deprecated
1024
 *   Use drupal_get_form('file_entity_edit')
1025
 */
1026
function file_entity_page_edit($file) {
1027
  return drupal_get_form('file_entity_edit', $file);
1028
}
1029

    
1030
/**
1031
 * Page callback for the file deletion confirmation form.
1032
 *
1033
 * @deprecated
1034
 *   Use drupal_get_form('file_entity_delete_form')
1035
 */
1036
function file_entity_page_delete($file) {
1037
  return drupal_get_form('file_entity_delete_form');
1038
}
1039

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

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

    
1063
  // Cap the upload size according to the system or user defined limit.
1064
  $max_filesize = parse_size(file_upload_max_size());
1065
  $file_entity_max_filesize = parse_size(variable_get('file_entity_max_filesize', ''));
1066

    
1067
  // If the user defined a size limit, use the smaller of the two.
1068
  if (!empty($file_entity_max_filesize)) {
1069
    $max_filesize = min($max_filesize, $file_entity_max_filesize);
1070
  }
1071

    
1072
  if (!empty($options['max_filesize']) && $options['max_filesize'] < $max_filesize) {
1073
    $max_filesize = parse_size($options['max_filesize']);
1074
  }
1075

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

    
1079
  // Add image validators.
1080
  $options += array('min_resolution' => 0, 'max_resolution' => 0);
1081
  if ($options['min_resolution'] || $options['max_resolution']) {
1082
    $validators['file_validate_image_resolution'] = array($options['max_resolution'], $options['min_resolution']);
1083
  }
1084

    
1085
  // Add other custom upload validators from options.
1086
  if (!empty($options['upload_validators'])) {
1087
    $validators += $options['upload_validators'];
1088
  }
1089

    
1090
  return $validators;
1091
}
1092

    
1093
function file_entity_upload_archive_form($form, &$form_state) {
1094
  $options = array(
1095
    'file_extensions' => archiver_get_extensions(),
1096
  );
1097

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

    
1108
  $form['pattern'] = array(
1109
    '#type' => 'textfield',
1110
    '#title' => t('Pattern'),
1111
    '#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.'),
1112
    '#default_value' => '.*',
1113
    '#required' => TRUE,
1114
  );
1115

    
1116
  $form['actions'] = array('#type' => 'actions');
1117
  $form['actions']['submit'] = array(
1118
    '#type' => 'submit',
1119
    '#value' => t('Submit'),
1120
  );
1121

    
1122
  form_load_include($form_state, 'inc', 'file_entity', 'file_entity.pages');
1123

    
1124
  return $form;
1125
}
1126

    
1127
/**
1128
 * Upload a file.
1129
 */
1130
function file_entity_upload_archive_form_submit($form, &$form_state) {
1131
  $form_state['files'] = array();
1132

    
1133
  if ($archive = file_load($form_state['values']['upload'])) {
1134
    if ($archiver = archiver_get_archiver($archive->uri)) {
1135
      $files = $archiver->listContents();
1136

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

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

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