--- a/core/lib/Drupal/Core/Theme/ThemeManager.php +++ b/core/lib/Drupal/Core/Theme/ThemeManager.php @@ -291,7 +291,8 @@ protected function theme($hook, $variables = array()) { // Set a variable for the 'theme_hook_suggestion'. This is used to // maintain backwards compatibility with template engines. $theme_hook_suggestion = $hook; - $info['preprocess functions'] = $base_hook_info['preprocess functions']; + $info['preprocess functions'] = array_merge($base_hook_info['preprocess functions'], (array) $info['preprocess functions']); + $info['preprocess functions'] = array_unique($info['preprocess functions']); } } if (isset($info['preprocess functions'])) { diff --git a/core/modules/system/src/Tests/Theme/ThemeTest.php b/core/modules/system/src/Tests/Theme/ThemeTest.php index 0cd4e4e..bb6c822 100644 --- a/core/modules/system/src/Tests/Theme/ThemeTest.php +++ b/core/modules/system/src/Tests/Theme/ThemeTest.php @@ -287,4 +287,19 @@ function testRegionClass() { $this->assertEqual(count($elements), 1, 'New class found.'); } + /** + * Ensures suggestion preprocess functions run even for default implementations. + * + * The theme hook used by this test has its base preprocess function in a + * separate file, so this test also ensures that that file is correctly loaded + * when needed. + */ + function testSuggestionPreprocessForDefaults() { + // Test with both an unprimed and primed theme registry. + drupal_theme_rebuild(); + for ($i = 0; $i < 2; $i++) { + $this->drupalGet('theme-test/preprocess-suggestions'); + $this->assertText('Theme hook implementor=test_theme_theme_test_preprocess__suggestion(). Foo=template_preprocess_theme_test_preprocess', 'Theme hook ran with data available from a preprocess function for the suggested hook.'); + } + } } diff --git a/core/modules/system/tests/modules/theme_test/src/ThemeTestController.php b/core/modules/system/tests/modules/theme_test/src/ThemeTestController.php index 335ed83..3e805f3 100644 --- a/core/modules/system/tests/modules/theme_test/src/ThemeTestController.php +++ b/core/modules/system/tests/modules/theme_test/src/ThemeTestController.php @@ -143,4 +143,12 @@ public function nonHtml() { return new JsonResponse(['theme_initialized' => $theme_initialized]); } + /** + * Menu callback for testing preprocess functions are being run for theme + * suggestions. + */ + function preprocessSuggestions() { + return array('#theme' => 'theme_test_preprocess_suggestions'); + } + } diff --git a/core/modules/system/tests/modules/theme_test/templates/theme-test-preprocess-suggestions.html.twig b/core/modules/system/tests/modules/theme_test/templates/theme-test-preprocess-suggestions.html.twig new file mode 100644 index 0000000..db8a8a4 --- /dev/null +++ b/core/modules/system/tests/modules/theme_test/templates/theme-test-preprocess-suggestions.html.twig @@ -0,0 +1 @@ +{{ foo }} diff --git a/core/modules/system/tests/modules/theme_test/theme_test.module b/core/modules/system/tests/modules/theme_test/theme_test.module index 7ba5c2d..64d45e7 100644 --- a/core/modules/system/tests/modules/theme_test/theme_test.module +++ b/core/modules/system/tests/modules/theme_test/theme_test.module @@ -55,6 +55,9 @@ function theme_test_theme($existing, $type, $theme, $path) { $info['test_theme_not_existing_function'] = array( 'function' => 'test_theme_not_existing_function', ); + $items['theme_test_preprocess_suggestions'] = array( + 'variables' => array('foo' => ''), + ); return $items; } @@ -90,6 +93,17 @@ function theme_theme_test_function_template_override($variables) { } /** + * Implements hook_theme_suggestions_HOOK(). + */ +function theme_test_theme_suggestions_theme_test_preprocess_suggestions($variables) { + return array('theme_test_preprocess_suggestions__' . 'suggestion'); +} + +function theme_test_preprocess_theme_test_preprocess_suggestions(&$variables) { + $variables['foo'] = 'Theme hook implementor=theme_theme_test_preprocess_suggestions().'; +} + +/** * Prepares variables for test render element templates. * * Default template: theme-test-render-element.html.twig. diff --git a/core/modules/system/tests/modules/theme_test/theme_test.routing.yml b/core/modules/system/tests/modules/theme_test/theme_test.routing.yml index 2dbd188..1ff61cf 100644 --- a/core/modules/system/tests/modules/theme_test/theme_test.routing.yml +++ b/core/modules/system/tests/modules/theme_test/theme_test.routing.yml @@ -103,3 +103,10 @@ theme_test.non_html: _controller: '\Drupal\theme_test\ThemeTestController::nonHtml' requirements: _access: 'TRUE' + +theme_test.preprocess_suggestions: + path: '/theme-test/preprocess-suggestions' + defaults: + _controller: '\Drupal\theme_test\ThemeTestController::preprocessSuggestions' + requirements: + _access: 'TRUE' diff --git a/core/themes/bartik/.bartik.theme.swp b/core/themes/bartik/.bartik.theme.swp new file mode 100644 index 0000000..8d19dbe Binary files /dev/null and b/core/themes/bartik/.bartik.theme.swp differ diff --git a/core/themes/bartik/bartik.theme b/core/themes/bartik/bartik.theme index 7148e84..afc942c 100644 --- a/core/themes/bartik/bartik.theme +++ b/core/themes/bartik/bartik.theme @@ -128,3 +128,11 @@ function _bartik_process_page(&$variables) { $variables['site_slogan'] = Xss::filterAdmin($site_config->get('slogan')); } } + +/** + * Tests a theme overriding a default hook with a suggestion. + */ +function bartik_preprocess_theme_test_preprocess_suggestions__suggestion(&$variables) { + $variables['foo'] = 'Theme hook implementor=test_theme_theme_test_preprocess__suggestion().'; +} + diff --git a/core/themes/bartik/templates/theme-test-preprocess-suggestions--suggestion.html.twig b/core/themes/bartik/templates/theme-test-preprocess-suggestions--suggestion.html.twig new file mode 100644 index 0000000..db8a8a4 --- /dev/null +++ b/core/themes/bartik/templates/theme-test-preprocess-suggestions--suggestion.html.twig @@ -0,0 +1 @@ +{{ foo }}