=== modified file 'includes/theme.inc' --- includes/theme.inc 2009-11-08 12:43:40 +0000 +++ includes/theme.inc 2009-11-09 12:42:35 +0000 @@ -1245,6 +1245,78 @@ 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; + } + } + + // If the admin theme is disabled, turn off the setting. + if (in_array(variable_get('admin_theme', 0), $theme_list)) { + variable_del('admin_theme'); + } + + 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-07 13:35:20 +0000 +++ modules/block/block.module 2009-11-09 11:53:15 +0000 @@ -498,34 +498,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/system/system-rtl.css' --- modules/system/system-rtl.css 2009-08-24 03:11:34 +0000 +++ modules/system/system-rtl.css 2009-11-09 13:57:23 +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 1em; +} +.system-themes-list-disabled .theme-selector { + float: right; + padding: 1em 0 1em 1em; +} +.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-08 13:23:41 +0000 +++ modules/system/system.admin.inc 2009-11-09 14:00:58 +0000 @@ -193,11 +193,8 @@ function system_settings_overview() { /** * Menu callback; displays a listing of all themes. - * - * @ingroup forms - * @see system_themes_form_submit() */ -function system_themes_form() { +function system_themes_page() { // Get current list of themes. $themes = system_rebuild_theme_data(); @@ -210,12 +207,16 @@ function system_themes_form() { uasort($themes, 'system_sort_modules_by_info_name'); - $status = array(); - $incompatible_core = array(); - $incompatible_php = array(); + $theme_default = variable_get('theme_default', 'garland'); + $theme_groups = array(); + + foreach ($themes as &$theme) { - foreach ($themes as $theme) { - $screenshot = NULL; + $admin_theme_options[$theme->name] = $theme->info['name']; + $theme->is_default = ($theme->name == $theme_default); + + // 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,50 +228,59 @@ 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; + if (drupal_theme_access($theme)) { + $theme->operations = array( + l(t('Configure'), 'admin/appearance/settings/' . $theme->name), + ); + if (!$theme->is_default) { + $theme->operations[] = l(t('Disable'), 'admin/appearance/disable/' . $theme->name); + $theme->operations[] = l(t('Set default'), 'admin/appearance/default/' . $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 (version_compare(phpversion(), $theme->info['php']) < 0) { - $incompatible_php[$theme->name] = $theme->info['php']; - } + $theme->operations = array( + l(t('Enable'), 'admin/appearance/enable/' . $theme->name), + l(t('Set default'), 'admin/appearance/default/' . $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; } + + // 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'); + $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', @@ -280,7 +290,7 @@ function system_themes_form() { ); $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 +300,40 @@ 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) { - // 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; - } - } - db_update('system') - ->fields(array('status' => 0)) - ->condition('type', 'theme') - ->execute(); - - 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(); - } - } - } - 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'], - ))); - } + variable_set('admin_theme', $form_state['values']['admin_theme']); + variable_set('node_admin_theme', $form_state['values']['node_admin_theme']); +} - // 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']); - } - 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'); +/** + * Menu callback? + */ +function system_set_default_theme($theme) { +// Enable the theme if it is currently disabled. + if (0) { + theme_enable(array($theme)); } - list_themes(TRUE); - menu_rebuild(); - drupal_theme_rebuild(); - drupal_set_message(t('The configuration options have been saved.')); - $form_state['redirect'] = 'admin/appearance'; - - // 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)); + $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' => $admin_theme, + '%selected_theme' => $theme, + ))); + } - return; + variable_set('theme_default', $theme); } /** @@ -776,6 +742,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) { @@ -2397,61 +2376,58 @@ 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 = '
'; + + // There are two possible theme groups. + $states = array( + 'enabled' => format_plural(count($theme_groups['enabled']), 'Enabled theme', 'Enabled themes'), + 'disabled' => format_plural(count($theme_groups['disabled']), 'Disabled theme', 'Disabled themes'), + ); + + foreach ($states as $state => $title) { + if (!count($theme_groups[$state])) { + // Skip this group of themes if no theme is there. continue; } + // Start new theme group. + $output .= '

'. $title .'

