Projet

Général

Profil

Paste
Télécharger (39,1 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / file_entity / file_entity.admin.inc @ 59ae487e

1
<?php
2
/**
3
 * @file
4
 * File administration and module settings UI.
5
 */
6

    
7
require_once dirname(__FILE__) . '/file_entity.pages.inc';
8

    
9
/**
10
 * List file administration filters that can be applied.
11
 */
12
function file_filters() {
13
  $visible_steam_wrappers = file_get_stream_wrappers(STREAM_WRAPPERS_VISIBLE);
14
  $options = array();
15
  foreach ($visible_steam_wrappers as $scheme => $information) {
16
    $options[$scheme] = check_plain($information['name']);
17
  }
18
  $filters['uri'] = array(
19
    'title' => t('scheme'),
20
    'options' => array(
21
      '[any]' => t('any'),
22
    ) + $options,
23
  );
24
  $filters['type'] = array(
25
    'title' => t('type'),
26
    'options' => array(
27
      '[any]' => t('any'),
28
    ) + file_entity_type_get_names(),
29
  );
30
  return $filters;
31
}
32

    
33
/**
34
 * Apply filters for file administration filters based on session.
35
 *
36
 * @param object $query
37
 *   A SelectQuery to which the filters should be applied.
38
 */
39
function file_entity_build_filter_query(SelectQueryInterface $query) {
40
  // Build query.
41
  $filter_data = isset($_SESSION['file_entity_overview_filter']) ? $_SESSION['file_entity_overview_filter'] : array();
42
  foreach ($filter_data as $index => $filter) {
43
    list($key, $value) = $filter;
44
    switch ($key) {
45
      case 'uri':
46
        $query->condition('fm.' . $key, $value . '%', 'LIKE');
47
        break;
48

    
49
      case 'type':
50
        $query->condition('fm.' . $key, $value);
51
        break;
52

    
53
    }
54
  }
55
}
56

    
57
/**
58
 * Return form for file administration filters.
59
 */
60
function file_entity_filter_form() {
61
  $session = isset($_SESSION['file_entity_overview_filter']) ? $_SESSION['file_entity_overview_filter'] : array();
62
  $filters = file_filters();
63

    
64
  $i = 0;
65
  $form['filters'] = array(
66
    '#type' => 'fieldset',
67
    '#title' => t('Show only items where'),
68
    '#theme' => 'exposed_filters__file_entity',
69
  );
70
  foreach ($session as $filter) {
71
    list($type, $value) = $filter;
72
    if ($type == 'term') {
73
      // Load term name from DB rather than search and parse options array.
74
      $value = module_invoke('taxonomy', 'term_load', $value);
75
      $value = $value->name;
76
    }
77
    else {
78
      $value = $filters[$type]['options'][$value];
79
    }
80
    $t_args = array('%property' => $filters[$type]['title'], '%value' => $value);
81
    if ($i++) {
82
      $form['filters']['current'][] = array('#markup' => t('and where %property is %value', $t_args));
83
    }
84
    else {
85
      $form['filters']['current'][] = array('#markup' => t('where %property is %value', $t_args));
86
    }
87
    if (in_array($type, array('type', 'uri'))) {
88
      // Remove the option if it is already being filtered on.
89
      unset($filters[$type]);
90
    }
91
  }
92

    
93
  $form['filters']['status'] = array(
94
    '#type' => 'container',
95
    '#attributes' => array('class' => array('clearfix')),
96
    '#prefix' => ($i ? '<div class="additional-filters">' . t('and where') . '</div>' : ''),
97
  );
98
  $form['filters']['status']['filters'] = array(
99
    '#type' => 'container',
100
    '#attributes' => array('class' => array('filters')),
101
  );
102
  foreach ($filters as $key => $filter) {
103
    $form['filters']['status']['filters'][$key] = array(
104
      '#type' => 'select',
105
      '#options' => $filter['options'],
106
      '#title' => $filter['title'],
107
      '#default_value' => '[any]',
108
    );
109
  }
110

    
111
  $form['filters']['status']['actions'] = array(
112
    '#type' => 'actions',
113
    '#attributes' => array('class' => array('container-inline')),
114
  );
115
  if (count($filters)) {
116
    $form['filters']['status']['actions']['submit'] = array(
117
      '#type' => 'submit',
118
      '#value' => count($session) ? t('Refine') : t('Filter'),
119
    );
120
  }
121
  if (count($session)) {
122
    $form['filters']['status']['actions']['undo'] = array('#type' => 'submit', '#value' => t('Undo'));
123
    $form['filters']['status']['actions']['reset'] = array('#type' => 'submit', '#value' => t('Reset'));
124
  }
125

    
126
  drupal_add_js('misc/form.js');
127

    
128
  return $form;
129
}
130

    
131
/**
132
 * Process result from file administration filter form.
133
 */
134
function file_entity_filter_form_submit($form, &$form_state) {
135
  $filters = file_filters();
136
  switch ($form_state['values']['op']) {
137
    case t('Filter'):
138
    case t('Refine'):
139
      // Apply every filter that has a choice selected other than 'any'.
140
      foreach ($filters as $filter => $options) {
141
        if (isset($form_state['values'][$filter]) && $form_state['values'][$filter] != '[any]') {
142
          // Flatten the options array to accommodate hierarchical/nested
143
          // options.
144
          $flat_options = form_options_flatten($filters[$filter]['options']);
145
          // Only accept valid selections offered on the dropdown, block bad
146
          // input.
147
          if (isset($flat_options[$form_state['values'][$filter]])) {
148
            $_SESSION['file_entity_overview_filter'][] = array($filter, $form_state['values'][$filter]);
149
          }
150
        }
151
      }
152
      break;
153

    
154
    case t('Undo'):
155
      array_pop($_SESSION['file_entity_overview_filter']);
156
      break;
157

    
158
    case t('Reset'):
159
      $_SESSION['file_entity_overview_filter'] = array();
160
      break;
161

    
162
  }
163
}
164

    
165
/**
166
 * Make mass update of files.
167
 *
168
 * Change all files in the $files array to update them with the field values in
169
 * $updates.
170
 *
171
 * IMPORTANT NOTE: This function is intended to work when called
172
 * from a form submit handler. Calling it outside of the form submission
173
 * process may not work correctly.
174
 *
175
 * @param array $files
176
 *   Array of file fids to update.
177
 * @param array $updates
178
 *   Array of key/value pairs with file field names and the
179
 *   value to update that field to.
180
 */
181
function file_entity_mass_update($files, $updates) {
182
  // We use batch processing to prevent timeout when updating a large number
183
  // of files.
184
  if (count($files) > 10) {
185
    $batch = array(
186
      'operations' => array(
187
        array(
188
          '_file_entity_mass_update_batch_process',
189
          array($files, $updates),
190
        ),
191
      ),
192
      'finished' => '_file_entity_mass_update_batch_finished',
193
      'title' => t('Processing'),
194
      // We use a single multi-pass operation, so the default
195
      // 'Remaining x of y operations' message will be confusing here.
196
      'progress_message' => '',
197
      'error_message' => t('The update has encountered an error.'),
198
      // The operations do not live in the .module file, so we need to
199
      // tell the batch engine which file to load before calling them.
200
      'file' => drupal_get_path('module', 'file_entity') . '/file_entity.admin.inc',
201
    );
202
    batch_set($batch);
203
  }
204
  else {
205
    foreach ($files as $fid) {
206
      _file_entity_mass_update_helper($fid, $updates);
207
    }
208
    drupal_set_message(t('The update has been performed.'));
209
  }
210
}
211

    
212
/**
213
 * File Mass Update - helper function.
214
 */
215
function _file_entity_mass_update_helper($fid, $updates) {
216
  $file = file_load($fid);
217
  // For efficiency manually save the original file before applying any changes.
218
  $file->original = clone $file;
219
  foreach ($updates as $name => $value) {
220
    $file->$name = $value;
221
  }
222
  file_save($file);
223
  return $file;
224
}
225

    
226
/**
227
 * File Mass Update Batch operation.
228
 */
229
function _file_entity_mass_update_batch_process($files, $updates, &$context) {
230
  if (!isset($context['sandbox']['progress'])) {
231
    $context['sandbox']['progress'] = 0;
232
    $context['sandbox']['max'] = count($files);
233
    $context['sandbox']['files'] = $files;
234
  }
235

    
236
  // Process files by groups of 5.
237
  $count = min(5, count($context['sandbox']['files']));
238
  for ($i = 1; $i <= $count; $i++) {
239
    // For each fid, load the file, reset the values, and save it.
240
    $fid = array_shift($context['sandbox']['files']);
241
    $file = _file_entity_mass_update_helper($fid, $updates);
242

    
243
    // Store result for post-processing in the finished callback.
244
    $context['results'][] = l($file->filename, 'file/' . $file->fid);
245

    
246
    // Update our progress information.
247
    $context['sandbox']['progress']++;
248
  }
249

    
250
  // Inform the batch engine that we are not finished,
251
  // and provide an estimation of the completion level we reached.
252
  if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
253
    $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
254
  }
255
}
256

    
257
/**
258
 * File Mass Update Batch 'finished' callback.
259
 */
