Project

General

Profile

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

root / drupal7 / sites / all / modules / file_entity / file_entity.pages.inc @ 9a28ac3f

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
  $form['upload'] = array(
126
    '#type' => 'managed_file',
127
    '#title' => t('Upload a new file'),
128
    '#upload_location' => file_entity_upload_destination_uri($options),
129
    '#upload_validators' => file_entity_get_upload_validators($options),
130
    '#progress_indicator' => 'bar',
131
    '#required' => TRUE,
132
    '#pre_render' => array('file_managed_file_pre_render', 'file_entity_upload_validators_pre_render'),
133
    '#default_value' => isset($form_state['storage']['upload']) ? $form_state['storage']['upload'] : NULL,
134
  );
135

    
136
  $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>'));
137

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

    
141
  if (!empty($extensions)) {
142

    
143
    $limits = '';
144

    
145
    foreach ($extensions as $position => $text) {
146
      $matches = array();
147
      preg_match('/(.*)\|(.*)/', $text, $matches);
148

    
149
      if (is_array($matches) && count($matches) == 3) {
150
        $extension = $matches[1];
151
        $filesize = $matches[2];
152

    
153
        $limits .= $extension . ': ' . $filesize . ',';
154
      }
155
    }
156

    
157
    $limits = rtrim($limits, ',');
158

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

    
161
  }
162

    
163
  $form['actions'] = array('#type' => 'actions');
164
  $form['actions']['next'] = array(
165
    '#type' => 'submit',
166
    '#value' => t('Next'),
167
  );
168

    
169
  form_load_include($form_state, 'inc', 'file_entity', 'file_entity.pages');
170

    
171
  return $form;
172
}
173

    
174
function file_entity_validate_size_extensions(stdClass $file, $file_limit = 0, $user_limit = 0) {
175
  global $user;
176
  $errors = array();
177

    
178
  // Current file extension.
179
  $current_extension = pathinfo($file->filename, PATHINFO_EXTENSION);
180

    
181
  // Get list of extensions.
182
  $extensions = explode("\n", variable_get('file_entity_max_filesize_extensions'));
183

    
184
  if ($extensions) {
185
    foreach ($extensions as $position => $text) {
186
      $matches = array();
187
      preg_match('/(.*)\|(.*)/', $text, $matches);
188

    
189
      if (is_array($matches) && count($matches) == 3) {
190
        $extension = $matches[1];
191
        $filesize = $matches[2];
192

    
193
        if (strtolower($extension) == strtolower($current_extension)) {
194
          $file_limit = parse_size($filesize);
195
        }
196
      }
197
    }
198
  }
199

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

    
204
  // Save a query by only calling file_space_used() when a limit is provided.
205
  if ($user_limit && (file_space_used($user->uid) + $file->filesize) > $user_limit) {
206
    $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)));
207
  }
208

    
209
  return $errors;
210
}
211

    
212
/**
213
 * Generate form fields for the second step in the add file wizard.
214
 */
215
function file_entity_add_upload_step_filetype($form, &$form_state, array $options = array()) {
216
  $file = file_load($form_state['storage']['upload']);
217
  $selected_files = $form['#options']['types'];
218

    
219
  $form['type'] = array(
220
    '#type' => 'radios',
221
    '#title' => t('File type'),
222
    '#options' => file_entity_get_filetype_candidates($file, $selected_files),
223
    '#default_value' => isset($form_state['storage']['type']) ? $form_state['storage']['type'] : NULL,
224
    '#required' => TRUE,
225
  );
226

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

    
239
  return $form;
240
}
241

    
242
/**
243
 * Generate form fields for the third step in the add file wizard.
244
 */
245
function file_entity_add_upload_step_scheme($form, &$form_state, array $options = array()) {
246
  $file = file_load($form_state['storage']['upload']);
247

    
248
  $schemes = array();
249
  foreach (file_get_stream_wrappers(STREAM_WRAPPERS_WRITE_VISIBLE) as $scheme => $info) {
250
    $schemes[$scheme] = check_plain($info['description']);
251
  }
252

    
253
  // Remove any schemes not found in the instance settings.
254
  if (!empty($options['schemes'])) {
255
    $schemes = array_intersect_key($schemes, array_flip($options['schemes']));
256
  }
257

    
258
  // Determine which scheme to use as the default value.
259
  if (isset($form_state['storage']['scheme'])) {
260
    $fallback_scheme = $form_state['storage']['scheme'];
261
  }
262
  elseif (!empty($options['uri_scheme'])) {
263
    $fallback_scheme = $options['uri_scheme'];
264
  }
265
  else {
266
    $fallback_scheme = file_default_scheme();
267
  }
268

    
269
  $form['scheme'] = array(
270
    '#type' => 'radios',
271
    '#title' => t('Destination'),
272
    '#options' => $schemes,
273
    '#default_value' => $fallback_scheme,
274
    '#required' => TRUE,
275
  );
276

    
277
  $form['actions'] = array('#type' => 'actions');
278
  $form['actions']['previous'] = array(
279
    '#type' => 'submit',
280
    '#value' => t('Previous'),
281
    '#limit_validation_errors' => array(),
282
    '#submit' => array('file_entity_add_upload_submit'),
283
  );
284
  $form['actions']['next'] = array(
285
    '#type' => 'submit',
286
    '#value' => t('Next'),
287
  );
288

    
289
  return $form;
290
}
291

    
292
/**
293
 * Generate form fields for the fourth step in the add file wizard.
294
 */
295
function file_entity_add_upload_step_fields($form, &$form_state, array $options = array()) {
296
  // Load the file and overwrite the filetype set on the previous screen.
297
  $file = file_load($form_state['storage']['upload']);
298
  $file->type = $form_state['storage']['type'];
299

    
300
  // Let users modify the filename here.
301
  $form['filename'] = array(
302
    '#type' => 'textfield',
303
    '#title' => t('Name'),
304
    '#default_value' => $file->filename,
305
    '#required' => TRUE,
306
    '#maxlength' => 255,
307
    '#weight' => -10,
308
  );
309

    
310
  // Add fields.
311
  field_attach_form('file', $file, $form, $form_state);
312

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

    
325
  return $form;
326
}
327

    
328
/**
329
 * Page callback to show file usage information.
330
 */
