Projet

Général

Profil

Paste
Télécharger (40 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / file_entity / file_entity.admin.inc @ 3acd948f

1
<?php
2

    
3
/**
4
 * @file
5
 * File administration and module settings UI.
6
 */
7

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

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

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

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

    
54
    }
55
  }
56
}
57

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

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

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

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

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

    
129
  return $form;
130
}
131

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

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

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

    
163
  }
164
}
165

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

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

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

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

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

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

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

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

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

    
284
  return $form;
285
}
286

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

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

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

    
333
  if (variable_get('file_entity_total_count_optimization', FALSE)) {
334
    // If the total_count is being retrieved by subqueries,
335
    // the table is not sortable by this column.
336
    unset($header['usage']['field']);
337
  }
338

    
339
  $query = db_select('file_managed', 'fm')->extend('PagerDefault')->extend('TableSort');
340
  if (!variable_get('file_entity_total_count_optimization', FALSE)) {
341
    $query->leftJoin('file_usage', 'fu', 'fm.fid = fu.fid');
342
    $query->groupBy('fm.fid');
343
    $query->groupBy('fm.uid');
344
    $query->groupBy('fm.timestamp');
345
    $query->addExpression('SUM(fu.count)', 'total_count');
346
  }
347
  file_entity_build_filter_query($query);
348

    
349
  $result = $query
350
    ->fields('fm', array('fid', 'uid'))
351
    ->limit(50)
352
    ->orderByHeader($header)
353
    ->addTag('file_access')
354
    ->execute()
355
    ->fetchAllAssoc('fid');
356

    
357
  if (variable_get('file_entity_total_count_optimization', FALSE)) {
358
    // Get the total_count in separate queries, otherwise the main
359
    // query will take too long.
360
    // This setting can be configured under /admin/config/development/performance.
361
    foreach ($result as &$file_result) {
362
      $count_query = db_select('file_usage', 'fu')
363
        ->fields('fu', array('fid', 'count'))
364
        ->condition('fu.fid', $file_result->fid, '=');
365
      $count_query->addExpression('SUM(fu.count)', 'total_count');
366
      $count_result = $count_query->execute()->fetchAll();
367

    
368
      if (!empty($count_result[0]->total_count)) {
369
        $file_result->total_count = $count_result[0]->total_count;
370
      }
371
    }
372
  }
373

    
374
  $files = file_load_multiple(array_keys($result));
375

    
376
  $uids = array();
377
  foreach ($files as $file) {
378
    $uids[] = $file->uid;
379
  }
380
  $accounts = !empty($uids) ? user_load_multiple(array_unique($uids)) : array();
381

    
382
  // Prepare the list of files.
383
  $destination = drupal_get_destination();
384
  $options = array();
385
  foreach ($files as $file) {
386
    $file_type = file_type_load($file->type);
387
    $account = isset($accounts[$file->uid]) ? $accounts[$file->uid] : NULL;
388
    $options[$file->fid] = array(
389
      'title' => array(
390
        'data' => array(
391
          '#type' => 'link',
392
          '#title' => $file->filename,
393
          '#href' => 'file/' . $file->fid,
394
        ),
395
      ),
396
      'type' => $file_type ? check_plain($file_type->label) : FILE_TYPE_NONE,
397
      'size' => format_size($file->filesize),
398
      'author' => theme('username', array('account' => $account)),
399
      'timestamp' => format_date($file->timestamp, 'short'),
400
      'usage' => format_plural((int) $result[$file->fid]->total_count, '1 place', '@count places'),
401
    );
402

    
403
    // Show a warning for files that do not exist.
404
    if (@!is_file($file->uri)) {
405
      $options[$file->fid]['#attributes']['class'][] = 'error';
406
      if (!file_stream_wrapper_get_instance_by_uri($file->uri)) {
407
        $options[$file->fid]['#attributes']['title'] = t('The stream wrapper for @scheme files is missing.', array('@scheme' => file_uri_scheme($file->uri)));
408
      }
409
      else {
410
        $options[$file->fid]['#attributes']['title'] = t('The file does not exist.');
411
      }
412
    }
413

    
414
    // Build a list of all the accessible operations for the current file.
415
    $operations = array();
416
    if (file_entity_access('update', $file)) {
417
      // Convert the usage count to a link.
418
      $options[$file->fid]['usage'] = l($options[$file->fid]['usage'], 'file/' . $file->fid . '/usage');
419
      $operations['edit'] = array(
420
        'title' => t('Edit'),
421
        'href' => 'file/' . $file->fid . '/edit',
422
        'query' => $destination,
423
      );
424
    }
425
    if (file_entity_access('delete', $file)) {
426
      $operations['delete'] = array(
427
        'title' => t('Delete'),
428
        'href' => 'file/' . $file->fid . '/delete',
429
        'query' => $destination,
430
      );
431
    }
432
    $options[$file->fid]['operations'] = array();
433
    if (count($operations) > 1) {
434
      // Render an unordered list of operations links.
435
      $options[$file->fid]['operations'] = array(
436
        'data' => array(
437
          '#theme' => 'links__file_entity_operations',
438
          '#links' => $operations,
439
          '#attributes' => array('class' => array('links', 'inline')),
440
        ),
441
      );
442
    }
443
    elseif (!empty($operations)) {
444
      // Render the first and only operation as a link.
445
      $link = reset($operations);
446
      $options[$file->fid]['operations'] = array(
447
        'data' => array(
448
          '#type' => 'link',
449
          '#title' => $link['title'],
450
          '#href' => $link['href'],
451
          '#options' => array('query' => $link['query']),
452
        ),
453
      );
454
    }
455
  }
