Index: modules/system/system.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.module,v
retrieving revision 1.333
diff -u -p -r1.333 system.module
--- modules/system/system.module	31 Jul 2006 11:25:54 -0000	1.333
+++ modules/system/system.module	1 Aug 2006 20:46:35 -0000
@@ -176,7 +176,12 @@ function system_menu($may_cache) {
       'description' => t('Enable or disable add-on modules for your site.'),
       'weight' => -10,
       'callback' => 'system_modules',
+      'callback arguments' => array('enabled'),
       'access' => $access);
+    $items[] = array('path' => 'admin/settings/modules/enabled', 'title' => t('enabled'),
+      'callback' => 'system_modules', 'callback arguments' => array('enabled'), 'access' => $access, 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10);
+    $items[] = array('path' => 'admin/settings/modules/disabled', 'title' => t('disabled'),
+      'callback' => 'system_modules', 'callback arguments' => array('disabled'), 'access' => $access, 'type' => MENU_LOCAL_TASK);
 
     // Settings:
     $items[] = array(
@@ -1150,101 +1155,234 @@ function system_themes_submit($form_id, 
 }
 
 /**
+ * Load the metadata associated with a module.
+ *
+ * @param $module
+ *   The name of the module
+ * @param $path
+ *   The path the module lives in, not including the module name.
+ */
+function system_get_meta_info($module, $path) {
+  $filename = $path . '/' . $module . '.meta';
+  $metadata = array();
+
+  if (file_exists($filename)) {
+    $metadata = parse_ini_file($filename, true);
+  }
+  if (!$metadata['package']) {
+    $metadata['package'] = t('uncategorized');
+  }
+  if (!$metadata['version']) {
+    $metadata['version'] = t('unknown');
+  }
+  if (!$metadata['name']) {
+    $metadata['name'] = $module;
+  }
+  if (!is_array($metadata['links'])) {
+    $metadata['links'] = array();
+  }
+  else {
+    $links = array();
+    foreach ($metadata['links'] as $link => $destination) {
+      $links[] = l($link, $destination);
+    }
+    $metadata['links'] = $links;
+  }
+  if ($metadata['dependencies']) {
+    $metadata['dependencies'] = explode(',', $metadata['dependencies']);
+  }
+  else {
+    $metadata['dependencies'] = array();
+  }
+  return $metadata;
+}
+
+/**
+ * This is called to update the system table when a module changes paths or
+ * new modules are discovered.
+ */
+function system_update_system_table($file) {
+  // log the critical hooks implemented by this module
+  $bootstrap = 0;
+  foreach (bootstrap_hooks() as $hook) {
+    if (module_hook($file->name, $hook)) {
+      $bootstrap = 1;
+      break;
+    }
+  }
+
+  // Update the contents of the system table:
+  if (isset($file->status) || (isset($file->old_filename) && $file->old_filename != $file->filename)) {
+    db_query("UPDATE {system} SET description = '%s', name = '%s', bootstrap = %d, filename = '%s' WHERE filename = '%s'", $file->description, $file->name, $bootstrap, $file->filename, $file->old_filename);
+  }
+  else {
+    // This is a new module.
+    db_query("INSERT INTO {system} (name, description, type, filename, status, throttle, bootstrap) VALUES ('%s', '%s', '%s', '%s', %d, %d, %d)", $file->name, $file->description, 'module', $file->filename, $file->status, $file->throttle, $bootstrap);
+  }
+}
+
+/**
+ * Helper function to reduce code. Provides a form that is a button or two
+ * keyed to the module.
+ */
+function system_module_form($module, $metadata, $type, $type2 = NULL) {
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => $type,
+  );
+  $form['metadata'] = array(
+    '#type' => 'value',
+    '#value' => $metadata,
+  );
+  if ($type2) {
+    $form['submit2'] = array(
+      '#type' => 'submit',
+      '#value' => $type2,
+    );
+  }
+  return drupal_get_form("system_module_form_$module", $form, 'system_module_form');
+}
+
+/**
  * Menu callback; displays a listing of all modules.
  */
