Commit 12d6d666 authored by Dries's avatar Dries

- Patch #211747 by chx, alex_b, dww: allow specifying version information as...

- Patch #211747 by chx, alex_b, dww: allow specifying version information as part of module dependencies.
parent 3625dddf
......@@ -145,6 +145,8 @@ Drupal 7.0, xxxx-xx-xx (development version)
* Upgraded the jQuery Forms library to 2.21.
* Added jQuery UI 1.7.2, which allows improvements to Drupal's user
experience.
- Better module version support.
* Modules now can specify which version of another module they depend on.
Drupal 6.0, 2008-02-13
----------------------
......
......@@ -14,7 +14,7 @@
* A three dimensional associated array, with the first keys being the names
* of the vertices, these can be strings or numbers. The second key is
* 'edges' and the third one are again vertices, each such key representing
* an edge. Values of array elements do not matter.
* an edge. Values of array elements are copied over.
*
* Example:
* @code
......@@ -108,7 +108,7 @@ function _drupal_depth_first_search(&$graph, &$state, $start, &$component = NULL
if (isset($graph[$start]['edges'])) {
foreach ($graph[$start]['edges'] as $end => $v) {
// Mark that $start can reach $end.
$graph[$start]['paths'][$end] = TRUE;
$graph[$start]['paths'][$end] = $v;
if (isset($graph[$end]['component']) && $component != $graph[$end]['component']) {
// This vertex already has a component, use that from now on and
......@@ -136,7 +136,7 @@ function _drupal_depth_first_search(&$graph, &$state, $start, &$component = NULL
// paths.
foreach ($graph[$start]['paths'] as $end => $v) {
if (isset($graph[$end])) {
$graph[$end]['reverse_paths'][$start] = TRUE;
$graph[$end]['reverse_paths'][$start] = $v;
}
}
......
......@@ -95,11 +95,52 @@ function module_list($refresh = FALSE, $sort = FALSE, $fixed_list = NULL) {
function _module_build_dependencies($files) {
require_once DRUPAL_ROOT . '/includes/graph.inc';
$roots = $files;
// We use named subpatterns and support every op that version_compare
// supports. Also, op is optional and defaults to equals.
$p_op = '(?P<operation>!=|==|=|<|<=|>|>=|<>)?';
// Core version is always optional: 7.x-2.x and 2.x is treated the same.
$p_core = '(?:' . preg_quote(DRUPAL_CORE_COMPATIBILITY) . '-)?';
$p_major = '(?P<major>\d+)';
// By setting the minor version to x, branches can be matched.
$p_minor = '(?P<minor>\d+|x)';
foreach ($files as $filename => $file) {
$graph[$file->name]['edges'] = array();
if (isset($file->info['dependencies']) && is_array($file->info['dependencies'])) {
foreach ($file->info['dependencies'] as $dependency_name) {
$graph[$file->name]['edges'][$dependency_name] = 1;
$value = array();
$parts = explode('(', $dependency_name, 2);
$dependency_name = trim($parts[0]);
if (isset($parts[1])) {
$value['original_version'] = ' (' . $parts[1];
foreach (explode(',', $parts[1]) as $version) {
if (preg_match("/^\s*$p_op\s*$p_core$p_major\.$p_minor/", $version, $matches)) {
$op = !empty($matches['operation']) ? $matches['operation'] : '=';
if ($matches['minor'] == 'x') {
// If a module is newer than 2.x then it's at least 3.0.
$matches['minor'] = 0;
if ($op == '>') {
$matches['major']++;
$op = '>=';
}
// If a module is older or equivalent than 2.x then it is older
// than 3.0.
if ($op == '<=') {
$matches['major']++;
$op = '<';
}
// Equivalence is checked by preg.
if ($op == '=' || $op == '==') {
$value['versions'][] = array('preg' => '/^' . $matches['major'] . '\./');
$op = '';
}
}
if ($op) {
$value['versions'][] = array('op' => $op, 'version' => $matches['major'] . '.' . $matches['minor']);
}
}
}
}
$graph[$file->name]['edges'][$dependency_name] = $value;
unset($roots[$dependency_name]);
}
}
......@@ -138,13 +179,13 @@ function module_load_install($module) {
/**
* Load a module include file.
*
*
* Examples:
* @code
* // Load node.admin.inc from the node module.
* module_load_include('inc', 'node', 'node.admin');
* // Load content_types.inc from the node module.
* module_load_include('inc', 'node', 'content_types');
* module_load_include('inc', 'node', 'content_types');
* @endcode
*
* Do not use this function to load an install file. Use module_load_install()
......@@ -155,7 +196,7 @@ function module_load_install($module) {
* @param $module
* The module to which the include file belongs.
* @param $name
* Optionally, specify the base file name (without the $type extension).
* Optionally, specify the base file name (without the $type extension).
* If not set, $module is used.
*/
function module_load_include($type, $module, $name = NULL) {
......@@ -194,7 +235,7 @@ function module_load_all_includes($type, $name = NULL) {
*/
function module_enable($module_list, $disable_modules_installed_hook = FALSE) {
$invoke_modules = array();
// Try to install the enabled modules and collect which were installed.
// $module_list is not changed and already installed modules are ignored.
$modules_installed = array_filter($module_list, '_drupal_install_module');
......
......@@ -165,3 +165,23 @@ function system_test_exit() {
watchdog('system_test', 'hook_exit');
}
/**
* Implement hook_system_info_alter().
*/
function system_test_system_info_alter(&$info, $file) {
// We need a static otherwise the last test will fail to alter common_test.
static $test;
if (($dependencies = variable_get('dependencies', array())) || $test) {
if ($file->name == 'module_test') {
$info['hidden'] = FALSE;
$info['dependencies'][] = array_shift($dependencies);
variable_set('dependencies', $dependencies);
$test = TRUE;
}
if ($file->name == 'common_test') {
$info['hidden'] = FALSE;
$info['version'] = '7.x-2.4';
}
}
}
......@@ -598,13 +598,32 @@ function system_modules($form_state = array()) {
foreach ($module->requires as $requires => $v) {
if (!isset($files[$requires])) {
$extra['requires'][$requires] = t('@module (<span class="admin-missing">missing</span>)', array('@module' => drupal_ucfirst($requires)));
$extra['disabled'] = TRUE;
$extra['disabled'] = TRUE;
}
else {
$requires_name = $files[$requires]->info['name'];
if ($v) {
$requires_name .= $v['original_version'];
$current_version = str_replace(DRUPAL_CORE_COMPATIBILITY . '-', '', $files[$requires]->info['version']);
foreach ($v['versions'] as $required_version) {
if ((isset($required_version['op']) && !version_compare($current_version, $required_version['version'], $required_version['op'])) ||
(isset($required_version['preg']) && !preg_match($required_version['preg'], $current_version))) {
$extra['requires'][$requires] = t('@module (<span class="admin-missing">incompatible with</span> version @version)', array(
'@module' => $requires_name,
'@version' => $files[$requires]->info['version'],
));
$extra['disabled'] = TRUE;
}
}
}
elseif (!$files[$requires]->status) {
$extra['requires'][$requires] = t('@module (<span class="admin-disabled">disabled</span>)', array('@module' => $files[$requires]->info['name']));
if (!isset($extra['requires'][$requires])) {
if ($files[$requires]->status) {
$extra['requires'][$requires] = t('@module (<span class="admin-enabled">enabled</span>)', array('@module' => $requires_name));
}
else {
$extra['requires'][$requires] = t('@module (<span class="admin-disabled">disabled</span>)', array('@module' => $requires_name));
}
}
else {
$extra['requires'][$requires] = t('@module (<span class="admin-enabled">enabled</span>)', array('@module' => $files[$requires]->info['name']));
}
}
// Generate link for module's help page, if there is one.
......
......@@ -197,6 +197,61 @@ class ModuleDependencyTestCase extends ModuleTestCase {
}
}
/**
* Test module dependency on specific versions.
*/
class ModuleVersionTestCase extends ModuleTestCase {
public static function getInfo() {
return array(
'name' => 'Module versions',
'description' => 'Check module version dependencies.',
'group' => 'Module',
);
}
function setup() {
parent::setUp('module_test');
}
/**
* Test version dependencies.
*/
function testModuleVersions() {
$dependencies = array(
// Alternating between being compatible and incompatible with 7.x-2.4.
// The first is always a compatible.
'common_test',
// Branch incompatibility.
'common_test (1.x)',
// Branch compatibility.
'common_test (2.x)',
// Another branch incompatibility.
'common_test (>2.x)',
// Another branch compatibility.
'common_test (<=2.x)',
// Another branch incompatibility.
'common_test (<2.x)',
// Another branch compatibility.
'common_test (>=2.x)',
// Nonsense, misses a dash. Incompatible with everything.
'common_test (=7.x2.x, >=2.4)',
// Core version is optional. Compatible.
'common_test (=7.x-2.x, >=2.4)',
// Test !=, explicitly incompatible.
'common_test (=2.x, !=2.4)',
// Three operations. Compatible.
'common_test (=2.x, !=2.3, <2.5)',
);
variable_set('dependencies', $dependencies);
$n = count($dependencies);
for ($i = 0; $i < $n; $i++) {
$this->drupalGet('admin/structure/modules');
$checkbox = $this->xpath('//input[@id="edit-modules-Testing-module-test-enable"]');
$this->assertEqual(!empty($checkbox[0]['disabled']), $i % 2, $dependencies[$i]);
}
}
}
/**
* Test required modules functionality.
*/
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment