Index: includes/module.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/module.inc,v retrieving revision 1.85 diff -u -F^f -r1.85 module.inc --- includes/module.inc 31 Aug 2006 22:12:15 -0000 1.85 +++ includes/module.inc 8 Sep 2006 03:14:15 -0000 @@ -159,6 +159,9 @@ function _module_parse_info_file($filena if (file_exists($filename)) { $info = parse_ini_file($filename); } + if(isset($info['dependencies'])) { + $info['dependencies'] = explode(" ", $info['dependencies']); + } return $info; } Index: modules/forum/forum.info =================================================================== RCS file: /cvs/drupal/drupal/modules/forum/forum.info,v retrieving revision 1.1 diff -u -F^f -r1.1 forum.info --- modules/forum/forum.info 31 Aug 2006 20:22:35 -0000 1.1 +++ modules/forum/forum.info 8 Sep 2006 03:14:15 -0000 @@ -1,4 +1,4 @@ ; $Id: forum.info,v 1.1 2006/08/31 20:22:35 dries Exp $ name = Forum description = Enables threaded discussions about general topics. - +dependencies = taxonomy comment Index: modules/system/admin.css =================================================================== RCS file: /cvs/drupal/drupal/modules/system/admin.css,v retrieving revision 1.7 diff -u -F^f -r1.7 admin.css --- modules/system/admin.css 6 Sep 2006 08:35:59 -0000 1.7 +++ modules/system/admin.css 8 Sep 2006 03:14:16 -0000 @@ -39,6 +39,11 @@ padding-right: 4px; } +div.admin-missing, div.admin-required { + font-size: 0.9em; + color: #444; +} + /** * Formatting for status report */ @@ -63,3 +68,4 @@ table.system-status-report tr.ok th { background-image: url('../../misc/watchdog-ok.png'); } + Index: modules/system/system.module =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.module,v retrieving revision 1.367 diff -u -F^f -r1.367 system.module --- modules/system/system.module 7 Sep 2006 08:03:08 -0000 1.367 +++ modules/system/system.module 8 Sep 2006 03:14:17 -0000 @@ -1217,49 +1217,192 @@ function system_themes_submit($form_id, } /** - * Menu callback; displays a listing of all modules. + * Menu callback; provides module enable/disable interface. + * + * Modules can be enabled or disabled and set for throttling if the throttle module is enabled. + * The list of modules gets populated by module.info files, which contain each module's name + * and description. + * + * Dependency checking is performed both forward and reverse. This keeps modules from being + * enabled that are missing dependencies as well as keeping modules being disabled that are + * being depended on. Dependencies are added to the info file space delimited. + * + * Example of .info file: + * name = Forum + * description = Enables threaded discussions about general topics. + * dependencies = taxonomy comment + * + * @return + * The form array. */ function system_modules() { - // Get current list of modules + // Get current list of modules. $files = module_rebuild_cache(); + + // Store information for validation hook. + $form['validation_modules'] = array('#type' => 'value', '#value' => array()); + + //Create store for disabled modules as browser will not. + $form['disabled_modules'] = array('#type' => 'value', '#value' => array()); + $dependents = array(); + // Traverse the files retrieved and build the form. foreach ($files as $filename => $file) { - $info = $file->info; - $form['name'][$file->name] = array('#value' => $info['name']); - $form['description'][$file->name] = array('#value' => t($info['description'])); - $options[$file->name] = ''; + // Populate validation list with only the necessities to save on session space. + $form['validation_modules']['#value'][$filename]['name'] = $file->info['name']; + $form['validation_modules']['#value'][$filename]['dependencies'] = $file->info['dependencies']; + $form['validation_modules']['#value'][$filename]['status'] = $file->status; + + $form['name'][$filename] = array('#value' => $file->info['name']); + $form['description'][$filename] = array('#value' => t($file->info['description'])); + $options[$filename] = ''; if ($file->status) { $status[] = $file->name; } if ($file->throttle) { $throttle[] = $file->name; } + + $missing_dependencies = array(); + // Check missing dependencies. + if (is_array($file->info['dependencies'])) { + foreach ($file->info['dependencies'] as $dependency) { + if (!isset($files[$dependency]) || !$files[$dependency]->status) { + $disabled[] = $filename; + if (isset($files[$dependency])) { + $missing_dependencies[] = $files[$dependency]->info['name']; + } else { + $missing_dependencies[] = t('Please download @dependency.', array('!modules' => 'http://drupal.org/project/Modules/', '@dependency' => $dependency)); + } + } + elseif ($file->status) { + $disabled[] = $dependency; + $dependents[$dependency][] = $file->info['name']; + $form['disabled_modules']['#value'][$dependency] = $file->status; + } + } + + // Add text for missing dependencies. + if (!empty($missing_dependencies)) { + $form['description'][$filename]['missing_dependencies'] = array( + '#value' => t('Missing dependencies: !missing_dependencies', + array('!missing_dependencies' => implode(', ', $missing_dependencies)) + ), + '#prefix' => '
', + '#suffix' => '
', + ); + } + } + } + + + // Add dependent descriptions if any + foreach ($dependents as $filename => $dependency) { + $form['description'][$filename]['required'] = array( + '#value' => t('Required by: @required', + array('@required' => implode(', ', $dependency)) + ), + '#prefix' => '
', + '#suffix' => '
', + ); } - // Handle status checkboxes, including overriding the generated - // checkboxes for required modules. - $form['status'] = array('#type' => 'checkboxes', '#default_value' => $status, '#options' => $options); + // Handle status checkboxes, including overriding + // the generated checkboxes for required modules. + $form['status'] = array( + '#type' => 'checkboxes', + '#default_value' => $status, + '#options' => $options, + '#process' => array( + 'expand_checkboxes' => array(), + 'system_module_disable' => array($disabled), + ), + ); $required = array('block', 'filter', 'node', 'system', 'user', 'watchdog'); foreach ($required as $require) { - $form['status'][$require] = array('#type' => 'hidden', '#value' => 1, '#suffix' => t('required')); + $form['status'][$require] = array( + '#type' => 'hidden', + '#value' => 1, + '#suffix' => t('required'), + ); } - /** - * Handle throttle checkboxes, including overriding the generated checkboxes for required modules. - */ + // Handle throttle checkboxes, including overriding the + // generated checkboxes for required modules. if (module_exists('throttle')) { - $form['throttle'] = array('#type' => 'checkboxes', '#default_value' => $throttle, '#options' => $options); + $form['throttle'] = array( + '#type' => 'checkboxes', + '#default_value' => $throttle, + '#options' => $options, + ); $throttle_required = array_merge($required, array('throttle')); foreach ($throttle_required as $require) { - $form['throttle'][$require] = array('#type' => 'hidden', '#value' => 0, '#suffix' => t('required')); + $form['throttle'][$require] = array( + '#type' => 'hidden', + '#value' => 0, + '#suffix' => t('required'), + ); } } - $form['buttons']['submit'] = array('#type' => 'submit', '#value' => t('Save configuration')); + $form['buttons']['submit'] = array( + '#type' => 'submit', + '#value' => t('Save configuration'), + ); return $form; } +/** + * Form process callback function to disbale check boxes. + */ +function system_module_disable($form, $form_values, $disabled) { + foreach ($disabled as $key) { + $form[$key]['#attributes']['disabled'] = 'disabled'; + } + return $form; +} + +/** + * Valiate callback; performs dependency integrity checks to ensure that modules don't get added + * if dependencies fail. + */ +function system_modules_validate($form_id, $form_values, $form) { + // Recheck values for submitted dependency errors. + foreach ($form_values['validation_modules'] as $name => $module) { + $failed_dependencies = array(); + // Is module enabled and does it have dependencies. + if (!$module['status'] && $form_values['status'][$name] && is_array($module['dependencies'])) { + foreach ($module['dependencies'] as $dependency) { + // Check if dependendency is enabled on the form. + if (!($form_values['status'][$dependency])) { + // Give appropriate task to enable dependency. + $failed_dependencies['tasks'][] = t('Please enable the %dependent module to continue.', + array('%dependent' => isset($form_values['validation_modules'][$dependency]) ? $form_values['validation_modules'][$dependency]['name'] : $dependency ) + ); + $failed_dependencies['dependencies'][] = $form_values['validation_modules'][$dependency]['name']; + } + } + } + if (!empty($failed_dependencies)) { + form_set_error("status][$name", + t('Could not enable the %module module, as it depends on %dependency.', + array( + '%module' => $form_values['validation_modules'][$name]['name'], + '%dependency' => implode(', ', $failed_dependencies['dependencies']), + ) + ) + ); + foreach ($failed_dependencies['tasks'] as $task) { + drupal_set_message($task, 'error'); + } + } + } +} + +/** + * Theme call back for the modules form. + */ function theme_system_modules($form) { foreach (element_children($form['name']) as $key) { $row = array(); @@ -1285,13 +1428,18 @@ function theme_system_modules($form) { return $output; } - +/** + * Submit callback; handles modules form submission. + */ function system_modules_submit($form_id, $edit) { include_once './includes/install.inc'; $new_modules = array(); - // Enable/disable modules that have already been installed - foreach ($edit['status'] as $key => $choice) { + // Merge in disabled active modules since they should be enabled. + // They don't appear because disabled checkboxes are not submited + // by browsers. + $modules = array_merge($edit['status'], $edit['disabled_modules']); + foreach ($modules as $key => $choice) { if ($choice) { if (drupal_get_installed_schema_version($key) == SCHEMA_UNINSTALLED) { $new_modules[] = $key;