331
function file_entity_usage_page($file) {
332
  drupal_set_title(t('<em>Usage of @type</em> @title', array('@type' => $file->type, '@title' => $file->filename)), PASS_THROUGH);
333

    
334
  $rows = array();
335
  $occured_entities = array();
336

    
337
  // Determine all of the locations where a file is used, then loop through the
338
  // occurrences and filter out any duplicates.
339
  foreach (file_usage_list($file) as $type) {
340
    foreach ($type as $entity_type => $entity_ids) {
341
      // There are cases where the actual entity doesn't exist.
342
      // We have to handle this.
343
      $entity_info = entity_get_info($entity_type);
344
      $entities = empty($entity_info) ? NULL : entity_load($entity_type, array_keys($entity_ids));
345

    
346
      foreach ($entity_ids as $entity_id => $count) {
347
        // If this entity has already been listed in the table, just add any
348
        // additional usage to the total count column in the table row and
349
        // continue on to the next iteration of the loop.
350
        if (isset($occured_entities[$entity_type][$entity_id])) {
351
          $rows[$occured_entities[$entity_type][$entity_id]][3] += $count;
352
          continue;
353
        }
354

    
355
        // Retrieve the label and the URI of the entity.
356
        $label = empty($entities[$entity_id]) ? t('(entity not loaded)') : entity_label($entity_type, $entities[$entity_id]);
357
        if (empty($label)) {
358
          $label = t('(entity label not loaded)');
359
        }
360
        $entity_uri = empty($entities[$entity_id]) ? NULL : entity_uri($entity_type, $entities[$entity_id]);
361

    
362
        // Link the label to the URI when possible.
363
        if (!empty($entity_uri['path']) && $entity_type != 'paragraphs_item') {
364
          if (empty($entity_uri['options'])) {
365
            $entity_label = l($label, $entity_uri['path']);
366
          }
367
          else {
368
            $entity_label = l($label, $entity_uri['path'], $entity_uri['options']);
369
          }
370
        }
371
        // For paragraphs items, we are searching for usages in nodes.
372
        elseif ($entity_type == 'paragraphs_item') {
373
          $paragraph_fields = field_read_fields(array('type' => 'paragraphs'));
374
          foreach ($paragraph_fields as $paragraph_field) {
375
            $field_name = $paragraph_field['field_name'];
376
            $query = new EntityFieldQuery();
377
            $query->entityCondition('entity_type', 'node')
378
              ->fieldCondition($field_name, 'value', $entity_id, '=');
379
            $nid = $query->execute();
380
            if (!empty($nid)) {
381
              $nid = implode(array_keys($nid['node']));
382
              if ($nid) {
383
                $node = node_load($nid);
384
                $entity_label = l($node->title, 'node/' . $nid);
385
              }
386
            }
387
          }
388
        }
389
        else {
390
          $entity_label = check_plain($label);
391
        }
392

    
393
        $rows[] = array(
394
          $entity_label,
395
          $entity_id,
396
          $entity_type,
397
          $count,
398
        );
399

    
400
        // Record the occurrence of the entity to ensure that it isn't listed in
401
        // the table again.
402
        $occured_entities[$entity_type][$entity_id] = count($rows) - 1;
403
      }
404
    }
405
  }
406

    
407
  $header = array(
408
    t('Entity label'),
409
    t('Entity ID'),
410
    t('Entity type'),
411
    t('Times this file used by this entity'),
412
  );
413
  $build['usage_table'] = array(
414
    '#theme' => 'table',
415
    '#header' => $header,
416
    '#rows' => $rows,
417
    '#caption' => t('This table lists all of the places where @filename is used.', array('@filename' => $file->filename)),
418
    '#empty' => t('This file is not currently used.'),
419
  );
420

    
421
  return $build;
422
}
423

    
424
/**
425
 * Get the candidate filetypes for a given file.
426
 *
427
 * Only filetypes for which the user has access to create entities are returned.
428
 *
429
 * @param array $file
430
 *   An upload file array from form_state.
431
 *
432
 * @return array
433
 *   An array of file type bundles that support the file's mime type.
434
 */
435
function file_entity_get_filetype_candidates($file, $selected_files = array()) {
436
  $types = module_invoke_all('file_type', $file);
437
  drupal_alter('file_type', $types, $file);
438

    
439
  // If no file types are selected in field instance settings, allow all
440
  // available types.
441
  if (!empty($selected_files)) {
442
    // Limit file type candidates to field allowed types.
443
    $types = array_intersect($types, $selected_files);
444
  }
445

    
446
  $candidates = array();
447
  foreach ($types as $type) {
448
    $file->type = $type;
449
    if (file_entity_access('create', $file)) {
450
      $candidates[$type] = file_entity_type_get_name($file);
451
    }
452
  }
453
  return $candidates;
454
}
455

    
456
/**
457
 * Submit handler for the add file form.
458
 */
