Projet

Général

Profil

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

root / drupal7 / sites / all / modules / libraries / libraries.module @ 5136ce55

1
<?php
2

    
3
/**
4
 * @file
5
 * External library handling for Drupal modules.
6
 */
7

    
8
/**
9
 * Implements hook_flush_caches().
10
 */
11
function libraries_flush_caches() {
12
  // Clear static caches.
13
  // We don't clear the 'libraries_load' static cache, because that could result
14
  // in libraries that had been loaded before the cache flushing to be loaded
15
  // again afterwards.
16
  foreach (array('libraries_get_path', 'libraries_info') as $name) {
17
    drupal_static_reset($name);
18
  }
19

    
20
  // @todo When upgrading from 1.x, update.php attempts to flush caches before
21
  //   the cache table has been created.
22
  // @see http://drupal.org/node/1477932
23
  if (db_table_exists('cache_libraries')) {
24
    return array('cache_libraries');
25
  }
26
}
27

    
28
/**
29
 * Gets the path of a library.
30
 *
31
 * @param $name
32
 *   The machine name of a library to return the path for.
33
 * @param $base_path
34
 *   Whether to prefix the resulting path with base_path().
35
 *
36
 * @return string
37
 *   The path to the specified library or FALSE if the library wasn't found.
38
 *
39
 * @ingroup libraries
40
 */
41
function libraries_get_path($name, $base_path = FALSE) {
42
  $libraries = &drupal_static(__FUNCTION__);
43

    
44
  if (!isset($libraries)) {
45
    $libraries = libraries_get_libraries();
46
  }
47

    
48
  $path = ($base_path ? base_path() : '');
49
  if (!isset($libraries[$name])) {
50
    return FALSE;
51
  }
52
  else {
53
    $path .= $libraries[$name];
54
  }
55

    
56
  return $path;
57
}
58

    
59
/**
60
 * Returns an array of library directories.
61
 *
62
 * Returns an array of library directories from the all-sites directory
63
 * (i.e. sites/all/libraries/), the profiles directory, and site-specific
64
 * directory (i.e. sites/somesite/libraries/). The returned array will be keyed
65
 * by the library name. Site-specific libraries are prioritized over libraries
66
 * in the default directories. That is, if a library with the same name appears
67
 * in both the site-wide directory and site-specific directory, only the
68
 * site-specific version will be listed.
69
 *
70
 * @return array
71
 *   A list of library directories.
72
 *
73
 * @ingroup libraries
74
 */
75
function libraries_get_libraries() {
76
  $searchdir = array();
77
  $profile = drupal_get_path('profile', drupal_get_profile());
78
  $config = conf_path();
79

    
80
  // Similar to 'modules' and 'themes' directories in the root directory,
81
  // certain distributions may want to place libraries into a 'libraries'
82
  // directory in Drupal's root directory.
83
  $searchdir[] = 'libraries';
84

    
85
  // Similar to 'modules' and 'themes' directories inside an installation
86
  // profile, installation profiles may want to place libraries into a
87
  // 'libraries' directory.
88
  $searchdir[] = "$profile/libraries";
89

    
90
  // Always search sites/all/libraries.
91
  $searchdir[] = 'sites/all/libraries';
92

    
93
  // Also search sites/<domain>/*.
94
  $searchdir[] = "$config/libraries";
95

    
96
  // Retrieve list of directories.
97
  $directories = array();
98
  $nomask = array('CVS');
99
  foreach ($searchdir as $dir) {
100
    if (is_dir($dir) && $handle = opendir($dir)) {
101
      while (FALSE !== ($file = readdir($handle))) {
102
        if (!in_array($file, $nomask) && $file[0] != '.') {
103
          if (is_dir("$dir/$file")) {
104
            $directories[$file] = "$dir/$file";
105
          }
106
        }
107
      }
108
      closedir($handle);
109
    }
110
  }
111

    
112
  return $directories;
113
}
114

    
115
/**
116
 * Looks for library info files.
117
 *
118
 * This function scans the following directories for info files:
119
 * - libraries
120
 * - profiles/$profilename/libraries
121
 * - sites/all/libraries
122
 * - sites/$sitename/libraries
123
 * - any directories specified via hook_libraries_info_file_paths()
124
 *
125
 * @return array
126
 *   An array of info files, keyed by library name. The values are the paths of
127
 *   the files.
128
 */
