b316b55579ef03b379489ae548d08288134e0421
5 * External library handling for Drupal modules.
9 * Implements hook_flush_caches().
11 function libraries_flush_caches() {
12 return array('cache_libraries');
16 * Gets the path of a library.
19 * The machine name of a library to return the path for.
21 * Whether to prefix the resulting path with base_path().
24 * The path to the specified library.
28 function libraries_get_path($name, $base_path = FALSE
) {
29 $libraries = &drupal_static(__FUNCTION__
);
31 if (!isset($libraries)) {
32 $libraries = libraries_get_libraries();
35 $path = ($base_path ?
base_path() : '');
36 if (!isset($libraries[$name])) {
37 // Most often, external libraries can be shared across multiple sites, so
38 // we return sites/all/libraries as the default path.
39 $path .
= 'sites/all/libraries/' .
$name;
42 $path .
= $libraries[$name];
49 * Returns an array of library directories.
51 * Returns an array of library directories from the all-sites directory
52 * (i.e. sites/all/libraries/), the profiles directory, and site-specific
53 * directory (i.e. sites/somesite/libraries/). The returned array will be keyed
54 * by the library name. Site-specific libraries are prioritized over libraries
55 * in the default directories. That is, if a library with the same name appears
56 * in both the site-wide directory and site-specific directory, only the
57 * site-specific version will be listed.
60 * A list of library directories.
64 function libraries_get_libraries() {
65 $directory = 'libraries';
67 $profile = drupal_get_profile();
68 $config = conf_path();
70 // Similar to 'modules' and 'themes' directories in the root directory,
71 // certain distributions may want to place libraries into a 'libraries'
72 // directory in Drupal's root directory.
73 $searchdir[] = $directory;
75 // The 'profiles' directory contains pristine collections of modules and
76 // themes as organized by a distribution. It is pristine in the same way
77 // that /modules is pristine for core; users should avoid changing anything
78 // there in favor of sites/all or sites/<domain> directories.
79 if (file_exists("profiles/$profile/$directory")) {
80 $searchdir[] = "profiles/$profile/$directory";
83 // Always search sites/all/*.
84 $searchdir[] = 'sites/all/' .
$directory;
86 // Also search sites/<domain>/*.
87 if (file_exists("$config/$directory")) {
88 $searchdir[] = "$config/$directory";
91 // Retrieve list of directories.
92 // @todo Core: Allow to scan for directories.
93 $directories = array();
94 $nomask = array('CVS');
95 foreach ($searchdir as
$dir) {
96 if (is_dir($dir) && $handle = opendir($dir)) {
97 while (FALSE
!== ($file = readdir($handle))) {
98 if (!in_array($file, $nomask) && $file[0] != '.') {
99 if (is_dir("$dir/$file")) {
100 $directories[$file] = "$dir/$file";
112 * Looks for library info files.
114 * This function scans the following directories for info files:
116 * - profiles/$profilename/libraries
117 * - sites/all/libraries
118 * - sites/$sitename/libraries
119 * - any directories specified via hook_libraries_info_file_paths()
122 * An array of info files, keyed by library name. The values are the paths of
125 function libraries_scan_info_files() {
126 $profile = drupal_get_profile();
127 $config = conf_path();
129 // Build a list of directories.
130 $directories = module_invoke_all('libraries_info_file_paths');
131 $directories[] = 'libraries';
132 $directories[] = "profiles/$profile/libraries";
133 $directories[] = 'sites/all/libraries';
134 $directories[] = "$config/libraries";
136 // Scan for info files.
138 foreach ($directories as
$dir) {
139 if (file_exists($dir)) {
140 $files = array_merge($files, file_scan_directory($dir, '@^[a-z0-9._-]+\.libraries\.info$@', array(
147 foreach ($files as
$filename => $file) {
148 $files[basename($filename, '.libraries')] = $file;
149 unset($files[$filename]);
155 * Returns information about registered libraries.
157 * The returned information is unprocessed, i.e. as registered by modules.
160 * (optional) The machine name of a library to return registered information
161 * for, or FALSE if no library with the given name exists. If omitted,
162 * information about all libraries is returned.
165 * An associative array containing registered information for all libraries,
166 * or the registered information for the library specified by $name.
168 * @see hook_libraries_info()
170 * @todo Re-introduce support for include file plugin system - either by copying
171 * Wysiwyg's code, or directly switching to CTools.
173 function libraries_info($name = NULL
) {
174 $libraries = &drupal_static(__FUNCTION__
);
176 if (!isset($libraries)) {
177 $libraries = array();
178 // Gather information from hook_libraries_info().
179 foreach (module_implements('libraries_info') as
$module) {
180 foreach (module_invoke($module, 'libraries_info') as
$machine_name => $properties) {
181 $properties['module'] = $module;
182 $libraries[$machine_name] = $properties;
185 // Gather information from .info files.
186 // .info files override module definitions.
187 foreach (libraries_scan_info_files() as
$machine_name => $file) {
188 $properties = drupal_parse_info_file($file->uri
);
189 $properties['info file'] = $file->uri
;
190 $libraries[$machine_name] = $properties;
194 foreach ($libraries as
$machine_name => &$properties) {
195 $properties += array(
196 'machine name' => $machine_name,
197 'name' => $machine_name,
199 'download url' => '',
201 'library path' => NULL
,
202 'version callback' => 'libraries_get_version',
203 'version arguments' => array(),
205 'variants' => array(),
206 'versions' => array(),
207 'integration files' => array(),
211 // Allow modules to alter the registered libraries.
212 drupal_alter('libraries_info', $libraries);
216 return !empty($libraries[$name]) ?
$libraries[$name] : FALSE
;
222 * Detect libraries and library versions.
224 * @todo We need to figure out whether, and if, how we want to retain the
225 * processed information. I.e. either use a static cache here, or make
226 * libraries_info() conditionally invoke libraries_detect($name). D7 only way:
227 * Re-use drupal_static() of libraries_info() - but would still require to
228 * update the (DB) cache (there likely will be one soon). Also, we probably do
229 * not want to ALWAYS parse ALL possible libraries; rather, the
230 * requesting/consuming module likely wants to know whether a list of
231 * supported libraries (possibly those registered by itself, or in a certain
232 * "category") is available... Food for thought.
235 * An array of libraries to detect, as returned from libraries_info().
237 * @see libraries_info()
239 function libraries_detect($libraries) {
240 foreach ($libraries as
$name => &$library) {
241 libraries_detect_library($library);
242 cache_set($name, $library, 'cache_libraries');
248 * Tries to detect a library and its installed version.
251 * An associative array describing a single library, as returned from
254 function libraries_detect_library(&$library) {
255 $library['installed'] = FALSE
;
257 // Check whether the library exists.
258 if (!isset($library['library path'])) {
259 $library['library path'] = libraries_get_path($library['machine name']);
261 if (!file_exists($library['library path'])) {
262 $library['error'] = 'not found';
263 $library['error message'] = t('The %library library could not be found.', array(
264 '%library' => $library['name'],
269 // Detect library version, if not hardcoded.
270 if (!isset($library['version'])) {
271 // We support both a single parameter, which is an associative array, and an
272 // indexed array of multiple parameters.
273 if (isset($library['version arguments'][0])) {
274 // Add the library as the first argument.
275 $library['version'] = call_user_func_array($library['version callback'], array_merge(array($library), $library['version arguments']));
278 $library['version'] = $library['version callback']($library, $library['version arguments']);
280 if (empty($library['version'])) {
281 $library['error'] = 'not detected';
282 $library['error message'] = t('The version of the %library library could not be detected.', array(
283 '%library' => $library['name'],
289 // Determine to which supported version the installed version maps.
290 if (!empty($library['versions'])) {
291 ksort($library['versions']);
293 foreach ($library['versions'] as
$supported_version => $version_properties) {
294 if (version_compare($library['version'], $supported_version, '>=')) {
295 $version = $supported_version;
299 $library['error'] = 'not supported';
300 $library['error message'] = t('The installed version %version of the %library library is not supported.', array(
301 '%version' => $library['version'],
302 '%library' => $library['name'],
307 // Apply version specific definitions and overrides.
308 $library = array_merge($library, $library['versions'][$version]);
309 unset($library['versions']);
312 // Check each variant if it is installed.
313 if (!empty($library['variants'])) {
314 foreach ($library['variants'] as
$variant_name => &$variant) {
315 // If no variant callback has been set, assume the variant to be
317 if (!isset($variant['variant callback'])) {
318 $variant['installed'] = TRUE
;
321 // We support both a single parameter, which is an associative array,
322 // and an indexed array of multiple parameters.
323 if (isset($variant['variant arguments'][0])) {
324 // Add the library as the first argument, and the variant name as the second.
325 $variant['installed'] = call_user_func_array($variant['variant callback'], array_merge(array($library, $variant_name), $variant['variant arguments']));
328 $variant['installed'] = $variant['variant callback']($library, $variant_name, $variant['variant arguments']);
330 if (!$variant['installed']) {
331 $variant['error'] = 'not found';
332 $variant['error message'] = t('The %variant variant of the %library library could not be found.', array(
333 '%variant' => $variant_name,
334 '%library' => $library['name'],
341 // If we end up here, the library should be usable.
342 $library['installed'] = TRUE
;
350 * The name of the library to load.
352 * The name of the variant to load. Note that only one variant of a library
353 * can be loaded within a single request. The variant that has been passed
354 * first is used; different variant names in subsequent calls are ignored.
357 * An associative array of the library information as returned from
358 * libraries_info(). The top-level properties contain the effective definition
359 * of the library (variant) that has been loaded. Additionally:
360 * - installed: Whether the library is installed, as determined by
361 * libraries_detect_library().
362 * - loaded: Either the amount of library files that have been loaded, or
363 * FALSE if the library could not be loaded.
364 * See hook_libraries_info() for more information.
366 function libraries_load($name, $variant = NULL
) {
367 $loaded = &drupal_static(__FUNCTION__
, array());
369 if (!isset($loaded[$name])) {
370 $library = cache_get($name, 'cache_libraries');
372 $library = $library->data
;
375 $library = libraries_info($name);
376 libraries_detect_library($library);
377 cache_set($name, $library, 'cache_libraries');
380 // If a variant was specified, override the top-level properties with the
381 // variant properties.
382 if (isset($variant)) {
383 // Ensure that the $variant key exists, and if it does not, set its
384 // 'installed' property to FALSE by default. This will prevent the loading
385 // of the library files below.
386 $library['variants'] += array($variant => array('installed' => FALSE
));
387 $library = array_merge($library, $library['variants'][$variant]);
389 // Regardless of whether a specific variant was requested or not, there can
390 // only be one variant of a library within a single request.
391 unset($library['variants']);
393 // If the library (variant) is installed, load it.
394 $library['loaded'] = FALSE
;
395 if ($library['installed']) {
396 $library['loaded'] = libraries_load_files($library);
398 $loaded[$name] = $library;
401 return $loaded[$name];
405 * Loads a library's files.
408 * An array of library information as returned by libraries_info().
411 * The number of loaded files.
413 function libraries_load_files($library) {
414 // Load integration files.
415 if (!empty($library['integration files'])) {
416 foreach ($library['integration files'] as
$module => $files) {
417 libraries_load_files(array(
420 'library path' => drupal_get_path('module', $module),
425 // Construct the full path to the library for later use.
426 $path = $library['library path'];
427 $path = ($library['path'] !== '' ?
$path .
'/' .
$library['path'] : $path);
429 // Count the number of loaded files for the return value.
432 // Load both the JavaScript and the CSS files.
433 // The parameters for drupal_add_js() and drupal_add_css() require special
435 // @see drupal_process_attached()
436 foreach (array('js', 'css') as
$type) {
437 if (!empty($library['files'][$type])) {
438 foreach ($library['files'][$type] as
$data => $options) {
439 // If the value is not an array, it's a filename and passed as first
440 // (and only) argument.
441 if (!is_array($options)) {
442 // Prepend the library path to the file name.
443 $data = "$path/$options";
446 // In some cases, the first parameter ($data) is an array. Arrays can't
447 // be passed as keys in PHP, so we have to get $data from the value
449 if (is_numeric($data)) {
450 $data = $options['data'];
451 unset($options['data']);
453 // Apply the default group if the group isn't explicitly given.
454 if (!isset($options['group'])) {
455 $options['group'] = ($type == 'js') ? JS_DEFAULT
: CSS_DEFAULT
;
457 call_user_func('drupal_add_' .
$type, $data, $options);
464 if (!empty($library['files']['php'])) {
465 foreach ($library['files']['php'] as
$file) {
466 $file_path = DRUPAL_ROOT .
'/' .
$path .
'/' .
$file;
467 if (file_exists($file_path)) {
468 require_once
$file_path;
478 * Gets the version information from an arbitrary library.
481 * An associative array containing all information about the library.
483 * An associative array containing with the following keys:
484 * - file: The filename to parse for the version, relative to the library
485 * path. For example: 'docs/changelog.txt'.
486 * - pattern: A string containing a regular expression (PCRE) to match the
487 * library version. For example: '@version\s+([0-9a-zA-Z\.-]+)@'.
488 * - lines: (optional) The maximum number of lines to search the pattern in.
490 * - cols: (optional) The maximum number of characters per line to take into
491 * account. Defaults to 200. In case of minified or compressed files, this
492 * prevents reading the entire file into memory.
495 * A string containing the version of the library.
497 * @see libraries_get_path()
499 function libraries_get_version($library, $options) {
508 $file = DRUPAL_ROOT .
'/' .
$library['library path'] .
'/' .
$options['file'];
509 if (empty($options['file']) || !file_exists($file)) {
512 $file = fopen($file, 'r');
513 while ($options['lines'] && $line = fgets($file, $options['cols'])) {
514 if (preg_match($options['pattern'], $line, $version)) {