Projet

Général

Profil

Paste
Télécharger (44,7 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / file_entity / file_entity.pages.inc @ 8e7483ab

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 Why basename? Why not drupal_basename which was working up until recently.
39
  $headers = array(
40
    'Content-Type' => mime_header_encode($file->filemime),
41
    'Content-Disposition' => 'attachment; filename="' . mime_header_encode(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 if (file_uri_scheme($file->uri) == 's3') {
60
    $url = file_create_url($file->uri);
61
    drupal_goto($url);
62
  }
63
  else {
64
    // For remote files, just redirect the user to that file's actual URL.
65
    $headers['Location'] = file_create_url($file->uri);
66
    foreach ($headers as $name => $value) {
67
      drupal_add_http_header($name, $value);
68
    }
69
    drupal_send_headers();
70
    drupal_exit();
71
  }
72
}
73

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

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

    
103
    case 2:
104
      return file_entity_add_upload_step_filetype($form, $form_state, $options);
105

    
106
    case 3:
107
      return file_entity_add_upload_step_scheme($form, $form_state, $options);
108

    
109
    case 4:
110
      return file_entity_add_upload_step_fields($form, $form_state, $options);
111

    
112
  }
113
}
114

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

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

    
136
  form_load_include($form_state, 'inc', 'file_entity', 'file_entity.pages');
137

    
138
  return $form;
139
}
140

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

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

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

    
168
  return $form;
169
}
170

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

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

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

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

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

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

    
218
  return $form;
219
}
220

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

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

    
239
  // Add fields.
240
  field_attach_form('file', $file, $form, $form_state);
241

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

    
254
  return $form;
255
}
256

    
257
/**
258
 * Page callback to show file usage information.
259
 */
260
function file_entity_usage_page($file) {
261
  drupal_set_title(t('<em>Usage of @type</em> @title', array('@type' => $file->type, '@title' => $file->filename)), PASS_THROUGH);
262

    
263
  $rows = array();
264
  $occured_entities = array();
265

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

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

    
284
        // Retrieve the label and the URI of the entity.
285
        $label = empty($entities[$entity_id]) ? $module : entity_label($entity_type, $entities[$entity_id]);
286
        $entity_uri = empty($entities[$entity_id]) ? NULL : entity_uri($entity_type, $entities[$entity_id]);
287

    
288
        // Link the label to the URI when possible.
289
        if (!empty($entity_uri['path']) && $entity_type != 'paragraphs_item') {
290
          $entity_label = l($label, $entity_uri['path']);
291
        }
292
        // For paragraphs items, we are searching for usages in nodes.
293
        elseif ($entity_type == 'paragraphs_item') {
294
          $paragraph_fields = field_read_fields(array('type' => 'paragraphs'));
295
          foreach ($paragraph_fields as $paragraph_field) {
296
            $field_name = $paragraph_field['field_name'];
297
            $query = new EntityFieldQuery();
298
            $query->entityCondition('entity_type', 'node')
299
              ->fieldCondition($field_name, 'value', $entity_id, '=');
300
            $nid = $query->execute();
301
            if (!empty($nid)) {
302
              $nid = implode(array_keys($nid['node']));
303
              if ($nid) {
304
                $node = node_load($nid);
305
                $entity_label = l($node->title, 'node/' . $nid);
306
              }
307
            }
308
          }
309
        }
310
        else {
311
          $entity_label = check_plain($label);
312
        }
313

    
314
        $rows[] = array($entity_label, $entity_type, $count);
315

    
316
        // Record the occurrence of the entity to ensure that it isn't listed in
317
        // the table again.
318
        $occured_entities[$entity_type][$entity_id] = count($rows) - 1;
319
      }
320
    }
321
  }
322

    
323
  $header = array(t('Entity'), t('Entity type'), t('Use count'));
324
  $build['usage_table'] = array(
325
    '#theme' => 'table',
326
    '#header' => $header,
327
    '#rows' => $rows,
328
    '#caption' => t('This table lists all of the places where @filename is used.', array('@filename' => $file->filename)),
329
    '#empty' => t('This file is not currently used.'),
330
  );
331

    
332
  return $build;
333
}
334

    
335
/**
336
 * Get the candidate filetypes for a given file.
337
 *
338
 * Only filetypes for which the user has access to create entities are returned.
339
 *
340
 * @param array $file
341
 *   An upload file array from form_state.
342
 *
343
 * @return array
344
 *   An array of file type bundles that support the file's mime type.
345
 */
346
function file_entity_get_filetype_candidates($file, $selected_files = array()) {
347
  $types = module_invoke_all('file_type', $file);
348
  drupal_alter('file_type', $types, $file);
349

    
350
  // If no file types are selected in field instance settings, allow all
351
  // available types.
352
  if (!empty($selected_files)) {
353
    // Limit file type candidates to field allowed types.
354
    $types = array_intersect($types, $selected_files);
355
  }
356

    
357
  $candidates = array();
358
  foreach ($types as $type) {
359
    $file->type = $type;
360
    if (file_entity_access('create', $file)) {
361
      $candidates[$type] = file_entity_type_get_name($file);
362
    }
363
  }
364
  return $candidates;
365
}
366

    
367
/**
368
 * Submit handler for the add file form.
369
 */
