Index: modules/system/system.admin.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.admin.inc,v retrieving revision 1.158 diff -u -p -r1.158 system.admin.inc --- modules/system/system.admin.inc 19 Jun 2009 20:35:05 -0000 1.158 +++ modules/system/system.admin.inc 26 Jun 2009 02:18:58 -0000 @@ -1082,6 +1082,174 @@ function system_modules_uninstall_submit } /** + * Menu callback for the module/theme installer. + */ +function system_installer() { + $errors = array(); + $data_set = array(); + + $modules = empty($_GET['modules']) ? array() : $_GET['modules']; + $existing_modules = system_get_module_data(); + $md5s = empty($_GET['md5s']) ? array() : $_GET['md5s']; + list($errors['modules'], $data_set['modules']) = _system_installer_data($modules, $existing_modules, $md5s, 'module'); + + $themes = empty($_GET['themes']) ? array() : $_GET['themes']; + $existing_themes = system_get_theme_data(); + list($errors['themes'], $data_set['themes']) = _system_installer_data($themes, $existing_themes, $md5s, 'theme'); + + if (empty($data_set['modules']) && empty($data_set['themes'])) { + $errors['modules'][0][] = t('You must choose at least one module or theme.'); + } + + foreach ($errors as $type => $objects) { + foreach ($objects as $name => $error_set) { + foreach ($error_set as $error) { + drupal_set_message($error, 'error'); + } + } + } + return drupal_get_form('system_installer_form', $data_set, $errors); +} + +function _system_installer_data($plugins, $existing_plugins, $md5s, $type) { + foreach ($plugins as $plugin => $release) { + if (isset($existing_plugins[$plugin])) { + $errors[$plugin]['install'] = t('The %plugin @type is already available on the @types page.', array('%plugin' => $existing_plugins[$plugin]->info['name'], '@url' => url('admin/build/' . $type .'s'), '@type' => $type)); + $data_set[$plugin] = array(array('title' => $existing_plugins[$plugin]->info['name']), $existing_plugins[$plugin]->info['version']); + } + else { + $project = array( + 'name' => $plugin, + 'info' => array( + 'version' => $release, + ), + ); + drupal_function_exists('_update_build_fetch_url'); + $url = _update_build_fetch_url($project); + $result = drupal_http_request($url); + $data = @update_parse_xml(array($result->data)); + if (empty($data[$plugin]) || empty($data[$plugin]['releases']) || empty($data[$plugin]['releases'][$release])) { + $errors[$plugin]['invalid'] = t('Could not find the %plugin @type.', array('%plugin' => $plugin, '@type' => $type)); + $data_set[$plugin] = array(array('title' => $plugin), $release); + continue; + } + else { + $data_set[$plugin] = array($data[$plugin], $release); + } + $mdhash = isset($data[$plugin]['releases'][$release]['mdhash']) ? $data[$plugin]['releases'][$release]['mdhash'] : FALSE; + if (!isset($md5s[$plugin]) || !$mdhash) { + $errors[$plugin]['md5'] = t('No md5 checksum was provided for %plugin.', array('%plugin' => $data[$plugin]['title'])); + } + elseif ($mdhash != $md5s[$plugin]) { + $errors[$plugin]['md5'] = t('Incorrect md5 checksum was provided for %plugin.', array('%plugin' => $data[$plugin]['title'])); + } + } + } + + return array($errors, $data_set); +} + +/** + * Forms API callback for the installer page. + */ +function system_installer_form($form_state, $data_set, $errors) { + $form = array(); + + $form['data'] = array( + '#type' => 'value', + '#value' => $data_set, + ); + + $form['#errors'] = $errors; + + foreach (array('themes', 'modules') as $type) { + if (!empty($data_set[$type])) { + foreach ($data_set[$type] as $plugin => $data) { + $form[$type][$plugin] = array('#tree' => TRUE); + if (isset($errors[$type][$plugin])) { + // Set the "fetch" element to FALSE because an error occurred and the + // module or theme should not be installed. + $form[$type][$plugin]['fetch'] = array( + '#type' => 'value', + '#value' => FALSE, + ); + $form[$type][$plugin]['error'] = array( + '#markup' => theme('image', 'misc/watchdog-error.png'), + ); + } + else { + $form[$type][$plugin]['fetch'] = array( + '#type' => 'checkbox', + '#default_value' => TRUE, + ); + } + } + } + } + + // @TODO: Create a reusable function to create a list of radios/settings for + // individual backends that can be used both for this form and the update + // form. + $form['file_transfer'] = array(); + + $form['install'] = array( + '#type' => 'submit', + '#value' => t('Install'), + ); + + return $form; +} + +/** + * Theming callback for system_installer_form(). + */ +function theme_system_installer_form($form) { + $output = ''; + + $output .= '

' . t('The following modules and themes are about to be installed on the site. Please take a moment to look this over and make sure everything is correct.') . '

'; + + $data_set = $form['data']['#value']; + $errors = $form['#errors']; + + foreach (array('modules', 'themes') as $type) { + if (!empty($data_set['modules'])) { + $title = ($type == 'modules' ? t('Modules') : t('Themes')); + $output .= '

' . $title . '

'; + + $rows = array(); + foreach ($data_set[$type] as $plugin => $data) { + list($data, $release) = $data; + $row = array(); + if (!isset($form['#errors'][$type][$plugin]) || isset($form['#errors'][$type][$plugin]['md5'])) { + $row[] = isset($form['#errors'][$type][$plugin]['md5']) ? drupal_render($form[$type][$plugin]['error']) : drupal_render($form[$type][$plugin]['fetch']); + $row[] = l($data['title'], $data['link']); + $row[] = l($release, $data['releases'][$release]['release_link']); + $row[] = format_date($data['releases'][$release]['date']); + $row[] = isset($form['#errors'][$type][$plugin]) ? theme('image', 'misc/watchdog-error.png') : theme('image', 'misc/watchdog-ok.png'); + $rows[] = isset($form['#errors'][$type][$plugin]) ? array('data' => $row, 'class' => 'error') : $row; + } + else { + $row[] = drupal_render($form[$type][$plugin]['error']); + $row[] = $data['title']; + $row[] = $release; + $row[] = t('Unknown'); + $row[] = theme('image', 'misc/watchdog-error.png'); + $rows[] = array( + 'data' => $row, + 'class' => 'error', + ); + } + } + $output .= theme('table', array(t('Fetch'), $title, t('Version'), t('Released'), t('MD5 sum confirmed')), $rows); + } + } + + $output .= drupal_render_children($form); + + return $output; +} + +/** * Menu callback. Display blocked IP addresses. */ function system_ip_blocking() { Index: modules/system/system.module =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.module,v retrieving revision 1.715 diff -u -p -r1.715 system.module --- modules/system/system.module 23 Jun 2009 12:11:19 -0000 1.715 +++ modules/system/system.module 25 Jun 2009 04:44:11 -0000 @@ -154,6 +154,10 @@ function system_theme() { 'arguments' => array('form' => NULL), 'file' => 'system.admin.inc', ), + 'system_installer_form' => array( + 'arguments' => array('form' => NULL), + 'file' => 'system.admin.inc', + ), 'status_report' => array( 'arguments' => array('requirements' => NULL), 'file' => 'system.admin.inc', @@ -597,6 +601,16 @@ function system_menu() { 'type' => MENU_CALLBACK, ); + $items['admin/build/install'] = array( + 'title' => 'Install', + 'page callback' => 'system_installer', + // Note that, while anyone with "administer site configuration" permissions + // can access the page, only individuals with file transfer credentials can + // actually proceed to install modules and themes from drupal.org. + 'access arguments' => array('administer site configuration'), + 'type' => MENU_CALLBACK, + ); + // Development menu category. $items['admin/development'] = array( 'title' => 'Development',