129
function libraries_scan_info_files() {
130
  $profile = drupal_get_path('profile', drupal_get_profile());
131
  $config = conf_path();
132

    
133
  // Build a list of directories.
134
  $directories = module_invoke_all('libraries_info_file_paths');
135
  $directories[] = 'libraries';
136
  $directories[] = "$profile/libraries";
137
  $directories[] = 'sites/all/libraries';
138
  $directories[] = "$config/libraries";
139

    
140
  // Scan for info files.
141
  $files = array();
142
  foreach ($directories as $dir) {
143
    if (file_exists($dir)) {
144
      $files = array_merge($files, file_scan_directory($dir, '@^[A-Za-z0-9._-]+\.libraries\.info$@', array(
145
        'key' => 'name',
146
        'recurse' => FALSE,
147
      )));
148
    }
149
  }
150

    
151
  foreach ($files as $filename => $file) {
152
    $files[basename($filename, '.libraries')] = $file;
153
    unset($files[$filename]);
154
  }
155

    
156
  return $files;
157
}
158

    
159
/**
160
 * Invokes library callbacks.
161
 *
162
 * @param $group
163
 *   A string containing the group of callbacks that is to be applied. Should be
164
 *   either 'info', 'pre-detect', 'post-detect', or 'load'.
165
 * @param $library
166
 *   An array of library information, passed by reference.
167
 */
168
function libraries_invoke($group, &$library) {
169
  // When introducing new callback groups in newer versions, stale cached
170
  // library information somehow reaches this point during the database update
171
  // before clearing the library cache.
172
  if (empty($library['callbacks'][$group])) {
173
    return;
174
  }
175

    
176
  foreach ($library['callbacks'][$group] as $callback) {
177
    libraries_traverse_library($library, $callback);
178
  }
179
}
180

    
181
/**
182
 * Helper function to apply a callback to all parts of a library.
183
 *
184
 * Because library declarations can include variants and versions, and those
185
 * version declarations can in turn include variants, modifying e.g. the 'files'
186
 * property everywhere it is declared can be quite cumbersome, in which case
187
 * this helper function is useful.
188
 *
189
 * @param $library
190
 *   An array of library information, passed by reference.
191
 * @param $callback
192
 *   A string containing the callback to apply to all parts of a library.
193
 */
194
function libraries_traverse_library(&$library, $callback) {
195
  // Always apply the callback to the top-level library.
196
  $callback($library, NULL, NULL);
197

    
198
  // Apply the callback to versions.
199
  if (isset($library['versions'])) {
200
    foreach ($library['versions'] as $version_string => &$version) {
201
      $callback($version, $version_string, NULL);
202
      // Versions can include variants as well.
203
      if (isset($version['variants'])) {
204
        foreach ($version['variants'] as $version_variant_name => &$version_variant) {
205
          $callback($version_variant, $version_string, $version_variant_name);
206
        }
207
      }
208
    }
209
  }
210

    
211
  // Apply the callback to variants.
212
  if (isset($library['variants'])) {
213
    foreach ($library['variants'] as $variant_name => &$variant) {
214
      $callback($variant, NULL, $variant_name);
215
    }
216
  }
217
}
218

    
219
/**
220
 * Library info callback to make all 'files' properties consistent.
221
 *
222
 * This turns libraries' file information declared as e.g.
223
 * @code
224
 * $library['files']['js'] = array('example_1.js', 'example_2.js');
225
 * @endcode
226
 * into
227
 * @code
228
 * $library['files']['js'] = array(
229
 *   'example_1.js' => array(),
230
 *   'example_2.js' => array(),
231
 * );
232
 * @endcode
233
 * It does the same for the 'integration files' property.
234
 *
235
 * @param $library
236
 *   An associative array of library information or a part of it, passed by
237
 *   reference.
238
 * @param $version
239
 *   If the library information belongs to a specific version, the version
240
 *   string. NULL otherwise.
241
 * @param $variant
242
 *   If the library information belongs to a specific variant, the variant name.
243
 *   NULL otherwise.
244
 *
245
 * @see libraries_info()
246
 * @see libraries_invoke()
247
 */
