Project

General

Profile

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

root / drupal7 / sites / all / modules / file_entity / file_entity.pages.inc @ 7b2d1845

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
    // If using S3 as a replacement for the file system, default headers
67
    // for downloading will cause the server to not respond. Remove them.
68
    if (module_exists('s3fs')) {
69
      unset($headers['Content-Length']);
70
      unset($headers['Content-Transfer-Encoding']);
71
    }
72
    foreach ($headers as $name => $value) {
73
      drupal_add_http_header($name, $value);
74
    }
75
    drupal_send_headers();
76
    drupal_exit();
77
  }
78
}
79

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

    
105
  switch ($step) {
106
    case 1:
107
      return file_entity_add_upload_step_upload($form, $form_state, $options);
108

    
109
    case 2:
110
      return file_entity_add_upload_step_filetype($form, $form_state, $options);
111

    
112
    case 3:
113
      return file_entity_add_upload_step_scheme($form, $form_state, $options);
114

    
115
    case 4:
116
      return file_entity_add_upload_step_fields($form, $form_state, $options);
117

    
118
  }
119
}
120

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

    
137
  $form['upload']['#description'] = t('Files must be less than !size.', array('!size' => '<strong>' . format_size($form['upload']['#upload_validators']['file_entity_validate_size_extensions'][0]) . '</strong>'));
138

    
139
  // Get list of extensions.
140
  $extensions = explode("\n", variable_get('file_entity_max_filesize_extensions'));
141

    
142
  if (!empty($extensions)) {
143
    $limits = array();
144

    
145
    // Limit extensions displayed to those in upload validator.
146
    $valid_extensions = array();
147
    if (!empty($upload_validators)) {
148
      foreach ($upload_validators['file_validate_extensions'] as $key => $values) {
149
        $valid_extensions[$key] = explode(' ', strtolower($values));
150
      }
151
    }
152
    // If there are valid extensions, merge sub-arrays into a single array.
153
    if (!empty($valid_extensions)) {
154
      $valid_extensions = call_user_func_array('array_merge', $valid_extensions);
155
    }
156

    
157
    foreach ($extensions as $position => $text) {
158
      $matches = array();
159
      preg_match('/(.*)\|(.*)/', $text, $matches);
160

    
161
      if (is_array($matches) && count($matches) == 3) {
162
        $extension = $matches[1];
163
        $filesize = $matches[2];
164

    
165
        if (empty($valid_extensions) || in_array(strtolower($extension), $valid_extensions)) {
166
          $limits[] = trim($extension) . ': <strong>' . trim($filesize) . '</strong>';
167
        }
168
      }
169
    }
170

    
171
    if (!empty($limits)) {
172
      // If less than or equal to 15 items to display, show as unordered list.
173
      // Otherwise, implode into a single list item.
174
      $list = array(
175
        '#theme' => 'item_list',
176
        '#list_type' => 'ul',
177
        '#items' => (count($limits) <= 15) ? $limits : array(implode(', ', $limits)),
178
      );
179

    
180
      if (!empty($valid_extensions)) {
181
        $form['upload']['#description'] = t('Files must be less than these sizes: !list',
182
          array(
183
            '!list' => render($list),
184
          )
185
        );
186
      }
187
      else {
188
        $form['upload']['#description'] = t('Files must be less than these sizes: !list The default maximum size is !size.',
189
          array(
190
            '!list' => render($list),
191
            '!size' => '<strong>' . format_size($form['upload']['#upload_validators']['file_entity_validate_size_extensions'][0]) . '</strong>'
192
          )
193
        );
194
      }
195
    }
196
  }
197

    
198
  $form['actions'] = array('#type' => 'actions');
199
  $form['actions']['next'] = array(
200
    '#type' => 'submit',
201
    '#value' => t('Next'),
202
  );
203

    
204
  form_load_include($form_state, 'inc', 'file_entity', 'file_entity.pages');
205

    
206
  return $form;
207
}
208

    
209
function file_entity_validate_size_extensions(stdClass $file, $file_limit = 0, $user_limit = 0) {
210
  global $user;
211
  $errors = array();
212

    
213
  // Current file extension.
214
  $current_extension = pathinfo($file->filename, PATHINFO_EXTENSION);
215

    
216
  // Get list of extensions.
217
  $extensions = explode("\n", variable_get('file_entity_max_filesize_extensions'));
218

    
219
  if ($extensions) {
220
    foreach ($extensions as $position => $text) {
221
      $matches = array();
222
      preg_match('/(.*)\|(.*)/', $text, $matches);
223

    
224
      if (is_array($matches) && count($matches) == 3) {
225
        $extension = $matches[1];
226
        $filesize = $matches[2];
227

    
228
        if (strtolower($extension) == strtolower($current_extension)) {
229
          $file_limit = parse_size($filesize);
230
        }
231
      }
232
    }
233
  }
234

    
235
  if ($file_limit && $file->filesize > $file_limit) {
236
    $errors[] = t('The file is %filesize exceeding the maximum file size of %maxsize.', array('%filesize' => format_size($file->filesize), '%maxsize' => format_size($file_limit)));
237
  }
238

    
239
  // Save a query by only calling file_space_used() when a limit is provided.
240
  if ($user_limit && (file_space_used($user->uid) + $file->filesize) > $user_limit) {
241
    $errors[] = t('The file is %filesize which would exceed your disk quota of %quota.', array('%filesize' => format_size($file->filesize), '%quota' => format_size($user_limit)));
242
  }
243

    
244
  return $errors;
245
}
246

    
247
/**
248
 * Generate form fields for the second step in the add file wizard.
249
 */
250
function file_entity_add_upload_step_filetype($form, &$form_state, array $options = array()) {
251
  $file = file_load($form_state['storage']['upload']);
252
  $selected_files = $form['#options']['types'];
253

    
254
  $form['type'] = array(
255
    '#type' => 'radios',
256
    '#title' => t('File type'),
257
    '#options' => file_entity_get_filetype_candidates($file, $selected_files),
258
    '#default_value' => isset($form_state['storage']['type']) ? $form_state['storage']['type'] : NULL,
259
    '#required' => TRUE,
260
  );
261

    
262
  $form['actions'] = array('#type' => 'actions');
263
  $form['actions']['previous'] = array(
264
    '#type' => 'submit',
265
    '#value' => t('Previous'),
266
    '#limit_validation_errors' => array(),
267
    '#submit' => array('file_entity_add_upload_submit'),
268
  );
269
  $form['actions']['next'] = array(
270
    '#type' => 'submit',
271
    '#value' => t('Next'),
272
  );
273

    
274
  return $form;
275
}
276

    
277
/**
278
 * Generate form fields for the third step in the add file wizard.
279
 */
