Index: includes/install.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/install.inc,v
retrieving revision 1.15
diff -u -p -r1.15 install.inc
--- includes/install.inc	18 Aug 2006 18:58:44 -0000	1.15
+++ includes/install.inc	21 Aug 2006 03:05:18 -0000
@@ -349,6 +349,21 @@ function drupal_install_module($module) 
 }
 
 /**
+ * Calls the uninstall function and updates the system table for a given module.
+ *
+ * @param module
+ *   The module to install.
+ */
+function drupal_uninstall_module($module, $confirm = false) {
+  module_load_install($module);
+  $retval = module_invoke($module, 'uninstall', $confirm);
+  if ($confirm || !$retval) {
+    drupal_set_installed_schema_version($module, SCHEMA_UNINSTALLED);
+  }
+  return $retval;
+}
+
+/**
  * Verify the state of the specified file.
  *
  * @param $file
Index: includes/module.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/module.inc,v
retrieving revision 1.82
diff -u -p -r1.82 module.inc
--- includes/module.inc	20 Aug 2006 05:57:40 -0000	1.82
+++ includes/module.inc	21 Aug 2006 03:05:18 -0000
@@ -103,9 +103,8 @@ function module_rebuild_cache() {
   ksort($files);
 
   foreach ($files as $filename => $file) {
-    drupal_get_filename('module', $file->name, $file->filename);
-    drupal_load('module', $file->name);
-
+    $file->metadata = module_get_meta_info($file->name, dirname($file->filename));
+    $files[$filename]->metadata = $file->metadata;
     // log the critical hooks implemented by this module
     $bootstrap = 0;
     foreach (bootstrap_hooks() as $hook) {
@@ -117,17 +116,93 @@ function module_rebuild_cache() {
 
     // 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', filename = '%s', bootstrap = %d WHERE filename = '%s'", $file->description, $file->name, $file->filename, $bootstrap, $file->old_filename);
+      db_query("UPDATE {system} SET description = '%s', name = '%s', filename = '%s', bootstrap = %d WHERE filename = '%s'", $file->metadata['description'], $file->name, $file->filename, $bootstrap, $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);
+      db_query("INSERT INTO {system} (name, description, type, filename, status, throttle, bootstrap) VALUES ('%s', '%s', '%s', '%s', %d, %d, %d)", $file->name, $file->metadata['description'], 'module', $file->filename, $file->status, $file->throttle, $bootstrap);
     }
   }
 
   return $files;
 }
+/**
+ * Parse drupal meta file format.
+ *
+ * @param $filename 
+ *   The path to the filename
+ * @return 
+ *   The meta data array
+ */
+function module_parse_meta_file($filename) {
+  $handle = fopen($filename, "r");
+  $meta = fread($handle, filesize($filename));
+  fclose($handle);
+  $metadata = array();    
+  $pattern = '@^\s*((?:[^:/]|/(?!/))+?)\s*:\s*(?:("(?:[^"]|(?<=\\\\)")*")|(\'(?:[^\']|(?<=\\\\)\')*\')|([^\n]*?))\s*($|//)@ms';    
+  if (preg_match_all($pattern, $meta, $matches, PREG_SET_ORDER)) {
+    foreach ($matches as $match) {
+      list(, $key, $value1, $value2, $value3) = $match;
+      $value = stripslashes(trim($value1, '"')) . stripslashes(trim($value2, "'")) . $value3;
+      $metadata[$key] = $value;
+    }
+  } 
+  return $metadata;
+}
+  
+/**
+ * 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 module_get_meta_info($module, $path) {
+  $filename = $path .'/'. $module .'.meta';
+  $metadata = array();
+  
+  if (file_exists($filename)) {
+    $metadata = module_parse_meta_file($filename);
+  }
 
+  // Fill in a bunch of safe defaults.
+  if (!$metadata['package']) {
+    $metadata['package'] = t('Uncategorized');
+  }
+  if (!$metadata['version']) {
+    $metadata['version'] = t('unknown');
+  }
+  if ($metadata['version'] == 'SYSTEM') {
+    $metadata['version'] = VERSION;
+  }
+  if (!$metadata['drupal']) {
+    $metadata['drupal'] = t('undefined');
+  }
+  if (!$metadata['name']) {
+    $metadata['name'] = $module;
+  }
+  if (!$metadata['links']) {
+    $metadata['links'] = array();
+  }
+  else {
+    $metadata['links'] = explode(",", $metadata['links']);
+    $links = array();
+    foreach ($metadata['links'] as $link) {
+      list($link, $destination) = explode('=', $link, 2);
+      $links[] = array('title' => trim($link), 'href' => trim($destination));
+    }
+    $metadata['links'] = $links;
+  }
+  if ($metadata['dependencies']) {
+    $metadata['dependencies'] = explode(',', $metadata['dependencies']);
+  }
+  else {
+    $metadata['dependencies'] = array();
+  }
+  return $metadata;
+}
+ 
 /**
  * Determine whether a given module exists.
  *
Index: modules/aggregator/aggregator.install
===================================================================
RCS file: /cvs/drupal/drupal/modules/aggregator/aggregator.install,v
retrieving revision 1.5
diff -u -p -r1.5 aggregator.install
--- modules/aggregator/aggregator.install	20 Aug 2006 06:38:50 -0000	1.5
+++ modules/aggregator/aggregator.install	21 Aug 2006 03:05:18 -0000
@@ -110,3 +110,14 @@ function aggregator_install() {
       break;
   }
 }
+
+/**
+ * Uninstall the aggregator module and drop its tables.
+ */
+function aggregator_uninstall() {
+  db_query("DROP TABLE {aggregator_category}");
+  db_query("DROP TABLE {aggregator_category_feed}");
+  db_query("DROP TABLE {aggregator_category_item}");
+  db_query("DROP TABLE {aggregator_feed}");
+  db_query("DROP TABLE {aggregator_item}");
+}
Index: modules/book/book.install
===================================================================
RCS file: /cvs/drupal/drupal/modules/book/book.install,v
retrieving revision 1.5
diff -u -p -r1.5 book.install
--- modules/book/book.install	20 Aug 2006 06:38:50 -0000	1.5
+++ modules/book/book.install	21 Aug 2006 03:05:18 -0000
@@ -28,3 +28,8 @@ function book_install() {
       break;
   }
 }
