diff --git a/libraries.api.php b/libraries.api.php index 9606185..4e2f248 100644 --- a/libraries.api.php +++ b/libraries.api.php @@ -15,15 +15,38 @@ * an associative array containing: * - name: The official, human-readable name of the library. * - vendor url: The URL of the homepage of the library. - * - download url: The URL of a web page on which the library can be obtained. - * - path: (optional) A relative path from the directory of the library to the - * actual library. Only required if the extracted download package contains - * the actual library files in a sub-directory. - * - library path: (optional) The absolute path to the library directory. This - * should not be declared normally, as it is automatically detected, to - * allow for multiple possible library locations. A valid use-case is an - * external library, in which case the full URL to the library should be - * specified here. + * - download url: (optional) The URL of a web page on which the library can + * be obtained. For external libraries, which cannot be downloaded, this can + * be omitted. + * - path: (optional) The absolute path to the library directory. This should + * not be declared normally, as it is automatically detected, to allow for + * multiple possible library locations. A valid use-case is an external + * library, in which case the full URL to the library should be specified + * here. + * - path callback: (optional) The name of a function that detects and returns + * the full path to the library. If the library cannot be found, it should + * return FALSE. The first argument is always $library, an array containing + * all library information as described here. There are two ways to declare + * the path callback's additional arguments, either as a single $options + * parameter or as multiple parameters, which correspond to the two ways to + * specify the argument values (see 'path arguments'). Defaults to + * libraries_get_path(). + * - path arguments: (optional) A list of arguments to pass to the path + * callback. Path arguments can be declared either as an associative array + * whose keys are the argument names or as an indexed array without + * specifying keys. If declared as an associative array, the arguments get + * passed to the location callback as a single $options parameter whose keys + * are the argument names (i.e. $options is identical to the specified + * array). If declared as an indexed array, the array values get passed to + * the version callback as seperate arguments in the order they were + * declared. The default location callback libraries_get_path() expects a + * single argument, which should be declared as an indexed array: + * - fallback: (optional) A fallback path to use in case the library is not + * found locally. This can be used for libraries that can be downloaded, + * but are also available externally. + * - directory: (optional) A relative path from the directory of the library + * to the actual library. Only required if the extracted download package + * contains the actual library files in a sub-directory. * - version: (optional) The version of the library. This should not be * declared normally, as it is automatically detected (see 'version * callback' below) to allow for version changes of libraries without code @@ -52,7 +75,7 @@ * array). If declared as an indexed array, the array values get passed to * the version callback as separate arguments in the order they were * declared. The default version callback libraries_get_version() expects a - * single, associative array with named keys: + * single, associative array with the following named keys: * - file: The filename to parse for the version, relative to the path * speficied as the 'library path' property (see above). For example: * 'docs/changelog.txt'. @@ -213,7 +236,7 @@ function hook_libraries_info() { 'download url' => 'http://example.com/download', // Optional: If, after extraction, the actual library files are contained in // 'sites/all/libraries/example/lib', specify the relative path here. - 'path' => 'lib', + 'directory' => 'lib', // Optional: Define a custom version detection callback, if required. 'version callback' => 'mymodule_get_version', // Specify arguments for the version callback. By default, @@ -360,7 +383,7 @@ function hook_libraries_info() { 'name' => 'TinyMCE', 'vendor url' => 'http://tinymce.moxiecode.com', 'download url' => 'http://tinymce.moxiecode.com/download.php', - 'path' => 'jscripts/tiny_mce', + 'directory' => 'jscripts/tiny_mce', // The regular expression catches two parts (the major and the minor // version), which libraries_get_version() doesn't allow. 'version callback' => 'tinymce_get_version', @@ -430,6 +453,48 @@ function hook_libraries_info() { ), ), ); + + // A simple external library. + $libraries['external'] = array( + 'name' => 'External library', + // This library does not have a download url! + 'vendor url' => 'http://example.com/external', + // It is only availably externally so we must declare the library path. + 'path' => 'http://example.com/external/source', + // The version cannot be detected programmatically, so it must be declared + // upfront. + 'version' => '1.0.1', + // The library only consists of one file, which is located at + // http://example.com/external/source/external.js. We have already declared + // the library path above, so we only need to specify the filename here. + 'files' => array( + 'js' => array( + 'external.js', + ), + ), + ); + + // A library that is both available locally and externally, i.e. you can + // download it to your server for performance or use the online version for + // convenience. + $libraries['openlayers'] = array( + 'name' => 'OpenLayers', + 'vendor url' => 'http://openlayers.org/', + 'download url' => 'http://trac.osgeo.org/openlayers/wiki/HowToDownload', + // Because we want people to be able to use the library locally, but fall + // back to the external library, we declare the optional 'fallback' option. + // The path we specify is then used as a fallback if the library is not + // found locally. + 'path arguments' => array('http://openlayers.org/api'), + // Regardless of whether it is available locally or externally, the files + // are the same. + 'files' => array( + 'js' => array( + 'OpenLayers.js', + ), + ), + ); + return $libraries; } diff --git a/libraries.module b/libraries.module index 503c001..762831e 100644 --- a/libraries.module +++ b/libraries.module @@ -28,32 +28,31 @@ function libraries_flush_caches() { /** * Gets the path of a library. * - * @param $name - * The machine name of a library to return the path for. - * @param $base_path - * Whether to prefix the resulting path with base_path(). + * @param string $library + * An associative array containing all information about the library. + * @param $fallback + * (optional) An external URL that, if specified, is returned if the library + * is not found locally. * - * @return + * @return string|false * The path to the specified library or FALSE if the library wasn't found. * * @ingroup libraries */ -function libraries_get_path($name, $base_path = FALSE) { - $libraries = &drupal_static(__FUNCTION__); +function libraries_get_path($library, $fallback = NULL) { + $name = $library['machine name']; - if (!isset($libraries)) { - $libraries = libraries_get_libraries(); - } + $directories = &drupal_static(__FUNCTION__); - $path = ($base_path ? base_path() : ''); - if (!isset($libraries[$name])) { - return FALSE; + if (!isset($directories)) { + $directories = libraries_get_directories(); } - else { - $path .= $libraries[$name]; + + if (isset($directories[$name])) { + return $directories[$name]; } - return $path; + return (isset($fallback) ? $fallback : FALSE); } /** @@ -72,7 +71,7 @@ function libraries_get_path($name, $base_path = FALSE) { * * @ingroup libraries */ -function libraries_get_libraries() { +function libraries_get_directories() { $searchdir = array(); $profile = drupal_get_path('profile', drupal_get_profile()); $config = conf_path(); @@ -440,8 +439,9 @@ function libraries_info_defaults(&$library, $name) { 'name' => $name, 'vendor url' => '', 'download url' => '', - 'path' => '', - 'library path' => NULL, + 'path callback' => 'libraries_get_path', + 'path arguments' => array(), + 'directory' => '', 'version callback' => 'libraries_get_version', 'version arguments' => array(), 'files' => array(), @@ -508,10 +508,19 @@ function libraries_detect($name) { $library['installed'] = FALSE; // Check whether the library exists. - if (!isset($library['library path'])) { - $library['library path'] = libraries_get_path($library['machine name']); + if (!isset($library['path'])) { + // We support both a single parameter, which is an associative array, and an + // indexed array of multiple parameters. + if (isset($library['path arguments'][0])) { + // Add the library name as the first argument. + $library['path'] = call_user_func_array($library['path callback'], array_merge(array($library), $library['path arguments'])); + } + else { + $library['path'] = $library['path callback']($library, $library['path arguments']); + } } - if ($library['library path'] === FALSE || !file_exists($library['library path'])) { + + if ($library['path'] == FALSE) { $library['error'] = 'not found'; $library['error message'] = t('The %library library could not be found.', array( '%library' => $library['name'], @@ -709,16 +718,16 @@ function libraries_load_files($library) { if (module_exists($provider)) { libraries_load_files(array( 'files' => $files, - 'path' => '', - 'library path' => drupal_get_path('module', $provider), + 'path' => drupal_get_path('module', $provider), + 'directory' => '', 'post-load integration files' => FALSE, )); } elseif (in_array($provider, $enabled_themes)) { libraries_load_files(array( 'files' => $files, - 'path' => '', - 'library path' => drupal_get_path('theme', $provider), + 'path' => drupal_get_path('theme', $provider), + 'directory' => '', 'post-load integration files' => FALSE, )); } @@ -726,8 +735,8 @@ function libraries_load_files($library) { } // Construct the full path to the library for later use. - $path = $library['library path']; - $path = ($library['path'] !== '' ? $path . '/' . $library['path'] : $path); + $path = $library['path']; + $path = ($library['directory'] !== '' ? $path . '/' . $library['directory'] : $path); // Count the number of loaded files for the return value. $count = 0; @@ -787,16 +796,16 @@ function libraries_load_files($library) { if (module_exists($provider)) { libraries_load_files(array( 'files' => $files, - 'path' => '', - 'library path' => drupal_get_path('module', $provider), + 'path' => drupal_get_path('module', $provider), + 'directory' => '', 'post-load integration files' => FALSE, )); } elseif (in_array($provider, $enabled_themes)) { libraries_load_files(array( 'files' => $files, - 'path' => '', - 'library path' => drupal_get_path('theme', $provider), + 'path' => drupal_get_path('theme', $provider), + 'directory' => '', 'post-load integration files' => FALSE, )); } @@ -857,7 +866,7 @@ function libraries_get_version($library, $options) { 'cols' => 200, ); - $file = DRUPAL_ROOT . '/' . $library['library path'] . '/' . $options['file']; + $file = DRUPAL_ROOT . '/' . $library['path'] . '/' . $options['file']; if (empty($options['file']) || !file_exists($file)) { return; } diff --git a/tests/libraries.test b/tests/libraries.test index 4192631..d615274 100644 --- a/tests/libraries.test +++ b/tests/libraries.test @@ -31,7 +31,11 @@ class LibrariesUnitTestCase extends DrupalUnitTestCase { // path' up-front. This is only used for testing purposed and is strongly // discouraged as it defeats the purpose of Libraries API in the first // place. - $this->assertEqual(libraries_get_path('example'), FALSE, 'libraries_get_path() returns FALSE for a missing library.'); + $this->assertEqual( + libraries_get_path(array('machine name' => 'example')), + FALSE, + 'libraries_get_path() returns FALSE for a missing library.' + ); } /** @@ -190,7 +194,7 @@ class LibrariesTestCase extends DrupalWebTestCase { // Test that library information is found correctly. $expected = array( 'name' => 'Example files', - 'library path' => drupal_get_path('module', 'libraries') . '/tests/libraries/example', + 'path' => drupal_get_path('module', 'libraries') . '/tests/libraries/example', 'version' => '1', 'files' => array( 'js' => array('example_1.js' => array()), @@ -337,7 +341,7 @@ class LibrariesTestCase extends DrupalWebTestCase { function testCallbacks() { $expected = array( 'name' => 'Example callback', - 'library path' => drupal_get_path('module', 'libraries') . '/tests/libraries/example', + 'path' => drupal_get_path('module', 'libraries') . '/tests/libraries/example', 'version' => '1', 'versions' => array( '1' => array( diff --git a/tests/modules/libraries_test_module/libraries_test_module.module b/tests/modules/libraries_test_module/libraries_test_module.module index 65f412e..39955bf 100644 --- a/tests/modules/libraries_test_module/libraries_test_module.module +++ b/tests/modules/libraries_test_module/libraries_test_module.module @@ -18,17 +18,18 @@ function libraries_test_module_libraries_info() { // Test library detection. $libraries['example_missing'] = array( 'name' => 'Example missing', - 'library path' => drupal_get_path('module', 'libraries') . '/tests/libraries/missing', + // Not specifying a path invokes libraries_get_path(), which will return + // FALSE. ); $libraries['example_undetected_version'] = array( 'name' => 'Example undetected version', - 'library path' => drupal_get_path('module', 'libraries') . '/tests/libraries/example', + 'path' => drupal_get_path('module', 'libraries') . '/tests/libraries/example', 'version callback' => '_libraries_test_module_return_version', 'version arguments' => array(FALSE), ); $libraries['example_unsupported_version'] = array( 'name' => 'Example unsupported version', - 'library path' => drupal_get_path('module', 'libraries') . '/tests/libraries/example', + 'path' => drupal_get_path('module', 'libraries') . '/tests/libraries/example', 'version callback' => '_libraries_test_module_return_version', 'version arguments' => array('1'), 'versions' => array( @@ -37,7 +38,7 @@ function libraries_test_module_libraries_info() { ); $libraries['example_supported_version'] = array( 'name' => 'Example supported version', - 'library path' => drupal_get_path('module', 'libraries') . '/tests/libraries/example', + 'path' => drupal_get_path('module', 'libraries') . '/tests/libraries/example', 'version callback' => '_libraries_test_module_return_version', 'version arguments' => array('1'), 'versions' => array( @@ -48,7 +49,7 @@ function libraries_test_module_libraries_info() { // Test the default version callback. $libraries['example_default_version_callback'] = array( 'name' => 'Example default version callback', - 'library path' => drupal_get_path('module', 'libraries') . '/tests/libraries/example', + 'path' => drupal_get_path('module', 'libraries') . '/tests/libraries/example', 'version arguments' => array( 'file' => 'README.txt', // Version 1 @@ -60,7 +61,7 @@ function libraries_test_module_libraries_info() { // Test a multiple-parameter version callback. $libraries['example_multiple_parameter_version_callback'] = array( 'name' => 'Example multiple parameter version callback', - 'library path' => drupal_get_path('module', 'libraries') . '/tests/libraries/example', + 'path' => drupal_get_path('module', 'libraries') . '/tests/libraries/example', // Version 1 'version callback' => '_libraries_test_module_get_version', 'version arguments' => array('README.txt', '/Version (\d+)/', 5), @@ -69,7 +70,7 @@ function libraries_test_module_libraries_info() { // Test a top-level files property. $libraries['example_files'] = array( 'name' => 'Example files', - 'library path' => drupal_get_path('module', 'libraries') . '/tests/libraries/example', + 'path' => drupal_get_path('module', 'libraries') . '/tests/libraries/example', 'version' => '1', 'files' => array( 'js' => array('example_1.js'), @@ -83,7 +84,7 @@ function libraries_test_module_libraries_info() { // these files should be automatically loaded when the library is loaded. $libraries['example_module_integration_files'] = array( 'name' => 'Example module integration files', - 'library path' => drupal_get_path('module', 'libraries') . '/tests/libraries/example', + 'path' => drupal_get_path('module', 'libraries') . '/tests/libraries/example', 'version' => '1', 'integration files' => array( 'libraries_test_module' => array( @@ -99,7 +100,7 @@ function libraries_test_module_libraries_info() { // example_1.php in libraries_test_module_post_load.inc. $libraries['example_module_integration_files_post_load'] = array( 'name' => 'Example module post-load integration files', - 'library path' => drupal_get_path('module', 'libraries') . '/tests/libraries/example', + 'path' => drupal_get_path('module', 'libraries') . '/tests/libraries/example', 'version' => '1', 'files' => array( 'php' => array('example_1.php'), @@ -115,7 +116,7 @@ function libraries_test_module_libraries_info() { // Test version overloading. $libraries['example_versions'] = array( 'name' => 'Example versions', - 'library path' => drupal_get_path('module', 'libraries') . '/tests/libraries/example', + 'path' => drupal_get_path('module', 'libraries') . '/tests/libraries/example', 'version' => '2', 'versions' => array( '1' => array( @@ -138,7 +139,7 @@ function libraries_test_module_libraries_info() { // Test variant detection. $libraries['example_variant_missing'] = array( 'name' => 'Example variant missing', - 'library path' => drupal_get_path('module', 'libraries') . '/tests/libraries/example', + 'path' => drupal_get_path('module', 'libraries') . '/tests/libraries/example', 'version' => '1', 'variants' => array( 'example_variant' => array( @@ -155,7 +156,7 @@ function libraries_test_module_libraries_info() { $libraries['example_variant'] = array( 'name' => 'Example variant', - 'library path' => drupal_get_path('module', 'libraries') . '/tests/libraries/example', + 'path' => drupal_get_path('module', 'libraries') . '/tests/libraries/example', 'version' => '1', 'variants' => array( 'example_variant' => array( @@ -173,7 +174,7 @@ function libraries_test_module_libraries_info() { // Test correct behaviour with multiple versions and multiple variants. $libraries['example_versions_and_variants'] = array( 'name' => 'Example versions and variants', - 'library path' => drupal_get_path('module', 'libraries') . '/tests/libraries/example', + 'path' => drupal_get_path('module', 'libraries') . '/tests/libraries/example', 'version' => '2', 'versions' => array( '1' => array( @@ -229,27 +230,27 @@ function libraries_test_module_libraries_info() { // This library acts as a dependency for the libraries below. $libraries['example_dependency'] = array( 'name' => 'Example dependency', - 'library path' => drupal_get_path('module', 'libraries') . '/tests/libraries/example', + 'path' => drupal_get_path('module', 'libraries') . '/tests/libraries/example', 'version' => '1.1', 'files' => array('js' => array('example_1.js')), ); $libraries['example_dependency_missing'] = array( 'name' => 'Example dependency missing', - 'library path' => drupal_get_path('module', 'libraries') . '/tests/libraries/example', + 'path' => drupal_get_path('module', 'libraries') . '/tests/libraries/example', 'version' => '1', 'dependencies' => array('example_missing'), 'files' => array('js' => array('example_1.js')), ); $libraries['example_dependency_incompatible'] = array( 'name' => 'Example dependency incompatible', - 'library path' => drupal_get_path('module', 'libraries') . '/tests/libraries/example', + 'path' => drupal_get_path('module', 'libraries') . '/tests/libraries/example', 'version' => '1', 'dependencies' => array('example_dependency (>1.1)'), 'files' => array('js' => array('example_1.js')), ); $libraries['example_dependency_compatible'] = array( 'name' => 'Example dependency compatible', - 'library path' => drupal_get_path('module', 'libraries') . '/tests/libraries/example', + 'path' => drupal_get_path('module', 'libraries') . '/tests/libraries/example', 'version' => '1', 'dependencies' => array('example_dependency (>=1.1)'), 'files' => array('js' => array('example_1.js')), @@ -258,7 +259,7 @@ function libraries_test_module_libraries_info() { // Test the applying of callbacks. $libraries['example_callback'] = array( 'name' => 'Example callback', - 'library path' => drupal_get_path('module', 'libraries') . '/tests/libraries/example', + 'path' => drupal_get_path('module', 'libraries') . '/tests/libraries/example', 'version' => '1', 'versions' => array( '1' => array( @@ -312,7 +313,7 @@ function libraries_test_module_libraries_info() { $libraries['example_path_variable_override'] = array( 'name' => 'Example path variable override', - 'library path' => drupal_get_path('module', 'libraries') . '/tests/libraries/example', + 'path' => drupal_get_path('module', 'libraries') . '/tests/libraries/example', 'version' => '1', 'files' => array( 'php' => array('example_1.php', 'example_2.php'), @@ -381,7 +382,7 @@ function _libraries_test_module_return_version($library, $version) { */ function _libraries_test_module_get_version($library, $file, $pattern, $lines = 20, $cols = 200) { - $file = DRUPAL_ROOT . '/' . $library['library path'] . '/' . $file; + $file = DRUPAL_ROOT . '/' . $library['path'] . '/' . $file; if (!file_exists($file)) { return; } diff --git a/tests/themes/libraries_test_theme/template.php b/tests/themes/libraries_test_theme/template.php index cbb53ac..4bd45b8 100644 --- a/tests/themes/libraries_test_theme/template.php +++ b/tests/themes/libraries_test_theme/template.php @@ -15,7 +15,7 @@ function libraries_test_theme_libraries_info() { ); $libraries['example_theme_integration_files'] = array( 'name' => 'Example theme integration file', - 'library path' => drupal_get_path('module', 'libraries') . '/tests/libraries/example', + 'path' => drupal_get_path('module', 'libraries') . '/tests/libraries/example', 'version' => '1', 'integration files' => array( 'libraries_test_theme' => array(