280
function file_entity_add_upload_step_scheme($form, &$form_state, array $options = array()) {
281
  $file = file_load($form_state['storage']['upload']);
282

    
283
  $schemes = array();
284
  foreach (file_get_stream_wrappers(STREAM_WRAPPERS_WRITE_VISIBLE) as $scheme => $info) {
285
    $schemes[$scheme] = check_plain($info['description']);
286
  }
287

    
288
  // Remove any schemes not found in the instance settings.
289
  if (!empty($options['schemes'])) {
290
    $schemes = array_intersect_key($schemes, array_flip($options['schemes']));
291
  }
292

    
293
  // Determine which scheme to use as the default value.
294
  if (isset($form_state['storage']['scheme'])) {
295
    $fallback_scheme = $form_state['storage']['scheme'];
296
  }
297
  elseif (!empty($options['uri_scheme'])) {
298
    $fallback_scheme = $options['uri_scheme'];
299
  }
300
  else {
301
    $fallback_scheme = file_default_scheme();
302
  }
303

    
304
  $form['scheme'] = array(
305
    '#type' => 'radios',
306
    '#title' => t('Destination'),
307
    '#options' => $schemes,
308
    '#default_value' => $fallback_scheme,
309
    '#required' => TRUE,
310
  );
311

    
312
  $form['actions'] = array('#type' => 'actions');
313
  $form['actions']['previous'] = array(
314
    '#type' => 'submit',
315
    '#value' => t('Previous'),
316
    '#limit_validation_errors' => array(),
317
    '#submit' => array('file_entity_add_upload_submit'),
318
  );
319
  $form['actions']['next'] = array(
320
    '#type' => 'submit',
321
    '#value' => t('Next'),
322
  );
323

    
324
  return $form;
325
}
326

    
327
/**
328
 * Generate form fields for the fourth step in the add file wizard.
329
 */
330
function file_entity_add_upload_step_fields($form, &$form_state, array $options = array()) {
331
  // Load the file and overwrite the filetype set on the previous screen.
332
  $file = file_load($form_state['storage']['upload']);
333
  $file->type = $form_state['storage']['type'];
334

    
335
  // Let users modify the filename here.
336
  $form['filename'] = array(
337
    '#type' => 'textfield',
338
    '#title' => t('Name'),
339
    '#default_value' => $file->filename,
340
    '#required' => TRUE,
341
    '#maxlength' => 255,
342
    '#weight' => -10,
343
  );
344

    
345
  // Add fields.
346
  field_attach_form('file', $file, $form, $form_state);
347

    
348
  $form['actions'] = array('#type' => 'actions');
349
  $form['actions']['previous'] = array(
350
    '#type' => 'submit',
351
    '#value' => t('Previous'),
352
    '#limit_validation_errors' => array(),
353
    '#submit' => array('file_entity_add_upload_submit'),
354
  );
355
  $form['actions']['submit'] = array(
356
    '#type' => 'submit',
357
    '#value' => t('Save'),
358
  );
359

    
360
  return $form;
361
}
362

    
363
/**
364
 * Page callback to show file usage information.
365
 */
366
function file_entity_usage_page($file) {
367
  drupal_set_title(t('<em>Usage of @type</em> @title', array('@type' => $file->type, '@title' => $file->filename)), PASS_THROUGH);
368

    
369
  $rows = array();
370
  $occured_entities = array();
371

    
372
  // Determine all of the locations where a file is used, then loop through the
373
  // occurrences and filter out any duplicates.
374
  foreach (file_usage_list($file) as $type) {
375
    foreach ($type as $entity_type => $entity_ids) {
376
      // There are cases where the actual entity doesn't exist.
377
      // We have to handle this.
378
      $entity_info = entity_get_info($entity_type);
379
      $entities = empty($entity_info) ? NULL : entity_load($entity_type, array_keys($entity_ids));
380

    
381
      foreach ($entity_ids as $entity_id => $count) {
382
        // If this entity has already been listed in the table, just add any
383
        // additional usage to the total count column in the table row and
384
        // continue on to the next iteration of the loop.
385
        if (isset($occured_entities[$entity_type][$entity_id])) {
386
          $rows[$occured_entities[$entity_type][$entity_id]][3] += $count;
387
          continue;
388
        }
389

    
390
        // Retrieve the label and the URI of the entity.
391
        $label = empty($entities[$entity_id]) ? t('(entity not loaded)') : entity_label($entity_type, $entities[$entity_id]);
392
        if (empty($label)) {
393
          $label = t('(entity label not loaded)');
394
        }
395
        $entity_label = $label;
396
        $entity_uri = empty($entities[$entity_id]) ? NULL : entity_uri($entity_type, $entities[$entity_id]);
397

    
398
        // Link the label to the URI when possible.
399
        if (!empty($entity_uri['path']) && $entity_type != 'paragraphs_item') {
400
          if (empty($entity_uri['options'])) {
401
            $entity_label = l($label, $entity_uri['path']);
402
          }
403
          else {
404
            $entity_label = l($label, $entity_uri['path'], $entity_uri['options']);
405
          }
406
        }
407
        // For paragraphs items, we are searching for usages in nodes.
408
        elseif ($entity_type == 'paragraphs_item') {
409
          $paragraph_fields = field_read_fields(array('type' => 'paragraphs'));
410
          foreach ($paragraph_fields as $paragraph_field) {
411
            $field_name = $paragraph_field['field_name'];
412
            $query = new EntityFieldQuery();
413
            $query->entityCondition('entity_type', 'node')
414
              ->fieldCondition($field_name, 'value', $entity_id, '=');
415
            $nid = $query->execute();
416
            if (!empty($nid)) {
417
              $nid = implode(array_keys($nid['node']));
418
              if ($nid) {
419
                $node = node_load($nid);
420
                $entity_label = l($node->title, 'node/' . $nid);
421
              }
422
            }
423
          }
424
        }
425
        else {
426
          $entity_label = check_plain($label);
427
        }
428

    
429
        $rows[] = array(
430
          $entity_label,
431
          $entity_id,
432
          $entity_type,
433
          $count,
434
        );
435

    
436
        // Record the occurrence of the entity to ensure that it isn't listed in
437
        // the table again.
438
        $occured_entities[$entity_type][$entity_id] = count($rows) - 1;
439
      }
440
    }
441
  }
442

    
443
  $header = array(
444
    t('Entity label'),
445
    t('Entity ID'),
446
    t('Entity type'),
447
    t('Times this file used by this entity'),
448
  );
449
  $build['usage_table'] = array(
450
    '#theme' => 'table',
451
    '#header' => $header,
452
    '#rows' => $rows,
453
    '#caption' => t('This table lists all of the places where @filename is used.', array('@filename' => $file->filename)),
454
    '#empty' => t('This file is not currently used.'),
455
  );
456

    
457
  return $build;
458
}
459

    
460
/**
461
 * Get the candidate filetypes for a given file.
462
 *
463
 * Only filetypes for which the user has access to create entities are returned.
464
 *
465
 * @param array $file
466
 *   An upload file array from form_state.
467
 *
468
 * @return array
469
 *   An array of file type bundles that support the file's mime type.
470
 */
471
function file_entity_get_filetype_candidates($file, $selected_files = array()) {
472
  $types = module_invoke_all('file_type', $file);
473
  drupal_alter('file_type', $types, $file);
474

    
475
  // If no file types are selected in field instance settings, allow all
476
  // available types.
477
  if (!empty($selected_files)) {
478
    // Limit file type candidates to field allowed types.
479
    $types = array_intersect($types, $selected_files);
480
  }
481

    
482
  $candidates = array();
483
  foreach ($types as $type) {
484
    $file->type = $type;
485
    if (file_entity_access('create', $file)) {
486
      $candidates[$type] = file_entity_type_get_name($file);
487
    }
488
  }
489
  return $candidates;
490
}
491

    
492
/**
493
 * Submit handler for the add file form.
494
 */
495
function file_entity_add_upload_submit($form, &$form_state) {
496
  $form_state['storage'] = isset($form_state['storage']) ? $form_state['storage'] : array();
497
  $form_state['storage'] = array_merge($form_state['storage'], $form_state['values']);
498

    
499
  // Field selected allowed types.
500
  $selected_files = $form['#options']['types'];
501

    
502
  // This var is set to TRUE when we are ready to save the file.
503
  $save = FALSE;
504
  $trigger = $form_state['triggering_element']['#id'];
505
  $triggered_next = $trigger == 'edit-next' || (strpos($trigger, 'edit-next--') === 0);
506
  $triggered_previous = $trigger == 'edit-previous' || (strpos($trigger, 'edit-previous--') === 0);
507
  $step_delta = ($triggered_previous) ? -1 : 1;
508

    
509
  $steps_to_check = array(2, 3);
510
  if ($triggered_previous) {
511
    // If the previous button was hit,
512
    // the step checking order should be reversed 3, 2.
513
    $steps_to_check = array_reverse($steps_to_check);
514
  }
515

    
516
  foreach ($steps_to_check as $step) {
517
    // Check if we can skip step 2 and 3.
518
    if (($form['#step'] == $step - 1 && $triggered_next) || ($form['#step'] == $step + 1 && $triggered_previous)) {
519
      $file = file_load($form_state['storage']['upload']);
520
      if ($step == 2) {
521
        // Check if we can skip step 2.
522
        $candidates = file_entity_get_filetype_candidates($file, $selected_files);
523
        if (count($candidates) == 1) {
524
          $candidates_keys = array_keys($candidates);
525
          // There is only one possible filetype for this file.
526
          // Skip the second page.
527
          $form['#step'] += $step_delta;
528
          $form_state['storage']['type'] = reset($candidates_keys);
529
        }
530
        elseif (!$candidates || variable_get('file_entity_file_upload_wizard_skip_file_type', FALSE)) {
531
          // Do not assign the file a file type.
532
          $form['#step'] += $step_delta;
533
          $form_state['storage']['type'] = FILE_TYPE_NONE;
534
        }
535
      }
536
      else {
537
        // Check if we can skip step 3.
538
        $options = $form['#options'];
539
        $schemes = file_get_stream_wrappers(STREAM_WRAPPERS_WRITE_VISIBLE);
540

    
541
        // Remove any schemes not found in the instance settings.
542
        if (!empty($options['schemes'])) {
543
          $schemes = array_intersect_key($schemes, $options['schemes']);
544
        }
545

    
546
        if (!file_entity_file_is_writeable($file)) {
547
          // The file is read-only (remote) and must use its provided scheme.
548
          $form['#step'] += $step_delta;
549
          $form_state['storage']['scheme'] = file_uri_scheme($file->uri);
550
        }
551
        elseif (count($schemes) == 1) {
552
          // There is only one possible stream wrapper for this file.
553
          // Skip the third page.
554
          $form['#step'] += $step_delta;
555
          $form_state['storage']['scheme'] = key($schemes);
556
        }
557
        elseif (variable_get('file_entity_file_upload_wizard_skip_scheme', FALSE)) {
558
          $form['#step'] += $step_delta;
559

    
560
          // Fallback to the URI scheme specified in the field settings
561
          // otherwise use the default file scheme.
562
          if (!empty($options['uri_scheme'])) {
563
            $form_state['storage']['scheme'] = $options['uri_scheme'];
564
          }
565
          else {
566
            $form_state['storage']['scheme'] = file_default_scheme();
567
          }
568
        }
569
      }
570
    }
571
  }
572

    
573
  // We have the filetype, check if we can skip step 4.
574
  if ($form['#step'] == 3 && $triggered_next) {
575
    $file = file_load($form_state['storage']['upload']);
576
    $form_state['file'] = $file;
577
    if (!field_info_instances('file', $form_state['storage']['type'])) {
578
      // This filetype doesn't have fields, save the file.
579
      $save = TRUE;
580
    }
581
    elseif (variable_get('file_entity_file_upload_wizard_skip_fields', FALSE)) {
582
      // Save the file with blanks fields.
583
      $save = TRUE;
584
    }
585
    // Allow other modules to choose to skip or complete step 4.
586
    drupal_alter('file_entity_file_upload_skip_fields', $save, $form_state);
587
  }
588

    
589
  // Form id's can vary depending on how many other forms are displayed, so we
590
  // need to do string comparissons. e.g edit-submit--2.
591
  if ($triggered_next) {
592
    $form_state['step'] = $form['#step'] + 1;
593
  }
594
  elseif ($triggered_previous) {
595
    $form_state['step'] = $form['#step'] - 1;
596
  }
597
  elseif (strpos($trigger, 'edit-submit') !== FALSE) {
598
    $save = TRUE;
599
  }
600

    
601
  if ($save) {
602
    $file = file_load($form_state['storage']['upload']);
603
    if ($file) {
604
      if (file_uri_scheme($file->uri) != $form_state['storage']['scheme']) {
605
        $file_destination = $form_state['storage']['scheme'] . '://' . file_uri_target($file->uri);
606
        $file_destination = file_stream_wrapper_uri_normalize($file_destination);
607
        if ($moved_file = file_move($file, $file_destination, FILE_EXISTS_RENAME)) {
608
          // Only re-assign the file object if file_move() did not fail.
609
          $file = $moved_file;
610
        }
611
      }
612
      $file->type = $form_state['storage']['type'];
613
      $file->display = TRUE;
614

    
615
      // Change the file from temporary to permanent.
616
      $file->status = FILE_STATUS_PERMANENT;
617

    
618
      // Save the form fields.
619
      // Keep in mind that the values for the Field API fields must be in
620
      // $form_state['values'] and not in ['storage']. This is true as long as
621
      // the fields are on the last page of the multi step form.
622
      entity_form_submit_build_entity('file', $file, $form, $form_state);
623

    
624
      file_save($file);
625
      $form_state['file'] = $file;
626
      drupal_set_message(t('@type %name was uploaded.', array('@type' => file_entity_type_get_name($file), '%name' => $file->filename)));
627
    }
628
    else {
629
      drupal_set_message(t('An error occurred and no file was uploaded.'), 'error');
630
      return;
631
    }
632

    
633
    // Figure out destination.
634
    if (user_access('administer files')) {
635
      $path = 'admin/content/file';
636
    }
637
    else {
638
      $path = 'file/' . $file->fid;
639
    }
640
    $form_state['redirect'] = $path;
641
  }
642
  else {
643
    $form_state['rebuild'] = TRUE;
644
  }
645

    
646
  // Clear the page and block caches.
647
  cache_clear_all();
648
}
649

    
650
/**
651
 * Determines the upload location for the file add upload form.
652
 *
653
 * @param array $params
654
 *   An array of parameters from the media browser.
655
 * @param array $data
656
 *   (optional) An array of token objects to pass to token_replace().
657
 *
658
 * @return string
659
 *   A file directory URI with tokens replaced.
660
 *
661
 * @see token_replace()
662
 */