260
function _file_entity_mass_update_batch_finished($success, $results, $operations) {
261
  if ($success) {
262
    drupal_set_message(t('The update has been performed.'));
263
  }
264
  else {
265
    drupal_set_message(t('An error occurred and processing did not complete.'), 'error');
266
    $message = format_plural(count($results), '1 item successfully processed:', '@count items successfully processed:');
267
    $message .= theme('item_list', array('items' => $results));
268
    drupal_set_message($message);
269
  }
270
}
271

    
272
/**
273
 * Menu callback: file administration.
274
 */
275
function file_entity_admin_file($form, $form_state) {
276
  if (isset($form_state['values']['operation']) && $form_state['values']['operation'] == 'delete') {
277
    return file_entity_multiple_delete_confirm($form, $form_state, array_filter($form_state['values']['files']));
278
  }
279
  $form['filter'] = file_entity_filter_form();
280
  $form['#submit'][] = 'file_entity_filter_form_submit';
281
  $form['admin'] = file_entity_admin_files();
282

    
283
  return $form;
284
}
285

    
286
/**
287
 * Form builder: Builds the file administration overview.
288
 */
289
function file_entity_admin_files() {
290
  $admin_access = user_access('administer files');
291

    
292
  // Build the 'Update options' form.
293
  $form['options'] = array(
294
    '#type' => 'fieldset',
295
    '#title' => t('Update options'),
296
    '#attributes' => array('class' => array('container-inline')),
297
    '#access' => $admin_access,
298
  );
299
  $options = array();
300
  foreach (module_invoke_all('file_operations') as $operation => $array) {
301
    $options[$operation] = $array['label'];
302
  }
303
  $form['options']['operation'] = array(
304
    '#type' => 'select',
305
    '#title' => t('Operation'),
306
    '#title_display' => 'invisible',
307
    '#options' => $options,
308
    '#default_value' => 'approve',
309
  );
310
  $form['options']['submit'] = array(
311
    '#type' => 'submit',
312
    '#value' => t('Update'),
313
    '#validate' => array('file_entity_admin_files_validate'),
314
    '#submit' => array('file_entity_admin_files_submit'),
315
  );
316

    
317
  // Build the sortable table header.
318
  $header = array(
319
    'title' => array('data' => t('Title'), 'field' => 'fm.filename'),
320
    'type' => array('data' => t('Type'), 'field' => 'fm.type'),
321
    'size' => array('data' => t('Size'), 'field' => 'fm.filesize'),
322
    'author' => t('Author'),
323
    'timestamp' => array(
324
      'data' => t('Updated'),
325
      'field' => 'fm.timestamp',
326
      'sort' => 'desc'),
327
    'usage' => array('data' => t('Used in'), 'field' => 'total_count'),
328
    'operations' => array('data' => t('Operations')),
329
  );
330

    
331
  $query = db_select('file_managed', 'fm')->extend('PagerDefault')->extend('TableSort');
332
  $query->leftJoin('file_usage', 'fu', 'fm.fid = fu.fid');
333
  $query->groupBy('fm.fid');
334
  $query->groupBy('fm.uid');
335
  $query->groupBy('fm.timestamp');
336
  $query->addExpression('SUM(fu.count)', 'total_count');
337
  file_entity_build_filter_query($query);
338

    
339
  $result = $query
340
    ->fields('fm', array('fid', 'uid'))
341
    ->limit(50)
342
    ->orderByHeader($header)
343
    ->addTag('file_access')
344
    ->execute()
345
    ->fetchAllAssoc('fid');
346
  $files = file_load_multiple(array_keys($result));
347

    
348
  $uids = array();
349
  foreach ($files as $file) {
350
    $uids[] = $file->uid;
351
  }
352
  $accounts = !empty($uids) ? user_load_multiple(array_unique($uids)) : array();
353

    
354
  // Prepare the list of files.
355
  $destination = drupal_get_destination();
356
  $options = array();
357
  foreach ($files as $file) {
358
    $file_type = file_type_load($file->type);
359
    $account = isset($accounts[$file->uid]) ? $accounts[$file->uid] : NULL;
360
    $options[$file->fid] = array(
361
      'title' => array(
362
        'data' => array(
363
          '#type' => 'link',
364
          '#title' => $file->filename,
365
          '#href' => 'file/' . $file->fid,
366
        ),
367
      ),
368
      'type' => $file_type ? check_plain($file_type->label) : FILE_TYPE_NONE,
369
      'size' => format_size($file->filesize),
370
      'author' => theme('username', array('account' => $account)),
371
      'timestamp' => format_date($file->timestamp, 'short'),
372
      'usage' => format_plural((int) $result[$file->fid]->total_count, '1 place', '@count places'),
373
    );
374

    
375
    // Show a warning for files that do not exist.
376
    if (@!is_file($file->uri)) {
377
      $options[$file->fid]['#attributes']['class'][] = 'error';
378
      if (!file_stream_wrapper_get_instance_by_uri($file->uri)) {
379
        $options[$file->fid]['#attributes']['title'] = t('The stream wrapper for @scheme files is missing.', array('@scheme' => file_uri_scheme($file->uri)));
380
      }
381
      else {
382
        $options[$file->fid]['#attributes']['title'] = t('The file does not exist.');
383
      }
384
    }
385

    
386
    // Build a list of all the accessible operations for the current file.
387
    $operations = array();
388
    if (file_entity_access('update', $file)) {
389
      // Convert the usage count to a link.
390
      $options[$file->fid]['usage'] = l($options[$file->fid]['usage'], 'file/' . $file->fid . '/usage');
391
      $operations['edit'] = array(
392
        'title' => t('Edit'),
393
        'href' => 'file/' . $file->fid . '/edit',
394
        'query' => $destination,
395
      );
396
    }
397
    if (file_entity_access('delete', $file)) {
398
      $operations['delete'] = array(
399
        'title' => t('Delete'),
400
        'href' => 'file/' . $file->fid . '/delete',
401
        'query' => $destination,
402
      );
403
    }
404
    $options[$file->fid]['operations'] = array();
405
    if (count($operations) > 1) {
406
      // Render an unordered list of operations links.
407
      $options[$file->fid]['operations'] = array(
408
        'data' => array(
409
          '#theme' => 'links__file_entity_operations',
410
          '#links' => $operations,
411
          '#attributes' => array('class' => array('links', 'inline')),
412
        ),
413
      );
414
    }
415
    elseif (!empty($operations)) {
416
      // Render the first and only operation as a link.
417
      $link = reset($operations);
418
      $options[$file->fid]['operations'] = array(
419
        'data' => array(
420
          '#type' => 'link',
421
          '#title' => $link['title'],
422
          '#href' => $link['href'],
423
          '#options' => array('query' => $link['query']),
424
        ),
425
      );
426
    }
427
  }
428

    
429
  // Only use a tableselect when the current user is able to perform any
430
  // operations.
431
  if ($admin_access) {
432
    $form['files'] = array(
433
      '#type' => 'tableselect',
434
      '#header' => $header,
435
      '#options' => $options,
436
      '#empty' => t('No files available.'),
437
    );
438
  }
439
  // Otherwise, use a simple table.
440
  else {
441
    $form['files'] = array(
442
      '#theme' => 'table',
443
      '#header' => $header,
444
      '#rows' => $options,
445
      '#empty' => t('No files available.'),
446
    );
447
  }
448

    
449
  $form['pager'] = array('#markup' => theme('pager'));
450
  return $form;
451
}
452

    
453
/**
454
 * Validate file_entity_admin_files form submissions.
455
 *
456
 * Check if any files have been selected to perform the chosen
457
 * 'Update option' on.
458
 */