456

    
457
  // Only use a tableselect when the current user is able to perform any
458
  // operations.
459
  if ($admin_access) {
460
    $form['files'] = array(
461
      '#type' => 'tableselect',
462
      '#header' => $header,
463
      '#options' => $options,
464
      '#empty' => t('No files available.'),
465
    );
466
  }
467
  // Otherwise, use a simple table.
468
  else {
469
    $form['files'] = array(
470
      '#theme' => 'table',
471
      '#header' => $header,
472
      '#rows' => $options,
473
      '#empty' => t('No files available.'),
474
    );
475
  }
476

    
477
  $form['pager'] = array('#markup' => theme('pager'));
478
  return $form;
479
}
480

    
481
/**
482
 * Validate file_entity_admin_files form submissions.
483
 *
484
 * Check if any files have been selected to perform the chosen
485
 * 'Update option' on.
486
 */
487
function file_entity_admin_files_validate($form, &$form_state) {
488
  // Error if there are no items to select.
489
  if (!is_array($form_state['values']['files']) || !count(array_filter($form_state['values']['files']))) {
490
    form_set_error('', t('No items selected.'));
491
  }
492
}
493

    
494
/**
495
 * Process file_entity_admin_files form submissions.
496
 *
497
 * Execute the chosen 'Update option' on the selected files.
498
 */
499
function file_entity_admin_files_submit($form, &$form_state) {
500
  $operations = module_invoke_all('file_operations');
501
  $operation = $operations[$form_state['values']['operation']];
502
  // Filter out unchecked files.
503
  $files = array_filter($form_state['values']['files']);
504
  if ($function = $operation['callback']) {
505
    // Add in callback arguments if present.
506
    if (isset($operation['callback arguments'])) {
507
      $args = array_merge(array($files), $operation['callback arguments']);
508
    }
509
    else {
510
      $args = array($files);
511
    }
512
    call_user_func_array($function, $args);
513

    
514
    cache_clear_all();
515
  }
516
  else {
517
    // We need to rebuild the form to go to a second step. For example, to
518
    // show the confirmation form for the deletion of files.
519
    $form_state['rebuild'] = TRUE;
520
  }
521
}
522

    
523
/**
524
 * File entity delete confirmation.
525
 */
