Commit e9f52b42 authored by Gábor Hojtsy's avatar Gábor Hojtsy

#141727 by merlinofchaos, dvessel, sun: restore themeability support for...

#141727 by merlinofchaos, dvessel, sun: restore themeability support for maintenance pages (regression)
parent 6d4b0a24
......@@ -369,7 +369,6 @@ function conf_init() {
*/
function drupal_get_filename($type, $name, $filename = NULL) {
static $files = array();
global $active_db;
if (!isset($files[$type])) {
$files[$type] = array();
......@@ -385,7 +384,7 @@ function drupal_get_filename($type, $name, $filename = NULL) {
// the database. This is required because this function is called both
// before we have a database connection (i.e. during installation) and
// when a database connection fails.
elseif ($active_db && (($file = db_result(db_query("SELECT filename FROM {system} WHERE name = '%s' AND type = '%s'", $name, $type))) && file_exists($file))) {
elseif (db_is_active() && (($file = db_result(db_query("SELECT filename FROM {system} WHERE name = '%s' AND type = '%s'", $name, $type))) && file_exists($file))) {
$files[$type][$name] = $file;
}
else {
......@@ -971,35 +970,16 @@ function _drupal_bootstrap($phase) {
}
/**
* Enables use of the theme system without requiring database access. Since
* there is not database access no theme will be enabled and the default
* themeable functions will be called. Some themeable functions can not be used
* without the full Drupal API loaded. For example, theme_page() is
* unavailable and theme_maintenance_page() must be used in its place.
* Enables use of the theme system without requiring database access.
*
* Loads and initializes the theme system for site installs, updates and when
* the site is in off-line mode. This also applies when the database fails.
*
* @see _drupal_maintenance_theme()
*/
function drupal_maintenance_theme() {
global $theme;
require_once './includes/path.inc';
require_once './includes/theme.inc';
require_once './includes/common.inc';
require_once './includes/unicode.inc';
require_once './includes/file.inc';
require_once './modules/filter/filter.module';
unicode_check();
drupal_add_css(drupal_get_path('module', 'system') .'/defaults.css', 'module');
drupal_add_css(drupal_get_path('module', 'system') .'/system.css', 'module');
drupal_add_css(drupal_get_path('module', 'system') .'/system-menus.css', 'module');
$theme = '';
// Special case registry of theme functions used by the installer
$themes = drupal_common_themes();
foreach ($themes as $hook => $info) {
if (!isset($info['file']) && !isset($info['function'])) {
$themes[$hook]['function'] = 'theme_'. $hook;
$themes[$hook]['theme path'] = 'misc';
}
}
_theme_set_registry($themes);
require_once './includes/theme.maintenance.inc';
_drupal_maintenance_theme();
}
/**
......
......@@ -2865,7 +2865,7 @@ function element_children($element) {
/**
* Provide theme registration for themes across .inc files.
*/
function drupal_common_themes() {
function drupal_common_theme() {
return array(
// theme.inc
'placeholder' => array(
......@@ -2876,6 +2876,10 @@ function drupal_common_themes() {
'template' => 'page',
),
'maintenance_page' => array(
'arguments' => array('content' => NULL, 'show_blocks' => TRUE, 'show_messages' => TRUE),
'template' => 'maintenance-page',
),
'update_page' => array(
'arguments' => array('content' => NULL, 'show_messages' => TRUE),
),
'install_page' => array(
......
......@@ -163,6 +163,14 @@ function db_set_active($name = 'default') {
return array_search($previous_db, $db_conns);
}
/**
* Returns a boolean depending on the availability of the database.
*/
function db_is_active() {
global $active_db;
return !empty($active_db);
}
/**
* Helper function for db_query().
*/
......
......@@ -615,7 +615,6 @@ function st($string, $args = array()) {
}
require_once './includes/theme.inc';
$GLOBALS['theme'] = 'theme';
// Transform arguments before inserting them
foreach ($args as $key => $value) {
switch ($key[0]) {
......
This diff is collapsed.
<?php
// $Id$
/**
* @file
* Theming for maintenance pages.
*/
/**
* Sets up the theming system for site installs, updates and when the site is
* in off-line mode. It also applies when the database is unavailable.
*
* Minnelli is always used for the initial install and update operations. In
* other cases, "settings.php" must have a "maintenance_theme" key set for the
* $conf variable in order to change the maintenance theme.
*/
function _drupal_maintenance_theme() {
global $theme, $theme_key;
// If $theme is already set, assume the others are set too, and do nothing.
if (isset($theme)) {
return;
}
require_once './includes/path.inc';
require_once './includes/theme.inc';
require_once './includes/common.inc';
require_once './includes/unicode.inc';
require_once './includes/file.inc';
require_once './includes/module.inc';
require_once './includes/database.inc';
unicode_check();
// Load module basics (needed for hook invokes).
$module_list['system']['filename'] = 'modules/system/system.module';
$module_list['filter']['filename'] = 'modules/filter/filter.module';
module_list(TRUE, FALSE, FALSE, $module_list);
drupal_load('module', 'system');
drupal_load('module', 'filter');
$themes = list_themes();
// Install and update pages are treated differently to prevent theming overrides.
if (defined('MAINTENANCE_MODE') && (MAINTENANCE_MODE == 'install' || MAINTENANCE_MODE == 'update')) {
$theme = 'minnelli';
}
else {
$theme = variable_get('maintenance_theme', 'minnelli');
}
// Store the identifier for retrieving theme settings with.
$theme_key = $theme;
// Find all our ancestor themes and put them in an array.
$base_theme = array();
$ancestor = $theme;
while ($ancestor && isset($themes[$ancestor]->base_theme)) {
$base_theme[] = $new_base_theme = $themes[$themes[$ancestor]->base_theme];
$ancestor = $themes[$ancestor]->base_theme;
}
_init_theme($themes[$theme], array_reverse($base_theme), '_theme_load_offline_registry');
// These are usually added from system_init() -except maintenance.css.
// When the database is inactive it's not called so we add it here.
drupal_add_css(drupal_get_path('module', 'system') .'/defaults.css', 'module');
drupal_add_css(drupal_get_path('module', 'system') .'/system.css', 'module');
drupal_add_css(drupal_get_path('module', 'system') .'/system-menus.css', 'module');
drupal_add_css(drupal_get_path('module', 'system') .'/maintenance.css', 'module');
}
/**
* This builds the registry when the site needs to bypass any database calls.
*/
function _theme_load_offline_registry($theme, $base_theme = NULL, $theme_engine = NULL) {
$registry = _theme_build_registry($theme, $base_theme, $theme_engine);
_theme_set_registry($registry);
}
/**
* Return a themed list of maintenance tasks to perform.
*/
function theme_task_list($items, $active = NULL) {
$done = isset($items[$active]) || $active == NULL;
$output = '<ol class="task-list">';
foreach ($items as $k => $item) {
if ($active == $k) {
$class = 'active';
$done = false;
}
else {
$class = $done ? 'done' : '';
}
$output .= '<li class="'. $class .'">'. $item .'</li>';
}
$output .= '</ol>';
return $output;
}
/**
* Generate a themed installation page.
*
* Note: this function is not themeable.
*
* @param $content
* The page content to show.
*/
function theme_install_page($content) {
drupal_set_header('Content-Type: text/html; charset=utf-8');
// Assign content.
$variables['content'] = $content;
// Delay setting the message variable so it can be processed below.
$variables['show_messages'] = FALSE;
// The maintenance preprocess function is recycled here.
template_preprocess_maintenance_page($variables);
// Special handling of error messages
$messages = drupal_set_message();
if (isset($messages['error'])) {
$title = count($messages['error']) > 1 ? st('The following errors must be resolved before you can continue the installation process') : st('The following error must be resolved before you can continue the installation process');
$variables['messages'] .= '<h3>'. $title .':</h3>';
$variables['messages'] .= theme('status_messages', 'error');
$variables['content'] .= '<p>'. st('Please check the error messages and <a href="!url">try again</a>.', array('!url' => request_uri())) .'</p>';
}
// Special handling of status messages
if (isset($messages['status'])) {
$warnings = count($messages['status']) > 1 ? st('The following installation warnings should be carefully reviewed, but in most cases may be safely ignored') : st('The following installation warning should be carefully reviewed, but in most cases may be safely ignored');
$variables['messages'] .= '<h4>'. $title .':</h4>';
$variables['messages'] .= theme('status_messages', 'status');
}
return theme_render_template('themes/garland/maintenance-page.tpl.php', $variables);
}
/**
* Generate a themed update page.
*
* Note: this function is not themeable.
*
* @param $content
* The page content to show.
* @param $show_messages
* Whether to output status and error messages.
* FALSE can be useful to postpone the messages to a subsequent page.
*/
function theme_update_page($content, $show_messages = TRUE) {
// Set required headers.
drupal_set_header('Content-Type: text/html; charset=utf-8');
// Assign content and show message flag.
$variables['content'] = $content;
$variables['show_messages'] = $show_messages;
// The maintenance preprocess function is recycled here.
template_preprocess_maintenance_page($variables);
return theme_render_template('themes/garland/maintenance-page.tpl.php', $variables);
}
/**
* The variables generated here is a mirror of template_preprocess_page().
* This preprocessor will run it's course when theme_maintenance_page() is
* invoked. It is also used in theme_install_page() and theme_update_page() to
* keep all the variables consistent.
*
* An alternate template file of "maintenance-page-offline.tpl.php" can be
* used when the database is offline to hide errors and completely replace the
* content.
*
* The $variables array contains the following arguments:
* - $content
* - $show_blocks
*
* @see maintenance-page.tpl.php
*/
function template_preprocess_maintenance_page(&$variables) {
// Add favicon
if (theme_get_setting('toggle_favicon')) {
drupal_set_html_head('<link rel="shortcut icon" href="'. check_url(theme_get_setting('favicon')) .'" type="image/x-icon" />');
}
global $theme;
// Retrieve the theme data to list all available regions.
$theme_data = _system_theme_data();
$regions = $theme_data[$theme]->info['regions'];
// Get all region content set with drupal_set_content().
foreach (array_keys($regions) as $region) {
// Assign region to a region variable.
$region_content = drupal_get_content($region);
isset($variables[$region]) ? $variables[$region] .= $region_content : $variables[$region] = $region_content;
}
// Setup layout variable.
$variables['layout'] = 'none';
if (!empty($variables['left'])) {
$variables['layout'] = 'left';
}
if (!empty($variables['right'])) {
$variables['layout'] = ($variables['layout'] == 'left') ? 'both' : 'right';
}
// Construct page title
if (drupal_get_title()) {
$head_title = array(strip_tags(drupal_get_title()), variable_get('site_name', 'Drupal'));
}
else {
$head_title = array(variable_get('site_name', 'Drupal'));
if (variable_get('site_slogan', '')) {
$head_title[] = variable_get('site_slogan', '');
}
}
$variables['head_title'] = implode(' | ', $head_title);
$variables['base_path'] = base_path();
$variables['breadcrumb'] = '';
$variables['feed_icons'] = '';
$variables['footer_message'] = filter_xss_admin(variable_get('site_footer', FALSE));
$variables['head'] = drupal_get_html_head();
$variables['help'] = '';
$variables['language'] = $GLOBALS['language'];
$variables['logo'] = theme_get_setting('logo');
$variables['messages'] = $variables['show_messages'] ? theme('status_messages') : '';
$variables['mission'] = '';
$variables['primary_links'] = array();
$variables['secondary_links'] = array();
$variables['search_box'] = '';
$variables['site_name'] = (theme_get_setting('toggle_name') ? variable_get('site_name', 'Drupal') : '');
$variables['site_slogan'] = (theme_get_setting('toggle_slogan') ? variable_get('site_slogan', '') : '');
$variables['css'] = drupal_add_css();
$variables['styles'] = drupal_get_css();
$variables['scripts'] = drupal_get_js();
$variables['tabs'] = '';
$variables['title'] = drupal_get_title();
$variables['closure'] = '';
// Compile a list of classes that are going to be applied to the body element.
$body_classes = array();
$body_classes[] = 'in-maintenance';
if (isset($variables['db_is_active']) && !$variables['db_is_active']) {
$body_classes[] = 'db-offline';
}
if ($variables['layout'] == 'both') {
$body_classes[] = 'two-sidebars';
}
elseif ($variables['layout'] == 'none') {
$body_classes[] = 'no-sidebars';
}
else {
$body_classes[] = 'one-sidebar sidebar-'. $variables['layout'];
}
$variables['body_classes'] = implode(' ', $body_classes);
// Dead databases will show error messages so supplying this template will
// allow themers to override the page and the content completely.
if (isset($variables['db_is_active']) && !$variables['db_is_active']) {
$variables['template_file'] = 'maintenance-page-offline';
}
}
......@@ -3,6 +3,8 @@
require_once './includes/install.inc';
define('MAINTENANCE_MODE', 'install');
/**
* The Drupal installation happens in a series of steps. We begin by verifying
* that the current environment meets our minimum requirements. We then go
......
<?php
// $Id$
/**
* @file maintenance-page.tpl.php
*
* Theme implementation to display a single Drupal page while off-line.
*
* All the available variables are mirrored in page.tpl.php. Some may be left
* blank but they are provided for consistency.
*
*
* @see template_preprocess()
* @see template_preprocess_maintenance_page()
*/
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<?php print $language->language ?>" lang="<?php print $language->language ?>">
<head>
<title><?php print $head_title; ?></title>
<?php print $head; ?>
<?php print $styles; ?>
<?php print $scripts; ?>
<script type="text/javascript"><?php /* Needed to avoid Flash of Unstyled Content in IE */ ?> </script>
</head>
<body class="<?php print $body_classes; ?>">
<div id="page">
<div id="header">
<div id="logo-title">
<?php if (!empty($logo)): ?>
<a href="<?php print $base_path; ?>" title="<?php print t('Home'); ?>" rel="home" id="logo">
<img src="<?php print $logo; ?>" alt="<?php print t('Home'); ?>" />
</a>
<?php endif; ?>
<div id="name-and-slogan">
<?php if (!empty($site_name)): ?>
<h1 id="site-name">
<a href="<?php print $base_path ?>" title="<?php print t('Home'); ?>" rel="home"><span><?php print $site_name; ?></span></a>
</h1>
<?php endif; ?>
<?php if (!empty($site_slogan)): ?>
<div id="site-slogan"><?php print $site_slogan; ?></div>
<?php endif; ?>
</div> <!-- /name-and-slogan -->
</div> <!-- /logo-title -->
<?php if (!empty($header)): ?>
<div id="header-region">
<?php print $header; ?>
</div>
<?php endif; ?>
</div> <!-- /header -->
<div id="container" class="clear-block">
<?php if (!empty($left)): ?>
<div id="sidebar-left" class="column sidebar">
<?php print $left; ?>
</div> <!-- /sidebar-left -->
<?php endif; ?>
<div id="main" class="column"><div id="main-squeeze">
<div id="content">
<?php if (!empty($title)): ?><h1 class="title" id="page-title"><?php print $title; ?></h1><?php endif; ?>
<?php if (!empty($messages)): print $messages; endif; ?>
<div id="content-content" class="clear-block">
<?php print $content; ?>
</div> <!-- /content-content -->
</div> <!-- /content -->
</div></div> <!-- /main-squeeze /main -->
<?php if (!empty($right)): ?>
<div id="sidebar-right" class="column sidebar">
<?php print $right; ?>
</div> <!-- /sidebar-right -->
<?php endif; ?>
</div> <!-- /container -->
<div id="footer-wrapper">
<div id="footer">
<?php print $footer_message; ?>
<?php if (!empty($footer)): print $footer; endif; ?>
</div> <!-- /footer -->
</div> <!-- /footer-wrapper -->
</div> <!-- /page -->
</body>
</html>
......@@ -77,7 +77,7 @@ function system_help($path, $arg) {
}
function system_theme() {
return array_merge(drupal_common_themes(), array(
return array_merge(drupal_common_theme(), array(
'system_theme_select_form' => array(
'arguments' => array('form' => NULL),
'file' => 'system.admin.inc',
......@@ -711,111 +711,119 @@ function system_theme_default() {
}
/**
* Collect data about all currently available themes
* Collect data about all currently available themes.
*
* @return
* Array of all available themes and their data.
*/
function system_theme_data() {
// Find themes
$themes = drupal_system_listing('\.info$', 'themes');
// Scan the installation theme .info files and their engines.
$themes = _system_theme_data();
// Find theme engines
$engines = drupal_system_listing('\.engine$', 'themes/engines');
// Remove all theme engines from the system table
db_query("DELETE FROM {system} WHERE type = '%s'", 'theme_engine');
// Extract current files from database.
system_get_files_database($themes, 'theme');
foreach ($engines as $engine) {
// Insert theme engine into system table
drupal_get_filename('theme_engine', $engine->name, $engine->filename);
drupal_load('theme_engine', $engine->name);
db_query("INSERT INTO {system} (name, type, filename, status, throttle, bootstrap) VALUES ('%s', '%s', '%s', %d, %d, %d)", $engine->name, 'theme_engine', $engine->filename, 1, 0, 0);
}
db_query("DELETE FROM {system} WHERE type = 'theme'");
$defaults = system_theme_default();
foreach ($themes as $theme) {
if (!isset($theme->owner)) {
$theme->owner = '';
}
$sub_themes = array();
// Read info files for each theme
foreach ($themes as $key => $theme) {
$themes[$key]->info = drupal_parse_info_file($theme->filename) + $defaults;
db_query("INSERT INTO {system} (name, owner, info, type, filename, status, throttle, bootstrap) VALUES ('%s', '%s', '%s', '%s', '%s', %d, %d, %d)", $theme->name, $theme->owner, serialize($theme->info), 'theme', $theme->filename, isset($theme->status) ? $theme->status : 0, 0, 0);
}
// Invoke hook_system_info_alter() to give installed modules a chance to
// modify the data in the .info files if necessary.
drupal_alter('system_info', $themes[$key]->info, $themes[$key]);
return $themes;
}
if (!empty($themes[$key]->info['base theme'])) {
$sub_themes[] = $key;
}
if (empty($themes[$key]->info['engine'])) {
$filename = dirname($themes[$key]->filename) .'/'. $themes[$key]->name .'.theme';
if (file_exists($filename)) {
$themes[$key]->owner = $filename;
$themes[$key]->prefix = $key;
/**
* Helper function to scan and collect theme .info data and their engines.
*/
function _system_theme_data() {
static $themes_info = array();
if (empty($theme_info)) {
// Find themes
$themes = drupal_system_listing('\.info$', 'themes');
// Find theme engines
$engines = drupal_system_listing('\.engine$', 'themes/engines');
$defaults = system_theme_default();
$sub_themes = array();
// Read info files for each theme
foreach ($themes as $key => $theme) {
$themes[$key]->info = drupal_parse_info_file($theme->filename) + $defaults;
// Invoke hook_system_info_alter() to give installed modules a chance to
// modify the data in the .info files if necessary.
drupal_alter('system_info', $themes[$key]->info, $themes[$key]);
if (!empty($themes[$key]->info['base theme'])) {
$sub_themes[] = $key;
}
}
else {
$engine = $themes[$key]->info['engine'];
if (isset($engines[$engine])) {
$themes[$key]->owner = $engines[$engine]->filename;
$themes[$key]->prefix = $engines[$engine]->name;
$themes[$key]->template = TRUE;
if (empty($themes[$key]->info['engine'])) {
$filename = dirname($themes[$key]->filename) .'/'. $themes[$key]->name .'.theme';
if (file_exists($filename)) {
$themes[$key]->owner = $filename;
$themes[$key]->prefix = $key;
}
}
}
// Give the stylesheets proper path information.
$pathed_stylesheets = array();
foreach ($themes[$key]->info['stylesheets'] as $media => $stylesheets) {
foreach ($stylesheets as $stylesheet) {
$pathed_stylesheets[$media][$stylesheet] = dirname($themes[$key]->filename) .'/'. $stylesheet;
else {
$engine = $themes[$key]->info['engine'];
if (isset($engines[$engine])) {
$themes[$key]->owner = $engines[$engine]->filename;
$themes[$key]->prefix = $engines[$engine]->name;
$themes[$key]->template = TRUE;
}
}
}
$themes[$key]->info['stylesheets'] = $pathed_stylesheets;
// Give the scripts proper path information.
$scripts = array();
foreach ($themes[$key]->info['scripts'] as $script) {
$scripts[$script] = dirname($themes[$key]->filename) .'/'. $script;
}
$themes[$key]->info['scripts'] = $scripts;
// Give the screenshot proper path information.
if (!empty($themes[$key]->info['screenshot'])) {
$themes[$key]->info['screenshot'] = dirname($themes[$key]->filename) .'/'. $themes[$key]->info['screenshot'];
}
}
// Give the stylesheets proper path information.
$pathed_stylesheets = array();
foreach ($themes[$key]->info['stylesheets'] as $media => $stylesheets) {
foreach ($stylesheets as $stylesheet) {
$pathed_stylesheets[$media][$stylesheet] = dirname($themes[$key]->filename) .'/'. $stylesheet;
}
}
$themes[$key]->info['stylesheets'] = $pathed_stylesheets;
// Now that we've established all our master themes, go back and fill in
// data for subthemes.
foreach ($sub_themes as $key) {
$base_key = system_find_base_theme($themes, $key);
if (!$base_key) {
continue;
}
// Copy the 'owner' and 'engine' over if the top level theme uses a
// theme engine.
if (isset($themes[$base_key]->owner)) {
if (isset($themes[$base_key]->info['engine'])) {
$themes[$key]->info['engine'] = $themes[$base_key]->info['engine'];
$themes[$key]->owner = $themes[$base_key]->owner;
$themes[$key]->prefix = $themes[$base_key]->prefix;
// Give the scripts proper path information.
$scripts = array();
foreach ($themes[$key]->info['scripts'] as $script) {
$scripts[$script] = dirname($themes[$key]->filename) .'/'. $script;
}
else {
$themes[$key]->prefix = $key;
$themes[$key]->info['scripts'] = $scripts;
// Give the screenshot proper path information.
if (!empty($themes[$key]->info['screenshot'])) {
$themes[$key]->info['screenshot'] = dirname($themes[$key]->filename) .'/'. $themes[$key]->info['screenshot'];
}
}
}
// Extract current files from database.
system_get_files_database($themes, 'theme');
db_query("DELETE FROM {system} WHERE type = 'theme'");
foreach ($themes as $theme) {
if (!isset($theme->owner)) {
$theme->owner = '';
// Now that we've established all our master themes, go back and fill in
// data for subthemes.
foreach ($sub_themes as $key) {
$base_key = system_find_base_theme($themes, $key);
if (!$base_key) {
continue;
}
// Copy the 'owner' and 'engine' over if the top level theme uses a
// theme engine.
if (isset($themes[$base_key]->owner)) {
if (isset($themes[$base_key]->info['engine'])) {
$themes[$key]->info['engine'] = $themes[$base_key]->info['engine'];
$themes[$key]->owner = $themes[$base_key]->owner;
$themes[$key]->prefix = $themes[$base_key]->prefix;
}
else {
$themes[$key]->prefix = $key;
}
}
}
db_query("INSERT INTO {system} (name, owner, info, type, filename, status, throttle, bootstrap) VALUES ('%s', '%s', '%s', '%s', '%s', %d, %d, %d)", $theme->name, $theme->owner, serialize($theme->info), 'theme', $theme->filename, isset($theme->status) ? $theme->status : 0, 0, 0);
$themes_info = $themes;
}
return $themes;
return $themes_info;
}
/**
......
......@@ -172,6 +172,15 @@
# 'site_name' => 'My Drupal site',
# 'theme_default' => 'minnelli',
# 'anonymous' => 'Visitor',
/**
* A custom theme can be set for the off-line page. This applies when the site
* is explicitly set to off-line mode through the administration page or when
* the database is inactive due to an error. It can be set through the
* 'maintenance_theme' key. The template file should also be copied into the
* theme. It is located inside 'modules/system/maintenance-page.tpl.php'.
* Note: This setting does not apply to installation and update pages.
*/
# 'maintenance_theme' => 'minnelli',
/**
* reverse_proxy accepts a boolean value.
*
......
<?php
// $Id$
/**
* @file maintenance-page.tpl.php
*
* This is an override of the default maintenance page. Used for Garland and
* Minnelli, this file should not be moved or modified since the installation