459
function file_entity_admin_files_validate($form, &$form_state) {
460
  // Error if there are no items to select.
461
  if (!is_array($form_state['values']['files']) || !count(array_filter($form_state['values']['files']))) {
462
    form_set_error('', t('No items selected.'));
463
  }
464
}
465

    
466
/**
467
 * Process file_entity_admin_files form submissions.
468
 *
469
 * Execute the chosen 'Update option' on the selected files.
470
 */
471
function file_entity_admin_files_submit($form, &$form_state) {
472
  $operations = module_invoke_all('file_operations');
473
  $operation = $operations[$form_state['values']['operation']];
474
  // Filter out unchecked files.
475
  $files = array_filter($form_state['values']['files']);
476
  if ($function = $operation['callback']) {
477
    // Add in callback arguments if present.
478
    if (isset($operation['callback arguments'])) {
479
      $args = array_merge(array($files), $operation['callback arguments']);
480
    }
481
    else {
482
      $args = array($files);
483
    }
484
    call_user_func_array($function, $args);
485

    
486
    cache_clear_all();
487
  }
488
  else {
489
    // We need to rebuild the form to go to a second step. For example, to
490
    // show the confirmation form for the deletion of files.
491
    $form_state['rebuild'] = TRUE;
492
  }
493
}
494

    
495
/**
496
 * File entity delete confirmation.
497
 */