526
function file_entity_multiple_delete_confirm($form, &$form_state, $files) {
527
  $form['files'] = array(
528
    '#prefix' => '<ul>',
529
    '#suffix' => '</ul>',
530
    '#tree' => TRUE,
531
  );
532
  // array_filter returns only elements with TRUE values.
533
  foreach ($files as $fid => $value) {
534
    $filename = db_query('SELECT filename FROM {file_managed} WHERE fid = :fid', array(':fid' => $fid))->fetchField();
535
    $form['files'][$fid] = array(
536
      '#type' => 'hidden',
537
      '#value' => $fid,
538
      '#prefix' => '<li>',
539
      '#suffix' => check_plain($filename) . "</li>\n",
540
    );
541
  }
542
  $form['operation'] = array('#type' => 'hidden', '#value' => 'delete');
543
  $form['#submit'][] = 'file_entity_multiple_delete_confirm_submit';
544
  $confirm_question = format_plural(count($files),
545
                                  'Are you sure you want to delete this item?',
546
                                  'Are you sure you want to delete these items?');
547
  return confirm_form($form,
548
                    $confirm_question,
549
                    'admin/content/file', t('This action cannot be undone.'),
550
                    t('Delete'), t('Cancel'));
551
}
552

    
553
/**
554
 * Submit handler for delete confirmation.
555
 */
556
function file_entity_multiple_delete_confirm_submit($form, &$form_state) {
557
  if ($form_state['values']['confirm']) {
558
    file_delete_multiple(array_keys($form_state['values']['files']));
559
    $count = count($form_state['values']['files']);
560
    watchdog('file_entity', 'Deleted @count files.', array('@count' => $count));
561
    drupal_set_message(format_plural($count, 'Deleted 1 file.', 'Deleted @count files.'));
562
  }
563
  $form_state['redirect'] = 'admin/content/file';
564
}
565

    
566
/**
567
 * Displays the file type admin overview page.
568
 */
569
function file_entity_list_types_page() {
570
  $file_entity_info = entity_get_info('file');
571
  // Newer versions of Drupal core require the "administer fields" permission
572
  // to access the Field UI.
573
  $field_ui = module_exists('field_ui') && (user_access('administer fields') || !function_exists('field_ui_admin_access'));
574
  $colspan = $field_ui ? 5 : 3;
575
  $header = array(
576
    array('data' => t('Name')),
577
    array('data' => t('Operations'), 'colspan' => $colspan),
578
    array('data' => t('Status')),
579
  );
580
  $rows = array();
581
  $weight = 0;
582
  $types = file_type_load_all(TRUE);
583
  $count = count($types);
584
  foreach ($types as $type) {
585
    $weight++;
586
    $row = array(
587
      array(
588
        'data' => theme('file_entity_file_type_overview',
589
          array(
590
            'label' => $type->label,
591
            'description' => $type->description,
592
          )
593
        ),
594
      ),
595
    );
596
    $path = isset($file_entity_info['bundles'][$type->type]['admin']['real path']) ? $file_entity_info['bundles'][$type->type]['admin']['real path'] : NULL;
597

    
598
    if (empty($type->disabled) && isset($path)) {
599
      $row[] = array('data' => l(t('edit file type'), $path . '/edit'));
600
      if ($field_ui) {
601
        $row[] = array('data' => l(t('manage fields'), $path . '/fields'));
602
        $row[] = array('data' => l(t('manage display'), $path . '/display'));
603
      }
604
      $row[] = array('data' => l(t('manage file display'), $path . '/file-display'));
605
    }
606
    else {
607
      $row += array_fill(1, $colspan - 1, '');
608
    }
609

    
610
    $admin_path = 'admin/structure/file-types/manage/' . $type->type;
611
    switch ($type->ctools_type) {
612
      // Configuration is in code.
613
      case 'Default':
614
        if (!empty($type->disabled)) {
615
          $row[] = l(t('enable'), $admin_path . '/enable');
616
        }
617
        else {
618
          $row[] = l(t('disable'), $admin_path . '/disable');
619
        }
620
        break;
621

    
622
      // Configuration is in DB.
623
      case 'Normal':
624
        if (!empty($type->disabled)) {
625
          $status = l(t('enable'), $admin_path . '/enable');
626
        }
627
        else {
628
          $status = l(t('disable'), $admin_path . '/disable');
629
        }
630
        $row[] = $status . ' | ' . l(t('delete'), $admin_path . '/delete');
631
        break;
632

    
633
      // Configuration is in code, but overridden in DB.
634
      case 'Overridden':
635
        if (!empty($type->disabled)) {
636
          $row[] = l(t('enable'), $admin_path . '/enable');
637
        }
638
        else {
639
          $row[] = l(t('disable'), $admin_path . '/disable') . ' | ' . l(t('revert'), $admin_path . '/revert');
640
        }
641
        break;
642
    }
643

    
644
    if (!empty($type->disabled)) {
645
      $row[] = t('Disabled');
646
      $rows[$weight + $count] = array('data' => $row, 'class' => array('ctools-export-ui-disabled'));
647
    }
648
    else {
649
      $row[] = $type->ctools_type;
650
      $rows[$weight] = array('data' => $row);
651
    }
652
  }
653

    
654
  // Move disabled items to the bottom.
655
  ksort($rows);
656

    
657
  $build['file_type_table'] = array(
658
    '#theme' => 'table',
659
    '#header' => $header,
660
    '#rows' => $rows,
661
    '#empty' => t('No file types available.'),
662
    '#attached' => array(
663
      'css' => array(drupal_get_path('module', 'ctools') . '/css/export-ui-list.css'),
664
    ),
665
  );
666

    
667
  return $build;
668
}
669

    
670
/**
671
 * Form callback; presents file display settings for a given view mode.
672
 */
