Commit b0894cea authored by Dries's avatar Dries

- Patch #812016 by effulgentsia, chx, redndahead, Damien Tournoud: themes...

- Patch #812016 by effulgentsia, chx, redndahead, Damien Tournoud: themes cannot always participate in drupal_alter().
parent ee691c59
...@@ -4592,13 +4592,19 @@ function _drupal_bootstrap_full() { ...@@ -4592,13 +4592,19 @@ function _drupal_bootstrap_full() {
// Initialize $_GET['q'] prior to invoking hook_init(). // Initialize $_GET['q'] prior to invoking hook_init().
drupal_path_initialize(); drupal_path_initialize();
// Set a custom theme for the current page, if there is one. We need to run
// this before invoking hook_init(), since any modules which initialize the // Let all modules take action before the menu system handles the request.
// theme system will prevent a custom theme from being correctly set later.
menu_set_custom_theme();
// Let all modules take action before menu system handles the request
// We do not want this while running update.php. // We do not want this while running update.php.
if (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update') { if (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update') {
// Prior to invoking hook_init(), initialize the theme (potentially a custom
// one for this page), so that:
// - Modules with hook_init() implementations that call theme() or
// theme_get_registry() don't initialize the incorrect theme.
// - The theme can have hook_*_alter() implementations affect page building
// (e.g., hook_form_alter(), hook_node_view_alter(), hook_page_alter()),
// ahead of when rendering starts.
menu_set_custom_theme();
drupal_theme_initialize();
module_invoke_all('init'); module_invoke_all('init');
} }
} }
......
...@@ -221,8 +221,8 @@ function _drupal_theme_initialize($theme, $base_theme = array(), $registry_callb ...@@ -221,8 +221,8 @@ function _drupal_theme_initialize($theme, $base_theme = array(), $registry_callb
} }
} }
if (function_exists($registry_callback)) { if (isset($registry_callback)) {
$registry_callback($theme, $base_theme, $theme_engine); _theme_registry_callback($registry_callback, array($theme, $base_theme, $theme_engine));
} }
} }
...@@ -233,26 +233,30 @@ function _drupal_theme_initialize($theme, $base_theme = array(), $registry_callb ...@@ -233,26 +233,30 @@ function _drupal_theme_initialize($theme, $base_theme = array(), $registry_callb
* The theme registry array if it has been stored in memory, NULL otherwise. * The theme registry array if it has been stored in memory, NULL otherwise.
*/ */
function theme_get_registry() { function theme_get_registry() {
return _theme_set_registry(); static $theme_registry = NULL;
if (!isset($theme_registry)) {
list($callback, $arguments) = _theme_registry_callback();
$theme_registry = call_user_func_array($callback, $arguments);
}
return $theme_registry;
} }
/** /**
* Store the theme registry in memory. * Set the callback that will be used by theme_get_registry() to fetch the registry.
*
* @param $registry
* A registry array as returned by _theme_build_registry()
* *
* @return * @param $callback
* The theme registry array stored in memory * The name of the callback function.
* @param $arguments
* The arguments to pass to the function.
*/ */
function _theme_set_registry($registry = NULL) { function _theme_registry_callback($callback = NULL, array $arguments = array()) {
static $theme_registry = NULL; static $stored;
if (isset($callback)) {
if (isset($registry)) { $stored = array($callback, $arguments);
$theme_registry = $registry;
} }
return $stored;
return $theme_registry;
} }
/** /**
...@@ -271,7 +275,6 @@ function _theme_load_registry($theme, $base_theme = NULL, $theme_engine = NULL) ...@@ -271,7 +275,6 @@ function _theme_load_registry($theme, $base_theme = NULL, $theme_engine = NULL)
$cache = cache_get("theme_registry:$theme->name", 'cache'); $cache = cache_get("theme_registry:$theme->name", 'cache');
if (isset($cache->data)) { if (isset($cache->data)) {
$registry = $cache->data; $registry = $cache->data;
_theme_set_registry($registry);
} }
else { else {
// If not, build one and cache it. // If not, build one and cache it.
...@@ -280,9 +283,9 @@ function _theme_load_registry($theme, $base_theme = NULL, $theme_engine = NULL) ...@@ -280,9 +283,9 @@ function _theme_load_registry($theme, $base_theme = NULL, $theme_engine = NULL)
// complete set of theme hooks. // complete set of theme hooks.
if (module_load_all(NULL)) { if (module_load_all(NULL)) {
_theme_save_registry($theme, $registry); _theme_save_registry($theme, $registry);
_theme_set_registry($registry);
} }
} }
return $registry;
} }
/** /**
......
...@@ -88,8 +88,7 @@ function _drupal_maintenance_theme() { ...@@ -88,8 +88,7 @@ function _drupal_maintenance_theme() {
* This builds the registry when the site needs to bypass any database calls. * 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) { function _theme_load_offline_registry($theme, $base_theme = NULL, $theme_engine = NULL) {
$registry = _theme_build_registry($theme, $base_theme, $theme_engine); return _theme_build_registry($theme, $base_theme, $theme_engine);
_theme_set_registry($registry);
} }
/** /**
......
...@@ -66,6 +66,14 @@ class ThemeUnitTest extends DrupalWebTestCase { ...@@ -66,6 +66,14 @@ class ThemeUnitTest extends DrupalWebTestCase {
$_GET['q'] = $q; $_GET['q'] = $q;
$this->assertTrue(in_array('page__front', $suggestions), t('Front page template was suggested.')); $this->assertTrue(in_array('page__front', $suggestions), t('Front page template was suggested.'));
} }
/**
* Ensures theme hook_*_alter() implementations can run before anything is rendered.
*/
function testAlter() {
$this->drupalGet('theme-test/alter');
$this->assertText('The altered data is test_theme_theme_test_alter_alter was invoked.', t('The theme was able to implement an alter hook during page building before anything was rendered.'));
}
} }
/** /**
...@@ -181,3 +189,30 @@ class ThemeHookInitUnitTest extends DrupalWebTestCase { ...@@ -181,3 +189,30 @@ class ThemeHookInitUnitTest extends DrupalWebTestCase {
$this->assertRaw('bartik/css/style.css', t("The default theme's CSS appears on the page when the theme system is initialized in hook_init().")); $this->assertRaw('bartik/css/style.css', t("The default theme's CSS appears on the page when the theme system is initialized in hook_init()."));
} }
} }
/**
* Tests autocompletion not loading registry.
*/
class ThemeFastTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'Theme fast initialization',
'description' => 'Test that autocompletion does not load the registry.',
'group' => 'Theme'
);
}
function setUp() {
parent::setUp('theme_test');
$this->account = $this->drupalCreateUser(array('access user profiles'));
}
/**
* Tests access to user autocompletion and verify the correct results.
*/
function testUserAutocomplete() {
$this->drupalLogin($this->account);
$this->drupalGet('user/autocomplete/' . $this->account->name);
$this->assertText('registry not initialized', t('The registry was not initialized'));
}
}
...@@ -12,6 +12,13 @@ function theme_test_menu() { ...@@ -12,6 +12,13 @@ function theme_test_menu() {
'theme callback' => '_theme_custom_theme', 'theme callback' => '_theme_custom_theme',
'type' => MENU_CALLBACK, 'type' => MENU_CALLBACK,
); );
$items['theme-test/alter'] = array(
'title' => 'Suggestion',
'page callback' => '_theme_test_alter',
'access arguments' => array('access content'),
'theme callback' => '_theme_custom_theme',
'type' => MENU_CALLBACK,
);
$items['theme-test/hook-init'] = array( $items['theme-test/hook-init'] = array(
'page callback' => 'theme_test_hook_init_page_callback', 'page callback' => 'theme_test_hook_init_page_callback',
'access callback' => TRUE, 'access callback' => TRUE,
...@@ -24,15 +31,36 @@ function theme_test_menu() { ...@@ -24,15 +31,36 @@ function theme_test_menu() {
* Implements hook_init(). * Implements hook_init().
*/ */
function theme_test_init() { function theme_test_init() {
// First, force the theme registry to be rebuilt on this page request. This if (arg(0) == 'theme-test' && arg(1) == 'hook-init') {
// allows us to test a full initialization of the theme system in the code // First, force the theme registry to be rebuilt on this page request. This
// below. // allows us to test a full initialization of the theme system in the code
drupal_theme_rebuild(); // below.
// Next, initialize the theme system by storing themed text in a global drupal_theme_rebuild();
// variable. We will use this later in theme_test_hook_init_page_callback() // Next, initialize the theme system by storing themed text in a global
// to test that even when the theme system is initialized this early, it is // variable. We will use this later in theme_test_hook_init_page_callback()
// still capable of returning output and theming the page as a whole. // to test that even when the theme system is initialized this early, it is
$GLOBALS['theme_test_output'] = theme('more_link', array('url' => 'user', 'title' => 'Themed output generated in hook_init()')); // still capable of returning output and theming the page as a whole.
$GLOBALS['theme_test_output'] = theme('more_link', array('url' => 'user', 'title' => 'Themed output generated in hook_init()'));
}
}
/**
* Implements hook_exit().
*/
function theme_test_exit() {
if (arg(0) == 'user') {
// Register a fake registry loading callback. If it gets called by
// theme_get_registry(), the registry has not been initialized yet.
_theme_registry_callback('_theme_test_load_registry', array());
print theme_get_registry() ? 'registry initialized' : 'registry not initialized';
}
}
/**
* Fake registry loading callback.
*/
function _theme_test_load_registry() {
return array();
} }
/** /**
...@@ -49,6 +77,19 @@ function _theme_custom_theme() { ...@@ -49,6 +77,19 @@ function _theme_custom_theme() {
return 'test_theme'; return 'test_theme';
} }
/**
* Page callback, calls drupal_alter().
*
* This is for testing that the theme can have hook_*_alter() implementations
* that run during page callback execution, even before theme() is called for
* the first time.
*/
function _theme_test_alter() {
$data = 'foo';
drupal_alter('theme_test_alter', $data);
return "The altered data is $data.";
}
/** /**
* Page callback, calls a theme hook suggestion. * Page callback, calls a theme hook suggestion.
*/ */
......
...@@ -9,3 +9,14 @@ function test_theme_breadcrumb__suggestion($variables) { ...@@ -9,3 +9,14 @@ function test_theme_breadcrumb__suggestion($variables) {
// when the suggestion has an implementation. // when the suggestion has an implementation.
return 'test_theme_breadcrumb__suggestion: ' . $variables['theme_test_preprocess_breadcrumb']; return 'test_theme_breadcrumb__suggestion: ' . $variables['theme_test_preprocess_breadcrumb'];
} }
/**
* Tests a theme implementing an alter hook.
*
* The confusing function name here is due to this being an implementation of
* the alter hook invoked when the 'theme_test' module calls
* drupal_alter('theme_test_alter').
*/
function test_theme_theme_test_alter_alter(&$data) {
$data = 'test_theme_theme_test_alter_alter was invoked';
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment