commit 6a4da6010e67dfffa45d4cea2e9800d173b4b588 Author: Pierre Rineau Date: Mon Nov 28 18:12:02 2016 +0100 Issue #1545964 by Pol, pounard, Rene Bakx, Kars-T, SebCorbin: Do not copy over the owner and engine of a theme if the child theme uses a different engine than the base theme diff --git a/includes/module.inc b/includes/module.inc index 2e25108..a068569 100644 --- a/includes/module.inc +++ b/includes/module.inc @@ -193,8 +193,11 @@ function system_list($type) { foreach (array_keys($lists['theme'][$key]->base_themes) as $base_theme) { $lists['theme'][$base_theme]->sub_themes[$key] = $lists['theme'][$key]->info['name']; } - // Add the base theme's theme engine info. - $lists['theme'][$key]->info['engine'] = isset($lists['theme'][$base_key]->info['engine']) ? $lists['theme'][$base_key]->info['engine'] : 'theme'; + // Add the base theme's theme engine info only when subtheme does not + // define one, this allows to use different theme engines. + if (!isset($lists['theme'][$key]->info['engine'])) { + $lists['theme'][$key]->info['engine'] = isset($lists['theme'][$base_key]->info['engine']) ? $lists['theme'][$base_key]->info['engine'] : 'theme'; + } } else { // A plain theme is its own engine. diff --git a/includes/theme.inc b/includes/theme.inc index 9b606e9..0e2e4a7 100644 --- a/includes/theme.inc +++ b/includes/theme.inc @@ -139,7 +139,7 @@ function drupal_theme_initialize() { * The callback to invoke to set the theme registry. */ function _drupal_theme_initialize($theme, $base_theme = array(), $registry_callback = '_theme_load_registry') { - global $theme_info, $base_theme_info, $theme_engine, $theme_path; + global $theme_info, $base_theme_info, $theme_path; $theme_info = $theme; $base_theme_info = $base_theme; @@ -201,37 +201,19 @@ function _drupal_theme_initialize($theme, $base_theme = array(), $registry_callb drupal_add_js($script, array('group' => JS_THEME, 'every_page' => TRUE)); } - $theme_engine = NULL; + // Initialize all themes and engines. + // Make sure of loading the current theme first so preprocess functions are + // loaded properly. + foreach (array($theme) + list_themes() as $current) { + include_once DRUPAL_ROOT . '/' . $current->owner; - // Initialize the theme. - if (isset($theme->engine)) { - // Include the engine. - include_once DRUPAL_ROOT . '/' . $theme->owner; - - $theme_engine = $theme->engine; - if (function_exists($theme_engine . '_init')) { - foreach ($base_theme as $base) { - call_user_func($theme_engine . '_init', $base); - } - call_user_func($theme_engine . '_init', $theme); - } - } - else { - // include non-engine theme files - foreach ($base_theme as $base) { - // Include the theme file or the engine. - if (!empty($base->owner)) { - include_once DRUPAL_ROOT . '/' . $base->owner; - } - } - // and our theme gets one too. - if (!empty($theme->owner)) { - include_once DRUPAL_ROOT . '/' . $theme->owner; + if (function_exists($current->engine . '_init')) { + call_user_func($current->engine . '_init', $current); } } if (isset($registry_callback)) { - _theme_registry_callback($registry_callback, array($theme, $base_theme, $theme_engine)); + _theme_registry_callback($registry_callback, array($theme, $base_theme)); } } @@ -304,8 +286,6 @@ function _theme_registry_callback($callback = NULL, array $arguments = array()) * @param $base_theme * An array of loaded $theme objects representing the ancestor themes in * oldest first order. - * @param $theme_engine - * The name of the theme engine. * @param $complete * Whether to load the complete theme registry or an instance of the * ThemeRegistry class. @@ -313,7 +293,7 @@ function _theme_registry_callback($callback = NULL, array $arguments = array()) * @return * The theme registry array, or an instance of the ThemeRegistry class. */ -function _theme_load_registry($theme, $base_theme = NULL, $theme_engine = NULL, $complete = TRUE) { +function _theme_load_registry($theme, $base_theme = NULL, $complete = TRUE) { if ($complete) { // Check the theme registry cache; if it exists, use it. $cached = cache_get("theme_registry:$theme->name"); @@ -322,7 +302,7 @@ function _theme_load_registry($theme, $base_theme = NULL, $theme_engine = NULL, } else { // If not, build one and cache it. - $registry = _theme_build_registry($theme, $base_theme, $theme_engine); + $registry = _theme_build_registry($theme, $base_theme); // Only persist this registry if all modules are loaded. This assures a // complete set of theme hooks. if (module_load_all(NULL)) { @@ -672,10 +652,8 @@ function _theme_process_registry(&$cache, $name, $type, $theme, $path) { * @param $base_theme * An array of loaded $theme objects representing the ancestor themes in * oldest first order. - * @param $theme_engine - * The name of the theme engine. */ -function _theme_build_registry($theme, $base_theme, $theme_engine) { +function _theme_build_registry($theme, $base_theme) { $cache = array(); // First, process the theme hooks advertised by modules. This will // serve as the basic registry. Since the list of enabled modules is the same @@ -698,15 +676,15 @@ function _theme_build_registry($theme, $base_theme, $theme_engine) { foreach ($base_theme as $base) { // If the base theme uses a theme engine, process its hooks. $base_path = dirname($base->filename); - if ($theme_engine) { - _theme_process_registry($cache, $theme_engine, 'base_theme_engine', $base->name, $base_path); + if (isset($base->engine)) { + _theme_process_registry($cache, $base->engine, 'base_theme_engine', $base->name, $base_path); } _theme_process_registry($cache, $base->name, 'base_theme', $base->name, $base_path); } // And then the same thing, but for the theme. - if ($theme_engine) { - _theme_process_registry($cache, $theme_engine, 'theme_engine', $theme->name, dirname($theme->filename)); + if (isset($theme->engine)) { + _theme_process_registry($cache, $theme->engine, 'theme_engine', $theme->name, dirname($theme->filename)); } // Finally, hooks provided by the theme itself. @@ -1167,16 +1145,14 @@ function theme($hook, $variables = array()) { $extension = '.tpl.php'; // The theme engine may use a different extension and a different renderer. - global $theme_engine; - if (isset($theme_engine)) { - if ($info['type'] != 'module') { - if (function_exists($theme_engine . '_render_template')) { - $render_function = $theme_engine . '_render_template'; - } - $extension_function = $theme_engine . '_extension'; - if (function_exists($extension_function)) { - $extension = $extension_function(); - } + if (isset($info['engine'])) { + $theme_engine = $info['engine']; + if (function_exists($theme_engine . '_render_template')) { + $render_function = $theme_engine . '_render_template'; + } + $extension_function = $theme_engine . '_extension'; + if (function_exists($extension_function)) { + $extension = $extension_function(); } } @@ -1309,10 +1285,21 @@ function drupal_find_theme_templates($cache, $extension, $path) { // used for filtering. This allows base themes to have sub-themes in its // folder hierarchy without affecting the base themes template discovery. $theme_paths = array(); - foreach (list_themes() as $theme_info) { + $current_theme = NULL; + $current_engine = NULL; + foreach (list_themes() as $key => $theme_info) { if (!empty($theme_info->base_theme)) { $theme_paths[$theme_info->base_theme][$theme_info->name] = dirname($theme_info->filename); } + $theme_path = dirname($theme_info->filename); + if ($path === $theme_path) { + $current_theme = $current_theme; + $current_engine = isset($theme_info->engine) ? $theme_info->engine : NULL; + } + else if (!$current_theme && 0 === strpos($theme_path, $current_theme)) { + $current_theme = $current_theme; + $current_engine = isset($theme_info->engine) ? $theme_info->engine : NULL; + } } foreach ($theme_paths as $basetheme => $subthemes) { foreach ($subthemes as $subtheme => $subtheme_path) { @@ -1349,6 +1336,7 @@ function drupal_find_theme_templates($cache, $extension, $path) { if (isset($cache[$hook])) { $implementations[$hook] = array( 'template' => $template, + 'engine' => $current_engine, 'path' => dirname($file->uri), ); } @@ -1375,6 +1363,7 @@ function drupal_find_theme_templates($cache, $extension, $path) { $arg_name = isset($info['variables']) ? 'variables' : 'render element'; $implementations[strtr($file, '-', '_')] = array( 'template' => $file, + 'engine' => $current_engine, 'path' => dirname($files[$match]->uri), $arg_name => $info[$arg_name], 'base hook' => $hook, diff --git a/includes/theme.maintenance.inc b/includes/theme.maintenance.inc index 6baf219..df8d446 100644 --- a/includes/theme.maintenance.inc +++ b/includes/theme.maintenance.inc @@ -87,8 +87,8 @@ function _drupal_maintenance_theme() { /** * Builds the registry when the site needs to bypass any database calls. */ -function _theme_load_offline_registry($theme, $base_theme = NULL, $theme_engine = NULL) { - return _theme_build_registry($theme, $base_theme, $theme_engine); +function _theme_load_offline_registry($theme, $base_theme = NULL) { + return _theme_build_registry($theme, $base_theme); } /** diff --git a/modules/simpletest/tests/theme.test b/modules/simpletest/tests/theme.test index 5f095bd..d2899a5 100644 --- a/modules/simpletest/tests/theme.test +++ b/modules/simpletest/tests/theme.test @@ -677,3 +677,122 @@ class ModuleProvidedThemeEngineTestCase extends DrupalWebTestCase { } } + +/** + * Tests the multi theme engine support. + */ +class ThemeEngineNyanCatTestCase extends DrupalWebTestCase { + public static function getInfo() { + return array( + 'name' => 'Theme engine test', + 'description' => 'Tests the theme engine.', + 'group' => 'Theme', + ); + } + + function setUp() { + parent::setUp('theme_test'); + theme_enable(array('test_theme')); + } + + /** + * Ensures a theme's template is overridable based on the 'template' filename. + */ + function testTemplateOverride() { + variable_set('theme_default', 'test_theme_nyan_cat'); + variable_set('admin_theme', 'test_theme_nyan_cat'); + + $this->drupalGet('theme-test/template-test-engine'); + $this->assertText('Success: Template overridden with Nyan Cat theme. All of them', 'Template overridden by Nyan Cat file.'); + } + +} + +/** + * Tests the multi theme engine support with a subtheme. + */ +class ThemeEngineNyanCatSubThemeTestCase extends DrupalWebTestCase { + public static function getInfo() { + return array( + 'name' => 'Sub theme engine test', + 'description' => 'Tests the theme engine on a subtheme.', + 'group' => 'Theme', + ); + } + + function setUp() { + parent::setUp('theme_test'); + theme_enable(array('test_theme', 'test_theme_nyan_cat', 'test_subtheme_nyan_cat')); + } + + /** + * Ensures a theme's template is overridable based on the 'template' filename. + */ + function testTemplateOverride() { + variable_set('theme_default', 'test_subtheme_nyan_cat'); + variable_set('admin_theme', 'test_subtheme_nyan_cat'); + + $this->drupalGet('theme-test/template-test-engine'); + $this->assertText('Success: Template overridden with Nyan Cat theme. All of them', 'Template overridden by Nyan Cat file.'); + } + + /** + * Ensures that a module providing both templates with different engines + * will all be correctly displayed, no matter is the theme, using their + * respective engines. + * + * Tests that phptemplate -> nyan_cat template override works. + */ + function testEngineCohabitationFromDefaultToNyan() { + variable_set('theme_default', 'test_theme'); + variable_set('admin_theme', 'test_theme'); + + $this->drupalGet('theme-test/engine-cohabitation'); + $this->assertText('Inheritance 1.'); + $this->assertText('Inheritance 2.'); + $this->assertText('Inheritance 3.'); + // This one is overriden. + $this->assertNoText('Inheritance 4.'); + $this->assertText('Inheritance 6.'); + // This one is in another theme. + $this->assertNoText('Inheritance 5.'); + } + + /** + * Ensures that a module providing both templates with different engines + * will all be correctly displayed, no matter is the theme, using their + * respective engines. + * + * Tests that nyan_cat -> phptemplate template override works. + */ + function testEngineCohabitationFromNyanToDefault() { + variable_set('theme_default', 'test_theme_nyan_cat'); + variable_set('admin_theme', 'test_theme_nyan_cat'); + + $this->drupalGet('theme-test/engine-cohabitation'); + $this->assertText('Inheritance 1.'); + $this->assertText('Inheritance 2.'); + $this->assertText('Inheritance 4.'); + // This one is overriden. + $this->assertNoText('Inheritance 3.'); + $this->assertText('Inheritance 5.'); + // This one is in another theme. + $this->assertNoText('Inheritance 6.'); + } + + /** + * Ensure the subtheme has inherited of the theme engine of it's base theme. + */ + function testEngineIsCorrectlyInherited() { + $infos = list_themes(); + + // Get the base theme name from the theme 'test_subtheme_nyan_cat' + $base_theme_name = $infos['test_subtheme_nyan_cat']->info['base theme']; + // Get the base theme + $base_theme = $infos[$base_theme_name]; + + // Compare their theme engine. + $this->assertEqual($infos['test_subtheme_nyan_cat']->info['engine'], $base_theme->info['engine']); + } + +} diff --git a/modules/simpletest/tests/theme_test.module b/modules/simpletest/tests/theme_test.module index 1dbc3b9..f7dce8d 100644 --- a/modules/simpletest/tests/theme_test.module +++ b/modules/simpletest/tests/theme_test.module @@ -17,6 +17,39 @@ function theme_test_theme($existing, $type, $theme, $path) { $items['theme_test_foo'] = array( 'variables' => array('foo' => NULL), ); + $items['theme_test_template_test_engine'] = array( + 'template' => 'theme_test.template_test_engine', + 'engine' => 'nyan_cat' + ); + // The following template is phptemplate per default, and will be displayed + // correctly even when using the 'test_theme_nyan_cat' theme, which is based + // upon the nyan_cat engine. + $items['theme_test_engine_inheritance_phpt'] = array( + 'variables' => array(), + 'template' => 'theme_test_engine_inheritance_phpt', + ); + // The following template is nyan_cat per default, and will be displayed + // correctly even when using the 'test_theme' theme, which is based upon + // the phptemplate engine. + $items['theme_test_engine_inheritance_nyan'] = array( + 'variables' => array(), + 'template' => 'theme_test_engine_inheritance_nyan', + 'engine' => 'nyan_cat' + ); + // In opposition to the two templates above, this one is phptemplate but will + // be correctly overriden by the 'test_theme_nyan_cat' which has a different + // engine. + $items['theme_test_engine_inheritance_phpt_overriden'] = array( + 'variables' => array(), + 'template' => 'theme_test_engine_inheritance_phpt_overriden', + ); + // Exact same as above, this one is nyan cat but will be correctly overriden + // by the 'test_theme' which has a different engine. + $items['theme_test_engine_inheritance_nyan_overriden'] = array( + 'variables' => array(), + 'template' => 'theme_test_engine_inheritance_nyan_overriden', + 'engine' => 'nyan_cat' + ); return $items; } @@ -28,6 +61,7 @@ function theme_test_system_theme_info() { $themes['test_basetheme'] = drupal_get_path('module', 'theme_test') . '/themes/test_basetheme/test_basetheme.info'; $themes['test_subtheme'] = drupal_get_path('module', 'theme_test') . '/themes/test_subtheme/test_subtheme.info'; $themes['test_theme_nyan_cat'] = drupal_get_path('module', 'theme_test') . '/themes/test_theme_nyan_cat/test_theme_nyan_cat.info'; + $themes['test_subtheme_nyan_cat'] = drupal_get_path('module', 'theme_test') . '/themes/test_subtheme_nyan_cat/test_subtheme_nyan_cat.info'; return $themes; } @@ -73,10 +107,42 @@ function theme_test_menu() { 'access callback' => TRUE, 'type' => MENU_CALLBACK, ); + $items['theme-test/template-test-engine'] = array( + 'page callback' => '_theme_test_template_test_engine', + 'access callback' => TRUE, + 'type' => MENU_CALLBACK, + ); + $items['theme-test/engine-cohabitation'] = array( + 'page callback' => '_theme_test_engine_inheritance', + 'access callback' => TRUE, + 'type' => MENU_CALLBACK, + ); + return $items; } /** + * Page callback, calls a theme hook suggestion. + */ +function _theme_test_template_test_engine() { + return array( + '#markup' => theme('theme_test_template_test_engine', array()), + ); +} + +/** + * Page callback, display various templates for various theme engines. + */ +function _theme_test_engine_inheritance() { + return array( + array('#theme' => 'theme_test_engine_inheritance_phpt'), + array('#theme' => 'theme_test_engine_inheritance_nyan'), + array('#theme' => 'theme_test_engine_inheritance_phpt_overriden'), + array('#theme' => 'theme_test_engine_inheritance_nyan_overriden'), + ); +} + +/** * Implements hook_init(). */ function theme_test_init() { diff --git a/modules/simpletest/tests/theme_test.template_test_engine.nyan-cat.html b/modules/simpletest/tests/theme_test.template_test_engine.nyan-cat.html new file mode 100644 index 0000000..cb21cb5 --- /dev/null +++ b/modules/simpletest/tests/theme_test.template_test_engine.nyan-cat.html @@ -0,0 +1 @@ +Success: Template overridden with Nyan Cat theme. 9kittens diff --git a/modules/simpletest/tests/theme_test_engine_inheritance_nyan.nyan-cat.html b/modules/simpletest/tests/theme_test_engine_inheritance_nyan.nyan-cat.html new file mode 100644 index 0000000..6e195f9 --- /dev/null +++ b/modules/simpletest/tests/theme_test_engine_inheritance_nyan.nyan-cat.html @@ -0,0 +1 @@ +Inheritance 2. \ No newline at end of file diff --git a/modules/simpletest/tests/theme_test_engine_inheritance_nyan_overriden.nyan-cat.html b/modules/simpletest/tests/theme_test_engine_inheritance_nyan_overriden.nyan-cat.html new file mode 100644 index 0000000..ba4f98b --- /dev/null +++ b/modules/simpletest/tests/theme_test_engine_inheritance_nyan_overriden.nyan-cat.html @@ -0,0 +1 @@ +Inheritance 4. \ No newline at end of file diff --git a/modules/simpletest/tests/theme_test_engine_inheritance_phpt.tpl.php b/modules/simpletest/tests/theme_test_engine_inheritance_phpt.tpl.php new file mode 100644 index 0000000..b6a330d --- /dev/null +++ b/modules/simpletest/tests/theme_test_engine_inheritance_phpt.tpl.php @@ -0,0 +1 @@ +Inheritance 1. \ No newline at end of file diff --git a/modules/simpletest/tests/theme_test_engine_inheritance_phpt_overriden.tpl.php b/modules/simpletest/tests/theme_test_engine_inheritance_phpt_overriden.tpl.php new file mode 100644 index 0000000..c4ae6e5 --- /dev/null +++ b/modules/simpletest/tests/theme_test_engine_inheritance_phpt_overriden.tpl.php @@ -0,0 +1 @@ +Inheritance 3. \ No newline at end of file diff --git a/modules/simpletest/tests/themes/test_subtheme_nyan_cat/test_subtheme_nyan_cat.info b/modules/simpletest/tests/themes/test_subtheme_nyan_cat/test_subtheme_nyan_cat.info new file mode 100644 index 0000000..a656ba5 --- /dev/null +++ b/modules/simpletest/tests/themes/test_subtheme_nyan_cat/test_subtheme_nyan_cat.info @@ -0,0 +1,5 @@ +name = 'Test subtheme with base theme and another theme engine' +description = 'Test subtheme with base theme and another theme engine.' +version = VERSION +core = 7.x +base theme = test_theme_nyan_cat diff --git a/modules/simpletest/tests/themes/test_theme/templates/theme_test_engine_inheritance_nyan_overriden.tpl.php b/modules/simpletest/tests/themes/test_theme/templates/theme_test_engine_inheritance_nyan_overriden.tpl.php new file mode 100644 index 0000000..12bca74 --- /dev/null +++ b/modules/simpletest/tests/themes/test_theme/templates/theme_test_engine_inheritance_nyan_overriden.tpl.php @@ -0,0 +1 @@ +Inheritance 6. \ No newline at end of file diff --git a/modules/simpletest/tests/themes/test_theme_nyan_cat/template.theme b/modules/simpletest/tests/themes/test_theme_nyan_cat/template.theme new file mode 100644 index 0000000..aadbce0 --- /dev/null +++ b/modules/simpletest/tests/themes/test_theme_nyan_cat/template.theme @@ -0,0 +1,13 @@ +