Projet

Général

Profil

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

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

1
<?php
2

    
3
/**
4
 * @file
5
 * API extensions of Drupal core's file.inc.
6
 */
7

    
8
/**
9
 * The {file_managed}.type value when the file type has not yet been determined.
10
 */
11
define('FILE_TYPE_NONE', 'undefined');
12

    
13
/**
14
 * Returns information about file formatters from hook_file_formatter_info().
15
 *
16
 * @param string $formatter_type
17
 *   (optional) A file formatter type name. If omitted, all file formatter
18
 *   will be returned.
19
 *
20
 * @return string|array
21
 *   Either a file formatter description, as provided by
22
 *   hook_file_formatter_info(), or an array of all existing file formatters,
23
 *   keyed by formatter type name.
24
 */
25
function file_info_formatter_types($formatter_type = NULL) {
26
  $info = &drupal_static(__FUNCTION__);
27
  if (!isset($info)) {
28
    $info = module_invoke_all('file_formatter_info');
29
    drupal_alter('file_formatter_info', $info);
30
    uasort($info, '_file_entity_sort_weight_label');
31
  }
32
  if ($formatter_type) {
33
    if (isset($info[$formatter_type])) {
34
      return $info[$formatter_type];
35
    }
36
  }
37
  else {
38
    return $info;
39
  }
40
}
41

    
42
/**
43
 * Clears caches that are related to file entity.
44
 *
45
 * Clears all cached configuration related to file types, formatters, and
46
 * display settings.
47
 */
48
function file_info_cache_clear() {
49
  // Clear the CTools managed caches.
50
  ctools_include('export');
51
  ctools_export_load_object_reset('file_type');
52
  ctools_export_load_object_reset('file_display');
53

    
54
  // Clear the formatter type cache, managed by file_info_formatter_types().
55
  drupal_static_reset('file_info_formatter_types');
56

    
57
  // Clear file type caches.
58
  drupal_static_reset('file_type_get_names');
59
}
60

    
61
/**
62
 * Construct a drupal_render() style array from an array of loaded files.
63
 *
64
 * @param array $files
65
 *   An array of files as returned by file_load_multiple().
66
 * @param string $view_mode
67
 *   View mode.
68
 * @param int $weight
69
 *   An integer representing the weight of the first file in the list.
70
 * @param string $langcode
71
 *   A string indicating the language field values are to be shown in. If no
72
 *   language is provided the current content language is used.
73
 *
74
 * @return array
75
 *   An array in the format expected by drupal_render().
76
 */
77
function file_view_multiple($files, $view_mode = 'full', $weight = 0, $langcode = NULL) {
78
  $build = array();
79

    
80
  $entities_by_view_mode = entity_view_mode_prepare('file', $files, $view_mode, $langcode);
81
  foreach ($entities_by_view_mode as $entity_view_mode => $entities) {
82
    field_attach_prepare_view('file', $entities, $entity_view_mode, $langcode);
83
    entity_prepare_view('file', $entities, $langcode);
84

    
85
    foreach ($entities as $entity) {
86
      $build['files'][$entity->fid] = file_view($entity, $entity_view_mode, $langcode);
87
    }
88
  }
89

    
90
  foreach ($files as $file) {
91
    $build['files'][$file->fid]['#weight'] = $weight;
92
    $weight++;
93
  }
94
  // Sort here, to preserve the input order of the entities that were passed to
95
  // this function.
96
  uasort($build['files'], 'element_sort');
97
  $build['files']['#sorted'] = TRUE;
98

    
99
  return $build;
100
}
101

    
102
/**
103
 * Generate an array for rendering the given file.
104
 *
105
 * @param object $file
106
 *   A file object.
107
 * @param string $view_mode
108
 *   View mode.
109
 * @param string $langcode
110
 *   (optional) A language code to use for rendering. Defaults to the global
111
 *   content language of the current request.
112
 *
113
 * @return array
114
 *   An array as expected by drupal_render().
115
 */
