Projet

Général

Profil

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

root / drupal7 / sites / all / modules / file_entity / file_entity.file_api.inc @ 66c11afc

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
  // Since $file->alt and $file->title were set in file_entity_file_load()
245
  // (which is a language-agnostic hook) they will not be in the correct
246
  // language if the file is being displayed in a language other than the
247
  // default one. Set them again here, using the correct language. This must
248
  // run after hook_file_displays_alter() since the Media module sets
249
  // $file->alt and $file->title again during that hook.
250
  if ($langcode != $GLOBALS['language_content']->language) {
251
    $languages = language_list();
252
    if (isset($languages[$langcode])) {
253
      if (!function_exists('file_entity_set_title_alt_properties')) {
254
        module_load_include('file.inc', 'file_entity');
255
      }
256
      file_entity_set_title_alt_properties(array($file), $languages[$langcode]);
257
    }
258
  }
259

    
260
  // Attempt to display the file with each of the possible displays. Stop after
261
  // the first successful one. See file_displays() for details.
262
  $element = NULL;
263
  foreach ($displays as $formatter_type => $display) {
264
    if (!empty($display['status'])) {
265
      $formatter_info = file_info_formatter_types($formatter_type);
266
      // Under normal circumstances, the UI prevents enabling formatters for
267
      // incompatible MIME types. In case this was somehow circumvented (for
268
      // example, a module updated its formatter definition without updating
269
      // existing display settings), perform an extra check here.
270
      if (isset($formatter_info['mime types'])) {
271
        if (!file_entity_match_mimetypes($formatter_info['mime types'], $file->filemime)) {
272
          continue;
273
        }
274
      }
275
      if (isset($formatter_info['view callback']) && ($function = $formatter_info['view callback']) && function_exists($function)) {
276
        $display['type'] = $formatter_type;
277
        if (!empty($formatter_info['default settings'])) {
278
          if (empty($display['settings'])) {
279
            $display['settings'] = array();
280
          }
281
          $display['settings'] += $formatter_info['default settings'];
282
        }
283
        $element = $function($file, $display, $langcode);
284
        if (isset($element)) {
285
          break;
286
        }
287
      }
288
    }
289
  }
290

    
291
  // As a last resort, fall back to showing a link to the file.
292
  if (!isset($element)) {
293
    $element = array(
294
      '#theme' => 'file_link',
295
      '#file' => $file,
296
    );
297
  }
298

    
299
  // Add defaults and return the element.
300
  $element += array(
301
    '#file' => $file,
302
    '#view_mode' => $view_mode,
303
    '#language' => $langcode,
304
  );
305

    
306
  return $element;
307
}
308

    
309
/**
310
 * @defgroup file_displays File displays API
311
 * @{
312
 * Functions to load and save information about file displays.
313
 */
314

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

    
352
  // If the requested view mode isn't configured to use a custom display for its
353
  // fields, then don't use a custom display for its file either.
354
  if ($view_mode != 'default') {
355
    $view_mode_settings = field_view_mode_settings('file', $file_type);
356
    $view_mode = !empty($view_mode_settings[$view_mode]['custom_settings']) ? $view_mode : 'default';
357
  }
358

    
359
  if (!isset($cache[$file_type][$view_mode])) {
360
    // Load the display configurations for the file type and view mode. If none
361
    // exist for the view mode, use the default view mode.
362
    $displays = file_displays_load($file_type, $view_mode, TRUE);
363
    if (empty($displays) && $view_mode != 'default') {
364
      $cache[$file_type][$view_mode] = file_displays($file_type, 'default');
365
    }
366
    else {
367
      // Convert the display objects to arrays and remove unnecessary keys.
368
      foreach ($displays as $formatter_name => $display) {
369
        $displays[$formatter_name] = array_intersect_key((array) $display, drupal_map_assoc(array('status', 'weight', 'settings')));
370
      }
371
      $cache[$file_type][$view_mode] = $displays;
372
    }
373
  }
374

    
375
  return $cache[$file_type][$view_mode];
376
}
377

    
378
/**
379
 * Returns an array of {file_display} objects for the file type and view mode.
380
 */
381
function file_displays_load($file_type, $view_mode, $key_by_formatter_name = FALSE) {
382
  ctools_include('export');
383

    
384
  $display_names = array();
385
  $prefix = $file_type . '__' . $view_mode . '__';
386
  foreach (array_keys(file_info_formatter_types()) as $formatter_name) {
387
    $display_names[] = $prefix . $formatter_name;
388
  }
389
  $displays = ctools_export_load_object('file_display', 'names', $display_names);
390

    
391
  if ($key_by_formatter_name) {
392
    $prefix_length = strlen($prefix);
393
    $rekeyed_displays = array();
394
    foreach ($displays as $name => $display) {
395
      $rekeyed_displays[substr($name, $prefix_length)] = $display;
396
    }
397
    $displays = $rekeyed_displays;
398
  }
399

    
400
  return $displays;
401
}
402

    
403
/**
404
 * Saves a {file_display} object to the database.
405
 */