248
function libraries_prepare_files(&$library, $version = NULL, $variant = NULL) {
249
  // Both the 'files' property and the 'integration files' property contain file
250
  // declarations, and we want to make both consistent.
251
  $file_types = array();
252
  if (isset($library['files'])) {
253
    $file_types[] = &$library['files'];
254
  }
255
  if (isset($library['integration files'])) {
256
    // Integration files are additionally keyed by module.
257
    foreach ($library['integration files'] as &$integration_files) {
258
      $file_types[] = &$integration_files;
259
    }
260
  }
261
  foreach ($file_types as &$files) {
262
    // Go through all supported types of files.
263
    foreach (array('js', 'css', 'php') as $type) {
264
      if (isset($files[$type])) {
265
        foreach ($files[$type] as $key => $value) {
266
          // Unset numeric keys and turn the respective values into keys.
267
          if (is_numeric($key)) {
268
            $files[$type][$value] = array();
269
            unset($files[$type][$key]);
270
          }
271
        }
272
      }
273
    }
274
  }
275
}
276

    
277
/**
278
 * Library post-detect callback to process and detect dependencies.
279
 *
280
 * It checks whether each of the dependencies of a library are installed and
281
 * available in a compatible version.
282
 *
283
 * @param $library
284
 *   An associative array of library information or a part of it, passed by
285
 *   reference.
286
 * @param $version
287
 *   If the library information belongs to a specific version, the version
288
 *   string. NULL otherwise.
289
 * @param $variant
290
 *   If the library information belongs to a specific variant, the variant name.
291
 *   NULL otherwise.
292
 *
293
 * @see libraries_info()
294
 * @see libraries_invoke()
295
 */
296
function libraries_detect_dependencies(&$library, $version = NULL, $variant = NULL) {
297
  if (isset($library['dependencies'])) {
298
    foreach ($library['dependencies'] as &$dependency_string) {
299
      $dependency_info = drupal_parse_dependency($dependency_string);
300
      $dependency = libraries_detect($dependency_info['name']);
301
      if (!$dependency['installed']) {
302
        $library['installed'] = FALSE;
303
        $library['error'] = 'missing dependency';
304
        $library['error message'] = t('The %dependency library, which the %library library depends on, is not installed.', array(
305
          '%dependency' => $dependency['name'],
306
          '%library' => $library['name'],
307
        ));
308
      }
309
      elseif (drupal_check_incompatibility($dependency_info, $dependency['version'])) {
310
        $library['installed'] = FALSE;
311
        $library['error'] = 'incompatible dependency';
312
        $library['error message'] = t('The version %dependency_version of the %dependency library is not compatible with the %library library.', array(
313
          '%dependency_version' => $dependency['version'],
314
          '%dependency' => $dependency['name'],
315
          '%library' => $library['name'],
316
        ));
317
      }
318

    
319
      // Remove the version string from the dependency, so libraries_load() can
320
      // load the libraries directly.
321
      $dependency_string = $dependency_info['name'];
322
    }
323
  }
324
}
325

    
326
/**
327
 * Returns information about registered libraries.
328
 *
329
 * The returned information is unprocessed; i.e., as registered by modules.
330
 *
331
 * @param $name
332
 *   (optional) The machine name of a library to return registered information
333
 *   for. If omitted, information about all registered libraries is returned.
334
 *
335
 * @return array|false
336
 *   An associative array containing registered information for all libraries,
337
 *   the registered information for the library specified by $name, or FALSE if
338
 *   the library $name is not registered.
339
 *
340
 * @see hook_libraries_info()
341
 *
342
 * @todo Re-introduce support for include file plugin system - either by copying
343
 *   Wysiwyg's code, or directly switching to CTools.
344
 */
