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 7 Jul 2009 08:59:32 -0000 @@ -0,0 +1,296 @@ + 'submit', + '#type' => 'submit', + '#value' => t('Continue'), + '#weight' => 100, + ); + + if (!isset($form_state['values'])) { + //First step. + _update_available_updates_form($form); + return $form; + } + + if (is_array($form_state['values']) && array_filter($form_state['values']['extensions_to_update'])) { + //we've already submitted once and there are updates to deal with. + //if there is a form error, we show the list of possible FileTransfer backends. + _update_confirmation_form($form, $form_state); + return $form; + } +} + + +function _update_finished_report(&$form, $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']), + ); +} + +function _update_available_updates_form(&$form) { + + 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 { + unset($form); + $form = array(); + $form['message'] = array( + '#type' => 'item', + '#markup' => t('There was a problem getting update information. Please try again later.'), + ); + return; + } + + $options = array(); + //sort in reverse order by name... because of uasort, + //this is required because later entries come first when same. + krsort($extension_data); + uasort($extension_data, create_function('$a, $b', 'return ($a["status"] == UPDATE_NOT_SECURE && $b["status"] != UPDATE_NOT_SECURE) ? -1 : 1;')); + + foreach ($extension_data as $name => $project) { + $options[$name]['title'] = l($project['title'], $project['link']); + if ($project['status'] == UPDATE_NOT_SECURE) { + $options[$name]['title'] .= ' ' . t('(Security update)'); + } + $options[$name]['type'] = check_plain($project['project_type']); + $options[$name]['#attributes'] = array('class' => _update_status_project_status_class($project['status'])); + $options[$name]['installed_version'] = $project['existing_version']; + $options[$name]['recommended_version'] = l($project['recommended'], $project['releases'][$project['recommended']]['release_link'], array('attributes' => array('title' => t('Release notes for @project_name', array('@project_name' => $project['title']))))); + } + + if (!$options) { + $form = array(); + $form['message'] = 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('Name'), 'type' => t('Type'), 'installed_version' => t('Installed version'), 'recommended_version' => t('Recommended version')), + ); +} + + +function _update_confirmation_form(&$form, &$form_state) { + + $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['information'] = array(); + $form['information']['#weight'] = -100; + $form['information']['backup_header'] = 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']['main_header'] = array( + '#prefix' => '' . 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': @@ -125,13 +134,22 @@ */ function update_menu() { $items = array(); - + $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', @@ -158,9 +176,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 +660,163 @@ /** * @} 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 get location of an extension + */ +function _update_get_extension_location($name) { + if ($_tmp = _update_get_extension_type_and_location($name)) { + return $_tmp['location']; + } +} + +/** + * Helper function, returns a an associative array of an extensions type and location + * returns false on failure. (Should really throw an exception) + */ +function _update_get_extension_type_and_location($name) { + if ($dir = drupal_get_path('module', $name)) { + $type = UPDATE_EXTENSION_TYPE_MODULE; + } + elseif ($dir = drupal_get_path('theme', $name)) { + $type = UPDATE_EXTENSION_TYPE_THEME; + } + else { + return FALSE; + } + return array('type' => UPDATE_EXTENSION_TYPE_THEME, 'location' => $dir); +} + +/** + * 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', '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, $filetransfer, &$context) { + if (!isset($context['sandbox']['starting'])) { + $context['sandbox']['starting'] = 1; + $context['message'] = t('Copying %extension to server', array('%extension' => $extension_name)); + $context['finished'] = .5; + return; + } + + $latest_version = _update_get_latest_version($extension_name); + $local_cache = update_get_file($latest_version['download_link']); + + $extension_destination_dir = DRUPAL_ROOT . '/' . _update_get_extension_location($extension_name); + if (!$extension_destination_dir) { + throw new Exception(t("Unable to find %extension_name", array('%extension_name' => $extension_destination_dir))); + } + + 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) { + drupal_set_message(t($e->getMessage(), $e->arguments), 'error'); + //Some better error handling is needed, but batch API doesn't seem to support any. + throw $e; + } + + $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('Pretending to Install %extension', array('%extension' => $extension_name)); + sleep(1); + $context['sandbox']['done'] += 1; + $context['finished'] = $context['sandbox']['done'] / 4; +} + +function update_batch_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; +} + +function update_get_filetransfer_settings($filetransfer_backend_name) { + return variable_get("update_filetransfer_connection_settings::{$filetransfer_backend_name}", array()); +} + +function update_set_filetransfer_settings($filetransfer_backend_name, $settings) { + variable_set("update_filetransfer_connection_settings::{$filetransfer_backend_name}", $settings); +} +