116
function file_view($file, $view_mode = 'full', $langcode = NULL) {
117
  if (!isset($langcode)) {
118
    $langcode = $GLOBALS['language_content']->language;
119
  }
120

    
121
  // Populate $file->content with a render() array.
122
  file_build_content($file, $view_mode, $langcode);
123

    
124
  $build = $file->content;
125
  // We don't need duplicate rendering info in $file->content.
126
  unset($file->content);
127

    
128
  $build += array(
129
    '#theme' => 'file_entity',
130
    '#file' => $file,
131
    '#view_mode' => $view_mode,
132
    '#language' => $langcode,
133
  );
134

    
135
  // Add contextual links for this file, except when the file is already being
136
  // displayed on its own page. Modules may alter this behavior (for example,
137
  // to restrict contextual links to certain view modes) by implementing
138
  // hook_file_view_alter().
139
  if (!empty($file->fid) && !($view_mode == 'full' && file_entity_is_page($file))) {
140
    $build['#contextual_links']['file'] = array('file', array($file->fid));
141
  }
142

    
143
  // Allow modules to modify the structured file.
144
  $type = 'file';
145
  drupal_alter(array('file_view', 'entity_view'), $build, $type);
146

    
147
  return $build;
148
}
149

    
150
/**
151
 * Builds a structured array representing the file's content.
152
 *
153
 * @param object $file
154
 *   A file object.
155
 * @param string $view_mode
156
 *   View mode, e.g. 'default', 'full', etc.
157
 * @param string $langcode
158
 *   (optional) A language code to use for rendering. Defaults to the global
159
 *   content language of the current request.
160
 */
161
function file_build_content($file, $view_mode = 'full', $langcode = NULL) {
162
  if (!isset($langcode)) {
163
    $langcode = $GLOBALS['language_content']->language;
164
  }
165

    
166
  // Remove previously built content, if exists.
167
  $file->content = array();
168

    
169
  // In case of a multiple view, file_view_multiple() already ran the
170
  // 'prepare_view' step. An internal flag prevents the operation from running
171
  // twice.
172
  // Allow modules to change the view mode.
173
  $view_mode = key(entity_view_mode_prepare('file', array($file->fid => $file), $view_mode, $langcode));
174
  field_attach_prepare_view('file', array($file->fid => $file), $view_mode, $langcode);
175
  entity_prepare_view('file', array($file->fid => $file), $langcode);
176

    
177
  // Build the actual file display.
178
  // @todo Figure out how to clean this crap up.
179
  $file->content['file'] = file_view_file($file, $view_mode, $langcode);
180
  if (isset($file->content['file'])) {
181
    if (isset($file->content['file']['#theme']) && $file->content['file']['#theme'] != 'file_link') {
182
      unset($file->content['file']['#file']);
183
    }
184
    unset($file->content['file']['#view_mode']);
185
    unset($file->content['file']['#language']);
186
  }
187
  else {
188
    unset($file->content['file']);
189
  }
190

    
191
  // Build fields content.
192
  $file->content += field_attach_view('file', $file, $view_mode, $langcode);
193

    
194
  $links = array();
195
  $file->content['links'] = array(
196
    '#theme' => 'links__file',
197
    '#pre_render' => array('drupal_pre_render_links'),
198
    '#attributes' => array('class' => array('links', 'inline')),
199
  );
200
  $file->content['links']['file'] = array(
201
    '#theme' => 'links__file__file',
202
    '#links' => $links,
203
    '#attributes' => array('class' => array('links', 'inline')),
204
  );
205

    
206
  // Allow modules to make their own additions to the file.
207
  module_invoke_all('file_view', $file, $view_mode, $langcode);
208
  module_invoke_all('entity_view', $file, 'file', $view_mode, $langcode);
209
}
210

    
211
/**
212
 * Generate an array for rendering just the file portion of a file entity.
213
 *
214
 * @param object $file
215
 *   A file object.
216
 * @param string|array $displays
217
 *   Can be either:
218
 *   - the name of a view mode;
219
 *   - or an array of custom display settings, as returned by file_displays().
220
 * @param string $langcode
221
 *   (optional) A language code to use for rendering. Defaults to the global
222
 *   content language of the current request.
223
 *
224
 * @return array
225
 *   An array as expected by drupal_render().
226
 */
