diff --git a/libraries.api.php b/libraries.api.php index f84e6af146c5952f63e1816901923515e5e16b88..155874eebec738c27233c1619644c978dd972a6e 100644 --- a/libraries.api.php +++ b/libraries.api.php @@ -22,20 +22,26 @@ * the actual library files in a sub-directory. * - version callback: (optional) The name of a function that detects and * returns the full version string of the library. Defaults to - * libraries_get_version(). + * libraries_get_version(). The first argument is always $library, an array + * containing all library information as described here. The following + * argument(s) can either be: + * - options: An associative array of additional information to pass to the + * version callback. In this case the version arguments (see below) must + * be declared as an associative array. + * - or any number of independent arguments. In this case the version + * arguments (see below) must be declared as an indexed array. * - version arguments: A list of arguments to pass to the version callback. * The default version callback libraries_get_version() expects a single, - * single, associative array with named keys: + * associative array with named keys: * - file: The filename to parse for the version, relative to the library * path. For example: 'docs/changelog.txt'. * - pattern: A string containing a regular expression (PCRE) to match the * library version. For example: '/@version (\d+)\.(\d+)/'. - * - lines: The maximum number of lines to search the pattern in. For - * example: 20. + * - lines: (optional) The maximum number of lines to search the pattern in. + * Defaults to 20. * - cols: (optional) The maximum number of characters per line to take into - * account. For example: 40. Defaults to unlimited. To be used if the file - * containing the library version is minified/compressed, i.e. reading a - * single line would read the entire library into memory. + * account. Defaults to 200. In case of minified or compressed files, this + * prevents reading the entire file into memory. * - files: An associative array of library files to load. Supported keys are: * - js: A list of JavaScript files to load, using the same syntax as Drupal * core's hook_library(). @@ -48,7 +54,22 @@ * uncompressed/source variant, those can be defined here. Each key should * describe the variant type, e.g. 'minified' or 'source'. Each value is an * associative array of top-level properties that are entirely overridden by - * the variant, most often just 'files'. Variants can be version specific. + * the variant, most often just 'files'. Additionally, each variant can + * contain following properties: + * - variant callback: (optional) The name of a function that detects + * returns TRUE or FALSE, depending on whether the variant is available or + * not. The first argument is always $library, an array containing all + * library information as described here. The seconds argument is always + * $name, a string containing the name of the variant. The following + * argument(s) can either be: + * - options: An associative array of additional information to pass to + * the version callback. In this case the version arguments (see below) + * must be declared as an associative array. + * - or any number of independent arguments. In this case the version + * arguments (see below) must be declared as an indexed array. + * If ommitted, the variant is expected to always be available. Variants + * can be version specific. + * - variant arguments: A list of arguments to pass to the variant callback. * - versions: (optional) An associative array of supported library versions. * Naturally, external libraries evolve over time and so do their APIs. In * case a library changes between versions, different 'files' may need to be @@ -62,6 +83,8 @@ * the same notion as the top-level 'files' property. Each specified file * should contain the full path to the file. * Additional top-level properties can be registered as needed. + * + * @see hook_library() */ function hook_libraries_info() { // The following is a full explanation of all properties. See below for more @@ -122,6 +145,10 @@ function hook_libraries_info() { 'skin/example.css', ), ), + 'variant callback' => 'mymodule_check_variant', + 'variant arguments' => array( + 'variant' => 'minified', + ), ), ), // Optional, but usually required: Override top-level properties for later @@ -203,9 +230,9 @@ function hook_libraries_info() { 'download url' => 'http://tinymce.moxiecode.com/download.php', 'path' => 'jscripts/tiny_mce', 'version arguments' => array( - // It can be easier to parse the first chars of a minified file instead of - // doing a multi-line pattern matching in a source file. See 'lines' and - // 'cols' below. + // It can be easier to parse the first characters of a minified file + // instead of doing a multi-line pattern matching in a source file. See + // 'lines' and 'cols' below. 'file' => 'jscripts/tiny_mce/tiny_mce.js', // Best practice: Document the actual version strings for later reference. // 2.x: this.majorVersion="2";this.minorVersion="1.3" @@ -278,3 +305,20 @@ function hook_libraries_info() { ); return $libraries; } + +/** + * Alter the library information before detection and caching takes place. + * + * The library definitions are passed by reference. A common use-case is adding + * a module's integration files to the library array, so that the files are + * loaded whenever the library is. As noted above, it is important to declare + * integration files inside of an array, whose key is the module name. + * + * @see hook_libraries_info() + */ +function hook_libraries_info_alter(&$libraries) { + $files = array( + 'php' => array('example_module.php_spellchecker.inc'), + ); + $libraries['php_spellchecker']['integration files']['example_module'] = $files; +} diff --git a/libraries.module b/libraries.module index 1045b4a97a6d1138a8f9dd795eaa650d45a2cc59..5720373ca2105bed49d8539333870092d5ca89e3 100644 --- a/libraries.module +++ b/libraries.module @@ -210,18 +210,22 @@ function libraries_detect_library(&$library) { } if (!file_exists($library['library path'])) { $library['error'] = t('%library could not be found.', array('%library' => $library['title'])); - continue; + return; } // Detect library version. - // Special handling for named arguments (single array). - if (!isset($library['version arguments'][0])) { - $library['version arguments'] = array($library['version arguments']); + // We support both a single parameter, which is an associative array, and an + // indexed array of multiple parameters. + if (isset($library['version arguments'][0])) { + // Add the library as the first argument. + $library['version'] = call_user_func_array($library['version callback'], array_merge(array($library), $library['version arguments'])); + } + else { + $library['version'] = $library['version callback']($library, $library['version arguments']); } - $library['version'] = call_user_func_array($library['version callback'], $library['version arguments']); if (empty($library['version'])) { $library['error'] = t('The version of %library could not be detected.', array('%library' => $library['title'])); - continue; + return; } // Determine to which supported version the installed version maps. @@ -229,13 +233,16 @@ function libraries_detect_library(&$library) { ksort($library['versions']); $version = 0; foreach ($library['versions'] as $supported_version => $version_properties) { - if (version_compare($library['installed version'], $supported_version, '>=')) { + if (version_compare($library['version'], $supported_version, '>=')) { $version = $supported_version; } } if (!$version) { - $library['error'] = t('The installed version %version of %library is not supported.', array('%version' => $library['installed version'], '%library' => $library['title'])); - continue; + $library['error'] = t('The installed version %version of %library is not supported.', array( + '%version' => $library['version'], + '%library' => $library['title'], + )); + return; } // Apply version specific definitions and overrides. @@ -243,10 +250,161 @@ function libraries_detect_library(&$library) { unset($library['versions']); } + // Check each variant if it is installed. + if (!empty($library['variants'])) { + foreach ($library['variants'] as $name => &$variant) { + // If no variant callback has been set, assume the variant to be + // installed. + $variant['installed'] = TRUE; + if (!empty($variant['variant callback'])) { + // We support both a single parameter, which is an associative array, + // and an indexed array of multiple parameters. + if (isset($variant['variant arguments'][0])) { + // Add the library as the first argument, and the variant name as the second. + $variant['installed'] = call_user_func_array($variant['variant callback'], array_merge(array($library, $name), $variant['variant arguments'])); + } + else { + $variant['installed'] = $variant['variant callback']($library, $name, $variant['variant arguments']); + } + if (empty($variant['installed'])) { + $variant['error'] = t('The %variant variant of %library could not be found.', array( + '%variant' => $name, + '%library' => $library['title'], + )); + } + } + } + } + // If we end up here, the library should be usable. $library['installed'] = TRUE; - if (!empty($library['path'])) { - $library['library path'] .= '/' . $library['path']; +} + +/** + * Loads a library. + * + * @param $library + * The name of the library to load. + * @param $variant + * The name of the variant to load. + */ +function libraries_load($library, $variant = NULL) { + $library = libraries_info($library); + libraries_detect_library($library); + libraries_load_files($library, $variant); +} + +/** + * Loads a library's files. + * + * @param $library + * The name of the library to load. + * @param $variant + * The name of the variant to load. + */ +function libraries_load_files($library, $variant = NULL) { + // Construct the full path to the library for later use. + $path = !empty($library['path']) ? $library['library path'] . '/' . $library['path'] : $library['library path']; + + // If a variant was specified, override the top-level properties with the + // variant properties. + if (!empty($variant) && !empty($library['variants'][$variant]['installed'])) { + $library = array_merge($library, $library['variants'][$variant]); + } + + // Load integration files. + if (!empty($library['integration files'])) { + foreach ($library['integration files'] as $module => $files) { + libraries_load_files(array( + 'files' => $files, + 'library path' => drupal_get_path('module', $module), + )); + } + } + + // Load both the JavaScript and the CSS files. + // The parameters for drupal_add_js() and drupal_add_css() require special + // handling. + // @see drupal_process_attached() + foreach (array('js', 'css') as $type) { + if (!empty($library['files'][$type])) { + foreach ($library['files'][$type] as $data => $options) { + // If the value is not an array, it's a filename and passed as first + // (and only) argument. + if (!is_array($options)) { + // Prepend the library path to the file name. + $data = "$path/$options"; + $options = NULL; + } + // In some cases, the first parameter ($data) is an array. Arrays can't be + // passed as keys in PHP, so we have to get $data from the value array. + if (is_numeric($data)) { + $data = $options['data']; + unset($options['data']); + } + // Apply the default weight if the weight isn't explicitly given. + if (!isset($options['weight'])) { + $options['weight'] = ($type == 'js') ? JS_DEFAULT : CSS_DEFAULT; + } + call_user_func('drupal_add_' . $type, $data, $options); + } + } + } + + // Load PHP files. + if (!empty($library['files']['php'])) { + foreach ($library['files']['php'] as $file) { + $file_path = DRUPAL_ROOT . '/' . $path . '/' . $file; + if (file_exists($file_path)) { + require_once $file_path; + } + } + } +} + +/** + * Gets the version information from an arbitrary library. + * + * @param $library + * An associative array containing all information about the library. + * @param $options + * An associative array containing with the following keys: + * - file: The filename to parse for the version, relative to the library + * path. For example: 'docs/changelog.txt'. + * - pattern: A string containing a regular expression (PCRE) to match the + * library version. For example: '/@version (\d+)\.(\d+)/'. + * - lines: (optional) The maximum number of lines to search the pattern in. + * Defaults to 20. + * - cols: (optional) The maximum number of characters per line to take into + * account. Defaults to 200. In case of minified or compressed files, this + * prevents reading the entire file into memory. + * + * @return + * A string containing the version of the library. + * + * @see libraries_get_path() + */ +function libraries_get_version($library, $options) { + // Provide defaults. + $options += array( + 'file' => '', + 'pattern' => '', + 'lines' => 20, + 'cols' => 200, + ); + + $file = DRUPAL_ROOT . '/' . $library['library path'] . '/' . $options['file']; + if (empty($options['file']) || !file_exists($file)) { + return; + } + $file = fopen($file, 'r'); + while ($options['lines'] && $line = fgets($file, $options['cols'])) { + if (preg_match($options['pattern'], $line, $version)) { + fclose($file); + return $version[1]; + } + $options['lines']--; } + fclose($file); } diff --git a/tests/example/example_installed.txt b/tests/example/example_installed.txt index cca2a47c353e382accba6e632312bb54394c1e3b..6324b312aeb53fa7ce168c6fc19d0601fc2ed5bc 100644 --- a/tests/example/example_installed.txt +++ b/tests/example/example_installed.txt @@ -4,3 +4,4 @@ Example library Version 2 +This file is an example file to test version detection. diff --git a/tests/example/example_installed_1.css b/tests/example/example_installed_1.css new file mode 100644 index 0000000000000000000000000000000000000000..358a8cd1ddfdd23fb6ce9a55f2a984a284a0c905 --- /dev/null +++ b/tests/example/example_installed_1.css @@ -0,0 +1,15 @@ +/* $Id$ */ + +/** + * @file + * Test CSS file for Libraries loading. + * + * Because we cannot test CSS programatically with SimpleTest, the CSS below can + * be useful for debugging with SimpleTest's verbose mode. Note that since the + * DOM cannot be manipulated via CSS, JavaScript loading needs to be functional + * for this to have any visible effect. + */ + +div#libraries-test { + color: red; +} diff --git a/tests/example/example_installed_1.js b/tests/example/example_installed_1.js new file mode 100644 index 0000000000000000000000000000000000000000..af23ac2f1cd64070f1fd3672ce171a032e10fb39 --- /dev/null +++ b/tests/example/example_installed_1.js @@ -0,0 +1,19 @@ +// $Id$ + +/** + * @file + * Test JavaScript file for Libraries loading. + * + * Because we cannot test JavaScript programatically with SimpleTest, the + * JavaScript below can be useful for debugging with SimpleTest's verbose mode. + */ + +(function ($) { + +Drupal.behaviors.librariesTest = { + attach: function(context, settings) { + $('h1#page-title').after('<div id="libraries-test">If this text shows up, the JavaScript file was loaded successfully. If this text is red, the CSS file was loaded successfully.</div>') + } +}; + +})(jQuery); diff --git a/tests/example/example_installed_1.php b/tests/example/example_installed_1.php new file mode 100644 index 0000000000000000000000000000000000000000..90446046801f9260ffa272ec7187a54506a3ad3d --- /dev/null +++ b/tests/example/example_installed_1.php @@ -0,0 +1,13 @@ +<?php +// $Id$ + +/** + * @file + * Test PHP file for Libraries loading. + */ + +/** + * Dummy function to see if this file was loaded. + */ +function _libraries_example_installed_1() { +} diff --git a/tests/example/example_installed_2.css b/tests/example/example_installed_2.css index a0749a0c2c0c92aeb7c5f5fa6ace589ef08aceeb..6a8ed7e8ce8f7f694789a8af90562f1268147ef9 100644 --- a/tests/example/example_installed_2.css +++ b/tests/example/example_installed_2.css @@ -1 +1,15 @@ /* $Id$ */ + +/** + * @file + * Test CSS file for Libraries loading. + * + * Because we cannot test CSS programatically with SimpleTest, the CSS below can + * be useful for debugging with SimpleTest's verbose mode. Note that since the + * DOM cannot be manipulated via CSS, JavaScript loading needs to be functional + * for this to have any visible effect. + */ + +div#libraries-test { + color: green; +} diff --git a/tests/example/example_installed_2.js b/tests/example/example_installed_2.js index cfa1da318d37e3d2f592a3c2a3504b8fcda1834e..f9926b24f6f26ca0702c8da177ddc7ebac3cc771 100644 --- a/tests/example/example_installed_2.js +++ b/tests/example/example_installed_2.js @@ -1 +1,19 @@ // $Id$ + +/** + * @file + * Test JavaScript file for Libraries loading. + * + * Because we cannot test JavaScript programatically with SimpleTest, the + * JavaScript below can be useful for debugging with SimpleTest's verbose mode. + */ + +(function ($) { + +Drupal.behaviors.librariesTest = { + attach: function(context, settings) { + $('h1#page-title').after('<div id="libraries-test">If this text shows up, the JavaScript file was loaded successfully. If this text is green, the CSS file was loaded successfully.</div>') + } +}; + +})(jQuery); diff --git a/tests/example/example_installed_2.php b/tests/example/example_installed_2.php index 34950aba0c14d2d128b4f3f69bab323f70db411f..3976fa9b58c499e4676a68b8bebfd2e20cdbeaf2 100644 --- a/tests/example/example_installed_2.php +++ b/tests/example/example_installed_2.php @@ -1,6 +1,11 @@ <?php // $Id$ +/** + * @file + * Test PHP file for Libraries loading. + */ + /** * Dummy function to see if this file was loaded. */ diff --git a/tests/example/example_installed_variant.css b/tests/example/example_installed_variant.css deleted file mode 100644 index a0749a0c2c0c92aeb7c5f5fa6ace589ef08aceeb..0000000000000000000000000000000000000000 --- a/tests/example/example_installed_variant.css +++ /dev/null @@ -1 +0,0 @@ -/* $Id$ */ diff --git a/tests/example/example_installed_variant.js b/tests/example/example_installed_variant.js deleted file mode 100644 index cfa1da318d37e3d2f592a3c2a3504b8fcda1834e..0000000000000000000000000000000000000000 --- a/tests/example/example_installed_variant.js +++ /dev/null @@ -1 +0,0 @@ -// $Id$ diff --git a/tests/example/example_installed_variant.php b/tests/example/example_installed_variant.php deleted file mode 100644 index 2b3fed775dce0d10e2b8bbfee27c54b5bfbe75a3..0000000000000000000000000000000000000000 --- a/tests/example/example_installed_variant.php +++ /dev/null @@ -1,8 +0,0 @@ -<?php -// $Id$ - -/** - * Dummy function to see if this file was loaded. - */ -function _libraries_example_installed_variant() { -} diff --git a/tests/example/example_installed_variant_1.css b/tests/example/example_installed_variant_1.css new file mode 100644 index 0000000000000000000000000000000000000000..894fa3ab660fb8950f4b423f6b6024d8242ba9f3 --- /dev/null +++ b/tests/example/example_installed_variant_1.css @@ -0,0 +1,15 @@ +/* $Id$ */ + +/** + * @file + * Test CSS file for Libraries loading. + * + * Because we cannot test CSS programatically with SimpleTest, the CSS below can + * be useful for debugging with SimpleTest's verbose mode. Note that since the + * DOM cannot be manipulated via CSS, JavaScript loading needs to be functional + * for this to have any visible effect. + */ + +div#libraries-test { + color: orange; +} diff --git a/tests/example/example_installed_variant_1.js b/tests/example/example_installed_variant_1.js new file mode 100644 index 0000000000000000000000000000000000000000..835d9f7d4f5d3adc469d9ba82eb2748794c9fbfa --- /dev/null +++ b/tests/example/example_installed_variant_1.js @@ -0,0 +1,19 @@ +// $Id$ + +/** + * @file + * Test JavaScript file for Libraries loading. + * + * Because we cannot test JavaScript programatically with SimpleTest, the + * JavaScript below can be useful for debugging with SimpleTest's verbose mode. + */ + +(function ($) { + +Drupal.behaviors.librariesTest = { + attach: function(context, settings) { + $('h1#page-title').after('<div id="libraries-test">If this text shows up, the JavaScript file was loaded successfully. If this text is orange, the CSS file was loaded successfully.</div>') + } +}; + +})(jQuery); diff --git a/tests/example/example_installed_variant_1.php b/tests/example/example_installed_variant_1.php new file mode 100644 index 0000000000000000000000000000000000000000..f68ed401e77cad2ad92d1b652412c6f371cae67f --- /dev/null +++ b/tests/example/example_installed_variant_1.php @@ -0,0 +1,13 @@ +<?php +// $Id$ + +/** + * @file + * Test PHP file for Libraries loading. + */ + +/** + * Dummy function to see if this file was loaded. + */ +function _libraries_example_installed_variant_1() { +} diff --git a/tests/example/example_installed_variant_2.css b/tests/example/example_installed_variant_2.css new file mode 100644 index 0000000000000000000000000000000000000000..4a8a9a1d38d97cad0c6ac66ff755e265bea605fd --- /dev/null +++ b/tests/example/example_installed_variant_2.css @@ -0,0 +1,15 @@ +/* $Id$ */ + +/** + * @file + * Test CSS file for Libraries loading. + * + * Because we cannot test CSS programatically with SimpleTest, the CSS below can + * be useful for debugging with SimpleTest's verbose mode. Note that since the + * DOM cannot be manipulated via CSS, JavaScript loading needs to be functional + * for this to have any visible effect. + */ + +div#libraries-test { + color: blue; +} diff --git a/tests/example/example_installed_variant_2.js b/tests/example/example_installed_variant_2.js new file mode 100644 index 0000000000000000000000000000000000000000..bae6104c26cb47b164b98ead7485e8e2139a3469 --- /dev/null +++ b/tests/example/example_installed_variant_2.js @@ -0,0 +1,19 @@ +// $Id$ + +/** + * @file + * Test JavaScript file for Libraries loading. + * + * Because we cannot test JavaScript programatically with SimpleTest, the + * JavaScript below can be useful for debugging with SimpleTest's verbose mode. + */ + +(function ($) { + +Drupal.behaviors.librariesTest = { + attach: function(context, settings) { + $('h1#page-title').after('<div id="libraries-test">If this text shows up, the JavaScript file was loaded successfully. If this text is blue, the CSS file was loaded successfully.</div>') + } +}; + +})(jQuery); diff --git a/tests/example/example_installed_variant_2.php b/tests/example/example_installed_variant_2.php new file mode 100644 index 0000000000000000000000000000000000000000..5d0960aeea5e0770d8fb40ea451bd987350227b6 --- /dev/null +++ b/tests/example/example_installed_variant_2.php @@ -0,0 +1,13 @@ +<?php +// $Id$ + +/** + * @file + * Test PHP file for Libraries loading. + */ + +/** + * Dummy function to see if this file was loaded. + */ +function _libraries_example_installed_variant_2() { +} diff --git a/tests/libraries.test b/tests/libraries.test index a6fb6421bb6913630a8fba493e3e7bd0fa0ad9ba..9381959e993cc4c967c1f17bdc379c248fb9e639 100644 --- a/tests/libraries.test +++ b/tests/libraries.test @@ -51,41 +51,103 @@ class LibrariesTestCase extends DrupalWebTestCase { )); $this->assertEqual($library['error'], $error, 'Unsupported library version found.'); + // Test supported library version. + $library = libraries_info('example_supported_version'); + libraries_detect_library($library); + $this->assertEqual($library['installed'], TRUE, 'Supported library version found.'); + // Test libraries_get_version(). - $library = libraries_info('example_installed'); + $library = libraries_info('example_default_version_callback'); libraries_detect_library($library); $version = '2'; $this->assertEqual($library['version'], $version, 'Expected version returned by default version callback.'); + // Test a multiple-parameter version callback. + $library = libraries_info('example_multiple_parameter_version_callback'); + libraries_detect_library($library); + $version = '2'; + $this->assertEqual($library['version'], $version, 'Expected version returned by multiple parameter version callback.'); + + // Test a top-level files property. + $library = libraries_info('example_simple'); + libraries_detect_library($library); + $files = array( + 'js' => array('example_installed_1.js'), + 'css' => array('example_installed_1.css'), + 'php' => array('example_installed_1.php'), + ); + $this->assertEqual($library['files'], $files, 'Top-level files property works.'); + // Test version-specific library files. + $library = libraries_info('example_versions'); + libraries_detect_library($library); $files = array( 'js' => array('example_installed_2.js'), 'css' => array('example_installed_2.css'), 'php' => array('example_installed_2.php'), ); $this->assertEqual($library['files'], $files, 'Version-specific library files found.'); - // @todo Ensure that default files are not contained. - // Test library loading. - $this->drupalGet('libraries_test'); + // Test missing variant. + $library = libraries_info('example_variant_missing'); + libraries_detect_library($library); + $variants = array_keys($library['variants']); + $error = t('The %variant variant of %library could not be found.', array( + '%variant' => $variants[0], + '%library' => $library['title'], + )); + $this->assertEqual($library['variants']['example_variant_1']['error'], $error, 'Missing variant not found.'); + + // Test existing variant. + $library = libraries_info('example_variant'); + libraries_detect_library($library); + $this->assertEqual($library['variants']['example_variant_1']['installed'], TRUE, 'Existing variant found.'); + + // Test loading of a simple library with a top-level files property. + $this->drupalGet('libraries_test/simple'); + $this->assertRaw('example_installed_1.js', 'A JavaScript file is loaded correctly.'); + $this->assertRaw('example_installed_1.css', 'A CSS file is loaded correctly.'); + $this->assertText('example_installed_1.php', 'A PHP file is loaded correctly.'); - $this->assertRaw('example_installed_2.js', 'The JavaScript file is loaded.'); - $this->assertRaw('example_installed_2.css', 'The CSS file is loaded.'); - $this->assertText('example_installed_2.php', 'The PHP file is loaded.'); + // Test loading of integration files. + $this->drupalGet('libraries_test/integration_files'); $this->assertRaw('libraries_test.js', 'The JavaScript integration file is loaded.'); $this->assertRaw('libraries_test.css', 'The CSS integration file is loaded.'); $this->assertText('libraries_test.inc', 'The PHP integration file is loaded.'); + // Test version overloading. + $this->drupalGet('libraries_test/versions'); + $this->assertNoRaw('example_installed_1.js', 'The JavaScript file of the wrong library version is not loaded.'); + $this->assertNoRaw('example_installed_1.css', 'The CSS file of the wrong library version is not loaded.'); + $this->assertNoText('example_installed_1.php', 'The PHP file of the wrong library version is not loaded.'); + $this->assertRaw('example_installed_2.js', 'The JavaScript file of the correct library version is loaded.'); + $this->assertRaw('example_installed_2.css', 'The CSS file of the correct library version is loaded.'); + $this->assertText('example_installed_2.php', 'The PHP file of the correct library version is loaded.'); + // Test variant loading. - // @todo Move onto libraries_test/variant (maintain namespace). - $this->drupalGet('libraries_test_variant'); + $this->drupalGet('libraries_test/variant'); + $this->assertNoRaw('example_installed_variant_2.js', 'The JavaScript file of the wrong library variant is not loaded.'); + $this->assertNoRaw('example_installed_variant_2.css', 'The CSS file of the wrong library variant is not loaded.'); + $this->assertNoText('example_installed_variant_2.php', 'The PHP file of the wrong library variant is not loaded.'); + $this->assertRaw('example_installed_variant_1.js', 'The JavaScript file of the correct library variant is loaded.'); + $this->assertRaw('example_installed_variant_1.css', 'The CSS file of the correct library variant is loaded.'); + $this->assertText('example_installed_variant_1.php', 'The PHP file of the correct library variant is loaded.'); + + // Test version overloading and variant loading. + $this->drupalGet('libraries_test/versions_and_variants'); + $this->assertNoRaw('example_installed_1.js', 'The JavaScript file of the wrong library version and variant is not loaded.'); + $this->assertNoRaw('example_installed_1.css', 'The CSS file of the wrong library version and variant is not loaded.'); + $this->assertNoText('example_installed_1.php', 'The PHP file of the wrong library version and variant is not loaded.'); + $this->assertNoRaw('example_installed_2.js', 'The JavaScript file of the wrong library version and variant is not loaded.'); + $this->assertNoRaw('example_installed_2.css', 'The CSS file of the wrong library version and variant is not loaded.'); + $this->assertNoText('example_installed_2.php', 'The PHP file of the wrong library version and variant is not loaded.'); + $this->assertNoRaw('example_installed_variant_1.js', 'The JavaScript file of the wrong library version and variant is not loaded.'); + $this->assertNoRaw('example_installed_variant_1.css', 'The CSS file of the wrong library version and variant is not loaded.'); + $this->assertNoText('example_installed_variant_1.php', 'The PHP file of the wrong library version and variant is not loaded.'); + $this->assertRaw('example_installed_variant_2.js', 'The JavaScript file of the correct library version and variant is loaded.'); + $this->assertRaw('example_installed_variant_2.css', 'The CSS file of the correct library version and variant is loaded.'); + $this->assertText('example_installed_variant_2.php', 'The PHP file of the correct library version and variant is loaded.'); - $this->assertRaw('example_installed_variant.js', 'The JavaScript file is loaded.'); - $this->assertRaw('example_installed_variant.css', 'The CSS file is loaded.'); - $this->assertText('example_installed_variant.php', 'The PHP file is loaded.'); - $this->assertRaw('libraries_test.js', 'The JavaScript integration file is loaded.'); - $this->assertRaw('libraries_test.css', 'The CSS integration file is loaded.'); - $this->assertText('libraries_test.inc', 'The PHP integration file is loaded.'); } } diff --git a/tests/libraries_test.css b/tests/libraries_test.css index a0749a0c2c0c92aeb7c5f5fa6ace589ef08aceeb..f394ca6f9debd10d7b3e125394975e1c2c8ae0d3 100644 --- a/tests/libraries_test.css +++ b/tests/libraries_test.css @@ -1 +1,15 @@ /* $Id$ */ + +/** + * @file + * Test CSS file for Libraries loading. + * + * Because we cannot test CSS programatically with SimpleTest, the CSS below can + * be useful for debugging with SimpleTest's verbose mode. Note that since the + * DOM cannot be manipulated via CSS, JavaScript loading needs to be functional + * for this to have any visible effect. + */ + +div#libraries-test { + color: purple; +} diff --git a/tests/libraries_test.inc b/tests/libraries_test.inc index fb21b717de740e6d28f1f6d64c76b90e41fd1200..844d4b423185d49d7f98483652aec981921afdb1 100644 --- a/tests/libraries_test.inc +++ b/tests/libraries_test.inc @@ -1,6 +1,10 @@ <?php // $Id$ +/** + * @file + * Test PHP file for Libraries loading. + /** * Dummy function to see if this file was loaded. */ diff --git a/tests/libraries_test.js b/tests/libraries_test.js index cfa1da318d37e3d2f592a3c2a3504b8fcda1834e..d16d23bd268853cc78cc13bee68f61d7b7a3f92a 100644 --- a/tests/libraries_test.js +++ b/tests/libraries_test.js @@ -1 +1,19 @@ // $Id$ + +/** + * @file + * Test JavaScript file for Libraries loading. + * + * Because we cannot test JavaScript programatically with SimpleTest, the + * JavaScript below can be useful for debugging with SimpleTest's verbose mode. + */ + +(function ($) { + +Drupal.behaviors.librariesTest = { + attach: function(context, settings) { + $('h1#page-title').after('<div id="libraries-test">If this text shows up, the JavaScript file was loaded successfully. If this text is purple, the CSS file was loaded successfully.</div>') + } +}; + +})(jQuery); diff --git a/tests/libraries_test.module b/tests/libraries_test.module index 246cdf06a3e689ee944dcbd036b7c291ece000ee..054be3bb80e45fe070fbfaf2ec7922fa1dd88e61 100644 --- a/tests/libraries_test.module +++ b/tests/libraries_test.module @@ -10,38 +10,41 @@ * Implements hook_libraries_info(). * * Note: DO NOT use drupal_get_path() in your implementations! Used for testing - * purposes only. + * purposes only. It is strongly discouraged to declare the 'library path' + * property as that will be detected by Libraries API automatically. */ function libraries_test_libraries_info() { // Test library detection. $libraries['example_missing'] = array( 'library path' => drupal_get_path('module', 'libraries') . '/tests/missing', - 'version callback' => 'libraries_test_get_version', - 'version arguments' => array( - 'version' => '1', - ), + 'version callback' => '_libraries_test_return_version', + 'version arguments' => array('1'), ); $libraries['example_undetected_version'] = array( 'library path' => drupal_get_path('module', 'libraries') . '/tests', - 'version callback' => 'libraries_test_get_version', - 'version arguments' => array( - 'version' => 'undetected', - ), + 'version callback' => '_libraries_test_return_version', + 'version arguments' => array(FALSE), ); $libraries['example_unsupported_version'] = array( 'library path' => drupal_get_path('module', 'libraries') . '/tests', - 'version callback' => 'libraries_test_get_version', - 'version arguments' => array( - 'version' => '1', + 'version callback' => '_libraries_test_return_version', + 'version arguments' => array('1'), + 'versions' => array( + '2' => array(), ), + ); + + $libraries['example_supported_version'] = array( + 'library path' => drupal_get_path('module', 'libraries') . '/tests', + 'version callback' => '_libraries_test_return_version', + 'version arguments' => array('2'), 'versions' => array( '2' => array(), ), ); - // Test version- and variant-overloading, default version callback and library - // loading. - $libraries['example_installed'] = array( + // Test the default version callback. + $libraries['example_default_version_callback'] = array( 'library path' => drupal_get_path('module', 'libraries') . '/tests/example', 'version arguments' => array( 'file' => 'example_installed.txt', @@ -49,82 +52,226 @@ function libraries_test_libraries_info() { 'pattern' => '/Version (\d+)/', 'lines' => 5, ), - // @todo Version-less files, if library declares versions? - // @todo These files don't exist. On purpose? + ); + + // Test a multiple-parameter version callback. + $libraries['example_multiple_parameter_version_callback'] = array( + 'library path' => drupal_get_path('module', 'libraries') . '/tests/example', + // Version 2 + 'version callback' => '_libraries_get_version', + 'version arguments' => array('example_installed.txt', '/Version (\d+)/', 5), + ); + + // Test a top-level files property. + $libraries['example_simple'] = array( + 'library path' => drupal_get_path('module', 'libraries') . '/tests/example', + 'version callback' => '_libraries_test_return_version', + 'version arguments' => array('1'), 'files' => array( 'js' => array( - 'example_installed.js', + 'example_installed_1.js', ), 'css' => array( - 'example_installed.css', + 'example_installed_1.css', ), 'php' => array( - 'example_installed.php' + 'example_installed_1.php' ), ), ); - $libraries['example_installed']['versions'] = array( - // @todo Files for version 1 do not exist. On purpose? - '1' => array( - 'files' => array( + // Test loading of integration files. + // Normally added by the corresponding module via hook_libraries_info_alter(), + // these files should be automatically loaded when the library is loaded. + $libraries['example_integration_files'] = array( + 'library path' => drupal_get_path('module', 'libraries') . '/tests/example', + 'version callback' => '_libraries_test_return_version', + 'version arguments' => array('2'), + 'integration files' => array( + 'libraries_test' => array( 'js' => array( - 'example_installed_1.js', + 'libraries_test.js', ), 'css' => array( - 'example_installed_1.css', + 'libraries_test.css', ), 'php' => array( - 'example_installed_1.php', + 'libraries_test.inc', ), ), ), - '2' => array( - 'files' => array( - 'js' => array( - 'example_installed_2.js', - ), - 'css' => array( - 'example_installed_2.css', + ); + + // Test version overloading. + $libraries['example_versions'] = array( + 'library path' => drupal_get_path('module', 'libraries') . '/tests/example', + 'version callback' => '_libraries_test_return_version', + 'version arguments' => array('2'), + 'versions' => array( + '1' => array( + 'files' => array( + 'js' => array( + 'example_installed_1.js', + ), + 'css' => array( + 'example_installed_1.css', + ), + 'php' => array( + 'example_installed_1.php', + ), ), - 'php' => array( - 'example_installed_2.php', + ), + '2' => array( + 'files' => array( + 'js' => array( + 'example_installed_2.js', + ), + 'css' => array( + 'example_installed_2.css', + ), + 'php' => array( + 'example_installed_2.php', + ), ), ), ), ); - // @todo Variant callback missing. - $libraries['example_installed']['variants'] = array( - // @todo Is "variant" the name of a variant? - 'variant' => array( - 'files' => array( - 'js' => array( - 'example_installed_variant.js', + // Test variant detection. + $libraries['example_variant_missing'] = array( + 'library path' => drupal_get_path('module', 'libraries') . '/tests/example', + 'version callback' => '_libraries_test_return_version', + 'version arguments' => array('2'), + 'variants' => array( + 'example_variant_1' => array( + 'files' => array( + 'js' => array( + 'example_installed_variant_1.js', + ), + 'css' => array( + 'example_installed_variant_1.css', + ), + 'php' => array( + 'example_installed_variant_1.php', + ), ), - 'css' => array( - 'example_installed_variant.css', + 'variant callback' => '_libraries_test_detect_variant', + 'variant arguments' => array(FALSE), + ), + ), + ); + + $libraries['example_variant'] = array( + 'library path' => drupal_get_path('module', 'libraries') . '/tests/example', + 'version callback' => '_libraries_test_return_version', + 'version arguments' => array('2'), + 'variants' => array( + 'example_variant_1' => array( + 'files' => array( + 'js' => array( + 'example_installed_variant_1.js', + ), + 'css' => array( + 'example_installed_variant_1.css', + ), + 'php' => array( + 'example_installed_variant_1.php', + ), ), - 'php' => array( - 'example_installed_variant.php', + 'variant callback' => '_libraries_test_detect_variant', + 'variant arguments' => array(TRUE), + ), + 'example_variant_2' => array( + 'files' => array( + 'js' => array( + 'example_installed_variant_2.js', + ), + 'css' => array( + 'example_installed_variant_2.css', + ), + 'php' => array( + 'example_installed_variant_2.php', + ), ), + 'variant callback' => '_libraries_test_detect_variant', + 'variant arguments' => array(TRUE), ), ), ); - // Integration files for example_installed library. - // Normally added by the corresponding module via hook_libraries_info_alter(), - // these files should be automatically loaded when the library is loaded. - $libraries['example_installed']['integration files'] = array( - 'libraries_test' => array( - 'js' => array( - 'libraries_test.js', - ), - 'css' => array( - 'libraries_test.css', + // Test correct behaviour with multiple versions and multiple variants. + $libraries['example_versions_and_variants'] = array( + 'library path' => drupal_get_path('module', 'libraries') . '/tests/example', + 'version callback' => '_libraries_test_return_version', + 'version arguments' => array('2'), + 'versions' => array( + '1' => array( + 'variants' => array( + 'example_variant_1' => array( + 'files' => array( + 'js' => array( + 'example_installed_1.js', + ), + 'css' => array( + 'example_installed_1.css', + ), + 'php' => array( + 'example_installed_1.php', + ), + ), + 'variant callback' => '_libraries_test_detect_variant', + 'variant arguments' => array(TRUE), + ), + 'example_variant_2' => array( + 'files' => array( + 'js' => array( + 'example_installed_variant_1.js', + ), + 'css' => array( + 'example_installed_variant_1.css', + ), + 'php' => array( + 'example_installed_variant_1.php', + ), + ), + 'variant callback' => '_libraries_test_detect_variant', + 'variant arguments' => array(TRUE), + ), + ), ), - 'php' => array( - 'libraries_test.inc', + '2' => array( + 'variants' => array( + 'example_variant_1' => array( + 'files' => array( + 'js' => array( + 'example_installed_2.js', + ), + 'css' => array( + 'example_installed_2.css', + ), + 'php' => array( + 'example_installed_2.php', + ), + ), + 'variant callback' => '_libraries_test_detect_variant', + 'variant arguments' => array(TRUE), + ), + 'example_variant_2' => array( + 'files' => array( + 'js' => array( + 'example_installed_variant_2.js', + ), + 'css' => array( + 'example_installed_variant_2.css', + ), + 'php' => array( + 'example_installed_variant_2.php', + ), + ), + 'variant callback' => '_libraries_test_detect_variant', + 'variant arguments' => array(TRUE), + ), + ), ), ), ); @@ -137,62 +284,125 @@ function libraries_test_libraries_info() { * * Returns exactly the version string entered as the $version parameter, unless * you specify 'undetected', in which case it returns nothing. + */ +function _libraries_test_return_version($library, $version) { + return $version; +} + +/** + * Gets the version information from an arbitrary library. + * + * This is an exact copy of libraries_get_version() except for the fact that it + * does not take a single associative array as a parameter but multiple + * parameters. Since we support both type of version callbacks this might be + * a useful reference for a custom version callback that uses multiple + * parameters + * + * @param $library + * An associative array containing all information about the library. + * @param $file + * The filename to parse for the version, relative to the library path. For + * example: 'docs/changelog.txt'. + * @param pattern + * A string containing a regular expression (PCRE) to match the library + * version. For example: '/@version (\d+)\.(\d+)/'. + * @param lines + * (optional) The maximum number of lines to search the pattern in. Defaults + * to 20. + * @param cols + * (optional) The maximum number of characters per line to take into account. + * Defaults to 200. In case of minified or compressed files, this prevents + * reading the entire file into memory. + * + * @return + * A string containing the version of the library. * - * @todo Shouldn't this get some info/arguments passed by Libraries API? (?) + * @see libraries_get_version() */ -function libraries_test_get_version($version) { - if ($version !== 'undetected') { - return $version; +function _libraries_get_version($library, $file, $pattern, $lines = 20, $cols = 200) { + + $file = DRUPAL_ROOT . '/' . $library['library path'] . '/' . $file; + if (!file_exists($file)) { + return; + } + $file = fopen($file, 'r'); + while ($lines && $line = fgets($file, $cols)) { + if (preg_match($pattern, $line, $version)) { + fclose($file); + return $version[1]; + } + $lines--; } + fclose($file); +} + +/** + * Detects the variant of an example library. + * + * Returns TRUE or FALSE depending on whether the $status parameter is 'missing' + * or 'found'. + */ +function _libraries_test_detect_variant($library, $name, $status) { + return $status; } /** * Implements hook_menu(). */ function libraries_test_menu() { - $items['libraries_test'] = array( - 'title' => 'Libraries loading test', - 'page callback' => 'libraries_test_load', + $items['libraries_test/simple'] = array( + 'title' => 'Test simple library', + 'page callback' => '_libraries_test_load', + 'page arguments' => array('example_simple'), + 'access callback' => TRUE, + ); + $items['libraries_test/integration_files'] = array( + 'title' => 'Test integration files', + 'page callback' => '_libraries_test_load', + 'page arguments' => array('example_integration_files'), + 'access callback' => TRUE, + ); + $items['libraries_test/versions'] = array( + 'title' => 'Test version loading', + 'page callback' => '_libraries_test_load', + 'page arguments' => array('example_versions'), + 'access callback' => TRUE, + ); + $items['libraries_test/variant'] = array( + 'title' => 'Test variant loading', + 'page callback' => '_libraries_test_load', + 'page arguments' => array('example_variant', 'example_variant_1'), 'access callback' => TRUE, ); - $items['libraries_test_variant'] = array( - 'title' => 'Libraries variant loading test', - 'page callback' => 'libraries_test_load_variant', + $items['libraries_test/versions_and_variants'] = array( + 'title' => 'Test concurrent version and variant loading', + 'page callback' => '_libraries_test_load', + 'page arguments' => array('example_versions_and_variants', 'example_variant_2'), 'access callback' => TRUE, ); return $items; } /** - * Loads the test library. + * Loads the test library with multiple versions. */ -function libraries_test_load() { - libraries_load('example_installed'); +function _libraries_test_load($library, $variant = NULL) { + libraries_load($library, $variant); // JavaScript and CSS files can be checked directly by SimpleTest, so we only // need to manually check for PHP files. $output = ''; - if (function_exists('example_installed_2')) { + if (function_exists('_libraries_example_installed_1')) { + $output .= 'example_installed_1.php'; + } + if (function_exists('_libraries_example_installed_2')) { $output .= 'example_installed_2.php'; } - // Check for the PHP integration file. - if (function_exists('libraries_test_1')) { - $output .= 'libraries_test.inc'; + if (function_exists('_libraries_example_installed_variant_1')) { + $output .= 'example_installed_variant_1.php'; } - return $output; -} - -/** - * Loads the test variant of the test library. - */ -function libraries_test_load_variant() { - libraries_load('example_installed', 'variant'); - // JavaScript and CSS files can be checked directly by SimpleTest, so we only - // need to manually check for PHP files. - $output = ''; - if (function_exists('example_installed_variant')) { - $output .= 'example_installed_variant.php'; + if (function_exists('_libraries_example_installed_variant_2')) { + $output .= 'example_installed_variant_2.php'; } - // Check for the PHP integration file. if (function_exists('libraries_test_1')) { $output .= 'libraries_test.inc'; }