'; - // Fetch info - $info = $form[$key]['info']['#value']; - // Localize theme description. - $description = t($info['description']); - // Make sure it is compatible and render the checkbox if so. - if (isset($form['status']['#incompatible_themes_core'][$key])) { - unset($form['status'][$key]); - $status = theme('image', array('path' => 'misc/watchdog-error.png', 'alt' => t('incompatible'), 'title' => t('Incompatible with this version of Drupal core'))); - $description .= '
' . t('This version is incompatible with the !core_version version of Drupal core.', array('!core_version' => VERSION)) . '
'; - } - elseif (isset($form['status']['#incompatible_themes_php'][$key])) { - unset($form['status'][$key]); - $status = theme('image', array('path' => 'misc/watchdog-error.png', 'alt' => t('incompatible'), 'title' => t('Incompatible with this version of PHP'))); - $php_required = $form['status']['#incompatible_themes_php'][$key]; - if (substr_count($php_required, '.') < 2) { - $php_required .= '.*'; - } - $description .= '
' . t('This theme requires PHP version @php_required and is incompatible with PHP version !php_version.', array('@php_required' => $php_required, '!php_version' => phpversion())) . '
'; - } - else { - $status = drupal_render($form['status'][$key]); - } + foreach($theme_groups[$state] as $theme) { - // Style theme info - $theme = '

' . $info['name'] . '

' . $description . '
'; + // Theme the screenshot. + $screenshot = $theme->screenshot ? theme('image', array('path' => $theme->screenshot, 'alt' => t('Screenshot for %theme theme', array('%theme' => $theme->info['name'])), 'title' => '', 'attributes' => array('class' => array('screenshot')), 'getsize' => FALSE)) : t('no screenshot'); - // Build rows - $row = array(); - $row[] = drupal_render($form[$key]['screenshot']); - $row[] = $theme; - $row[] = isset($info['version']) ? $info['version'] : ''; - $row[] = array('data' => $status, 'align' => 'center'); - if ($form['theme_default']) { - $row[] = array('data' => drupal_render($form['theme_default'][$key]), 'align' => 'center'); - $row[] = array('data' => drupal_render($form[$key]['operations']), 'align' => 'center'); + // Localize the theme description. + $description = t($theme->info['description']); + + // Make sure to provide feedback on compatibility. + if (!empty($theme->incompatible_core)) { + $description .= '
' . t('This version is incompatible with the !core_version version of Drupal core.', array('!core_version' => VERSION)) . '
'; + } + elseif (!empty($theme->incompatible_php)) { + if (substr_count($theme->info['php'], '.') < 2) { + $theme->info['php'] .= '.*'; + } + $description .= '
' . t('This theme requires PHP version @php_required and is incompatible with PHP version !php_version.', array('@php_required' => $theme->info['php'], '!php_version' => phpversion())) . '
'; + } + + // Style theme info + $notes = count($theme->notes) ? ' (' . join(', ', $theme->notes) . ')' : ''; + $theme->classes[] = 'theme-selector'; + $theme->classes[] = 'clearfix'; + $output .= '
' . $screenshot . '

' . $theme->info['name'] . ' ' . (isset($theme->info['version']) ? $theme->info['version'] : '') . $notes . '