+
+function book_uninstall() {
+  db_query("DROP TABLE {book}");
+}
+
Index: modules/contact/contact.install
===================================================================
RCS file: /cvs/drupal/drupal/modules/contact/contact.install,v
retrieving revision 1.4
diff -u -p -r1.4 contact.install
--- modules/contact/contact.install	20 Aug 2006 06:38:50 -0000	1.4
+++ modules/contact/contact.install	21 Aug 2006 03:05:18 -0000
@@ -30,3 +30,7 @@ function contact_install() {
       break;
   }
 }
+
+function contact_uninstall() {
+  db_query("DROP TABLE {contact}");
+}
Index: modules/drupal/drupal.install
===================================================================
RCS file: /cvs/drupal/drupal/modules/drupal/drupal.install,v
retrieving revision 1.4
diff -u -p -r1.4 drupal.install
--- modules/drupal/drupal.install	20 Aug 2006 06:38:50 -0000	1.4
+++ modules/drupal/drupal.install	21 Aug 2006 03:05:18 -0000
@@ -52,3 +52,8 @@ function drupal_install() {
       break;
   }
 }
+
+function drupal_uninstall() {
+  db_query("DROP TABLE {client}");
+  db_query("DROP TABLE {client_system}");
+}
Index: modules/forum/forum.install
===================================================================
RCS file: /cvs/drupal/drupal/modules/forum/forum.install,v
retrieving revision 1.5
diff -u -p -r1.5 forum.install
--- modules/forum/forum.install	20 Aug 2006 06:38:50 -0000	1.5
+++ modules/forum/forum.install	21 Aug 2006 03:05:18 -0000
@@ -26,3 +26,11 @@ function forum_install() {
       break;
   }
 }