459
function file_entity_add_upload_submit($form, &$form_state) {
460
  $form_state['storage'] = isset($form_state['storage']) ? $form_state['storage'] : array();
461
  $form_state['storage'] = array_merge($form_state['storage'], $form_state['values']);
462

    
463
  // Field selected allowed types.
464
  $selected_files = $form['#options']['types'];
465

    
466
  // This var is set to TRUE when we are ready to save the file.
467
  $save = FALSE;
468
  $trigger = $form_state['triggering_element']['#id'];
469
  $triggered_next = $trigger == 'edit-next' || (strpos($trigger, 'edit-next--') === 0);
470
  $triggered_previous = $trigger == 'edit-previous' || (strpos($trigger, 'edit-previous--') === 0);
471
  $step_delta = ($triggered_previous) ? -1 : 1;
472

    
473
  $steps_to_check = array(2, 3);
474
  if ($triggered_previous) {
475
    // If the previous button was hit,
476
    // the step checking order should be reversed 3, 2.
477
    $steps_to_check = array_reverse($steps_to_check);
478
  }
479

    
480
  foreach ($steps_to_check as $step) {
481
    // Check if we can skip step 2 and 3.
482
    if (($form['#step'] == $step - 1 && $triggered_next) || ($form['#step'] == $step + 1 && $triggered_previous)) {
483
      $file = file_load($form_state['storage']['upload']);
484
      if ($step == 2) {
485
        // Check if we can skip step 2.
486
        $candidates = file_entity_get_filetype_candidates($file, $selected_files);
487
        if (count($candidates) == 1) {
488
          $candidates_keys = array_keys($candidates);
489
          // There is only one possible filetype for this file.
490
          // Skip the second page.
491
          $form['#step'] += $step_delta;
492
          $form_state['storage']['type'] = reset($candidates_keys);
493
        }
494
        elseif (!$candidates || variable_get('file_entity_file_upload_wizard_skip_file_type', FALSE)) {
495
          // Do not assign the file a file type.
496
          $form['#step'] += $step_delta;
497
          $form_state['storage']['type'] = FILE_TYPE_NONE;
498
        }
499
      }
500
      else {
501
        // Check if we can skip step 3.
502
        $options = $form['#options'];
503
        $schemes = file_get_stream_wrappers(STREAM_WRAPPERS_WRITE_VISIBLE);
504

    
505
        // Remove any schemes not found in the instance settings.
506
        if (!empty($options['schemes'])) {
507
          $schemes = array_intersect_key($schemes, $options['schemes']);
508
        }
509

    
510
        if (!file_entity_file_is_writeable($file)) {
511
          // The file is read-only (remote) and must use its provided scheme.
512
          $form['#step'] += $step_delta;
513
          $form_state['storage']['scheme'] = file_uri_scheme($file->uri);
514
        }
515
        elseif (count($schemes) == 1) {
516
          // There is only one possible stream wrapper for this file.
517
          // Skip the third page.
518
          $form['#step'] += $step_delta;
519
          $form_state['storage']['scheme'] = key($schemes);
520
        }
521
        elseif (variable_get('file_entity_file_upload_wizard_skip_scheme', FALSE)) {
522
          $form['#step'] += $step_delta;
523

    
524
          // Fallback to the URI scheme specified in the field settings
525
          // otherwise use the default file scheme.
526
          if (!empty($options['uri_scheme'])) {
527
            $form_state['storage']['scheme'] = $options['uri_scheme'];
528
          }
529
          else {
530
            $form_state['storage']['scheme'] = file_default_scheme();
531
          }
532
        }
533
      }
534
    }
535
  }
536

    
537
  // We have the filetype, check if we can skip step 4.
538
  if ($form['#step'] == 3 && $triggered_next) {
539
    $file = file_load($form_state['storage']['upload']);
540
    $form_state['file'] = $file;
541
    if (!field_info_instances('file', $form_state['storage']['type'])) {
542
      // This filetype doesn't have fields, save the file.
543
      $save = TRUE;
544
    }
545
    elseif (variable_get('file_entity_file_upload_wizard_skip_fields', FALSE)) {
546
      // Save the file with blanks fields.
547
      $save = TRUE;
548
    }
549
    // Allow other modules to choose to skip or complete step 4.
550
    drupal_alter('file_entity_file_upload_skip_fields', $save, $form_state);
551
  }
552

    
553
  // Form id's can vary depending on how many other forms are displayed, so we
554
  // need to do string comparissons. e.g edit-submit--2.
555
  if ($triggered_next) {
556
    $form_state['step'] = $form['#step'] + 1;
557
  }
558
  elseif ($triggered_previous) {
559
    $form_state['step'] = $form['#step'] - 1;
560
  }
561
  elseif (strpos($trigger, 'edit-submit') !== FALSE) {
562
    $save = TRUE;
563
  }
564

    
565
  if ($save) {
566
    $file = file_load($form_state['storage']['upload']);
567
    if ($file) {
568
      if (file_uri_scheme($file->uri) != $form_state['storage']['scheme']) {
569
        $file_destination = $form_state['storage']['scheme'] . '://' . file_uri_target($file->uri);
570
        $file_destination = file_stream_wrapper_uri_normalize($file_destination);
571
        if ($moved_file = file_move($file, $file_destination, FILE_EXISTS_RENAME)) {
572
          // Only re-assign the file object if file_move() did not fail.
573
          $file = $moved_file;
574
        }
575
      }
576
      $file->type = $form_state['storage']['type'];
577
      $file->display = TRUE;
578

    
579
      // Change the file from temporary to permanent.
580
      $file->status = FILE_STATUS_PERMANENT;
581

    
582
      // Save the form fields.
583
      // Keep in mind that the values for the Field API fields must be in
584
      // $form_state['values'] and not in ['storage']. This is true as long as
585
      // the fields are on the last page of the multi step form.
586
      entity_form_submit_build_entity('file', $file, $form, $form_state);
587

    
588
      file_save($file);
589
      $form_state['file'] = $file;
590
      drupal_set_message(t('@type %name was uploaded.', array('@type' => file_entity_type_get_name($file), '%name' => $file->filename)));
591
    }
592
    else {
593
      drupal_set_message(t('An error occurred and no file was uploaded.'), 'error');
594
      return;
595
    }
596

    
597
    // Figure out destination.
598
    if (user_access('administer files')) {
599
      $path = 'admin/content/file';
600
    }
601
    else {
602
      $path = 'file/' . $file->fid;
603
    }
604
    $form_state['redirect'] = $path;
605
  }
606
  else {
607
    $form_state['rebuild'] = TRUE;
608
  }
609

    
610
  // Clear the page and block caches.
611
  cache_clear_all();
612
}
613

    
614
/**
615
 * Determines the upload location for the file add upload form.
616
 *
617
 * @param array $params
618
 *   An array of parameters from the media browser.
619
 * @param array $data
620
 *   (optional) An array of token objects to pass to token_replace().
621
 *
622
 * @return string
623
 *   A file directory URI with tokens replaced.
624
 *
625
 * @see token_replace()
626
 */
627
function file_entity_upload_destination_uri(array $params, array $data = array()) {
628
  $params += array(
629
    'uri_scheme' => file_default_scheme(),
630
    'file_directory' => variable_get('file_entity_default_file_directory', ''),
631
  );
632

    
633
  $destination = trim($params['file_directory'], '/');
634

    
635
  // Replace tokens.
636
  $destination = decode_entities(token_replace($destination, $data));
637

    
638
  return $params['uri_scheme'] . '://' . $destination;
639
}
640

    
641
/**
642
 * Form for uploading multiple files.
643
 */