673
function file_entity_file_display_form($form, &$form_state, $file_type, $view_mode) {
674
  $form['#file_type'] = $file_type->type;
675
  $form['#view_mode'] = $view_mode;
676
  $form['#tree'] = TRUE;
677
  $form['#attached']['js'][] = drupal_get_path('module', 'file_entity') . '/file_entity.admin.js';
678

    
679
  // Retrieve available formatters for this file type and load all configured
680
  // filters for existing text formats.
681
  $formatters = file_info_formatter_types();
682
  foreach ($formatters as $name => $formatter) {
683
    if (!empty($formatter['hidden'])) {
684
      unset($formatters[$name]);
685
    }
686
    if (isset($formatter['mime types'])) {
687
      if (file_entity_match_mimetypes($formatter['mime types'], $file_type->mimetypes)) {
688
        continue;
689
      }
690
      unset($formatters[$name]);
691
    }
692
  }
693
  $current_displays = file_displays_load($file_type->type, $view_mode, TRUE);
694
  foreach ($current_displays as $name => $display) {
695
    $current_displays[$name] = (array) $display;
696
  }
697

    
698
  // Formatter status.
699
  $form['displays']['status'] = array(
700
    '#type' => 'item',
701
    '#title' => t('Enabled displays'),
702
    '#prefix' => '<div id="file-displays-status-wrapper">',
703
    '#suffix' => '</div>',
704
  );
705
  $i = 0;
706
  foreach ($formatters as $name => $formatter) {
707
    $form['displays']['status'][$name] = array(
708
      '#type' => 'checkbox',
709
      '#title' => check_plain($formatter['label']),
710
      '#default_value' => !empty($current_displays[$name]['status']),
711
      '#description' => isset($formatter['description']) ? filter_xss($formatter['description']) : NULL,
712
      '#parents' => array('displays', $name, 'status'),
713
      '#weight' => (isset($formatter['weight']) ? $formatter['weight'] : 0) + ($i / 1000),
714
    );
715
    $i++;
716
  }
717

    
718
  // Formatter order (tabledrag).
719
  $form['displays']['order'] = array(
720
    '#type' => 'item',
721
    '#title' => t('Display precedence order'),
722
    '#theme' => 'file_entity_file_display_order',
723
  );
724
  foreach ($formatters as $name => $formatter) {
725
    $form['displays']['order'][$name]['label'] = array(
726
      '#markup' => check_plain($formatter['label']),
727
    );
728
    $form['displays']['order'][$name]['weight'] = array(
729
      '#type' => 'weight',
730
      '#title' => t('Weight for @title', array('@title' => $formatter['label'])),
731
      '#title_display' => 'invisible',
732
      '#delta' => 50,
733
      '#default_value' => isset($current_displays[$name]['weight']) ? $current_displays[$name]['weight'] : 0,
734
      '#parents' => array('displays', $name, 'weight'),
735
    );
736
    $form['displays']['order'][$name]['#weight'] = $form['displays']['order'][$name]['weight']['#default_value'];
737
  }
738

    
739
  // Formatter settings.
740
  $form['display_settings_title'] = array(
741
    '#type' => 'item',
742
    '#title' => t('Display settings'),
743
  );
744
  $form['display_settings'] = array(
745
    '#type' => 'vertical_tabs',
746
  );
747
  $i = 0;
748
  foreach ($formatters as $name => $formatter) {
749
    if (isset($formatter['settings callback']) && ($function = $formatter['settings callback']) && function_exists($function)) {
750
      $defaults = !empty($formatter['default settings']) ? $formatter['default settings'] : array();
751
      $settings = !empty($current_displays[$name]['settings']) ? $current_displays[$name]['settings'] : array();
752
      $settings += $defaults;
753
      $settings_form = $function($form, $form_state, $settings, $name, $file_type->type, $view_mode);
754
      if (!empty($settings_form)) {
755
        $form['displays']['settings'][$name] = array(
756
          '#type' => 'fieldset',
757
          '#title' => check_plain($formatter['label']),
758
          '#parents' => array('displays', $name, 'settings'),
759
          '#group' => 'display_settings',
760
          '#weight' => (isset($formatter['weight']) ? $formatter['weight'] : 0) + ($i / 1000),
761
        ) + $settings_form;
762
      }
763
    }
764
    $i++;
765
  }
766

    
767
  $form['actions'] = array('#type' => 'actions');
768
  $form['actions']['submit'] = array(
769
    '#type' => 'submit',
770
    '#value' => t('Save configuration'),
771
  );
772

    
773
  return $form;
774
}
775

    
776
/**
777
 * Process file display settings form submissions.
778
 */
