? views
? includes/table.inc
Index: commands/pm/pm.drush.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/drush/commands/pm/pm.drush.inc,v
retrieving revision 1.149
diff -u -p -r1.149 pm.drush.inc
--- commands/pm/pm.drush.inc	3 Dec 2010 16:32:56 -0000	1.149
+++ commands/pm/pm.drush.inc	4 Dec 2010 19:20:52 -0000
@@ -187,7 +187,8 @@ function pm_drush_command() {
   );
   $items['pm-releases'] = array(
     'description' => 'Print release information for given projects.',
-    'drupal dependencies' => array($update),
+// what do we do about this?  we only need it if we are bootstrapped
+//  'drupal dependencies' => array($update),
     'arguments' => array(
       'projects' => 'A list of drupal.org project names. Defaults to \'drupal\'',
     ),
@@ -195,6 +196,7 @@ function pm_drush_command() {
       'drush pm-releases cck zen' => 'View releases for cck and Zen projects.',
     ),
     'deprecated-aliases' => array('info'),
+    'bootstrap' => DRUSH_BOOTSTRAP_DRUSH, // No bootstrap required.
   );
   $items['pm-download'] = array(
     'description' => 'Download projects from drupal.org or other sources.',
@@ -205,6 +207,7 @@ function pm_drush_command() {
       'drush dl cck zen es' => 'Download latest versions of CCK, Zen and Spanish translations for your Drupal version.',
       'drush dl og-1.3' => 'Download a specfic version of Organic groups module for my version of Drupal.',
       'drush dl diff-6.x-2.x' => 'Download a specific development branch of diff module for a specific Drupal version.',
+      'drush dl views --dev' => 'Download the most recent development branch of the views module.',
     ),
     'arguments' => array(
       'projects' => 'A list of drpal.org project names, with optional version. Defaults to \'drupal\'',
@@ -215,6 +218,9 @@ function pm_drush_command() {
       '--source' => 'The base URL which provides project release history in XML. Defaults to http://updates.drupal.org/release-history.',
       '--notes' => 'Show release notes after each project is downloaded.',
       '--variant' => "Only useful for install profiles. Possible values: 'core', 'no-core', 'make'.",
+      '--dev' => "Download a development release. Optional.",
+      '--select' => "Select the version to download interactively from a list of available releases.",
+      '--all' => "Useful only with --select; shows all available releases instead of a short list of recent releases.",
       '--drupal-project-rename' => 'Alternate name for "drupal-x.y" directory when downloading Drupal project. If empty, will defaults to "drupal".',
       '--pipe' => 'Returns a list of the names of the extensions (modules and themes) contained in the downloaded projects.',
     ),
@@ -937,6 +943,18 @@ function _pm_find_common_path($project_t
 }
 
 /**
+ * Implementation of drush_COMMAND_init().
+ *
+ * pm-releases doesn't require any bootstrap level to operate but it 
+ * will use a bootstrapped site if available in order to mark the
+ * installed version of projects.
+ */
+function drush_pm_releases_init() {
+  drush_bootstrap_max();
+}
+
+
+/**
  * Command callback. Show available releases for given project(s).
  */
 function drush_pm_releases() {
@@ -956,7 +974,7 @@ function drush_pm_releases() {
     foreach ($project['releases'] as $release) {
       $rows[] = array(
         $release['version'],
-        format_date($release['date'], 'custom', 'Y-M-d'),
+        date('Y-M-d', $release['date']),
         implode(', ', $release['release_status'])
       );
     }
@@ -965,6 +983,114 @@ function drush_pm_releases() {
   }
 }
 
+
+function _drush_pm_get_releases_from_xml($xml, $project) {
+  foreach (array('title', 'short_name', 'dc:creator', 'api_version', 'recommended_major', 'supported_majors', 'default_major', 'project_status', 'link') as $item) {
+    if (array_key_exists($item, $xml)) {
+      $value = $xml->xpath($item);
+      $project_info[$item] = (string)$value[0];
+    }
+  }
+
+  $recommended_major = @$xml->xpath("/project/recommended_major");
+  $recommended_major = (string)$recommended_major[0];
+  $supported_majors = @$xml->xpath("/project/supported_majors");
+  $supported_majors = array_flip(explode(',', (string)$supported_majors[0]));
+  $releases_xml = @$xml->xpath("/project/releases/release[status='published']");
+  $recommended_version = NULL;
+  foreach ($releases_xml as $release) {
+    $release_info = array();
+    foreach (array('name', 'version', 'tag', 'version_major', 'version_extra', 'status', 'release_link', 'download_link', 'date', 'mdhash', 'filesize') as $item) {
+      if (array_key_exists($item, $release)) {
+        $value = $release->xpath($item);
+        $release_info[$item] = (string)$value[0];
+      }
+    }
+    $statuses = array();
+    if (empty($release_info['version_extra'])) {
+      if (array_key_exists($release_info['version_major'], $supported_majors)) {
+        $statuses[] = "Supported";
+        unset($supported_majors[$release_info['version_major']]);
+      }
+      if ($release_info['version_major'] == $recommended_major) {
+        $statuses[] = "Recommended";
+        $recommended_version = $release_info['version'];
+        $recommended_major = NULL;
+      }
+    }
+    elseif ($release_info['version_extra'] == "dev") {
+      $statuses[] = "Development";
+    }
+    foreach ($release->xpath('terms/term/value') as $release_type) {
+      if (("$release_type" != "Bug fixes") && ("$release_type" != "New features")) {
+        $statuses[] = "$release_type";
+      }
+    }
+    $release_info['release_status'] = $statuses;
+    $releases[$release_info['version']] = $release_info;
+  }
+  
+  $project_info['releases'] = $releases;
+  $project_info['recommended'] = $recommended_version;
+  
+  return $project_info;
+}
+
+/**
+ * Return an array of available releases for given project(s).
+ * Generates content equivalent to the 'releases' array returned by _drush_pm_get_releases.
+ */
+function _drush_pm_releases_from_xml($xml, $project) {
+  // If we are bootstrapped, we will get the release info from _drush_pm_get_releases,
+  // so that we can include information about the installed release.
+  $releases = array();
+  if (drush_get_context('DRUSH_BOOTSTRAP_PHASE') >= DRUSH_BOOTSTRAP_DRUPAL_FULL) {
+    $info = _drush_pm_get_releases_from_update_status(array($project));
+    $releases = $info[$project]['releases'];
+  }
+  // If we are not bootstrapped, then we will extract the release info from
+  // the release-history xml.
+  else {
+    $project_info = _drush_pm_get_releases_from_xml($xml, $project);
+    $releases = $project_info['releases'];
+  }
+  return $releases;
+}
+ 
+function _drush_pm_filter_releases($releases, $all = FALSE, $dev = FALSE) {
+  $options = array();
+  $limits_list = array();
+  foreach ($releases as $version => $release_info) {
+    if (!$dev || ((array_key_exists('version_extra', $release_info)) && ($release_info['version_extra'] == 'dev'))) {
+      $release_status = implode(', ', $release_info['release_status']);
+      $limits_key = $release_info['version_major'] . $release_status;
+      // We keep track of how many releases with the given status exists, and show only the first
+      // two that are exactly the same.  This is mostly to filter out the many releases with
+      // empty release status strings, and gives us a reasonable approximation for the other releases.
+      $limits_list[$limits_key] = (array_key_exists($limits_key, $limits_list) ? $limits_list[$limits_key] : 0) + 1;
+      if ($all || ($limits_list[$limits_key] <= 2)) {
+        $options[$release_info['version']] = $release_info;
+      }
+    }
+  }
+  return $options;
+}
+
+/**
+ * Return an array of available releases for given project(s).
+ *
+ * Helper function for pm-download.
+ */
+function _drush_pm_download_releases_choice($xml, $project, $all = FALSE, $dev = FALSE) {
+  $releases = _drush_pm_filter_releases(_drush_pm_releases_from_xml($xml, $project), $all, $dev);
+  
+  $options = array();
+  foreach($releases as $version => $release) {
+    $options[$version] = array($version, '-', date('Y-M-d', $release['date']), '-', implode(', ', $release['release_status']));
+  }
+  return $options;
+}
+
 /**
  * Get releases for given projects and fill in status information.
  *
@@ -975,6 +1101,15 @@ function drush_pm_releases() {
  * @see _drush_pm_releasenotes()
  */
 function _drush_pm_get_releases($projects) {
+  if (drush_get_context('DRUSH_BOOTSTRAP_PHASE') >= DRUSH_BOOTSTRAP_DRUPAL_FULL) {
+    return _drush_pm_get_releases_from_update_status($projects);
+  }
+  else {
+    return _drush_pm_get_releases_from_release_history($projects);    
+  }
+}
+
+function _drush_pm_get_releases_from_update_status($projects) {
   // We don't provide for other options here, so we supply an explicit path.
   drush_include_engine('update_info', 'drupal', NULL, DRUSH_BASE_PATH . '/commands/pm/update_info');
 
@@ -986,8 +1121,8 @@ function _drush_pm_get_releases($project
       continue;
     }
     if ($info[$project]['project_status'] == 'unpublished') {
-      drush_set_error('DRUSH_PM_PROJECT_UNPUBLISHED', dt("Project !project is unpublished ans has no releases available.", array('!project' => $key)), 'warning');
-      unset($info[$key]);
+      drush_set_error('DRUSH_PM_PROJECT_UNPUBLISHED', dt("Project !project is unpublished and has no releases available.", array('!project' => $project)), 'warning');
+      unset($info[$project]);
       continue;
     }
   }
@@ -1065,6 +1200,17 @@ function _drush_pm_get_releases($project
 }
 
 /**
+ * Implementation of drush_COMMAND_init().
+ *
+ * pm-releasenotes doesn't require any bootstrap level to operate but it 
+ * will use a bootstrapped site if available in order to mark the
+ * installed version of projects.
+ */
+function drush_pm_releasenotes_init() {
+  drush_bootstrap_max();
+}
+
+/**
  * Command callback. Show release notes for given project(s).
  */
 function drush_pm_releasenotes() {
@@ -1090,6 +1236,7 @@ function _drush_pm_releasenotes($request
   $requests = pm_parse_project_version($requests);
   // Get the releases.
   $info = _drush_pm_get_releases(array_keys($requests));
+  drush_print_r($info);
   if (!$info) {
     return drush_log(dt('No valid projects given.'), 'ok');
   }
@@ -1097,7 +1244,7 @@ function _drush_pm_releasenotes($request
   foreach ($info as $key => $project) {
     $selected_versions = array();
     // #TODO# project<->package-name problem. See _drush_pm_get_releases().
-    if (!isset($project['installed'])) {
+    if (!isset($project['installed']) && (drush_get_context('DRUSH_BOOTSTRAP_PHASE') >= DRUSH_BOOTSTRAP_DRUPAL_FULL)) {
       // Installed package's version (e.g. cck) is detected by
       // _drush_pm_get_releases(). Attempted to obtain information
       // about it from update_cache.
@@ -1715,6 +1862,56 @@ function drush_pm_download_validate() {
   }
 }
 
+function _drush_pm_get_releases_from_release_history($requests) {
+  $info = array();
+
+  // Parse out project name and version.
+  $requests = pm_parse_project_version($requests);
+
+  // Get release history for each request and download the project.
+  $project_types = pm_project_types();
+  $project_types_xpath = '(value="' . implode('" or value="', $project_types) . '")';
+  foreach ($requests as $name => $request) {
+    // Cleanup. We need to unset those variables to avoid using garbage from
+    // previous iteration.
+    unset($error, $release, $releases, $types);
+
+    // Don't rely on UPDATE_DEFAULT_URL since perhaps we are not fully
+    // bootstrapped.
+    $url = drush_get_option('source', 'http://updates.drupal.org/release-history') . '/' . $request['name'] . '/' . $request['drupal_version'];
+    drush_log('Downloading release history from ' . $url);
+    // Some hosts have allow_url_fopen disabled.
+    if (!$xml = @simplexml_load_file($url)) {
+      if (!drush_shell_exec("wget $url")) {
+        drush_shell_exec("curl -O $url");
+      }
+      $filename = explode('/', $url);
+      $filename = array_pop($filename);
+      $xml = simplexml_load_file($filename);
+      drush_op('unlink', $filename);
+    }
+    if (!$xml) {
+      // We are not getting here since drupal.org always serves an XML response.
+      drush_set_error('DRUSH_PM_DOWNLOAD_FAILED', dt('Could not download project status information from !url', array('!url' => $url)));
+      continue;
+    }
+    if ($error = $xml->xpath('/error')) {
+      drush_set_error('DRUSH_PM_COULD_NOT_LOAD_UPDATE_FILE', $error[0]);
+      continue;
+    }
+    // Unpublished project?
+    $project_status = $xml->xpath('/project/project_status');
+    if ($project_status[0][0] == 'unpublished') {
+      drush_set_error('DRUSH_PM_PROJECT_UNPUBLISHED', dt("Project !project is unpublished and has no releases available.", array('!project' => $name)), 'warning');
+      continue;
+    }
+
+    $project_info = _drush_pm_get_releases_from_xml($xml, $name);
+    $info[$name] = $project_info;
+  }
+  return $info;
+}
+
 /**
  * Command callback. Download Drupal core or any project.
  */
@@ -1760,6 +1957,12 @@ function drush_pm_download() {
       drush_set_error('DRUSH_PM_COULD_NOT_LOAD_UPDATE_FILE', $error[0]);
       continue;
     }
+    // Unpublished project?
+    $project_status = $xml->xpath('/project/project_status');
+    if ($project_status[0][0] == 'unpublished') {
+      drush_set_error('DRUSH_PM_PROJECT_UNPUBLISHED', dt("Project !project is unpublished and has no releases available.", array('!project' => $name)), 'warning');
+      continue;
+    }
 
     // Try to get the specified release.
     if (!empty($request['version'])) {
@@ -1791,6 +1994,24 @@ function drush_pm_download() {
     if (!empty($recommended_releases)) {
       $releases = $recommended_releases;
     }
+    $release_type = 'recommended';
+    if (drush_get_option('dev', FALSE)) {
+      $releases = @$xml->xpath("/project/releases/release[status='published'][version_extra='dev']");
+      $release_type = 'development';
+    }
+    if (drush_get_option('select', FALSE) || empty($releases)) {
+      $options = _drush_pm_download_releases_choice($xml, $request['name'], drush_get_option('all', FALSE), drush_get_option('dev', FALSE));
+      if (empty($releases)) {
+        drush_print(dt('There is no !type release for project !project.', array('!type' => $release_type, '!project' => $request['name'])));
+      }
+      $choice = drush_choice($options, dt('Choose one of the available releases:'));
+      if ($choice) {
+        $releases = $xml->xpath("/project/releases/release[status='published'][version='" . $choice . "']");
+      }
+      else {
+        continue;
+      }
+    }
     if (empty($releases)) {
       drush_log(dt('There is no *recommended* release for project !project on Drupal !drupal_version. Ask the maintainer to review http://drupal.org/node/197584 and create/recommend a release in order to be compatible with drush and the drupal.org security broadcast system. A recommended development snapshot release is sufficient. Alternatively, run pm-releases command and explicity pm-download any non-recommended release that might be available.', array('!drupal_version' => $request['drupal_version'], '!project' => $request['name'])), 'ok');
       continue;
Index: includes/drush.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/drush/includes/drush.inc,v
retrieving revision 1.153
diff -u -p -r1.153 drush.inc
--- includes/drush.inc	3 Dec 2010 08:11:07 -0000	1.153
+++ includes/drush.inc	4 Dec 2010 19:20:53 -0000
@@ -1215,19 +1215,27 @@ function drush_confirm($msg, $indent = 0
 function drush_choice($options, $prompt = 'Enter a number.', $label = '!value') {
   print dt($prompt) . "\n";
 
-  drush_print('  [0] : Cancel');
+  $rows[] = array('[0]', ':', 'Cancel');
   $selection_number = 0;
   foreach ($options as $key => $option) {
     if ((substr($key, 0, 3) == '-- ') && (substr($key, -3) == ' --')) {
-      drush_print("  ".$option);
-      continue;
+      $rows[] = array('', '', $option);
+    }
+    else {
+      $selection_number++;
+      $row = array("[$selection_number]", ':');
+      if (is_array($option)) {
+        $row = array_merge($row, $option);
+      }
+      else {
+        $row[] = dt($label, array('!number' => $selection_number, '!key' => $key, '!value' => $option));
+      }
+      $rows[] = $row;
+      $selection_list[$selection_number] = $key;
     }
-    $selection_number++;
-    $message = dt($label, array('!number' => $selection_number, '!key' => $key, '!value' => $option));
-    drush_print(dt("  [!number] : !message", array('!number' => $selection_number, '!message' => $message)));
-    $selection_list[$selection_number] = $key;
   }
-
+  drush_print_table($rows);
+  
   while ($line = trim(fgets(STDIN))) {
     if (array_key_exists($line, $selection_list)) {
       return $selection_list[$line];