370
function file_entity_add_upload_submit($form, &$form_state) {
371
  $form_state['storage'] = isset($form_state['storage']) ? $form_state['storage'] : array();
372
  $form_state['storage'] = array_merge($form_state['storage'], $form_state['values']);
373

    
374
  // Field selected allowed types.
375
  $selected_files = $form['#options']['types'];
376

    
377
  // This var is set to TRUE when we are ready to save the file.
378
  $save = FALSE;
379
  $trigger = $form_state['triggering_element']['#id'];
380
  $triggered_next = $trigger == 'edit-next' || (strpos($trigger, 'edit-next--') === 0);
381
  $triggered_previous = $trigger == 'edit-previous' || (strpos($trigger, 'edit-previous--') === 0);
382
  $step_delta = ($triggered_previous) ? -1 : 1;
383

    
384
  $steps_to_check = array(2, 3);
385
  if ($triggered_previous) {
386
    // If the previous button was hit,
387
    // the step checking order should be reversed 3, 2.
388
    $steps_to_check = array_reverse($steps_to_check);
389
  }
390

    
391
  foreach ($steps_to_check as $step) {
392
    // Check if we can skip step 2 and 3.
393
    if (($form['#step'] == $step - 1 && $triggered_next) || ($form['#step'] == $step + 1 && $triggered_previous)) {
394
      $file = file_load($form_state['storage']['upload']);
395
      if ($step == 2) {
396
        // Check if we can skip step 2.
397
        $candidates = file_entity_get_filetype_candidates($file, $selected_files);
398
        if (count($candidates) == 1) {
399
          $candidates_keys = array_keys($candidates);
400
          // There is only one possible filetype for this file.
401
          // Skip the second page.
402
          $form['#step'] += $step_delta;
403
          $form_state['storage']['type'] = reset($candidates_keys);
404
        }
405
        elseif (!$candidates || variable_get('file_entity_file_upload_wizard_skip_file_type', FALSE)) {
406
          // Do not assign the file a file type.
407
          $form['#step'] += $step_delta;
408
          $form_state['storage']['type'] = FILE_TYPE_NONE;
409
        }
410
      }
411
      else {
412
        // Check if we can skip step 3.
413
        $options = $form['#options'];
414
        $schemes = file_get_stream_wrappers(STREAM_WRAPPERS_WRITE_VISIBLE);
415

    
416
        // Remove any schemes not found in the instance settings.
417
        if (!empty($options['schemes'])) {
418
          $schemes = array_intersect_key($schemes, $options['schemes']);
419
        }
420

    
421
        if (!file_entity_file_is_writeable($file)) {
422
          // The file is read-only (remote) and must use its provided scheme.
423
          $form['#step'] += $step_delta;
424
          $form_state['storage']['scheme'] = file_uri_scheme($file->uri);
425
        }
426
        elseif (count($schemes) == 1) {
427
          // There is only one possible stream wrapper for this file.
428
          // Skip the third page.
429
          $form['#step'] += $step_delta;
430
          $form_state['storage']['scheme'] = key($schemes);
431
        }
432
        elseif (variable_get('file_entity_file_upload_wizard_skip_scheme', FALSE)) {
433
          $form['#step'] += $step_delta;
434

    
435
          // Fallback to the URI scheme specified in the field settings
436
          // otherwise use the default file scheme.
437
          if (!empty($options['uri_scheme'])) {
438
            $form_state['storage']['scheme'] = $options['uri_scheme'];
439
          }
440
          else {
441
            $form_state['storage']['scheme'] = file_default_scheme();
442
          }
443
        }
444
      }
445
    }
446
  }
447

    
448
  // We have the filetype, check if we can skip step 4.
449
  if ($form['#step'] == 3 && $triggered_next) {
450
    $file = file_load($form_state['storage']['upload']);
451
    $form_state['file'] = $file;
452
    if (!field_info_instances('file', $form_state['storage']['type'])) {
453
      // This filetype doesn't have fields, save the file.
454
      $save = TRUE;
455
    }
456
    elseif (variable_get('file_entity_file_upload_wizard_skip_fields', FALSE)) {
457
      // Save the file with blanks fields.
458
      $save = TRUE;
459
    }
460
    // Allow other modules to choose to skip or complete step 4.
461
    drupal_alter('file_entity_file_upload_skip_fields', $save, $form_state);
462
  }
463

    
464
  // Form id's can vary depending on how many other forms are displayed, so we
465
  // need to do string comparissons. e.g edit-submit--2.
466
  if ($triggered_next) {
467
    $form_state['step'] = $form['#step'] + 1;
468
  }
469
  elseif ($triggered_previous) {
470
    $form_state['step'] = $form['#step'] - 1;
471
  }
472
  elseif (strpos($trigger, 'edit-submit') !== FALSE) {
473
    $save = TRUE;
474
  }
475

    
476
  if ($save) {
477
    $file = file_load($form_state['storage']['upload']);
478
    if ($file) {
479
      if (file_uri_scheme($file->uri) != $form_state['storage']['scheme']) {
480
        $file_destination = $form_state['storage']['scheme'] . '://' . file_uri_target($file->uri);
481
        $file_destination = file_stream_wrapper_uri_normalize($file_destination);
482
        if ($moved_file = file_move($file, $file_destination, FILE_EXISTS_RENAME)) {
483
          // Only re-assign the file object if file_move() did not fail.
484
          $file = $moved_file;
485
        }
486
      }
487
      $file->type = $form_state['storage']['type'];
488
      $file->display = TRUE;
489

    
490
      // Change the file from temporary to permanent.
491
      $file->status = FILE_STATUS_PERMANENT;
492

    
493
      // Save the form fields.
494
      // Keep in mind that the values for the Field API fields must be in
495
      // $form_state['values'] and not in ['storage']. This is true as long as
496
      // the fields are on the last page of the multi step form.
497
      entity_form_submit_build_entity('file', $file, $form, $form_state);
498

    
499
      file_save($file);
500
      $form_state['file'] = $file;
501
      drupal_set_message(t('@type %name was uploaded.', array('@type' => file_entity_type_get_name($file), '%name' => $file->filename)));
502
    }
503
    else {
504
      drupal_set_message(t('An error occurred and no file was uploaded.'), 'error');
505
      return;
506
    }
507

    
508
    // Figure out destination.
509
    if (user_access('administer files')) {
510
      $path = 'admin/content/file';
511
    }
512
    else {
513
      $path = 'file/' . $file->fid;
514
    }
515
    $form_state['redirect'] = $path;
516
  }
517
  else {
518
    $form_state['rebuild'] = TRUE;
519
  }
520

    
521
  // Clear the page and block caches.
522
  cache_clear_all();
523
}
524

    
525
/**
526
 * Determines the upload location for the file add upload form.
527
 *
528
 * @param array $params
529
 *   An array of parameters from the media browser.
530
 * @param array $data
531
 *   (optional) An array of token objects to pass to token_replace().
532
 *
533
 * @return string
534
 *   A file directory URI with tokens replaced.
535
 *
536
 * @see token_replace()
537
 */
