summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDries Buytaert <dries@buytaert.net>2009-12-01 00:39:35 +0000
committerDries Buytaert <dries@buytaert.net>2009-12-01 00:39:35 +0000
commit886cc50e75cc045bfce6e655a169f996b0cd5f5c (patch)
treeec9149e625ad3d8f37ef6bd1fd0ba8b3019bad9e
parent3db9fc144cbf8e5d5687c6c90b9526680de67364 (diff)
downloadbrdo-886cc50e75cc045bfce6e655a169f996b0cd5f5c.tar.gz
brdo-886cc50e75cc045bfce6e655a169f996b0cd5f5c.tar.bz2
- Patch #491214 by JohnAlbin, Gábor Hojtsy, eigentor: implement a more usable top level Appearance page. Yay.
-rw-r--r--includes/theme.inc67
-rw-r--r--modules/block/block.module29
-rw-r--r--modules/block/block.test5
-rw-r--r--modules/simpletest/tests/menu.test5
-rw-r--r--modules/system/system-rtl.css23
-rw-r--r--modules/system/system.admin.inc378
-rw-r--r--modules/system/system.api.php17
-rw-r--r--modules/system/system.css81
-rw-r--r--modules/system/system.module35
-rw-r--r--modules/system/system.test5
-rw-r--r--modules/update/update.module24
-rw-r--r--themes/garland/minnelli/screenshot.pngbin6665 -> 11612 bytes
-rw-r--r--themes/garland/screenshot.pngbin6634 -> 11533 bytes
-rw-r--r--themes/garland/style.css7
-rw-r--r--themes/seven/screenshot.pngbin17598 -> 12684 bytes
-rw-r--r--themes/seven/style.css15
-rw-r--r--themes/stark/screenshot.pngbin6327 -> 12010 bytes
17 files changed, 493 insertions, 198 deletions
diff --git a/includes/theme.inc b/includes/theme.inc
index b3128cfbe..8fb6a5acb 100644
--- a/includes/theme.inc
+++ b/includes/theme.inc
@@ -1243,6 +1243,73 @@ function theme_render_template($template_file, $variables) {
}
/**
+ * 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
diff --git a/modules/block/block.module b/modules/block/block.module
index 0d924653b..38464ffcc 100644
--- a/modules/block/block.module
+++ b/modules/block/block.module
@@ -497,34 +497,11 @@ 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);
}
}
diff --git a/modules/block/block.test b/modules/block/block.test
index 544191915..60f994a84 100644
--- a/modules/block/block.test
+++ b/modules/block/block.test
@@ -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);
diff --git a/modules/simpletest/tests/menu.test b/modules/simpletest/tests/menu.test
index c601b111f..cab0dc9aa 100644
--- a/modules/simpletest/tests/menu.test
+++ b/modules/simpletest/tests/menu.test
@@ -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."));
diff --git a/modules/system/system-rtl.css b/modules/system/system-rtl.css
index 21563a6ca..4c4b0601b 100644
--- a/modules/system/system-rtl.css
+++ b/modules/system/system-rtl.css
@@ -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;
}
diff --git a/modules/system/system.admin.inc b/modules/system/system.admin.inc
index 90930c298..33e22362f 100644
--- a/modules/system/system.admin.inc
+++ b/modules/system/system.admin.inc
@@ -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();
}
/**
@@ -803,6 +877,19 @@ function system_sort_modules_by_info_name($a, $b) {
}
/**
+ * 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 +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
- $theme = '<div class="theme-info"><h2>' . $info['name'] . '</h2><div class="description">' . $description . '</div></div>';
+ // Theme the screenshot.
+ $screenshot = $theme->screenshot ? theme('image', $theme->screenshot) : '<div class="no-screenshot">' . t('no screenshot') . '</div>';
- // 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']);
+
+ // Style theme info
+ $notes = count($theme->notes) ? ' (' . join(', ', $theme->notes) . ')' : '';
+ $theme->classes[] = 'theme-selector';
+ $theme->classes[] = 'clearfix';
+ $output .= '<div class="'. join(' ', $theme->classes) .'">' . $screenshot . '<div class="theme-info"><h3>' . $theme->info['name'] . ' ' . (isset($theme->info['version']) ? $theme->info['version'] : '') . $notes . '</h3><div class="theme-description">' . $description . '</div>';
+
+ // Make sure to provide feedback on compatibility.
+ if (!empty($theme->incompatible_core)) {
+ $output .= '<div class="incompatible">' . t('This version is incompatible with the !core_version version of Drupal core.', array('!core_version' => VERSION)) . '</div>';
+ }
+ elseif (!empty($theme->incompatible_php)) {
+ if (substr_count($theme->info['php'], '.') < 2) {
+ $theme->info['php'] .= '.*';
+ }
+ $output .= '<div class="incompatible">' . 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())) . '</div>';
+ }
+ else {
+ $output .= theme('links', array('links' => $theme->operations, 'attributes' => array('class' => array('operations', 'clearfix'))));
+ }
+ $output .= '</div></div>';
}
- $rows[] = $row;
+ $output .= '</div>';
}
+ $output .= '</div>';
- $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;
}
diff --git a/modules/system/system.api.php b/modules/system/system.api.php
index 082807a0c..24643966a 100644
--- a/modules/system/system.api.php
+++ b/modules/system/system.api.php
@@ -2673,6 +2673,23 @@ function hook_page_delivery_callback_alter(&$callback) {
}
/**
+ * Alters theme operation links.
+ *
+ * @param $theme_groups
+ * An associative array containing groups of themes.
+ *
+ * @see system_themes_page()
+ */
+function hook_system_themes_page_alter(&$theme_groups) {
+ foreach ($theme_groups as $state => &$group) {
+ foreach($theme_groups[$state] as &$theme) {
+ // Add a foo link to each list of theme operations.
+ $theme->operations[] = l(t('Foo'), 'admin/appearance/foo', array('query' => array('theme' => $theme->name)));
+ }
+ }
+}
+
+/**
* Alters inbound URL requests.
*
* @param $path
diff --git a/modules/system/system.css b/modules/system/system.css
index 124e0eaee..8cc025ffa 100644
--- a/modules/system/system.css
+++ b/modules/system/system.css
@@ -522,9 +522,88 @@ html.js .js-hide {
/*
** Styles for the system themes page (admin/appearance)
*/
-#system-themes-form div.incompatible {
+.system-themes-list {
+ margin-bottom: 20px;
+}
+.system-themes-list-disabled {
+ border-top: 1px solid #cdcdcd;
+ padding-top: 20px;
+}
+.system-themes-list h2 {
+ margin: 0;
+}
+.theme-selector {
+ padding-top: 20px;
+}
+.theme-selector .screenshot,
+.theme-selector .no-screenshot {
+ border: 1px solid #e0e0d8;
+ padding: 2px;
+ vertical-align: bottom;
+ width: 294px;
+ height: 219px;
+ line-height: 219px;
+ text-align: center;
+}
+.theme-default .screenshot {
+ border: 1px solid #aaa;
+}
+.system-themes-list-enabled .theme-selector .screenshot,
+.system-themes-list-enabled .theme-selector .no-screenshot {
+ float: left; /* LTR */
+ margin: 0 20px 0 0; /* LTR */
+}
+.system-themes-list-disabled .theme-selector .screenshot,
+.system-themes-list-disabled .theme-selector .no-screenshot {
+ width: 194px;
+ height: 144px;
+ line-height: 144px;
+}
+.theme-selector h3 {
+ font-weight: normal;
+}
+.theme-default h3 {
+ font-weight: bold;
+}
+.system-themes-list-enabled .theme-selector h3 {
+ margin-top: 0;
+}
+.system-themes-list-disabled .theme-selector {
+ width: 300px;
+ float: left; /* LTR */
+ padding: 20px 20px 20px 0; /* LTR */
+}
+.system-themes-list-enabled .theme-info {
+ max-width: 940px;
+}
+.system-themes-list-disabled .theme-info {
+ min-height: 170px;
+}
+.theme-selector .incompatible {
+ margin-top: 10px;
font-weight: bold;
}
+.theme-selector .operations {
+ margin: 10px 0 0 0;
+ padding: 0;
+}
+.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 */
+}
+#system-themes-admin-form {
+ clear: left;
+}
/*
** Password strength indicator
diff --git a/modules/system/system.module b/modules/system/system.module
index a400691ca..57048bbf8 100644
--- a/modules/system/system.module
+++ b/modules/system/system.module
@@ -133,8 +133,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(
@@ -581,23 +581,44 @@ 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,
'file' => 'system.admin.inc',
);
- $items['admin/appearance/select'] = array(
+ $items['admin/appearance/list'] = array(
'title' => 'List',
- 'description' => 'Select the default theme for your site.',
+ 'description' => 'Select and configure your site theme.',
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -1,
'file' => 'system.admin.inc',
);
+ $items['admin/appearance/enable'] = array(
+ 'title' => 'Enable theme',
+ 'page callback' => 'system_theme_enable',
+ 'access arguments' => array('administer site configuration'),
+ 'type' => MENU_CALLBACK,
+ 'file' => 'system.admin.inc',
+ );
+ $items['admin/appearance/disable'] = array(
+ 'title' => 'Disable theme',
+ 'page callback' => 'system_theme_disable',
+ 'access arguments' => array('administer site configuration'),
+ 'type' => MENU_CALLBACK,
+ 'file' => 'system.admin.inc',
+ );
+ $items['admin/appearance/default'] = array(
+ 'title' => 'Set default theme',
+ 'page callback' => 'system_theme_default',
+ 'access arguments' => array('administer site configuration'),
+ 'type' => MENU_CALLBACK,
+ 'file' => 'system.admin.inc',
+ );
$items['admin/appearance/settings'] = array(
- 'title' => 'Configure',
+ 'title' => 'Settings',
'description' => 'Configure default and theme specific settings.',
+ 'page callback' => 'drupal_get_form',
'page arguments' => array('system_theme_settings'),
'access arguments' => array('administer site configuration'),
'type' => MENU_LOCAL_TASK,
diff --git a/modules/system/system.test b/modules/system/system.test
index f8fcaa043..27c327675 100644
--- a/modules/system/system.test
+++ b/modules/system/system.test
@@ -1185,9 +1185,10 @@ class SystemThemeFunctionalTest extends DrupalWebTestCase {
* Test the administration theme functionality.
*/
function testAdministrationTheme() {
+ theme_enable(array('stark'));
+ variable_set('theme_default', 'stark');
// Enable an administration theme and show it on the node admin pages.
$edit = array(
- 'theme_default' => 'stark',
'admin_theme' => 'garland',
'node_admin_theme' => TRUE,
);
@@ -1218,8 +1219,8 @@ class SystemThemeFunctionalTest extends DrupalWebTestCase {
$this->assertRaw('themes/stark', t('Site default theme used on the add content page.'));
// Reset to the default theme settings.
+ variable_set('theme_default', 'garland');
$edit = array(
- 'theme_default' => 'garland',
'admin_theme' => '0',
'node_admin_theme' => FALSE,
);
diff --git a/modules/update/update.module b/modules/update/update.module
index 20f6dbe75..5527472a6 100644
--- a/modules/update/update.module
+++ b/modules/update/update.module
@@ -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()
*/
diff --git a/themes/garland/minnelli/screenshot.png b/themes/garland/minnelli/screenshot.png
index e858b38bf..518871b2f 100644
--- a/themes/garland/minnelli/screenshot.png
+++ b/themes/garland/minnelli/screenshot.png
Binary files differ
diff --git a/themes/garland/screenshot.png b/themes/garland/screenshot.png
index 7a9dcc19f..3075fe017 100644
--- a/themes/garland/screenshot.png
+++ b/themes/garland/screenshot.png
Binary files differ
diff --git a/themes/garland/style.css b/themes/garland/style.css
index 8b2cb6747..f52f5b4e4 100644
--- a/themes/garland/style.css
+++ b/themes/garland/style.css
@@ -1075,6 +1075,13 @@ tr.taxonomy-term-divider-bottom {
border-top: 1px dotted #CCC;
}
+.theme-selector h3 {
+ margin: 10px 0;
+}
+.theme-selector .operations li {
+ list-style-image: none;
+}
+
/**
* CSS support
*/
diff --git a/themes/seven/screenshot.png b/themes/seven/screenshot.png
index 46469c7c7..cfafad3da 100644
--- a/themes/seven/screenshot.png
+++ b/themes/seven/screenshot.png
Binary files differ
diff --git a/themes/seven/style.css b/themes/seven/style.css
index f45f1fca8..1efd71f8b 100644
--- a/themes/seven/style.css
+++ b/themes/seven/style.css
@@ -722,6 +722,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,
diff --git a/themes/stark/screenshot.png b/themes/stark/screenshot.png
index f49fafde1..ca6f3c69e 100644
--- a/themes/stark/screenshot.png
+++ b/themes/stark/screenshot.png
Binary files differ