345
function &libraries_info($name = NULL) {
346
  // This static cache is re-used by libraries_detect() to save memory.
347
  $libraries = &drupal_static(__FUNCTION__);
348

    
349
  if (!isset($libraries)) {
350
    $libraries = array();
351

    
352
    // Gather information from hook_libraries_info() in enabled modules.
353
    foreach (module_implements('libraries_info') as $module) {
354
      foreach (module_invoke($module, 'libraries_info') as $machine_name => $properties) {
355
        $properties['info type'] = 'module';
356
        $properties['module'] = $module;
357
        $libraries[$machine_name] = $properties;
358
      }
359
    }
360

    
361
    // Gather information from hook_libraries_info() in enabled themes.
362
    $themes = array();
363
    foreach (list_themes() as $theme_name => $theme_info) {
364
      if ($theme_info->status && file_exists(drupal_get_path('theme', $theme_name) . '/template.php')) {
365
        // Collect a list of viable themes for re-use when calling the alter
366
        // hook.
367
        $themes[] = $theme_name;
368

    
369
        include_once drupal_get_path('theme', $theme_name) . '/template.php';
370

    
371
        $function = $theme_name . '_libraries_info';
372
        if (function_exists($function)) {
373
          foreach ($function() as $machine_name => $properties) {
374
            $properties['info type'] = 'theme';
375
            $properties['theme'] = $theme_name;
376
            $libraries[$machine_name] = $properties;
377
          }
378
        }
379
      }
380
    }
381

    
382
    // Gather information from .info files.
383
    // .info files override module definitions.
384
    foreach (libraries_scan_info_files() as $machine_name => $file) {
385
      $properties = drupal_parse_info_file($file->uri);
386
      $properties['info type'] = 'info file';
387
      $properties['info file'] = $file->uri;
388
      $libraries[$machine_name] = $properties;
389
    }
390

    
391
    // Provide defaults.
392
    foreach ($libraries as $machine_name => &$properties) {
393
      libraries_info_defaults($properties, $machine_name);
394
    }
395

    
396
    // Allow enabled modules and themes to alter the registered libraries.
397
    // drupal_alter() only takes the currently active theme into account, not
398
    // all enabled themes.
399
    foreach (module_implements('libraries_info_alter') as $module) {
400
      $function = $module . '_libraries_info_alter';
401
      $function($libraries);
402
    }
403
    foreach ($themes as $theme) {
404
      $function = $theme . '_libraries_info_alter';
405
      // The template.php file was included above.
406
      if (function_exists($function)) {
407
        $function($libraries);
408
      }
409
    }
410

    
411
    // Invoke callbacks in the 'info' group.
412
    foreach ($libraries as &$properties) {
413
      libraries_invoke('info', $properties);
414
    }
415
  }
416

    
417
  if (isset($name)) {
418
    if (!empty($libraries[$name])) {
419
      return $libraries[$name];
420
    }
421
    else {
422
      $false = FALSE;
423
      return $false;
424
    }
425
  }
426
  return $libraries;
427
}
428

    
429
/**
430
 * Applies default properties to a library definition.
431
 *
432
 * @param array $library
433
 *   An array of library information, passed by reference.
434
 * @param string $name
435
 *   The machine name of the passed-in library.
436
 *
437
 * @return array
438
 *   The library information array with defaults populated.
439
 */
440
function libraries_info_defaults(array &$library, $name) {
441
  $library += array(
442
    'machine name' => $name,
443
    'name' => $name,
444
    'vendor url' => '',
445
    'download url' => '',
446
    'download file url' => '',
447
    'path' => '',
448
    'library path' => NULL,
449
    'version callback' => 'libraries_get_version',
450
    'version arguments' => array(),
451
    'files' => array(),
452
    'dependencies' => array(),
453
    'variants' => array(),
454
    'versions' => array(),
455
    'integration files' => array(),
456
    'callbacks' => array(),
457
    // @todo Remove in 7.x-3.x
458
    'post-load integration files' => FALSE,
459
  );
460
  $library['callbacks'] += array(
461
    'info' => array(),
462
    'pre-detect' => array(),
463
    'post-detect' => array(),
464
    'pre-dependencies-load' => array(),
465
    'pre-load' => array(),
466
    'post-load' => array(),
467
  );
468

    
469
  // Add our own callbacks before any others.
470
  array_unshift($library['callbacks']['info'], 'libraries_prepare_files');
471
  array_unshift($library['callbacks']['post-detect'], 'libraries_detect_dependencies');
472

    
473
  return $library;
474
}
475

    
476
/**
477
 * Tries to detect a library and its installed version.
478
 *
479
 * @param string $name
480
 *   (optional) The machine name of a library to detect and return registered
481
 *   information for. If omitted, information about all registered libraries is
482
 *   returned.
483
 *
484
 * @return array|false
485
 *   An associative array containing registered information for all libraries,
486
 *   the registered information for the library specified by $name, or FALSE if
487
 *   the library $name is not registered.
488
 *   In addition to the keys returned by libraries_info(), the following keys
489
 *   are contained:
490
 *   - installed: A boolean indicating whether the library is installed. Note
491
 *     that not only the top-level library, but also each variant contains this
492
 *     key.
493
 *   - version: If the version could be detected, the full version string.
494
 *   - error: If an error occurred during library detection, one of the
495
 *     following error statuses: "not found", "not detected", "not supported".
496
 *   - error message: If an error occurred during library detection, a detailed
497
 *     error message.
498
 *
499
 * @see libraries_info()
500
 */
