? .DS_Store ? head.db ? updates-395474-54.patch ? updates-395474-86.patch ? modules/.DS_Store ? modules/update/.DS_Store ? sites/all/.DS_Store ? sites/all/modules/permission_select ? sites/default/files ? sites/default/settings.php 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 07:17:52 -0000 @@ -0,0 +1,282 @@ + 'item', + '#title' => t('Update log'), + '#markup' => theme('item_list', $results['messages']), + ); + return $form; + } + + if (!isset($form_state['values'])) { + $form['projects'] = array(); + // First step. + $form['#theme'] = 'update_available_updates_form'; + 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')) { + switch ($project['status']) { + case UPDATE_NOT_SECURE: + case UPDATE_REVOKED: + $type = 'security'; + break; + case UPDATE_NOT_SUPPORTED: + $type = 'unsupported'; + break; + case UPDATE_UNKNOWN: + case UPDATE_NOT_FETCHED: + case UPDATE_NOT_CHECKED: + case UPDATE_NOT_CURRENT: + $type = 'recommended'; + break; + } + $project['type'] = $type; + $project['title'] = l(check_plain($project['title']), $project['link']); + if ($project['project_type'] == 'theme') { + $project['title'] .= t(' (Theme)'); + } + if ($type == 'unsupported') { + $project['title'] .= t(' (Unsupported)'); + } + elseif ($type == 'security') { + $project['title'] .= t(' (Security Update)'); + } + $project['recommended_version'] = l($project['recommended'], $project['releases'][$project['recommended']]['release_link']); + $form['projects'][$name] = array( + '#type' => 'checkbox', + '#project' => $project, + '#weight' => ($type == 'recommended' ? 0 : -1), + ); + + } + } + } + else { + $form['message'] = array( + '#type' => 'item', + '#markup' => t('There was a problem getting update information. Please try again later.'), + ); + return $form; + } + + if (!count($form['projects'])) { + $form['message'] = array( + '#type' => 'item', + '#markup' => t('All of your projects are up to date.'), + ); + return $form; + } + $form['projects']['#tree'] = TRUE; + $form['submit'] = array( + '#type' => 'submit', + '#value' => t('Continue'), + '#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' => '

', + '#value' => t('Step 1: Backup your site'), + '#suffix' => '

', + ); + + $form['information']['backup_message'] = array( + '#prefix' => '

', + '#value' => 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' => '

', + '#value' => t('Step 2: Provide your server connection details'), + '#suffix' => '

', + ); + + $form['projects'] = array ( + '#type' => 'value', + '#value' => array_keys(array_filter($form_state['values']['projects'])), + ); + + 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' => (variable_get('site_offline', FALSE) == FALSE) ? t('Put site into maintenance mode and install updates') : t('Install updates'), + '#weight' => 100, + ); + } + + return $form; +} + +function update_update_form_validate($form, &$form_state) { + if (isset($form_state['values']['connection_settings'])) { + $filetransfer_backend_to_test = $form_state['values']['connection_settings']['update_filetransfer_default']; + $connection_settings = $form_state['values']['connection_settings'][$filetransfer_backend_to_test]; + $available_backends = module_invoke_all('filetransfer_backends'); + $filetransfer_class = $available_backends[$filetransfer_backend_to_test]['class']; + $filetransfer = call_user_func_array(array($filetransfer_class, 'factory'), array(DRUPAL_ROOT, $connection_settings)); + try { + $filetransfer->connect(); + } + catch(Exception $e) { + form_set_error('connection_settings', $e->getMessage()); + } + } +} + +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'] == 'process_updates') { + // Save the connection settings to the DB. + $filetransfer_backend_to_use = $form_state['values']['connection_settings']['update_filetransfer_default']; + $connection_settings = $form_state['values']['connection_settings'][$filetransfer_backend_to_use]; + + // This is a hack, perhaps we should add an attribute to non-stored fields? + // For SSH keys we wouldn't want to use a password field for instance. + $connection_settings_to_save = array(); + foreach ($connection_settings as $key => $value) { + if ($form['connection_settings'][$filetransfer_backend_to_use][$key]['#type'] != 'password') { + $connection_settings_to_save[$key] = $value; + } + } + // Set this one as the default update method. + variable_set('update_filetransfer_default', $filetransfer_backend_to_use); + // Save the connection settings minus the password + variable_set("update_filetransfer_connection_settings_" . $filetransfer_backend_to_use, $connection_settings_to_save); + + $operations = array(); + foreach ($form_state['values']['projects'] as $project) { + // HACK: Bad workflow here... don't know how to determine what type of project this is. + $project_type = UPDATE_EXTENSION_TYPE_MODULE; + // Put this in the begining (download everything first) + $operations['1_get_' . $project] = array('update_get_extension', array($project)); + // Put these on the end + $operations['2_put_' . $project] = array( + 'update_copy_extension', + array( + $project, + $project_type, + update_get_default_filetransfer($connection_settings) + ), + ); + $operations['3_install_' . $project] = array('update_install_extension', array($project)); + } + ksort($operations); + + $batch = array( + 'title' => t('Installing updates'), + 'init_message' => t('Preparing update operation'), + 'operations' => $operations, + 'finished' => 'update_batch_finished', + ); + batch_set($batch); + } +} + +/** + * Theme project status report. + * + * @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"; + if (isset($form['message'])) { + $output .= drupal_render($form['message']); + return $output; + } + $rows = array(); + drupal_add_css(drupal_get_path('module', 'update') . '/update.css'); + foreach (element_children($form['projects']) as $key) { + $rows[] = array( + 'class' => $form['projects'][$key]['#project']['type'], + 'data' => array( + drupal_render($form['projects'][$key]), + array( + 'class' => 'update-project-name', + 'data' => $form['projects'][$key]['#project']['title'], + ), + $form['projects'][$key]['#project']['existing_version'], + l($form['projects'][$key]['#project']['recommended'], $form['projects'][$key]['#project']['releases'][$form['projects'][$key]['#project']['recommended']]['release_link']), + ), + ); + //$form['project'][$key]; + } + $output .= theme('table', array(theme('table_select_header_cell'), t('Name'), t('Current Version'), t('Recommended Version')), $rows); + $output .= drupal_render_children($form); + return $output; +} +/* + case UPDATE_NOT_SECURE: + case UPDATE_REVOKED: + $type = 'security'; + break; + case UPDATE_NOT_SUPPORTED: + $type = 'unsupported'; + break; + case UPDATE_UNKNOWN: + case UPDATE_NOT_FETCHED: + case UPDATE_NOT_CHECKED: + case UPDATE_NOT_CURRENT: + $type = 'recommended'; +*/ \ No newline at end of file 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 7 Jul 2009 07:17:52 -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 { +.update tr.security, +.update tr.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 */ -} +td.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 7 Jul 2009 07:17:52 -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 7 Jul 2009 07:17:52 -0000 @@ -0,0 +1,12 @@ +(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); \ No newline at end of file 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 7 Jul 2009 07:17:52 -0000 @@ -62,6 +62,16 @@ define('UPDATE_NOT_FETCHED', -3); 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,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': @@ -87,6 +96,12 @@ function update_help($path, $arg) { } } } + + 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': @@ -125,11 +140,12 @@ function update_help($path, $arg) { */ 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, ); @@ -155,15 +171,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 +642,134 @@ function update_flush_caches() { /** * @} 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) { + 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 = ($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) { + 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('Installing %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_default = variable_get('update_filetransfer_default', NULL); + $settings = variable_get("update_filetransfer_connection_settings_" . $update_filetransfer_default, array()); + $settings = array_merge($settings, $overrides); + $available_backends = module_invoke_all('filetransfer_backends'); + $filetransfer = call_user_func_array("{$available_backends[$update_filetransfer_default]['class']}::factory", array(DRUPAL_ROOT, $settings)); + return $filetransfer; +}