227
function file_view_file($file, $displays = 'full', $langcode = NULL) {
228
  if (!isset($langcode)) {
229
    $langcode = $GLOBALS['language_content']->language;
230
  }
231

    
232
  // Prepare incoming display specifications.
233
  if (is_string($displays)) {
234
    // Allow modules to change the view mode.
235
    $view_mode = key(entity_view_mode_prepare('file', array($file->fid => $file), $displays, $langcode));
236
    $displays = file_displays($file->type, $view_mode);
237
  }
238
  else {
239
    $view_mode = '_custom_display';
240
  }
241
  drupal_alter('file_displays', $displays, $file, $view_mode);
242
  _file_sort_array_by_weight($displays);
243

    
244
  // Attempt to display the file with each of the possible displays. Stop after
245
  // the first successful one. See file_displays() for details.
246
  $element = NULL;
247
  foreach ($displays as $formatter_type => $display) {
248
    if (!empty($display['status'])) {
249
      $formatter_info = file_info_formatter_types($formatter_type);
250
      // Under normal circumstances, the UI prevents enabling formatters for
251
      // incompatible MIME types. In case this was somehow circumvented (for
252
      // example, a module updated its formatter definition without updating
253
      // existing display settings), perform an extra check here.
254
      if (isset($formatter_info['mime types'])) {
255
        if (!file_entity_match_mimetypes($formatter_info['mime types'], $file->filemime)) {
256
          continue;
257
        }
258
      }
259
      if (isset($formatter_info['view callback']) && ($function = $formatter_info['view callback']) && function_exists($function)) {
260
        $display['type'] = $formatter_type;
261
        if (!empty($formatter_info['default settings'])) {
262
          if (empty($display['settings'])) {
263
            $display['settings'] = array();
264
          }
265
          $display['settings'] += $formatter_info['default settings'];
266
        }
267
        $element = $function($file, $display, $langcode);
268
        if (isset($element)) {
269
          break;
270
        }
271
      }
272
    }
273
  }
274

    
275
  // As a last resort, fall back to showing a link to the file.
276
  if (!isset($element)) {
277
    $element = array(
278
      '#theme' => 'file_link',
279
      '#file' => $file,
280
    );
281
  }
282

    
283
  // Add defaults and return the element.
284
  $element += array(
285
    '#file' => $file,
286
    '#view_mode' => $view_mode,
287
    '#language' => $langcode,
288
  );
289

    
290
  return $element;
291
}
292

    
293
/**
294
 * @defgroup file_displays File displays API
295
 * @{
296
 * Functions to load and save information about file displays.
297
 */
298

    
299
/**
300
 * Returns an array of displays to use for a file type in a given view mode.
301
 *
302
 * It is common for a site to be configured with broadly defined file types
303
 * (e.g., 'video'), and to have different files of this type require different
304
 * displays (for example, the code required to display a YouTube video is
305
 * different than the code required to display a local QuickTime video).
306
 * Therefore, the site administrator can configure multiple displays for a given
307
 * file type. This function returns all of the displays that the administrator
308
 * enabled for the given file type in the given view mode. file_view_file() then
309
 * invokes each of these, and passes the specific file to display. Each display
310
 * implementation can inspect the file, and either return a render array (if it
311
 * is capable of displaying the file), or return nothing (if it is incapable of
312
 * displaying the file). The first render array returned is the one used.
313
 *
314
 * @param string $file_type
315
 *   The type of file.
316
 * @param string $view_mode
317
 *   The view mode.
318
 *
319
 * @return array
320
 *   An array keyed by the formatter type name. Each item in the array contains
321
 *   the following key/value pairs:
322
 *   - status: Whether this display is enabled. If not TRUE, file_view_file()
323
 *     skips over it.
324
 *   - weight: An integer that determines the order of precedence within the
325
 *     returned array. The lowest weight display capable of displaying the file
326
 *     is used.
327
 *   - settings: An array of key/value pairs specific to the formatter type. See
328
 *     hook_file_formatter_info() for details.
329
 *
330
 * @see hook_file_formatter_info()
331
 * @see file_view_file()
332
 */