779
function file_entity_file_display_form_submit($form, &$form_state) {
780
  $file_type = $form['#file_type'];
781
  $view_mode = $form['#view_mode'];
782
  $displays = isset($form_state['values']['displays']) ? $form_state['values']['displays'] : array();
783
  $displays_original = file_displays_load($file_type, $view_mode, TRUE);
784
  foreach ($displays as $formatter_name => $display) {
785
    $display_original = isset($displays_original[$formatter_name]) ? $displays_original[$formatter_name] : file_display_new($file_type, $view_mode, $formatter_name);
786
    $display += (array) $display_original;
787
    file_display_save((object) $display);
788
  }
789
  drupal_set_message(t('Your settings have been saved.'));
790
}
791

    
792
/**
793
 * Returns HTML for the file type overview page.
794
 *
795
 * Specifically, this returns HTML for a file type label and description.
796
 */
797
function theme_file_entity_file_type_overview($variables) {
798
  return check_plain($variables['label']) . '<div class="description">' . $variables['description'] . '</div>';
799
}
800

    
801
/**
802
 * Returns HTML for a file display's display order table.
803
 */
804
function theme_file_entity_file_display_order($variables) {
805
  $element = $variables['element'];
806

    
807
  $rows = array();
808
  foreach (element_children($element, TRUE) as $name) {
809
    $element[$name]['weight']['#attributes']['class'][] = 'file-display-order-weight';
810
    $rows[] = array(
811
      'data' => array(
812
        drupal_render($element[$name]['label']),
813
        drupal_render($element[$name]['weight']),
814
      ),
815
      'class' => array('draggable'),
816
    );
817
  }
818
  $output = drupal_render_children($element);
819
  $output .= theme('table', array('rows' => $rows, 'attributes' => array('id' => 'file-displays-order')));
820
  drupal_add_tabledrag('file-displays-order', 'order', 'sibling', 'file-display-order-weight', NULL, NULL, TRUE);
821

    
822
  return $output;
823
}
824

    
825
/**
826
 * Form constructor for the file type settings form.
827
 *
828
 * @param object $type
829
 *   The file type.
830
 *
831
 * @see file_entity_file_type_form_validate()
832
 * @see file_entity_file_type_form_submit()
833
 */