501
function libraries_detect($name = NULL) {
502
  if (!isset($name)) {
503
    $libraries = &libraries_info();
504
    foreach ($libraries as $name => $library) {
505
      libraries_detect($name);
506
    }
507
    return $libraries;
508
  }
509

    
510
  // Re-use the statically cached value of libraries_info() to save memory.
511
  $library = &libraries_info($name);
512

    
513
  // Exit early if the library was not found.
514
  if ($library === FALSE) {
515
    return $library;
516
  }
517

    
518
  // If 'installed' is set, library detection ran already.
519
  if (isset($library['installed'])) {
520
    return $library;
521
  }
522

    
523
  $library['installed'] = FALSE;
524

    
525
  // Check whether the library exists.
526
  if (!isset($library['library path'])) {
527
    $library['library path'] = libraries_get_path($library['machine name']);
528
  }
529
  if ($library['library path'] === FALSE || !file_exists($library['library path'])) {
530
    $library['error'] = 'not found';
531
    $library['error message'] = t('The %library library could not be found.', array(
532
      '%library' => $library['name'],
533
    ));
534
    return $library;
535
  }
536

    
537
  // Invoke callbacks in the 'pre-detect' group.
538
  libraries_invoke('pre-detect', $library);
539

    
540
  // Detect library version, if not hardcoded.
541
  if (!isset($library['version'])) {
542
    // We support both a single parameter, which is an associative array, and an
543
    // indexed array of multiple parameters.
544
    if (isset($library['version arguments'][0])) {
545
      // Add the library as the first argument.
546
      $library['version'] = call_user_func_array($library['version callback'], array_merge(array($library), $library['version arguments']));
547
    }
548
    else {
549
      $library['version'] = call_user_func($library['version callback'], $library, $library['version arguments']);
550
    }
551
    if (empty($library['version'])) {
552
      $library['error'] = 'not detected';
553
      $library['error message'] = t('The version of the %library library could not be detected.', array(
554
        '%library' => $library['name'],
555
      ));
556
      return $library;
557
    }
558
  }
559

    
560
  // Determine to which supported version the installed version maps.
561
  if (!empty($library['versions'])) {
562
    ksort($library['versions']);
563
    $version = 0;
564
    foreach ($library['versions'] as $supported_version => $version_properties) {
565
      if (version_compare($library['version'], $supported_version, '>=')) {
566
        $version = $supported_version;
567
      }
568
    }
569
    if (!$version) {
570
      $library['error'] = 'not supported';
571
      $library['error message'] = t('The installed version %version of the %library library is not supported.', array(
572
        '%version' => $library['version'],
573
        '%library' => $library['name'],
574
      ));
575
      return $library;
576
    }
577

    
578
    // Apply version specific definitions and overrides.
579
    $library = array_merge($library, $library['versions'][$version]);
580
    unset($library['versions']);
581
  }
582

    
583
  // Check each variant if it is installed.
584
  if (!empty($library['variants'])) {
585
    foreach ($library['variants'] as $variant_name => &$variant) {
586
      // If no variant callback has been set, assume the variant to be
587
      // installed.
588
      if (!isset($variant['variant callback'])) {
589
        $variant['installed'] = TRUE;
590
      }
591
      else {
592
        // We support both a single parameter, which is an associative array,
593
        // and an indexed array of multiple parameters.
594
        if (isset($variant['variant arguments'][0])) {
595
          // Add the library as the first argument, and the variant name as the second.
596
          $variant['installed'] = call_user_func_array($variant['variant callback'], array_merge(array($library, $variant_name), $variant['variant arguments']));
597
        }
598
        else {
599
          $variant['installed'] = $variant['variant callback']($library, $variant_name, $variant['variant arguments']);
600
        }
601
        if (!$variant['installed']) {
602
          $variant['error'] = 'not found';
603
          $variant['error message'] = t('The %variant variant of the %library library could not be found.', array(
604
            '%variant' => $variant_name,
605
            '%library' => $library['name'],
606
          ));
607
        }
608
      }
609
    }
610
  }
611

    
612
  // If we end up here, the library should be usable.
613
  $library['installed'] = TRUE;
614

    
615
  // Invoke callbacks in the 'post-detect' group.
616
  libraries_invoke('post-detect', $library);
617

    
618
  return $library;
619
}
620

    
621
/**
622
 * Loads a library.
623
 *
624
 * @param $name
625
 *   The name of the library to load.
626
 * @param $variant
627
 *   The name of the variant to load. Note that only one variant of a library
628
 *   can be loaded within a single request. The variant that has been passed
629
 *   first is used; different variant names in subsequent calls are ignored.
630
 *
631
 * @return
632
 *   An associative array of the library information as returned from
633
 *   libraries_info(). The top-level properties contain the effective definition
634
 *   of the library (variant) that has been loaded. Additionally:
635
 *   - installed: Whether the library is installed, as determined by
636
 *     libraries_detect_library().
637
 *   - loaded: Either the amount of library files that have been loaded, or
638
 *     FALSE if the library could not be loaded.
639
 *   See hook_libraries_info() for more information.
640
 */
