Change the version back to dev.
[project/libraries.git] / libraries.module
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
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
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
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 * @library
433 * An array of library information, passed by reference.
434 * @name
435 * The machine name of the passed-in library.
436 */
437 function libraries_info_defaults(&$library, $name) {
438 $library += array(
439 'machine name' => $name,
440 'name' => $name,
441 'vendor url' => '',
442 'download url' => '',
443 'path' => '',
444 'library path' => NULL,
445 'version callback' => 'libraries_get_version',
446 'version arguments' => array(),
447 'files' => array(),
448 'dependencies' => array(),
449 'variants' => array(),
450 'versions' => array(),
451 'integration files' => array(),
452 'callbacks' => array(),
453 // @todo Remove in 7.x-3.x
454 'post-load integration files' => FALSE,
455 );
456 $library['callbacks'] += array(
457 'info' => array(),
458 'pre-detect' => array(),
459 'post-detect' => array(),
460 'pre-dependencies-load' => array(),
461 'pre-load' => array(),
462 'post-load' => array(),
463 );
464
465 // Add our own callbacks before any others.
466 array_unshift($library['callbacks']['info'], 'libraries_prepare_files');
467 array_unshift($library['callbacks']['post-detect'], 'libraries_detect_dependencies');
468
469 return $library;
470 }
471
472 /**
473 * Tries to detect a library and its installed version.
474 *
475 * @param $name
476 * The machine name of a library to return registered information for.
477 *
478 * @return array|false
479 * An associative array containing registered information for the library
480 * specified by $name, or FALSE if the library $name is not registered.
481 * In addition to the keys returned by libraries_info(), the following keys
482 * are contained:
483 * - installed: A boolean indicating whether the library is installed. Note
484 * that not only the top-level library, but also each variant contains this
485 * key.
486 * - version: If the version could be detected, the full version string.
487 * - error: If an error occurred during library detection, one of the
488 * following error statuses: "not found", "not detected", "not supported".
489 * - error message: If an error occurred during library detection, a detailed
490 * error message.
491 *
492 * @see libraries_info()
493 */
494 function libraries_detect($name) {
495 // Re-use the statically cached value of libraries_info() to save memory.
496 $library = &libraries_info($name);
497
498 // Exit early if the library was not found.
499 if ($library === FALSE) {
500 return $library;
501 }
502
503 // If 'installed' is set, library detection ran already.
504 if (isset($library['installed'])) {
505 return $library;
506 }
507
508 $library['installed'] = FALSE;
509
510 // Check whether the library exists.
511 if (!isset($library['library path'])) {
512 $library['library path'] = libraries_get_path($library['machine name']);
513 }
514 if ($library['library path'] === FALSE || !file_exists($library['library path'])) {
515 $library['error'] = 'not found';
516 $library['error message'] = t('The %library library could not be found.', array(
517 '%library' => $library['name'],
518 ));
519 return $library;
520 }
521
522 // Invoke callbacks in the 'pre-detect' group.
523 libraries_invoke('pre-detect', $library);
524
525 // Detect library version, if not hardcoded.
526 if (!isset($library['version'])) {
527 // We support both a single parameter, which is an associative array, and an
528 // indexed array of multiple parameters.
529 if (isset($library['version arguments'][0])) {
530 // Add the library as the first argument.
531 $library['version'] = call_user_func_array($library['version callback'], array_merge(array($library), $library['version arguments']));
532 }
533 else {
534 $library['version'] = $library['version callback']($library, $library['version arguments']);
535 }
536 if (empty($library['version'])) {
537 $library['error'] = 'not detected';
538 $library['error message'] = t('The version of the %library library could not be detected.', array(
539 '%library' => $library['name'],
540 ));
541 return $library;
542 }
543 }
544
545 // Determine to which supported version the installed version maps.
546 if (!empty($library['versions'])) {
547 ksort($library['versions']);
548 $version = 0;
549 foreach ($library['versions'] as $supported_version => $version_properties) {
550 if (version_compare($library['version'], $supported_version, '>=')) {
551 $version = $supported_version;
552 }
553 }
554 if (!$version) {
555 $library['error'] = 'not supported';
556 $library['error message'] = t('The installed version %version of the %library library is not supported.', array(
557 '%version' => $library['version'],
558 '%library' => $library['name'],
559 ));
560 return $library;
561 }
562
563 // Apply version specific definitions and overrides.
564 $library = array_merge($library, $library['versions'][$version]);
565 unset($library['versions']);
566 }
567
568 // Check each variant if it is installed.
569 if (!empty($library['variants'])) {
570 foreach ($library['variants'] as $variant_name => &$variant) {
571 // If no variant callback has been set, assume the variant to be
572 // installed.
573 if (!isset($variant['variant callback'])) {
574 $variant['installed'] = TRUE;
575 }
576 else {
577 // We support both a single parameter, which is an associative array,
578 // and an indexed array of multiple parameters.
579 if (isset($variant['variant arguments'][0])) {
580 // Add the library as the first argument, and the variant name as the second.
581 $variant['installed'] = call_user_func_array($variant['variant callback'], array_merge(array($library, $variant_name), $variant['variant arguments']));
582 }
583 else {
584 $variant['installed'] = $variant['variant callback']($library, $variant_name, $variant['variant arguments']);
585 }
586 if (!$variant['installed']) {
587 $variant['error'] = 'not found';
588 $variant['error message'] = t('The %variant variant of the %library library could not be found.', array(
589 '%variant' => $variant_name,
590 '%library' => $library['name'],
591 ));
592 }
593 }
594 }
595 }
596
597 // If we end up here, the library should be usable.
598 $library['installed'] = TRUE;
599
600 // Invoke callbacks in the 'post-detect' group.
601 libraries_invoke('post-detect', $library);
602
603 return $library;
604 }
605
606 /**
607 * Loads a library.
608 *
609 * @param $name
610 * The name of the library to load.
611 * @param $variant
612 * The name of the variant to load. Note that only one variant of a library
613 * can be loaded within a single request. The variant that has been passed
614 * first is used; different variant names in subsequent calls are ignored.
615 *
616 * @return
617 * An associative array of the library information as returned from
618 * libraries_info(). The top-level properties contain the effective definition
619 * of the library (variant) that has been loaded. Additionally:
620 * - installed: Whether the library is installed, as determined by
621 * libraries_detect_library().
622 * - loaded: Either the amount of library files that have been loaded, or
623 * FALSE if the library could not be loaded.
624 * See hook_libraries_info() for more information.
625 */
626 function libraries_load($name, $variant = NULL) {
627 $loaded = &drupal_static(__FUNCTION__, array());
628
629 if (!isset($loaded[$name])) {
630 $library = cache_get($name, 'cache_libraries');
631 if ($library) {
632 $library = $library->data;
633 }
634 else {
635 $library = libraries_detect($name);
636 cache_set($name, $library, 'cache_libraries');
637 }
638
639 // Exit early if the library was not found.
640 if ($library === FALSE) {
641 $loaded[$name] = $library;
642 return $loaded[$name];
643 }
644
645 // If a variant was specified, override the top-level properties with the
646 // variant properties.
647 if (isset($variant)) {
648 // Ensure that the $variant key exists, and if it does not, set its
649 // 'installed' property to FALSE by default. This will prevent the loading
650 // of the library files below.
651 $library['variants'] += array($variant => array('installed' => FALSE));
652 $library = array_merge($library, $library['variants'][$variant]);
653 }
654 // Regardless of whether a specific variant was requested or not, there can
655 // only be one variant of a library within a single request.
656 unset($library['variants']);
657
658 // Invoke callbacks in the 'pre-dependencies-load' group.
659 libraries_invoke('pre-dependencies-load', $library);
660
661 // If the library (variant) is installed, load it.
662 $library['loaded'] = FALSE;
663 if ($library['installed']) {
664 // Load library dependencies.
665 if (isset($library['dependencies'])) {
666 foreach ($library['dependencies'] as $dependency) {
667 libraries_load($dependency);
668 }
669 }
670
671 // Invoke callbacks in the 'pre-load' group.
672 libraries_invoke('pre-load', $library);
673
674 // Load all the files associated with the library.
675 $library['loaded'] = libraries_load_files($library);
676
677 // Invoke callbacks in the 'post-load' group.
678 libraries_invoke('post-load', $library);
679 }
680 $loaded[$name] = $library;
681 }
682
683 return $loaded[$name];
684 }
685
686 /**
687 * Loads a library's files.
688 *
689 * @param $library
690 * An array of library information as returned by libraries_info().
691 *
692 * @return
693 * The number of loaded files.
694 */
695 function libraries_load_files($library) {
696 // Load integration files.
697 if (!$library['post-load integration files'] && !empty($library['integration files'])) {
698 $enabled_themes = array();
699 foreach (list_themes() as $theme_name => $theme) {
700 if ($theme->status) {
701 $enabled_themes[] = $theme_name;
702 }
703 }
704 foreach ($library['integration files'] as $provider => $files) {
705 if (module_exists($provider)) {
706 libraries_load_files(array(
707 'files' => $files,
708 'path' => '',
709 'library path' => drupal_get_path('module', $provider),
710 'post-load integration files' => FALSE,
711 ));
712 }
713 elseif (in_array($provider, $enabled_themes)) {
714 libraries_load_files(array(
715 'files' => $files,
716 'path' => '',
717 'library path' => drupal_get_path('theme', $provider),
718 'post-load integration files' => FALSE,
719 ));
720 }
721 }
722 }
723
724 // Construct the full path to the library for later use.
725 $path = $library['library path'];
726 $path = ($library['path'] !== '' ? $path . '/' . $library['path'] : $path);
727
728 // Count the number of loaded files for the return value.
729 $count = 0;
730
731 // Load both the JavaScript and the CSS files.
732 // The parameters for drupal_add_js() and drupal_add_css() require special
733 // handling.
734 // @see drupal_process_attached()
735 foreach (array('js', 'css') as $type) {
736 if (!empty($library['files'][$type])) {
737 foreach ($library['files'][$type] as $data => $options) {
738 // If the value is not an array, it's a filename and passed as first
739 // (and only) argument.
740 if (!is_array($options)) {
741 $data = $options;
742 $options = array();
743 }
744 // In some cases, the first parameter ($data) is an array. Arrays can't
745 // be passed as keys in PHP, so we have to get $data from the value
746 // array.
747 if (is_numeric($data)) {
748 $data = $options['data'];
749 unset($options['data']);
750 }
751 // Prepend the library path to the file name.
752 $data = "$path/$data";
753 // Apply the default group if the group isn't explicitly given.
754 if (!isset($options['group'])) {
755 $options['group'] = ($type == 'js') ? JS_DEFAULT : CSS_DEFAULT;
756 }
757 call_user_func('drupal_add_' . $type, $data, $options);
758 $count++;
759 }
760 }
761 }
762
763 // Load PHP files.
764 if (!empty($library['files']['php'])) {
765 foreach ($library['files']['php'] as $file => $array) {
766 $file_path = DRUPAL_ROOT . '/' . $path . '/' . $file;
767 if (file_exists($file_path)) {
768 _libraries_require_once($file_path);
769 $count++;
770 }
771 }
772 }
773
774 // Load integration files.
775 if ($library['post-load integration files'] && !empty($library['integration files'])) {
776 $enabled_themes = array();
777 foreach (list_themes() as $theme_name => $theme) {
778 if ($theme->status) {
779 $enabled_themes[] = $theme_name;
780 }
781 }
782 foreach ($library['integration files'] as $provider => $files) {
783 if (module_exists($provider)) {
784 libraries_load_files(array(
785 'files' => $files,
786 'path' => '',
787 'library path' => drupal_get_path('module', $provider),
788 'post-load integration files' => FALSE,
789 ));
790 }
791 elseif (in_array($provider, $enabled_themes)) {
792 libraries_load_files(array(
793 'files' => $files,
794 'path' => '',
795 'library path' => drupal_get_path('theme', $provider),
796 'post-load integration files' => FALSE,
797 ));
798 }
799 }
800 }
801
802 return $count;
803 }
804
805 /**
806 * Wrapper function for require_once.
807 *
808 * A library file could set a $path variable in file scope. Requiring such a
809 * file directly in libraries_load_files() would lead to the local $path
810 * variable being overridden after the require_once statement. This would
811 * break loading further files. Therefore we use this trivial wrapper which has
812 * no local state that can be tampered with.
813 *
814 * @param $file_path
815 * The file path of the file to require.
816 */
817 function _libraries_require_once($file_path) {
818 require_once $file_path;
819 }
820
821
822 /**
823 * Gets the version information from an arbitrary library.
824 *
825 * @param $library
826 * An associative array containing all information about the library.
827 * @param $options
828 * An associative array containing with the following keys:
829 * - file: The filename to parse for the version, relative to the library
830 * path. For example: 'docs/changelog.txt'.
831 * - pattern: A string containing a regular expression (PCRE) to match the
832 * library version. For example: '@version\s+([0-9a-zA-Z\.-]+)@'. Note that
833 * the returned version is not the match of the entire pattern (i.e.
834 * '@version 1.2.3' in the above example) but the match of the first
835 * sub-pattern (i.e. '1.2.3' in the above example).
836 * - lines: (optional) The maximum number of lines to search the pattern in.
837 * Defaults to 20.
838 * - cols: (optional) The maximum number of characters per line to take into
839 * account. Defaults to 200. In case of minified or compressed files, this
840 * prevents reading the entire file into memory.
841 *
842 * @return
843 * A string containing the version of the library.
844 *
845 * @see libraries_get_path()
846 */
847 function libraries_get_version($library, $options) {
848 // Provide defaults.
849 $options += array(
850 'file' => '',
851 'pattern' => '',
852 'lines' => 20,
853 'cols' => 200,
854 );
855
856 $file = DRUPAL_ROOT . '/' . $library['library path'] . '/' . $options['file'];
857 if (empty($options['file']) || !file_exists($file)) {
858 return;
859 }
860 $file = fopen($file, 'r');
861 while ($options['lines'] && $line = fgets($file, $options['cols'])) {
862 if (preg_match($options['pattern'], $line, $version)) {
863 fclose($file);
864 return $version[1];
865 }
866 $options['lines']--;
867 }
868 fclose($file);
869 }