834
function file_entity_file_type_form($form, &$form_state, $type = NULL) {
835
  if (!isset($type->type)) {
836
    // This is a new type.
837
    $type = (object) array(
838
      'type' => '',
839
      'label' => '',
840
      'description' => '',
841
      'mimetypes' => array(),
842
    );
843
  }
844
  $form['#file_type'] = $type;
845

    
846
  $form['label'] = array(
847
    '#type' => 'textfield',
848
    '#title' => t('Name'),
849
    '#description' => t('This is the human readable name of the file type.'),
850
    '#required' => TRUE,
851
    '#default_value' => $type->label,
852
  );
853

    
854
  $form['type'] = array(
855
    '#type' => 'machine_name',
856
    '#default_value' => $type->type,
857
    '#maxlength' => 255,
858
    '#disabled' => (bool) $type->type,
859
    '#machine_name' => array(
860
      'exists' => 'file_type_load',
861
      'source' => array('label'),
862
    ),
863
    '#description' => t('A unique machine-readable name for this file type. It must only contain lowercase letters, numbers, and underscores.'),
864
  );
865

    
866
  $form['description'] = array(
867
    '#type' => 'textarea',
868
    '#title' => t('Description'),
869
    '#description' => t('This is the description of the file type.'),
870
    '#default_value' => $type->description,
871
  );
872

    
873
  $form['mimetypes'] = array(
874
    '#type' => 'textarea',
875
    '#title' => t('Mimetypes'),
876
    '#description' => t('Enter one mimetype per line.'),
877
    '#default_value' => implode("\n", $type->mimetypes),
878
  );
879

    
880
  include_once DRUPAL_ROOT . '/includes/file.mimetypes.inc';
881
  $mimetypes = file_mimetype_mapping();
882

    
883
  $form['mimetype_mapping'] = array(
884
    '#type' => 'fieldset',
885
    '#title' => t('Mimetype List'),
886
    '#collapsible' => TRUE,
887
    '#collapsed' => TRUE,
888
  );
889
  $form['mimetype_mapping']['mapping'] = array(
890
    '#theme' => 'item_list',
891
    '#items' => $mimetypes['mimetypes'],
892
  );
893

    
894
  $form['actions'] = array('#type' => 'actions');
895

    
896
  $form['actions']['submit'] = array(
897
    '#type' => 'submit',
898
    '#value' => t('Save'),
899
  );
900
  if (!empty($type->type)) {
901
    $form['actions']['delete'] = array(
902
      '#type' => 'submit',
903
      '#value' => t('Delete'),
904
    );
905
  }
906

    
907
  return $form;
908
}
909

    
910
/**
911
 * Form validation handler for file_entity_file_type_form().
912
 *
913
 * @see file_entity_file_type_form_submit()
914
 */
915
function file_entity_file_type_form_validate($form, &$form_state) {
916
  include_once DRUPAL_ROOT . '/includes/file.mimetypes.inc';
917
  $mimetype_mapping = file_mimetype_mapping();
918

    
919
  $valid_mimetypes = $mimetype_mapping['mimetypes'];
920
  $submitted_mimetypes = array_filter(array_map('trim', explode("\n", $form_state['values']['mimetypes'])));
921

    
922
  $invalid_mimetypes = array();
923
  foreach ($submitted_mimetypes as $mimetype) {
924
    if (!file_entity_match_mimetypes($mimetype, $valid_mimetypes)) {
925
      $invalid_mimetypes[] = $mimetype;
926
    }
927
  }
928

    
929
  foreach ($invalid_mimetypes as $mimetype) {
930
    form_set_error('mimetypes', t('The mimetype %mimetype is not a valid mimetype.', array('%mimetype' => $mimetype)));
931
  }
932
}
933

    
934
/**
935
 * Form submission handler for file_entity_file_type_form().
936
 *
937
 * @see file_entity_file_type_form_validate()
938
 */
939
function file_entity_file_type_form_submit($form, &$form_state) {
940
  if (!empty($form['#file_type']->type)) {
941
    $type = file_type_load($form['#file_type']->type);
942
  }
943
  else {
944
    $type = (object) array(
945
      'type' => $form_state['values']['type'],
946
    );
947
  }
948
  if ($form_state['values']['op'] == t('Delete')) {
949
    $form_state['redirect'] = 'admin/structure/file-types/manage/' . $type->type . '/delete';
950
    return;
951
  }
952
  $type->label = $form_state['values']['label'];
953
  $type->description = $form_state['values']['description'];
954
  $type->mimetypes = array_filter(array_map('trim', explode("\n", $form_state['values']['mimetypes'])));
955

    
956
  file_type_save($type);
957

    
958
  drupal_set_message(t('The file type %type has been updated.', array('%type' => $type->label)));
959
  $form_state['redirect'] = 'admin/structure/file-types';
960
}
961

    
962
/**
963
 * Menu callback; disable a single file type.
964
 */