498
function file_entity_multiple_delete_confirm($form, &$form_state, $files) {
499
  $form['files'] = array(
500
    '#prefix' => '<ul>',
501
    '#suffix' => '</ul>',
502
    '#tree' => TRUE,
503
  );
504
  // array_filter returns only elements with TRUE values.
505
  foreach ($files as $fid => $value) {
506
    $filename = db_query('SELECT filename FROM {file_managed} WHERE fid = :fid', array(':fid' => $fid))->fetchField();
507
    $form['files'][$fid] = array(
508
      '#type' => 'hidden',
509
      '#value' => $fid,
510
      '#prefix' => '<li>',
511
      '#suffix' => check_plain($filename) . "</li>\n",
512
    );
513
  }
514
  $form['operation'] = array('#type' => 'hidden', '#value' => 'delete');
515
  $form['#submit'][] = 'file_entity_multiple_delete_confirm_submit';
516
  $confirm_question = format_plural(count($files),
517
                                  'Are you sure you want to delete this item?',
518
                                  'Are you sure you want to delete these items?');
519
  return confirm_form($form,
520
                    $confirm_question,
521
                    'admin/content/file', t('This action cannot be undone.'),
522
                    t('Delete'), t('Cancel'));
523
}
524

    
525
/**
526
 * Submit handler for delete confirmation.
527
 */
528
function file_entity_multiple_delete_confirm_submit($form, &$form_state) {
529
  if ($form_state['values']['confirm']) {
530
    file_delete_multiple(array_keys($form_state['values']['files']));
531
    $count = count($form_state['values']['files']);
532
    watchdog('file_entity', 'Deleted @count files.', array('@count' => $count));
533
    drupal_set_message(format_plural($count, 'Deleted 1 file.', 'Deleted @count files.'));
534
  }
535
  $form_state['redirect'] = 'admin/content/file';
536
}
537

    
538
/**
539
 * Displays the file type admin overview page.
540
 */
541
function file_entity_list_types_page() {
542
  $file_entity_info = entity_get_info('file');
543
  // Newer versions of Drupal core require the "administer fields" permission
544
  // to access the Field UI.
545
  $field_ui = module_exists('field_ui') && (user_access('administer fields') || !function_exists('field_ui_admin_access'));
546
  $colspan = $field_ui ? 5 : 3;
547
  $header = array(
548
    array('data' => t('Name')),
549
    array('data' => t('Operations'), 'colspan' => $colspan),
550
    array('data' => t('Status')),
551
  );
552
  $rows = array();
553
  $weight = 0;
554
  $types = file_type_load_all(TRUE);
555
  $count = count($types);
556
  foreach ($types as $type) {
557
    $weight++;
558
    $row = array(
559
      array(
560
        'data' => theme('file_entity_file_type_overview',
561
          array(
562
            'label' => $type->label,
563
            'description' => $type->description,
564
          )
565
        ),
566
      ),
567
    );
568
    $path = isset($file_entity_info['bundles'][$type->type]['admin']['real path']) ? $file_entity_info['bundles'][$type->type]['admin']['real path'] : NULL;
569

    
570
    if (empty($type->disabled) && isset($path)) {
571
      $row[] = array('data' => l(t('edit file type'), $path . '/edit'));
572
      if ($field_ui) {
573
        $row[] = array('data' => l(t('manage fields'), $path . '/fields'));
574
        $row[] = array('data' => l(t('manage display'), $path . '/display'));
575
      }
576
      $row[] = array('data' => l(t('manage file display'), $path . '/file-display'));
577
    }
578
    else {
579
      $row += array_fill(1, $colspan - 1, '');
580
    }
581

    
582
    $admin_path = 'admin/structure/file-types/manage/' . $type->type;
583
    switch ($type->ctools_type) {
584
      // Configuration is in code.
585
      case 'Default':
586
        if (!empty($type->disabled)) {
587
          $row[] = l(t('enable'), $admin_path . '/enable');
588
        }
589
        else {
590
          $row[] = l(t('disable'), $admin_path . '/disable');
591
        }
592
        break;
593

    
594
      // Configuration is in DB.
595
      case 'Normal':
596
        if (!empty($type->disabled)) {
597
          $status = l(t('enable'), $admin_path . '/enable');
598
        }
599
        else {
600
          $status = l(t('disable'), $admin_path . '/disable');
601
        }
602
        $row[] = $status . ' | ' . l(t('delete'), $admin_path . '/delete');
603
        break;
604

    
605
      // Configuration is in code, but overridden in DB.
606
      case 'Overridden':
607
        if (!empty($type->disabled)) {
608
          $row[] = l(t('enable'), $admin_path . '/enable');
609
        }
610
        else {
611
          $row[] = l(t('disable'), $admin_path . '/disable') . ' | ' . l(t('revert'), $admin_path . '/revert');
612
        }
613
        break;
614
    }
615

    
616
    if (!empty($type->disabled)) {
617
      $row[] = t('Disabled');
618
      $rows[$weight + $count] = array('data' => $row, 'class' => array('ctools-export-ui-disabled'));
619
    }
620
    else {
621
      $row[] = $type->ctools_type;
622
      $rows[$weight] = array('data' => $row);
623
    }
624
  }
625

    
626
  // Move disabled items to the bottom.
627
  ksort($rows);
628

    
629
  $build['file_type_table'] = array(
630
    '#theme' => 'table',
631
    '#header' => $header,
632
    '#rows' => $rows,
633
    '#empty' => t('No file types available.'),
634
    '#attached' => array(
635
      'css' => array(drupal_get_path('module', 'ctools') . '/css/export-ui-list.css'),
636
    ),
637
  );
638

    
639
  return $build;
640
}
641

    
642
/**
643
 * Form callback; presents file display settings for a given view mode.
644
 */
