? modules/update/theme
? modules/update/update.test
Index: modules/update/update-confirm.tpl.php
===================================================================
RCS file: modules/update/update-confirm.tpl.php
diff -N modules/update/update-confirm.tpl.php
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ modules/update/update-confirm.tpl.php 25 Jun 2009 17:46:08 -0000
@@ -0,0 +1,12 @@
+
+
+Learn more about how to take a
+backup.', array ('@backup_url' => url('http://drupal.org/node/22281')));
+?>
+
+
+
+
+
\ No newline at end of file
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 25 Jun 2009 17:46:10 -0000
@@ -0,0 +1,276 @@
+ 'submit',
+ '#value' => 'Submit',
+ '#weight' => 100,
+ );
+
+ $available_backends = module_invoke_all('filetransfer_backends');
+ foreach ($available_backends as $k => $v) {
+ $options[$k] = $v['title'];
+ }
+
+ $form['update_filetransfer_preferred'] = array(
+ '#title' => t('Server connection type'),
+ '#type' => 'select',
+ '#options' => $options,
+ '#default_value' => variable_get('update_filetransfer_preferred', NULL),
+ );
+
+ if (isset($form_state['values'])) {
+ $update_filetransfer_preferred = $form_state['values']['update_filetransfer_preferred'];
+ $active_backend = $available_backends[$update_filetransfer_preferred];
+ //Get the settings form for this filetransfer backend
+ $form['connection_settings'] = call_user_func($active_backend['settings_form']);
+
+ $current_settings = variable_get("update_filetransfer_connection_settings::{$update_filetransfer_preferred}", array());
+ //set the saved values
+ foreach ($form['connection_settings'] as $name => &$element) {
+ if (isset($current_settings[$name])) {
+ $element['#default_value'] = $current_settings[$name];
+ }
+ }
+ $form['connection_settings']['#tree'] = TRUE;
+ $form['submit']['#value'] = 'Save';
+ }
+
+ return $form;
+}
+
+function update_settings_filetransfer_validate($form, &$form_state) {
+ if ($form_state['clicked_button']['#value'] == 'Save') {
+ $update_filetransfer_preferred = $form_state['values']['update_filetransfer_preferred'];
+ $available_backends = module_invoke_all('filetransfer_backends');
+ $filetransfer = call_user_func_array("{$available_backends[$update_filetransfer_preferred]['class']}::factory", array(DRUPAL_ROOT, $form_state['values']['connection_settings']));
+ try {
+ $filetransfer->connect();
+ } catch(Exception $e) {
+ form_set_error('update_filetransfer_preferred', $e->getMessage());
+ }
+ //test it out.
+ }
+}
+
+function update_settings_filetransfer_submit($form, &$form_state) {
+ if ($form_state['clicked_button']['#value'] == 'Save') {
+ //save it to the DB.
+ $update_filetransfer_preferred = $form_state['values']['update_filetransfer_preferred'];
+ $connection_settings_to_save = array();
+ foreach ($form_state['values']['connection_settings'] as $key => $value) {
+ //This is a hack, perhaps we should add an attribute to non-stored fields?
+ if ($form['connection_settings'][$key]['#type'] != 'password') {
+ $connection_settings_to_save[$key] = $value;
+ }
+ }
+ variable_set('update_filetransfer_preferred', $update_filetransfer_preferred);
+ variable_set("update_filetransfer_connection_settings::{$update_filetransfer_preferred}", $connection_settings_to_save);
+ } else {
+ $form_state['rebuild'] = TRUE;
+ }
+}
+
+function update_update_form($form_state, $extension_data = array()) {
+
+ $form = array();
+
+ if (isset($_SESSION['update_batch_results']) && $results = $_SESSION['update_batch_results']) {
+ unset($_SESSION['update_batch_results']);
+
+ drupal_set_title(t("Update / Installation report"));
+
+ if ($results['success']) {
+ drupal_set_message(t("Update was completed successfully! Your site has been taken out of maintinance mode."));
+ } else {
+ drupal_set_message(t("Update failed! See the log below for more information. Your site is still in maintinance mode"), 'error');
+ }
+
+ $form['log'] = array (
+ '#type' => 'item',
+ '#title' => t('Test results'),
+ '#markup' => theme('item_list', $results['messages']),
+ );
+ return $form;
+ }
+
+ $form['#action'] = '/admin/update';
+
+ $form['submit'] = array(
+ '#name' => 'submit',
+ '#type' => 'submit',
+ '#value' => t('Continue'),
+ '#weight' => 100,
+ );
+
+ if (!variable_get('update_filetransfer_preferred', NULL)) {
+ $message = t('In order to process updates, you need to setup your server connection details.', array ('@connection_setup_url' => url('admin/settings/updates/server-settings', array('query' => drupal_get_destination()))));
+ $form['submit']['#type'] = 'item';
+ $form['submit']['#markup'] = $message . '
';
+ drupal_set_message($message);
+ }
+
+ if(!isset($form_state['values'])) {
+ if ($available = update_get_available(TRUE)) {
+ module_load_include('inc', 'update', 'update.compare');
+ $extension_data = update_calculate_project_data($available);
+ foreach ($extension_data as $name => $project) {
+ //Filter out extensions which are dev versions, updated or core
+ if (($project['install_type'] == 'dev') || ($project['status'] == UPDATE_CURRENT) || ($project['project_type'] == 'core')) {
+ unset($extension_data[$name]);
+ }
+ }
+ }
+ else {
+ return array();
+ //SHould do something like this:
+ //return theme('update_report', _update_no_data());
+ }
+ //First Step, select add-ons
+ $options = array();
+ foreach ($extension_data as $name => $project) {
+ $options[$name]['title'] = check_plain($project['title']);
+ $options[$name]['type'] = check_plain($project['project_type']);
+ $options[$name]['status'] = theme('update_status_project_status',$project['status']);
+ $options[$name]['installed_version'] = $project['existing_version'];
+ $options[$name]['recommended_version'] = $project['recommended'];
+ }
+
+ if (!$options) {
+ $form['no_updates'] = array (
+ '#type' => 'item',
+ '#markup' => t('All of your extensions are up to date.'),
+ );
+ return $form;
+ }
+
+ $form['extensions_to_update'] = array (
+ '#type' => 'tableselect',
+ '#title' => 'choose',
+ '#options' => $options,
+ '#header' => array('title' => t('Title'),'type' => t('Type'), 'status' => t('Status'), 'installed_version' => t('Installed version'), 'recommended_version' => t('Recommended version')),
+ );
+ return $form;
+ }
+
+ if (is_array($form_state['values']) && array_filter($form_state['values']['extensions_to_update'])) {
+ //we've already submitted once.
+ $form['submit']['#name'] = 'process_updates';
+ if (variable_get('site_offline', FALSE) == FALSE) {
+ $form['submit']['#value'] = t('Put site into maintinance mode and install updates');
+ } else {
+ $form['submit']['#value'] = t('Install updates');
+ }
+
+ $form['extensions_to_update'] = array (
+ '#type' => 'value',
+ '#value' => array_filter(array_values($form_state['values']['extensions_to_update'])),
+ );
+
+ $form['#theme'] = 'update_confirm';
+
+ $update_filetransfer_preferred = variable_get('update_filetransfer_preferred');
+ $available_backends = module_invoke_all('filetransfer_backends');
+ $form['connection_settings'] = call_user_func($available_backends[$update_filetransfer_preferred]['settings_form']);
+ $form['connection_settings']['#tree'] = TRUE;
+ $current_settings = variable_get("update_filetransfer_connection_settings::{$update_filetransfer_preferred}", array());
+ foreach ($form['connection_settings'] as $name => &$element) {
+ if (isset($current_settings[$name])) {
+ $element['#default_value'] = $current_settings[$name];
+ }
+ }
+ return $form;
+ }
+}
+
+function update_update_form_submit($form, &$form_state) {
+ //We always want to rebuild unless we are done.
+ $form_state['rebuild'] = TRUE;
+
+ if ($form_state['clicked_button']['#name'] == 'site_offline') {
+ //take the site offline and rebuild.
+ variable_set('site_offline', TRUE);
+ }
+
+ if ($form_state['clicked_button']['#name'] == 'process_updates') {
+ $connection_settings = $form_state['values']['connection_settings'];
+ $operations = array();
+ foreach ($form_state['values']['extensions_to_update'] as $extension) {
+ //HACK: Bad workflow here... don't know how to determine what type of extension this is.
+ $extension_type = UPDATE_EXTENSION_TYPE_MODULE;
+ //put this in the begining (download everything first)
+ $operations['1_get_' . $extension] = array('update_get_extension', array($extension));
+ //put these on the end
+ $operations['2_put_' . $extension] = array('update_copy_extension', array($extension, $extension_type, update_get_default_filetransfer($connection_settings)));
+ $operations['3_install_' . $extension] = array('update_install_extension', array($extension));
+ }
+ ksort($operations);
+
+ $batch = array(
+ 'title' => t('Installing updates'),
+ 'init_message' => t('Preparing update operation'),
+ 'operations' => $operations,
+ 'finished' => 'update_finished',
+ );
+ batch_set($batch);
+ // When not in a form submit handler (which are the 'natural' places
+ // to set up a batch processing), you additionally have to manually
+ // trigger redirection to the processing / progress bar page, using:
+ batch_process();
+ //theme('update_report', )
+ }
+}
+
+
+/**
+ * Returns an icon
+ */
+function theme_update_status_project_status($status) {
+ switch ($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;
+ }
+ return $icon;
+}
+
+
+
+/**
+ * Theme project status report.
+ *
+ * @ingroup themeable
+ */
+function theme_update_available_updates_form($data) {
+ foreach ($data as $project) {
+ $rows[] = _update_build_project_status_row($project);
+ }
+ return theme('table', array('', 'Name', 'Type', 'Status'), $rows);
+}
Index: modules/update/update.info
===================================================================
RCS file: /cvs/drupal/drupal/modules/update/update.info,v
retrieving revision 1.5
diff -u -r1.5 update.info
--- modules/update/update.info 11 Oct 2008 02:33:12 -0000 1.5
+++ modules/update/update.info 25 Jun 2009 17:46:10 -0000
@@ -7,6 +7,6 @@
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
Index: modules/update/update.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/update/update.module,v
retrieving revision 1.37
diff -u -r1.37 update.module
--- modules/update/update.module 8 Jun 2009 05:00:11 -0000 1.37
+++ modules/update/update.module 25 Jun 2009 17:46:10 -0000
@@ -62,6 +62,16 @@
define('UPDATE_MAX_FETCH_ATTEMPTS', 2);
/**
+ * Used when determining how to act on a given extension when removing / installing
+ */
+define('UPDATE_EXTENSION_TYPE_MODULE', 1);
+
+/**
+ * Used when determining how to act on a given extension when removing / installing
+ */
+define('UPDATE_EXTENSION_TYPE_THEME', 2);
+
+/**
* Implement hook_help().
*/
function update_help($path, $arg) {
@@ -69,8 +79,9 @@
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)))) . '
';
+ $output .= t('Key: !warning_icon Recommended update', array('!warning_icon' => theme('image', 'misc/watchdog-warning.png', t('warning'), t('warning'))));
+ $output .= t('  !error_icon Critical update / Security fix', array('!error_icon' => theme('image', 'misc/watchdog-error.png', t('error'), t('error'))));
+
return $output;
case 'admin/build/themes':
case 'admin/build/modules':
@@ -87,6 +98,12 @@
}
}
}
+
+ case 'admin/settings/updates/server-settings':
+ $output = '' . t('In order to update / extend your site from the browser you need to configure how to connect to your server.');
+ $output .= t(' If you do not know what to enter on this form, please contact your hosting provider for help.') .'
';
+ return $output;
+ break;
case 'admin/reports/updates/settings':
case 'admin/reports/status':
@@ -129,10 +146,19 @@
$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 modules and themes',
+ '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.',
@@ -140,6 +166,19 @@
'page arguments' => array('update_settings'),
'access arguments' => array('administer site configuration'),
);
+ $items['admin/settings/updates/updates'] = array(
+ 'title' => 'Updates',
+ 'type' => MENU_DEFAULT_LOCAL_TASK,
+ 'weight' => -10,
+ );
+ $items['admin/settings/updates/server-settings'] = array(
+ 'title' => 'Server Settings',
+ 'page title' => 'Server Settings',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('update_settings_filetransfer'),
+ 'access arguments' => array('administer site configuration'),
+ 'type' => MENU_LOCAL_TASK,
+ );
$items['admin/reports/updates/check'] = array(
'title' => 'Manual update check',
'page callback' => 'update_manual_status',
@@ -158,9 +197,19 @@
'update_settings' => array(
'arguments' => array('form' => NULL),
),
+ 'update_confirm' => array(
+ 'template' => 'theme/update-confirm',
+ 'arguments' => array('form' => NULL),
+ ),
'update_report' => array(
'arguments' => array('data' => NULL),
),
+ 'update_report_form' => array(
+ 'arguments' => array('form' => NULL),
+ ),
+ 'update_status_project_status' => array(
+ 'arguments' => array('status' => NULL),
+ ),
'update_version' => array(
'arguments' => array('version' => NULL, 'tag' => NULL, 'class' => NULL),
),
@@ -632,3 +681,131 @@
/**
* @} End of "defgroup update_status_cache".
*/
+
+function _update_get_latest_version($name) {
+ if ($available = update_get_available(FALSE)) {
+ module_load_include('inc', 'update', 'update.compare');
+ $extension_data = update_calculate_project_data($available);
+ $project = $extension_data[$name];
+ return $project['releases'][$project['latest_version']];
+ }
+}
+
+
+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)) {
+ // $result->data is the actual contents of the downloaded file. This saves
+ // it into a local file, whose path is stored in $local. $local is stored
+ // relative to the Drupal installation.
+ return system_retrieve_file($url, $local);
+ } else {
+ return $local;
+ }
+}
+
+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);
+ return $archive_tar->extract($extraction_dir);
+}
+
+/**
+ * Helper function to determine if it is a module or a theme
+ */
+function _update_get_extension_type($directory) {
+ if (count(file_scan_directory($directory, "/\.module$/")) > 0) {
+ return UPDATE_EXTENSION_TYPE_MODULE;
+ }
+ else {
+ return UPDATE_EXTENSION_TYPE_THEME;
+ }
+}
+
+/**
+ * Batch operations
+ */
+
+function update_get_extension($extension_name, &$context) {
+ if (!isset($context['sandbox']['starting'])) {
+ $context['sandbox']['starting'] = 1;
+ $context['message'] = t('Downloading %extension', array('%extension' => $extension_name));
+ $context['finished'] = 1 / 2;
+ return;
+ }
+ $latest_version = _update_get_latest_version($extension_name);
+ if ($local_cache = update_get_file($latest_version['download_link'])) {
+ watchdog('update', t('Downloaded %extension to %local_cache', array('%extension' => $extension_name, '%local_cache' => $local_cache)));
+ } else {
+ $context['success'] = FALSE;
+ $content['results'][] = t('Failed to download %extension', array('%extension' => $extension_name));
+ }
+
+ $context['finished'] = 1;
+}
+
+function update_copy_extension($extension_name, $extension_type, $filetransfer, &$context) {
+ $latest_version = _update_get_latest_version($extension_name);
+ $local_cache = update_get_file($latest_version['download_link']);
+ $extension_destination_dir = ($extension_type == UPDATE_EXTENSION_TYPE_MODULE) ? DRUPAL_ROOT . '/sites/all/modules/' . $extension_name : DRUPAL_ROOT . '/sites/all/themes/' . $extension_name;
+ if (update_untar($local_cache)) {
+ $extension_source_dir = file_directory_temp() . '/update-extraction/' . $extension_name;
+ }
+ try {
+ $filetransfer->removeDirectory($extension_destination_dir);
+ $filetransfer->copyDirectory($extension_source_dir, $extension_destination_dir);
+ } catch(Exception $e) {
+ $context['message'] = t($e->getMessage(), $e->arguments);
+ }
+
+ if (!isset($context['sandbox']['starting'])) {
+ $context['sandbox']['starting'] = 1;
+ $context['message'] = t('Copying %extension to server', array('%extension' => $extension_name));
+ return;
+ }
+ system_retrieve_file($file, 'update-cache', TRUE);
+ $context['finished'] = 1;
+
+}
+
+function update_install_extension($extension_name, &$context) {
+ if (!isset($context['sandbox']['done'])) {
+ $context['sandbox']['done'] = 0;
+ }
+ if (!$context['results']) {
+ $context['results'][] = t('Installed %extension', array('%extension' => $extension_name));
+ }
+
+ $context['message'] = t('Installing %extension', array('%extension' => $extension_name));
+ sleep(1);
+ $context['sandbox']['done'] += 1;
+ $context['finished'] = $context['sandbox']['done'] / 4;
+}
+
+function update_finished($success, $results) {
+ if ($success) {
+ variable_set('site_offline', FALSE);
+ }
+ $_SESSION['update_batch_results']['success'] = $success;
+ $_SESSION['update_batch_results']['messages'] = $results;
+}
+
+function update_get_default_filetransfer($overrides = array()) {
+ //Fire up the connection class
+ $update_filetransfer_preferred = variable_get('update_filetransfer_preferred', NULL);
+ $settings = variable_get("update_filetransfer_connection_settings::{$update_filetransfer_preferred}", array());
+ $settings = array_merge($settings, $overrides);
+ $available_backends = module_invoke_all('filetransfer_backends');
+ $filetransfer = call_user_func_array("{$available_backends[$update_filetransfer_preferred]['class']}::factory", array(DRUPAL_ROOT, $settings));
+ return $filetransfer;
+}
+