965
function file_entity_type_enable_confirm($form, &$form_state, $type) {
966
  $form['type'] = array('#type' => 'value', '#value' => $type->type);
967
  $form['label'] = array('#type' => 'value', '#value' => $type->label);
968
  $message = t('Are you sure you want to enable the file type %type?', array('%type' => $type->label));
969
  return confirm_form($form, $message, 'admin/structure/file-types', '', t('Enable'));
970
}
971

    
972
/**
973
 * Process file type disable confirm submissions.
974
 */
975
function file_entity_type_enable_confirm_submit($form, &$form_state) {
976
  file_type_enable($form_state['values']['type']);
977
  $t_args = array('%label' => $form_state['values']['label']);
978
  drupal_set_message(t('The file type %label has been enabled.', $t_args));
979
  watchdog('file_entity', 'Enabled file type %label.', $t_args, WATCHDOG_NOTICE);
980
  $form_state['redirect'] = 'admin/structure/file-types';
981
}
982

    
983
/**
984
 * Menu callback; disable a single file type.
985
 */
986
function file_entity_type_disable_confirm($form, &$form_state, $type) {
987
  $form['type'] = array('#type' => 'value', '#value' => $type->type);
988
  $form['label'] = array('#type' => 'value', '#value' => $type->label);
989

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

    
993
  $num_files = db_query("SELECT COUNT(*) FROM {file_managed} WHERE type = :type", array(':type' => $type->type))->fetchField();
994
  if ($num_files) {
995
    $caption .= '<p>' . format_plural($num_files, '%type is used by 1 file on
996
      your site. If you disable this file type, you will not be able to edit
997
      the %type file and it may not display correctly.', '%type is used by
998
      @count files on your site. If you remove %type, you will not be able to
999
      edit the %type file and it may not display correctly.',
1000
    array('%type' => $type->label)) . '</p>';
1001
  }
1002

    
1003
  return confirm_form($form, $message, 'admin/structure/file-types', $caption, t('Disable'));
1004
}
1005

    
1006

    
1007
/**
1008
 * Process file type disable confirm submissions.
1009
 */
1010
function file_entity_type_disable_confirm_submit($form, &$form_state) {
1011
  file_type_disable($form_state['values']['type']);
1012
  $t_args = array('%label' => $form_state['values']['label']);
1013
  drupal_set_message(t('The file type %label has been disabled.', $t_args));
1014
  watchdog('file_entity', 'Disabled file type %label.', $t_args, WATCHDOG_NOTICE);
1015
  $form_state['redirect'] = 'admin/structure/file-types';
1016
}
1017

    
1018
/**
1019
 * Menu callback; revert a single file type.
1020
 */
1021
function file_entity_type_revert_confirm($form, &$form_state, $type) {
1022
  $form['type'] = array('#type' => 'value', '#value' => $type->type);
1023
  $form['label'] = array('#type' => 'value', '#value' => $type->label);
1024
  $message = t('Are you sure you want to revert the file type %type?', array('%type' => $type->label));
1025
  return confirm_form($form, $message, 'admin/structure/file-types', '', t('Revert'));
1026
}
1027

    
1028
/**
1029
 * Process file type delete confirm submissions.
1030
 */
1031
function file_entity_type_revert_confirm_submit($form, &$form_state) {
1032
  // @NOTE deleting the file_type from the DB actually reverts it to code.
1033
  file_type_delete($form_state['values']['type']);
1034
  $t_args = array('%label' => $form_state['values']['label']);
1035
  drupal_set_message(t('The file type %label has been reverted.', $t_args));
1036
  watchdog('file_entity', 'Reverted file type %label.', $t_args, WATCHDOG_NOTICE);
1037
  $form_state['redirect'] = 'admin/structure/file-types';
1038
}
1039

    
1040
/**
1041
 * Menu callback; delete a single file type.
1042
 */
1043
function file_entity_type_delete_confirm($form, &$form_state, $type) {
1044
  $form['type'] = array('#type' => 'value', '#value' => $type->type);
1045
  $form['label'] = array('#type' => 'value', '#value' => $type->label);
1046

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

    
1050
  $num_files = db_query("SELECT COUNT(*) FROM {file_managed} WHERE type = :type", array(':type' => $type->type))->fetchField();
1051
  if ($num_files) {
1052
    $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>';
1053
  }
1054

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

    
1057
  return confirm_form($form, $message, 'admin/structure/file-types', $caption, t('Delete'));
1058
}
1059

    
1060
/**
1061
 * Process file type delete confirm submissions.
1062
 */