645
function file_entity_file_display_form($form, &$form_state, $file_type, $view_mode) {
646
  $form['#file_type'] = $file_type->type;
647
  $form['#view_mode'] = $view_mode;
648
  $form['#tree'] = TRUE;
649
  $form['#attached']['js'][] = drupal_get_path('module', 'file_entity') . '/file_entity.admin.js';
650

    
651
  // Retrieve available formatters for this file type and load all configured
652
  // filters for existing text formats.
653
  $formatters = file_info_formatter_types();
654
  foreach ($formatters as $name => $formatter) {
655
    if (!empty($formatter['hidden'])) {
656
      unset($formatters[$name]);
657
    }
658
    if (isset($formatter['mime types'])) {
659
      if (file_entity_match_mimetypes($formatter['mime types'], $file_type->mimetypes)) {
660
        continue;
661
      }
662
      unset($formatters[$name]);
663
    }
664
  }
665
  $current_displays = file_displays_load($file_type->type, $view_mode, TRUE);
666
  foreach ($current_displays as $name => $display) {
667
    $current_displays[$name] = (array) $display;
668
  }
669

    
670
  // Formatter status.
671
  $form['displays']['status'] = array(
672
    '#type' => 'item',
673
    '#title' => t('Enabled displays'),
674
    '#prefix' => '<div id="file-displays-status-wrapper">',
675
    '#suffix' => '</div>',
676
  );
677
  $i = 0;
678
  foreach ($formatters as $name => $formatter) {
679
    $form['displays']['status'][$name] = array(
680
      '#type' => 'checkbox',
681
      '#title' => check_plain($formatter['label']),
682
      '#default_value' => !empty($current_displays[$name]['status']),
683
      '#description' => isset($formatter['description']) ? filter_xss($formatter['description']) : NULL,
684
      '#parents' => array('displays', $name, 'status'),
685
      '#weight' => (isset($formatter['weight']) ? $formatter['weight'] : 0) + ($i / 1000),
686
    );
687
    $i++;
688
  }
689

    
690
  // Formatter order (tabledrag).
691
  $form['displays']['order'] = array(
692
    '#type' => 'item',
693
    '#title' => t('Display precedence order'),
694
    '#theme' => 'file_entity_file_display_order',
695
  );
696
  foreach ($formatters as $name => $formatter) {
697
    $form['displays']['order'][$name]['label'] = array(
698
      '#markup' => check_plain($formatter['label']),
699
    );
700
    $form['displays']['order'][$name]['weight'] = array(
701
      '#type' => 'weight',
702
      '#title' => t('Weight for @title', array('@title' => $formatter['label'])),
703
      '#title_display' => 'invisible',
704
      '#delta' => 50,
705
      '#default_value' => isset($current_displays[$name]['weight']) ? $current_displays[$name]['weight'] : 0,
706
      '#parents' => array('displays', $name, 'weight'),
707
    );
708
    $form['displays']['order'][$name]['#weight'] = $form['displays']['order'][$name]['weight']['#default_value'];
709
  }
710

    
711
  // Formatter settings.
712
  $form['display_settings_title'] = array(
713
    '#type' => 'item',
714
    '#title' => t('Display settings'),
715
  );
716
  $form['display_settings'] = array(
717
    '#type' => 'vertical_tabs',
718
  );
719
  $i = 0;
720
  foreach ($formatters as $name => $formatter) {
721
    if (isset($formatter['settings callback']) && ($function = $formatter['settings callback']) && function_exists($function)) {
722
      $defaults = !empty($formatter['default settings']) ? $formatter['default settings'] : array();
723
      $settings = !empty($current_displays[$name]['settings']) ? $current_displays[$name]['settings'] : array();
724
      $settings += $defaults;
725
      $settings_form = $function($form, $form_state, $settings, $name, $file_type->type, $view_mode);
726
      if (!empty($settings_form)) {
727
        $form['displays']['settings'][$name] = array(
728
          '#type' => 'fieldset',
729
          '#title' => check_plain($formatter['label']),
730
          '#parents' => array('displays', $name, 'settings'),
731
          '#group' => 'display_settings',
732
          '#weight' => (isset($formatter['weight']) ? $formatter['weight'] : 0) + ($i / 1000),
733
        ) + $settings_form;
734
      }
735
    }
736
    $i++;
737
  }
738

    
739
  $form['actions'] = array('#type' => 'actions');
740
  $form['actions']['submit'] = array(
741
    '#type' => 'submit',
742
    '#value' => t('Save configuration'),
743
  );
744

    
745
  return $form;
746
}
747

    
748
/**
749
 * Process file display settings form submissions.
750
 */
