From 7e163dbb196c57e1a2bdaba894b8456e78f745b3 Mon Sep 17 00:00:00 2001 From: Nathaniel Catchpole <catch@35733.no-reply.drupal.org> Date: Sun, 29 Sep 2013 08:19:59 +0100 Subject: [PATCH] Issue #1751194 by Cottser, mikl, effulgentsia, benjifisher: Introduce hook_theme_suggestions_HOOK() and hook_theme_suggestions_HOOK_alter(). --- core/includes/theme.inc | 134 +++++++++--------- .../Core/Extension/UpdateModuleHandler.php | 5 + core/modules/block/block.module | 51 ++++--- .../block/Tests/BlockPreprocessUnitTest.php | 57 ++++++++ .../BlockTemplateSuggestionsUnitTest.php | 13 +- core/modules/field/field.module | 25 ++-- core/modules/forum/forum.module | 44 +++--- core/modules/node/node.module | 18 ++- core/modules/search/search.pages.inc | 16 ++- .../Tests/Theme/ThemeSuggestionsAlterTest.php | 120 ++++++++++++++++ core/modules/system/system.module | 48 +++++++ .../theme_suggestions_test.info.yml | 7 + .../theme_suggestions_test.module | 27 ++++ .../Drupal/theme_test/ThemeTestController.php | 28 ++++ .../theme-test-specific-suggestions.html.twig | 2 + .../theme-test-suggestion-provided.html.twig | 2 + .../theme-test-suggestions.html.twig | 2 + .../modules/theme_test/theme_test.module | 29 ++++ .../modules/theme_test/theme_test.routing.yml | 28 ++++ ...ecific-suggestions--variant--foo.html.twig | 5 + ...st-specific-suggestions--variant.html.twig | 5 + ...me-test-suggestion-provided--foo.html.twig | 2 + ...est-suggestions--module-override.html.twig | 2 + ...test-suggestions--theme-override.html.twig | 2 + .../tests/themes/test_theme/test_theme.theme | 40 ++++++ core/modules/system/theme.api.php | 61 ++++++++ core/modules/taxonomy/taxonomy.module | 17 ++- core/modules/views/views.module | 44 ++++-- core/modules/views_ui/views_ui.theme.inc | 8 +- 29 files changed, 699 insertions(+), 143 deletions(-) create mode 100644 core/modules/block/lib/Drupal/block/Tests/BlockPreprocessUnitTest.php create mode 100644 core/modules/system/lib/Drupal/system/Tests/Theme/ThemeSuggestionsAlterTest.php create mode 100644 core/modules/system/tests/modules/theme_suggestions_test/theme_suggestions_test.info.yml create mode 100644 core/modules/system/tests/modules/theme_suggestions_test/theme_suggestions_test.module create mode 100644 core/modules/system/tests/modules/theme_test/templates/theme-test-specific-suggestions.html.twig create mode 100644 core/modules/system/tests/modules/theme_test/templates/theme-test-suggestion-provided.html.twig create mode 100644 core/modules/system/tests/modules/theme_test/templates/theme-test-suggestions.html.twig create mode 100644 core/modules/system/tests/themes/test_theme/templates/theme-test-specific-suggestions--variant--foo.html.twig create mode 100644 core/modules/system/tests/themes/test_theme/templates/theme-test-specific-suggestions--variant.html.twig create mode 100644 core/modules/system/tests/themes/test_theme/templates/theme-test-suggestion-provided--foo.html.twig create mode 100644 core/modules/system/tests/themes/test_theme/templates/theme-test-suggestions--module-override.html.twig create mode 100644 core/modules/system/tests/themes/test_theme/templates/theme-test-suggestions--theme-override.html.twig diff --git a/core/includes/theme.inc b/core/includes/theme.inc index 2f4a601021cd..094a339e2bff 100644 --- a/core/includes/theme.inc +++ b/core/includes/theme.inc @@ -862,17 +862,16 @@ function drupal_find_base_themes($themes, $key, $used_keys = array()) { * a noticeable performance penalty. * * @subsection sub_alternate_suggestions Suggesting Alternate Hooks - * There are two special variables that these preprocess functions can set: - * 'theme_hook_suggestion' and 'theme_hook_suggestions'. These will be merged - * together to form a list of 'suggested' alternate theme hooks to use, in - * reverse order of priority. theme_hook_suggestion will always be a higher - * priority than items in theme_hook_suggestions. theme() will use the highest - * priority implementation that exists. If none exists, theme() will use the - * implementation for the theme hook it was called with. These suggestions are - * similar to, and are used for similar reasons as, calling theme() with an - * array as the $hook parameter (see below). The difference is whether the - * suggestions are determined by the code that calls theme() or by a preprocess - * function. + * Alternate hooks can be suggested by implementing the hook-specific + * hook_theme_suggestions_HOOK_alter() or the generic + * hook_theme_suggestions_alter(). These alter hooks are used to manipulate an + * array of suggested alternate theme hooks to use, in reverse order of + * priority. theme() will use the highest priority implementation that exists. + * If none exists, theme() will use the implementation for the theme hook it was + * called with. These suggestions are similar to and are used for similar + * reasons as calling theme() with an array as the $hook parameter (see below). + * The difference is whether the suggestions are determined by the code that + * calls theme() or by altering the suggestions via the suggestion alter hooks. * * @param $hook * The name of the theme hook to call. If the name contains a @@ -1002,11 +1001,41 @@ function theme($hook, $variables = array()) { 'theme_hook_original' => $original_hook, ); - // Invoke the variable preprocessors, if any. The preprocessors may specify - // alternate suggestions for which hook's template/function to use. If the - // hook is a suggestion of a base hook, invoke the variable preprocessors of - // the base hook, but retain the suggestion as a high priority suggestion to - // be used unless overridden by a variable preprocessor function. + // Set base hook for later use. For example if '#theme' => 'node__article' + // is called, we run hook_theme_suggestions_node_alter() rather than + // hook_theme_suggestions_node__article_alter(), and also pass in the base + // hook as the last parameter to the suggestions alter hooks. + if (isset($info['base hook'])) { + $base_theme_hook = $info['base hook']; + } + else { + $base_theme_hook = $hook; + } + + // Invoke hook_theme_suggestions_HOOK(). + $suggestions = Drupal::moduleHandler()->invokeAll('theme_suggestions_' . $base_theme_hook, array($variables)); + // If theme() was invoked with a direct theme suggestion like + // '#theme' => 'node__article', add it to the suggestions array before + // invoking suggestion alter hooks. + if (isset($info['base hook'])) { + $suggestions[] = $hook; + } + // Allow suggestions to be altered via hook_theme_suggestions_HOOK_alter(). + Drupal::moduleHandler()->alter('theme_suggestions_' . $base_theme_hook, $suggestions, $variables); + + // Check if each suggestion exists in the theme registry, and if so, + // use it instead of the hook that theme() was called with. For example, a + // function may call theme('node', ...), but a module can add + // 'node__article' as a suggestion via hook_theme_suggestions_HOOK_alter(), + // enabling a theme to have an alternate template file for article nodes. + foreach (array_reverse($suggestions) as $suggestion) { + if (isset($hooks[$suggestion])) { + $info = $hooks[$suggestion]; + break; + } + } + + // Invoke the variable preprocessors, if any. if (isset($info['base hook'])) { $base_hook = $info['base hook']; $base_hook_info = $hooks[$base_hook]; @@ -1017,44 +1046,20 @@ function theme($hook, $variables = array()) { include_once DRUPAL_ROOT . '/' . $include_file; } } + // Replace the preprocess functions with those from the base hook. if (isset($base_hook_info['preprocess functions'])) { - $variables['theme_hook_suggestion'] = $hook; - $hook = $base_hook; - $info = $base_hook_info; + // 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']; } } if (isset($info['preprocess functions'])) { - $variables['theme_hook_suggestions'] = array(); foreach ($info['preprocess functions'] as $preprocessor_function) { if (function_exists($preprocessor_function)) { $preprocessor_function($variables, $hook, $info); } } - // If the preprocess functions specified hook suggestions, and the - // suggestion exists in the theme registry, use it instead of the hook that - // theme() was called with. This allows the preprocess step to route to a - // more specific theme hook. For example, a function may call - // theme('node', ...), but a preprocess function can add 'node__article' as - // a suggestion, enabling a theme to have an alternate template file for - // article nodes. Suggestions are checked in the following order: - // - The 'theme_hook_suggestion' variable is checked first. It overrides - // all others. - // - The 'theme_hook_suggestions' variable is checked in FILO order, so the - // last suggestion added to the array takes precedence over suggestions - // added earlier. - $suggestions = array(); - if (!empty($variables['theme_hook_suggestions'])) { - $suggestions = $variables['theme_hook_suggestions']; - } - if (!empty($variables['theme_hook_suggestion'])) { - $suggestions[] = $variables['theme_hook_suggestion']; - } - foreach (array_reverse($suggestions) as $suggestion) { - if (isset($hooks[$suggestion])) { - $info = $hooks[$suggestion]; - break; - } - } } // Generate the output using either a function or a template. @@ -1117,6 +1122,16 @@ function theme($hook, $variables = array()) { if (isset($info['path'])) { $template_file = $info['path'] . '/' . $template_file; } + // Add the theme suggestions to the variables array just before rendering + // the template for backwards compatibility with template engines. + $variables['theme_hook_suggestions'] = $suggestions; + // For backwards compatibility, pass 'theme_hook_suggestion' on to the + // template engine. This is only set when calling a direct suggestion like + // '#theme' => 'menu_tree__shortcut_default' when the template exists in the + // current theme. + if (isset($theme_hook_suggestion)) { + $variables['theme_hook_suggestion'] = $theme_hook_suggestion; + } $output = $render_function($template_file, $variables); } @@ -2593,11 +2608,6 @@ function template_preprocess_html(&$variables) { drupal_add_html_head($element, $name); } - // Populate the page template suggestions. - if ($suggestions = theme_get_suggestions(arg(), 'html')) { - $variables['theme_hook_suggestions'] = $suggestions; - } - drupal_add_library('system', 'html5shiv', TRUE); // Render page_top and page_bottom into top level variables. @@ -2706,11 +2716,6 @@ function template_preprocess_page(&$variables) { $variables['node'] = $node; } - // Populate the page template suggestions. - if ($suggestions = theme_get_suggestions(arg(), 'page')) { - $variables['theme_hook_suggestions'] = $suggestions; - } - // Prepare render array for messages. drupal_get_messages() is called later, // when this variable is rendered in a theme function or template file. $variables['messages'] = array( @@ -2731,9 +2736,10 @@ function template_preprocess_page(&$variables) { /** * Generate an array of suggestions from path arguments. * - * This is typically called for adding to the 'theme_hook_suggestions' or - * 'attributes' class key variables from within preprocess functions, when - * wanting to base the additional suggestions on the path of the current page. + * This is typically called for adding to the suggestions in + * hook_theme_suggestions_HOOK_alter() or adding to 'attributes' class key + * variables from within preprocess functions, when wanting to base the + * additional suggestions or classes on the path of the current page. * * @param $args * An array of path arguments, such as from function arg(). @@ -2747,9 +2753,8 @@ function template_preprocess_page(&$variables) { * * @return * An array of suggestions, suitable for adding to - * $variables['theme_hook_suggestions'] within a preprocess function or to - * $variables['attributes']['class'] if the suggestions represent extra CSS - * classes. + * hook_theme_suggestions_HOOK_alter() or to $variables['attributes']['class'] + * if the suggestions represent extra CSS classes. */ function theme_get_suggestions($args, $base, $delimiter = '__') { @@ -2923,12 +2928,6 @@ function template_preprocess_maintenance_page(&$variables) { $variables['attributes']['class'][] = 'sidebar-' . $variables['layout']; } - // 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['theme_hook_suggestion'] = 'maintenance_page__offline'; - } - $variables['head'] = drupal_get_html_head(); // While this code is used in the installer, the language module may not be @@ -2989,7 +2988,6 @@ function template_preprocess_region(&$variables) { $variables['attributes']['class'][] = 'region'; $variables['attributes']['class'][] = drupal_html_class('region-' . $variables['region']); - $variables['theme_hook_suggestions'][] = 'region__' . $variables['region']; } /** diff --git a/core/lib/Drupal/Core/Extension/UpdateModuleHandler.php b/core/lib/Drupal/Core/Extension/UpdateModuleHandler.php index 5fcf9901c7b6..7d7ac9c93f3e 100644 --- a/core/lib/Drupal/Core/Extension/UpdateModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/UpdateModuleHandler.php @@ -24,6 +24,11 @@ public function getImplementations($hook) { if (substr($hook, -6) === '_alter') { return array(); } + // theme() is called during updates and fires hooks, so whitelist the + // system module. + if (substr($hook, 0, 6) == 'theme_') { + return array('system'); + } switch ($hook) { // hook_requirements is necessary for updates to work. case 'requirements': diff --git a/core/modules/block/block.module b/core/modules/block/block.module index 8bcce1f7c1a1..f1112d60fb90 100644 --- a/core/modules/block/block.module +++ b/core/modules/block/block.module @@ -484,6 +484,39 @@ function block_rebuild() { } } +/** + * Implements hook_theme_suggestions_HOOK(). + */ +function block_theme_suggestions_block(array $variables) { + $suggestions = array(); + + $suggestions[] = 'block__' . $variables['elements']['#configuration']['module']; + // Hyphens (-) and underscores (_) play a special role in theme suggestions. + // Theme suggestions should only contain underscores, because within + // drupal_find_theme_templates(), underscores are converted to hyphens to + // match template file names, and then converted back to underscores to match + // pre-processing and other function names. So if your theme suggestion + // contains a hyphen, it will end up as an underscore after this conversion, + // and your function names won't be recognized. So, we need to convert + // hyphens to underscores in block deltas for the theme suggestions. + + // We can safely explode on : because we know the Block plugin type manager + // enforces that delimiter for all derivatives. + $parts = explode(':', $variables['elements']['#plugin_id']); + $suggestion = 'block'; + while ($part = array_shift($parts)) { + $suggestions[] = $suggestion .= '__' . strtr($part, '-', '_'); + } + + if ($id = $variables['elements']['#block']->id()) { + $config_id = explode('.', $id); + $machine_name = array_pop($config_id); + $suggestions[] = 'block__' . $machine_name; + } + + return $suggestions; +} + /** * Prepares variables for block templates. * @@ -527,29 +560,11 @@ function template_preprocess_block(&$variables) { // Add default class for block content. $variables['content_attributes']['class'][] = 'content'; - $variables['theme_hook_suggestions'][] = 'block__' . $variables['configuration']['module']; - // Hyphens (-) and underscores (_) play a special role in theme suggestions. - // Theme suggestions should only contain underscores, because within - // drupal_find_theme_templates(), underscores are converted to hyphens to - // match template file names, and then converted back to underscores to match - // pre-processing and other function names. So if your theme suggestion - // contains a hyphen, it will end up as an underscore after this conversion, - // and your function names won't be recognized. So, we need to convert - // hyphens to underscores in block deltas for the theme suggestions. - - // We can safely explode on : because we know the Block plugin type manager - // enforces that delimiter for all derivatives. - $parts = explode(':', $variables['plugin_id']); - $suggestion = 'block'; - while ($part = array_shift($parts)) { - $variables['theme_hook_suggestions'][] = $suggestion .= '__' . strtr($part, '-', '_'); - } // Create a valid HTML ID and make sure it is unique. if ($id = $variables['elements']['#block']->id()) { $config_id = explode('.', $id); $machine_name = array_pop($config_id); $variables['attributes']['id'] = drupal_html_id('block-' . $machine_name); - $variables['theme_hook_suggestions'][] = 'block__' . $machine_name; } } diff --git a/core/modules/block/lib/Drupal/block/Tests/BlockPreprocessUnitTest.php b/core/modules/block/lib/Drupal/block/Tests/BlockPreprocessUnitTest.php new file mode 100644 index 000000000000..513fda54ee14 --- /dev/null +++ b/core/modules/block/lib/Drupal/block/Tests/BlockPreprocessUnitTest.php @@ -0,0 +1,57 @@ +<?php + +/** + * @file + * Contains \Drupal\block\Tests\BlockPreprocessUnitTest. + */ + +namespace Drupal\block\Tests; + +use Drupal\simpletest\WebTestBase; + +/** + * Unit tests for template_preprocess_block(). + */ +class BlockPreprocessUnitTest extends WebTestBase { + + /** + * Modules to enable. + * + * @var array + */ + public static $modules = array('block'); + + public static function getInfo() { + return array( + 'name' => 'Block preprocess', + 'description' => 'Test the template_preprocess_block() function.', + 'group' => 'Block', + ); + } + + /** + * Tests block classes with template_preprocess_block(). + */ + function testBlockClasses() { + // Define a block with a derivative to be preprocessed, which includes both + // an underscore (not transformed) and a hyphen (transformed to underscore), + // and generates possibilities for each level of derivative. + // @todo Clarify this comment. + $block = entity_create('block', array( + 'plugin' => 'system_menu_block:admin', + 'region' => 'footer', + 'id' => \Drupal::config('system.theme')->get('default') . '.machinename', + )); + + $variables = array(); + $variables['elements']['#block'] = $block; + $variables['elements']['#configuration'] = $block->getPlugin()->getConfiguration(); + $variables['elements']['#plugin_id'] = $block->get('plugin'); + $variables['elements']['content'] = array(); + // Test adding a class to the block content. + $variables['content_attributes']['class'][] = 'test-class'; + template_preprocess_block($variables); + $this->assertEqual($variables['content_attributes']['class'], array('test-class', 'content'), 'Default .content class added to block content_attributes'); + } + +} diff --git a/core/modules/block/lib/Drupal/block/Tests/BlockTemplateSuggestionsUnitTest.php b/core/modules/block/lib/Drupal/block/Tests/BlockTemplateSuggestionsUnitTest.php index cf50e7880406..8289eb4ed1dd 100644 --- a/core/modules/block/lib/Drupal/block/Tests/BlockTemplateSuggestionsUnitTest.php +++ b/core/modules/block/lib/Drupal/block/Tests/BlockTemplateSuggestionsUnitTest.php @@ -10,7 +10,7 @@ use Drupal\simpletest\WebTestBase; /** - * Unit tests for template_preprocess_block(). + * Unit tests for block_theme_suggestions_block(). */ class BlockTemplateSuggestionsUnitTest extends WebTestBase { @@ -24,13 +24,13 @@ class BlockTemplateSuggestionsUnitTest extends WebTestBase { public static function getInfo() { return array( 'name' => 'Block template suggestions', - 'description' => 'Test the template_preprocess_block() function.', + 'description' => 'Test the block_theme_suggestions_block() function.', 'group' => 'Block', ); } /** - * Test if template_preprocess_block() handles the suggestions right. + * Tests template suggestions from block_theme_suggestions_block(). */ function testBlockThemeHookSuggestions() { // Define a block with a derivative to be preprocessed, which includes both @@ -48,11 +48,8 @@ function testBlockThemeHookSuggestions() { $variables['elements']['#configuration'] = $block->getPlugin()->getConfiguration(); $variables['elements']['#plugin_id'] = $block->get('plugin'); $variables['elements']['content'] = array(); - // Test adding a class to the block content. - $variables['content_attributes']['class'][] = 'test-class'; - template_preprocess_block($variables); - $this->assertEqual($variables['theme_hook_suggestions'], array('block__system', 'block__system_menu_block', 'block__system_menu_block__admin', 'block__machinename')); - $this->assertEqual($variables['content_attributes']['class'], array('test-class', 'content'), 'Default .content class added to block content_attributes'); + $suggestions = block_theme_suggestions_block($variables); + $this->assertEqual($suggestions, array('block__system', 'block__system_menu_block', 'block__system_menu_block__admin', 'block__machinename')); } } diff --git a/core/modules/field/field.module b/core/modules/field/field.module index 01c9181edaa0..f4a6299dcd1f 100644 --- a/core/modules/field/field.module +++ b/core/modules/field/field.module @@ -659,6 +659,22 @@ function field_page_build(&$page) { $page['#attached']['css'][$path . '/css/field.module.css'] = array('every_page' => TRUE); } +/** + * Implements hook_theme_suggestions_HOOK(). + */ +function field_theme_suggestions_field(array $variables) { + $suggestions = array(); + $element = $variables['element']; + + $suggestions[] = 'field__' . $element['#field_type']; + $suggestions[] = 'field__' . $element['#field_name']; + $suggestions[] = 'field__' . $element['#entity_type'] . '__' . $element['#bundle']; + $suggestions[] = 'field__' . $element['#entity_type'] . '__' . $element['#field_name']; + $suggestions[] = 'field__' . $element['#entity_type'] . '__' . $element['#field_name'] . '__' . $element['#bundle']; + + return $suggestions; +} + /** * Prepares variables for field templates. * @@ -716,15 +732,6 @@ function template_preprocess_field(&$variables, $hook) { $variables['attributes']['class'][] = 'clearfix'; } - // Add specific suggestions that can override the default implementation. - $variables['theme_hook_suggestions'] = array( - 'field__' . $element['#field_type'], - 'field__' . $element['#field_name'], - 'field__' . $element['#entity_type'] . '__' . $element['#bundle'], - 'field__' . $element['#entity_type'] . '__' . $element['#field_name'], - 'field__' . $element['#entity_type'] . '__' . $element['#field_name'] . '__' . $element['#bundle'], - ); - static $default_attributes; if (!isset($default_attributes)) { $default_attributes = new Attribute; diff --git a/core/modules/forum/forum.module b/core/modules/forum/forum.module index 77ef530003fe..090d5934bead 100644 --- a/core/modules/forum/forum.module +++ b/core/modules/forum/forum.module @@ -588,6 +588,33 @@ function forum_preprocess_block(&$variables) { } } +/** + * Implements hook_theme_suggestions_HOOK(). + */ +function forum_theme_suggestions_forums(array $variables) { + $suggestions = array(); + $tid = $variables['term']->id(); + + // Provide separate template suggestions based on what's being output. Topic + // ID is also accounted for. Check both variables to be safe then the inverse. + // Forums with topic IDs take precedence. + if ($variables['forums'] && !$variables['topics']) { + $suggestions[] = 'forums__containers'; + $suggestions[] = 'forums__' . $tid; + $suggestions[] = 'forums__containers__' . $tid; + } + elseif (!$variables['forums'] && $variables['topics']) { + $suggestions[] = 'forums__topics'; + $suggestions[] = 'forums__' . $tid; + $suggestions[] = 'forums__topics__' . $tid; + } + else { + $suggestions[] = 'forums__' . $tid; + } + + return $suggestions; +} + /** * Prepares variables for forums templates. * @@ -635,23 +662,6 @@ function template_preprocess_forums(&$variables) { else { $variables['topics'] = array(); } - - // Provide separate template suggestions based on what's being output. Topic id is also accounted for. - // Check both variables to be safe then the inverse. Forums with topic ID's take precedence. - if ($variables['forums'] && !$variables['topics']) { - $variables['theme_hook_suggestions'][] = 'forums__containers'; - $variables['theme_hook_suggestions'][] = 'forums__' . $variables['tid']; - $variables['theme_hook_suggestions'][] = 'forums__containers__' . $variables['tid']; - } - elseif (!$variables['forums'] && $variables['topics']) { - $variables['theme_hook_suggestions'][] = 'forums__topics'; - $variables['theme_hook_suggestions'][] = 'forums__' . $variables['tid']; - $variables['theme_hook_suggestions'][] = 'forums__topics__' . $variables['tid']; - } - else { - $variables['theme_hook_suggestions'][] = 'forums__' . $variables['tid']; - } - } else { $variables['forums'] = array(); diff --git a/core/modules/node/node.module b/core/modules/node/node.module index 376efa447986..24f46ecfd5af 100644 --- a/core/modules/node/node.module +++ b/core/modules/node/node.module @@ -641,6 +641,19 @@ function node_preprocess_block(&$variables) { } } +/** + * Implements hook_theme_suggestions_HOOK(). + */ +function node_theme_suggestions_node(array $variables) { + $suggestions = array(); + $node = $variables['elements']['#node']; + + $suggestions[] = 'node__' . $node->bundle(); + $suggestions[] = 'node__' . $node->id(); + + return $suggestions; +} + /** * Prepares variables for node templates. * @@ -730,11 +743,6 @@ function template_preprocess_node(&$variables) { if (isset($variables['preview'])) { $variables['attributes']['class'][] = 'preview'; } - - // Clean up name so there are no underscores. - $variables['theme_hook_suggestions'][] = 'node__' . $node->bundle(); - $variables['theme_hook_suggestions'][] = 'node__' . $node->id(); - $variables['content_attributes']['class'][] = 'content'; } diff --git a/core/modules/search/search.pages.inc b/core/modules/search/search.pages.inc index 9d4aebee702d..5b1e1a06153a 100644 --- a/core/modules/search/search.pages.inc +++ b/core/modules/search/search.pages.inc @@ -74,6 +74,13 @@ function search_view($plugin_id = NULL, $keys = '') { return $build; } +/** + * Implements hook_theme_suggestions_HOOK(). + */ +function search_theme_suggestions_search_results(array $variables) { + return array('search_results__' . $variables['plugin_id']); +} + /** * Prepares variables for search results templates. * @@ -100,7 +107,13 @@ function template_preprocess_search_results(&$variables) { // @todo Revisit where this help text is added, see also // http://drupal.org/node/1918856. $variables['help'] = search_help('search#noresults', drupal_help_arg()); - $variables['theme_hook_suggestions'][] = 'search_results__' . $variables['plugin_id']; +} + +/** + * Implements hook_theme_suggestions_HOOK(). + */ +function search_theme_suggestions_search_result(array $variables) { + return array('search_result__' . $variables['plugin_id']); } /** @@ -148,7 +161,6 @@ function template_preprocess_search_result(&$variables) { // Provide separated and grouped meta information.. $variables['info_split'] = $info; $variables['info'] = implode(' - ', $info); - $variables['theme_hook_suggestions'][] = 'search_result__' . $variables['plugin_id']; } /** diff --git a/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeSuggestionsAlterTest.php b/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeSuggestionsAlterTest.php new file mode 100644 index 000000000000..00cd0081aeac --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeSuggestionsAlterTest.php @@ -0,0 +1,120 @@ +<?php + +/** + * @file + * Contains \Drupal\system\Tests\Theme\ThemeSuggestionsAlterTest. + */ + +namespace Drupal\system\Tests\Theme; + +use Drupal\simpletest\WebTestBase; + +/** + * Tests theme suggestion alter hooks. + */ +class ThemeSuggestionsAlterTest extends WebTestBase { + + /** + * Modules to enable. + * + * @var array + */ + public static $modules = array('theme_test'); + + public static function getInfo() { + return array( + 'name' => 'Theme suggestions alter', + 'description' => 'Test theme suggestion alter hooks.', + 'group' => 'Theme', + ); + } + + function setUp() { + parent::setUp(); + theme_enable(array('test_theme')); + } + + /** + * Tests that hooks to provide theme suggestions work. + */ + function testTemplateSuggestions() { + $this->drupalGet('theme-test/suggestion-provided'); + $this->assertText('Template for testing suggestions provided by the module declaring the theme hook.'); + + // Enable test_theme, it contains a template suggested by theme_test.module + // in theme_test_theme_suggestions_theme_test_suggestion_provided(). + config('system.theme') + ->set('default', 'test_theme') + ->save(); + + $this->drupalGet('theme-test/suggestion-provided'); + $this->assertText('Template overridden based on suggestion provided by the module declaring the theme hook.'); + } + + /** + * Tests that theme suggestion alter hooks work for templates. + */ + function testTemplateSuggestionsAlter() { + $this->drupalGet('theme-test/suggestion-alter'); + $this->assertText('Original template.'); + + // Enable test_theme and test that themes can alter template suggestions. + config('system.theme') + ->set('default', 'test_theme') + ->save(); + $this->drupalGet('theme-test/suggestion-alter'); + $this->assertText('Template overridden based on new theme suggestion provided by the test_theme theme.'); + + // Enable the theme_suggestions_test module to test modules implementing + // suggestions alter hooks. + \Drupal::moduleHandler()->install(array('theme_suggestions_test')); + $this->drupalGet('theme-test/suggestion-alter'); + $this->assertText('Template overridden based on new theme suggestion provided by a module.'); + } + + /** + * Tests that theme suggestion alter hooks work for specific theme calls. + */ + function testSpecificSuggestionsAlter() { + // Test that the default template is rendered. + $this->drupalGet('theme-test/specific-suggestion-alter'); + $this->assertText('Template for testing specific theme calls.'); + + config('system.theme') + ->set('default', 'test_theme') + ->save(); + + // Test a specific theme call similar to '#theme' => 'node__article'. + $this->drupalGet('theme-test/specific-suggestion-alter'); + $this->assertText('Template matching the specific theme call.'); + $this->assertText('theme_test_specific_suggestions__variant', 'Specific theme call is added to the suggestions array.'); + + // Ensure that the base hook is used to determine the suggestion alter hook. + \Drupal::moduleHandler()->install(array('theme_suggestions_test')); + $this->drupalGet('theme-test/specific-suggestion-alter'); + $this->assertText('Template overridden based on suggestion alter hook determined by the base hook.'); + $this->assertTrue(strpos($this->drupalGetContent(), 'theme_test_specific_suggestions__variant') < strpos($this->drupalGetContent(), 'theme_test_specific_suggestions__variant__foo'), 'Specific theme call is added to the suggestions array before the suggestions alter hook.'); + } + + /** + * Tests that theme suggestion alter hooks work for theme functions. + */ + function testThemeFunctionSuggestionsAlter() { + $this->drupalGet('theme-test/function-suggestion-alter'); + $this->assertText('Original theme function.'); + + // Enable test_theme and test that themes can alter theme suggestions. + config('system.theme') + ->set('default', 'test_theme') + ->save(); + $this->drupalGet('theme-test/function-suggestion-alter'); + $this->assertText('Theme function overridden based on new theme suggestion provided by the test_theme theme.'); + + // Enable the theme_suggestions_test module to test modules implementing + // suggestions alter hooks. + \Drupal::moduleHandler()->install(array('theme_suggestions_test')); + $this->drupalGet('theme-test/function-suggestion-alter'); + $this->assertText('Theme function overridden based on new theme suggestion provided by a module.'); + } + +} diff --git a/core/modules/system/system.module b/core/modules/system/system.module index 820d57d1955a..8437e4c47c3f 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -909,6 +909,54 @@ function system_menu() { return $items; } +/** + * Implements hook_theme_suggestions_HOOK(). + */ +function system_theme_suggestions_html(array $variables) { + return theme_get_suggestions(arg(), 'html'); +} + +/** + * Implements hook_theme_suggestions_HOOK(). + */ +function system_theme_suggestions_page(array $variables) { + return theme_get_suggestions(arg(), 'page'); +} + +/** + * Implements hook_theme_suggestions_HOOK(). + */ +function system_theme_suggestions_maintenance_page(array $variables) { + $suggestions = array(); + + // Dead databases will show error messages so supplying this template will + // allow themers to override the page and the content completely. + $offline = defined('MAINTENANCE_MODE'); + try { + drupal_is_front_page(); + } + catch (Exception $e) { + // The database is not yet available. + $offline = TRUE; + } + if ($offline) { + $suggestions[] = 'maintenance_page__offline'; + } + + return $suggestions; +} + +/** + * Implements hook_theme_suggestions_HOOK(). + */ +function system_theme_suggestions_region(array $variables) { + $suggestions = array(); + if (!empty($variables['elements']['#region'])) { + $suggestions[] = 'region__' . $variables['elements']['#region']; + } + return $suggestions; +} + /** * Theme callback for the default batch page. */ diff --git a/core/modules/system/tests/modules/theme_suggestions_test/theme_suggestions_test.info.yml b/core/modules/system/tests/modules/theme_suggestions_test/theme_suggestions_test.info.yml new file mode 100644 index 000000000000..95f429f2894a --- /dev/null +++ b/core/modules/system/tests/modules/theme_suggestions_test/theme_suggestions_test.info.yml @@ -0,0 +1,7 @@ +name: 'Theme suggestions test' +type: module +description: 'Support module for testing theme suggestions.' +package: Testing +version: VERSION +core: 8.x +hidden: true diff --git a/core/modules/system/tests/modules/theme_suggestions_test/theme_suggestions_test.module b/core/modules/system/tests/modules/theme_suggestions_test/theme_suggestions_test.module new file mode 100644 index 000000000000..9ecaeebd63bc --- /dev/null +++ b/core/modules/system/tests/modules/theme_suggestions_test/theme_suggestions_test.module @@ -0,0 +1,27 @@ +<?php + +/** + * @file + * Support module for testing theme suggestions. + */ + +/** + * Implements hook_theme_suggestions_HOOK_alter(). + */ +function theme_suggestions_test_theme_suggestions_theme_test_suggestions_alter(array &$suggestions, array $variables) { + $suggestions[] = 'theme_test_suggestions__' . 'module_override'; +} + +/** + * Implements hook_theme_suggestions_HOOK_alter(). + */ +function theme_suggestions_test_theme_suggestions_theme_test_function_suggestions_alter(array &$suggestions, array $variables) { + $suggestions[] = 'theme_test_function_suggestions__' . 'module_override'; +} + +/** + * Implements hook_theme_suggestions_HOOK_alter(). + */ +function theme_suggestions_test_theme_suggestions_theme_test_specific_suggestions_alter(array &$suggestions, array $variables) { + $suggestions[] = 'theme_test_specific_suggestions__' . 'variant__foo'; +} diff --git a/core/modules/system/tests/modules/theme_test/lib/Drupal/theme_test/ThemeTestController.php b/core/modules/system/tests/modules/theme_test/lib/Drupal/theme_test/ThemeTestController.php index 31bbd93613d0..8da6c62206c0 100644 --- a/core/modules/system/tests/modules/theme_test/lib/Drupal/theme_test/ThemeTestController.php +++ b/core/modules/system/tests/modules/theme_test/lib/Drupal/theme_test/ThemeTestController.php @@ -92,4 +92,32 @@ public function testRequestListener() { return $GLOBALS['theme_test_output']; } + /** + * Menu callback for testing suggestion alter hooks with template files. + */ + function suggestionProvided() { + return array('#theme' => 'theme_test_suggestion_provided'); + } + + /** + * Menu callback for testing suggestion alter hooks with template files. + */ + function suggestionAlter() { + return array('#theme' => 'theme_test_suggestions'); + } + + /** + * Menu callback for testing suggestion alter hooks with specific suggestions. + */ + function specificSuggestionAlter() { + return array('#theme' => 'theme_test_specific_suggestions__variant'); + } + + /** + * Menu callback for testing suggestion alter hooks with theme functions. + */ + function functionSuggestionAlter() { + return array('#theme' => 'theme_test_function_suggestions'); + } + } diff --git a/core/modules/system/tests/modules/theme_test/templates/theme-test-specific-suggestions.html.twig b/core/modules/system/tests/modules/theme_test/templates/theme-test-specific-suggestions.html.twig new file mode 100644 index 000000000000..6e112fd723af --- /dev/null +++ b/core/modules/system/tests/modules/theme_test/templates/theme-test-specific-suggestions.html.twig @@ -0,0 +1,2 @@ +{# Output for Theme API test #} +Template for testing specific theme calls. diff --git a/core/modules/system/tests/modules/theme_test/templates/theme-test-suggestion-provided.html.twig b/core/modules/system/tests/modules/theme_test/templates/theme-test-suggestion-provided.html.twig new file mode 100644 index 000000000000..c9d96dd0587d --- /dev/null +++ b/core/modules/system/tests/modules/theme_test/templates/theme-test-suggestion-provided.html.twig @@ -0,0 +1,2 @@ +{# Output for Theme API test #} +Template for testing suggestions provided by the module declaring the theme hook. diff --git a/core/modules/system/tests/modules/theme_test/templates/theme-test-suggestions.html.twig b/core/modules/system/tests/modules/theme_test/templates/theme-test-suggestions.html.twig new file mode 100644 index 000000000000..dfc848c94d9c --- /dev/null +++ b/core/modules/system/tests/modules/theme_test/templates/theme-test-suggestions.html.twig @@ -0,0 +1,2 @@ +{# Output for Theme API test #} +Original template. 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 08e9c0128695..7c205c88ff85 100644 --- a/core/modules/system/tests/modules/theme_test/theme_test.module +++ b/core/modules/system/tests/modules/theme_test/theme_test.module @@ -14,6 +14,21 @@ function theme_test_theme($existing, $type, $theme, $path) { $items['theme_test_template_test_2'] = array( 'template' => 'theme_test.template_test', ); + $items['theme_test_suggestion_provided'] = array( + 'template' => 'theme-test-suggestion-provided', + 'variables' => array(), + ); + $items['theme_test_specific_suggestions'] = array( + 'template' => 'theme-test-specific-suggestions', + 'variables' => array(), + ); + $items['theme_test_suggestions'] = array( + 'template' => 'theme-test-suggestions', + 'variables' => array(), + ); + $items['theme_test_function_suggestions'] = array( + 'variables' => array(), + ); $items['theme_test_foo'] = array( 'variables' => array('foo' => NULL), ); @@ -131,3 +146,17 @@ function template_preprocess_theme_test_render_element(&$variables) { function theme_theme_test_render_element_children($variables) { return drupal_render($variables['element']); } + +/** + * Returns HTML for a theme function suggestion test. + */ +function theme_theme_test_function_suggestions($variables) { + return 'Original theme function.'; +} + +/** + * Implements hook_theme_suggestions_HOOK(). + */ +function theme_test_theme_suggestions_theme_test_suggestion_provided(array $variables) { + return array('theme_test_suggestion_provided__' . 'foo'); +} 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 b75ee53f1cf5..b4a7fd64fe98 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 @@ -41,3 +41,31 @@ theme_test.request_listener: _content: '\Drupal\theme_test\ThemeTestController::testRequestListener' requirements: _access: 'TRUE' + +suggestion_alter: + path: '/theme-test/suggestion-alter' + defaults: + _content: '\Drupal\theme_test\ThemeTestController::suggestionAlter' + requirements: + _permission: 'access content' + +suggestion_provided: + path: '/theme-test/suggestion-provided' + defaults: + _content: '\Drupal\theme_test\ThemeTestController::suggestionProvided' + requirements: + _permission: 'access content' + +specific_suggestion_alter: + path: '/theme-test/specific-suggestion-alter' + defaults: + _content: '\Drupal\theme_test\ThemeTestController::specificSuggestionAlter' + requirements: + _permission: 'access content' + +function_suggestion_alter: + path: '/theme-test/function-suggestion-alter' + defaults: + _content: '\Drupal\theme_test\ThemeTestController::functionSuggestionAlter' + requirements: + _permission: 'access content' diff --git a/core/modules/system/tests/themes/test_theme/templates/theme-test-specific-suggestions--variant--foo.html.twig b/core/modules/system/tests/themes/test_theme/templates/theme-test-specific-suggestions--variant--foo.html.twig new file mode 100644 index 000000000000..7e0b485ce227 --- /dev/null +++ b/core/modules/system/tests/themes/test_theme/templates/theme-test-specific-suggestions--variant--foo.html.twig @@ -0,0 +1,5 @@ +{# Output for Theme API test #} +Template overridden based on suggestion alter hook determined by the base hook. + +<p>Theme hook suggestions: +{{ theme_hook_suggestions|join("<br />") }}</p> diff --git a/core/modules/system/tests/themes/test_theme/templates/theme-test-specific-suggestions--variant.html.twig b/core/modules/system/tests/themes/test_theme/templates/theme-test-specific-suggestions--variant.html.twig new file mode 100644 index 000000000000..655db4e059d5 --- /dev/null +++ b/core/modules/system/tests/themes/test_theme/templates/theme-test-specific-suggestions--variant.html.twig @@ -0,0 +1,5 @@ +{# Output for Theme API test #} +Template matching the specific theme call. + +<p>Theme hook suggestions: +{{ theme_hook_suggestions|join("<br />") }}</p> diff --git a/core/modules/system/tests/themes/test_theme/templates/theme-test-suggestion-provided--foo.html.twig b/core/modules/system/tests/themes/test_theme/templates/theme-test-suggestion-provided--foo.html.twig new file mode 100644 index 000000000000..eec7992998b0 --- /dev/null +++ b/core/modules/system/tests/themes/test_theme/templates/theme-test-suggestion-provided--foo.html.twig @@ -0,0 +1,2 @@ +{# Output for Theme API test #} +Template overridden based on suggestion provided by the module declaring the theme hook. diff --git a/core/modules/system/tests/themes/test_theme/templates/theme-test-suggestions--module-override.html.twig b/core/modules/system/tests/themes/test_theme/templates/theme-test-suggestions--module-override.html.twig new file mode 100644 index 000000000000..26ce57bdae28 --- /dev/null +++ b/core/modules/system/tests/themes/test_theme/templates/theme-test-suggestions--module-override.html.twig @@ -0,0 +1,2 @@ +{# Output for Theme API test #} +Template overridden based on new theme suggestion provided by a module. diff --git a/core/modules/system/tests/themes/test_theme/templates/theme-test-suggestions--theme-override.html.twig b/core/modules/system/tests/themes/test_theme/templates/theme-test-suggestions--theme-override.html.twig new file mode 100644 index 000000000000..dee829f9750e --- /dev/null +++ b/core/modules/system/tests/themes/test_theme/templates/theme-test-suggestions--theme-override.html.twig @@ -0,0 +1,2 @@ +{# Output for Theme API test #} +Template overridden based on new theme suggestion provided by the test_theme theme. diff --git a/core/modules/system/tests/themes/test_theme/test_theme.theme b/core/modules/system/tests/themes/test_theme/test_theme.theme index 62b5abf15b41..9b10b2b03d23 100644 --- a/core/modules/system/tests/themes/test_theme/test_theme.theme +++ b/core/modules/system/tests/themes/test_theme/test_theme.theme @@ -29,3 +29,43 @@ function test_theme_theme_test__suggestion($variables) { function test_theme_theme_test_alter_alter(&$data) { $data = 'test_theme_theme_test_alter_alter was invoked'; } + +/** + * Implements hook_theme_suggestions_HOOK_alter(). + */ +function test_theme_theme_suggestions_theme_test_suggestions_alter(array &$suggestions, array $variables) { + // Theme alter hooks run after module alter hooks, so add this theme + // suggestion to the beginning of the array so that the suggestion added by + // the theme_suggestions_test module can be picked up when that module is + // enabled. + array_unshift($suggestions, 'theme_test_suggestions__' . 'theme_override'); +} + +/** + * Implements hook_theme_suggestions_HOOK_alter(). + */ +function test_theme_theme_suggestions_theme_test_function_suggestions_alter(array &$suggestions, array $variables) { + // Theme alter hooks run after module alter hooks, so add this theme + // suggestion to the beginning of the array so that the suggestion added by + // the theme_suggestions_test module can be picked up when that module is + // enabled. + array_unshift($suggestions, 'theme_test_function_suggestions__' . 'theme_override'); +} + +/** + * Returns HTML for a theme function suggestion test. + * + * Implements the theme_test_function_suggestions__theme_override suggestion. + */ +function test_theme_theme_test_function_suggestions__theme_override($variables) { + return 'Theme function overridden based on new theme suggestion provided by the test_theme theme.'; +} + +/** + * Returns HTML for a theme function suggestion test. + * + * Implements the theme_test_function_suggestions__module_override suggestion. + */ +function test_theme_theme_test_function_suggestions__module_override($variables) { + return 'Theme function overridden based on new theme suggestion provided by a module.'; +} diff --git a/core/modules/system/theme.api.php b/core/modules/system/theme.api.php index ebda67117d44..34758dafda12 100644 --- a/core/modules/system/theme.api.php +++ b/core/modules/system/theme.api.php @@ -157,6 +157,67 @@ function hook_preprocess_HOOK(&$variables) { $variables['attributes']['typeof'] = array('foaf:Image'); } +/** + * Provides alternate named suggestions for a specific theme hook. + * + * This hook allows the module implementing hook_theme() for a theme hook to + * provide alternative theme function or template name suggestions. This hook is + * only invoked for the first module implementing hook_theme() for a theme hook. + * + * HOOK is the least-specific version of the hook being called. For example, if + * '#theme' => 'node__article' is called, then node_theme_suggestions_node() + * will be invoked, not node_theme_suggestions_node__article(). The specific + * hook called (in this case 'node__article') is available in + * $variables['theme_hook_original']. + * + * @todo Add @code sample. + * + * @param array $variables + * An array of variables passed to the theme hook. Note that this hook is + * invoked before any preprocessing. + * + * @return array + * An array of theme suggestions. + * + * @see hook_theme_suggestions_HOOK_alter() + */ +function hook_theme_suggestions_HOOK(array $variables) { + $suggestions = array(); + + $suggestions[] = 'node__' . $variables['elements']['#langcode']; + + return $suggestions; +} + +/** + * Alters named suggestions for a specific theme hook. + * + * This hook allows any module or theme to provide altenative theme function or + * template name suggestions and reorder or remove suggestions provided by + * hook_theme_suggestions_HOOK() or by earlier invocations of this hook. + * + * HOOK is the least-specific version of the hook being called. For example, if + * '#theme' => 'node__article' is called, then node_theme_suggestions_node() + * will be invoked, not node_theme_suggestions_node__article(). The specific + * hook called (in this case 'node__article') is available in + * $variables['theme_hook_original']. + * + * @todo Add @code sample. + * + * @param array $suggestions + * An array of theme suggestions. + * @param array $variables + * An array of variables passed to the theme hook. Note that this hook is + * invoked before any preprocessing. + * + * @see hook_theme_suggestions_HOOK() + */ +function hook_theme_suggestions_HOOK_alter(array &$suggestions, array $variables) { + if (empty($variables['header'])) { + $suggestions[] = 'hookname__' . 'no_header'; + } +} + /** * Respond to themes being enabled. * diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module index 4f9cec1be3a5..62eb1281002e 100644 --- a/core/modules/taxonomy/taxonomy.module +++ b/core/modules/taxonomy/taxonomy.module @@ -406,6 +406,20 @@ function taxonomy_term_view_multiple(array $terms, $view_mode = 'full', $langcod return entity_view_multiple($terms, $view_mode, $langcode); } +/** + * Implements hook_theme_suggestions_HOOK(). + */ +function taxonomy_theme_suggestions_taxonomy_term(array $variables) { + $suggestions = array(); + + $term = $variables['elements']['#term']; + + $suggestions[] = 'taxonomy_term__' . $term->bundle(); + $suggestions[] = 'taxonomy_term__' . $term->id(); + + return $suggestions; +} + /** * Prepares variables for taxonomy term templates. * @@ -447,9 +461,6 @@ function template_preprocess_taxonomy_term(&$variables) { $variables['attributes']['class'][] = 'taxonomy-term'; $vocabulary_name_css = str_replace('_', '-', $term->bundle()); $variables['attributes']['class'][] = 'vocabulary-' . $vocabulary_name_css; - - $variables['theme_hook_suggestions'][] = 'taxonomy_term__' . $term->bundle(); - $variables['theme_hook_suggestions'][] = 'taxonomy_term__' . $term->id(); } /** diff --git a/core/modules/views/views.module b/core/modules/views/views.module index c238f4f77c56..7a6b346cd3f3 100644 --- a/core/modules/views/views.module +++ b/core/modules/views/views.module @@ -261,15 +261,13 @@ function views_preprocess_node(&$variables) { // \Drupal\views\Plugin\views\row\EntityRow::preRender(). if (!empty($variables['node']->view) && $variables['node']->view->storage->id()) { $variables['view'] = $variables['node']->view; - $variables['theme_hook_suggestions'][] = 'node__view__' . $variables['node']->view->storage->id(); - if (!empty($variables['node']->view->current_display)) { - $variables['theme_hook_suggestions'][] = 'node__view__' . $variables['node']->view->storage->id() . '__' . $variables['node']->view->current_display; - - // If a node is being rendered in a view, and the view does not have a path, - // prevent drupal from accidentally setting the $page variable: - if ($variables['page'] && $variables['view_mode'] == 'full' && !$variables['view']->display_handler->hasPath()) { - $variables['page'] = FALSE; - } + // If a node is being rendered in a view, and the view does not have a path, + // prevent drupal from accidentally setting the $page variable: + if (!empty($variables['view']->current_display) + && $variables['page'] + && $variables['view_mode'] == 'full' + && !$variables['view']->display_handler->hasPath()) { + $variables['page'] = FALSE; } } @@ -279,6 +277,19 @@ function views_preprocess_node(&$variables) { } } +/** + * Implements hook_theme_suggestions_HOOK_alter(). + */ +function views_theme_suggestions_node_alter(array &$suggestions, array $variables) { + $node = $variables['elements']['#node']; + if (!empty($node->view) && $node->view->storage->id()) { + $suggestions[] = 'node__view__' . $node->view->storage->id(); + if (!empty($node->view->current_display)) { + $suggestions[] = 'node__view__' . $node->view->storage->id() . '__' . $node->view->current_display; + } + } +} + /** * A theme preprocess function to automatically allow view-based node * templates if called from a view. @@ -288,9 +299,18 @@ function views_preprocess_comment(&$variables) { // \Drupal\views\Plugin\views\row\EntityRow::preRender(). if (!empty($variables['comment']->view) && $variables['comment']->view->storage->id()) { $variables['view'] = &$variables['comment']->view; - $variables['theme_hook_suggestions'][] = 'comment__view__' . $variables['comment']->view->storage->id(); - if (!empty($variables['node']->view->current_display)) { - $variables['theme_hook_suggestions'][] = 'comment__view__' . $variables['comment']->view->storage->id() . '__' . $variables['comment']->view->current_display; + } +} + +/** + * Implements hook_theme_suggestions_HOOK_alter(). + */ +function views_theme_suggestions_comment_alter(array &$suggestions, array $variables) { + $comment = $variables['elements']['#comment']; + if (!empty($comment->view) && $comment->view->storage->id()) { + $suggestions[] = 'comment__view__' . $comment->view->storage->id(); + if (!empty($comment->view->current_display)) { + $suggestions[] = 'comment__view__' . $comment->view->storage->id() . '__' . $comment->view->current_display; } } } diff --git a/core/modules/views_ui/views_ui.theme.inc b/core/modules/views_ui/views_ui.theme.inc index 8ae5733c4868..38a8fe4f52c4 100644 --- a/core/modules/views_ui/views_ui.theme.inc +++ b/core/modules/views_ui/views_ui.theme.inc @@ -483,5 +483,11 @@ function template_preprocess_views_ui_view_preview_section(&$variables) { ); $variables['links'] = $build; } - $variables['theme_hook_suggestions'][] = 'views_ui_view_preview_section__' . $variables['section']; +} + +/** + * Implements hook_theme_suggestions_HOOK(). + */ +function views_ui_theme_suggestions_views_ui_view_preview_section(array $variables) { + return array('views_ui_view_preview_section__' . $variables['section']); } -- GitLab