333
function file_displays($file_type, $view_mode) {
334
  $cache = &drupal_static(__FUNCTION__, array());
335

    
336
  // If the requested view mode isn't configured to use a custom display for its
337
  // fields, then don't use a custom display for its file either.
338
  if ($view_mode != 'default') {
339
    $view_mode_settings = field_view_mode_settings('file', $file_type);
340
    $view_mode = !empty($view_mode_settings[$view_mode]['custom_settings']) ? $view_mode : 'default';
341
  }
342

    
343
  if (!isset($cache[$file_type][$view_mode])) {
344
    // Load the display configurations for the file type and view mode. If none
345
    // exist for the view mode, use the default view mode.
346
    $displays = file_displays_load($file_type, $view_mode, TRUE);
347
    if (empty($displays) && $view_mode != 'default') {
348
      $cache[$file_type][$view_mode] = file_displays($file_type, 'default');
349
    }
350
    else {
351
      // Convert the display objects to arrays and remove unnecessary keys.
352
      foreach ($displays as $formatter_name => $display) {
353
        $displays[$formatter_name] = array_intersect_key((array) $display, drupal_map_assoc(array('status', 'weight', 'settings')));
354
      }
355
      $cache[$file_type][$view_mode] = $displays;
356
    }
357
  }
358

    
359
  return $cache[$file_type][$view_mode];
360
}
361

    
362
/**
363
 * Returns an array of {file_display} objects for the file type and view mode.
364
 */
365
function file_displays_load($file_type, $view_mode, $key_by_formatter_name = FALSE) {
366
  ctools_include('export');
367

    
368
  $display_names = array();
369
  $prefix = $file_type . '__' . $view_mode . '__';
370
  foreach (array_keys(file_info_formatter_types()) as $formatter_name) {
371
    $display_names[] = $prefix . $formatter_name;
372
  }
373
  $displays = ctools_export_load_object('file_display', 'names', $display_names);
374

    
375
  if ($key_by_formatter_name) {
376
    $prefix_length = strlen($prefix);
377
    $rekeyed_displays = array();
378
    foreach ($displays as $name => $display) {
379
      $rekeyed_displays[substr($name, $prefix_length)] = $display;
380
    }
381
    $displays = $rekeyed_displays;
382
  }
383

    
384
  return $displays;
385
}
386

    
387
/**
388
 * Saves a {file_display} object to the database.
389
 */
390
function file_display_save($display) {
391
  ctools_include('export');
392
  ctools_export_crud_save('file_display', $display);
393
  file_info_cache_clear();
394
}
395

    
396
/**
397
 * Creates a new {file_display} object.
398
 */
399
function file_display_new($file_type, $view_mode, $formatter_name) {
400
  ctools_include('export');
401
  $display = ctools_export_crud_new('file_display');
402
  file_info_cache_clear();
403
  $display->name = implode('__', array($file_type, $view_mode, $formatter_name));
404
  return $display;
405
}
406

    
407
/**
408
 * @} End of "defgroup file_displays".
409
 */
410

    
411
/**
412
 * @defgroup file_types File types API
413
 * @{
414
 * Functions to load and save information about file types.
415
 */
416

    
417
/**
418
 * Load a file type configuration object.
419
 *
420
 * @param string $name
421
 *   The file type machine name to load.
422
 *
423
 * @return object
424
 *   The file type object, or FALSE if it does not exist.
425
 */
426
function file_type_load($name) {
427
  ctools_include('export');
428
  $type = ctools_export_crud_load('file_type', $name);
429
  return isset($type) ? $type : FALSE;
430
}
431

    
432
/**
433
 * Load multiple file type configuration objects.
434
 *
435
 * @param array $names
436
 *   An array of file type machine names to load.
437
 *
438
 * @return array
439
 *   An array of file type objects, keyed by machine name.
440
 */