406
function file_display_save($display) {
407
  ctools_include('export');
408
  ctools_export_crud_save('file_display', $display);
409
  file_info_cache_clear();
410
}
411

    
412
/**
413
 * Creates a new {file_display} object.
414
 */
415
function file_display_new($file_type, $view_mode, $formatter_name) {
416
  ctools_include('export');
417
  $display = ctools_export_crud_new('file_display');
418
  file_info_cache_clear();
419
  $display->name = implode('__', array($file_type, $view_mode, $formatter_name));
420
  return $display;
421
}
422

    
423
/**
424
 * @} End of "defgroup file_displays".
425
 */
426

    
427
/**
428
 * @defgroup file_types File types API
429
 * @{
430
 * Functions to load and save information about file types.
431
 */
432

    
433
/**
434
 * Load a file type configuration object.
435
 *
436
 * @param string $name
437
 *   The file type machine name to load.
438
 *
439
 * @return object
440
 *   The file type object, or FALSE if it does not exist.
441
 */
442
function file_type_load($name) {
443
  ctools_include('export');
444
  $type = ctools_export_crud_load('file_type', $name);
445
  return isset($type) ? $type : FALSE;
446
}
447

    
448
/**
449
 * Load multiple file type configuration objects.
450
 *
451
 * @param array $names
452
 *   An array of file type machine names to load.
453
 *
454
 * @return array
455
 *   An array of file type objects, keyed by machine name.
456
 */
457
function file_type_load_multiple(array $names) {
458
  ctools_include('export');
459
  return ctools_export_crud_load_multiple('file_type', $names);
460
}
461

    
462
/**
463
 * Load all file type configuration objects.
464
 *
465
 * This includes all enabled and disabled file types.
466
 *
467
 * @param bool $reset
468
 *   If TRUE, the static cache of all file types will be flushed prior to
469
 *   loading. This can be important on listing pages where file types might
470
 *   have changed on the page load.
471
 *
472
 * @return array
473
 *   An array of file type objects, keyed by machine name.
474
 *
475
 * @see file_type_get_enabled_types()
476
 * @see file_type_get_disabled_types()
477
 */
478
function file_type_load_all($reset = FALSE) {
479
  ctools_include('export');
480
  return ctools_export_crud_load_all('file_type', $reset);
481
}
482

    
483
/**
484
 * Returns an array of enabled file types.
485
 */
486
function file_type_get_enabled_types() {
487
  $types = file_type_load_all();
488
  return array_filter($types, 'file_type_is_enabled');
489
}
490

    
491
/**
492
 * Returns an array of disabled file types.
493
 */
494
function file_type_get_disabled_types() {
495
  $types = file_type_load_all();
496
  return array_filter($types, 'file_type_is_disabled');
497
}
498

    
499
/**
500
 * Returns TRUE if a file type is enabled, FALSE otherwise.
501
 */
502
function file_type_is_enabled($type) {
503
  return empty($type->disabled);
504
}
505

    
506
/**
507
 * Returns TRUE if a file type is disabled, FALSE otherwise.
508
 */
509
function file_type_is_disabled($type) {
510
  return !empty($type->disabled);
511
}
512

    
513
/**
514
 * Returns an array of valid file extensions.
515
 */
516
function file_type_get_valid_extensions($type) {
517
  include_once DRUPAL_ROOT . '/includes/file.mimetypes.inc';
518
  $mapping = file_mimetype_mapping();
519

    
520
  $type_extensions = array();
521
  $type_ext_keys = array();
522
  if (!empty($type->mimetypes)) {
523
    foreach ($mapping['mimetypes'] as $ext_key => $mimetype) {
524
      if (file_entity_match_mimetypes($mimetype, $type->mimetypes)) {
525
        $type_ext_keys[] = $ext_key;
526
      }
527
    }
528

    
529
    if ($type_ext_keys) {
530
      $type_extensions = array_intersect($mapping['extensions'], $type_ext_keys);
531
      $type_extensions = array_keys($type_extensions);
532
    }
533
  }
534

    
535
  return $type_extensions;
536
}
537

    
538
/**
539
 * Updates an existing file type or creates a new one.
540
 *
541
 * This function can be called on its own, or via the CTools exportables
542
 * 'save callback' for {file_type} objects.
543
 */
