=== modified file 'includes/theme.inc' --- includes/theme.inc 2009-11-20 04:29:42 +0000 +++ includes/theme.inc 2009-11-24 16:42:57 +0000 @@ -1243,6 +1243,73 @@ function theme_render_template($template } /** + * Enable a given list of themes. + * + * @param $theme_list + * An array of theme names. + */ +function theme_enable($theme_list) { + drupal_clear_css_cache(); + + foreach ($theme_list as $key) { + db_update('system') + ->fields(array('status' => 1)) + ->condition('type', 'theme') + ->condition('name', $key) + ->execute(); + } + + list_themes(TRUE); + menu_rebuild(); + drupal_theme_rebuild(); + + // Notify locale module about new themes being enabled, so translations can + // be imported. This might start a batch, and only return to the redirect + // path after that. + module_invoke('locale', 'system_update', $theme_list); + + // Invoke hook_themes_enabled after the themes have been enabled. + module_invoke_all('themes_enabled', $theme_list); + + return; +} + +/** + * Disable a given list of themes. + * + * @param $theme_list + * An array of theme names. + */ +function theme_disable($theme_list) { + // Don't disable the default theme. + if ($pos = array_search(variable_get('theme_default', 'garland'), $theme_list) !== FALSE) { + unset($theme_list[$pos]); + if (empty($theme_list)) { + return; + } + } + + drupal_clear_css_cache(); + + foreach ($theme_list as $key) { + db_update('system') + ->fields(array('status' => 0)) + ->condition('type', 'theme') + ->condition('name', $key) + ->execute(); + } + + list_themes(TRUE); + menu_rebuild(); + drupal_theme_rebuild(); + + // Invoke hook_themes_enabled after the themes have been enabled. + module_invoke_all('themes_disabled', $theme_list); + + return; +} + +/** * @defgroup themeable Default theme implementations * @{ * Functions and templates that present output to the user, and can be === modified file 'modules/block/block.module' --- modules/block/block.module 2009-11-22 21:24:37 +0000 +++ modules/block/block.module 2009-11-22 22:29:31 +0000 @@ -497,34 +497,11 @@ function block_form_user_profile_form_al } /** - * Implement hook_form_FORM_ID_alter(). - */ -function block_form_system_themes_form_alter(&$form, &$form_state) { - // This function needs to fire before the theme changes are recorded in the - // database, otherwise it will populate the default list of blocks from the - // new theme, which is empty. - array_unshift($form['#submit'], 'block_system_themes_form_submit'); -} - -/** * Initialize blocks for enabled themes. */ -function block_system_themes_form_submit(&$form, &$form_state) { - if ($form_state['values']['op'] == t('Save configuration')) { - if (is_array($form_state['values']['status'])) { - foreach ($form_state['values']['status'] as $key => $choice) { - if ($choice || $form_state['values']['theme_default'] == $key) { - block_theme_initialize($key); - } - } - } - if ($form_state['values']['admin_theme'] && $form_state['values']['admin_theme'] !== variable_get('admin_theme', 0)) { - // If we're changing themes, make sure the theme has its blocks initialized. - $has_blocks = (bool) db_query_range('SELECT 1 FROM {block} WHERE theme = :theme', 0, 1, array(':theme' => $form_state['values']['admin_theme']))->fetchField(); - if (!$has_blocks) { - block_theme_initialize($form_state['values']['admin_theme']); - } - } +function block_themes_enabled($theme_list) { + foreach ($theme_list as $theme) { + block_theme_initialize($theme); } } === modified file 'modules/block/block.test' --- modules/block/block.test 2009-11-10 17:27:53 +0000 +++ modules/block/block.test 2009-11-23 18:21:40 +0000 @@ -237,7 +237,7 @@ class NonDefaultBlockAdmin extends Drupa function testNonDefaultBlockAdmin() { $admin_user = $this->drupalCreateUser(array('administer blocks', 'administer site configuration')); $this->drupalLogin($admin_user); - $this->drupalPost('admin/appearance', array('status[stark]' => 1), t('Save configuration')); + theme_enable(array('stark')); $this->drupalGet('admin/structure/block/list/stark'); } } @@ -277,7 +277,8 @@ class NewDefaultThemeBlocks extends Drup // Turn on the Stark theme and ensure that it contains all of the blocks // that Garland did. - $this->drupalPost('admin/appearance', array('theme_default' => 'stark'), t('Save configuration')); + theme_enable(array('stark')); + variable_set('theme_default', 'stark'); $result = db_query("SELECT * FROM {block} WHERE theme='stark'"); foreach ($result as $block) { unset($block->theme, $block->bid); === modified file 'modules/simpletest/tests/menu.test' --- modules/simpletest/tests/menu.test 2009-11-19 15:50:23 +0000 +++ modules/simpletest/tests/menu.test 2009-11-23 18:17:21 +0000 @@ -80,10 +80,7 @@ class MenuIncTestCase extends DrupalWebT $this->assertRaw('garland/style.css', t("The default theme's CSS appears on the page.")); // Now enable the theme and request it again. - $admin_user = $this->drupalCreateUser(array('administer site configuration')); - $this->drupalLogin($admin_user); - $this->drupalPost('admin/appearance', array('status[stark]' => 1), t('Save configuration')); - $this->drupalLogout(); + theme_enable(array('stark')); $this->drupalGet('menu-test/theme-callback/use-stark-theme'); $this->assertText('Requested theme: stark. Actual theme: stark.', t('The theme callback system uses an optional theme once it has been enabled.')); $this->assertRaw('stark/layout.css', t("The optional theme's CSS appears on the page.")); === modified file 'modules/system/system-rtl.css' --- modules/system/system-rtl.css 2009-08-24 03:11:34 +0000 +++ modules/system/system-rtl.css 2009-11-11 13:10:31 +0000 @@ -90,6 +90,28 @@ input.password-confirm { margin-left: 10px; margin-right: 0; } + +.system-themes-list-enabled .theme-selector .screenshot { + float: right; + margin: 0 0 0 20px; +} +.system-themes-list-disabled .theme-selector { + float: right; + padding: 20px 0 20px 20px; +} +.theme-selector .operations li { + float: right; + border-right: none; + border-left: 1px solid #cdcdcd; +} +.theme-selector .operations li.last { + padding: 0 0.7em 0 0; + border-left: none; +} +.theme-selector .operations li.first { + padding: 0 0 0 0.7em; +} + .password-strength-title { float: right; } === modified file 'modules/system/system.admin.inc' --- modules/system/system.admin.inc 2009-11-22 18:42:55 +0000 +++ modules/system/system.admin.inc 2009-11-24 17:25:44 +0000 @@ -192,12 +192,9 @@ function system_settings_overview() { } /** - * Menu callback; displays a listing of all themes. - * - * @ingroup forms - * @see system_themes_form_submit() + * Retrieve the list of themes that are not hidden. */ -function system_themes_form() { +function _system_theme_list() { // Get current list of themes. $themes = system_rebuild_theme_data(); @@ -209,13 +206,25 @@ function system_themes_form() { } uasort($themes, 'system_sort_modules_by_info_name'); + return $themes; +} - $status = array(); - $incompatible_core = array(); - $incompatible_php = array(); +/** + * Menu callback; displays a listing of all themes. + */ +function system_themes_page() { + // Get current list of themes. + $themes =& _system_theme_list(); + + $theme_default = variable_get('theme_default', 'garland'); + $theme_groups = array(); + + foreach ($themes as &$theme) { + $admin_theme_options[$theme->name] = $theme->info['name']; + $theme->is_default = ($theme->name == $theme_default); - foreach ($themes as $theme) { - $screenshot = NULL; + // Identify theme screenshot. + $theme->screenshot = NULL; // Create a list which includes the current theme and all its base themes. if (isset($themes[$theme->name]->base_themes)) { $theme_keys = array_keys($themes[$theme->name]->base_themes); @@ -227,60 +236,91 @@ function system_themes_form() { // Look for a screenshot in the current theme or in its closest ancestor. foreach (array_reverse($theme_keys) as $theme_key) { if (isset($themes[$theme_key]) && file_exists($themes[$theme_key]->info['screenshot'])) { - $screenshot = $themes[$theme_key]->info['screenshot']; + $theme->screenshot = $themes[$theme_key]->info['screenshot']; break; } } - $screenshot = $screenshot ? theme('image', array('path' => $screenshot, 'alt' => t('Screenshot for !theme theme', array('!theme' => $theme->info['name'])), 'title' => '', 'attributes' => array('class' => array('screenshot')), 'getsize' => FALSE)) : t('no screenshot'); - - $form[$theme->name]['screenshot'] = array('#markup' => $screenshot); - $form[$theme->name]['info'] = array( - '#type' => 'value', - '#value' => $theme->info, - ); - $options[$theme->name] = $theme->info['name']; - $form[$theme->name]['operations'] = drupal_theme_access($theme) ? array('#type' => 'link', '#title' => t('configure'), '#href' => 'admin/appearance/settings/' . $theme->name) : array(); - - if (!empty($theme->status)) { - $status[] = $theme->name; - } - else { - // Ensure this theme is compatible with this version of core. - // Require the 'content' region to make sure the main page - // content has a common place in all themes. - if (!isset($theme->info['core']) || ($theme->info['core'] != DRUPAL_CORE_COMPATIBILITY) || (!isset($theme->info['regions']['content']))) { - $incompatible_core[] = $theme->name; + if (empty($theme->status)) { + // Ensure this theme is compatible with this version of core. + // Require the 'content' region to make sure the main page + // content has a common place in all themes. + $theme->incompatible_core = !isset($theme->info['core']) || ($theme->info['core'] != DRUPAL_CORE_COMPATIBILITY) || (!isset($theme->info['regions']['content'])); + $theme->incompatible_php = version_compare(phpversion(), $theme->info['php']) < 0; + } + $query['token'] = drupal_get_token('system-theme-operation-link'); + $theme->operations = array(); + if (!empty($theme->status) || !$theme->incompatible_core && !$theme->incompatible_php) { + // Create the operations links. + $query['theme'] = $theme->name; + if (drupal_theme_access($theme)) { + $theme->operations[] = array( + 'title' => t('Configure'), + 'href' => 'admin/appearance/settings/' . $theme->name, + ); } - if (version_compare(phpversion(), $theme->info['php']) < 0) { - $incompatible_php[$theme->name] = $theme->info['php']; + if (!empty($theme->status)) { + if (!$theme->is_default) { + $theme->operations[] = array( + 'title' => t('Disable'), + 'href' => 'admin/appearance/disable', + 'query' => $query, + ); + $theme->operations[] = array( + 'title' => t('Set default'), + 'href' => 'admin/appearance/default', + 'query' => $query, + ); + } } + else { + $theme->operations[] = array( + 'title' => t('Enable'), + 'href' => 'admin/appearance/enable', + 'query' => $query, + ); + $theme->operations[] = array( + 'title' => t('Set default'), + 'href' => 'admin/appearance/default', + 'query' => $query, + ); + } + } + + // Add notes to default and administration theme. + $theme->notes = array(); + $theme->classes = array(); + if ($theme->is_default) { + $theme->classes[] = 'theme-default'; + $theme->notes[] = t('default theme'); } + + // Sort enabled and disabled themes into their own groups. + $theme_groups[$theme->status ? 'enabled' : 'disabled'][] = $theme; } - $form['status'] = array( - '#type' => 'checkboxes', - '#options' => array_fill_keys(array_keys($options), ''), - '#default_value' => $status, - '#incompatible_themes_core' => drupal_map_assoc($incompatible_core), - '#incompatible_themes_php' => $incompatible_php, - ); - $form['theme_default'] = array( - '#type' => 'radios', - '#options' => array_fill_keys(array_keys($options), ''), - '#default_value' => variable_get('theme_default', 'garland'), - ); + uasort($theme_groups['enabled'], 'system_sort_themes'); + drupal_alter('system_themes_page', $theme_groups); + + $admin_form = drupal_get_form('system_themes_admin_form', $admin_theme_options); + return theme('system_themes_page', array('theme_groups' => $theme_groups)) . drupal_render($admin_form); +} +/** + * Form to select the administration theme. + * + * @ingroup forms + * @see system_themes_admin_form_submit() + */ +function system_themes_admin_form($form, &$form_state, $theme_options) { // Administration theme settings. $form['admin_theme'] = array( '#type' => 'fieldset', '#title' => t('Administration theme'), - '#collapsible' => TRUE, - '#collapsed' => TRUE, ); $form['admin_theme']['admin_theme'] = array( '#type' => 'select', - '#options' => array(0 => t('Default theme')) + $options, + '#options' => array(0 => t('Default theme')) + $theme_options, '#title' => t('Administration theme'), '#description' => t('Choose "Default theme" to always use the same theme as the rest of the site.'), '#default_value' => variable_get('admin_theme', 0), @@ -290,84 +330,107 @@ function system_themes_form() { '#title' => t('Use the administration theme when editing or creating content'), '#default_value' => variable_get('node_admin_theme', '0'), ); - - $form['buttons']['submit'] = array( + $form['admin_theme']['submit'] = array( '#type' => 'submit', '#value' => t('Save configuration'), ); - return $form; } /** - * Process system_themes_form form submissions. + * Process system_themes_admin_form form submissions. */ -function system_themes_form_submit($form, &$form_state) { - drupal_clear_css_cache(); +function system_themes_admin_form_submit($form, &$form_state) { + drupal_set_message(t('The configuration options have been saved.')); + variable_set('admin_theme', $form_state['values']['admin_theme']); + variable_set('node_admin_theme', $form_state['values']['node_admin_theme']); +} + +/** + * Menu callback; Enables a theme. + */ +function system_theme_enable() { + if (isset($_REQUEST['theme']) && isset($_REQUEST['token']) && drupal_valid_token($_REQUEST['token'], 'system-theme-operation-link')) { + $theme = $_REQUEST['theme']; + // Get current list of themes. + $themes =& _system_theme_list(); - // Store list of previously enabled themes and disable all themes - $old_theme_list = $new_theme_list = array(); - foreach (list_themes() as $theme) { - if ($theme->status) { - $old_theme_list[] = $theme->name; + // Check if the specified theme is one recognized by the system. + if (!empty($themes[$theme])) { + theme_enable(array($theme)); + drupal_set_message(t('The %theme theme has been enabled.', array('%theme' => $themes[$theme]->info['name']))); + } + else { + drupal_set_message(t('The %theme theme was not found.', array('%theme' => $theme)), 'error'); } + drupal_goto('admin/appearance'); } - db_update('system') - ->fields(array('status' => 0)) - ->condition('type', 'theme') - ->execute(); + return drupal_access_denied(); +} - if ($form_state['values']['op'] == t('Save configuration')) { - if (is_array($form_state['values']['status'])) { - foreach ($form_state['values']['status'] as $key => $choice) { - // Always enable the default theme, despite its status checkbox being checked: - if ($choice || $form_state['values']['theme_default'] == $key) { - $new_theme_list[] = $key; - db_update('system') - ->fields(array('status' => 1)) - ->condition('type', 'theme') - ->condition('name', $key) - ->execute(); - } +/** + * Menu callback; Disables a theme. + */ +function system_theme_disable() { + if (isset($_REQUEST['theme']) && isset($_REQUEST['token']) && drupal_valid_token($_REQUEST['token'], 'system-theme-operation-link')) { + $theme = $_REQUEST['theme']; + // Get current list of themes. + $themes =& _system_theme_list(); + + // Check if the specified theme is one recognized by the system. + if (!empty($themes[$theme])) { + if ($theme == variable_get('theme_default', 'garland')) { + // Don't disable the default theme. + drupal_set_message(t('%theme is the default theme and cannot be disabled.', array('%theme' => $themes[$theme]->info['name'])), 'error'); + } + else { + theme_disable(array($theme)); + drupal_set_message(t('The %theme theme has been disabled.', array('%theme' => $themes[$theme]->info['name']))); } } - if ($form_state['values']['admin_theme'] && $form_state['values']['admin_theme'] != $form_state['values']['theme_default']) { - drupal_set_message(t('Please note that the administration theme is still set to the %admin_theme theme; consequently, the theme on this page remains unchanged. All non-administrative sections of the site, however, will show the selected %selected_theme theme by default.', array( - '%admin_theme' => $form_state['values']['admin_theme'], - '%selected_theme' => $form_state['values']['theme_default'], - ))); + else { + drupal_set_message(t('The %theme theme was not found.', array('%theme' => $theme)), 'error'); } - - // Save the variables. - variable_set('theme_default', $form_state['values']['theme_default']); - variable_set('admin_theme', $form_state['values']['admin_theme']); - variable_set('node_admin_theme', $form_state['values']['node_admin_theme']); + drupal_goto('admin/appearance'); } - else { - // Revert to defaults: only Garland is enabled. - variable_del('theme_default'); - variable_del('admin_theme'); - variable_del('node_admin_theme'); - db_update('system') - ->fields(array('status' => 1)) - ->condition('type', 'theme') - ->condition('name', 'garland') - ->execute(); - $new_theme_list = array('garland'); - } - - list_themes(TRUE); - menu_rebuild(); - drupal_theme_rebuild(); - drupal_set_message(t('The configuration options have been saved.')); - $form_state['redirect'] = 'admin/appearance'; + return drupal_access_denied(); +} - // Notify locale module about new themes being enabled, so translations can - // be imported. This might start a batch, and only return to the redirect - // path after that. - module_invoke('locale', 'system_update', array_diff($new_theme_list, $old_theme_list)); +/** + * Menu callback; Set the default theme. + */ +function system_theme_default() { + if (isset($_REQUEST['theme']) && isset($_REQUEST['token']) && drupal_valid_token($_REQUEST['token'], 'system-theme-operation-link')) { + $theme = $_REQUEST['theme']; + // Get current list of themes. + $themes =& _system_theme_list(); - return; + // Check if the specified theme is one recognized by the system. + if (!empty($themes[$theme])) { + // Enable the theme if it is currently disabled. + if (empty($themes[$theme]->status)) { + theme_enable(array($theme)); + } + // Set the default theme. + variable_set('theme_default', $theme); + // The status message depends on whether an admin theme is currently in use. + $admin_theme = variable_get('admin_theme', 0); + if ($admin_theme != $theme) { + drupal_set_message(t('Please note that the administration theme is still set to the %admin_theme theme; consequently, the theme on this page remains unchanged. All non-administrative sections of the site, however, will show the selected %selected_theme theme by default.', array( + '%admin_theme' => $themes[$admin_theme]->info['name'], + '%selected_theme' => $themes[$theme]->info['name'], + ))); + } + else { + drupal_set_message(t('%theme is now the default theme.', array('%theme' => $themes[$theme]->info['name']))); + } + } + else { + drupal_set_message(t('The %theme theme was not found.', array('%theme' => $theme)), 'error'); + } + drupal_goto('admin/appearance'); + } + return drupal_access_denied(); } /** @@ -803,6 +866,19 @@ function system_sort_modules_by_info_nam } /** + * Array sorting callback; sorts modules or themes by their name. + */ +function system_sort_themes($a, $b) { + if ($a->is_default) { + return -1; + } + if ($b->is_default) { + return 1; + } + return strcasecmp($a->info['name'], $b->info['name']); +} + +/** * Build a table row for the system modules page. */ function _system_modules_build_row($info, $extra) { @@ -2418,61 +2494,62 @@ function theme_system_modules_uninstall( * * @param $variables * An associative array containing: - * - form: An associative array containing the structure of the form. + * - theme_groups: An associative array containing groups of themes. * * @ingroup themeable */ -function theme_system_themes_form($variables) { - $form = $variables['form']; +function theme_system_themes_page($variables) { + $theme_groups = $variables['theme_groups']; - foreach (element_children($form) as $key) { - // Only look for themes - if (!isset($form[$key]['info'])) { + $output = '