663
function file_entity_upload_destination_uri(array $params, array $data = array()) {
664
  $params += array(
665
    'uri_scheme' => file_default_scheme(),
666
    'file_directory' => variable_get('file_entity_default_file_directory', ''),
667
  );
668

    
669
  $destination = trim($params['file_directory'], '/');
670

    
671
  // Replace tokens.
672
  $destination = decode_entities(token_replace($destination, $data));
673

    
674
  return $params['uri_scheme'] . '://' . $destination;
675
}
676

    
677
/**
678
 * Form for uploading multiple files.
679
 */
680
function file_entity_add_upload_multiple($form, &$form_state, $params = array()) {
681
  $form = file_entity_add_upload($form, $form_state, $params);
682
  unset($form['upload']['#title']);
683
  // The validators will be set from plupload anyway. This isn't pretty,
684
  // but don't allow it to show up twice.
685
  unset($form['upload']['#description']);
686

    
687
  $form['upload']['#type'] = 'plupload';
688

    
689
  // Ensure that we call the plupload_element_pre_render function.
690
  // If it isn't called, it doesn't set the JS settings that transfers the
691
  // list of allowed file extensions to the PLUpload widget.
692
  // We override the 'file_entity_upload_validators_pre_render' setting if it
693
  // exists, because both pre-render hooks adds the upload-help with list of
694
  // allowed file extensions.
695
  $index = array_search('file_entity_upload_validators_pre_render', $form['upload']['#pre_render']);
696
  if ($index !== FALSE) {
697
    $form['upload']['#pre_render'][$index] = 'plupload_element_pre_render';
698
  }
699
  else {
700
    $form['upload']['#pre_render'][] = 'plupload_element_pre_render';
701
  }
702

    
703
  $form['submit']['#value'] = t('Start upload');
704
  return $form;
705
}
706

    
707
/**
708
 * Submit handler for the multiple upload form.
709
 */