+
+function forum_uninstall($confirm = false) {
+  if (!$confirm) {
+    return t('All your forums and forum posts will be deleted.');
+  }
+  db_query("DROP TABLE {forum}");
+  db_query("DELETE FROM {node} WHERE type = 'forum'");
+}
Index: modules/locale/locale.install
===================================================================
RCS file: /cvs/drupal/drupal/modules/locale/locale.install,v
retrieving revision 1.4
diff -u -p -r1.4 locale.install
--- modules/locale/locale.install	20 Aug 2006 06:38:50 -0000	1.4
+++ modules/locale/locale.install	21 Aug 2006 03:05:19 -0000
@@ -67,3 +67,9 @@ function locale_install() {
   }
   db_query("INSERT INTO {locales_meta} (locale, name, enabled, isdefault) VALUES ('en', 'English', '1', '1')");
 }
+
+function locale_uninstall() {
+  db_query("DROP TABLE {locales_meta}");
+  db_query("DROP TABLE {locales_source}");
+  db_query("DROP TABLE {locales_target}");
+}
Index: modules/poll/poll.install
===================================================================
RCS file: /cvs/drupal/drupal/modules/poll/poll.install,v
retrieving revision 1.6
diff -u -p -r1.6 poll.install
--- modules/poll/poll.install	20 Aug 2006 06:38:50 -0000	1.6
+++ modules/poll/poll.install	21 Aug 2006 03:05:19 -0000
@@ -63,3 +63,9 @@ function poll_install() {
       break;
   }
 }
+
+function poll_uninstall() {
+  db_query("DROP TABLE {poll}");
+  db_query("DROP TABLE {poll_votes}");
+  db_query("DROP TABLE {poll_choices}");
+}
Index: modules/profile/profile.install
===================================================================
RCS file: /cvs/drupal/drupal/modules/profile/profile.install,v
retrieving revision 1.6
diff -u -p -r1.6 profile.install
--- modules/profile/profile.install	20 Aug 2006 06:38:50 -0000	1.6
+++ modules/profile/profile.install	21 Aug 2006 03:05:19 -0000
@@ -63,3 +63,8 @@ function profile_install() {
       break;
   }
 }
+
+function profile_uninstall() {
+  db_query("DROP TABLE {profile_fields}");
+  db_query("DROP TABLE {profile_values}");
+}
Index: modules/search/search.install
===================================================================
RCS file: /cvs/drupal/drupal/modules/search/search.install,v
retrieving revision 1.5
diff -u -p -r1.5 search.install
--- modules/search/search.install	20 Aug 2006 06:38:50 -0000	1.5
+++ modules/search/search.install	21 Aug 2006 03:05:19 -0000
@@ -58,3 +58,9 @@ function search_install() {
       break;
   }
 }
+
+function search_uninstall() {
+  db_query("DROP TABLE {search_dataset}");
+  db_query("DROP TABLE {search_index}");
+  db_query("DROP TABLE {search_total}");
+}
Index: modules/statistics/statistics.install
===================================================================
RCS file: /cvs/drupal/drupal/modules/statistics/statistics.install,v
retrieving revision 1.5
diff -u -p -r1.5 statistics.install
--- modules/statistics/statistics.install	20 Aug 2006 06:38:50 -0000	1.5
+++ modules/statistics/statistics.install	21 Aug 2006 03:05:19 -0000
@@ -36,3 +36,7 @@ function statistics_install() {
       break;
   }
 }