641
function libraries_load($name, $variant = NULL) {
642
  $loaded = &drupal_static(__FUNCTION__, array());
643

    
644
  if (!isset($loaded[$name])) {
645
    $library = cache_get($name, 'cache_libraries');
646
    if ($library) {
647
      $library = $library->data;
648
    }
649
    else {
650
      $library = libraries_detect($name);
651
      cache_set($name, $library, 'cache_libraries');
652
    }
653

    
654
    // Exit early if the library was not found.
655
    if ($library === FALSE) {
656
      $loaded[$name] = $library;
657
      return $loaded[$name];
658
    }
659

    
660
    // If a variant was specified, override the top-level properties with the
661
    // variant properties.
662
    if (isset($variant)) {
663
      // Ensure that the $variant key exists, and if it does not, set its
664
      // 'installed' property to FALSE by default. This will prevent the loading
665
      // of the library files below.
666
      $library['variants'] += array($variant => array('installed' => FALSE));
667
      $library = array_merge($library, $library['variants'][$variant]);
668
    }
669
    // Regardless of whether a specific variant was requested or not, there can
670
    // only be one variant of a library within a single request.
671
    unset($library['variants']);
672

    
673
    // Invoke callbacks in the 'pre-dependencies-load' group.
674
    libraries_invoke('pre-dependencies-load', $library);
675

    
676
    // If the library (variant) is installed, load it.
677
    $library['loaded'] = FALSE;
678
    if ($library['installed']) {
679
      // Load library dependencies.
680
      if (isset($library['dependencies'])) {
681
        foreach ($library['dependencies'] as $dependency) {
682
          libraries_load($dependency);
683
        }
684
      }
685

    
686
      // Invoke callbacks in the 'pre-load' group.
687
      libraries_invoke('pre-load', $library);
688

    
689
      // Load all the files associated with the library.
690
      $library['loaded'] = libraries_load_files($library);
691

    
692
      // Invoke callbacks in the 'post-load' group.
693
      libraries_invoke('post-load', $library);
694
    }
695
    $loaded[$name] = $library;
696
  }
697

    
698
  return $loaded[$name];
699
}
700

    
701
/**
702
 * Loads a library's files.
703
 *
704
 * @param $library
705
 *   An array of library information as returned by libraries_info().
706
 *
707
 * @return
708
 *   The number of loaded files.
709
 */