441
function file_type_load_multiple(array $names) {
442
  ctools_include('export');
443
  return ctools_export_crud_load_multiple('file_type', $names);
444
}
445

    
446
/**
447
 * Load all file type configuration objects.
448
 *
449
 * This includes all enabled and disabled file types.
450
 *
451
 * @param bool $reset
452
 *   If TRUE, the static cache of all file types will be flushed prior to
453
 *   loading. This can be important on listing pages where file types might
454
 *   have changed on the page load.
455
 *
456
 * @return array
457
 *   An array of file type objects, keyed by machine name.
458
 *
459
 * @see file_type_get_enabled_types()
460
 * @see file_type_get_disabled_types()
461
 */
462
function file_type_load_all($reset = FALSE) {
463
  ctools_include('export');
464
  return ctools_export_crud_load_all('file_type', $reset);
465
}
466

    
467
/**
468
 * Returns an array of enabled file types.
469
 */
470
function file_type_get_enabled_types() {
471
  $types = file_type_load_all();
472
  return array_filter($types, 'file_type_is_enabled');
473
}
474

    
475
/**
476
 * Returns an array of disabled file types.
477
 */
478
function file_type_get_disabled_types() {
479
  $types = file_type_load_all();
480
  return array_filter($types, 'file_type_is_disabled');
481
}
482

    
483
/**
484
 * Returns TRUE if a file type is enabled, FALSE otherwise.
485
 */
486
function file_type_is_enabled($type) {
487
  return empty($type->disabled);
488
}
489

    
490
/**
491
 * Returns TRUE if a file type is disabled, FALSE otherwise.
492
 */
493
function file_type_is_disabled($type) {
494
  return !empty($type->disabled);
495
}
496

    
497
/**
498
 * Returns an array of valid file extensions.
499
 */
500
function file_type_get_valid_extensions($type) {
501
  include_once DRUPAL_ROOT . '/includes/file.mimetypes.inc';
502
  $mapping = file_mimetype_mapping();
503

    
504
  $type_extensions = array();
505
  $type_ext_keys = array();
506
  if (!empty($type->mimetypes)) {
507
    foreach ($mapping['mimetypes'] as $ext_key => $mimetype) {
508
      if (file_entity_match_mimetypes($mimetype, $type->mimetypes)) {
509
        $type_ext_keys[] = $ext_key;
510
      }
511
    }
512

    
513
    if ($type_ext_keys) {
514
      $type_extensions = array_intersect($mapping['extensions'], $type_ext_keys);
515
      $type_extensions = array_keys($type_extensions);
516
    }
517
  }
518

    
519
  return $type_extensions;
520
}
521

    
522
/**
523
 * Updates an existing file type or creates a new one.
524
 *
525
 * This function can be called on its own, or via the CTools exportables
526
 * 'save callback' for {file_type} objects.
527
 */