544
function file_type_save($type) {
545
  // Get the old type object, so we now can issue the correct insert/update
546
  // queries.
547
  if (!empty($type->old_type) && $type->old_type != $type->type) {
548
    $rename_bundle = TRUE;
549
    $old_type = file_type_load($type->old_type);
550
  }
551
  else {
552
    $rename_bundle = FALSE;
553
    $old_type = file_type_load($type->type);
554
  }
555

    
556
  // The type and label fields are required, but description is optional.
557
  if (!isset($type->description)) {
558
    $type->description = '';
559
  }
560
  $fields = array(
561
    'type' => $type->type,
562
    'label' => $type->label,
563
    'description' => $type->description,
564
    'mimetypes' => serialize($type->mimetypes),
565
  );
566

    
567
  // Update an existing type object, whether with a modified 'type' property or
568
  // not.
569
  if ($old_type) {
570
    if ($old_type->export_type & EXPORT_IN_DATABASE) {
571
      db_update('file_type')
572
        ->fields($fields)
573
        ->condition('type', $old_type->type)
574
        ->execute();
575
    }
576
    else {
577
      db_insert('file_type')
578
        ->fields($fields)
579
        ->execute();
580
    }
581
    if ($rename_bundle) {
582
      field_attach_rename_bundle('file', $old_type->type, $type->type);
583
    }
584
    module_invoke_all('file_type_update', $type);
585
    $status = SAVED_UPDATED;
586
  }
587
  // Insert a new type object.
588
  else {
589
    db_insert('file_type')
590
      ->fields($fields)
591
      ->execute();
592
    field_attach_create_bundle('file', $type->type);
593
    module_invoke_all('file_type_insert', $type);
594
    $status = SAVED_NEW;
595
  }
596

    
597
  // Clear the necessary caches.
598
  file_info_cache_clear();
599

    
600
  // Ensure the type has the correct export_type in case the $type parameter
601
  // continues to be used by the calling function after this function completes.
602
  if (empty($type->export_type)) {
603
    $type->export_type = 0;
604
  }
605
  $type->export_type |= EXPORT_IN_DATABASE;
606

    
607
  return $status;
608
}
609

    
610
/**
611
 * Deletes a file type from the database.
612
 *
613
 * This function can be called on its own, or via the CTools exportables
614
 * 'delete callback' for {file_type} objects.
615
 *
616
 * @param object|string $type
617
 *   Either a loaded file type object or the machine-name of the type.
618
 */
619
function file_type_delete($type) {
620
  $type = is_string($type) ? file_type_load($type) : $type;
621

    
622
  db_delete('file_type')
623
    ->condition('type', $type->type)
624
    ->execute();
625

    
626
  // Remove this type from CToolS status variable.
627
  $status = variable_get('default_file_type', array());
628
  unset($status[$type->type]);
629
  variable_set('default_file_type', $status);
630

    
631
  file_info_cache_clear();
632

    
633
  // After deleting from the database, check if the type still exists as a
634
  // code-provided default type. If not, consider the type fully deleted and
635
  // invoke the needed hooks.
636
  if (!file_type_load($type->type)) {
637
    field_attach_delete_bundle('file', $type->type);
638
    module_invoke_all('file_type_delete', $type);
639
  }
640
}
641

    
642

    
643
/**
644
 * Enable a file type.
645
 *
646
 * @param string $type
647
 *   Type of the file_type to disable
648
 */
649
function file_type_enable($type) {
650
  ctools_include('export');
651
  ctools_export_crud_enable('file_type', $type);
652
}
653

    
654

    
655
/**
656
 * Disable a file type.
657
 *
658
 * @param string $type
659
 *   Type of the file_type to disable
660
 */
661
function file_type_disable($type) {
662
  ctools_include('export');
663
  ctools_export_crud_disable('file_type', $type);
664
}
665

    
666

    
667
/**
668
 * Determines file type for a given file.
669
 *
670
 * @param object $file
671
 *   File object.
672
 *
673
 * @return string
674
 *   Machine name of file type that should be used for given file.
675
 */
676
function file_get_type($file) {
677
  $types = module_invoke_all('file_type', $file);
678
  drupal_alter('file_type', $types, $file);
679

    
680
  return empty($types) ? NULL : reset($types);
681
}
682

    
683
/**
684
 * @} End of "defgroup file_types".
685
 */
686

    
687
/**
688
 * Sorts an array by weight.
689
 *
690
 * Helper function to sort an array by the value of each item's 'weight' key,
691
 * while preserving relative order of items that have equal weight.
692
 */