751
function file_entity_file_display_form_submit($form, &$form_state) {
752
  $file_type = $form['#file_type'];
753
  $view_mode = $form['#view_mode'];
754
  $displays = isset($form_state['values']['displays']) ? $form_state['values']['displays'] : array();
755
  $displays_original = file_displays_load($file_type, $view_mode, TRUE);
756
  foreach ($displays as $formatter_name => $display) {
757
    $display_original = isset($displays_original[$formatter_name]) ? $displays_original[$formatter_name] : file_display_new($file_type, $view_mode, $formatter_name);
758
    $display += (array) $display_original;
759
    file_display_save((object) $display);
760
  }
761
  drupal_set_message(t('Your settings have been saved.'));
762
}
763

    
764
/**
765
 * Returns HTML for the file type overview page.
766
 *
767
 * Specifically, this returns HTML for a file type label and description.
768
 */
769
function theme_file_entity_file_type_overview($variables) {
770
  return check_plain($variables['label']) . '<div class="description">' . $variables['description'] . '</div>';
771
}
772

    
773
/**
774
 * Returns HTML for a file display's display order table.
775
 */
776
function theme_file_entity_file_display_order($variables) {
777
  $element = $variables['element'];
778

    
779
  $rows = array();
780
  foreach (element_children($element, TRUE) as $name) {
781
    $element[$name]['weight']['#attributes']['class'][] = 'file-display-order-weight';
782
    $rows[] = array(
783
      'data' => array(
784
        drupal_render($element[$name]['label']),
785
        drupal_render($element[$name]['weight']),
786
      ),
787
      'class' => array('draggable'),
788
    );
789
  }
790
  $output = drupal_render_children($element);
791
  $output .= theme('table', array('rows' => $rows, 'attributes' => array('id' => 'file-displays-order')));
792
  drupal_add_tabledrag('file-displays-order', 'order', 'sibling', 'file-display-order-weight', NULL, NULL, TRUE);
793

    
794
  return $output;
795
}
796

    
797
/**
798
 * Form constructor for the file type settings form.
799
 *
800
 * @param object $type
801
 *   The file type.
802
 *
803
 * @see file_entity_file_type_form_validate()
804
 * @see file_entity_file_type_form_submit()
805
 */
806
function file_entity_file_type_form($form, &$form_state, $type = NULL) {
807
  if (!isset($type->type)) {
808
    // This is a new type.
809
    $type = (object) array(
810
      'type' => '',
811
      'label' => '',
812
      'description' => '',
813
      'mimetypes' => array(),
814
    );
815
  }
816
  $form['#file_type'] = $type;
817

    
818
  $form['label'] = array(
819
    '#type' => 'textfield',
820
    '#title' => t('Name'),
821
    '#description' => t('This is the human readable name of the file type.'),
822
    '#required' => TRUE,
823
    '#default_value' => $type->label,
824
  );
825

    
826
  $form['type'] = array(
827
    '#type' => 'machine_name',
828
    '#default_value' => $type->type,
829
    '#maxlength' => 255,
830
    '#disabled' => (bool) $type->type,
831
    '#machine_name' => array(
832
      'exists' => 'file_type_load',
833
      'source' => array('label'),
834
    ),
835
    '#description' => t('A unique machine-readable name for this file type. It must only contain lowercase letters, numbers, and underscores.'),
836
  );
837

    
838
  $form['description'] = array(
839
    '#type' => 'textarea',
840
    '#title' => t('Description'),
841
    '#description' => t('This is the description of the file type.'),
842
    '#default_value' => $type->description,
843
  );
844

    
845
  $form['mimetypes'] = array(
846
    '#type' => 'textarea',
847
    '#title' => t('Mimetypes'),
848
    '#description' => t('Enter one mimetype per line.'),
849
    '#default_value' => implode("\n", $type->mimetypes),
850
  );
851

    
852
  include_once DRUPAL_ROOT . '/includes/file.mimetypes.inc';
853
  $mimetypes = file_mimetype_mapping();
854

    
855
  $form['mimetype_mapping'] = array(
856
    '#type' => 'fieldset',
857
    '#title' => t('Mimetype List'),
858
    '#collapsible' => TRUE,
859
    '#collapsed' => TRUE,
860
  );
861
  $form['mimetype_mapping']['mapping'] = array(
862
    '#theme' => 'item_list',
863
    '#items' => $mimetypes['mimetypes'],
864
  );
865

    
866
  $form['actions'] = array('#type' => 'actions');
867

    
868
  $form['actions']['submit'] = array(
869
    '#type' => 'submit',
870
    '#value' => t('Save'),
871
  );
872
  if (!empty($type->type)) {
873
    $form['actions']['delete'] = array(
874
      '#type' => 'submit',
875
      '#value' => t('Delete'),
876
    );
877
  }
878

    
879
  return $form;
880
}
881

    
882
/**
883
 * Form validation handler for file_entity_file_type_form().
884
 *
885
 * @see file_entity_file_type_form_submit()
886
 */
887
function file_entity_file_type_form_validate($form, &$form_state) {
888
  include_once DRUPAL_ROOT . '/includes/file.mimetypes.inc';
889
  $mimetype_mapping = file_mimetype_mapping();
890

    
891
  $valid_mimetypes = $mimetype_mapping['mimetypes'];
892
  $submitted_mimetypes = array_filter(array_map('trim', explode("\n", $form_state['values']['mimetypes'])));
893

    
894
  $invalid_mimetypes = array();
895
  foreach ($submitted_mimetypes as $mimetype) {
896
    if (!file_entity_match_mimetypes($mimetype, $valid_mimetypes)) {
897
      $invalid_mimetypes[] = $mimetype;
898
    }
899
  }
900

    
901
  foreach ($invalid_mimetypes as $mimetype) {
902
    form_set_error('mimetypes', t('The mimetype %mimetype is not a valid mimetype.', array('%mimetype' => $mimetype)));
903
  }
904
}
905

    
906
/**
907
 * Form submission handler for file_entity_file_type_form().
908
 *
909
 * @see file_entity_file_type_form_validate()
910
 */