538
function file_entity_upload_destination_uri(array $params, array $data = array()) {
539
  $params += array(
540
    'uri_scheme' => file_default_scheme(),
541
    'file_directory' => variable_get('file_entity_default_file_directory', ''),
542
  );
543

    
544
  $destination = trim($params['file_directory'], '/');
545

    
546
  // Replace tokens.
547
  $destination = decode_entities(token_replace($destination, $data));
548

    
549
  return $params['uri_scheme'] . '://' . $destination;
550
}
551

    
552
/**
553
 * Form for uploading multiple files.
554
 */
555
function file_entity_add_upload_multiple($form, &$form_state, $params = array()) {
556
  $form = file_entity_add_upload($form, $form_state, $params);
557
  unset($form['upload']['#title']);
558
  // The validators will be set from plupload anyway. This isn't pretty,
559
  // but don't allow it to show up twice.
560
  unset($form['upload']['#description']);
561

    
562
  $form['upload']['#type'] = 'plupload';
563

    
564
  // Ensure that we call the plupload_element_pre_render function.
565
  // If it isn't called, it doesn't set the JS settings that transfers the
566
  // list of allowed file extensions to the PLUpload widget.
567
  // We override the 'file_entity_upload_validators_pre_render' setting if it
568
  // exists, because both pre-render hooks adds the upload-help with list of
569
  // allowed file extensions.
570
  $index = array_search('file_entity_upload_validators_pre_render', $form['upload']['#pre_render']);
571
  if ($index !== FALSE) {
572
    $form['upload']['#pre_render'][$index] = 'plupload_element_pre_render';
573
  }
574
  else {
575
    $form['upload']['#pre_render'][] = 'plupload_element_pre_render';
576
  }
577

    
578
  $form['submit']['#value'] = t('Start upload');
579
  return $form;
580
}
581

    
582
/**
583
 * Submit handler for the multiple upload form.
584
 */
