diff -u includes/theme.maintenance.inc includes/theme.maintenance.inc
--- includes/theme.maintenance.inc 15 Oct 2009 09:49:31 -0000
+++ includes/theme.maintenance.inc 15 Oct 2009 11:00:32 -0000
@@ -235,12 +235,12 @@
* - message: The log message.
* - success: A boolean indicating failure or success.
*/
-function theme_update_plugin_manager_message($variables) {
+function theme_authorize_message($variables) {
$output = '';
$message = $variables['message'];
$success = $variables['success'];
if ($success) {
- $output .= '
' . check_plain($message) . '';
+ $output .= '' . $message . '';
}
else {
$output .= '' . t('Failed') . ': ' . $message . '';
diff -u includes/updater.inc includes/updater.inc
--- includes/updater.inc 15 Oct 2009 08:54:53 -0000
+++ includes/updater.inc 15 Oct 2009 10:47:36 -0000
@@ -220,6 +220,9 @@
throw new UpdaterException(t('Fatal error in update, cowardly refusing to wipe out the install directory.'));
}
+ // Make sure the installation parent directory exists and is writable.
+ $this->prepareInstallDirectory($filetransfer, $args['install_dir']);
+
// Note: If the project is installed in sites/all, it will not be
// deleted. It will be installed in sites/default as that will override
// the sites/all reference and not break other sites which are using it.
@@ -230,10 +233,14 @@
// Copy the directory in place.
$filetransfer->copyDirectory($this->source, $args['install_dir']);
-// plugin_make_world_executable($filetransfer, $args['install_dir'] . '/' . $this->name);
+
+ // Make sure what we just installed is readable by the web server.
+ $this->makeWorldReadable($filetransfer, $args['install_dir'] . '/' . $this->name);
+
// Run the updates.
// @TODO: decide if we want to implement this.
$this->postUpdate();
+
// For now, just return a list of links of things to do.
return $this->postUpdateTasks();
}
@@ -257,9 +264,16 @@
try {
// Establish arguments with possible overrides.
$args = $this->getInstallArgs($overrides);
+
+ // Make sure the installation parent directory exists and is writable.
+ $this->prepareInstallDirectory($filetransfer, $args['install_dir']);
+
// Copy the directory in place.
$filetransfer->copyDirectory($this->source, $args['install_dir']);
-// plugin_make_world_executable($filetransfer, $args['install_dir'] . '/' . $this->name);
+
+ // Make sure what we just installed is readable by the web server.
+ $this->makeWorldReadable($filetransfer, $args['install_dir'] . '/' . $this->name);
+
// Potentially enable something?
// @TODO: decide if we want to implement this.
$this->postInstall();
@@ -272,6 +286,67 @@
}
/**
+ * Make sure the installation parent directory exists and is writable.
+ *
+ * @param FileTransfer $filetransfer
+ * Object which is a child of FileTransfer.
+ * @param string $directory
+ * The installation directory to prepare.
+ */
+ public function prepareInstallDirectory(&$filetransfer, $directory) {
+ // Make the parent dir writable if need be and create the dir.
+ if (!is_dir($directory)) {
+ $parent_dir = dirname($directory);
+ if (!is_writable($parent_dir)) {
+ @chmod($parent_dir, 0755);
+ // It is expected that this will fail if the directory is owned by the
+ // FTP user. If the FTP user == web server, it will succeed.
+ try {
+ $filetransfer->createDirectory($directory);
+ $this->makeWorldReadable($filetransfer, $directory);
+ }
+ catch (FileTransferException $e) {
+ // Probably still not writable. Try to chmod and do it again.
+ // @todo: Make a new exception class so we can catch it differently.
+ try {
+ $old_perms = substr(sprintf('%o', fileperms($parent_dir)), -4);
+ $filetransfer->chmod($parent_dir, 0755);
+ $filetransfer->createDirectory($directory);
+ $this->makeWorldReadable($filetransfer, $directory);
+ // Put the permissions back.
+ $filetransfer->chmod($parent_dir, intval($old_perms, 8));
+ }
+ catch (FileTransferException $e) {
+ $message = t($e->getMessage(), $e->arguments);
+ $throw_message = t('Unable to create %directory due to the following: %reason', array('%directory' => $install_location, '%reason' => $message));
+ throw new UpdaterException($throw_message);
+ }
+ }
+ // Put the parent directory back.
+ @chmod($parent_dir, 0555);
+ }
+ }
+ }
+
+ /**
+ * Ensure that a given directory is world readable.
+ *
+ * @param FileTransfer $filetransfer
+ * Object which is a child of FileTransfer.
+ * @param string $path
+ * The file path to make world readable.
+ * @param bool $recursive
+ * If the chmod should be applied recursively.
+ */
+ public function makeWorldReadable(&$filetransfer, $path, $recursive = TRUE) {
+ if (!is_executable($path)) {
+ // Set it to read + execute.
+ $new_perms = substr(sprintf('%o', fileperms($path)), -4, -1) . "5";
+ $filetransfer->chmod($path, intval($new_perms, 8), $recursive);
+ }
+ }
+
+ /**
* Perform a backup.
*
* @todo Not implemented.
diff -u modules/update/update.authorize.inc modules/update/update.authorize.inc
--- modules/update/update.authorize.inc 15 Oct 2009 01:20:59 -0000
+++ modules/update/update.authorize.inc 15 Oct 2009 11:22:35 -0000
@@ -43,7 +43,7 @@
'title' => t('Installing updates'),
'init_message' => t('Preparing to update your site'),
'operations' => $operations,
- 'finished' => 'update_authorize_batch_finished',
+ 'finished' => 'update_authorize_update_batch_finished',
'file' => drupal_get_path('module', 'update') . '/update.authorize.inc',
);
@@ -85,7 +85,7 @@
'init_message' => t('Preparing to install'),
'operations' => $operations,
// @todo Use a different finished callback for different messages?
- 'finished' => 'update_authorize_batch_finished',
+ 'finished' => 'update_authorize_install_batch_finished',
'file' => drupal_get_path('module', 'update') . '/update.authorize.inc',
);
batch_set($batch);
@@ -167,24 +167,94 @@
}
/**
- * Batch callback for when the authorized operations batch is finished.
+ * Batch callback for when the authorized update batch is finished.
+ *
+ * This processes the results and stashes them into SESSION such that
+ * authorize.php will render a report. Also responsible for putting the site
+ * back online and clearing the update status cache after a successful update.
*/
-function update_authorize_batch_finished($success, $results) {
+function update_authorize_update_batch_finished($success, $results) {
foreach ($results['log'] as $project => $messages) {
if (!empty($messages['#abort'])) {
$success = FALSE;
}
}
+ $offline = variable_get('site_offline', FALSE);
if ($success) {
+ // Now that the update completed, we need to clear the cache of available
+ // update data and recompute our status, so prevent show bogus results.
+ _update_authorize_clear_update_status();
+
+ if ($offline) {
+ variable_set('site_offline', FALSE);
+ $page_message = array(
+ 'message' => t('Update was completed successfully. Your site has been taken out of maintenance mode.'),
+ 'type' => 'status',
+ );
+ }
+ else {
+ $page_message = array(
+ 'message' => t('Update was completed successfully.'),
+ 'type' => 'status',
+ );
+ }
+ }
+ elseif (!$offline) {
+ $page_message = array(
+ 'message' => t('Update failed! See the log below for more information.'),
+ 'type' => 'error',
+ );
+ }
+ else {
+ $page_message = array(
+ 'message' => t('Update failed! See the log below for more information. Your site is still in maintenance mode.'),
+ 'type' => 'error',
+ );
+ }
+
+ // Set all these values into the SESSION so authorize.php can display them.
+ $_SESSION['authorize_results']['success'] = $success;
+ $_SESSION['authorize_results']['page_message'] = $page_message;
+ $_SESSION['authorize_results']['messages'] = $results['log'];
+ $_SESSION['authorize_results']['tasks'] = $results['tasks'];
+}
+
+/**
+ * Batch callback for when the authorized install batch is finished.
+ *
+ * This processes the results and stashes them into SESSION such that
+ * authorize.php will render a report. Also responsible for putting the site
+ * back online after a successful install if necessary.
+ */
+function update_authorize_install_batch_finished($success, $results) {
+ foreach ($results['log'] as $project => $messages) {
+ if (!empty($messages['#abort'])) {
+ $success = FALSE;
+ }
+ }
+ $offline = variable_get('site_offline', FALSE);
+ if ($success && $offline) {
variable_set('site_offline', FALSE);
$page_message = array(
- 'message' => t('Update was completed successfully! Your site has been taken out of maintenance mode.'),
+ 'message' => t('Installation was completed successfully. Your site has been taken out of maintenance mode.'),
+ 'type' => 'status',
+ );
+ }
+ elseif ($success && !$offline) {
+ $page_message = array(
+ 'message' => t('Installation was completed successfully.'),
'type' => 'status',
);
}
+ elseif (!$success && !$offline) {
+ $page_message = array(
+ 'message' => t('Installation failed! See the log below for more information.'),
+ 'type' => 'error',
+ );
+ }
else {
$page_message = array(
- 'message' => t('Update failed! See the log below for more information. Your site is still in maintenance mode.'),
+ 'message' => t('Installation failed! See the log below for more information. Your site is still in maintenance mode.'),
'type' => 'error',
);
}
@@ -209,0 +280,23 @@
+/**
+ * Private helper function to clear cached available update status data.
+ *
+ * Since this function is run at such a low bootstrap level, update.module is
+ * not loaded. So, we can't just call _update_cache_clear(). However, the
+ * database is bootstrapped, so we can do a query ourselves to clear out what
+ * we want to clear.
+ *
+ * Note that we do not want to just truncate the table, since that would
+ * remove items related to currently pending fetch attempts.
+ *
+ * @see update_authorize_update_batch_finished()
+ * @see _update_cache_clear()
+ */
+function _update_authorize_clear_update_status() {
+ $query = db_delete('cache_update');
+ $query->condition(
+ db_or()
+ ->condition('cid', 'update_project_%', 'LIKE')
+ ->condition('cid', 'available_releases::%', 'LIKE')
+ );
+ $query->execute();
+}