911
function file_entity_file_type_form_submit($form, &$form_state) {
912
  if (!empty($form['#file_type']->type)) {
913
    $type = file_type_load($form['#file_type']->type);
914
  }
915
  else {
916
    $type = (object) array(
917
      'type' => $form_state['values']['type'],
918
    );
919
  }
920
  if ($form_state['values']['op'] == t('Delete')) {
921
    $form_state['redirect'] = 'admin/structure/file-types/manage/' . $type->type . '/delete';
922
    return;
923
  }
924
  $type->label = $form_state['values']['label'];
925
  $type->description = $form_state['values']['description'];
926
  $type->mimetypes = array_filter(array_map('trim', explode("\n", $form_state['values']['mimetypes'])));
927

    
928
  file_type_save($type);
929

    
930
  drupal_set_message(t('The file type %type has been updated.', array('%type' => $type->label)));
931
  $form_state['redirect'] = 'admin/structure/file-types';
932
}
933

    
934

    
935
/**
936
 * Menu callback; disable a single file type.
937
 */
938
function file_entity_type_enable_confirm($form, &$form_state, $type) {
939
  $form['type'] = array('#type' => 'value', '#value' => $type->type);
940
  $form['label'] = array('#type' => 'value', '#value' => $type->label);
941
  $message = t('Are you sure you want to enable the file type %type?', array('%type' => $type->label));
942
  return confirm_form($form, $message, 'admin/structure/file-types', '', t('Enable'));
943
}
944

    
945

    
946
/**
947
 * Process file type disable confirm submissions.
948
 */
949
function file_entity_type_enable_confirm_submit($form, &$form_state) {
950
  file_type_enable($form_state['values']['type']);
951
  $t_args = array('%label' => $form_state['values']['label']);
952
  drupal_set_message(t('The file type %label has been enabled.', $t_args));
953
  watchdog('file_entity', 'Enabled file type %label.', $t_args, WATCHDOG_NOTICE);
954
  $form_state['redirect'] = 'admin/structure/file-types';
955
  return;
956
}
957

    
958

    
959
/**
960
 * Menu callback; disable a single file type.
961
 */
962
function file_entity_type_disable_confirm($form, &$form_state, $type) {
963
  $form['type'] = array('#type' => 'value', '#value' => $type->type);
964
  $form['label'] = array('#type' => 'value', '#value' => $type->label);
965

    
966
  $message = t('Are you sure you want to disable the file type %type?', array('%type' => $type->label));
967
  $caption = '';
968

    
969
  $num_files = db_query("SELECT COUNT(*) FROM {file_managed} WHERE type = :type", array(':type' => $type->type))->fetchField();
970
  if ($num_files) {
971
    $caption .= '<p>' . format_plural($num_files, '%type is used by 1 file on
972
      your site. If you disable this file type, you will not be able to edit
973
      the %type file and it may not display correctly.', '%type is used by
974
      @count files on your site. If you remove %type, you will not be able to
975
      edit the %type file and it may not display correctly.',
976
    array('%type' => $type->label)) . '</p>';
977
  }
978

    
979
  return confirm_form($form, $message, 'admin/structure/file-types', $caption, t('Disable'));
980
}
981

    
982

    
983
/**
984
 * Process file type disable confirm submissions.
985
 */
986
function file_entity_type_disable_confirm_submit($form, &$form_state) {
987
  file_type_disable($form_state['values']['type']);
988
  $t_args = array('%label' => $form_state['values']['label']);
989
  drupal_set_message(t('The file type %label has been disabled.', $t_args));
990
  watchdog('file_entity', 'Disabled file type %label.', $t_args, WATCHDOG_NOTICE);
991
  $form_state['redirect'] = 'admin/structure/file-types';
992
  return;
993
}
994

    
995

    
996
/**
997
 * Menu callback; revert a single file type.
998
 */
999
function file_entity_type_revert_confirm($form, &$form_state, $type) {
1000
  $form['type'] = array('#type' => 'value', '#value' => $type->type);
1001
  $form['label'] = array('#type' => 'value', '#value' => $type->label);
1002
  $message = t('Are you sure you want to revert the file type %type?', array('%type' => $type->label));
1003
  return confirm_form($form, $message, 'admin/structure/file-types', '', t('Revert'));
1004
}
1005

    
1006

    
1007
/**
1008
 * Process file type delete confirm submissions.
1009
 */
1010
function file_entity_type_revert_confirm_submit($form, &$form_state) {
1011
  // @NOTE deleting the file_type from the DB actually reverts it to code.
1012
  file_type_delete($form_state['values']['type']);
1013
  $t_args = array('%label' => $form_state['values']['label']);
1014
  drupal_set_message(t('The file type %label has been reverted.', $t_args));
1015
  watchdog('file_entity', 'Reverted file type %label.', $t_args, WATCHDOG_NOTICE);
1016
  $form_state['redirect'] = 'admin/structure/file-types';
1017
  return;
1018
}
1019

    
1020

    
1021
/**
1022
 * Menu callback; delete a single file type.
1023
 */
