Projet

Général

Profil

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

root / drupal7 / sites / all / modules / file_entity / file_entity.admin.inc @ 2b3c8cc1

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
  $field_ui = module_exists('field_ui');
544
  $colspan = $field_ui ? 5 : 3;
545
  $header = array(
546
    array('data' => t('Name')),
547
    array('data' => t('Operations'), 'colspan' => $colspan),
548
    array('data' => t('Status')),
549
  );
550
  $rows = array();
551
  $weight = 0;
552
  $types = file_type_load_all(TRUE);
553
  $count = count($types);
554
  foreach ($types as $type) {
555
    $weight++;
556
    $row = array(
557
      array(
558
        'data' => theme('file_entity_file_type_overview',
559
          array(
560
            'label' => $type->label,
561
            'description' => $type->description,
562
          )
563
        ),
564
      ),
565
    );
566
    $path = isset($file_entity_info['bundles'][$type->type]['admin']['real path']) ? $file_entity_info['bundles'][$type->type]['admin']['real path'] : NULL;
567

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

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

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

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

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

    
624
  // Move disabled items to the bottom.
625
  ksort($rows);
626

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

    
637
  return $build;
638
}
639

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

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

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

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

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

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

    
743
  return $form;
744
}
745

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

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

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

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

    
792
  return $output;
793
}
794

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

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

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

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

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

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

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

    
864
  $form['actions'] = array('#type' => 'actions');
865

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

    
877
  return $form;
878
}
879

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

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

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

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

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

    
926
  file_type_save($type);
927

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

    
932

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

    
943

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

    
956

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

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

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

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

    
980

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

    
993

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

    
1004

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

    
1018

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

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

    
1029
  $num_files = db_query("SELECT COUNT(*) FROM {file_managed} WHERE type = :type", array(':type' => $type->type))->fetchField();
1030
  if ($num_files) {
1031
    $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>';
1032
  }
1033

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

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

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

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

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

    
1053
/**
1054
 * Form callback for file_entity settings.
1055
 */
1056
function file_entity_settings_form($form, &$form_state) {
1057
  $form['file_entity_max_filesize'] = array(
1058
    '#type' => 'textfield',
1059
    '#title' => t('Maximum upload size'),
1060
    '#default_value' => variable_get('file_entity_max_filesize', ''),
1061
    '#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()))),
1062
    '#size' => 10,
1063
    '#element_validate' => array('_file_generic_settings_max_filesize'),
1064
  );
1065

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

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

    
1087
  // Provide default token values.
1088
  if (module_exists('token')) {
1089
    $form['token_help'] = array(
1090
      '#theme' => 'token_tree',
1091
      '#token_types' => array('file'),
1092
      '#dialog' => TRUE,
1093
    );
1094
    $form['file_entity_alt']['#description'] .= t('This field supports tokens.');
1095
    $form['file_entity_title']['#description'] .= t('This field supports tokens.');
1096
  }
1097
  $form['file_upload_wizard'] = array(
1098
    '#type' => 'fieldset',
1099
    '#title' => t('File upload wizard'),
1100
    '#collapsible' => TRUE,
1101
    '#collapsed' => FALSE,
1102
    '#description' => t('Configure the steps available when uploading a new file.'),
1103
  );
1104
  $form['file_upload_wizard']['file_entity_file_upload_wizard_skip_file_type'] = array(
1105
    '#type' => 'checkbox',
1106
    '#title' => t('Skip filetype selection.'),
1107
    '#default_value' => variable_get('file_entity_file_upload_wizard_skip_file_type', FALSE),
1108
    '#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.'),
1109
  );
1110
  $form['file_upload_wizard']['file_entity_file_upload_wizard_skip_scheme'] = array(
1111
    '#type' => 'checkbox',
1112
    '#title' => t('Skip scheme selection.'),
1113
    '#default_value' => variable_get('file_entity_file_upload_wizard_skip_scheme', FALSE),
1114
    '#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.'),
1115
  );
1116
  $form['file_upload_wizard']['file_entity_file_upload_wizard_skip_fields'] = array(
1117
    '#type' => 'checkbox',
1118
    '#title' => t('Skip available fields.'),
1119
    '#default_value' => variable_get('file_entity_file_upload_wizard_skip_fields', FALSE),
1120
    '#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.'),
1121
  );
1122

    
1123
  return system_settings_form($form);
1124
}