1063
function file_entity_type_delete_confirm_submit($form, &$form_state) {
1064
  file_type_delete($form_state['values']['type']);
1065

    
1066
  $t_args = array('%label' => $form_state['values']['label']);
1067
  drupal_set_message(t('The file type %label has been deleted.', $t_args));
1068
  watchdog('file_entity', 'Deleted file type %label.', $t_args, WATCHDOG_NOTICE);
1069

    
1070
  $form_state['redirect'] = 'admin/structure/file-types';
1071
}
1072

    
1073
/**
1074
 * Form callback for file_entity settings.
1075
 */
1076
function file_entity_settings_form($form, &$form_state) {
1077
  $form['file_entity_max_filesize'] = array(
1078
    '#type' => 'textfield',
1079
    '#title' => t('Maximum upload size'),
1080
    '#default_value' => variable_get('file_entity_max_filesize', ''),
1081
    '#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()))),
1082
    '#size' => 10,
1083
    '#element_validate' => array('_file_generic_settings_max_filesize'),
1084
  );
1085

    
1086
  $form['file_entity_default_allowed_extensions'] = array(
1087
    '#type' => 'textfield',
1088
    '#title' => t('Default allowed file extensions'),
1089
    '#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'),
1090
    '#description' => t('Separate extensions with a space or comma and do not include the leading dot.'),
1091
    '#maxlength' => NULL,
1092
  );
1093

    
1094
  $form['file_entity_alt'] = array(
1095
    '#type' => 'textfield',
1096
    '#title' => t('Alt attribute'),
1097
    '#description' => t('The text to use as value for the <em>img</em> tag <em>alt</em> attribute.'),
1098
    '#default_value' => variable_get('file_entity_alt', '[file:field_file_image_alt_text]'),
1099
  );
1100
  $form['file_entity_title'] = array(
1101
    '#type' => 'textfield',
1102
    '#title' => t('Title attribute'),
1103
    '#description' => t('The text to use as value for the <em>img</em> tag <em>title</em> attribute.'),
1104
    '#default_value' => variable_get('file_entity_title', '[file:field_file_image_title_text]'),
1105
  );
1106

    
1107
  // Provide default token values.
1108
  if (module_exists('token')) {
1109
    $form['token_help'] = array(
1110
      '#theme' => 'token_tree',
1111
      '#token_types' => array('file'),
1112
      '#dialog' => TRUE,
1113
    );
1114
    $form['file_entity_alt']['#description'] .= t('This field supports tokens.');
1115
    $form['file_entity_title']['#description'] .= t('This field supports tokens.');
1116
  }
1117
  $form['file_upload_wizard'] = array(
1118
    '#type' => 'fieldset',
1119
    '#title' => t('File upload wizard'),
1120
    '#collapsible' => TRUE,
1121
    '#collapsed' => FALSE,
1122
    '#description' => t('Configure the steps available when uploading a new file.'),
1123
  );
1124
  $form['file_upload_wizard']['file_entity_file_upload_wizard_skip_file_type'] = array(
1125
    '#type' => 'checkbox',
1126
    '#title' => t('Skip filetype selection.'),
1127
    '#default_value' => variable_get('file_entity_file_upload_wizard_skip_file_type', FALSE),
1128
    '#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.'),
1129
  );
1130
  $form['file_upload_wizard']['file_entity_file_upload_wizard_skip_scheme'] = array(
1131
    '#type' => 'checkbox',
1132
    '#title' => t('Skip scheme selection.'),
1133
    '#default_value' => variable_get('file_entity_file_upload_wizard_skip_scheme', FALSE),
1134
    '#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.'),
1135
  );
1136
  $form['file_upload_wizard']['file_entity_file_upload_wizard_skip_fields'] = array(
1137
    '#type' => 'checkbox',
1138
    '#title' => t('Skip available fields.'),
1139
    '#default_value' => variable_get('file_entity_file_upload_wizard_skip_fields', FALSE),
1140
    '#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.'),
1141
  );
1142

    
1143
  return system_settings_form($form);
1144
}