Index: drush_mm/drush_mm.inc
===================================================================
RCS file: drush_mm/drush_mm.inc
diff -N drush_mm/drush_mm.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ drush_mm/drush_mm.inc	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,296 @@
+<?php
+
+/**
+ * Get all folder from the given directory
+ * 
+ * from php.net
+ * - getRecursiveFolderList
+ */
+function _drush_mm_get_recursive_folder_list($curDir, $currentA=false) {                   
+  $dirs = glob($curDir . '/*', GLOB_ONLYDIR);    
+  $cur = 0;
+  foreach($dirs as $dir) {
+    $currentA[$cur]['path'] = $dir;
+    $currentA[$cur] = _drush_mm_get_recursive_folder_list($dir, $currentA[$cur]);
+    ++$cur;
+  }
+  return $currentA;
+}
+
+/**
+ * Callback to collect info from each *.info file found
+ * 
+ * Each info file is parsed and stored into a module_info array
+ * Some fields are used for calculating the dependencies graph
+ * Other fields are nice for generating the dot output.
+ */
+function _drush_mm_get_module_info($item, $key){
+  global $_drush_mm_module_info;
+  
+  foreach( glob( $item .'/*.info') as $file){
+    $base_name= basename( $file, '.info');
+    
+    // check later whether module is encountered more then once
+    $module_info[ $base_name ]['count']++;
+
+    // nice to know to recalculate the original package
+    $module_info[ $base_name ]['dir']= dirname( $file);
+ 
+    $lines= explode( "\n", file_get_contents( $file));
+    
+    $_drush_mm_module_info[ $base_name]['name']= _drush_mm_get_named_value( 'name', $lines);
+    $_drush_mm_module_info[ $base_name]['package']= _drush_mm_get_named_value( 'package', $lines);
+    $_drush_mm_module_info[ $base_name]['description']= _drush_mm_get_named_value( 'description', $lines);
+    
+    $dependencies = _drush_mm_get_named_value( 'dependencies', $lines);
+    if( count($dependencies)){
+      foreach( explode(" ", $dependencies) as $depends){
+        if( strlen( $depends)>0){
+          // TODO place a warning somehow ... see NOTE
+          
+          // NOTE : some modules have faulty dependencies
+          // This makes a corrupt dot file.
+          $_drush_mm_module_info[ $base_name]['depends'][]=$depends;
+        }
+      }
+    }
+  }
+}
+
+/**
+ * get the first matching line of trimmed value out of name = value constructs
+ * 
+ * @param $name string to match before = sign
+ * @param $lines to search for the first occurence
+ * 
+ * @return trimmed value of "name = value"
+ * 
+ */
+function _drush_mm_get_named_value( $name, $lines){
+  $result = preg_grep("/\s*$name\s*=/", $lines);
+  if( count( $result)){
+    // get first matching line only
+    $result = array_shift( $result);
+    // return only value
+    return array_pop(split("=",$result, 2));
+  }
+  return;
+}
+
+function _drush_mm_list_modules( $modules_info){
+  $list = array();
+  
+  foreach( $modules_info as $key=>$value){
+    if( $value['name']){
+      $list[ $value['status']][]=  $key;
+    } else {
+      $list['error'][]= $key;
+    }
+  }
+  
+  sort( $list['error']);
+  sort( $list[0]);
+  sort( $list[1]);
+  
+  if( count($list['error'])){
+    echo "According to the system table available modules but not installed!\n";
+    echo "  ERROR  : " . implode( " ", $list['error']) . "\n";
+  }
+  echo "enabled  :\n  " . implode( " ", $list[1]) . "\n";
+  echo "disabled :\n  " . implode( " ", $list[0]) . "\n";
+  
+}
+
+function _drush_mm_generate_dot( $modules_info){
+  // print_r( $modules_info);
+  echo "digraph {\n";
+  
+  // make this a two pass
+  // 1. nodes
+  // 1.1 use subgraphs for same package based modules
+  // 1.2 use colors 
+  // 2. connections
+  foreach( $modules_info as $key=>$value){
+    echo '  "'.$key .'" [ style=filled, color='
+      . ( isset( $value['name']) ? ($value['status'] ? 'green' : 'yellow') : 'red')
+      ." ];";
+      
+  	if( isset( $value['depends'])){
+      foreach( $value['depends'] as $dep){
+        echo "  \"$key\" -> \"$dep\" \n";
+      }
+    } else {
+      echo "  \"$key\" \n";
+    }
+  }
+  echo "}";
+}
+
+function _drush_mm_get_modules_info(){
+  // storage for all module info
+  global $_drush_mm_module_info;
+  $_drush_mm_module_info= array();
+
+  foreach( $paths= _drush_mm_get_module_paths() as $path){
+    $tree = _drush_mm_get_recursive_folder_list( $path);
+    array_walk_recursive( $tree, '_drush_mm_get_module_info');
+  }
+  
+  // TODO: add the system settings
+  $result= db_query("SELECT name, status FROM {system} WHERE type = 'module'");
+  while( $module= db_fetch_object($result)){
+  	$_drush_mm_module_info[$module->name]['status']= $module->status;
+  }
+  
+  // fill in the rdepends
+  foreach( $_drush_mm_module_info as $key=>$module){
+    foreach( $module['depends'] as $depend){
+      // registrer the reverse dependencies
+      $_drush_mm_module_info[$depend]['rdepends'][]= $key;
+    }
+  }
+  //print_r($_drush_mm_module_info);
+}
+
+/**
+ * based on the modules at hand the depends list is generated
+ * 
+ */
+function _drush_mm_get_module_list( $modules, $enable){
+  global $_drush_mm_module_info;
+  _drush_mm_get_modules_info();
+
+  // Test the given list
+  $missing_modules= array();
+  foreach( $modules as $module){
+    if( !array_key_exists( $module, $_drush_mm_module_info)){
+      $missing_modules[]= $module;
+    }
+  }
+  
+  if( count( $missing_modules)){
+    drush_die(t("The following modules where not found. '!modules'" , array( "!modules"=> implode( ", ", $missing_modules))));
+  }
+  
+  // Generate the list
+  $modules_to_use = array();
+
+  foreach( $modules as $module){
+  	$modules_to_use[$module]= $_drush_mm_module_info[$module];
+  }
+  
+  $dependency_key = $enable ? 'depends' : 'rdepends';
+  
+  // add dependencies
+  $more= TRUE;
+  while( $more){
+    $more= FALSE;
+    foreach( $modules_to_use as $key=>$module){
+      // add all (r)depends to the list
+      foreach( $module[ $dependency_key] as $depend){
+        if( !array_key_exists( $depend, $modules_to_use)){
+          $modules_to_use[$depend]= $_drush_mm_module_info[$depend];
+		  $more= TRUE;
+        }
+      }
+    }
+  }  
+
+  $graph= _drush_mm_build_graph( $modules_to_use, $dependency_key);
+  
+  $result= _drush_mm_graph_tsl( $graph);
+  
+  if( array_shift($result) != "_drupal_"){
+    drush_die(t("Unexpected result for '!modules'." , array( "!modules"=> implode( ", ", $result))));
+  }
+  if( array_pop($result) != "_root_"){
+    drush_die(t("Unexpected result for '!modules'." , array( "!modules"=> implode( ", ", $result))));
+  }
+  
+  return $result;
+
+}
+
+function _drush_mm_build_graph( $modules_to_use, $dependency_key){
+  // build graph
+  $graph= array();
+  foreach($modules_to_use as $key => $module){
+    // make undependants dependant
+    if( count($module[ $dependency_key])){
+      foreach( $module[$dependency_key] as $depend){
+        $graph[$key][$depend]++;
+      }
+    } else {
+      // we make the independant 'drupal' dependent
+      $graph[$key]['_drupal_']++;
+    }
+  }
+
+  // make all rdepend-less rdepend to _root_
+  foreach( $graph as $key=>$node){
+  	if( !count( $modules_to_use[$key]['rdepend'])){
+  	  $graph['_root_'][$key]++;
+  	}
+  }
+  return $graph;
+}
+
+function _drush_mm_get_module_paths(){
+  $paths = array();
+  
+  if (DRUSH_URI) {
+    $path = conf_path();
+    $paths['site'] = DRUSH_DRUPAL_ROOT .'/'. $path .'/modules/';
+  }
+  
+  if (file_exists(DRUSH_DRUPAL_ROOT .'/sites/all/modules/')) {
+    $paths['all'] = DRUSH_DRUPAL_ROOT .'/sites/all/modules/';
+  }
+  
+  $paths['core'] = DRUSH_DRUPAL_ROOT .'/modules/';
+  
+  return $paths;
+}
+
+/**
+ * Generares a topological sorted list
+ * 
+ * @param $graph
+ *   the graph to get a TSL from
+ */
+function _drush_mm_graph_tsl( $graph){
+  $result= array();
+  
+  $reset = TRUE;
+  foreach( $graph as $key=>$value){
+    $result= _drush_mm_graph_df($graph, $key, $reset);
+    $reset= FALSE;
+  }
+
+  return $result;
+}
+
+/**
+ * do a depth first search
+ * 
+ * Each node will be visited. The more depth first.
+ */
+function _drush_mm_graph_df( $graph, $node, $reset=false){
+  static $visited;
+  static $result;
+  
+  if( !isset( $visited) || $reset){
+    $visited= array();
+    $result= array();
+  }
+  
+  if( !isset($visited[$node])){
+    $visited[$node]=TRUE;
+    foreach( $graph[$node] as $key=> $_node){
+      _drush_mm_graph_df( $graph, $key);
+    }
+    array_push($result, $node);
+  }
+  
+  return $result;
+}
Index: drush_mm/drush_mm.info
===================================================================
RCS file: drush_mm/drush_mm.info
diff -N drush_mm/drush_mm.info
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ drush_mm/drush_mm.info	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,5 @@
+; $Id$
+name = Drush Module Manager
+description = Allows you to enable/disable modules and generated a graph from the command line.
+dependencies = drush
+package = drush
Index: drush_mm/README.txt
===================================================================
RCS file: drush_mm/README.txt
diff -N drush_mm/README.txt
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ drush_mm/README.txt	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,23 @@
+// $Id$
+
+DESCRIPTION
+-----------
+
+drush_mm.module (The Drupal Shell Module Manager) allows you to enable and disable modules from the command line.
+
+It provides three commands
+- enable  : enabled the given module and all modules it depends on
+- disable : disable the given module and all modules depending on
+- dot     : generated a dot file for further processing into a webpage
+
+Run these as "drush mm <command>"
+
+Run "drush help mm <command>" to see supported command line options and arguments.
+
+REQUIREMENTS
+------------
+none
+
+------------
+Written by Clemens Tolboom (ngnp) <http://b>.
+No warranties of any kind. Use with care.
Index: drush_mm/drush_mm.module
===================================================================
RCS file: drush_mm/drush_mm.module
diff -N drush_mm/drush_mm.module
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ drush_mm/drush_mm.module	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,160 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ *   The drush Module Manager
+ */
+
+/**
+ * Implementation of hook_help().
+ */
+function drush_mm_help($section) {
+  switch ($section) {
+    case 'drush:mm list':
+      return t("Usage: drush [options] mm list ...
+lists a computer readable name of the installed modules.\n\n");
+  case 'drush:mm enable':
+      return t("Usage: drush [options] mm enable <module_1> <module_2> ...
+<module_n> is the computer readable name of one of the installed modules.\n
+After enabling, you still have to configure the modules through the modules
+administration page\n\n");
+    case 'drush:mm disable':
+      return t("Warning: do not disable required core modules!!!\n\n
+Usage: drush [options] mm disable <module_1> <module_2> ...
+<module_n> is the computer readable name of one of the modules to install.\n\n");
+    case 'drush:mm uninstall':
+      return t("Warning: do not uninstall required core modules!!!\n\n
+Usage: drush [options] mm uninstall <module_1> <module_2> ...
+<module_n> is the computer readable name of one of the modules to uninstall.\n\n");
+      case 'drush:mm dot':
+      return t("Usage: drush [options] mm dot ...
+generated a graphviz dot file for further processing.\n\n");
+  }
+}
+
+/**
+ * Implementation of hook_drush_command().
+ */
+function drush_mm_drush_command() {
+  $items['mm list'] = array(
+    'callback' => 'drush_mm_list_modules',
+    'description' => 'Lists the available modules'
+  ); 
+
+  $items['mm enable'] = array(
+    'callback' => 'drush_mm_enable_module',
+    'description' => 'Enables (and if necessary installs) the given modules and depend'
+  ); 
+
+  $items['mm disable'] = array(
+    'callback' => 'drush_mm_disable_module',
+    'description' => 'Disable the given modules and reverse dependants'
+  ); 
+
+  $items['mm uninstall'] = array(
+    'callback' => 'drush_mm_uninstall_module',
+    'description' => 'Uninstalls the given modules and reverse dependants'
+  ); 
+
+  $items['mm dot'] = array(
+    'callback' => 'drush_mm_dot',
+    'description' => 'Generates a dot file of all modules'
+  ); 
+  return $items;
+}
+
+function drush_mm_list_modules() {
+  require_once "drush_mm.inc";
+  global $_drush_mm_module_info;
+  _drush_mm_get_modules_info();
+
+  _drush_mm_list_modules($_drush_mm_module_info);
+}
+
+function drush_mm_dot() {
+  require_once "drush_mm.inc";
+  global $_drush_mm_module_info;
+  _drush_mm_get_modules_info();
+
+  
+  _drush_mm_generate_dot($_drush_mm_module_info);
+}
+/**
+ * enables the given modules
+ * 
+ * If all modules are present the are enabled
+ */
+function drush_mm_enable_module() {
+  $modules = func_get_args();
+  _drush_mm_endis_module( TRUE, $modules);
+}
+
+function drush_mm_uninstall_module(){
+  $modules = func_get_args();
+  
+  if( empty($modules)){
+    drush_die(t("No modules specified.\n\nRun drush help mm uninstall for more information."));
+  }
+  
+  drush_verbose( t("First disabling modules ..."));
+  call_user_func_array( 'drush_mm_disable_module', $modules);
+
+  drush_verbose( t("Now uninstalling modules ..."));
+  
+  $enable= FALSE;
+  require_once "drush_mm.inc";
+  $result= _drush_mm_get_module_list( $modules, $enable);
+
+  foreach( $result as $module){
+    if( !DRUSH_SIMULATE){
+      include_once './includes/install.inc';
+      drush_verbose( t("Uninstalling module $module"));
+      drupal_uninstall_module( $module);
+    }
+  }
+}
+
+function _drush_mm_endis_module( $enable, $modules){
+  if (empty($modules)) {
+    if( $enable){
+  	  drush_die(t("No modules specified.\n\nRun drush help mm enable for more information."));
+    } else {
+      drush_die(t("No modules specified.\n\nRun drush help mm disable for more information."));
+    }
+  }
+
+  require_once "drush_mm.inc";
+  $result= _drush_mm_get_module_list( $modules, $enable);
+  
+  if( $enable){
+    drush_verbose(t("The following modules will be enabled: '!modules'" , array( "!modules"=> implode( ", ", $result))));
+  	//module_enable( $result);
+    include_once './includes/install.inc';
+    drush_verbose( t("Installing modules"));
+    foreach( $result as $module){
+      drush_verbose( t("Enabling (and mayby installing) module: $module"));
+      if( !DRUSH_SIMULATE){
+      	// only FRESH installed modules gets enabled
+        drupal_install_modules( array($module));
+        // enable for sure
+        // TODO: this will not solve the 'failed install' scenario
+        module_enable( array($module));
+      }
+    }
+  } else {
+    drush_verbose(t("The following modules will be disabled: '!modules'" , array( "!modules"=> implode( ", ", $result))));
+  	foreach( $result as $module){
+      drush_verbose( t("Disabling module: $module"));
+      if( !DRUSH_SIMULATE){
+        module_disable( $result);
+      }
+    }
+  }
+}
+
+function drush_mm_disable_module() {
+  $modules = func_get_args();
+  _drush_mm_endis_module( FALSE, $modules);
+}
+?>