710
function libraries_load_files($library) {
711
  // As this key was added after 7.x-2.1 cached library structures might not
712
  // have it.
713
  $library += array('post-load integration files' => FALSE);
714

    
715
  // Load integration files.
716
  if (!$library['post-load integration files'] && !empty($library['integration files'])) {
717
    $enabled_themes = array();
718
    foreach (list_themes() as $theme_name => $theme) {
719
      if ($theme->status) {
720
        $enabled_themes[] = $theme_name;
721
      }
722
    }
723
    foreach ($library['integration files'] as $provider => $files) {
724
      if (module_exists($provider)) {
725
        libraries_load_files(array(
726
          'files' => $files,
727
          'path' => '',
728
          'library path' => drupal_get_path('module', $provider),
729
          'post-load integration files' => FALSE,
730
        ));
731
      }
732
      elseif (in_array($provider, $enabled_themes)) {
733
        libraries_load_files(array(
734
          'files' => $files,
735
          'path' => '',
736
          'library path' => drupal_get_path('theme', $provider),
737
          'post-load integration files' => FALSE,
738
        ));
739
      }
740
    }
741
  }
742

    
743
  // Construct the full path to the library for later use.
744
  $path = $library['library path'];
745
  $path = ($library['path'] !== '' ? $path . '/' . $library['path'] : $path);
746

    
747
  // Count the number of loaded files for the return value.
748
  $count = 0;
749

    
750
  // Load both the JavaScript and the CSS files.
751
  // The parameters for drupal_add_js() and drupal_add_css() require special
752
  // handling.
753
  // @see drupal_process_attached()
754
  foreach (array('js', 'css') as $type) {
755
    if (!empty($library['files'][$type])) {
756
      foreach ($library['files'][$type] as $data => $options) {
757
        // If the value is not an array, it's a filename and passed as first
758
        // (and only) argument.
759
        if (!is_array($options)) {
760
          $data = $options;
761
          $options = array();
762
        }
763
        // In some cases, the first parameter ($data) is an array. Arrays can't
764
        // be passed as keys in PHP, so we have to get $data from the value
765
        // array.
766
        if (is_numeric($data)) {
767
          $data = $options['data'];
768
          unset($options['data']);
769
        }
770
        // Prepend the library path to the file name.
771
        $data = "$path/$data";
772
        // Apply the default group if the group isn't explicitly given.
773
        if (!isset($options['group'])) {
774
          $options['group'] = ($type == 'js') ? JS_DEFAULT : CSS_DEFAULT;
775
        }
776
        call_user_func('drupal_add_' . $type, $data, $options);
777
        $count++;
778
      }
779
    }
780
  }
781

    
782
  // Load PHP files.
783
  if (!empty($library['files']['php'])) {
784
    foreach ($library['files']['php'] as $file => $array) {
785
      $file_path = DRUPAL_ROOT . '/' . $path . '/' . $file;
786
      if (file_exists($file_path)) {
787
        _libraries_require_once($file_path);
788
        $count++;
789
      }
790
    }
791
  }
792

    
793
  // Load integration files.
794
  if ($library['post-load integration files'] && !empty($library['integration files'])) {
795
    $enabled_themes = array();
796
    foreach (list_themes() as $theme_name => $theme) {
797
      if ($theme->status) {
798
        $enabled_themes[] = $theme_name;
799
      }
800
    }
801
    foreach ($library['integration files'] as $provider => $files) {
802
      if (module_exists($provider)) {
803
        libraries_load_files(array(
804
          'files' => $files,
805
          'path' => '',
806
          'library path' => drupal_get_path('module', $provider),
807
          'post-load integration files' => FALSE,
808
        ));
809
      }
810
      elseif (in_array($provider, $enabled_themes)) {
811
        libraries_load_files(array(
812
          'files' => $files,
813
          'path' => '',
814
          'library path' => drupal_get_path('theme', $provider),
815
          'post-load integration files' => FALSE,
816
        ));
817
      }
818
    }
819
  }
820

    
821
  return $count;
822
}
823

    
824
/**
825
 * Wrapper function for require_once.
826
 *
827
 * A library file could set a $path variable in file scope. Requiring such a
828
 * file directly in libraries_load_files() would lead to the local $path
829
 * variable being overridden after the require_once statement. This would
830
 * break loading further files. Therefore we use this trivial wrapper which has
831
 * no local state that can be tampered with.
832
 *
833
 * @param $file_path
834
 *   The file path of the file to require.
835
 */