710
function file_entity_add_upload_multiple_submit($form, &$form_state) {
711
  $upload_location = !empty($form['upload']['#upload_location']) ?
712
    $form['upload']['#upload_location'] . '/' :
713
    variable_get('file_default_scheme', 'public') . '://';
714

    
715
  // Ensure writable destination directory for the files.
716
  file_prepare_directory($upload_location, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
717

    
718
  // We can't use file_save_upload() because of
719
  // http://www.jacobsingh.name/content/tight-coupling-no-not.
720
  foreach ($form_state['values']['upload'] as $uploaded_file) {
721
    if ($uploaded_file['status'] == 'done') {
722
      $source = $uploaded_file['tmppath'];
723
      $destination = file_stream_wrapper_uri_normalize($upload_location . $uploaded_file['name']);
724
      // Rename it to its original name, and put it in its final home.
725
      // Note - not using file_move here because if we call file_get_mime
726
      // (in file_uri_to_object) while it has a .tmp extension, it horks.
727
      $destination = file_unmanaged_move($source, $destination, FILE_EXISTS_RENAME);
728

    
729
      $file = file_uri_to_object($destination);
730
      $file->status = FILE_STATUS_PERMANENT;
731
      file_save($file);
732

    
733
      $form_state['files'][$file->fid] = $file;
734
    }
735
    else {
736
      // @todo: move this to element validate or something.
737
      form_set_error('pud', t('The specified file %name could not be uploaded.', array('%name' => $uploaded_file['name'])));
738
    }
739
  }
740

    
741
  // Redirect to the file edit page.
742
  if (file_entity_access('update', $file) && module_exists('media_bulk_upload')) {
743
    $destination = array();
744
    if (isset($_GET['destination'])) {
745
      $destination = drupal_get_destination();
746
      unset($_GET['destination']);
747
    }
748
    elseif (user_access('administer files')) {
749
      $destination = array('destination' => 'admin/content/file');
750
    }
751
    $form_state['redirect'] = array('admin/content/file/edit-multiple/' . implode(' ', array_keys($form_state['files'])), array('query' => $destination));
752
  }
753
  else {
754
    $form_state['redirect'] = user_access('administer files') ? 'admin/content/file' : '<front>';
755
  }
756

    
757
  // Clear the page and block caches.
758
  cache_clear_all();
759
}
760

    
761
/**
762
 * Page callback: Form constructor for the file edit form.
763
 *
764
 * Path: file/%file/edit
765
 *
766
 * @param object $file
767
 *   A file object from file_load().
768
 *
769
 * @see file_entity_menu()
770
 *
771
 * @todo Rename this form to file_edit_form to ease into core.
772
 */
773
function file_entity_edit($form, &$form_state, $file) {
774
  drupal_set_title(t('<em>Edit @type</em> @title', array('@type' => $file->type, '@title' => $file->filename)), PASS_THROUGH);
775

    
776
  $form_state['file'] = $file;
777

    
778
  $form['#attributes']['class'][] = 'file-form';
779
  if (!empty($file->type)) {
780
    $form['#attributes']['class'][] = 'file-' . $file->type . '-form';
781
  }
782

    
783
  // Basic file information.
784
  // These elements are just values so they are not even sent to the client.
785
  foreach (array('fid', 'type', 'uid', 'timestamp') as $key) {
786
    $form[$key] = array(
787
      '#type' => 'value',
788
      '#value' => isset($file->$key) ? $file->$key : NULL,
789
    );
790
  }
791

    
792
  $form['filename'] = array(
793
    '#type' => 'textfield',
794
    '#title' => t('Name'),
795
    '#default_value' => $file->filename,
796
    '#required' => TRUE,
797
    '#maxlength' => 255,
798
    '#weight' => -10,
799
  );
800

    
801
  // Add a 'replace this file' upload field if the file is writeable.
802
  if (file_entity_file_is_writeable($file)) {
803
    // Set up replacement file validation.
804
    $replacement_options = !empty($form_state['#upload_options']) ? $form_state['#upload_options'] : array();
805

    
806
    // The replacement file must have an extension valid for the original type.
807
    $file_extensions = array();
808
    $file_type_name = isset($file->type) ? $file->type : file_get_type($file);
809
    if (!empty($replacement_options['file_extensions'])) {
810
      $file_extensions = explode(' ', $replacement_options['file_extensions']);
811
    }
812
    elseif ($file_type_name && ($file_type = file_type_load($file_type_name))) {
813
      $file_extensions = file_type_get_valid_extensions($file_type);
814
    }
815

    
816
    // Set allowed file extensions.
817
    if (!empty($file_extensions)) {
818
      // Set to type based file extensions.
819
      $replacement_options['file_extensions'] = implode(' ', $file_extensions);
820
    }
821
    else {
822
      // Fallback to the extension of the current file.
823
      $replacement_options['file_extensions'] = pathinfo($file->uri, PATHINFO_EXTENSION);
824
    }
825

    
826
    $form['replace_upload'] = array(
827
      '#type' => 'file',
828
      '#title' => t('Replace file'),
829
      '#description' => t('This file will replace the existing file. This action cannot be undone.'),
830
      '#upload_validators' => file_entity_get_upload_validators($replacement_options),
831
      '#pre_render' => array('file_entity_upload_validators_pre_render'),
832
    );
833
    $form['replace_keep_original_filename'] = array(
834
      '#type' => 'checkbox',
835
      '#title' => t('Keep original filename'),
836
      '#default_value' => variable_get('file_entity_file_replace_options_keep_original_filename', FALSE),
837
      '#description' => t('Rename the newly uploaded file to the name of the original file. This action cannot be undone.'),
838
    );
839
  }
840

    
841
  $form['preview'] = file_view_file($file, 'preview');
842

    
843
  $form['additional_settings'] = array(
844
    '#type' => 'vertical_tabs',
845
    '#weight' => 99,
846
  );
847

    
848
  // File destination information for administrators.
849
  $form['destination'] = array(
850
    '#type' => 'fieldset',
851
    '#access' => user_access('administer files') && file_entity_file_is_writeable($file),
852
    '#title' => t('Destination'),
853
    '#collapsible' => TRUE,
854
    '#collapsed' => TRUE,
855
    '#group' => 'additional_settings',
856
    '#attributes' => array(
857
      'class' => array('file-form-destination'),
858
    ),
859
    '#attached' => array(
860
      'js' => array(
861
        drupal_get_path('module', 'file_entity') . '/file_entity.js',
862
      ),
863
    ),
864
  );
865

    
866
  $options = array();
867
  foreach (file_get_stream_wrappers(STREAM_WRAPPERS_WRITE_VISIBLE) as $scheme => $info) {
868
    $options[$scheme] = check_plain($info['name']);
869
  }
870

    
871
  $form['destination']['scheme'] = array(
872
    '#type' => 'radios',
873
    '#title' => t('Destination'),
874
    '#options' => $options,
875
    '#default_value' => file_uri_scheme($file->uri),
876
  );
877

    
878
  // File user information for administrators.
879
  $form['user'] = array(
880
    '#type' => 'fieldset',
881
    '#access' => user_access('administer files'),
882
    '#title' => t('User information'),
883
    '#collapsible' => TRUE,
884
    '#collapsed' => TRUE,
885
    '#group' => 'additional_settings',
886
    '#attributes' => array(
887
      'class' => array('file-form-user'),
888
    ),
889
    '#attached' => array(
890
      'js' => array(
891
        drupal_get_path('module', 'file_entity') . '/file_entity.js',
892
        array(
893
          'type' => 'setting',
894
          'data' => array('anonymous' => variable_get('anonymous', t('Anonymous'))),
895
        ),
896
      ),
897
    ),
898
    '#weight' => 90,
899
  );
900
  $form['user']['name'] = array(
901
    '#type' => 'textfield',
902
    '#title' => t('Associated with'),
903
    '#maxlength' => 60,
904
    '#autocomplete_path' => 'user/autocomplete',
905
    '#default_value' => (!empty($file->uid) && $user = user_load($file->uid)) ? $user->name : '',
906
    '#weight' => -1,
907
    '#description' => t('Leave blank for %anonymous.', array('%anonymous' => variable_get('anonymous', t('Anonymous')))),
908
  );
909

    
910
  // Add the buttons.
911
  $form['actions'] = array('#type' => 'actions');
912
  $form['actions']['submit'] = array(
913
    '#type' => 'submit',
914
    '#value' => t('Save'),
915
    '#weight' => 5,
916
    '#submit' => array('file_entity_edit_submit'),
917
    '#validate' => array('file_entity_edit_validate'),
918
  );
919
  $form['actions']['delete'] = array(
920
    '#type' => 'submit',
921
    '#value' => t('Delete'),
922
    '#weight' => 10,
923
    '#submit' => array('file_entity_edit_delete_submit'),
924
    '#access' => file_entity_access('delete', $file),
925
  );
926

    
927
  // Build the URL for the cancel button taking into account that there might be
928
  // a "destination" that includes query string variables.
929
  $parameters = drupal_get_query_parameters();
930
  $destination = isset($parameters['destination']) ? $parameters['destination'] : 'file/' . $file->fid;
931
  $url = drupal_parse_url($destination);
932

    
933
  $form['actions']['cancel'] = array(
934
    '#type' => 'link',
935
    '#title' => t('Cancel'),
936
    '#href' => $url['path'],
937
    '#options' => array('query' => $url['query']),
938
    '#weight' => 15,
939
  );
940

    
941
  $langcode = function_exists('entity_language') ? entity_language('file', $file) : NULL;
942
  field_attach_form('file', $file, $form, $form_state, $langcode);
943

    
944
  return $form;
945
}
946

    
947
/**
948
 * Form validation handler for file_entity_edit().
949
 */
950
function file_entity_edit_validate($form, &$form_state) {
951
  $file = (object) $form_state['values'];
952

    
953
  // Validate the "associated user" field.
954
  if (!empty($file->name) && !($account = user_load_by_name($file->name))) {
955
    // The use of empty() is mandatory in the context of usernames
956
    // as the empty string denotes the anonymous user. In case we
957
    // are dealing with an anonymous user we set the user ID to 0.
958
    form_set_error('name', t('The username %name does not exist.', array('%name' => $file->name)));
959
  }
960

    
961
  // Handle the replacement file if uploaded.
962
  if (isset($form_state['values']['replace_upload'])) {
963
    // Save the file as a temporary file.
964
    $file = file_save_upload('replace_upload', $form['replace_upload']['#upload_validators']);
965
    if (!empty($file)) {
966
      // Put the temporary file in form_values so we can save it on submit.
967
      $form_state['values']['replace_upload'] = $file;
968
    }
969
    elseif ($file === FALSE) {
970
      // File uploaded failed.
971
      form_set_error('replace_upload', t('The replacement file could not be uploaded.'));
972
    }
973
  }
974

    
975
  // Run entity form validation.
976
  entity_form_field_validate('file', $form, $form_state);
977
}
978

    
979
/**
980
 * Form submission handler for the 'Save' button for file_entity_edit().
981
 */
982
function file_entity_edit_submit($form, &$form_state) {
983
  $file = $form_state['file'];
984
  $orphaned_uri = '';
985

    
986
  // Check if a replacement file has been uploaded.
987
  if (!empty($form_state['values']['replace_upload'])) {
988
    $replacement = $form_state['values']['replace_upload'];
989
    // Existing image metadata is stored in $file->height and $file->width.
990
    // Loop through the replacement metadata and update existing values.
991
    if (!empty($replacement->metadata)) {
992
      foreach ($replacement->metadata as $metadata_key => $metadata_value) {
993
        if (isset($file->{$metadata_key})) {
994
          $file->{$metadata_key} = $metadata_value;
995
        }
996
      }
997
    }
998
    // Move file from temp to permanent home.
999
    if (!empty($form_state['values']['replace_keep_original_filename'])
1000
    && $form_state['values']['replace_keep_original_filename']) {
1001
      $destination_uri = rtrim($file->uri, drupal_basename($file->uri)) . drupal_basename($file->uri);
1002
    }
1003
    else {
1004
      $destination_uri = rtrim($file->uri, drupal_basename($file->uri)) . drupal_basename($replacement->uri);
1005
    }
1006
    $replace_mode = $destination_uri == $file->uri ? FILE_EXISTS_REPLACE : FILE_EXISTS_RENAME;
1007
    if ($new_file_uri = file_unmanaged_copy($replacement->uri, $destination_uri, $replace_mode)) {
1008
      // @todo Add watchdog() about replaced file here?
1009

    
1010
      // Remove temporary file.
1011
      file_delete($replacement);
1012

    
1013
      // Update if the uri target has changed.
1014
      if ($new_file_uri != $file->uri) {
1015
        // Store the original file uri to delete if save is successful.
1016
        $orphaned_uri = $file->uri;
1017

    
1018
        // Update file entity uri.
1019
        $file->uri = $new_file_uri;
1020
      }
1021
    }
1022
  }
1023

    
1024
  // Run entity form submit handling and save the file.
1025
  entity_form_submit_build_entity('file', $file, $form, $form_state);
1026

    
1027
  // A user might assign the associated user by entering a user name in the file
1028
  // edit form, which we then need to translate to a user ID.
1029
  if (isset($file->name)) {
1030
    // The use of isset() is mandatory in the context of user IDs, because
1031
    // user ID 0 denotes the anonymous user.
1032
    if ($user = user_load_by_name($file->name)) {
1033
      $file->uid = $user->uid;
1034
    }
1035
    else {
1036
      // Anonymous user.
1037
      $file->uid = 0;
1038
    }
1039
  }
1040
  elseif ($file->uid) {
1041
    $user = user_load($file->uid);
1042
    $file->name = $user->name;
1043
  }
1044

    
1045
  if (file_uri_scheme($file->uri) != $form_state['values']['scheme']) {
1046
    $file_destination = $form_state['values']['scheme'] . '://' . file_uri_target($file->uri);
1047
    $file_destination = file_stream_wrapper_uri_normalize($file_destination);
1048
    $file_destination_dirname = drupal_dirname($file_destination);
1049
    // Create the directory in case it doesn't exist.
1050
    file_prepare_directory($file_destination_dirname, FILE_CREATE_DIRECTORY);
1051
    if ($moved_file = file_move($file, $file_destination, FILE_EXISTS_RENAME)) {
1052
      // Only re-assign the file object if file_move() did not fail.
1053
      $file = $moved_file;
1054
    }
1055
  }
1056

    
1057
  file_save($file);
1058

    
1059
  $args = array(
1060
    '@type' => file_entity_type_get_name($file),
1061
    '%title' => entity_label('file', $file),
1062
  );
1063
  watchdog('file', '@type: updated %title.', $args);
1064
  drupal_set_message(t('@type %title has been updated.', $args));
1065

    
1066
  // Clean up orphaned file.
1067
  if (!empty($orphaned_uri)) {
1068
    file_unmanaged_delete($orphaned_uri);
1069

    
1070
    $args['@orphaned'] = file_uri_target($orphaned_uri);
1071
    watchdog('file', '@type: deleted orphaned file @orphaned for %title.', $args);
1072
    drupal_set_message(t('The replaced @type @orphaned has been deleted.', $args));
1073
  }
1074

    
1075
  $form_state['redirect'] = 'file/' . $file->fid;
1076

    
1077
  // Clear the page and block caches.
1078
  cache_clear_all();
1079
}
1080

    
1081
/**
1082
 * Form submission handler for the 'Delete' button for file_entity_edit().
1083
 */
1084
function file_entity_edit_delete_submit($form, &$form_state) {
1085
  $fid = $form_state['values']['fid'];
1086
  $destination = array();
1087
  if (isset($_GET['destination'])) {
1088
    $destination = drupal_get_destination();
1089
    unset($_GET['destination']);
1090
  }
1091
  $form_state['redirect'] = array('file/' . $fid . '/delete', array('query' => $destination));
1092

    
1093
  // Clear the page and block caches.
1094
  cache_clear_all();
1095
}
1096

    
1097
/**
1098
 * Page callback: Form constructor for the file deletion confirmation form.
1099
 *
1100
 * Path: file/%file/delete
1101
 *
1102
 * @param object $file
1103
 *   A file object from file_load().
1104
 *
1105
 * @see file_entity_menu()
1106
 */
1107
function file_entity_delete_form($form, &$form_state, $file) {
1108
  $form_state['file'] = $file;
1109

    
1110
  $form['fid'] = array(
1111
    '#type' => 'value',
1112
    '#value' => $file->fid,
1113
  );
1114

    
1115
  $description = t('This action cannot be undone.');
1116
  if ($references = file_usage_list($file)) {
1117
    $description .= ' ' . t('This file is currently in use and may cause problems if deleted.');
1118
  }
1119

    
1120
  return confirm_form($form,
1121
    t('Are you sure you want to delete the file %title?', array(
1122
      '%title' => entity_label('file', $file),
1123
    )),
1124
    'file/' . $file->fid,
1125
    $description,
1126
    t('Delete')
1127
  );
1128
}
1129

    
1130
/**
1131
 * Form submission handler for file_entity_delete_form().
1132
 */
1133
function file_entity_delete_form_submit($form, &$form_state) {
1134
  if ($form_state['values']['confirm'] && $file = file_load($form_state['values']['fid'])) {
1135
    // Use file_delete_multiple() rather than file_delete() since we want to
1136
    // avoid unwanted validation and usage checking.
1137
    file_delete_multiple(array($file->fid));
1138

    
1139
    $args = array(
1140
      '@type' => file_entity_type_get_name($file),
1141
      '%title' => entity_label('file', $file),
1142
    );
1143
    watchdog('file', '@type: deleted %title.', $args);
1144
    drupal_set_message(t('@type %title has been deleted.', $args));
1145
  }
1146

    
1147
  $form_state['redirect'] = '<front>';
1148

    
1149
  // Clear the page and block caches.
1150
  cache_clear_all();
1151
}
1152

    
1153
/**
1154
 * Form constructor for file deletion confirmation form.
1155
 *
1156
 * @param array $files
1157
 *   An array of file objects.
1158
 */
1159
function file_entity_multiple_delete_form($form, &$form_state, array $files) {
1160
  $form['files'] = array(
1161
    '#prefix' => '<ul>',
1162
    '#suffix' => '</ul>',
1163
    '#tree' => TRUE,
1164
  );
1165

    
1166
  $files_have_usage = FALSE;
1167
  foreach ($files as $fid => $file) {
1168
    $title = entity_label('file', $file);
1169
    $usage = file_usage_list($file);
1170
    if (!empty($usage)) {
1171
      $files_have_usage = TRUE;
1172
      $title = t('@title (in use)', array('@title' => $title));
1173
    }
1174
    else {
1175
      $title = check_plain($title);
1176
    }
1177
    $form['files'][$fid] = array(
1178
      '#type' => 'hidden',
1179
      '#value' => $fid,
1180
      '#prefix' => '<li>',
1181
      '#suffix' => $title . "</li>\n",
1182
    );
1183
  }
1184

    
1185
  $form['operation'] = array(
1186
    '#type' => 'hidden',
1187
    '#value' => 'delete',
1188
  );
1189

    
1190
  $description = t('This action cannot be undone.');
1191
  if ($files_have_usage) {
1192
    $description .= ' ' . t('Some of the files are currently in use and may cause problems if deleted.');
1193
  }
1194

    
1195
  return confirm_form(
1196
    $form,
1197
    format_plural(count($files), 'Are you sure you want to delete this file?', 'Are you sure you want to delete these files?'),
1198
    'admin/content/file',
1199
    $description,
1200
    t('Delete')
1201
  );
1202
}
1203

    
1204
/**
1205
 * Form submission handler for file_entity_multiple_delete_form().
1206
 */
1207
function file_entity_multiple_delete_form_submit($form, &$form_state) {
1208
  if ($form_state['values']['confirm'] && $fids = array_keys($form_state['values']['files'])) {
1209
    file_delete_multiple($fids);
1210
    $count = count($fids);
1211
    watchdog('file', 'Deleted @count files.', array('@count' => $count));
1212
    drupal_set_message(format_plural($count, 'Deleted one file.', 'Deleted @count files.'));
1213
  }
1214
  $form_state['redirect'] = 'admin/content/file';
1215

    
1216
  // Clear the page and block caches.
1217
  cache_clear_all();
1218
}
1219

    
1220
/**
1221
 * Page callback for the file edit form.
1222
 *
1223
 * @deprecated
1224
 *   Use drupal_get_form('file_entity_edit')
1225
 */
1226
function file_entity_page_edit($file) {
1227
  return drupal_get_form('file_entity_edit', $file);
1228
}
1229

    
1230
/**
1231
 * Page callback for the file deletion confirmation form.
1232
 *
1233
 * @deprecated
1234
 *   Use drupal_get_form('file_entity_delete_form')
1235
 */
1236
function file_entity_page_delete($file) {
1237
  return drupal_get_form('file_entity_delete_form');
1238
}
1239

    
1240
/**
1241
 * Retrieves the upload validators for a file.
1242
 *
1243
 * @param array $options
1244
 *   (optional) An array of options for file validation.
1245
 *
1246
 * @return array
1247
 *   An array suitable for passing to file_save_upload() or for a managed_file
1248
 *   or upload element's '#upload_validators' property.
1249
 */
1250
function file_entity_get_upload_validators(array $options = array()) {
1251
  // Set up file upload validators.
1252
  $validators = array();
1253

    
1254
  // Validate file extensions. If there are no file extensions in $params and
1255
  // there are no Media defaults, there is no file extension validation.
1256
  if (!empty($options['file_extensions'])) {
1257
    $validators['file_validate_extensions'] = array($options['file_extensions']);
1258
  }
1259
  else {
1260
    $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'));
1261
  }
1262

    
1263
  // Cap the upload size according to the system or user defined limit.
1264
  $max_filesize = parse_size(file_upload_max_size());
1265
  $file_entity_max_filesize = parse_size(variable_get('file_entity_max_filesize', ''));
1266

    
1267
  // If the user defined a size limit, use the smaller of the two.
1268
  if (!empty($file_entity_max_filesize)) {
1269
    $max_filesize = min($max_filesize, $file_entity_max_filesize);
1270
  }
1271

    
1272
  if (!empty($options['max_filesize']) && $options['max_filesize'] < $max_filesize) {
1273
    $max_filesize = parse_size($options['max_filesize']);
1274
  }
1275

    
1276
  // There is always a file size limit due to the PHP server limit.
1277
  $validators['file_entity_validate_size_extensions'] = array($max_filesize);
1278

    
1279
  // Add image validators.
1280
  $options += array('min_resolution' => 0, 'max_resolution' => 0);
1281
  if ($options['min_resolution'] || $options['max_resolution']) {
1282
    $validators['file_validate_image_resolution'] = array($options['max_resolution'], $options['min_resolution']);
1283
  }
1284

    
1285
  // Add other custom upload validators from options.
1286
  if (!empty($options['upload_validators'])) {
1287
    $validators += $options['upload_validators'];
1288
  }
1289

    
1290
  return $validators;
1291
}
1292

    
1293
function file_entity_upload_archive_form($form, &$form_state) {
1294
  $options = array(
1295
    'file_extensions' => archiver_get_extensions(),
1296
  );
1297

    
1298
  $form['upload'] = array(
1299
    '#type' => 'managed_file',
1300
    '#title' => t('Upload an archive file'),
1301
    '#upload_location' => NULL, // Upload to the temporary directory.
1302
    '#upload_validators' => file_entity_get_upload_validators($options),
1303
    '#progress_indicator' => 'bar',
1304
    '#required' => TRUE,
1305
    '#pre_render' => array('file_managed_file_pre_render', 'file_entity_upload_validators_pre_render'),
1306
  );
1307

    
1308
  $form['pattern'] = array(
1309
    '#type' => 'textfield',
1310
    '#title' => t('Pattern'),
1311
    '#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.'),
1312
    '#default_value' => '.*',
1313
    '#required' => TRUE,
1314
  );
1315

    
1316
  $form['actions'] = array('#type' => 'actions');
1317
  $form['actions']['submit'] = array(
1318
    '#type' => 'submit',
1319
    '#value' => t('Submit'),
1320
  );
1321

    
1322
  form_load_include($form_state, 'inc', 'file_entity', 'file_entity.pages');
1323

    
1324
  return $form;
1325
}
1326

    
1327
/**
1328
 * Upload a file.
1329
 */
