? 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	16 Mar 2010 23:03:44 -0000
@@ -836,6 +836,14 @@ function drush_pm_update() {
   $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.
+  if (drush_get_context('DRUSH_PM_CORE_UPDATE_AVAILABLE', FALSE)) {
+    call_user_func_array('drush_invoke', array('pm-updatecode', 'drupal'));
+  }
 }
 
 /**
@@ -845,6 +853,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 +877,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	16 Mar 2010 23:03:44 -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. \nIn order to update Drupal core with this tool, first disable all of the modules listed above and then run pm-updatecode again.\n"));
+      drush_print(dt("e.g.:\n"));
+      drush_print(dt("  drush pm-disable !modules", array("!modules" => implode(' ',$module_list))));
+      drush_print(dt("  drush pm-updatecode"));
+      drush_print(dt("  drush updatedb"));
+      drush_print(dt("  drush pm-enable !modules\n", array("!modules" => implode(' ',$module_list))));
+      drush_print(dt("\nAlternately, run 'pm-updatecode drupal' to upgrade Drupal core only."));
+      drush_set_context('DRUSH_PM_CORE_UPDATE_AVAILABLE', TRUE);
+    }
   }
 
   if (empty($updateable)) {
@@ -102,6 +128,78 @@ 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');
+  
+  drush_print(dt('Code updates will be made to drupal core.'));
+  drush_print(dt("Note: Updating core can potentially break your site. It is NOT recommended to update production sites without prior testing."));
+  if (!empty($module_list)) {
+    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 there are any non-core modules enabled, disable them
+  if (!empty($module_list)) {
+    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('.htaccess', 'robots.txt', '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 +225,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 +232,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'])) {
+    // Run update on one project
+    $version_control = pm_update_project($project);
+    if ($update_status === FALSE) {
       return FALSE;
     }
-    if (!$version_control->pre_update($project)) {
-      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 +294,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	16 Mar 2010 23:03:44 -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