644
function file_entity_add_upload_multiple($form, &$form_state, $params = array()) {
645
  $form = file_entity_add_upload($form, $form_state, $params);
646
  unset($form['upload']['#title']);
647
  // The validators will be set from plupload anyway. This isn't pretty,
648
  // but don't allow it to show up twice.
649
  unset($form['upload']['#description']);
650

    
651
  $form['upload']['#type'] = 'plupload';
652

    
653
  // Ensure that we call the plupload_element_pre_render function.
654
  // If it isn't called, it doesn't set the JS settings that transfers the
655
  // list of allowed file extensions to the PLUpload widget.
656
  // We override the 'file_entity_upload_validators_pre_render' setting if it
657
  // exists, because both pre-render hooks adds the upload-help with list of
658
  // allowed file extensions.
659
  $index = array_search('file_entity_upload_validators_pre_render', $form['upload']['#pre_render']);
660
  if ($index !== FALSE) {
661
    $form['upload']['#pre_render'][$index] = 'plupload_element_pre_render';
662
  }
663
  else {
664
    $form['upload']['#pre_render'][] = 'plupload_element_pre_render';
665
  }
666

    
667
  $form['submit']['#value'] = t('Start upload');
668
  return $form;
669
}
670

    
671
/**
672
 * Submit handler for the multiple upload form.
673
 */
