? .DS_Store ? head.db ? updates-395474-107.patch ? updates-395474-54.patch ? updates-395474-86.patch ? modules/.DS_Store ? modules/update/.DS_Store ? sites/all/.DS_Store ? sites/all/modules/.DS_Store ? sites/all/modules/dhtml_menu ? sites/default/.DS_Store ? sites/default/files ? sites/default/modules ? sites/default/settings.php Index: update.php =================================================================== RCS file: /cvs/drupal/drupal/update.php,v retrieving revision 1.291 diff -u -p -r1.291 update.php --- update.php 23 Jun 2009 12:06:09 -0000 1.291 +++ update.php 8 Jul 2009 07:01:38 -0000 @@ -651,102 +651,112 @@ 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_init_language(); - - // 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(); +/** + * Main update function: decides which forms to show and shows them. + */ +function update_run_updates() { + // 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); + global $user, $update_free_access; + $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_init_language(); -// 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(); + // Set up theme system for the maintenance page. + drupal_maintenance_theme(); - update_fix_d7_requirements(); - update_fix_compatibility(); + // Check the update requirements for Drupal. + update_check_requirements(); - $op = isset($_REQUEST['op']) ? $_REQUEST['op'] : ''; - switch ($op) { - // update.php ops + // Redirect to the update information page if all requirements were met. + install_goto('update.php?op=info'); + } - case 'selection': - if (isset($_GET['token']) && $_GET['token'] == drupal_get_token('update')) { - $output = update_selection_page(); + 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; + + // 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); } } -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 ($_SERVER['SCRIPT_FILENAME'] == __FILE__) { + update_run_updates(); } 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 8 Jul 2009 07:01:38 -0000 @@ -0,0 +1,250 @@ + 'item', + '#title' => t('Update log'), + '#markup' => theme('item_list', $results['messages']), + ); + return $form; + } + $form['#theme'] = 'update_available_updates_form'; + if (!isset($form_state['values'])) { + $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')) { + 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; + default: + $type = 'skip'; + } + if ($type == 'skip') { + continue; + } + $options[$name]['title'] = l($project['title'], $project['link']); + if ($project['project_type'] == 'theme') { + $options[$name]['title'] .= t(' (Theme)'); + } + if ($type == 'unsupported') { + $options[$name]['title'] .= t(' (Unsupported)'); + $options[$name]['#weight'] = -1; + } + elseif ($type == 'security') { + $options[$name]['title'] .= t(' (Security Update)'); + $options[$name]['#weight'] = -1; + } + $options[$name]['#attributes'] = array('class' => 'update-' . $type); + $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']))))); + } + } + } + else { + $form['message'] = array( + '#type' => 'item', + '#markup' => t('There was a problem getting update information. Please try again later.'), + ); + return $form; + } + + if (!count($options)) { + $form['message'] = array( + '#type' => 'item', + '#markup' => t('All of your projects are up to date.'), + ); + return $form; + } + uasort($options, 'element_sort'); + $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('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('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': @@ -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,161 @@ 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, $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_get_extension_location($name) { + if ($type_and_location = _update_get_extension_type_and_location($name)) { + return $type_and_location['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); +} + + +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; +}