diff --git a/includes/common.inc b/includes/common.inc index 0437ec1..25e1e3a 100644 --- a/includes/common.inc +++ b/includes/common.inc @@ -5241,7 +5241,10 @@ function _drupal_bootstrap_full() { // (e.g., hook_form_alter(), hook_node_view_alter(), hook_page_alter()), // ahead of when rendering starts. menu_set_custom_theme(); - drupal_theme_initialize(); + // Initialize the theme with the final initialization flag, this ensures + // that the theme is properly set even if it was initialized by calling + // theme related function during bootstrap. + drupal_theme_initialize(TRUE); module_invoke_all('init'); } } diff --git a/includes/theme.inc b/includes/theme.inc index 8d5348d..d2fc40e 100644 --- a/includes/theme.inc +++ b/includes/theme.inc @@ -66,12 +66,17 @@ function _drupal_theme_access($theme) { /** * Initializes the theme system by loading the theme. + * + * @param bool $final_init + * If enabled cache / static cache is cleared and the css / js files of the + * theme are added to the output. This should only be used once after a full + * drupal bootstrap. */ -function drupal_theme_initialize() { +function drupal_theme_initialize($final_init = FALSE) { global $theme, $user, $theme_key; - // If $theme is already set, assume the others are set, too, and do nothing - if (isset($theme)) { + // If $theme is already set, assume the others are set, too, and do nothing. + if (isset($theme) && empty($final_init)) { return; } @@ -97,7 +102,16 @@ function drupal_theme_initialize() { $ancestor = $themes[$ancestor]->base_theme; $base_theme[] = $themes[$ancestor]; } - _drupal_theme_initialize($themes[$theme], array_reverse($base_theme)); + // Initialize the theme but register the theme related css / js + // files just if the final initialization is requested. + _drupal_theme_initialize($themes[$theme], array_reverse($base_theme), '_theme_load_registry', $final_init); + if ($final_init) { + // Reset related caches that may have been built if the final initialization + // is requested. + drupal_static_reset('theme_get_registry'); + $drupal_static_fast['registry'] = &drupal_static('theme_get_registry'); + $drupal_static_fast['registry'] = array(); + } // Themes can have alter functions, so reset the drupal_alter() cache. drupal_static_reset('drupal_alter'); @@ -137,70 +151,80 @@ function drupal_theme_initialize() { * be first. * @param $registry_callback * The callback to invoke to set the theme registry. + * @param bool $register_files + * If enabled the css / js files of the theme are added to the output. This + * shouln't be triggered before durpal isn't fully bootstrapped since this + * could lead to a theme mix up if hook_custom_theme() is used. */ -function _drupal_theme_initialize($theme, $base_theme = array(), $registry_callback = '_theme_load_registry') { +function _drupal_theme_initialize($theme, $base_theme = array(), $registry_callback = '_theme_load_registry', $register_files = FALSE) { global $theme_info, $base_theme_info, $theme_engine, $theme_path; $theme_info = $theme; $base_theme_info = $base_theme; $theme_path = dirname($theme->filename); - // Prepare stylesheets from this theme as well as all ancestor themes. - // We work it this way so that we can have child themes override parent - // theme stylesheets easily. - $final_stylesheets = array(); + if ($register_files) { - // Grab stylesheets from base theme - foreach ($base_theme as $base) { - if (!empty($base->stylesheets)) { - foreach ($base->stylesheets as $media => $stylesheets) { + // Prepare stylesheets from this theme as well as all ancestor themes. + // We work it this way so that we can have child themes override parent + // theme stylesheets easily. + $final_stylesheets = array(); + + // Grab stylesheets from base theme + foreach ($base_theme as $base) { + if (!empty($base->stylesheets)) { + foreach ($base->stylesheets as $media => $stylesheets) { + foreach ($stylesheets as $name => $stylesheet) { + $final_stylesheets[$media][$name] = $stylesheet; + } + } + } + } + + // Add stylesheets used by this theme. + if (!empty($theme->stylesheets)) { + foreach ($theme->stylesheets as $media => $stylesheets) { foreach ($stylesheets as $name => $stylesheet) { $final_stylesheets[$media][$name] = $stylesheet; } } } - } - // Add stylesheets used by this theme. - if (!empty($theme->stylesheets)) { - foreach ($theme->stylesheets as $media => $stylesheets) { - foreach ($stylesheets as $name => $stylesheet) { - $final_stylesheets[$media][$name] = $stylesheet; + // And now add the stylesheets properly + foreach ($final_stylesheets as $media => $stylesheets) { + foreach ($stylesheets as $stylesheet) { + drupal_add_css($stylesheet, array( + 'group' => CSS_THEME, + 'every_page' => TRUE, + 'media' => $media + )); } } - } - // And now add the stylesheets properly - foreach ($final_stylesheets as $media => $stylesheets) { - foreach ($stylesheets as $stylesheet) { - drupal_add_css($stylesheet, array('group' => CSS_THEME, 'every_page' => TRUE, 'media' => $media)); - } - } + // Do basically the same as the above for scripts + $final_scripts = array(); - // Do basically the same as the above for scripts - $final_scripts = array(); + // Grab scripts from base theme + foreach ($base_theme as $base) { + if (!empty($base->scripts)) { + foreach ($base->scripts as $name => $script) { + $final_scripts[$name] = $script; + } + } + } - // Grab scripts from base theme - foreach ($base_theme as $base) { - if (!empty($base->scripts)) { - foreach ($base->scripts as $name => $script) { + // Add scripts used by this theme. + if (!empty($theme->scripts)) { + foreach ($theme->scripts as $name => $script) { $final_scripts[$name] = $script; } } - } - // Add scripts used by this theme. - if (!empty($theme->scripts)) { - foreach ($theme->scripts as $name => $script) { - $final_scripts[$name] = $script; + // Add scripts used by this theme. + foreach ($final_scripts as $script) { + drupal_add_js($script, array('group' => JS_THEME, 'every_page' => TRUE)); } } - - // Add scripts used by this theme. - foreach ($final_scripts as $script) { - drupal_add_js($script, array('group' => JS_THEME, 'every_page' => TRUE)); - } - $theme_engine = NULL; // Initialize the theme. diff --git a/includes/theme.maintenance.inc b/includes/theme.maintenance.inc index 6baf219..6763257 100644 --- a/includes/theme.maintenance.inc +++ b/includes/theme.maintenance.inc @@ -71,7 +71,7 @@ function _drupal_maintenance_theme() { $base_theme[] = $new_base_theme = $themes[$themes[$ancestor]->base_theme]; $ancestor = $themes[$ancestor]->base_theme; } - _drupal_theme_initialize($themes[$theme], array_reverse($base_theme), '_theme_load_offline_registry'); + _drupal_theme_initialize($themes[$theme], array_reverse($base_theme), '_theme_load_offline_registry', TRUE); // These are usually added from system_init() -except maintenance.css. // When the database is inactive it's not called so we add it here. diff --git a/modules/simpletest/simpletest.info b/modules/simpletest/simpletest.info index 7b139ba..0866f60 100644 --- a/modules/simpletest/simpletest.info +++ b/modules/simpletest/simpletest.info @@ -14,6 +14,7 @@ files[] = tests/batch.test files[] = tests/bootstrap.test files[] = tests/cache.test files[] = tests/common.test +files[] = tests/custom_theme_boot_test.test files[] = tests/database_test.test files[] = tests/entity_crud.test files[] = tests/entity_crud_hook_test.test diff --git a/modules/simpletest/tests/custom_theme_boot_test.info b/modules/simpletest/tests/custom_theme_boot_test.info new file mode 100644 index 0000000..3b1d2dd --- /dev/null +++ b/modules/simpletest/tests/custom_theme_boot_test.info @@ -0,0 +1,6 @@ +name = Custom theme boot tests +description = A support module for custom theme hook boot testing. +core = 7.x +package = Testing +version = VERSION +hidden = TRUE diff --git a/modules/simpletest/tests/custom_theme_boot_test.module b/modules/simpletest/tests/custom_theme_boot_test.module new file mode 100644 index 0000000..d89402e --- /dev/null +++ b/modules/simpletest/tests/custom_theme_boot_test.module @@ -0,0 +1,37 @@ +type == 'page') { + return 'garland'; + } + elseif ($node->type == 'article') { + return 'seven'; + } + } +} + +/** + * Implements hook_entity_load(). + */ +function custom_theme_boot_test_entity_load($entities, $type) { + if ($type == 'node') { + foreach ($entities as $entity) { + // This is going to initialize the theme and break everything if not + // properly handled. + $entity->foo = l(t('foo'), ''); + } + } +} diff --git a/modules/simpletest/tests/custom_theme_boot_test.test b/modules/simpletest/tests/custom_theme_boot_test.test new file mode 100644 index 0000000..67f1804 --- /dev/null +++ b/modules/simpletest/tests/custom_theme_boot_test.test @@ -0,0 +1,68 @@ + 'Custom theme boot tests', + 'description' => 'Test that triggering custom theme on boot wont compromise the theme registry.', + 'group' => 'Theme', + ); + } + + public function setUp() { + parent::setUp('custom_theme_boot_test'); + // Create and login admin user. + $this->admin_user = $this->drupalCreateUser(array( + 'access administration pages', + 'administer site configuration', + 'administer themes', + )); + } + + /** + * Create four nodes and ensure they're loaded correctly. + */ + public function testNodeMultipleLoad() { + // Create nodes to fetch. + $node1 = $this->drupalCreateNode(array( + 'type' => 'article', + 'promote' => 1, + )); + $node2 = $this->drupalCreateNode(array( + 'type' => 'page', + 'promote' => 1, + )); + $this->drupalLogin($this->admin_user); + $this->drupalGet('admin/appearance'); + + // Enable Garland theme. + $this->clickLink('Enable'); + // Enable Seven theme. + $this->clickLink('Enable'); + + // Clear all caches. + cache_clear_all(); + + // Fetch the node. This triggers hook_entity_load(), which is implemented by + // custom_theme_boot_test.module. The implementation calls l() which again + // end's up loading the theme. + $this->drupalGet('node/' . $node1->nid); + $this->assertText('seven'); + $this->assertNoText('garland'); + $this->assertNoText('bartik'); + + $this->drupalGet('node/' . $node2->nid); + $this->assertText('garland'); + $this->assertNoText('seven'); + $this->assertNoText('bartik'); + } +}