Project

General

Profile

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

root / drupal7 / sites / all / modules / file_entity / file_entity.admin.inc @ 082b75eb

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
    $total_count = (int) isset($result[$file->fid]->total_count) ? $result[$file->fid]->total_count : 0;
389
    $options[$file->fid] = array(
390
      'title' => array(
391
        'data' => array(
392
          '#type' => 'link',
393
          '#title' => $file->filename,
394
          '#href' => 'file/' . $file->fid,
395
        ),
396
      ),
397
      'type' => $file_type ? check_plain($file_type->label) : FILE_TYPE_NONE,
398
      'size' => format_size($file->filesize),
399
      'author' => theme('username', array('account' => $account)),
400
      'timestamp' => format_date($file->timestamp, 'short'),
401
      'usage' => format_plural($total_count, '1 place', '@count places'),
402
    );
403

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
668
  return $build;
669
}
670

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

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

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

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

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

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

    
774
  return $form;
775
}
776

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

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

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

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

    
823
  return $output;
824
}
825

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

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

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

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

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

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

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

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

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

    
908
  return $form;
909
}
910

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

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

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

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

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

    
957
  file_type_save($type);
958

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

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

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

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

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

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

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

    
1007

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

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

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

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

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

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

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

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

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

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

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

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

    
1087
  $form['file_entity_default_file_directory'] = array(
1088
    '#type' => 'textfield',
1089
    '#title' => t('Default file directory'),
1090
    '#default_value' => variable_get('file_entity_default_file_directory', ''),
1091
    '#maxlength' => NULL,
1092
  );
1093
  if (module_exists('token')) {
1094
    $form['file_entity_default_file_directory']['#description'] = t('Optional subdirectory within the upload destination where files will be stored if the file is uploaded through the file entity overview page and the directory is not specified otherwise. Do not include preceding or trailing slashes. This field supports tokens.  Suggest using: [current-date:custom:Y]/[current-date:custom:m]/[current-date:custom:d]');
1095
    $form['file_entity_default_file_directory']['tokens'] = array(
1096
      '#theme' => 'token_tree',
1097
      '#dialog' => TRUE,
1098
    );
1099
  }
1100
  else {
1101
    $form['file_entity_default_file_directory']['#description'] = t('Optional subdirectory within the upload destination where files will be stored if the file is uploaded through the file entity overview page and the directory is not specified otherwise. Do not include preceding or trailing slashes.');
1102
  }
1103

    
1104
  $form['file_entity_default_allowed_extensions'] = array(
1105
    '#type' => 'textfield',
1106
    '#title' => t('Default allowed file extensions'),
1107
    '#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'),
1108
    '#description' => t('Separate extensions with a space and do not include the leading dot.'),
1109
    '#maxlength' => NULL,
1110
  );
1111

    
1112
  $form['file_entity_alt'] = array(
1113
    '#type' => 'textfield',
1114
    '#title' => t('Alt attribute'),
1115
    '#description' => t('The text to use as value for the <em>img</em> tag <em>alt</em> attribute.'),
1116
    '#default_value' => variable_get('file_entity_alt', '[file:field_file_image_alt_text]'),
1117
  );
1118
  $form['file_entity_title'] = array(
1119
    '#type' => 'textfield',
1120
    '#title' => t('Title attribute'),
1121
    '#description' => t('The text to use as value for the <em>img</em> tag <em>title</em> attribute.'),
1122
    '#default_value' => variable_get('file_entity_title', '[file:field_file_image_title_text]'),
1123
  );
1124

    
1125
  // Provide default token values.
1126
  if (module_exists('token')) {
1127
    $form['token_help'] = array(
1128
      '#theme' => 'token_tree',
1129
      '#token_types' => array('file'),
1130
      '#dialog' => TRUE,
1131
    );
1132
    $form['file_entity_alt']['#description'] .= t('This field supports tokens.');
1133
    $form['file_entity_title']['#description'] .= t('This field supports tokens.');
1134
  }
1135
  $form['file_upload_wizard'] = array(
1136
    '#type' => 'fieldset',
1137
    '#title' => t('File upload wizard'),
1138
    '#collapsible' => TRUE,
1139
    '#collapsed' => FALSE,
1140
    '#description' => t('Configure the steps available when uploading a new file.'),
1141
  );
1142
  $form['file_upload_wizard']['file_entity_file_upload_wizard_skip_file_type'] = array(
1143
    '#type' => 'checkbox',
1144
    '#title' => t('Skip filetype selection.'),
1145
    '#default_value' => variable_get('file_entity_file_upload_wizard_skip_file_type', FALSE),
1146
    '#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.'),
1147
  );
1148
  $form['file_upload_wizard']['file_entity_file_upload_wizard_skip_scheme'] = array(
1149
    '#type' => 'checkbox',
1150
    '#title' => t('Skip scheme selection.'),
1151
    '#default_value' => variable_get('file_entity_file_upload_wizard_skip_scheme', FALSE),
1152
    '#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.'),
1153
  );
1154
  $form['file_upload_wizard']['file_entity_file_upload_wizard_skip_fields'] = array(
1155
    '#type' => 'checkbox',
1156
    '#title' => t('Skip available fields.'),
1157
    '#default_value' => variable_get('file_entity_file_upload_wizard_skip_fields', FALSE),
1158
    '#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.'),
1159
  );
1160
  $form['file_replace_options'] = array(
1161
    '#type' => 'fieldset',
1162
    '#title' => t('File replace optons'),
1163
    '#collapsible' => TRUE,
1164
    '#collapsed' => FALSE,
1165
    '#description' => t('Default settings for how to handle file name changes during replace.'),
1166
  );
1167
  $form['file_replace_options']['file_entity_file_replace_options_keep_original_filename'] = array(
1168
    '#type' => 'checkbox',
1169
    '#title' => t('Keep original file name'),
1170
    '#default_value' => variable_get('file_entity_file_replace_options_keep_original_filename', FALSE),
1171
    '#description' => t('Rename the newly uploaded file to the name of the original file. This action cannot be undone.'),
1172
  );
1173

    
1174
  return system_settings_form($form);
1175
}