585
function file_entity_add_upload_multiple_submit($form, &$form_state) {
586
  $upload_location = !empty($form['upload']['#upload_location']) ?
587
    $form['upload']['#upload_location'] . '/' :
588
    variable_get('file_default_scheme', 'public') . '://';
589

    
590
  // Ensure writable destination directory for the files.
591
  file_prepare_directory($upload_location, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
592

    
593
  // We can't use file_save_upload() because of
594
  // http://www.jacobsingh.name/content/tight-coupling-no-not.
595
  foreach ($form_state['values']['upload'] as $uploaded_file) {
596
    if ($uploaded_file['status'] == 'done') {
597
      $source = $uploaded_file['tmppath'];
598
      $destination = file_stream_wrapper_uri_normalize($upload_location . $uploaded_file['name']);
599
      // Rename it to its original name, and put it in its final home.
600
      // Note - not using file_move here because if we call file_get_mime
601
      // (in file_uri_to_object) while it has a .tmp extension, it horks.
602
      $destination = file_unmanaged_move($source, $destination, FILE_EXISTS_RENAME);
603

    
604
      $file = file_uri_to_object($destination);
605
      $file->status = FILE_STATUS_PERMANENT;
606
      file_save($file);
607

    
608
      $form_state['files'][$file->fid] = $file;
609
    }
610
    else {
611
      // @todo: move this to element validate or something.
612
      form_set_error('pud', t('The specified file %name could not be uploaded.', array('%name' => $uploaded_file['name'])));
613
    }
614
  }
615

    
616
  // Redirect to the file edit page.
617
  if (file_entity_access('update', $file) && module_exists('media_bulk_upload')) {
618
    $destination = array();
619
    if (isset($_GET['destination'])) {
620
      $destination = drupal_get_destination();
621
      unset($_GET['destination']);
622
    }
623
    elseif (user_access('administer files')) {
624
      $destination = array('destination' => 'admin/content/file');
625
    }
626
    $form_state['redirect'] = array('admin/content/file/edit-multiple/' . implode(' ', array_keys($form_state['files'])), array('query' => $destination));
627
  }
628
  else {
629
    $form_state['redirect'] = user_access('administer files') ? 'admin/content/file' : '<front>';
630
  }
631

    
632
  // Clear the page and block caches.
633
  cache_clear_all();
634
}
635

    
636
/**
637
 * Page callback: Form constructor for the file edit form.
638
 *
639
 * Path: file/%file/edit
640
 *
641
 * @param object $file
642
 *   A file object from file_load().
643
 *
644
 * @see file_entity_menu()
645
 *
646
 * @todo Rename this form to file_edit_form to ease into core.
647
 */
648
function file_entity_edit($form, &$form_state, $file) {
649
  drupal_set_title(t('<em>Edit @type</em> @title', array('@type' => $file->type, '@title' => $file->filename)), PASS_THROUGH);
650

    
651
  $form_state['file'] = $file;
652

    
653
  $form['#attributes']['class'][] = 'file-form';
654
  if (!empty($file->type)) {
655
    $form['#attributes']['class'][] = 'file-' . $file->type . '-form';
656
  }
657

    
658
  // Basic file information.
659
  // These elements are just values so they are not even sent to the client.
660
  foreach (array('fid', 'type', 'uid', 'timestamp') as $key) {
661
    $form[$key] = array(
662
      '#type' => 'value',
663
      '#value' => isset($file->$key) ? $file->$key : NULL,
664
    );
665
  }
666

    
667
  $form['filename'] = array(
668
    '#type' => 'textfield',
669
    '#title' => t('Name'),
670
    '#default_value' => $file->filename,
671
    '#required' => TRUE,
672
    '#maxlength' => 255,
673
    '#weight' => -10,
674
  );
675

    
676
  // Add a 'replace this file' upload field if the file is writeable.
677
  if (file_entity_file_is_writeable($file)) {
678
    // Set up replacement file validation.
679
    $replacement_options = !empty($form_state['#upload_options']) ? $form_state['#upload_options'] : array();
680

    
681
    // The replacement file must have an extension valid for the original type.
682
    $file_extensions = array();
683
    $file_type_name = isset($file->type) ? $file->type : file_get_type($file);
684
    if (!empty($replacement_options['file_extensions'])) {
685
      $file_extensions = explode(' ', $replacement_options['file_extensions']);
686
    }
687
    elseif ($file_type_name && ($file_type = file_type_load($file_type_name))) {
688
      $file_extensions = file_type_get_valid_extensions($file_type);
689
    }
690

    
691
    // Set allowed file extensions.
692
    if (!empty($file_extensions)) {
693
      // Set to type based file extensions.
694
      $replacement_options['file_extensions'] = implode(' ', $file_extensions);
695
    }
696
    else {
697
      // Fallback to the extension of the current file.
698
      $replacement_options['file_extensions'] = pathinfo($file->uri, PATHINFO_EXTENSION);
699
    }
700

    
701
    $form['replace_upload'] = array(
702
      '#type' => 'file',
703
      '#title' => t('Replace file'),
704
      '#description' => t('This file will replace the existing file. This action cannot be undone.'),
705
      '#upload_validators' => file_entity_get_upload_validators($replacement_options),
706
      '#pre_render' => array('file_entity_upload_validators_pre_render'),
707
    );
708
    $form['replace_keep_original_filename'] = array(
709
      '#type' => 'checkbox',
710
      '#title' => t('Keep original filename'),
711
      '#default_value' => variable_get('file_entity_file_replace_options_keep_original_filename', FALSE),
712
      '#description' => t('Rename the newly uploaded file to the name of the original file. This action cannot be undone.'),
713
    );
714
  }
715

    
716
  $form['preview'] = file_view_file($file, 'preview');
717

    
718
  $form['additional_settings'] = array(
719
    '#type' => 'vertical_tabs',
720
    '#weight' => 99,
721
  );
722

    
723
  // File destination information for administrators.
724
  $form['destination'] = array(
725
    '#type' => 'fieldset',
726
    '#access' => user_access('administer files') && file_entity_file_is_writeable($file),
727
    '#title' => t('Destination'),
728
    '#collapsible' => TRUE,
729
    '#collapsed' => TRUE,
730
    '#group' => 'additional_settings',
731
    '#attributes' => array(
732
      'class' => array('file-form-destination'),
733
    ),
734
    '#attached' => array(
735
      'js' => array(
736
        drupal_get_path('module', 'file_entity') . '/file_entity.js',
737
      ),
738
    ),
739
  );
740

    
741
  $options = array();
742
  foreach (file_get_stream_wrappers(STREAM_WRAPPERS_WRITE_VISIBLE) as $scheme => $info) {
743
    $options[$scheme] = check_plain($info['name']);
744
  }
745

    
746
  $form['destination']['scheme'] = array(
747
    '#type' => 'radios',
748
    '#title' => t('Destination'),
749
    '#options' => $options,
750
    '#default_value' => file_uri_scheme($file->uri),
751
  );
752

    
753
  // File user information for administrators.
754
  $form['user'] = array(
755
    '#type' => 'fieldset',
756
    '#access' => user_access('administer files'),
757
    '#title' => t('User information'),
758
    '#collapsible' => TRUE,
759
    '#collapsed' => TRUE,
760
    '#group' => 'additional_settings',
761
    '#attributes' => array(
762
      'class' => array('file-form-user'),
763
    ),
764
    '#attached' => array(
765
      'js' => array(
766
        drupal_get_path('module', 'file_entity') . '/file_entity.js',
767
        array(
768
          'type' => 'setting',
769
          'data' => array('anonymous' => variable_get('anonymous', t('Anonymous'))),
770
        ),
771
      ),
772
    ),
773
    '#weight' => 90,
774
  );
775
  $form['user']['name'] = array(
776
    '#type' => 'textfield',
777
    '#title' => t('Associated with'),
778
    '#maxlength' => 60,
779
    '#autocomplete_path' => 'user/autocomplete',
780
    '#default_value' => (!empty($file->uid) && $user = user_load($file->uid)) ? $user->name : '',
781
    '#weight' => -1,
782
    '#description' => t('Leave blank for %anonymous.', array('%anonymous' => variable_get('anonymous', t('Anonymous')))),
783
  );
784

    
785
  // Add the buttons.
786
  $form['actions'] = array('#type' => 'actions');
787
  $form['actions']['submit'] = array(
788
    '#type' => 'submit',
789
    '#value' => t('Save'),
790
    '#weight' => 5,
791
    '#submit' => array('file_entity_edit_submit'),
792
    '#validate' => array('file_entity_edit_validate'),
793
  );
794
  $form['actions']['delete'] = array(
795
    '#type' => 'submit',
796
    '#value' => t('Delete'),
797
    '#weight' => 10,
798
    '#submit' => array('file_entity_edit_delete_submit'),
799
    '#access' => file_entity_access('delete', $file),
800
  );
801

    
802
  // Build the URL for the cancel button taking into account that there might be
803
  // a "destination" that includes query string variables.
804
  $parameters = drupal_get_query_parameters();
805
  $destination = isset($parameters['destination']) ? $parameters['destination'] : 'file/' . $file->fid;
806
  $url = drupal_parse_url($destination);
807

    
808
  $form['actions']['cancel'] = array(
809
    '#type' => 'link',
810
    '#title' => t('Cancel'),
811
    '#href' => $url['path'],
812
    '#options' => array('query' => $url['query']),
813
    '#weight' => 15,
814
  );
815

    
816
  $langcode = function_exists('entity_language') ? entity_language('file', $file) : NULL;
817
  field_attach_form('file', $file, $form, $form_state, $langcode);
818

    
819
  return $form;
820
}
821

    
822
/**
823
 * Form validation handler for file_entity_edit().
824
 */
825
function file_entity_edit_validate($form, &$form_state) {
826
  $file = (object) $form_state['values'];
827

    
828
  // Validate the "associated user" field.
829
  if (!empty($file->name) && !($account = user_load_by_name($file->name))) {
830
    // The use of empty() is mandatory in the context of usernames
831
    // as the empty string denotes the anonymous user. In case we
832
    // are dealing with an anonymous user we set the user ID to 0.
833
    form_set_error('name', t('The username %name does not exist.', array('%name' => $file->name)));
834
  }
835

    
836
  // Handle the replacement file if uploaded.
837
  if (isset($form_state['values']['replace_upload'])) {
838
    // Save the file as a temporary file.
839
    $file = file_save_upload('replace_upload', $form['replace_upload']['#upload_validators']);
840
    if (!empty($file)) {
841
      // Put the temporary file in form_values so we can save it on submit.
842
      $form_state['values']['replace_upload'] = $file;
843
    }
844
    elseif ($file === FALSE) {
845
      // File uploaded failed.
846
      form_set_error('replace_upload', t('The replacement file could not be uploaded.'));
847
    }
848
  }
849

    
850
  // Run entity form validation.
851
  entity_form_field_validate('file', $form, $form_state);
852
}
853

    
854
/**
855
 * Form submission handler for the 'Save' button for file_entity_edit().
856
 */
857
function file_entity_edit_submit($form, &$form_state) {
858
  $file = $form_state['file'];
859
  $orphaned_uri = '';
860

    
861
  // Check if a replacement file has been uploaded.
862
  if (!empty($form_state['values']['replace_upload'])) {
863
    $replacement = $form_state['values']['replace_upload'];
864
    // Existing image metadata is stored in $file->height and $file->width.
865
    // Loop through the replacement metadata and update existing values.
866
    if (!empty($replacement->metadata)) {
867
      foreach ($replacement->metadata as $metadata_key => $metadata_value) {
868
        if (isset($file->{$metadata_key})) {
869
          $file->{$metadata_key} = $metadata_value;
870
        }
871
      }
872
    }
873
    // Move file from temp to permanent home.
874
    if (!empty($form_state['values']['replace_keep_original_filename'])
875
    && $form_state['values']['replace_keep_original_filename']) {
876
      $destination_uri = rtrim($file->uri, drupal_basename($file->uri)) . drupal_basename($file->uri);
877
    }
878
    else {
879
      $destination_uri = rtrim($file->uri, drupal_basename($file->uri)) . drupal_basename($replacement->uri);
880
    }
881
    $replace_mode = $destination_uri == $file->uri ? FILE_EXISTS_REPLACE : FILE_EXISTS_RENAME;
882
    if ($new_file_uri = file_unmanaged_copy($replacement->uri, $destination_uri, $replace_mode)) {
883
      // @todo Add watchdog() about replaced file here?
884

    
885
      // Remove temporary file.
886
      file_delete($replacement);
887

    
888
      // Update if the uri target has changed.
889
      if ($new_file_uri != $file->uri) {
890
        // Store the original file uri to delete if save is successful.
891
        $orphaned_uri = $file->uri;
892

    
893
        // Update file entity uri.
894
        $file->uri = $new_file_uri;
895
      }
896
    }
897
  }
898

    
899
  // Run entity form submit handling and save the file.
900
  entity_form_submit_build_entity('file', $file, $form, $form_state);
901

    
902
  // A user might assign the associated user by entering a user name in the file
903
  // edit form, which we then need to translate to a user ID.
904
  if (isset($file->name)) {
905
    // The use of isset() is mandatory in the context of user IDs, because
906
    // user ID 0 denotes the anonymous user.
907
    if ($user = user_load_by_name($file->name)) {
908
      $file->uid = $user->uid;
909
    }
910
    else {
911
      // Anonymous user.
912
      $file->uid = 0;
913
    }
914
  }
915
  elseif ($file->uid) {
916
    $user = user_load($file->uid);
917
    $file->name = $user->name;
918
  }
919

    
920
  if (file_uri_scheme($file->uri) != $form_state['values']['scheme']) {
921
    $file_destination = $form_state['values']['scheme'] . '://' . file_uri_target($file->uri);
922
    $file_destination = file_stream_wrapper_uri_normalize($file_destination);
923
    $file_destination_dirname = drupal_dirname($file_destination);
924
    // Create the directory in case it doesn't exist.
925
    file_prepare_directory($file_destination_dirname, FILE_CREATE_DIRECTORY);
926
    if ($moved_file = file_move($file, $file_destination, FILE_EXISTS_RENAME)) {
927
      // Only re-assign the file object if file_move() did not fail.
928
      $file = $moved_file;
929
    }
930
  }
931

    
932
  file_save($file);
933

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

    
941
  // Clean up orphaned file.
942
  if (!empty($orphaned_uri)) {
943
    file_unmanaged_delete($orphaned_uri);
944

    
945
    $args['@orphaned'] = file_uri_target($orphaned_uri);
946
    watchdog('file', '@type: deleted orphaned file @orphaned for %title.', $args);
947
    drupal_set_message(t('The replaced @type @orphaned has been deleted.', $args));
948
  }
949

    
950
  $form_state['redirect'] = 'file/' . $file->fid;
951

    
952
  // Clear the page and block caches.
953
  cache_clear_all();
954
}
955

    
956
/**
957
 * Form submission handler for the 'Delete' button for file_entity_edit().
958
 */
959
function file_entity_edit_delete_submit($form, &$form_state) {
960
  $fid = $form_state['values']['fid'];
961
  $destination = array();
962
  if (isset($_GET['destination'])) {
963
    $destination = drupal_get_destination();
964
    unset($_GET['destination']);
965
  }
966
  $form_state['redirect'] = array('file/' . $fid . '/delete', array('query' => $destination));
967

    
968
  // Clear the page and block caches.
969
  cache_clear_all();
970
}
971

    
972
/**
973
 * Page callback: Form constructor for the file deletion confirmation form.
974
 *
975
 * Path: file/%file/delete
976
 *
977
 * @param object $file
978
 *   A file object from file_load().
979
 *
980
 * @see file_entity_menu()
981
 */
982
function file_entity_delete_form($form, &$form_state, $file) {
983
  $form_state['file'] = $file;
984

    
985
  $form['fid'] = array(
986
    '#type' => 'value',
987
    '#value' => $file->fid,
988
  );
989

    
990
  $description = t('This action cannot be undone.');
991
  if ($references = file_usage_list($file)) {
992
    $description .= ' ' . t('This file is currently in use and may cause problems if deleted.');
993
  }
994

    
995
  return confirm_form($form,
996
    t('Are you sure you want to delete the file %title?', array(
997
      '%title' => entity_label('file', $file),
998
    )),
999
    'file/' . $file->fid,
1000
    $description,
1001
    t('Delete')
1002
  );
1003
}
1004

    
1005
/**
1006
 * Form submission handler for file_entity_delete_form().
1007
 */
1008
function file_entity_delete_form_submit($form, &$form_state) {
1009
  if ($form_state['values']['confirm'] && $file = file_load($form_state['values']['fid'])) {
1010
    // Use file_delete_multiple() rather than file_delete() since we want to
1011
    // avoid unwanted validation and usage checking.
1012
    file_delete_multiple(array($file->fid));
1013

    
1014
    $args = array(
1015
      '@type' => file_entity_type_get_name($file),
1016
      '%title' => entity_label('file', $file),
1017
    );
1018
    watchdog('file', '@type: deleted %title.', $args);
1019
    drupal_set_message(t('@type %title has been deleted.', $args));
1020
  }
1021

    
1022
  $form_state['redirect'] = '<front>';
1023

    
1024
  // Clear the page and block caches.
1025
  cache_clear_all();
1026
}
1027

    
1028
/**
1029
 * Form constructor for file deletion confirmation form.
1030
 *
1031
 * @param array $files
1032
 *   An array of file objects.
1033
 */
1034
function file_entity_multiple_delete_form($form, &$form_state, array $files) {
1035
  $form['files'] = array(
1036
    '#prefix' => '<ul>',
1037
    '#suffix' => '</ul>',
1038
    '#tree' => TRUE,
1039
  );
1040

    
1041
  $files_have_usage = FALSE;
1042
  foreach ($files as $fid => $file) {
1043
    $title = entity_label('file', $file);
1044
    $usage = file_usage_list($file);
1045
    if (!empty($usage)) {
1046
      $files_have_usage = TRUE;
1047
      $title = t('@title (in use)', array('@title' => $title));
1048
    }
1049
    else {
1050
      $title = check_plain($title);
1051
    }
1052
    $form['files'][$fid] = array(
1053
      '#type' => 'hidden',
1054
      '#value' => $fid,
1055
      '#prefix' => '<li>',
1056
      '#suffix' => $title . "</li>\n",
1057
    );
1058
  }
1059

    
1060
  $form['operation'] = array(
1061
    '#type' => 'hidden',
1062
    '#value' => 'delete',
1063
  );
1064

    
1065
  $description = t('This action cannot be undone.');
1066
  if ($files_have_usage) {
1067
    $description .= ' ' . t('Some of the files are currently in use and may cause problems if deleted.');
1068
  }
1069

    
1070
  return confirm_form(
1071
    $form,
1072
    format_plural(count($files), 'Are you sure you want to delete this file?', 'Are you sure you want to delete these files?'),
1073
    'admin/content/file',
1074
    $description,
1075
    t('Delete')
1076
  );
1077
}
1078

    
1079
/**
1080
 * Form submission handler for file_entity_multiple_delete_form().
1081
 */
1082
function file_entity_multiple_delete_form_submit($form, &$form_state) {
1083
  if ($form_state['values']['confirm'] && $fids = array_keys($form_state['values']['files'])) {
1084
    file_delete_multiple($fids);
1085
    $count = count($fids);
1086
    watchdog('file', 'Deleted @count files.', array('@count' => $count));
1087
    drupal_set_message(format_plural($count, 'Deleted one file.', 'Deleted @count files.'));
1088
  }
1089
  $form_state['redirect'] = 'admin/content/file';
1090

    
1091
  // Clear the page and block caches.
1092
  cache_clear_all();
1093
}
1094

    
1095
/**
1096
 * Page callback for the file edit form.
1097
 *
1098
 * @deprecated
1099
 *   Use drupal_get_form('file_entity_edit')
1100
 */
1101
function file_entity_page_edit($file) {
1102
  return drupal_get_form('file_entity_edit', $file);
1103
}
1104

    
1105
/**
1106
 * Page callback for the file deletion confirmation form.
1107
 *
1108
 * @deprecated
1109
 *   Use drupal_get_form('file_entity_delete_form')
1110
 */
1111
function file_entity_page_delete($file) {
1112
  return drupal_get_form('file_entity_delete_form');
1113
}
1114

    
1115
/**
1116
 * Retrieves the upload validators for a file.
1117
 *
1118
 * @param array $options
1119
 *   (optional) An array of options for file validation.
1120
 *
1121
 * @return array
1122
 *   An array suitable for passing to file_save_upload() or for a managed_file
1123
 *   or upload element's '#upload_validators' property.
1124
 */
1125
function file_entity_get_upload_validators(array $options = array()) {
1126
  // Set up file upload validators.
1127
  $validators = array();
1128

    
1129
  // Validate file extensions. If there are no file extensions in $params and
1130
  // there are no Media defaults, there is no file extension validation.
1131
  if (!empty($options['file_extensions'])) {
1132
    $validators['file_validate_extensions'] = array($options['file_extensions']);
1133
  }
1134
  else {
1135
    $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'));
1136
  }
1137

    
1138
  // Cap the upload size according to the system or user defined limit.
1139
  $max_filesize = parse_size(file_upload_max_size());
1140
  $file_entity_max_filesize = parse_size(variable_get('file_entity_max_filesize', ''));
1141

    
1142
  // If the user defined a size limit, use the smaller of the two.
1143
  if (!empty($file_entity_max_filesize)) {
1144
    $max_filesize = min($max_filesize, $file_entity_max_filesize);
1145
  }
1146

    
1147
  if (!empty($options['max_filesize']) && $options['max_filesize'] < $max_filesize) {
1148
    $max_filesize = parse_size($options['max_filesize']);
1149
  }
1150

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

    
1154
  // Add image validators.
1155
  $options += array('min_resolution' => 0, 'max_resolution' => 0);
1156
  if ($options['min_resolution'] || $options['max_resolution']) {
1157
    $validators['file_validate_image_resolution'] = array($options['max_resolution'], $options['min_resolution']);
1158
  }
1159

    
1160
  // Add other custom upload validators from options.
1161
  if (!empty($options['upload_validators'])) {
1162
    $validators += $options['upload_validators'];
1163
  }
1164

    
1165
  return $validators;
1166
}
1167

    
1168
function file_entity_upload_archive_form($form, &$form_state) {
1169
  $options = array(
1170
    'file_extensions' => archiver_get_extensions(),
1171
  );
1172

    
1173
  $form['upload'] = array(
1174
    '#type' => 'managed_file',
1175
    '#title' => t('Upload an archive file'),
1176
    '#upload_location' => NULL, // Upload to the temporary directory.
1177
    '#upload_validators' => file_entity_get_upload_validators($options),
1178
    '#progress_indicator' => 'bar',
1179
    '#required' => TRUE,
1180
    '#pre_render' => array('file_managed_file_pre_render', 'file_entity_upload_validators_pre_render'),
1181
  );
1182

    
1183
  $form['pattern'] = array(
1184
    '#type' => 'textfield',
1185
    '#title' => t('Pattern'),
1186
    '#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.'),
1187
    '#default_value' => '.*',
1188
    '#required' => TRUE,
1189
  );
1190

    
1191
  $form['actions'] = array('#type' => 'actions');
1192
  $form['actions']['submit'] = array(
1193
    '#type' => 'submit',
1194
    '#value' => t('Submit'),
1195
  );
1196

    
1197
  form_load_include($form_state, 'inc', 'file_entity', 'file_entity.pages');
1198

    
1199
  return $form;
1200
}
1201

    
1202
/**
1203
 * Upload a file.
1204
 */