528
function file_type_save($type) {
529
  // Get the old type object, so we now can issue the correct insert/update
530
  // queries.
531
  if (!empty($type->old_type) && $type->old_type != $type->type) {
532
    $rename_bundle = TRUE;
533
    $old_type = file_type_load($type->old_type);
534
  }
535
  else {
536
    $rename_bundle = FALSE;
537
    $old_type = file_type_load($type->type);
538
  }
539

    
540
  // The type and label fields are required, but description is optional.
541
  if (!isset($type->description)) {
542
    $type->description = '';
543
  }
544
  $fields = array(
545
    'type' => $type->type,
546
    'label' => $type->label,
547
    'description' => $type->description,
548
    'mimetypes' => serialize($type->mimetypes),
549
  );
550

    
551
  // Update an existing type object, whether with a modified 'type' property or
552
  // not.
553
  if ($old_type) {
554
    if ($old_type->export_type & EXPORT_IN_DATABASE) {
555
      db_update('file_type')
556
        ->fields($fields)
557
        ->condition('type', $old_type->type)
558
        ->execute();
559
    }
560
    else {
561
      db_insert('file_type')
562
        ->fields($fields)
563
        ->execute();
564
    }
565
    if ($rename_bundle) {
566
      field_attach_rename_bundle('file', $old_type->type, $type->type);
567
    }
568
    module_invoke_all('file_type_update', $type);
569
    $status = SAVED_UPDATED;
570
  }
571
  // Insert a new type object.
572
  else {
573
    db_insert('file_type')
574
      ->fields($fields)
575
      ->execute();
576
    field_attach_create_bundle('file', $type->type);
577
    module_invoke_all('file_type_insert', $type);
578
    $status = SAVED_NEW;
579
  }
580

    
581
  // Clear the necessary caches.
582
  file_info_cache_clear();
583

    
584
  // Ensure the type has the correct export_type in case the $type parameter
585
  // continues to be used by the calling function after this function completes.
586
  if (empty($type->export_type)) {
587
    $type->export_type = 0;
588
  }
589
  $type->export_type |= EXPORT_IN_DATABASE;
590

    
591
  return $status;
592
}
593

    
594
/**
595
 * Deletes a file type from the database.
596
 *
597
 * This function can be called on its own, or via the CTools exportables
598
 * 'delete callback' for {file_type} objects.
599
 *
600
 * @param object|string $type
601
 *   Either a loaded file type object or the machine-name of the type.
602
 */
603
function file_type_delete($type) {
604
  $type = is_string($type) ? file_type_load($type) : $type;
605

    
606
  db_delete('file_type')
607
    ->condition('type', $type->type)
608
    ->execute();
609

    
610
  // Remove this type from CToolS status variable.
611
  $status = variable_get('default_file_type', array());
612
  unset($status[$type->type]);
613
  variable_set('default_file_type', $status);
614

    
615
  file_info_cache_clear();
616

    
617
  // After deleting from the database, check if the type still exists as a
618
  // code-provided default type. If not, consider the type fully deleted and
619
  // invoke the needed hooks.
620
  if (!file_type_load($type->type)) {
621
    field_attach_delete_bundle('file', $type->type);
622
    module_invoke_all('file_type_delete', $type);
623
  }
624
}
625

    
626

    
627
/**
628
 * Enable a file type.
629
 *
630
 * @param string $type
631
 *   Type of the file_type to disable
632
 */
633
function file_type_enable($type) {
634
  ctools_include('export');
635
  ctools_export_crud_enable('file_type', $type);
636
}
637

    
638

    
639
/**
640
 * Disable a file type.
641
 *
642
 * @param string $type
643
 *   Type of the file_type to disable
644
 */
645
function file_type_disable($type) {
646
  ctools_include('export');
647
  ctools_export_crud_disable('file_type', $type);
648
}
649

    
650

    
651
/**
652
 * Determines file type for a given file.
653
 *
654
 * @param object $file
655
 *   File object.
656
 *
657
 * @return string
658
 *   Machine name of file type that should be used for given file.
659
 */
660
function file_get_type($file) {
661
  $types = module_invoke_all('file_type', $file);
662
  drupal_alter('file_type', $types, $file);
663

    
664
  return empty($types) ? NULL : reset($types);
665
}
666

    
667
/**
668
 * @} End of "defgroup file_types".
669
 */
670

    
671
/**
672
 * Sorts an array by weight.
673
 *
674
 * Helper function to sort an array by the value of each item's 'weight' key,
675
 * while preserving relative order of items that have equal weight.
676
 */
677
function _file_sort_array_by_weight(&$a) {
678
  $i = 0;
679
  foreach ($a as $key => $item) {
680
    if (!isset($a[$key]['weight'])) {
681
      $a[$key]['weight'] = 0;
682
    }
683
    $original_weight[$key] = $a[$key]['weight'];
684
    $a[$key]['weight'] += $i / 1000;
685
    $i++;
686
  }
687
  uasort($a, 'drupal_sort_weight');
688
  foreach ($a as $key => $item) {
689
    $a[$key]['weight'] = $original_weight[$key];
690
  }
691
}
692

    
693
/**
694
 * User sort function to sort by weight, then label/name.
695
 */