836
function _libraries_require_once($file_path) {
837
  require_once $file_path;
838
}
839

    
840

    
841
/**
842
 * Gets the version information from an arbitrary library.
843
 *
844
 * @param $library
845
 *   An associative array containing all information about the library.
846
 * @param $options
847
 *   An associative array containing with the following keys:
848
 *   - file: The filename to parse for the version, relative to the library
849
 *     path. For example: 'docs/changelog.txt'.
850
 *   - pattern: A string containing a regular expression (PCRE) to match the
851
 *     library version. For example: '@version\s+([0-9a-zA-Z\.-]+)@'. Note that
852
 *     the returned version is not the match of the entire pattern (i.e.
853
 *     '@version 1.2.3' in the above example) but the match of the first
854
 *     sub-pattern (i.e. '1.2.3' in the above example).
855
 *   - lines: (optional) The maximum number of lines to search the pattern in.
856
 *     Defaults to 20.
857
 *   - cols: (optional) The maximum number of characters per line to take into
858
 *     account. Defaults to 200. In case of minified or compressed files, this
859
 *     prevents reading the entire file into memory.
860
 *
861
 * @return
862
 *   A string containing the version of the library.
863
 *
864
 * @see libraries_get_path()
865
 */
866
function libraries_get_version($library, $options) {
867
  // Provide defaults.
868
  $options += array(
869
    'file' => '',
870
    'pattern' => '',
871
    'lines' => 20,
872
    'cols' => 200,
873
  );
874

    
875
  $file = DRUPAL_ROOT . '/' . $library['library path'] . '/' . $options['file'];
876
  if (empty($options['file']) || !file_exists($file)) {
877
    return;
878
  }
879
  $file = fopen($file, 'r');
880
  while ($options['lines'] && $line = fgets($file, $options['cols'])) {
881
    if (preg_match($options['pattern'], $line, $version)) {
882
      fclose($file);
883
      return $version[1];
884
    }
885
    $options['lines']--;
886
  }
887
  fclose($file);
888
}
889

    
890
/**
891
 * Implements hook_help().
892
 */
893
function libraries_help($path, $arg) {
894
  switch ($path) {
895
    case 'admin/reports/libraries':
896
      return t('Click on a library for a status report or detailed installation instructions in case the library is not installed correctly.');
897
  }
898
}
899

    
900
/**
901
 * Implements hook_menu().
902
 */
903
function libraries_menu() {
904
  $items = array();
905
  $items['admin/reports/libraries'] = array(
906
    'title' => 'Libraries',
907
    'description' => 'An overview of libraries installed on this site.',
908
    'page callback' => 'drupal_get_form',
909
    'page arguments' => array('libraries_admin_overview'),
910
    'access arguments' => array('access site reports'),
911
    'file' => 'libraries.admin.inc'
912
  );
913
  $items['admin/reports/libraries/%libraries_ui'] = array(
914
    'title' => 'Library status report',
915
    'description' => 'Status overview for a single library',
916
    'page callback' => 'drupal_get_form',
917
    'page arguments' => array('libraries_admin_library_status_form', 3),
918
    'access arguments' => array('access site reports'),
919
    'file' => 'libraries.admin.inc'
920
  );
921
  return $items;
922
}
923

    
924
/**
925
 * Loads library information for display in the user interface.
926
 *
927
 * This can be used as a menu loader function by specifying a '%libraries_ui'
928
 * parameter in a path.
929
 *
930
 * We do not use libraries_load() (and, thus, a '%libraries' parameter) directly
931
 * for displaying library information in the user interface as we do not want
932
 * the library files to be loaded.
933
 *
934
 * @param string $name
935
 *   The machine name of a library to return registered information for.
936
 *
937
 * @return array|false
938
 *   An associative array containing registered information for the library
939
 *   specified by $name, or FALSE if the library $name is not registered.
940
 *
941
 * @see libraries_detect()
942
 * @see libraries_menu()
943
 */
944
function libraries_ui_load($name) {
945
  return libraries_detect($name);
946
}