Index: update.php
===================================================================
RCS file: /cvs/drupal/drupal/update.php,v
retrieving revision 1.292
diff -u -p -r1.292 update.php
--- update.php 14 Jul 2009 10:22:15 -0000 1.292
+++ update.php 20 Jul 2009 14:15:44 -0000
@@ -2,11 +2,6 @@
// $Id: update.php,v 1.292 2009/07/14 10:22:15 dries Exp $
/**
- * Root directory of Drupal installation.
- */
-define('DRUPAL_ROOT', getcwd());
-
-/**
* @file
* Administrative page for handling updates from one Drupal version to another.
*
@@ -382,24 +377,7 @@ function update_results_page() {
$output .= '
The following queries were executed
';
foreach ($_SESSION['update_results'] as $module => $updates) {
$output .= '' . $module . ' module
';
- foreach ($updates as $number => $queries) {
- if ($number != '#abort') {
- $output .= 'Update #' . $number . '
';
- $output .= '';
- foreach ($queries as $query) {
- if ($query['success']) {
- $output .= '- ' . $query['query'] . '
';
- }
- else {
- $output .= '- Failed: ' . $query['query'] . '
';
- }
- }
- if (!count($queries)) {
- $output .= '- No queries
';
- }
- }
- $output .= '
';
- }
+ $output .= _update_results_list($updates);
}
$output .= '';
}
@@ -409,6 +387,36 @@ function update_results_page() {
return $output;
}
+/**
+ * Helper function to render a list of updates.
+ *
+ * @param array $updates a list of updates
+ * @return string
+ */
+function _update_results_list($updates) {
+ $output = '';
+ foreach ($updates as $number => $queries) {
+ if ($number != '#abort') {
+ $output .= 'Update #' . $number . '
';
+ $output .= '';
+ foreach ($queries as $query) {
+ if ($query['success']) {
+ $output .= '- ' . $query['query'] . '
';
+ }
+ else {
+ $output .= '- Failed: ' . $query['query'] . '
';
+ }
+ }
+ if (!count($queries)) {
+ $output .= '- No queries
';
+ }
+ }
+ $output .= '
';
+ }
+
+ return $output;
+}
+
function update_info_page() {
// Change query-strings on css/js files to enforce reload for all users.
_drupal_flush_css_js();
@@ -651,102 +659,118 @@ function update_check_requirements() {
}
}
-// Some unavoidable errors happen because the database is not yet up-to-date.
-// Our custom error handler is not yet installed, so we just suppress them.
-ini_set('display_errors', FALSE);
-
-// We prepare a minimal bootstrap for the update requirements check to avoid
-// reaching the PHP memory limit.
-require_once DRUPAL_ROOT . '/includes/bootstrap.inc';
-update_prepare_d7_bootstrap();
-
-// Determine if the current user has access to run update.php.
-drupal_bootstrap(DRUPAL_BOOTSTRAP_SESSION);
-$update_access_allowed = !empty($update_free_access) || $user->uid == 1;
-
-// Only allow the requirements check to proceed if the current user has access
-// to run updates (since it may expose sensitive information about the site's
-// configuration).
-$op = isset($_REQUEST['op']) ? $_REQUEST['op'] : '';
-if (empty($op) && $update_access_allowed) {
- require_once DRUPAL_ROOT . '/includes/install.inc';
- require_once DRUPAL_ROOT . '/includes/file.inc';
- require_once DRUPAL_ROOT . '/modules/system/system.install';
-
- // Load module basics.
- include_once DRUPAL_ROOT . '/includes/module.inc';
- $module_list['system']['filename'] = 'modules/system/system.module';
- $module_list['filter']['filename'] = 'modules/filter/filter.module';
- module_list(TRUE, FALSE, $module_list);
- drupal_load('module', 'system');
- drupal_load('module', 'filter');
-
- // Set up $language, since the installer components require it.
- drupal_language_initialize();
-
- // Set up theme system for the maintenance page.
- drupal_maintenance_theme();
-
- // Check the update requirements for Drupal.
- update_check_requirements();
-
- // Redirect to the update information page if all requirements were met.
- install_goto('update.php?op=info');
-}
-
-drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
-drupal_maintenance_theme();
-
-// Turn error reporting back on. From now on, only fatal errors (which are
-// not passed through the error handler) will cause a message to be printed.
-ini_set('display_errors', TRUE);
-
-// Only proceed with updates if the user is allowed to run them.
-if ($update_access_allowed) {
-
- include_once DRUPAL_ROOT . '/includes/install.inc';
- include_once DRUPAL_ROOT . '/includes/batch.inc';
- drupal_load_updates();
-
- update_fix_d7_requirements();
- update_fix_compatibility();
+/**
+ * Main update function: decides which forms to show and shows them.
+ */
+function update_run_updates() {
+ global $user, $update_free_access;
+ // Some unavoidable errors happen because the database is not yet up-to-date.
+ // Our custom error handler is not yet installed, so we just suppress them.
+ ini_set('display_errors', FALSE);
+
+ // We prepare a minimal bootstrap for the update requirements check to avoid
+ // reaching the PHP memory limit.
+ require_once DRUPAL_ROOT . '/includes/bootstrap.inc';
+ update_prepare_d7_bootstrap();
+
+ // Determine if the current user has access to run update.php.
+ drupal_bootstrap(DRUPAL_BOOTSTRAP_SESSION);
+ $update_access_allowed = !empty($update_free_access) || $user->uid == 1;
+
+ // Only allow the requirements check to proceed if the current user has access
+ // to run updates (since it may expose sensitive information about the site's
+ // configuration).
$op = isset($_REQUEST['op']) ? $_REQUEST['op'] : '';
- switch ($op) {
- // update.php ops
-
- case 'selection':
- if (isset($_GET['token']) && $_GET['token'] == drupal_get_token('update')) {
- $output = update_selection_page();
+ if (empty($op) && $update_access_allowed) {
+ require_once DRUPAL_ROOT . '/includes/install.inc';
+ require_once DRUPAL_ROOT . '/includes/file.inc';
+ require_once DRUPAL_ROOT . '/modules/system/system.install';
+
+ // Load module basics.
+ include_once DRUPAL_ROOT . '/includes/module.inc';
+ $module_list['system']['filename'] = 'modules/system/system.module';
+ $module_list['filter']['filename'] = 'modules/filter/filter.module';
+ module_list(TRUE, FALSE, $module_list);
+ drupal_load('module', 'system');
+ drupal_load('module', 'filter');
+
+ // Set up $language, since the installer components require it.
+ drupal_language_initialize();
+
+ // Set up theme system for the maintenance page.
+ drupal_maintenance_theme();
+
+ // Check the update requirements for Drupal.
+ update_check_requirements();
+
+ // Redirect to the update information page if all requirements were met.
+ install_goto('update.php?op=info');
+ }
+
+ drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
+ drupal_maintenance_theme();
+
+ // Turn error reporting back on. From now on, only fatal errors (which are
+ // not passed through the error handler) will cause a message to be printed.
+ ini_set('display_errors', TRUE);
+
+ // Only proceed with updates if the user is allowed to run them.
+ if ($update_access_allowed) {
+
+ include_once DRUPAL_ROOT . '/includes/install.inc';
+ include_once DRUPAL_ROOT . '/includes/batch.inc';
+ drupal_load_updates();
+
+ update_fix_d7_requirements();
+ update_fix_compatibility();
+
+ $op = isset($_REQUEST['op']) ? $_REQUEST['op'] : '';
+ switch ($op) {
+ // update.php ops
+
+ case 'selection':
+ if (isset($_GET['token']) && $_GET['token'] == drupal_get_token('update')) {
+ $output = update_selection_page();
+ break;
+ }
+
+ case 'Apply pending updates':
+ if (isset($_GET['token']) && $_GET['token'] == drupal_get_token('update')) {
+ update_batch();
+ break;
+ }
+
+ case 'info':
+ $output = update_info_page();
break;
- }
-
- case 'Apply pending updates':
- if (isset($_GET['token']) && $_GET['token'] == drupal_get_token('update')) {
- update_batch();
+
+ case 'results':
+ $output = update_results_page();
break;
- }
-
- case 'info':
- $output = update_info_page();
- break;
-
- case 'results':
- $output = update_results_page();
- break;
-
- // Regular batch ops : defer to batch processing API
- default:
- update_task_list('run');
- $output = _batch_page();
- break;
- }
-}
-else {
- $output = update_access_denied_page();
-}
-if (isset($output) && $output) {
- // We defer the display of messages until all updates are done.
- $progress_page = ($batch = batch_get()) && isset($batch['running']);
- print theme('update_page', $output, !$progress_page);
+
+ // Regular batch ops : defer to batch processing API
+ default:
+ update_task_list('run');
+ $output = _batch_page();
+ break;
+ }
+ }
+ else {
+ $output = update_access_denied_page();
+ }
+ if (isset($output) && $output) {
+ // We defer the display of messages until all updates are done.
+ $progress_page = ($batch = batch_get()) && isset($batch['running']);
+ print theme('update_page', $output, !$progress_page);
+ }
}
+
+if (realpath($_SERVER['SCRIPT_FILENAME']) == __FILE__) {
+ /**
+ * Root directory of Drupal installation.
+ */
+ define('DRUPAL_ROOT', getcwd());
+
+ update_run_updates();
+}
\ No newline at end of file
Index: modules/system/system.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.module,v
retrieving revision 1.725
diff -u -p -r1.725 system.module
--- modules/system/system.module 19 Jul 2009 13:42:50 -0000 1.725
+++ modules/system/system.module 20 Jul 2009 14:15:47 -0000
@@ -1279,27 +1279,28 @@ function system_filetransfer_backend_for
function _system_filetransfer_backend_form_common() {
$form = array();
- $form['hostname'] = array (
+ $form['hostname'] = array(
'#type' => 'textfield',
'#title' => t('Host'),
'#default_value' => 'localhost',
);
-
- $form['port'] = array (
+
+ $form['port'] = array(
'#type' => 'textfield',
'#title' => t('Port'),
'#default_value' => NULL,
);
-
- $form['username'] = array (
+
+ $form['username'] = array(
'#type' => 'textfield',
'#title' => t('Username'),
);
-
- $form['password'] = array (
+
+ $form['password'] = array(
'#type' => 'password',
'#title' => t('Password'),
'#description' => t('This is not saved in the database and is only used to test the connection'),
+ '#filetransfer_save' => FALSE,
);
return $form;
Index: modules/update/update.admin.inc
===================================================================
RCS file: modules/update/update.admin.inc
diff -N modules/update/update.admin.inc
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ modules/update/update.admin.inc 20 Jul 2009 14:15:47 -0000
@@ -0,0 +1,447 @@
+ $messages) {
+ $output .= '' . $project . '
';
+ $output .=_update_results_list($messages);
+ }
+
+ $form['log'] = array(
+ '#type' => 'item',
+ '#title' => '' . t('Update log') . "
",
+ '#markup' => $output,
+ '#prefix' => '',
+ '#suffix' => '
',
+ );
+ return $form;
+ }
+
+ if (!isset($form_state['values'])) {
+ $form['#theme'] = 'update_available_updates_form';
+ $form['projects'] = array();
+ // First step.
+ $options = array();
+ if ($available = update_get_available(TRUE)) {
+ module_load_include('inc', 'update', 'update.compare');
+ $project_data = update_calculate_project_data($available);
+ foreach ($project_data as $name => $project) {
+ // Filter out projects which are dev versions, updated or core
+ if (($project['install_type'] != 'dev') && ($project['project_type'] != 'core') && $project['status'] != UPDATE_CURRENT) {
+ $options[$name]['title'] = l($project['title'], $project['link']);
+ if ($project['project_type'] == 'theme') {
+ $options[$name]['title'] .= t(' (Theme)');
+ }
+ $options[$name]['installed_version'] = $project['existing_version'];
+ $options[$name]['recommended_version'] = $project['recommended'] . ' ' . l(t('(Release notes)'), $project['releases'][$project['recommended']]['release_link'], array('attributes' => array('title' => t('Release notes for @project_name', array('@project_name' => $project['title'])))));
+
+ switch ($project['status']) {
+ case UPDATE_NOT_SECURE:
+ case UPDATE_REVOKED:
+ $options[$name]['title'] .= t(' (Security Update)');
+ $options[$name]['#weight'] = -2;
+ $type = 'security';
+ break;
+ case UPDATE_NOT_SUPPORTED:
+ $type = 'unsupported';
+ $options[$name]['title'] .= t(' (Unsupported)');
+ $options[$name]['#weight'] = -1;
+ break;
+ case UPDATE_UNKNOWN:
+ case UPDATE_NOT_FETCHED:
+ case UPDATE_NOT_CHECKED:
+ case UPDATE_NOT_CURRENT:
+ $type = 'recommended';
+ break;
+ default:
+ // Continues out of the switch and then out of the foreach.
+ continue 2;
+ }
+ $options[$name]['#attributes'] = array('class' => 'update-' . $type);
+ }
+ }
+ }
+ else {
+ $form['message'] = array(
+ '#markup' => t('There was a problem getting update information. Please try again later.'),
+ );
+ return $form;
+ }
+
+ if (!count($options)) {
+ $form['message'] = array(
+ '#markup' => t('All of your projects are up to date.'),
+ );
+ return $form;
+ }
+ $form['projects'] = array(
+ '#type' => 'tableselect',
+ '#options' => $options,
+ '#header' => array('title' => array('data' => t('Name'), 'class' => 'update-project-name'), 'installed_version' => t('Installed version'), 'recommended_version' => t('Recommended version')),
+ );
+
+ $form['#attached_css'][] = drupal_get_path('module', 'update') . '/update.css';
+ $form['submit'] = array(
+ '#type' => 'submit',
+ '#value' => t('Install these updates'),
+ '#weight' => 100,
+ );
+ }
+ else if (is_array($form_state['values']) && array_filter($form_state['values']['projects'])) {
+ $form['#attached_js'][] = drupal_get_path('module', 'update') . '/update.js';
+ // Get all the available ways to transfer files.
+ $available_backends = module_invoke_all('filetransfer_backends');
+ if (!count($available_backends)) {
+ // @TODO: Clean up this error handling
+ drupal_set_message(t('Unable to continue, not available methods of file transfer'), 'error');
+ return array();
+ }
+
+ $form['information']['#weight'] = -100;
+ $form['information']['backup_header'] = array(
+ '#prefix' => '',
+ '#markup' => t('Step 1: Backup your site'),
+ '#suffix' => '
',
+ );
+
+ $form['information']['backup_message'] = array(
+ '#prefix' => '',
+ '#markup' => t('We do not currently have a web based backup tool. Learn more about how to take a backup.', array('@backup_url' => url('http://drupal.org/node/22281'))),
+ '#suffix' => '
',
+ );
+
+ $form['information']['maint_header'] = array(
+ '#prefix' => '',
+ '#markup' => t('Step 2: Put your site into maintenance mode'),
+ '#suffix' => '
',
+ );
+
+ $form['information']['maint_message'] = array(
+ '#prefix' => '',
+ '#markup' => t('It is strongly recommended that you put your site into maintenance mode while performing an update.'),
+ '#suffix' => '
',
+ );
+
+ $form['information']['site_offline'] = array(
+ '#title' => t('Perform updates with site in maintenance mode'),
+ '#type' => 'checkbox',
+ '#default_value' => TRUE,
+ );
+
+ $form['information']['main_header'] = array(
+ '#prefix' => '',
+ '#markup' => t('Step 3: Provide your server connection details'),
+ '#suffix' => '
',
+ );
+
+ uasort($available_backends, 'drupal_sort_weight');
+
+ // Decide on a default backend.
+ if (isset($form_state['values']['connection_settings']['update_filetransfer_default'])) {
+ $update_filetransfer_default = $form_state['values']['connection_settings']['update_filetransfer_default'];
+ }
+ elseif ($update_filetransfer_default = variable_get('update_filetransfer_default', NULL));
+ else {
+ $update_filetransfer_default = key($available_backends);
+ }
+
+ $form['connection_settings']['#tree'] = TRUE;
+ $form['connection_settings']['update_filetransfer_default'] = array(
+ '#type' => 'select',
+ '#title' => t('Connection method'),
+ '#default_value' => array($update_filetransfer_default),
+ );
+
+ // Build a hidden fieldset for each one.
+ foreach ($available_backends as $name => $backend) {
+ $form['connection_settings']['update_filetransfer_default']['#options'][$name] = $backend['title'];
+ $form['connection_settings'][$name] = array (
+ '#type' => 'fieldset',
+ '#attributes' => array('class' => "filetransfer-$name filetransfer"),
+ '#title' => t('@backend connection settings', array('@backend' => $backend['title'])),
+ );
+ $current_settings = variable_get("update_filetransfer_connection_settings_" . $name, array());
+ $form['connection_settings'][$name] += system_get_filetransfer_settings_form($name, $current_settings);
+ }
+
+ $form['submit'] = array(
+ '#name' => 'process_updates',
+ '#type' => 'submit',
+ '#value' => t('Install updates'),
+ '#weight' => 100,
+ );
+ }
+
+ return $form;
+}
+
+/**
+ * Validate function for the main update form.
+ *
+ * @see update_update_form
+ */
+function update_update_form_validate($form, &$form_state) {
+ if (isset($form_state['values']['connection_settings'])) {
+ $backend = $form_state['values']['connection_settings']['update_filetransfer_default'];
+ $filetransfer = update_get_filetransfer($backend, $form_state['values']['connection_settings'][$backend]);
+ try {
+ $filetransfer->connect();
+ }
+ catch(Exception $e) {
+ form_set_error('connection_settings', $e->getMessage());
+ }
+ }
+}
+
+/**
+ * Submit function for the main update form.
+ *
+ * @see update_update_form
+ */
+function update_update_form_submit($form, &$form_state) {
+ if ($form_state['clicked_button']['#name'] == 'process_updates') {
+ variable_set('site_offline', $form_state['values']['site_offline']);
+ // Save the connection settings to the DB.
+ $filetransfer_backend = $form_state['values']['connection_settings']['update_filetransfer_default'];
+
+ $connection_settings = array();
+ foreach ($form_state['values']['connection_settings'][$filetransfer_backend] as $key => $value) {
+ if (!isset($form['connection_settings'][$filetransfer_backend][$key]['#filetransfer_save']) ||
+ $form['connection_settings'][$filetransfer_backend][$key]['#filetransfer_save']) {
+ $connection_settings[$key] = $value;
+ }
+ }
+ // Set this one as the default update method.
+ variable_set('update_filetransfer_default', $filetransfer_backend);
+ // Save the connection settings minus the password.
+ variable_set("update_filetransfer_connection_settings_" . $filetransfer_backend, $connection_settings);
+
+ $operations = array();
+ foreach ($form_state['storage']['projects'] as $project) {
+ // Put this in the begining (download everything first)
+ $operations[] = array('update_batch_get_project', array($project));
+ // Put these on the end
+ $operations[] = array(
+ 'update_batch_copy_project',
+ array(
+ $project,
+ update_get_filetransfer($filetransfer_backend, $form_state['values']['connection_settings'][$filetransfer_backend])
+ ),
+ );
+ $operations[] = array('update_batch_update_project', array($project));
+ }
+
+ $batch = array(
+ 'title' => t('Installing updates'),
+ 'init_message' => t('Preparing update operation'),
+ 'operations' => $operations,
+ 'finished' => 'update_batch_finished',
+ 'file' => drupal_get_path('module', 'update') . '/update.admin.inc',
+ );
+ batch_set($batch);
+ }
+ else {
+ $form_state['storage']['projects'] = array_keys(array_filter($form_state['values']['projects']));
+ }
+}
+
+
+/**
+ * Batch operations related to installing / updating projects
+ *
+ **/
+
+
+/**
+ * Batch operation: download a project and put it in a temporary cache.
+ *
+ * @param string $project name of the project being installed
+ * @param array &$context BatchAPI storage
+ *
+ * @return void;
+ */
+function update_batch_get_project($project, &$context) {
+ $context['results'][$project] = array();
+ if (!isset($context['sandbox']['started'])) {
+ $context['sandbox']['started'] = TRUE;
+ $context['message'] = t('Downloading %project', array('%project' => $project));
+ $context['finished'] = 0;
+ return;
+ }
+ $latest_version = _update_get_latest_version($project);
+ if ($local_cache = update_get_file($latest_version['download_link'])) {
+ watchdog('update', t('Downloaded %project to %local_cache', array('%project' => $project, '%local_cache' => $local_cache)));
+ }
+ else {
+ $context['success'] = FALSE;
+ $content['results'][$project][] = t('Failed to download %project', array('%project' => $project));
+ }
+}
+
+/**
+ * Batch operation: copy a project to it's proper place.
+ * For updates, will locate the current project and replace it.
+ * For new installs, will download and try to determine the type from the info file
+ * and then place it variable_get(update_default_{$type}_location) i.e. update_default_module_location().
+ *
+ * @param string $project name of the project being installed
+ * @param FileTransfer $filetransfer A valid FileTransfer class
+ * @param array &$context BatchAPI storage
+ *
+ * @return void
+ */
+function update_batch_copy_project($project, $filetransfer, &$context) {
+ if (!empty($context['results'][$project]['#abort'])) {
+ $context['#finished'] = 1;
+ return;
+ }
+ if (!isset($context['sandbox']['starting'])) {
+ $context['sandbox']['starting'] = 1;
+ $context['message'] = t('Copying %project to server', array('%project' => $project));
+ $context['finished'] = 0;
+ return;
+ }
+
+ // @TODO: This needs to be revised. We should prioritize minor upgrades.
+ $latest_version = _update_get_latest_version($project);
+ // This is the .tar.gz from d.o.
+ $local_cache = update_get_file($latest_version['download_link']);
+
+ //this extracts the file into the standard place.
+ try {
+ update_untar($local_cache);
+ } catch (Exception $e) {
+ _update_batch_create_message($context['results'][$project], $e->getMessage(), FALSE);
+ $context['results'][$project]['#abort'] = TRUE;
+ return;
+ }
+
+ $project_source_dir = file_directory_temp() . '/update-extraction/' . $project;
+
+ if ($project_info = _update_get_project_type_and_location($project)) {
+ // This is an update
+ $is_install = FALSE;
+ $location = $project_info['location'];
+ } else {
+ //This is a fresh install, not an update
+ $type = _update_get_project_type($project_location . '/' . $project . '.info');
+ if (!$type) {
+ _update_batch_create_message($context['results'][$project], t("Unable to determine project type for %name", array("%name" => $project)), FALSE);
+ $context['results'][$project]['#abort'] = TRUE;
+ return;
+ }
+
+ $default_locations = array(
+ 'module' => 'sites/all/modules',
+ 'theme' => 'sites/all/themes',
+ 'theme_engine' => 'sites/all/themes/engines'
+ );
+
+ $location = variable_get("update_default_{$type}_location", $default_locations[$type]) . '/' . $project;
+ $is_install = TRUE;
+ }
+
+ $project_destination_dir = DRUPAL_ROOT . '/' . $location;
+
+ try {
+ if (!$is_install) {
+ $filetransfer->removeDirectory($project_destination_dir);
+ }
+ $filetransfer->copyDirectory($project_source_dir, $project_destination_dir);
+ }
+ catch (Exception $e) {
+ _update_batch_create_message($context['results'][$project], t($e->getMessage(), $e->arguments), FALSE);
+ $context['results'][$project]['#abort'];
+ return;
+ }
+ _update_batch_create_message($context['results'][$project], t('Installed %project_name successfully', array('%project_name' => $project)));
+ $context['finished'] = 1;
+}
+
+/**
+ * Set the batch to run the update functions.
+ */
+function update_batch_update_project($project, &$context) {
+ if (!empty($context['results'][$project]['#abort'])) {
+ $context['#finished'] = 1;
+ return;
+ }
+ $project_info = _update_get_project_type_and_location($project);
+ if ($project_info['type'] != 'module') {
+ $context['finished'] = 1;
+ return;
+ }
+ $operations = array();
+ require_once './update.php';
+
+ foreach (_update_get_schema_updates($project) as $update) {
+ update_do_one($project, $update, $context);
+ }
+}
+
+/**
+ * Batch callback for when the batch is finished.
+ */
+function update_batch_finished($success, $results) {
+ foreach ($results as $module => $messages) {
+ if (!empty($messages['#abort'])) {
+ $success = FALSE;
+ }
+ }
+ $_SESSION['update_batch_results']['success'] = $success;
+ $_SESSION['update_batch_results']['messages'] = $results;
+}
+
+/**
+ *
+ * Very stupid function to provide compatibility with update.php's
+ * silly functions
+ * Some better error handling is needed, but batch API doesn't seem to support any.
+ *
+ * @param array $project_results
+ * @param string $message
+ * @param bool $success
+ */
+function _update_batch_create_message(&$project_results, $message, $success = TRUE) {
+ $next_number = count($project_results) + 1;
+ $project_results[$next_number] = array(array('query' => $message, 'success' => $success));
+}
+
+/**
+ * Theme main updates page.
+ *
+ * @ingroup themeable
+ */
+function theme_update_available_updates_form($form) {
+ $last = variable_get('update_last_check', 0);
+ $output = '' . ($last ? t('Last checked: @time ago', array('@time' => format_interval(REQUEST_TIME - $last))) : t('Last checked: never'));
+ $output .= ' (' . l(t('Check manually'), 'admin/reports/updates/check') . ')';
+ $output .= "
\n";
+ $output .= drupal_render_children($form);
+ return $output;
+}
Index: modules/update/update.css
===================================================================
RCS file: /cvs/drupal/drupal/modules/update/update.css,v
retrieving revision 1.5
diff -u -p -r1.5 update.css
--- modules/update/update.css 29 Apr 2009 03:57:21 -0000 1.5
+++ modules/update/update.css 20 Jul 2009 14:15:47 -0000
@@ -1,110 +1,10 @@
-/* $Id: update.css,v 1.5 2009/04/29 03:57:21 webchick Exp $ */
+/* $Id$ */
-.update .project {
- font-weight: bold;
- font-size: 110%;
- padding-left: .25em; /* LTR */
- height: 22px;
-}
-
-.update .version-status {
- float: right; /* LTR */
- padding-right: 10px; /* LTR */
- font-size: 110%;
- height: 20px;
-}
-
-.update .version-status .icon {
- padding-left: .5em; /* LTR */
-}
-
-.update .version-date {
- white-space: nowrap;
-}
-
-.update .info {
- margin: 0;
- padding: 1em 1em .25em 1em;
-}
-
-.update tr td {
- border-top: 1px solid #ccc;
- border-bottom: 1px solid #ccc;
-}
-
-.update tr.error {
+table tbody tr.update-security,
+table tbody tr.update-unsupported {
background: #fcc;
}
-.update tr.error .version-recommended {
- background: #fdd;
-}
-
-.update tr.ok {
- background: #dfd;
-}
-
-.update tr.warning {
- background: #ffd;
-}
-
-.update tr.warning .version-recommended {
- background: #ffe;
-}
-
-.current-version, .new-version {
- direction: ltr; /* Note: version numbers should always be LTR. */
-}
-
-.update tr.unknown {
- background: #ddd;
-}
-
-table.update,
-.update table.version {
- width: 100%;
- margin-top: .5em;
-}
-
-.update table.version tbody {
- border: none;
-}
-
-.update table.version tr,
-.update table.version td {
- line-height: .9em;
- padding: 0;
- margin: 0;
- border: none;
-}
-
-.update table.version .version-title {
- padding-left: 1em; /* LTR */
- width: 14em;
-}
-
-.update table.version .version-details {
- padding-right: .5em; /* LTR */
-}
-
-.update table.version .version-links {
- text-align: right; /* LTR */
- padding-right: 1em; /* LTR */
-}
-
-.update table.version-security .version-title {
- color: #970F00;
-}
-
-.update table.version-recommended-strong .version-title {
- font-weight: bold;
-}
-
-.update .security-error {
- font-weight: bold;
- color: #970F00;
-}
-
-.update .check-manually {
- padding-left: 1em; /* LTR */
-}
+th.update-project-name {
+ width: 50%;
+}
\ No newline at end of file
Index: modules/update/update.info
===================================================================
RCS file: /cvs/drupal/drupal/modules/update/update.info,v
retrieving revision 1.5
diff -u -p -r1.5 update.info
--- modules/update/update.info 11 Oct 2008 02:33:12 -0000 1.5
+++ modules/update/update.info 20 Jul 2009 14:15:47 -0000
@@ -7,6 +7,7 @@ core = 7.x
files[] = update.module
files[] = update.compare.inc
files[] = update.fetch.inc
-files[] = update.report.inc
+files[] = update.admin.inc
files[] = update.settings.inc
files[] = update.install
+files[] = update.js
Index: modules/update/update.js
===================================================================
RCS file: modules/update/update.js
diff -N modules/update/update.js
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ modules/update/update.js 20 Jul 2009 14:15:47 -0000
@@ -0,0 +1,13 @@
+// $Id$
+(function ($) {
+
+Drupal.behaviors.updateFileTransferForm = {
+ attach: function(context) {
+ $('#edit-connection-settings-update-filetransfer-default').change(function() {
+ $('.filetransfer').hide().filter('.filetransfer-' + $(this).val()).show();
+ });
+ $('.filetransfer').hide().filter('.filetransfer-' + $('#edit-connection-settings-update-filetransfer-default').val()).show();
+ }
+}
+
+})(jQuery);
Index: modules/update/update.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/update/update.module,v
retrieving revision 1.37
diff -u -p -r1.37 update.module
--- modules/update/update.module 8 Jun 2009 05:00:11 -0000 1.37
+++ modules/update/update.module 20 Jul 2009 14:15:48 -0000
@@ -69,8 +69,7 @@ function update_help($path, $arg) {
case 'admin/reports/updates':
global $base_url;
$output = '' . t('Here you can find information about available updates for your installed modules and themes. Note that each module or theme is part of a "project", which may or may not have the same name, and might include multiple modules or themes within it.') . '
';
- $output .= '' . t('To extend the functionality or to change the look of your site, a number of contributed modules and themes are available.', array('@modules' => 'http://drupal.org/project/modules', '@themes' => 'http://drupal.org/project/themes')) . '
';
- $output .= '' . t('Each time Drupal core or a contributed module or theme is updated, it is important that update.php is run.', array('@update-php' => url($base_url . '/update.php', array('external' => TRUE)))) . '
';
+
return $output;
case 'admin/build/themes':
case 'admin/build/modules':
@@ -129,10 +128,22 @@ function update_menu() {
$items['admin/reports/updates'] = array(
'title' => 'Available updates',
'description' => 'Get a status report about available updates for your installed modules and themes.',
- 'page callback' => 'update_status',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('update_update_form'),
+ 'access arguments' => array('administer site configuration'),
+ 'weight' => 10,
+ );
+
+ $items['admin/update'] = array(
+ 'title' => 'Updating your site',
+ 'description' => 'Second step of site update / new project install',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('update_update_form'),
'access arguments' => array('administer site configuration'),
'weight' => 10,
+ 'type' => MENU_CALLBACK,
);
+
$items['admin/settings/updates'] = array(
'title' => 'Updates',
'description' => 'Change frequency of checks for available updates to your installed modules and themes, and how you would like to be notified.',
@@ -155,15 +166,9 @@ function update_menu() {
*/
function update_theme() {
return array(
- 'update_settings' => array(
+ 'update_available_updates_form' => array(
'arguments' => array('form' => NULL),
),
- 'update_report' => array(
- 'arguments' => array('data' => NULL),
- ),
- 'update_version' => array(
- 'arguments' => array('version' => NULL, 'tag' => NULL, 'class' => NULL),
- ),
);
}
@@ -632,3 +637,139 @@ function update_flush_caches() {
/**
* @} End of "defgroup update_status_cache".
*/
+
+/**
+ * Get a file from the server, or if it was already downloaded, get the local
+ * path to the file.
+ *
+ * @param $url
+ * The URL of the file on the server.
+ */
+function update_get_file($url) {
+ // Get each of the specified files.
+ $parsed_url = parse_url($url);
+ $local = file_directory_temp() . '/update-cache/' . basename($parsed_url['path']);
+ if (!file_exists(file_directory_temp() . '/update-cache/')) {
+ mkdir(file_directory_temp() . '/update-cache/');
+ }
+ // Check the cache and download the file if needed.
+ if (!file_exists($local)) {
+ return system_retrieve_file($url, $local);
+ }
+ else {
+ return $local;
+ }
+}
+
+
+/**
+ * Untar a file, using the Archive_Tar class.
+ *
+ * @param string $file the filename you wish to extract
+ *
+ * @return void
+ * @throws Exception on failure.
+ */
+function update_untar($file) {
+ $extraction_dir = file_directory_temp() . '/update-extraction';
+ if (!file_exists($extraction_dir)) {
+ mkdir($extraction_dir);
+ }
+ $archive_tar = new Archive_Tar($file);
+ if (!$archive_tar->extract($extraction_dir)) {
+ throw new Exception(t('Unable to extact %file', array('%file' => $file)));
+ }
+}
+
+/**
+ * Get a filetransfer class.
+ *
+ * @param $method
+ * The filetransfer method to get the class for.
+ * @param $overrides
+ * A set of overrides over the defaults.
+ */
+function update_get_filetransfer($method, $overrides = array()) {
+ // Fire up the connection class
+ $settings = variable_get("update_filetransfer_connection_settings_" . $method, array());
+ $settings = array_merge($settings, $overrides);
+ $available_backends = module_invoke_all('filetransfer_backends');
+ $filetransfer = call_user_func_array($available_backends[$method]['class'] . '::factory', array(DRUPAL_ROOT, $settings));
+ return $filetransfer;
+}
+
+
+/**
+ * Helper function, returns a an associative array of a project's type and
+ * location returns false on failure.
+ */
+function _update_get_project_type_and_location($name) {
+ foreach (array('module', 'theme') as $type) {
+ if ($dir = drupal_get_path($type, $name)) {
+ return array('type' => $type, 'location' => $dir);
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * Get's the latest release of a project
+ *
+ * @param string $name Name of the project
+ * @return array An array of information about the latest recommended
+ * release of the project
+ */
+function _update_get_latest_version($name) {
+ if ($available = update_get_available(FALSE)) {
+ module_load_include('inc', 'update', 'update.compare');
+ $project_data = update_calculate_project_data($available);
+ $project = $project_data[$name];
+ return $project['releases'][$project['latest_version']];
+ }
+}
+
+/**
+ * Returns the available updates for a given module in an array
+ *
+ * @param $project
+ * The name of the module.
+ */
+function _update_get_schema_updates($project) {
+ require_once './includes/install.inc';
+ $module_info = _update_get_project_type_and_location($project);
+ if ($module_info['type'] != 'module') {
+ return array();
+ }
+ module_load_include('install', $project);
+
+ if (!$updates = drupal_get_schema_versions($project)) {
+ return array();
+ }
+ $updates_to_run = array();
+ $version = drupal_get_installed_schema_version($project, FALSE);
+ $max_version = max($updates);
+ if ($version < $max_version) {
+ foreach ($updates as $update) {
+ if ($update > $version) {
+ $updates_to_run[] = $update;
+ }
+ }
+ }
+ return $updates_to_run;
+}
+
+/**
+ * Helper function, given an info file, will determine what type of project it is
+ *
+ * @param string $info_file path to info file
+ *
+ * @return string project type, could be theme or module.
+ */
+function _update_get_project_type($info_file) {
+ $info = drupal_parse_info_file($info_file);
+ if ($info['engine']) {
+ return 'theme';
+ //we can assume this is a theme
+ }
+ return 'module';
+}
\ No newline at end of file
Index: modules/update/update.report.inc
===================================================================
RCS file: modules/update/update.report.inc
diff -N modules/update/update.report.inc
--- modules/update/update.report.inc 6 Jun 2009 06:26:13 -0000 1.18
+++ /dev/null 1 Jan 1970 00:00:00 -0000
@@ -1,243 +0,0 @@
-' . ($last ? t('Last checked: @time ago', array('@time' => format_interval(REQUEST_TIME - $last))) : t('Last checked: never'));
- $output .= ' (' . l(t('Check manually'), 'admin/reports/updates/check') . ')';
- $output .= "\n";
-
- if (!is_array($data)) {
- $output .= '' . $data . '
';
- return $output;
- }
-
- $header = array();
- $rows = array();
-
- $notification_level = variable_get('update_notification_threshold', 'all');
-
- foreach ($data as $project) {
- switch ($project['status']) {
- case UPDATE_CURRENT:
- $class = 'ok';
- $icon = theme('image', 'misc/watchdog-ok.png', t('ok'), t('ok'));
- break;
- case UPDATE_UNKNOWN:
- case UPDATE_NOT_FETCHED:
- $class = 'unknown';
- $icon = theme('image', 'misc/watchdog-warning.png', t('warning'), t('warning'));
- break;
- case UPDATE_NOT_SECURE:
- case UPDATE_REVOKED:
- case UPDATE_NOT_SUPPORTED:
- $class = 'error';
- $icon = theme('image', 'misc/watchdog-error.png', t('error'), t('error'));
- break;
- case UPDATE_NOT_CHECKED:
- case UPDATE_NOT_CURRENT:
- default:
- $class = 'warning';
- $icon = theme('image', 'misc/watchdog-warning.png', t('warning'), t('warning'));
- break;
- }
-
- $row = '';
- switch ($project['status']) {
- case UPDATE_NOT_SECURE:
- $row .= '' . t('Security update required!') . '';
- break;
- case UPDATE_REVOKED:
- $row .= '' . t('Revoked!') . '';
- break;
- case UPDATE_NOT_SUPPORTED:
- $row .= '' . t('Not supported!') . '';
- break;
- case UPDATE_NOT_CURRENT:
- $row .= '' . t('Update available') . '';
- break;
- case UPDATE_CURRENT:
- $row .= '' . t('Up to date') . '';
- break;
- default:
- $row .= check_plain($project['reason']);
- break;
- }
- $row .= '' . $icon . '';
- $row .= "
\n";
-
- $row .= '';
- if (isset($project['title'])) {
- if (isset($project['link'])) {
- $row .= l($project['title'], $project['link']);
- }
- else {
- $row .= check_plain($project['title']);
- }
- }
- else {
- $row .= check_plain($project['name']);
- }
- $row .= ' ' . check_plain($project['existing_version']);
- if ($project['install_type'] == 'dev' && !empty($project['datestamp'])) {
- $row .= ' (' . format_date($project['datestamp'], 'custom', 'Y-M-d') . ')';
- }
- $row .= "
\n";
-
- $row .= "\n";
-
- if (isset($project['recommended'])) {
- if ($project['status'] != UPDATE_CURRENT || $project['existing_version'] !== $project['recommended']) {
-
- // First, figure out what to recommend.
- // If there's only 1 security update and it has the same version we're
- // recommending, give it the same CSS class as if it was recommended,
- // but don't print out a separate "Recommended" line for this project.
- if (!empty($project['security updates']) && count($project['security updates']) == 1 && $project['security updates'][0]['version'] === $project['recommended']) {
- $security_class = ' version-recommended version-recommended-strong';
- }
- else {
- $security_class = '';
- $version_class = 'version-recommended';
- // Apply an extra class if we're displaying both a recommended
- // version and anything else for an extra visual hint.
- if ($project['recommended'] !== $project['latest_version']
- || !empty($project['also'])
- || ($project['install_type'] == 'dev'
- && isset($project['dev_version'])
- && $project['latest_version'] !== $project['dev_version']
- && $project['recommended'] !== $project['dev_version'])
- || (isset($project['security updates'][0])
- && $project['recommended'] !== $project['security updates'][0])
- ) {
- $version_class .= ' version-recommended-strong';
- }
- $row .= theme('update_version', $project['releases'][$project['recommended']], t('Recommended version:'), $version_class);
- }
-
- // Now, print any security updates.
- if (!empty($project['security updates'])) {
- foreach ($project['security updates'] as $security_update) {
- $row .= theme('update_version', $security_update, t('Security update:'), 'version-security' . $security_class);
- }
- }
- }
-
- if ($project['recommended'] !== $project['latest_version']) {
- $row .= theme('update_version', $project['releases'][$project['latest_version']], t('Latest version:'), 'version-latest');
- }
- if ($project['install_type'] == 'dev'
- && $project['status'] != UPDATE_CURRENT
- && isset($project['dev_version'])
- && $project['recommended'] !== $project['dev_version']) {
- $row .= theme('update_version', $project['releases'][$project['dev_version']], t('Development version:'), 'version-latest');
- }
- }
-
- if (isset($project['also'])) {
- foreach ($project['also'] as $also) {
- $row .= theme('update_version', $project['releases'][$also], t('Also available:'), 'version-also-available');
- }
- }
-
- $row .= "
\n"; // versions div.
-
- $row .= "\n";
- if (!empty($project['extra'])) {
- $row .= '\n"; // extra div.
- }
-
- $row .= '
';
- sort($project['includes']);
- $row .= t('Includes: %includes', array('%includes' => implode(', ', $project['includes'])));
- $row .= "
\n";
-
- $row .= "
\n"; // info div.
-
- if (!isset($rows[$project['project_type']])) {
- $rows[$project['project_type']] = array();
- }
- $rows[$project['project_type']][] = array(
- 'class' => $class,
- 'data' => array($row),
- );
- }
-
- $project_types = array(
- 'core' => t('Drupal core'),
- 'module' => t('Modules'),
- 'theme' => t('Themes'),
- 'disabled-module' => t('Disabled modules'),
- 'disabled-theme' => t('Disabled themes'),
- );
- foreach ($project_types as $type_name => $type_label) {
- if (!empty($rows[$type_name])) {
- $output .= "\n" . $type_label . "
\n";
- $output .= theme('table', $header, $rows[$type_name], array('class' => 'update'));
- }
- }
- drupal_add_css(drupal_get_path('module', 'update') . '/update.css');
- return $output;
-}
-
-/**
- * Theme the version display of a project.
- *
- * @ingroup themeable
- */
-function theme_update_version($version, $tag, $class) {
- $output = '';
- $output .= '';
- $output .= '';
- $output .= '' . $tag . " | \n";
- $output .= '';
- $output .= l($version['version'], $version['release_link']);
- $output .= ' (' . format_date($version['date'], 'custom', 'Y-M-d') . ')';
- $output .= " | \n";
- $output .= '';
- $links = array();
- $links['update-download'] = array(
- 'title' => t('Download'),
- 'href' => $version['download_link'],
- );
- $links['update-release-notes'] = array(
- 'title' => t('Release notes'),
- 'href' => $version['release_link'],
- );
- $output .= theme('links', $links);
- $output .= ' | ';
- $output .= '
';
- $output .= "
\n";
- return $output;
-}