-function system_modules() {
+function system_modules($type) {
   // Get current list of modules
   $files = system_listing('\.module$', 'modules', 'name', 0);
-
   // Extract current files from database.
   system_get_files_database($files, 'module');
 
   ksort($files);
 
+  $required = array('block', 'filter', 'node', 'system', 'user', 'watchdog');
   foreach ($files as $filename => $file) {
-    drupal_get_filename('module', $file->name, $file->filename);
-    drupal_load('module', $file->name);
-
-    $file->description = module_invoke($file->name, 'help', 'admin/settings/modules#description');
+    $metadata = system_get_meta_info($file->name, dirname($file->filename));
+    if (($type == 'enabled' && $file->status) || ($type == 'disabled' && !$file->status)) {
+      $packages[$metadata['package']][] = $file->name;
+      $options[$file->name] = '';
+      system_update_system_table($file);
+    }
+    $file->description = $metadata['description'];
 
-    $form['name'][$file->name] = array('#value' => $file->name);
-    $form['description'][$file->name] = array('#value' => $file->description);
-    $options[$file->name] = '';
+    // for later dependency checking.
     if ($file->status) {
-      $status[] = $file->name;
+      $metadata['enabled'] = true;
     }
-    if ($file->throttle) {
-      $throttle[] = $file->name;
+    else if ($file->schema_version != -1) {
+      $metadata['uninstalled'] = true;
     }
-
-    // log the critical hooks implemented by this module
-    $bootstrap = 0;
-    foreach (bootstrap_hooks() as $hook) {
-      if (module_hook($file->name, $hook)) {
-        $bootstrap = 1;
-        break;
+    $metdata['filename'] = $file->filename;
+    $modules[$filename] = $metadata;
+  }
+  // Re-order packages to force core modules to the top.
+  $core = $packages[t('drupal core modules')];
+  $required = $packages[t('required modules')];
+  unset($packages[t('drupal core modules')]);
+  ksort($packages);
+  $packages = array_merge(array(t('required modules') => $required, t('drupal core modules') => $core), $packages);
+
+  // set header for the rest.
+  $header = array(t('Name'), t('Version'), t('Description'), t('Operations'));
+
+  foreach ($packages as $group => $package) {
+    $rows = array();
+    foreach ($package as $module) {
+      // Check for dependencies.
+      $depends = '';
+      $can_change = true;
+      if (!$modules[$module]['enabled']) {
+        // disabled modules don't get their links
+        $modules[$module]['links'] = array();
+        foreach ($modules[$module]['dependencies'] as $dependency) {
+          $depends .= (!$depends ? t('Depends on: ') : ', ') . "<em>$dependency</em>";
+          if (!$modules[$dependency]['enabled']) {
+            $depends .= t(' (not present)');
+            $can_change = false;
+          }
+        }
+        if ($can_change) {
+          if ($modules[$module]['uninstalled']) {
+            $modules[$module]['links'][] = system_module_form($module, $modules[$module], t('Enable'), t('Uninstall'));
+          } else {
+            $modules[$module]['links'][] = system_module_form($module, $modules[$module], t('Install'));  
+          }
+        }
+      }
+      else {
+        // Check for modules that depend on this module.
+        foreach ($modules as $dependency => $module_data) {
+          if (in_array($module, $module_data['dependencies']) && $module_data['enabled']) {
+            $depends .= (!$depends ? t('Required by: ') : ', ') . "<em>$dependency</em>";
+            $can_change = false;
+          }
+        }
+        if ($can_change && !in_array($module, $required)) {
+          $modules[$module]['links'][] = system_module_form($module, $modules[$module], t('Disable'));
+        }
       }
-    }
 
-    // Update the contents of the system table:
-    if (isset($file->status) || (isset($file->old_filename) && $file->old_filename != $file->filename)) {
-      db_query("UPDATE {system} SET description = '%s', name = '%s', bootstrap = %d, filename = '%s' WHERE filename = '%s'", $file->description, $file->name, $bootstrap, $file->filename, $file->old_filename);
-    }
-    else {
-      // This is a new module.
-      db_query("INSERT INTO {system} (name, description, type, filename, status, throttle, bootstrap) VALUES ('%s', '%s', '%s', '%s', %d, %d, %d)", $file->name, $file->description, 'module', $file->filename, $file->status, $file->throttle, $bootstrap);
+      $row = array();
+      $row[] = $modules[$module]['name'];
+      $row[] = $modules[$module]['version'];
+      $row[] = $modules[$module]['description'];
+      $row[] = array('data' => '<div class="container-inline">' . implode(', ', $modules[$module]['links']) . '</div>', 'nowrap' => TRUE);
+      $rows[] = $row;
+      if ($depends) {
+        $rows[] = array('', array('data' => $depends, 'colspan' => 3));
+      }
     }
-  }
-
-
-  // Handle status checkboxes, including overriding the generated
-  // checkboxes for required modules.
-  $form['status'] = array('#type' => 'checkboxes', '#default_value' => $status, '#options' => $options);
-  $required = array('block', 'filter', 'node', 'system', 'user', 'watchdog');
-  foreach ($required as $require) {
-    $form['status'][$require] = array('#type' => 'hidden', '#value' => 1, '#suffix' => t('required'));
-  }
-
-  /**
-   * Handle throttle checkboxes, including overriding the generated checkboxes for required modules.
-   */
-  if (module_exist('throttle')) {
-    $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'));
+    if ($rows) {
+      $output .= "<div class=\"modules-header\"><strong>$group</strong></div>";
+      $output .= theme('table', $header, $rows, array('width' => '100%'));
     }
   }
 
-  $form['buttons']['submit'] = array('#type' => 'submit', '#value' => t('Save configuration'));
-
-  return drupal_get_form('system_modules', $form);
+  $output .= form_render($form);
+  return $output;
 }
 
-function theme_system_modules($form) {
-  foreach (element_children($form['name']) as $key) {
-    $row = array();
-    $row[] = form_render($form['name'][$key]);
-    $row[] = form_render($form['description'][$key]);
-    $row[] = array('data' => form_render($form['status'][$key]), 'align' => 'center');
-
-    if (module_exist('throttle')) {
-      $row[] = array('data' => form_render($form['throttle'][$key]), 'align' => 'center');
+function system_module_form_submit($form_id, $form_values) {
+  $module = str_replace('system_module_form_', '', $form_id);
+  $metadata = $form_values['metadata'];
+
+  // include the module's .install file
+  $install_file = dirname($metadata['filename']) .'/'. $module .'.install';
+  if (is_file($install_file)) {
+    include_once $install_file;
+  }
+
+  $op = $_POST['op'];
+  if ($op == t('Install') || $op == t('Enable')) {
+    $status = 1;
+    if ($op == t('Install')) {
+      include './includes/install.inc';
+      // Set the installed schema version for newly installed modules
+      $versions = drupal_get_schema_versions($module);
+      drupal_set_installed_schema_version($module, $versions ? max($versions) : SCHEMA_INSTALLED);
+      module_invoke($module, 'install');
+      drupal_set_message(t('%module installed.', array('%module' => $metadata['name'])));
+    } else {
+      module_invoke($module, 'enable');
+      drupal_set_message(t('%module enabled.', array('%module' => $metadata['name'])));
     }
-    $rows[] = $row;
   }
-
-  $header = array(t('Name'), t('Description'), t('Enabled'));
-  if (module_exist('throttle')) {
-    $header[] = t('Throttle');
+  else {
+    $status = 0;
+    if ($op == t('Uninstall')) {
+      include './includes/install.inc';
+      drupal_set_installed_schema_version($module, SCHEMA_UNINSTALLED);
+      drupal_set_message(t('%module uninstalled.', array('%module' => $metadata['name'])));
+    }
+    else {
+      module_invoke($module, 'disable');
+      drupal_set_message(t('%module disabled.', array('%module' => $metadata['name'])));
+    }
   }
-
-  $output = theme('table', $header, $rows);
-  $output .= form_render($form);
-  return $output;
+  db_query("UPDATE {system} SET status = %d WHERE type = 'module' AND name = '%s'", $status, $module);
+  menu_rebuild();
 }
 
-
+/*
 function system_modules_submit($form_id, $edit) {
   db_query("UPDATE {system} SET status = 0, throttle = 0 WHERE type = 'module'");
 
@@ -1283,7 +1421,7 @@ function system_modules_submit($form_id,
   drupal_set_message(t('The configuration options have been saved.'));
   return 'admin/settings/modules';
 }
-
+*/
 
 /**
  * Menu callback; displays a module's settings page.