674
function file_entity_add_upload_multiple_submit($form, &$form_state) {
675
  $upload_location = !empty($form['upload']['#upload_location']) ?
676
    $form['upload']['#upload_location'] . '/' :
677
    variable_get('file_default_scheme', 'public') . '://';
678

    
679
  // Ensure writable destination directory for the files.
680
  file_prepare_directory($upload_location, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
681

    
682
  // We can't use file_save_upload() because of
683
  // http://www.jacobsingh.name/content/tight-coupling-no-not.
684
  foreach ($form_state['values']['upload'] as $uploaded_file) {
685
    if ($uploaded_file['status'] == 'done') {
686
      $source = $uploaded_file['tmppath'];
687
      $destination = file_stream_wrapper_uri_normalize($upload_location . $uploaded_file['name']);
688
      // Rename it to its original name, and put it in its final home.
689
      // Note - not using file_move here because if we call file_get_mime
690
      // (in file_uri_to_object) while it has a .tmp extension, it horks.
691
      $destination = file_unmanaged_move($source, $destination, FILE_EXISTS_RENAME);
692

    
693
      $file = file_uri_to_object($destination);
694
      $file->status = FILE_STATUS_PERMANENT;
695
      file_save($file);
696

    
697
      $form_state['files'][$file->fid] = $file;
698
    }
699
    else {
700
      // @todo: move this to element validate or something.
701
      form_set_error('pud', t('The specified file %name could not be uploaded.', array('%name' => $uploaded_file['name'])));
702
    }
703
  }
704

    
705
  // Redirect to the file edit page.
706
  if (file_entity_access('update', $file) && module_exists('media_bulk_upload')) {
707
    $destination = array();
708
    if (isset($_GET['destination'])) {
709
      $destination = drupal_get_destination();
710
      unset($_GET['destination']);
711
    }
712
    elseif (user_access('administer files')) {
713
      $destination = array('destination' => 'admin/content/file');
714
    }
715
    $form_state['redirect'] = array('admin/content/file/edit-multiple/' . implode(' ', array_keys($form_state['files'])), array('query' => $destination));
716
  }
717
  else {
718
    $form_state['redirect'] = user_access('administer files') ? 'admin/content/file' : '<front>';
719
  }
720

    
721
  // Clear the page and block caches.
722
  cache_clear_all();
723
}
724

    
725
/**
726
 * Page callback: Form constructor for the file edit form.
727
 *
728
 * Path: file/%file/edit
729
 *
730
 * @param object $file
731
 *   A file object from file_load().
732
 *
733
 * @see file_entity_menu()
734
 *
735
 * @todo Rename this form to file_edit_form to ease into core.
736
 */
737
function file_entity_edit($form, &$form_state, $file) {
738
  drupal_set_title(t('<em>Edit @type</em> @title', array('@type' => $file->type, '@title' => $file->filename)), PASS_THROUGH);
739

    
740
  $form_state['file'] = $file;
741

    
742
  $form['#attributes']['class'][] = 'file-form';
743
  if (!empty($file->type)) {
744
    $form['#attributes']['class'][] = 'file-' . $file->type . '-form';
745
  }
746

    
747
  // Basic file information.
748
  // These elements are just values so they are not even sent to the client.
749
  foreach (array('fid', 'type', 'uid', 'timestamp') as $key) {
750
    $form[$key] = array(
751
      '#type' => 'value',
752
      '#value' => isset($file->$key) ? $file->$key : NULL,
753
    );
754
  }
755

    
756
  $form['filename'] = array(
757
    '#type' => 'textfield',
758
    '#title' => t('Name'),
759
    '#default_value' => $file->filename,
760
    '#required' => TRUE,
761
    '#maxlength' => 255,
762
    '#weight' => -10,
763
  );
764

    
765
  // Add a 'replace this file' upload field if the file is writeable.
766
  if (file_entity_file_is_writeable($file)) {
767
    // Set up replacement file validation.
768
    $replacement_options = !empty($form_state['#upload_options']) ? $form_state['#upload_options'] : array();
769

    
770
    // The replacement file must have an extension valid for the original type.
771
    $file_extensions = array();
772
    $file_type_name = isset($file->type) ? $file->type : file_get_type($file);
773
    if (!empty($replacement_options['file_extensions'])) {
774
      $file_extensions = explode(' ', $replacement_options['file_extensions']);
775
    }
776
    elseif ($file_type_name && ($file_type = file_type_load($file_type_name))) {
777
      $file_extensions = file_type_get_valid_extensions($file_type);
778
    }
779

    
780
    // Set allowed file extensions.
781
    if (!empty($file_extensions)) {
782
      // Set to type based file extensions.
783
      $replacement_options['file_extensions'] = implode(' ', $file_extensions);
784
    }
785
    else {
786
      // Fallback to the extension of the current file.
787
      $replacement_options['file_extensions'] = pathinfo($file->uri, PATHINFO_EXTENSION);
788
    }
789

    
790
    $form['replace_upload'] = array(
791
      '#type' => 'file',
792
      '#title' => t('Replace file'),
793
      '#description' => t('This file will replace the existing file. This action cannot be undone.'),
794
      '#upload_validators' => file_entity_get_upload_validators($replacement_options),
795
      '#pre_render' => array('file_entity_upload_validators_pre_render'),
796
    );
797
    $form['replace_keep_original_filename'] = array(
798
      '#type' => 'checkbox',
799
      '#title' => t('Keep original filename'),
800
      '#default_value' => variable_get('file_entity_file_replace_options_keep_original_filename', FALSE),
801
      '#description' => t('Rename the newly uploaded file to the name of the original file. This action cannot be undone.'),
802
    );
803
  }
804

    
805
  $form['preview'] = file_view_file($file, 'preview');
806

    
807
  $form['additional_settings'] = array(
808
    '#type' => 'vertical_tabs',
809
    '#weight' => 99,
810
  );
811

    
812
  // File destination information for administrators.
813
  $form['destination'] = array(
814
    '#type' => 'fieldset',
815
    '#access' => user_access('administer files') && file_entity_file_is_writeable($file),
816
    '#title' => t('Destination'),
817
    '#collapsible' => TRUE,
818
    '#collapsed' => TRUE,
819
    '#group' => 'additional_settings',
820
    '#attributes' => array(
821
      'class' => array('file-form-destination'),
822
    ),
823
    '#attached' => array(
824
      'js' => array(
825
        drupal_get_path('module', 'file_entity') . '/file_entity.js',
826
      ),
827
    ),
828
  );
829

    
830
  $options = array();
831
  foreach (file_get_stream_wrappers(STREAM_WRAPPERS_WRITE_VISIBLE) as $scheme => $info) {
832
    $options[$scheme] = check_plain($info['name']);
833
  }
834

    
835
  $form['destination']['scheme'] = array(
836
    '#type' => 'radios',
837
    '#title' => t('Destination'),
838
    '#options' => $options,
839
    '#default_value' => file_uri_scheme($file->uri),
840
  );
841

    
842
  // File user information for administrators.
843
  $form['user'] = array(
844
    '#type' => 'fieldset',
845
    '#access' => user_access('administer files'),
846
    '#title' => t('User information'),
847
    '#collapsible' => TRUE,
848
    '#collapsed' => TRUE,
849
    '#group' => 'additional_settings',
850
    '#attributes' => array(
851
      'class' => array('file-form-user'),
852
    ),
853
    '#attached' => array(
854
      'js' => array(
855
        drupal_get_path('module', 'file_entity') . '/file_entity.js',
856
        array(
857
          'type' => 'setting',
858
          'data' => array('anonymous' => variable_get('anonymous', t('Anonymous'))),
859
        ),
860
      ),
861
    ),
862
    '#weight' => 90,
863
  );
864
  $form['user']['name'] = array(
865
    '#type' => 'textfield',
866
    '#title' => t('Associated with'),
867
    '#maxlength' => 60,
868
    '#autocomplete_path' => 'user/autocomplete',
869
    '#default_value' => (!empty($file->uid) && $user = user_load($file->uid)) ? $user->name : '',
870
    '#weight' => -1,
871
    '#description' => t('Leave blank for %anonymous.', array('%anonymous' => variable_get('anonymous', t('Anonymous')))),
872
  );
873

    
874
  // Add the buttons.
875
  $form['actions'] = array('#type' => 'actions');
876
  $form['actions']['submit'] = array(
877
    '#type' => 'submit',
878
    '#value' => t('Save'),
879
    '#weight' => 5,
880
    '#submit' => array('file_entity_edit_submit'),
881
    '#validate' => array('file_entity_edit_validate'),
882
  );
883
  $form['actions']['delete'] = array(
884
    '#type' => 'submit',
885
    '#value' => t('Delete'),
886
    '#weight' => 10,
887
    '#submit' => array('file_entity_edit_delete_submit'),
888
    '#access' => file_entity_access('delete', $file),
889
  );
890

    
891
  // Build the URL for the cancel button taking into account that there might be
892
  // a "destination" that includes query string variables.
893
  $parameters = drupal_get_query_parameters();
894
  $destination = isset($parameters['destination']) ? $parameters['destination'] : 'file/' . $file->fid;
895
  $url = drupal_parse_url($destination);
896

    
897
  $form['actions']['cancel'] = array(
898
    '#type' => 'link',
899
    '#title' => t('Cancel'),
900
    '#href' => $url['path'],
901
    '#options' => array('query' => $url['query']),
902
    '#weight' => 15,
903
  );
904

    
905
  $langcode = function_exists('entity_language') ? entity_language('file', $file) : NULL;
906
  field_attach_form('file', $file, $form, $form_state, $langcode);
907

    
908
  return $form;
909
}
910

    
911
/**
912
 * Form validation handler for file_entity_edit().
913
 */
914
function file_entity_edit_validate($form, &$form_state) {
915
  $file = (object) $form_state['values'];
916

    
917
  // Validate the "associated user" field.
918
  if (!empty($file->name) && !($account = user_load_by_name($file->name))) {
919
    // The use of empty() is mandatory in the context of usernames
920
    // as the empty string denotes the anonymous user. In case we
921
    // are dealing with an anonymous user we set the user ID to 0.
922
    form_set_error('name', t('The username %name does not exist.', array('%name' => $file->name)));
923
  }
924

    
925
  // Handle the replacement file if uploaded.
926
  if (isset($form_state['values']['replace_upload'])) {
927
    // Save the file as a temporary file.
928
    $file = file_save_upload('replace_upload', $form['replace_upload']['#upload_validators']);
929
    if (!empty($file)) {
930
      // Put the temporary file in form_values so we can save it on submit.
931
      $form_state['values']['replace_upload'] = $file;
932
    }
933
    elseif ($file === FALSE) {
934
      // File uploaded failed.
935
      form_set_error('replace_upload', t('The replacement file could not be uploaded.'));
936
    }
937
  }
938

    
939
  // Run entity form validation.
940
  entity_form_field_validate('file', $form, $form_state);
941
}
942

    
943
/**
944
 * Form submission handler for the 'Save' button for file_entity_edit().
945
 */
946
function file_entity_edit_submit($form, &$form_state) {
947
  $file = $form_state['file'];
948
  $orphaned_uri = '';
949

    
950
  // Check if a replacement file has been uploaded.
951
  if (!empty($form_state['values']['replace_upload'])) {
952
    $replacement = $form_state['values']['replace_upload'];
953
    // Existing image metadata is stored in $file->height and $file->width.
954
    // Loop through the replacement metadata and update existing values.
955
    if (!empty($replacement->metadata)) {
956
      foreach ($replacement->metadata as $metadata_key => $metadata_value) {
957
        if (isset($file->{$metadata_key})) {
958
          $file->{$metadata_key} = $metadata_value;
959
        }
960
      }
961
    }
962
    // Move file from temp to permanent home.
963
    if (!empty($form_state['values']['replace_keep_original_filename'])
964
    && $form_state['values']['replace_keep_original_filename']) {
965
      $destination_uri = rtrim($file->uri, drupal_basename($file->uri)) . drupal_basename($file->uri);
966
    }
967
    else {
968
      $destination_uri = rtrim($file->uri, drupal_basename($file->uri)) . drupal_basename($replacement->uri);
969
    }
970
    $replace_mode = $destination_uri == $file->uri ? FILE_EXISTS_REPLACE : FILE_EXISTS_RENAME;
971
    if ($new_file_uri = file_unmanaged_copy($replacement->uri, $destination_uri, $replace_mode)) {
972
      // @todo Add watchdog() about replaced file here?
973

    
974
      // Remove temporary file.
975
      file_delete($replacement);
976

    
977
      // Update if the uri target has changed.
978
      if ($new_file_uri != $file->uri) {
979
        // Store the original file uri to delete if save is successful.
980
        $orphaned_uri = $file->uri;
981

    
982
        // Update file entity uri.
983
        $file->uri = $new_file_uri;
984
      }
985
    }
986
  }
987

    
988
  // Run entity form submit handling and save the file.
989
  entity_form_submit_build_entity('file', $file, $form, $form_state);
990

    
991
  // A user might assign the associated user by entering a user name in the file
992
  // edit form, which we then need to translate to a user ID.
993
  if (isset($file->name)) {
994
    // The use of isset() is mandatory in the context of user IDs, because
995
    // user ID 0 denotes the anonymous user.
996
    if ($user = user_load_by_name($file->name)) {
997
      $file->uid = $user->uid;
998
    }
999
    else {
1000
      // Anonymous user.
1001
      $file->uid = 0;
1002
    }
1003
  }
1004
  elseif ($file->uid) {
1005
    $user = user_load($file->uid);
1006
    $file->name = $user->name;
1007
  }
1008

    
1009
  if (file_uri_scheme($file->uri) != $form_state['values']['scheme']) {
1010
    $file_destination = $form_state['values']['scheme'] . '://' . file_uri_target($file->uri);
1011
    $file_destination = file_stream_wrapper_uri_normalize($file_destination);
1012
    $file_destination_dirname = drupal_dirname($file_destination);
1013
    // Create the directory in case it doesn't exist.
1014
    file_prepare_directory($file_destination_dirname, FILE_CREATE_DIRECTORY);
1015
    if ($moved_file = file_move($file, $file_destination, FILE_EXISTS_RENAME)) {
1016
      // Only re-assign the file object if file_move() did not fail.
1017
      $file = $moved_file;
1018
    }
1019
  }
1020

    
1021
  file_save($file);
1022

    
1023
  $args = array(
1024
    '@type' => file_entity_type_get_name($file),
1025
    '%title' => entity_label('file', $file),
1026
  );
1027
  watchdog('file', '@type: updated %title.', $args);
1028
  drupal_set_message(t('@type %title has been updated.', $args));
1029

    
1030
  // Clean up orphaned file.
1031
  if (!empty($orphaned_uri)) {
1032
    file_unmanaged_delete($orphaned_uri);
1033

    
1034
    $args['@orphaned'] = file_uri_target($orphaned_uri);
1035
    watchdog('file', '@type: deleted orphaned file @orphaned for %title.', $args);
1036
    drupal_set_message(t('The replaced @type @orphaned has been deleted.', $args));
1037
  }
1038

    
1039
  $form_state['redirect'] = 'file/' . $file->fid;
1040

    
1041
  // Clear the page and block caches.
1042
  cache_clear_all();
1043
}
1044

    
1045
/**
1046
 * Form submission handler for the 'Delete' button for file_entity_edit().
1047
 */
1048
function file_entity_edit_delete_submit($form, &$form_state) {
1049
  $fid = $form_state['values']['fid'];
1050
  $destination = array();
1051
  if (isset($_GET['destination'])) {
1052
    $destination = drupal_get_destination();
1053
    unset($_GET['destination']);
1054
  }
1055
  $form_state['redirect'] = array('file/' . $fid . '/delete', array('query' => $destination));
1056

    
1057
  // Clear the page and block caches.
1058
  cache_clear_all();
1059
}
1060

    
1061
/**
1062
 * Page callback: Form constructor for the file deletion confirmation form.
1063
 *
1064
 * Path: file/%file/delete
1065
 *
1066
 * @param object $file
1067
 *   A file object from file_load().
1068
 *
1069
 * @see file_entity_menu()
1070
 */
1071
function file_entity_delete_form($form, &$form_state, $file) {
1072
  $form_state['file'] = $file;
1073

    
1074
  $form['fid'] = array(
1075
    '#type' => 'value',
1076
    '#value' => $file->fid,
1077
  );
1078

    
1079
  $description = t('This action cannot be undone.');
1080
  if ($references = file_usage_list($file)) {
1081
    $description .= ' ' . t('This file is currently in use and may cause problems if deleted.');
1082
  }
1083

    
1084
  return confirm_form($form,
1085
    t('Are you sure you want to delete the file %title?', array(
1086
      '%title' => entity_label('file', $file),
1087
    )),
1088
    'file/' . $file->fid,
1089
    $description,
1090
    t('Delete')
1091
  );
1092
}
1093

    
1094
/**
1095
 * Form submission handler for file_entity_delete_form().
1096
 */
1097
function file_entity_delete_form_submit($form, &$form_state) {
1098
  if ($form_state['values']['confirm'] && $file = file_load($form_state['values']['fid'])) {
1099
    // Use file_delete_multiple() rather than file_delete() since we want to
1100
    // avoid unwanted validation and usage checking.
1101
    file_delete_multiple(array($file->fid));
1102

    
1103
    $args = array(
1104
      '@type' => file_entity_type_get_name($file),
1105
      '%title' => entity_label('file', $file),
1106
    );
1107
    watchdog('file', '@type: deleted %title.', $args);
1108
    drupal_set_message(t('@type %title has been deleted.', $args));
1109
  }
1110

    
1111
  $form_state['redirect'] = '<front>';
1112

    
1113
  // Clear the page and block caches.
1114
  cache_clear_all();
1115
}
1116

    
1117
/**
1118
 * Form constructor for file deletion confirmation form.
1119
 *
1120
 * @param array $files
1121
 *   An array of file objects.
1122
 */
1123
function file_entity_multiple_delete_form($form, &$form_state, array $files) {
1124
  $form['files'] = array(
1125
    '#prefix' => '<ul>',
1126
    '#suffix' => '</ul>',
1127
    '#tree' => TRUE,
1128
  );
1129

    
1130
  $files_have_usage = FALSE;
1131
  foreach ($files as $fid => $file) {
1132
    $title = entity_label('file', $file);
1133
    $usage = file_usage_list($file);
1134
    if (!empty($usage)) {
1135
      $files_have_usage = TRUE;
1136
      $title = t('@title (in use)', array('@title' => $title));
1137
    }
1138
    else {
1139
      $title = check_plain($title);
1140
    }
1141
    $form['files'][$fid] = array(
1142
      '#type' => 'hidden',
1143
      '#value' => $fid,
1144
      '#prefix' => '<li>',
1145
      '#suffix' => $title . "</li>\n",
1146
    );
1147
  }
1148

    
1149
  $form['operation'] = array(
1150
    '#type' => 'hidden',
1151
    '#value' => 'delete',
1152
  );
1153

    
1154
  $description = t('This action cannot be undone.');
1155
  if ($files_have_usage) {
1156
    $description .= ' ' . t('Some of the files are currently in use and may cause problems if deleted.');
1157
  }
1158

    
1159
  return confirm_form(
1160
    $form,
1161
    format_plural(count($files), 'Are you sure you want to delete this file?', 'Are you sure you want to delete these files?'),
1162
    'admin/content/file',
1163
    $description,
1164
    t('Delete')
1165
  );
1166
}
1167

    
1168
/**
1169
 * Form submission handler for file_entity_multiple_delete_form().
1170
 */
1171
function file_entity_multiple_delete_form_submit($form, &$form_state) {
1172
  if ($form_state['values']['confirm'] && $fids = array_keys($form_state['values']['files'])) {
1173
    file_delete_multiple($fids);
1174
    $count = count($fids);
1175
    watchdog('file', 'Deleted @count files.', array('@count' => $count));
1176
    drupal_set_message(format_plural($count, 'Deleted one file.', 'Deleted @count files.'));
1177
  }
1178
  $form_state['redirect'] = 'admin/content/file';
1179

    
1180
  // Clear the page and block caches.
1181
  cache_clear_all();
1182
}
1183

    
1184
/**
1185
 * Page callback for the file edit form.
1186
 *
1187
 * @deprecated
1188
 *   Use drupal_get_form('file_entity_edit')
1189
 */
1190
function file_entity_page_edit($file) {
1191
  return drupal_get_form('file_entity_edit', $file);
1192
}
1193

    
1194
/**
1195
 * Page callback for the file deletion confirmation form.
1196
 *
1197
 * @deprecated
1198
 *   Use drupal_get_form('file_entity_delete_form')
1199
 */
1200
function file_entity_page_delete($file) {
1201
  return drupal_get_form('file_entity_delete_form');
1202
}
1203

    
1204
/**
1205
 * Retrieves the upload validators for a file.
1206
 *
1207
 * @param array $options
1208
 *   (optional) An array of options for file validation.
1209
 *
1210
 * @return array
1211
 *   An array suitable for passing to file_save_upload() or for a managed_file
1212
 *   or upload element's '#upload_validators' property.
1213
 */
1214
function file_entity_get_upload_validators(array $options = array()) {
1215
  // Set up file upload validators.
1216
  $validators = array();
1217

    
1218
  // Validate file extensions. If there are no file extensions in $params and
1219
  // there are no Media defaults, there is no file extension validation.
1220
  if (!empty($options['file_extensions'])) {
1221
    $validators['file_validate_extensions'] = array($options['file_extensions']);
1222
  }
1223
  else {
1224
    $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'));
1225
  }
1226

    
1227
  // Cap the upload size according to the system or user defined limit.
1228
  $max_filesize = parse_size(file_upload_max_size());
1229
  $file_entity_max_filesize = parse_size(variable_get('file_entity_max_filesize', ''));
1230

    
1231
  // If the user defined a size limit, use the smaller of the two.
1232
  if (!empty($file_entity_max_filesize)) {
1233
    $max_filesize = min($max_filesize, $file_entity_max_filesize);
1234
  }
1235

    
1236
  if (!empty($options['max_filesize']) && $options['max_filesize'] < $max_filesize) {
1237
    $max_filesize = parse_size($options['max_filesize']);
1238
  }
1239

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

    
1243
  // Add image validators.
1244
  $options += array('min_resolution' => 0, 'max_resolution' => 0);
1245
  if ($options['min_resolution'] || $options['max_resolution']) {
1246
    $validators['file_validate_image_resolution'] = array($options['max_resolution'], $options['min_resolution']);
1247
  }
1248

    
1249
  // Add other custom upload validators from options.
1250
  if (!empty($options['upload_validators'])) {
1251
    $validators += $options['upload_validators'];
1252
  }
1253

    
1254
  return $validators;
1255
}
1256

    
1257
function file_entity_upload_archive_form($form, &$form_state) {
1258
  $options = array(
1259
    'file_extensions' => archiver_get_extensions(),
1260
  );
1261

    
1262
  $form['upload'] = array(
1263
    '#type' => 'managed_file',
1264
    '#title' => t('Upload an archive file'),
1265
    '#upload_location' => NULL, // Upload to the temporary directory.
1266
    '#upload_validators' => file_entity_get_upload_validators($options),
1267
    '#progress_indicator' => 'bar',
1268
    '#required' => TRUE,
1269
    '#pre_render' => array('file_managed_file_pre_render', 'file_entity_upload_validators_pre_render'),
1270
  );
1271

    
1272
  $form['pattern'] = array(
1273
    '#type' => 'textfield',
1274
    '#title' => t('Pattern'),
1275
    '#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.'),
1276
    '#default_value' => '.*',
1277
    '#required' => TRUE,
1278
  );
1279

    
1280
  $form['actions'] = array('#type' => 'actions');
1281
  $form['actions']['submit'] = array(
1282
    '#type' => 'submit',
1283
    '#value' => t('Submit'),
1284
  );
1285

    
1286
  form_load_include($form_state, 'inc', 'file_entity', 'file_entity.pages');
1287

    
1288
  return $form;
1289
}
1290

    
1291
/**
1292
 * Upload a file.
1293
 */