1205
function file_entity_upload_archive_form_submit($form, &$form_state) {
1206
  $form_state['files'] = array();
1207

    
1208
  if ($archive = file_load($form_state['values']['upload'])) {
1209
    if ($archiver = archiver_get_archiver($archive->uri)) {
1210
      $files = $archiver->listContents();
1211
      $destination = pathinfo($archive->filename, PATHINFO_FILENAME);
1212

    
1213
      // Prepare a temporary directory to extract the archive to before saving
1214
      // it is content.
1215
      $extract_location = 'temporary://' . $destination;
1216
      $extract_location = file_destination($extract_location, FILE_EXISTS_RENAME);
1217
      if (!file_prepare_directory($extract_location, FILE_MODIFY_PERMISSIONS | FILE_CREATE_DIRECTORY)) {
1218
        throw new Exception(t('Unable to prepar, a temporary directory %dir for extraction.', array('%dir' => $extract_location)));
1219
      }
1220

    
1221
      // Prepare target directory where files are going to be saved.
1222
      $target_dir = file_default_scheme() . '://' . $destination;
1223
      $target_dir = file_destination($target_dir, FILE_EXISTS_RENAME);
1224
      if (!file_prepare_directory($target_dir, FILE_MODIFY_PERMISSIONS | FILE_CREATE_DIRECTORY)) {
1225
        throw new Exception(t('Unable to prepar, a directory %dir for extraction.', array('%dir' => $target_dir)));
1226
      }
1227

    
1228
      $archiver->extract($extract_location);
1229
      $pattern = '/' . $form_state['values']['pattern'] . '/';
1230
      if ($extracted_files = file_scan_directory($extract_location, $pattern)) {
1231
        foreach ($extracted_files as $extracted_file) {
1232
          $file = new stdClass();
1233
          $file->fid = NULL;
1234
           $file->uid = $archive->uid;
1235
          $file->filename = $extracted_file->filename;
1236
          $file->origname = $extracted_file->filename;
1237
          $file->status = FILE_STATUS_PERMANENT;
1238
          $file->filemime = file_get_mimetype($extracted_file->filename);
1239

    
1240
          // destination uri should match the current file directory hierarchy.
1241
          $file->uri = $target_dir . str_replace($extract_location, '', $extracted_file->uri);
1242

    
1243
          // Rename potentially executable files, to help prevent exploits (i.e. will
1244
          // rename filename.php.foo and filename.php to filename.php.foo.txt and
1245
          // filename.php.txt, respectively). Don't rename if 'allow_insecure_uploads'
1246
          // evaluates to TRUE.
1247
          if (!variable_get('allow_insecure_uploads', 0) && preg_match('/\.(php|pl|py|cgi|asp|js)(\.|$)/i', $file->filename) && (substr($file->filename, -4) != '.txt')) {
1248
            $file->filemime = 'text/plain';
1249
            $file->uri .= '.txt';
1250
            $file->filename .= '.txt';
1251
          }
1252

    
1253
          // prepare destination path for the extracted file while keeping the
1254
          // directory hierarchy of the file.
1255
          $destination = pathinfo($file->uri, PATHINFO_DIRNAME);
1256
          if (!file_prepare_directory($destination, FILE_MODIFY_PERMISSIONS | FILE_CREATE_DIRECTORY)) {
1257
            throw new Exception(t('Unable to prepar, a directory %dir for extraction.', array('%dir' => $destination)));
1258
          }
1259

    
1260
          if (!file_unmanaged_move($extracted_file->uri, $file->uri)) {
1261
            throw new Exception(t('Could not move uploaded file %file to destination %destination.', array('%file' => $extracted_file->filename, '%destination' => $file->uri)));
1262
            return FALSE;
1263
          }
1264

    
1265
          file_save($file);
1266
          $form_state['files'][$file->fid] = $file;
1267
        }
1268
      }
1269
      // Delete extract location
1270
      file_unmanaged_delete_recursive($extract_location);
1271
      drupal_set_message(t('Extracted %file and added @count new files.', array('%file' => $archive->filename, '@count' => count($files))));
1272
    }
1273
    else {
1274
      throw new Exception(t('Cannot extract %file, not a valid archive.', array('%file' => $archive->uri)));
1275
    }
1276
  }
1277

    
1278
  // Redirect to the file edit page.
1279
  if (file_entity_access('edit') && module_exists('multiform')) {
1280
    $destination = array('destination' => 'admin/content/file');
1281
    if (isset($_GET['destination'])) {
1282
      $destination = drupal_get_destination();
1283
      unset($_GET['destination']);
1284
    }
1285
    $form_state['redirect'] = array('admin/content/file/edit-multiple/' . implode(' ', array_keys($form_state['files'])), array('query' => $destination));
1286
  }
1287
  else {
1288
    $form_state['redirect'] = 'admin/content/file';
1289
  }
1290
}