1024
function file_entity_type_delete_confirm($form, &$form_state, $type) {
1025
  $form['type'] = array('#type' => 'value', '#value' => $type->type);
1026
  $form['label'] = array('#type' => 'value', '#value' => $type->label);
1027

    
1028
  $message = t('Are you sure you want to delete the file type %type?', array('%type' => $type->label));
1029
  $caption = '';
1030

    
1031
  $num_files = db_query("SELECT COUNT(*) FROM {file_managed} WHERE type = :type", array(':type' => $type->type))->fetchField();
1032
  if ($num_files) {
1033
    $caption .= '<p>' . format_plural($num_files, '%type is used by 1 file on your site. If you remove this file type, you will not be able to edit the %type file and it may not display correctly.', '%type is used by @count pieces of file on your site. If you remove %type, you will not be able to edit the %type file and it may not display correctly.', array('%type' => $type->label)) . '</p>';
1034
  }
1035

    
1036
  $caption .= '<p>' . t('This action cannot be undone.') . '</p>';
1037

    
1038
  return confirm_form($form, $message, 'admin/structure/file-types', $caption, t('Delete'));
1039
}
1040

    
1041
/**
1042
 * Process file type delete confirm submissions.
1043
 */
1044
function file_entity_type_delete_confirm_submit($form, &$form_state) {
1045
  file_type_delete($form_state['values']['type']);
1046

    
1047
  $t_args = array('%label' => $form_state['values']['label']);
1048
  drupal_set_message(t('The file type %label has been deleted.', $t_args));
1049
  watchdog('file_entity', 'Deleted file type %label.', $t_args, WATCHDOG_NOTICE);
1050

    
1051
  $form_state['redirect'] = 'admin/structure/file-types';
1052
  return;
1053
}
1054

    
1055
/**
1056
 * Form callback for file_entity settings.
1057
 */
1058
function file_entity_settings_form($form, &$form_state) {
1059
  $form['file_entity_max_filesize'] = array(
1060
    '#type' => 'textfield',
1061
    '#title' => t('Maximum upload size'),
1062
    '#default_value' => variable_get('file_entity_max_filesize', ''),
1063
    '#description' => t('Enter a value like "512" (bytes), "80 KB" (kilobytes) or "50 MB" (megabytes) in order to restrict the allowed file size. If left empty the file sizes will be limited only by PHP\'s maximum post and file upload sizes (current max limit <strong>%limit</strong>).', array('%limit' => format_size(file_upload_max_size()))),
1064
    '#size' => 10,
1065
    '#element_validate' => array('_file_generic_settings_max_filesize'),
1066
  );
1067

    
1068
  $form['file_entity_default_allowed_extensions'] = array(
1069
    '#type' => 'textfield',
1070
    '#title' => t('Default allowed file extensions'),
1071
    '#default_value' => 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'),
1072
    '#description' => t('Separate extensions with a space or comma and do not include the leading dot.'),
1073
    '#maxlength' => NULL,
1074
  );
1075

    
1076
  $form['file_entity_alt'] = array(
1077
    '#type' => 'textfield',
1078
    '#title' => t('Alt attribute'),
1079
    '#description' => t('The text to use as value for the <em>img</em> tag <em>alt</em> attribute.'),
1080
    '#default_value' => variable_get('file_entity_alt', '[file:field_file_image_alt_text]'),
1081
  );
1082
  $form['file_entity_title'] = array(
1083
    '#type' => 'textfield',
1084
    '#title' => t('Title attribute'),
1085
    '#description' => t('The text to use as value for the <em>img</em> tag <em>title</em> attribute.'),
1086
    '#default_value' => variable_get('file_entity_title', '[file:field_file_image_title_text]'),
1087
  );
1088

    
1089
  // Provide default token values.
1090
  if (module_exists('token')) {
1091
    $form['token_help'] = array(
1092
      '#theme' => 'token_tree',
1093
      '#token_types' => array('file'),
1094
      '#dialog' => TRUE,
1095
    );
1096
    $form['file_entity_alt']['#description'] .= t('This field supports tokens.');
1097
    $form['file_entity_title']['#description'] .= t('This field supports tokens.');
1098
  }
1099
  $form['file_upload_wizard'] = array(
1100
    '#type' => 'fieldset',
1101
    '#title' => t('File upload wizard'),
1102
    '#collapsible' => TRUE,
1103
    '#collapsed' => FALSE,
1104
    '#description' => t('Configure the steps available when uploading a new file.'),
1105
  );
1106
  $form['file_upload_wizard']['file_entity_file_upload_wizard_skip_file_type'] = array(
1107
    '#type' => 'checkbox',
1108
    '#title' => t('Skip filetype selection.'),
1109
    '#default_value' => variable_get('file_entity_file_upload_wizard_skip_file_type', FALSE),
1110
    '#description' => t('The file type selection step is only available if the uploaded file falls into two or more file types. If this step is skipped, files with no available file type or two or more file types will not be assigned a file type.'),
1111
  );
1112
  $form['file_upload_wizard']['file_entity_file_upload_wizard_skip_scheme'] = array(
1113
    '#type' => 'checkbox',
1114
    '#title' => t('Skip scheme selection.'),
1115
    '#default_value' => variable_get('file_entity_file_upload_wizard_skip_scheme', FALSE),
1116
    '#description' => t('The scheme selection step is only available if two or more file destinations, such as public local files served by the webserver and private local files served by Drupal, are available. If this step is skipped, files will automatically be saved using the default download method.'),
1117
  );
1118
  $form['file_upload_wizard']['file_entity_file_upload_wizard_skip_fields'] = array(
1119
    '#type' => 'checkbox',
1120
    '#title' => t('Skip available fields.'),
1121
    '#default_value' => variable_get('file_entity_file_upload_wizard_skip_fields', FALSE),
1122
    '#description' => t('The field selection step is only available if the file type the file belongs to has any available fields. If this step is skipped, any fields on the file will be left blank.'),
1123
  );
1124

    
1125
  return system_settings_form($form);
1126
}