1294
function file_entity_upload_archive_form_submit($form, &$form_state) {
1295
  $form_state['files'] = array();
1296

    
1297
  if ($archive = file_load($form_state['values']['upload'])) {
1298
    if ($archiver = archiver_get_archiver($archive->uri)) {
1299
      $files = $archiver->listContents();
1300
      $destination = pathinfo($archive->filename, PATHINFO_FILENAME);
1301

    
1302
      // Prepare a temporary directory to extract the archive to before saving
1303
      // it is content.
1304
      $extract_location = 'temporary://' . $destination;
1305
      $extract_location = file_destination($extract_location, FILE_EXISTS_RENAME);
1306
      if (!file_prepare_directory($extract_location, FILE_MODIFY_PERMISSIONS | FILE_CREATE_DIRECTORY)) {
1307
        throw new Exception(t('Unable to prepare, a temporary directory %dir for extraction.', array('%dir' => $extract_location)));
1308
      }
1309

    
1310
      // Prepare target directory where files are going to be saved.
1311
      $target_dir = file_default_scheme() . '://' . $destination;
1312
      $target_dir = file_destination($target_dir, FILE_EXISTS_RENAME);
1313
      if (!file_prepare_directory($target_dir, FILE_MODIFY_PERMISSIONS | FILE_CREATE_DIRECTORY)) {
1314
        throw new Exception(t('Unable to prepare, a directory %dir for extraction.', array('%dir' => $target_dir)));
1315
      }
1316

    
1317
      $archiver->extract($extract_location);
1318
      $pattern = '/' . $form_state['values']['pattern'] . '/';
1319
      if ($extracted_files = file_scan_directory($extract_location, $pattern)) {
1320
        foreach ($extracted_files as $extracted_file) {
1321
          $file = new stdClass();
1322
          $file->fid = NULL;
1323
           $file->uid = $archive->uid;
1324
          $file->filename = $extracted_file->filename;
1325
          $file->origname = $extracted_file->filename;
1326
          $file->status = FILE_STATUS_PERMANENT;
1327
          $file->filemime = file_get_mimetype($extracted_file->filename);
1328

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

    
1332
          // Rename potentially executable files, to help prevent exploits (i.e. will
1333
          // rename filename.php.foo and filename.php to filename.php.foo.txt and
1334
          // filename.php.txt, respectively). Don't rename if 'allow_insecure_uploads'
1335
          // evaluates to TRUE.
1336
          if (!variable_get('allow_insecure_uploads', 0) && preg_match('/\.(php|pl|py|cgi|asp|js)(\.|$)/i', $file->filename) && (substr($file->filename, -4) != '.txt')) {
1337
            $file->filemime = 'text/plain';
1338
            $file->uri .= '.txt';
1339
            $file->filename .= '.txt';
1340
          }
1341

    
1342
          // prepare destination path for the extracted file while keeping the
1343
          // directory hierarchy of the file.
1344
          $destination = pathinfo($file->uri, PATHINFO_DIRNAME);
1345
          if (!file_prepare_directory($destination, FILE_MODIFY_PERMISSIONS | FILE_CREATE_DIRECTORY)) {
1346
            throw new Exception(t('Unable to prepare, a directory %dir for extraction.', array('%dir' => $destination)));
1347
          }
1348

    
1349
          if (!file_unmanaged_move($extracted_file->uri, $file->uri)) {
1350
            throw new Exception(t('Could not move uploaded file %file to destination %destination.', array('%file' => $extracted_file->filename, '%destination' => $file->uri)));
1351
            return FALSE;
1352
          }
1353

    
1354
          file_save($file);
1355
          $form_state['files'][$file->fid] = $file;
1356
        }
1357
      }
1358
      // Delete extract location
1359
      file_unmanaged_delete_recursive($extract_location);
1360
      drupal_set_message(t('Extracted %file and added @count new files.', array('%file' => $archive->filename, '@count' => count($files))));
1361
    }
1362
    else {
1363
      throw new Exception(t('Cannot extract %file, not a valid archive.', array('%file' => $archive->uri)));
1364
    }
1365
  }
1366

    
1367
  // Redirect to the file edit page.
1368
  if (file_entity_access('edit') && module_exists('multiform')) {
1369
    $destination = array('destination' => 'admin/content/file');
1370
    if (isset($_GET['destination'])) {
1371
      $destination = drupal_get_destination();
1372
      unset($_GET['destination']);
1373
    }
1374
    $form_state['redirect'] = array('admin/content/file/edit-multiple/' . implode(' ', array_keys($form_state['files'])), array('query' => $destination));
1375
  }
1376
  else {
1377
    $form_state['redirect'] = 'admin/content/file';
1378
  }
1379
}