' . $description . '
' . theme('item_list', array('items' => $theme->operations)) . '
'; } - $rows[] = $row; + $output .= '
'; } + $output .= '
'; - $header = array(t('Screenshot'), t('Name'), t('Version'), t('Enabled'), t('Default'), t('Operations')); - $output = theme('table', array('header' => $header, 'rows' => $rows)); - $output .= drupal_render_children($form); return $output; } === modified file 'modules/system/system.css' --- modules/system/system.css 2009-11-08 11:46:22 +0000 +++ modules/system/system.css 2009-11-09 13:55:43 +0000 @@ -513,9 +513,61 @@ html.js .js-hide { /* ** Styles for the system themes page (admin/appearance) */ -#system-themes-form div.incompatible { +.system-themes-list { + margin-bottom: 1em; +} +.system-themes-list-disabled { + border-top: 1px solid #cdcdcd; + padding-top: 1em; +} +.theme-selector { + padding-top: 1em; +} +.theme-selector .screenshot { + border: 1px solid #e0e0d8; + padding: 2px; +} +.theme-default .screenshot { + border: 1px solid #aaa; +} +.theme-selector h3 { + font-weight: normal; +} +.theme-default h3 { font-weight: bold; } +.system-themes-list-enabled .theme-selector .screenshot { + float: left; /* LTR */ + margin: 0 1em 0 0; /* LTR */ +} +.system-themes-list-enabled .theme-selector h3 { + margin-top: 0; +} +.system-themes-list-disabled .theme-selector { + width: 256px; + float: left; /* LTR */ + padding: 1em 1em 1em 0; /* LTR */ +} +.system-themes-list-disabled .theme-selector .screenshot { + padding-bottom: 0.5em; +} +.theme-selector .operations { + margin-top: 1em; +} +.theme-selector .operations li { + float: left; /* LTR */ + margin: 0; + padding: 0 0.7em; + list-style-type: none; + border-right: 1px solid #cdcdcd; /* LTR */ +} +.theme-selector .operations li.last { + padding: 0 0 0 0.7em; /* LTR */ + border-right: none; /* LTR */ +} +.theme-selector .operations li.first { + padding: 0 0.7em 0 0; /* LTR */ +} /* ** Password strength indicator === modified file 'modules/system/system.module' --- modules/system/system.module 2009-11-08 10:29:23 +0000 +++ modules/system/system.module 2009-11-09 03:50:43 +0000 @@ -134,8 +134,8 @@ function system_help($path, $arg) { */ function system_theme() { return array_merge(drupal_common_theme(), array( - 'system_themes_form' => array( - 'render element' => 'form', + 'system_themes_page' => array( + 'variables' => array('theme_groups' => NULL), 'file' => 'system.admin.inc', ), 'system_settings_form' => array( @@ -582,8 +582,7 @@ function system_menu() { $items['admin/appearance'] = array( 'title' => 'Appearance', 'description' => 'Select and configure your site theme.', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('system_themes_form'), + 'page callback' => 'system_themes_page', 'access arguments' => array('administer site configuration'), 'position' => 'left', 'weight' => -6, === modified file 'modules/update/update.module' --- modules/update/update.module 2009-10-25 19:52:47 +0000 +++ modules/update/update.module 2009-11-09 11:54:50 +0000 @@ -396,22 +396,30 @@ function update_cron() { } /** - * Implement hook_form_FORM_ID_alter(). + * Implement hook_themes_enabled(). * - * Adds a submit handler to the system modules and themes forms, so that if a - * site admin saves either form, we invalidate the cache of available updates. + * If themes are enabled, we invalidate the cache of available updates. + */ +function update_themes_enabled($themes) { + // Clear all update module caches. + _update_cache_clear(); +} + +/** + * Implement hook_themes_disabled(). * - * @see _update_cache_clear() + * If themes are disabled, we invalidate the cache of available updates. */ -function update_form_system_themes_form_alter(&$form, $form_state) { - $form['#submit'][] = 'update_cache_clear_submit'; +function update_themes_disabled($themes) { + // Clear all update module caches. + _update_cache_clear(); } /** * Implement hook_form_FORM_ID_alter(). * - * Adds a submit handler to the system modules and themes forms, so that if a - * site admin saves either form, we invalidate the cache of available updates. + * Adds a submit handler to the system modules form, so that if a site admin + * saves the form, we invalidate the cache of available updates. * * @see _update_cache_clear() */ === modified file 'themes/garland/minnelli/screenshot.png' Binary files themes/garland/minnelli/screenshot.png 2006-10-29 13:17:37 +0000 and themes/garland/minnelli/screenshot.png 2009-11-09 12:03:53 +0000 differ === modified file 'themes/garland/screenshot.png' Binary files themes/garland/screenshot.png 2006-10-29 13:17:37 +0000 and themes/garland/screenshot.png 2009-11-09 12:04:10 +0000 differ === modified file 'themes/seven/screenshot.png' Binary files themes/seven/screenshot.png 2009-08-03 23:34:57 +0000 and themes/seven/screenshot.png 2009-11-09 12:03:30 +0000 differ === modified file 'themes/seven/style.css' --- themes/seven/style.css 2009-10-18 04:35:30 +0000 +++ themes/seven/style.css 2009-11-09 13:45:47 +0000 @@ -671,6 +671,21 @@ div.admin-panel { display: block; } +/* admin/appearance */ +#block-system-main #system-themes-page h2 { + font-weight: normal; + text-transform: uppercase; +} +#block-system-main .theme-selector h3 { + font-weight: normal; +} +#block-system-main .theme-default h3 { + font-weight: bold; +} +#block-system-main .system-themes-list-enabled .theme-selector h3 { + margin-top: 0; +} + /* admin/content and admin/people */ #block-system-main dl.multiselect, #block-system-main dl.multiselect dt, === modified file 'themes/stark/screenshot.png' Binary files themes/stark/screenshot.png 2009-01-30 23:06:23 +0000 and themes/stark/screenshot.png 2009-11-09 12:03:08 +0000 differ