+
+function statistics_uninstall() {
+  db_query("DROP TABLE {accesslog}");
+}
Index: modules/system/admin.css
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/admin.css,v
retrieving revision 1.2
diff -u -p -r1.2 admin.css
--- modules/system/admin.css	17 Aug 2006 21:39:47 -0000	1.2
+++ modules/system/admin.css	21 Aug 2006 03:05:19 -0000
@@ -49,3 +49,49 @@ div.admin .expert-link {
   margin-right: 1em;
   padding-right: 4px;
 }
+
+/* modules administration */
+
+.modules-button {
+  cursor: pointer;
+  padding: 0px 0px 0px 0px;
+  font-size: 12px;
+  font-weight: normal;
+}
+
+.modules-header{
+  font-size: 18px;
+  border: none;
+  font-weight: normal;
+  background-color: white;
+  padding-top: 1em;
+}
+
+.modules-heading {
+  font-size: 13px;
+}
+
+.modules-operations {
+  font-size: 13px;
+  text-align: right;
+}
+
+td.modules-links, .modules-buttons {
+  text-align: right;
+} 
+
+td.modules-links {
+  font-size: 90%;
+  padding-right: .2em;
+}
+
+ul.modules-links {
+  margin-top: 0;
+  margin-bottom: 0;
+  margin-right: .2em;
+  list-style: none;
+}
+
+table.modules-table {
+  width: 100%;
+}
Index: modules/system/system.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.module,v
retrieving revision 1.348
diff -u -p -r1.348 system.module
--- modules/system/system.module	20 Aug 2006 05:57:40 -0000	1.348
+++ modules/system/system.module	21 Aug 2006 03:05:21 -0000
@@ -43,8 +43,10 @@ function system_help($section) {
       $theme = array_pop($reference);
       return t('<p>These options control the display settings for the <code>%template</code> theme. When your site is displayed using this theme, these settings will be used. By clicking "Reset to defaults," you can choose to use the <a href="@global">global settings</a> for this theme.</p>', array('%template' => $theme, '@global' => url('admin/build/themes/settings')));
     case 'admin/settings/modules':
-      return t('<p>Modules are plugins for Drupal that extend its core functionality. Here you can select which modules are enabled. Click on the name of the module in the navigation menu for their individual configuration pages. Once a module is enabled, new <a href="@permissions">permissions</a> might be made available. Modules can automatically be temporarily disabled to reduce server load when your site becomes extremely busy by enabling the throttle.module and checking throttle. The auto-throttle functionality must be enabled on the <a href="@throttle">throttle configuration page</a> after having enabled the throttle module.</p>
-<p>It is important that <a href="@update-php">update.php</a> is run every time a module is updated to a newer version.</p>', array('@permissions' => url('admin/user/access'), '@throttle' => url('admin/settings/throttle'), '@update-php' => $base_url .'/update.php'));
+      return t('<p>Modules are plugins for Drupal that extend its core functionality. Here you can see which modules are enabled. Some modules may include links to their administration pages. Once a module is enabled, new <a href="@permissions">permissions</a> might be made available. Modules can automatically be temporarily disabled to reduce server load when your site becomes extremely busy by enabling the throttle.module and visiting the throttle administration pages.</p><p>If a module is required by another module that is enabled, you will not be able to disable that module until all modules that require it are disabled.</p>
+<p>It is important that <a href="@update-php">update.php</a> is run every time a module is updated to a newer version.</p>', array('@permissions' => url('admin/user/access'), '@update-php' => $base_url .'/update.php'));
+    case 'admin/settings/modules/disabled':
+      return t('<p>Modules are plugins for Drupal that extend its core functionality. Here you can install new modules or re-enable/uninstall previously disabled modules. Once a module is enabled, new <a href="@permissions">permissions</a> might be made available.</p><p><strong>Note that uninstalling a module will remove that module\'s data from your system, and that this operation is not recoverable!</strong> If you are not sure, be sure to make a backup of your database before selecting uninstall!</p><p>If a module is required by another module, you will not be able to enable that module until all modules that it requires are enabled.</p>', array('@permissions' => url('admin/user/access')));
   }
 }
 
@@ -185,9 +187,28 @@ function system_menu($may_cache) {
       'title' => t('modules'),
       'description' => t('Enable or disable add-on modules for your site.'),
       'weight' => -10,
-      'callback' => 'drupal_get_form',
-      'callback arguments' => array('system_modules'),
+      '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);    
+    $items[] = array('path' => 'admin/settings/modules/uninstall',
+      'title' => t('confirm uninstall'),
+      'callback' => 'drupal_get_form',
+      'callback arguments' => array('system_confirm_uninstall'), 
+      'access' => $access, 
+      'type' => MENU_CALLBACK);    
 
     // Settings:
     $items[] = array(
@@ -1199,115 +1220,287 @@ function system_themes_submit($form_id, 
 }
 
 /**
+ * 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,
+    '#attributes' => array('class' => 'modules-button')
+  );
+
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => $type,
+    '#attributes' => array('class' => 'modules-button')
+  );
+  
+  $form['metadata'] = array(
+    '#type' => 'value',
+    '#value' => $metadata,
+    '#attributes' => array('class' => 'modules-button')
+  );
+  
+  $form['#submit']['system_module_form_submit'] = array();
+
+  if ($type2) {
+    $form['submit2'] = array(
+      '#type' => 'submit',
+      '#value' => $type2,
+      '#attributes' => array('class' => 'modules-button')
+    );
+  }
+  return $form;
+}
+
+function system_forms() {
+  static $forms = array();
+  if (!$forms) {
+    $files = system_listing('\.module$', 'modules', 'name', 0);
+    foreach ($files as $file) {
+      $forms["system_module_form_$file->name"]['callback'] = 'system_module_form';
+    }
+  }
+  return $forms;
+
+}
+
+/**
  * Menu callback; displays a listing of all modules.
  */
-function system_modules() {
+function system_modules($type) {
   // Get current list of modules
   $files = module_rebuild_cache();
+  $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 = $file->metadata;
+    if (($type == 'enabled' && $file->status) || ($type == 'disabled' && !$file->status)) {
+      $packages[$metadata['package']][] = $file->name;
+      $options[$file->name] = '';
+    }
 
-    $form['name'][$file->name] = array('#value' => $file->name);
-    $form['description'][$file->name] = array('#value' => $file->description);
-    $options[$file->name] = '';
+    $file->description = $metadata['description'];
+ 
+    // 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;
     }
+    $metadata['filename'] = $file->filename;
+    $modules[$filename] = $metadata;
   }
 
-  // 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'));
+  if (!$packages) {
+    return t('<p>There are currently no modules that you can enable.</p>');
   }
 
-  /**
-   * 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);
-    $throttle_required = array_merge($required, array('throttle'));
-    foreach ($throttle_required as $require) {
-      $form['throttle'][$require] = array('#type' => 'hidden', '#value' => 0, '#suffix' => t('required'));
+  $top_array = array();
+  // Re-order packages to force core modules to the top.
+  foreach (array(t('Required modules'), t('Drupal core modules')) as $key) {
+    if (is_array($packages[$key])) { 
+      $top_array[$key] = $packages[$key];
+      unset($packages[$key]);
     }
   }
 
-  $form['buttons']['submit'] = array('#type' => 'submit', '#value' => t('Save configuration'));
+  ksort($packages);
+  $packages = array_merge($top_array, $packages);
 
-  return $form;
-}
-
-function theme_system_modules($form) {
-  foreach (element_children($form['name']) as $key) {
-    $row = array();
-    $row[] = drupal_render($form['name'][$key]);
-    $row[] = drupal_render($form['description'][$key]);
-    $row[] = array('data' => drupal_render($form['status'][$key]), 'align' => 'center');
+  $header = array(
+    array('data' => t('Name')       , 'header' => TRUE, 'class' => 'modules-heading'),
+    array('data' => t('Version')    , 'header' => TRUE, 'class' => 'modules-heading'),
+    array('data' => t('Description'), 'header' => TRUE, 'class' => 'modules-heading'),
+    array('data' => t('Operations') , 'header' => TRUE, 'class' => 'modules-operations', 'colspan' => 2),
+  );
+       
+  $rows = array();
+  foreach ($packages as $group => $package) {
+    $rows[] = array(
+      array(
+        'colspan' => 5,
+        'header'  => TRUE,
+        'data' => $group,
+        'class' => 'modules-header', 
+      )
+    );
+    $rows[] = $header;
+    foreach ($package as $module) {
+      $modules[$module]['buttons'] = array();
+    
+      // Check for dependencies.
+      $depends = '';
+      $can_change = TRUE;
+      $compatible = strpos(VERSION, $modules[$module]['drupal']) === 0;
+          
+      if (!$modules[$module]['enabled']) {
+        // disabled modules and 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&&$compatible) {
+          if ($modules[$module]['uninstalled']) {
+            $modules[$module]['buttons'][] = drupal_get_form("system_module_form_$module", $module, $modules[$module], t('Enable'), t('Uninstall'));
+          } 
+          else {
+            $modules[$module]['buttons'][] = drupal_get_form("system_module_form_$module", $module, $modules[$module], t('Install'));  
+          }
+        }
+        else {
+          if ($modules[$module]['uninstalled']) {
+            $modules[$module]['buttons'][] = drupal_get_form("system_module_form_$module", $module, $modules[$module], t('Uninstall'));
+          } 
+        }
+      }
+      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]['buttons'][] = drupal_get_form("system_module_form_$module", $module, $modules[$module], t('Disable'));
+        }
+      }
+      $cells = array();
+      $cells[] = $modules[$module]['name'];
+      $cells[] = $modules[$module]['version'];
+      $cells[] = $modules[$module]['description'];
+      if ($help = module_invoke($module, 'help', "admin/help#$module")) {
+        $modules[$module]['links'][] = array('title' => t('help'), 'href' => "admin/help/$module");
+      }
+      if ($perms = module_invoke($module, 'perm')) {
+        $modules[$module]['links'][] = array('title' => t('permissions'), 'href' => 'admin/user/access', 'fragment' => "module-$module");
+      }
 
-    if (module_exists('throttle')) {
-      $row[] = array('data' => drupal_render($form['throttle'][$key]), 'align' => 'center');
+      $cells[] = array(
+        'data' => theme('system_module_links', $modules[$module]['links']),
+        'class' => 'modules-links',
+      );
+      if ($modules[$module]['buttons']) {
+        $cells[] = array(
+          'data' => implode(' ', $modules[$module]['buttons']),
+          'nowrap' => TRUE,
+          'class' => 'modules-buttons',
+        );
+      }
+      else {
+        $cells[3]['colspan'] = 2;
+      }
+      $rows[] = $cells;
+      if ($depends) {
+        $rows[] = array('', array(
+          'data' => $depends, 
+          'colspan' => 4),
+        );
+      }
+      if (!$compatible) {
+        $rows[] = array('', array(
+          'data' => t('(This module is for Drupal version %version and not compatible with your current version)', array('%version' => $modules[$module]['drupal'])), 
+          'colspan' => 4)
+        );
+      }
     }
-    $rows[] = $row;
   }
 
-  $header = array(t('Name'), t('Description'), t('Enabled'));
-  if (module_exists('throttle')) {
-    $header[] = t('Throttle');
-  }
+  if ($rows) {
+    $output .= theme('table', array(), $rows , array('class' => 'modules-table'));
+  }  
 
-  $output = theme('table', $header, $rows);
-  $output .= drupal_render($form);
   return $output;
 }
 
-
-function system_modules_submit($form_id, $edit) {
+function theme_system_module_links($links) {
+  if ($links) {
+    $output = '<ul class="modules-links">';
+    foreach ($links as $link) {
+      $output .= '<li> ' .  l($link['title'], $link['href'], $link['attributes'], $link['query'], $link['fragment']) . '</li>';
+    }
+    $output .= '</ul>';
+  }
+  return $output;
+}
+/**
+ * Implementation of the module form hook_submit().
+ * @param $form_id
+ *  The id of the form.
+ * @param $form_values
+ *  The values passed back from the client.
+ */
+function system_module_form_submit($form_id, $form_values) {
   include_once './includes/install.inc';
-  $new_modules = array();
-  foreach ($edit['status'] as $key => $choice) {
-    if ($choice) {
-      if (drupal_get_installed_schema_version($key) == SCHEMA_UNINSTALLED) {
-        $new_modules[] = $key;
-      }
-      else {
-        module_enable($key);
+  $module = str_replace('system_module_form_', '', $form_id);
+  $metadata = $form_values['metadata'];
+
+  $op = $_POST['op'];
+  if ($op == t('Install') || $op == t('Enable')) {
+    $status = 1;
+    if ($op == t('Install')) {
+      drupal_install_module($module);
+      drupal_set_message(t('%module installed.', array('%module' => $metadata['name'])));
+    } 
+    else {
+      module_enable($module);
+      drupal_set_message(t('%module enabled.', array('%module' => $metadata['name'])));
+    }
+  }
+  else {
+    $status = 0;
+    if ($op == t('Uninstall')) {
+      $confirm = drupal_uninstall_module($module);
+      if ($confirm) {
+        return "admin/settings/modules/uninstall/$module";
       }
+      drupal_set_message(t('%module uninstalled.', array('%module' => $metadata['name'])));
     }
     else {
-      module_disable($key);
+      module_disable($module);
+      drupal_set_message(t('%module disabled.', array('%module' => $metadata['name'])));
     }
   }
 
-  module_list(TRUE, FALSE);
+  menu_rebuild();
+  return $_GET['q'];
+}
 
-  foreach ($new_modules as $module) {
-    drupal_install_module($module);
+function system_confirm_uninstall($module = NULL) {
+  if (!$module) {
+    return drupal_not_found();
   }
+  include_once './includes/install.inc';
+  $message = drupal_uninstall_module($module);
 
-  if (is_array($edit['throttle'])) {
-    foreach ($edit['throttle'] as $key => $choice) {
-      if ($choice) {
-        db_query("UPDATE {system} SET throttle = 1 WHERE type = 'module' and name = '%s'", $key);
-      }
-    }
+  if (!$message) {
+    return drupal_goto('admin/settings/modules/disabled');
   }
 
-  menu_rebuild();
-  node_types_rebuild();
-
-  drupal_set_message(t('The configuration options have been saved.'));
-  return 'admin/settings/modules';
+  $form['module'] = array('#type' => 'value', '#value' => $module);
+  return confirm_form($form,
+    t('Are you sure you want to uninstall @module?', array('@module' => $module)),
+    $_GET['destination'] ? $_GET['destination'] : 'admin/settings/modules/disabled',
+    '<p>' . $message . '</p>',
+    t('Uninstall'), t('Cancel')
+  );
 }
 
+function system_confirm_uninstall_submit($form_id, $form) {
+  if ($form['confirm']) {
+    include_once './includes/install.inc';
+    drupal_uninstall_module($form['module'], true);
+    drupal_set_message(t('%module uninstalled.'), array('%module' => $form['module']));
+  }
+  return 'admin/settings/modules/disabled';
+}
 
 /**
  * Menu callback; displays a module's settings page.
Index: modules/throttle/throttle.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/throttle/throttle.module,v
retrieving revision 1.65
diff -u -p -r1.65 throttle.module
--- modules/throttle/throttle.module	18 Aug 2006 18:58:46 -0000	1.65
+++ modules/throttle/throttle.module	21 Aug 2006 03:05:21 -0000
@@ -14,10 +14,26 @@ function throttle_menu($may_cache) {
       'path' => 'admin/settings/throttle',
       'description' => t('Control how your site cuts out content during heavy load.'),
       'title' => t('throttle'),
+      'access' => user_access('administer site configuration'),
+      'callback' => 'drupal_get_form',
+      'callback arguments' => array('throttle_admin_modules'),
+      'type' => MENU_NORMAL_ITEM,
+    );
+    $items[] = array(
+      'path' => 'admin/settings/throttle/modules',
+      'title' => t('modules'),
+      'callback' => 'drupal_get_form',
+      'callback arguments' => array('throttle_admin_modules'),
+      'access' => user_access('administer site configuration'),
+      'type' => MENU_DEFAULT_LOCAL_TASK,
+    );
+    $items[] = array(
+      'path' => 'admin/settings/throttle/settings',
+      'title' => t('settings'),
       'callback' => 'drupal_get_form',
       'callback arguments' => array('throttle_admin_settings'),
       'access' => user_access('administer site configuration'),
-      'type' => MENU_NORMAL_ITEM
+      'type' => MENU_LOCAL_TASK,
     );
   }
 
@@ -171,3 +187,65 @@ function throttle_admin_settings() {
 
   return system_settings_form($form);
 }
+
+function throttle_admin_modules() {
+  // Get current list of modules
+  $result = db_query("SELECT * FROM {system} WHERE type = 'module' AND status = 1");
+
+  $required = array('block', 'filter', 'node', 'system', 'user', 'watchdog', 'throttle');
+  while ($file = db_fetch_object($result)) {
+    if (!in_array($file->name, $required) && file_exists($file->filename)) {
+      $form['name'][$file->name] = array('#value' => $file->name);
+      $form['description'][$file->name] = array('#value' => $file->description);
+      $options[$file->name] = '';
+      $form['original'][$file->name] = array(
+        '#type' => 'value', 
+        '#value' => $file->throttle,
+      );
+      $form['original']['#tree'] = TRUE;
+      if ($file->throttle) {
+        $throttle[] = $file->name;
+      }
+    }
+  }
+
+  // Handle status checkboxes, including overriding the generated
+  // checkboxes for required modules.
+  $form['throttle'] = array('#type' => 'checkboxes', '#default_value' => $throttle, '#options' => $options);
+  foreach ($throttle_required as $require) {
+    $form['throttle'][$require] = array('#type' => 'hidden', '#value' => 0, '#suffix' => t('required'));
+  }
+
+  $form['buttons']['submit'] = array('#type' => 'submit', '#value' => t('Save configuration'));
+
+  return $form;
+}
+
+function theme_throttle_admin_modules($form) {
+  foreach (element_children($form['name']) as $key) {
+    $row = array();
+    $row[] = drupal_render($form['name'][$key]);
+    $row[] = drupal_render($form['description'][$key]);
+    $row[] = array('data' => drupal_render($form['throttle'][$key]), 'align' => 'center');
+    $rows[] = $row;
+  }
+
+  $header = array(t('Name'), t('Description'), t('Throttle'));
+
+  $output = theme('table', $header, $rows);
+  $output .= drupal_render($form);
+  return $output;
+}
+
+function throttle_admin_modules_submit($form_id, $edit) {
+  foreach ($edit['throttle'] as $key => $choice) {
+    if ($choice && !$edit['original'][$key]) {
+      db_query("UPDATE {system} SET throttle = 1 WHERE type = 'module' and name = '%s'", $key);
+    }
+    else if (!$choice && $edit['original'][$key]) {
+      db_query("UPDATE {system} SET throttle = 0 WHERE type = 'module' and name = '%s'", $key);
+    }
+  }
+  
+  drupal_set_message(t('The configuration has been saved.'));
+}