696
function _file_entity_sort_weight_label($a, $b) {
697
  $a_weight = isset($a['weight']) ? $a['weight'] : 0;
698
  $b_weight = isset($b['weight']) ? $b['weight'] : 0;
699
  if ($a_weight == $b_weight) {
700
    $a_label = isset($a['label']) ? $a['label'] : '';
701
    $b_label = isset($b['label']) ? $b['label'] : '';
702
    return strcasecmp($a_label, $b_label);
703
  }
704
  else {
705
    return $a_weight < $b_weight ? -1 : 1;
706
  }
707
}
708

    
709
/**
710
 * Returns a file object which can be passed to file_save().
711
 *
712
 * @param string $uri
713
 *   A string containing the URI, path, or filename.
714
 * @param bool $use_existing
715
 *   (Optional) If TRUE and there's an existing file in the {file_managed}
716
 *   table with the passed in URI, then that file object is returned.
717
 *   Otherwise, a new file object is returned. Default is TRUE.
718
 *
719
 * @return object|bool
720
 *   A file object, or FALSE on error.
721
 *
722
 * @todo This should probably be named
723
 *   file_load_by_uri($uri, $create_if_not_exists).
724
 * @todo Remove this function when http://drupal.org/node/685818 is fixed.
725
 */
726
function file_uri_to_object($uri, $use_existing = TRUE) {
727
  $file = FALSE;
728
  $uri = file_stream_wrapper_uri_normalize($uri);
729

    
730
  if ($use_existing) {
731
    // We should always attempt to re-use a file if possible.
732
    $files = entity_load('file', FALSE, array('uri' => $uri));
733
    $file = !empty($files) ? reset($files) : FALSE;
734
  }
735

    
736
  if (empty($file)) {
737
    $file = new stdClass();
738
    $file->uid = $GLOBALS['user']->uid;
739
    $file->filename = drupal_basename($uri);
740
    $file->uri = $uri;
741
    $file->filemime = file_get_mimetype($uri);
742
    // This is gagged because some uris will not support it.
743
    $file->filesize = @filesize($uri);
744
    $file->timestamp = REQUEST_TIME;
745
    $file->status = FILE_STATUS_PERMANENT;
746
  }
747

    
748
  return $file;
749
}
750

    
751
/**
752
 * Delete multiple files.
753
 *
754
 * Unlike core's file_delete(), this function does not care about file usage
755
 * or skip on invalid URIs. Just deletes the damn file like it should.
756
 *
757
 * @param array $fids
758
 *   An array of file IDs.
759
 */
760
function file_delete_multiple(array $fids) {
761
  $transaction = db_transaction();
762
  if (!empty($fids) && $files = file_load_multiple($fids)) {
763
    try {
764
      foreach ($files as $fid => $file) {
765
        module_invoke_all('file_delete', $file);
766
        module_invoke_all('entity_delete', $file, 'file');
767
        // Skip calling field_attach_delete as file_entity_file_delete()
768
        // does this.
769
        // field_attach_delete('file', $file);
770
        // Remove this file from the search index if needed.
771
        // This code is implemented in file_entity module rather than in search
772
        // module, because node module is implementing search module's API,
773
        // not the other way around.
774
        if (module_exists('search')) {
775
          search_reindex($fid, 'file');
776
        }
777

    
778
        // Make sure the file is deleted before removing its row from the
779
        // database, so UIs can still find the file in the database.
780
        if (file_valid_uri($file->uri)) {
781
          file_unmanaged_delete($file->uri);
782
        }
783
      }
784

    
785
      db_delete('file_managed')
786
        ->condition('fid', $fids, 'IN')
787
        ->execute();
788
      db_delete('file_usage')
789
        ->condition('fid', $fids, 'IN')
790
        ->execute();
791
    }
792
    catch (Exception $e) {
793
      $transaction->rollback();
794
      watchdog_exception('file', $e);
795
      throw $e;
796
    }
797

    
798
    // Clear the page and block and file_load_multiple caches.
799
    entity_get_controller('file')->resetCache();
800
  }
801
}