693
function _file_sort_array_by_weight(&$a) {
694
  $i = 0;
695
  foreach ($a as $key => $item) {
696
    if (!isset($a[$key]['weight'])) {
697
      $a[$key]['weight'] = 0;
698
    }
699
    $original_weight[$key] = $a[$key]['weight'];
700
    $a[$key]['weight'] += $i / 1000;
701
    $i++;
702
  }
703
  uasort($a, 'drupal_sort_weight');
704
  foreach ($a as $key => $item) {
705
    $a[$key]['weight'] = $original_weight[$key];
706
  }
707
}
708

    
709
/**
710
 * User sort function to sort by weight, then label/name.
711
 */
712
function _file_entity_sort_weight_label($a, $b) {
713
  $a_weight = isset($a['weight']) ? $a['weight'] : 0;
714
  $b_weight = isset($b['weight']) ? $b['weight'] : 0;
715
  if ($a_weight == $b_weight) {
716
    $a_label = isset($a['label']) ? $a['label'] : '';
717
    $b_label = isset($b['label']) ? $b['label'] : '';
718
    return strcasecmp($a_label, $b_label);
719
  }
720
  else {
721
    return $a_weight < $b_weight ? -1 : 1;
722
  }
723
}
724

    
725
/**
726
 * Returns a file object which can be passed to file_save().
727
 *
728
 * @param string $uri
729
 *   A string containing the URI, path, or filename.
730
 * @param bool $use_existing
731
 *   (Optional) If TRUE and there's an existing file in the {file_managed}
732
 *   table with the passed in URI, then that file object is returned.
733
 *   Otherwise, a new file object is returned. Default is TRUE.
734
 *
735
 * @return object|bool
736
 *   A file object, or FALSE on error.
737
 *
738
 * @todo This should probably be named
739
 *   file_load_by_uri($uri, $create_if_not_exists).
740
 * @todo Remove this function when http://drupal.org/node/685818 is fixed.
741
 */
742
function file_uri_to_object($uri, $use_existing = TRUE) {
743
  $file = FALSE;
744
  $uri = file_stream_wrapper_uri_normalize($uri);
745

    
746
  if ($use_existing) {
747
    // We should always attempt to re-use a file if possible.
748
    $files = entity_load('file', FALSE, array('uri' => $uri));
749
    $file = !empty($files) ? reset($files) : FALSE;
750
  }
751

    
752
  if (empty($file)) {
753
    $file = new stdClass();
754
    $file->uid = $GLOBALS['user']->uid;
755
    $file->filename = drupal_basename($uri);
756
    $file->uri = $uri;
757
    $file->filemime = file_get_mimetype($uri);
758
    // This is gagged because some uris will not support it.
759
    $file->filesize = @filesize($uri);
760
    $file->timestamp = REQUEST_TIME;
761
    $file->status = FILE_STATUS_PERMANENT;
762
  }
763

    
764
  return $file;
765
}
766

    
767
/**
768
 * Delete multiple files.
769
 *
770
 * Unlike core's file_delete(), this function does not care about file usage
771
 * or skip on invalid URIs. Just deletes the damn file like it should.
772
 *
773
 * @param array $fids
774
 *   An array of file IDs.
775
 */
776
function file_delete_multiple(array $fids) {
777
  $transaction = db_transaction();
778
  if (!empty($fids) && $files = file_load_multiple($fids)) {
779
    try {
780
      foreach ($files as $fid => $file) {
781
        module_invoke_all('file_delete', $file);
782
        module_invoke_all('entity_delete', $file, 'file');
783
        // Skip calling field_attach_delete as file_entity_file_delete()
784
        // does this.
785
        // field_attach_delete('file', $file);
786
        // Remove this file from the search index if needed.
787
        // This code is implemented in file_entity module rather than in search
788
        // module, because node module is implementing search module's API,
789
        // not the other way around.
790
        if (module_exists('search')) {
791
          search_reindex($fid, 'file');
792
        }
793

    
794
        // Make sure the file is deleted before removing its row from the
795
        // database, so UIs can still find the file in the database.
796
        if (file_valid_uri($file->uri)) {
797
          file_unmanaged_delete($file->uri);
798
        }
799
      }
800

    
801
      db_delete('file_managed')
802
        ->condition('fid', $fids, 'IN')
803
        ->execute();
804
      db_delete('file_usage')
805
        ->condition('fid', $fids, 'IN')
806
        ->execute();
807
    }
808
    catch (Exception $e) {
809
      $transaction->rollback();
810
      watchdog_exception('file', $e);
811
      throw $e;
812
    }
813

    
814
    // Clear the page and block and file_load_multiple caches.
815
    entity_get_controller('file')->resetCache();
816
  }
817
}