? notthere.php
? 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.97
diff -u -p -r1.97 pm.drush.inc
--- commands/pm/pm.drush.inc	9 Mar 2010 02:07:01 -0000	1.97
+++ commands/pm/pm.drush.inc	17 Mar 2010 20:38:08 -0000
@@ -833,9 +833,23 @@ function drush_pm_refresh() {
  * Command callback. Execute updatecode.
  */
 function drush_pm_update() {
+  // Signal that we will update drush core after the drush modules
+  // are updated, if an update to core is available.
+  drush_set_context('DRUSH_PM_UPDATE_ALL', TRUE);
+  
+  // Call pm-updatecode.  updatedb will be called in the post-update process.
   $args = func_get_args();
   array_unshift($args, 'pm-updatecode');
   call_user_func_array('drush_invoke', $args);
+  
+  // pm-updatecode will not do a core update of Drupal
+  // on the same invocation where non-core modules are
+  // updated.  If there is a core update available, then
+  // call pm-updatecode a second time to update core
+  // (but only if the first run finished successfully).
+  if (drush_get_context('DRUSH_PM_CORE_UPDATE_AVAILABLE', FALSE) && (drush_get_error() == DRUSH_SUCCESS)) {
+    call_user_func_array('drush_invoke', array('pm-updatecode', 'drupal'));
+  }
 }
 
 /**
@@ -845,6 +859,7 @@ function drush_pm_update() {
 function drush_pm_post_pm_update() {
   // Use drush_backend_invoke to start a subprocess. Cleaner that way.
   drush_backend_invoke('updatedb');
+  _drush_pm_reenable_modules();
 }
 
 /**
@@ -868,6 +883,9 @@ function drush_pm_post_pm_updatecode() {
       }
     }
   }
+  
+  // If we automatically disabled modules, warn the user
+  _drush_pm_disabled_modules_warning();
 }
 
 
Index: commands/pm/updatecode.pm.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/drush/commands/pm/updatecode.pm.inc,v
retrieving revision 1.6
diff -u -p -r1.6 updatecode.pm.inc
--- commands/pm/updatecode.pm.inc	6 Mar 2010 13:58:14 -0000	1.6
+++ commands/pm/updatecode.pm.inc	17 Mar 2010 20:38:08 -0000
@@ -89,8 +89,34 @@ function drush_pm_updatecode() {
   }
 
   if (isset($updateable['drupal'])) {
-    drush_print("NOTE: A code update for the Drupal core is available. \nDrupal itself can't yet be updated by this tool. Please update Drupal manually.\n");
+    $drupal_project = $updateable['drupal'];
+    unset($projects['drupal']);
     unset($updateable['drupal']);
+    $module_list = array_keys($projects);      
+    
+    // We can only upgrade drupal core if there are no non-core
+    // modules enabled.  _pm_update_core will disable the
+    // modules passed in, and insure that they are enabled again
+    // when we're done.  However, each run of pm-updatecode will
+    // update -either- core, or the non-core modules; never both.
+    // This simplifies rollbacks.
+    if (empty($updateable)) {
+      return _pm_update_core($drupal_project, $module_list);
+    }
+    // If there are modules other than drupal core enabled, then go
+    // ahead and print instructions on how to upgrade core.  We will
+    // also continue, allowing the user to upgrade any upgradable
+    // modules if desired.
+    else {
+      drush_print(dt("NOTE: A code update for the Drupal core is available."));
+      if (drush_get_context('DRUSH_PM_UPDATE_ALL', FALSE)) {
+        drush_print(dt("Drupal core will be updated after all of the non-core modules are updated.\n"));
+      }
+      else {
+        drush_print(dt("Drupal core cannot be updated at the same time that non-core modules are updated.  In order to update Drupal core with this tool, first allow the update of the modules listed above to complet, and then run pm-updatecode again.\n"));
+      }
+      drush_set_context('DRUSH_PM_CORE_UPDATE_AVAILABLE', TRUE);
+    }
   }
 
   if (empty($updateable)) {
@@ -102,6 +128,82 @@ function drush_pm_updatecode() {
 }
 
 /**
+ * Update drupal core, following interactive confirmation from the user.
+ *
+ * @param $project
+ *   The drupal project information from the drupal.org update service,
+ *   copied from $projects['drupal'].  @see drush_pm_updatecode.
+ * @param $module_list
+ *   A list of the non-core modules that are enabled.  These must be disabled
+ *   before core can be updated.
+ */
+function _pm_update_core($project, $module_list = array()) {
+  drush_include_engine('package_handler', drush_get_option('package-handler', 'wget'));
+  $drupal_root = drush_get_context('DRUSH_DRUPAL_ROOT');
+  $disable_modules = (!empty($module_list) && drush_get_option('disable-modules', FALSE));
+  
+  drush_print(dt('Code updates will be made to drupal core.'));
+  drush_print(dt("WARNING:  Updating core will discard any modifications made to Drupal core files, most noteworthy among these are .htaccess and robots.txt.  If you have made any modifications to these files, please back them up before updating so that you can re-create your modifications in the updated version of the file."));
+  drush_print(dt("Note: Updating core can potentially break your site. It is NOT recommended to update production sites without prior testing."));
+  if ($disable_modules) {
+    drush_print(dt("Prior to updating core, the following modules will be disabled: !modules", array("!modules" => implode(' ',$module_list))));
+    drush_print(dt("They will be re-enabled after the update is complete."));
+  }  
+  if(!drush_confirm(dt('Do you really want to continue?'))) {
+    drush_die('Aborting.');
+  }
+  
+  // If the '--disable-modules' flag is set, and if there are 
+  // any non-core modules enabled, disable them.  They will be
+  // re-enabled at the end of the update.
+  if ($disable_modules) {
+    drush_set_context('DRUSH_PM_DISABLED_MODULES', $module_list);
+    drush_backend_invoke_args('pm-disable', $module_list, array('y' => TRUE));
+  }
+  
+  // Create a directory 'core' if it does not already exist
+  $project['path'] = 'drupal-' . $project['candidate_version'];
+  $project['full_project_path'] = $drupal_root . '/' . $project['path'];
+  mkdir($project['full_project_path']);
+  
+  // Create a list of files and folders that are user-customized or otherwise
+  // not part of the update process
+  $project['skip_list'] = array('backup', 'sites', $project['path']);
+  
+  // Move all files and folders in $drupal_root to the new 'core' directory
+  // except for the items in the skip list
+  _pm_update_move_files($drupal_root, $project['full_project_path'], $project['skip_list']);
+  
+  // Update core. 
+  $version_control = pm_update_project($project);
+  if ($version_control === FALSE) {
+    return FALSE;
+  }
+  
+  // Take the updated files in the 'core' directory that have been updated,
+  // and move all except for the items in the skip list back to
+  // the drupal root
+  _pm_update_move_files($project['full_project_path'], $drupal_root, $project['skip_list']);
+  drush_delete_dir($project['full_project_path']);
+  
+  pm_update_complete($project, $version_control);
+  
+  return TRUE;
+}
+
+/**
+ * Move some files from one location to another
+ */
+function _pm_update_move_files($src_dir, $dest_dir, $skip_list) {
+  $items_to_move = drush_scan_directory($src_dir, '/.*/', array_merge(array('.', '..', 'CVS'), $skip_list), 0, FALSE);
+  foreach ($items_to_move as $filename => $info) {
+    drush_delete_dir($dest_dir . '/' . basename($filename));
+    $move_result = rename($filename,  $dest_dir . '/' . basename($filename));
+  }
+  return TRUE;
+}
+
+/**
  * Update packages according to an array of releases, following interactive
  * confirmation from the user.
  *
@@ -127,7 +229,6 @@ function pm_update_packages($projects) {
   }
 
   // Now we start the actual updating.
-  $updated = array();
   foreach($projects as $project) {
     if (empty($project['path'])) {
       return drush_set_error('DRUSH_PM_UPDATING_NO_PROJECT_PATH', dt('The !project project path is not available, perhaps the !type is enabled but has been deleted from disk.', array('!project' => $project['name'], '!type' => $project['project_type'])));
@@ -135,36 +236,58 @@ function pm_update_packages($projects) {
     drush_log(dt('Starting to update !project code at !dir...', array('!project' => $project['title'], '!dir' => $project['path'])));
     // Create the projects directory and base (parent) directory.
     $project['full_project_path'] = $drupal_root . '/' . $project['path'];
-    $project['base_project_path'] = dirname($project['full_project_path']);
     // Check that the directory exists, and is where we expect it to be.
     if (stripos($project['path'], $project['project_type']) === FALSE || !is_dir($project['full_project_path'])) {
       return drush_set_error('DRUSH_PM_UPDATING_PATH_NOT_FOUND', dt('The !project directory could not be found within the !types directory at !full_project_path, perhaps the project is enabled but has been deleted from disk.', array('!project' => $project['name'], '!type' => $project['project_type'], '!full_project_path' => $project['full_project_path'])));
     }
-
-    // Check we have a version control system, and it clears it's pre-flight.
-    if (!$version_control = drush_pm_include_version_control($project['full_project_path'])) {
-      return FALSE;
-    }
-    if (!$version_control->pre_update($project)) {
+    // Run update on one project
+    $version_control = pm_update_project($project);
+    if ($update_status === FALSE) {
       return FALSE;
     }
-    // Add the project to a context so we can roll back if needed.
-    $updated[] = $project;
-    drush_set_context('DRUSH_PM_UPDATED', $updated);
-
-    if (!package_handler_update_project($project, $project['releases'][$project['candidate_version']])) {
-      return drush_set_error('DRUSH_PM_UPDATING_FAILED', dt('Updating project !project failed. Attempting to roll back to previously installed version.', array('!project' => $project['name'])));
-    }
-    else {
-      drush_print(dt('Project !project was updated successfully. Installed version is now !version.', array('!project' => $project['name'], '!version' => $project['candidate_version'])));
-      drush_command_invoke_all('pm_post_update', $project['name'], $project['releases'][$project['candidate_version']]);
-      $version_control->post_update($project);
-    }
+    pm_update_complete($project, $version_control);
   }
   // Clear the cache, since some projects could have moved around.
   drush_drupal_cache_clear_all();
 }
 
+/**
+ * Update one project -- a module, theme or Drupal core
+ *
+ * @param $project
+ *   The project to upgrade.  $project['full_project_path'] must be set
+ *   to the location where this project is stored.
+ */
+function pm_update_project($project) {
+  $project['base_project_path'] = dirname($project['full_project_path']);
+  // Check we have a version control system, and it clears it's pre-flight.
+  if (!$version_control = drush_pm_include_version_control($project['full_project_path'])) {
+    return FALSE;
+  }
+  if (!$version_control->pre_update($project)) {
+    return FALSE;
+  }
+  // Add the project to a context so we can roll back if needed.
+  $updated = drush_get_context('DRUSH_PM_UPDATED');
+  $updated[] = $project;
+  drush_set_context('DRUSH_PM_UPDATED', $updated);
+
+  if (!package_handler_update_project($project, $project['releases'][$project['candidate_version']])) {
+    return drush_set_error('DRUSH_PM_UPDATING_FAILED', dt('Updating project !project failed. Attempting to roll back to previously installed version.', array('!project' => $project['name'])));
+  }
+  
+  return $version_control;
+}
+
+/**
+ * Run the post-update hooks after updatecode is complete for one project.
+ */
+function pm_update_complete($project, $version_control) {
+  drush_print(dt('Project !project was updated successfully. Installed version is now !version.', array('!project' => $project['name'], '!version' => $project['candidate_version'])));
+  drush_command_invoke_all('pm_post_update', $project['name'], $project['releases'][$project['candidate_version']]);
+  $version_control->post_update($project);
+}
+
 function drush_pm_updatecode_rollback() {
   $projects = array_reverse(drush_get_context('DRUSH_PM_UPDATED', array()));
   foreach($projects as $project) {
@@ -175,6 +298,30 @@ function drush_pm_updatecode_rollback() 
       return FALSE;
     }
     $version_control->rollback($project);
+    
+    // Post rollback, we will do additional repair if the project is drupal core.
+    if ($project['name'] == 'drupal') {
+      $drupal_root = drush_get_context('DRUSH_DRUPAL_ROOT');
+      _pm_update_move_files($project['full_project_path'], $drupal_root, $project['skip_list']);
+      drush_delete_dir($project['full_project_path']);
+    }
+  }
+  _drush_pm_reenable_modules();
+}
+
+function _drush_pm_reenable_modules() {
+  $modules = drush_get_context('DRUSH_PM_DISABLED_MODULES', array());
+  if (!empty($modules)) {
+    drush_backend_invoke_args('pm-enable', $modules, array('y' => TRUE));
+  }
+}
+
+function _drush_pm_disabled_modules_warning() {
+  $modules = drush_get_context('DRUSH_PM_DISABLED_MODULES', array());
+  if (!empty($modules)) {
+    drush_print(dt("n.b.: 'pm-updatecode drupal' disabled some modules.  Re-enable them after running updatedb:"));
+    drush_print(dt("  drush updatedb")); 
+    drush_print(dt("  drush pm-enable !modules\n", array("!modules" => implode(' ',$modules))));
   }
 }
 
Index: includes/drush.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/drush/includes/drush.inc,v
retrieving revision 1.96
diff -u -p -r1.96 drush.inc
--- includes/drush.inc	6 Mar 2010 12:46:30 -0000	1.96
+++ includes/drush.inc	17 Mar 2010 20:38:08 -0000
@@ -472,6 +472,28 @@ function _drush_delete_registered_files(
 }
 
 /**
+ * Deletes the provided file or folder and
+ * everything inside it.
+ */
+function drush_delete_dir($dir) {
+  if (!file_exists($dir)) {
+    return TRUE;
+  }
+  if (!is_dir($dir)) {
+    return unlink($dir);
+  }
+  foreach (scandir($dir) as $item) {
+    if ($item == '.' || $item == '..') {
+      continue;
+    }
+    if (!drush_delete_dir($dir.DIRECTORY_SEPARATOR.$item)) {
+      return FALSE;
+    }
+  }
+  return rmdir($dir);
+}
+
+/**
  * Calls a given function, passing through all arguments unchanged.
  *
  * This should be used when calling possibly mutative or destructive functions