1330
function file_entity_upload_archive_form_submit($form, &$form_state) {
1331
  $form_state['files'] = array();
1332

    
1333
  if ($archive = file_load($form_state['values']['upload'])) {
1334
    if ($archiver = archiver_get_archiver($archive->uri)) {
1335
      $files = $archiver->listContents();
1336
      $destination = pathinfo($archive->filename, PATHINFO_FILENAME);
1337

    
1338
      // Prepare a temporary directory to extract the archive to before saving
1339
      // it is content.
1340
      $extract_location = 'temporary://' . $destination;
1341
      $extract_location = file_destination($extract_location, FILE_EXISTS_RENAME);
1342
      if (!file_prepare_directory($extract_location, FILE_MODIFY_PERMISSIONS | FILE_CREATE_DIRECTORY)) {
1343
        throw new Exception(t('Unable to prepare, a temporary directory %dir for extraction.', array('%dir' => $extract_location)));
1344
      }
1345

    
1346
      // Prepare target directory where files are going to be saved.
1347
      $target_dir = file_default_scheme() . '://' . $destination;
1348
      $target_dir = file_destination($target_dir, FILE_EXISTS_RENAME);
1349
      if (!file_prepare_directory($target_dir, FILE_MODIFY_PERMISSIONS | FILE_CREATE_DIRECTORY)) {
1350
        throw new Exception(t('Unable to prepare, a directory %dir for extraction.', array('%dir' => $target_dir)));
1351
      }
1352

    
1353
      $archiver->extract($extract_location);
1354
      $pattern = '/' . $form_state['values']['pattern'] . '/';
1355
      if ($extracted_files = file_scan_directory($extract_location, $pattern)) {
1356
        foreach ($extracted_files as $extracted_file) {
1357
          $file = new stdClass();
1358
          $file->fid = NULL;
1359
           $file->uid = $archive->uid;
1360
          $file->filename = $extracted_file->filename;
1361
          $file->origname = $extracted_file->filename;
1362
          $file->status = FILE_STATUS_PERMANENT;
1363
          $file->filemime = file_get_mimetype($extracted_file->filename);
1364

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

    
1368
          // Rename potentially executable files, to help prevent exploits (i.e. will
1369
          // rename filename.php.foo and filename.php to filename.php.foo.txt and
1370
          // filename.php.txt, respectively). Don't rename if 'allow_insecure_uploads'
1371
          // evaluates to TRUE.
1372
          if (!variable_get('allow_insecure_uploads', 0) && preg_match('/\.(php|pl|py|cgi|asp|js)(\.|$)/i', $file->filename) && (substr($file->filename, -4) != '.txt')) {
1373
            $file->filemime = 'text/plain';
1374
            $file->uri .= '.txt';
1375
            $file->filename .= '.txt';
1376
          }
1377

    
1378
          // prepare destination path for the extracted file while keeping the
1379
          // directory hierarchy of the file.
1380
          $destination = pathinfo($file->uri, PATHINFO_DIRNAME);
1381
          if (!file_prepare_directory($destination, FILE_MODIFY_PERMISSIONS | FILE_CREATE_DIRECTORY)) {
1382
            throw new Exception(t('Unable to prepare, a directory %dir for extraction.', array('%dir' => $destination)));
1383
          }
1384

    
1385
          if (!file_unmanaged_move($extracted_file->uri, $file->uri)) {
1386
            throw new Exception(t('Could not move uploaded file %file to destination %destination.', array('%file' => $extracted_file->filename, '%destination' => $file->uri)));
1387
            return FALSE;
1388
          }
1389

    
1390
          file_save($file);
1391
          $form_state['files'][$file->fid] = $file;
1392
        }
1393
      }
1394
      // Delete extract location
1395
      file_unmanaged_delete_recursive($extract_location);
1396
      drupal_set_message(t('Extracted %file and added @count new files.', array('%file' => $archive->filename, '@count' => count($files))));
1397
    }
1398
    else {
1399
      throw new Exception(t('Cannot extract %file, not a valid archive.', array('%file' => $archive->uri)));
1400
    }
1401
  }
1402

    
1403
  // Redirect to the file edit page.
1404
  if (file_entity_access('edit') && module_exists('multiform')) {
1405
    $destination = array('destination' => 'admin/content/file');
1406
    if (isset($_GET['destination'])) {
1407
      $destination = drupal_get_destination();
1408
      unset($_GET['destination']);
1409
    }
1410
    $form_state['redirect'] = array('admin/content/file/edit-multiple/' . implode(' ', array_keys($form_state['files'])), array('query' => $destination));
1411
  }
1412
  else {
1413
    $form_state['redirect'] = 'admin/content/file';
1414
  }
1415
}