Commit 886cc50e authored by Dries's avatar Dries
Browse files

- Patch #491214 by JohnAlbin, Gábor Hojtsy, eigentor: implement a more usable...

- Patch #491214 by JohnAlbin, Gábor Hojtsy, eigentor: implement a more usable top level Appearance page. Yay.
parent 3db9fc14
......@@ -1242,6 +1242,73 @@ function theme_render_template($template_file, $variables) {
return ob_get_clean(); // End buffering and return its contents
}
/**
* 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
* @{
......
......@@ -496,35 +496,12 @@ function block_form_user_profile_form_alter(&$form, &$form_state) {
}
}
/**
* 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);
}
}
......
......@@ -237,7 +237,7 @@ class NonDefaultBlockAdmin extends DrupalWebTestCase {
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 DrupalWebTestCase {
// 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);
......
......@@ -80,10 +80,7 @@ class MenuIncTestCase extends DrupalWebTestCase {
$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."));
......
......@@ -90,6 +90,29 @@ input.password-confirm {
margin-left: 10px;
margin-right: 0;
}
.system-themes-list-enabled .theme-selector .screenshot,
.system-themes-list-enabled .theme-selector .no-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;
}
......
......@@ -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,102 @@ 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 = array(
'path' => $themes[$theme_key]->info['screenshot'],
'alt' => t('Screenshot for !theme theme', array('!theme' => $theme->info['name'])),
'title' => t('Screenshot for !theme theme', array('!theme' => $theme->info['name'])),
'attributes' => array('class' => array('screenshot')),
'getsize' => FALSE,
);
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 (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;
}
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;
$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('Settings'),
'href' => 'admin/appearance/settings/' . $theme->name,
'attributes' => array('title' => t('Settings for !theme theme', array('!theme' => $theme->info['name']))),
);
}
if (!empty($theme->status)) {
if (!$theme->is_default) {
$theme->operations[] = array(
'title' => t('Disable'),
'href' => 'admin/appearance/disable',
'query' => $query,
'attributes' => array('title' => t('Disable !theme theme', array('!theme' => $theme->info['name']))),
);
$theme->operations[] = array(
'title' => t('Set default'),
'href' => 'admin/appearance/default',
'query' => $query,
'attributes' => array('title' => t('Set !theme as default theme', array('!theme' => $theme->info['name']))),
);
}
}
if (version_compare(phpversion(), $theme->info['php']) < 0) {
$incompatible_php[$theme->name] = $theme->info['php'];
else {
$theme->operations[] = array(
'title' => t('Enable'),
'href' => 'admin/appearance/enable',
'query' => $query,
'attributes' => array('title' => t('Enable !theme theme', array('!theme' => $theme->info['name']))),
);
}
}
// 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'),
// There are two possible theme groups.
$theme_group_titles = array(
'enabled' => format_plural(count($theme_groups['enabled']), 'Enabled theme', 'Enabled themes'),
'disabled' => format_plural(count($theme_groups['disabled']), 'Disabled theme', 'Disabled themes'),
);
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, 'theme_group_titles' => $theme_group_titles)) . 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 +341,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();
}
/**
......@@ -802,6 +876,19 @@ function system_sort_modules_by_info_name($a, $b) {
return strcasecmp($a->info['name'], $b->info['name']);
}
/**
* 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.
*/
......@@ -2418,61 +2505,56 @@ function theme_system_modules_uninstall($variables) {
*
* @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 = '<div id="system-themes-page">';
foreach ($variables['theme_group_titles'] as $state => $title) {
if (!count($theme_groups[$state])) {
// Skip this group of themes if no theme is there.
continue;
}
// Start new theme group.
$output .= '<div class="system-themes-list system-themes-list-'. $state .' clearfix"><h2>'. $title .'</h2>';
// 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 .= '<div class="incompatible">' . t('This version is incompatible with the !core_version version of Drupal core.', array('!core_version' => VERSION)) . '</div>';
}
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 .= '<div class="incompatible">' . t('This theme requires PHP version @php_required and is incompatible with PHP version !php_version.', array('@php_required' => $php_required, '!php_version' => phpversion())) . '</div>';
}
else {
$status = drupal_render($form['status'][$key]);
}
foreach($theme_groups[$state] as $theme) {
// Style theme info