diff --git a/core/includes/theme.inc b/core/includes/theme.inc index 504030e..d27dc05 100644 --- a/core/includes/theme.inc +++ b/core/includes/theme.inc @@ -399,6 +399,7 @@ function _theme_process_registry(&$cache, $name, $type, $theme, $path) { // Processor functions work in two distinct phases with the process // functions always being executed after the preprocess functions. $variable_process_phases = array( + 'template suggestion functions' => 'template_suggestions', 'preprocess functions' => 'preprocess', 'process functions' => 'process', ); @@ -831,18 +832,6 @@ function drupal_find_base_themes($themes, $key, $used_keys = array()) { * need to be fast, and calling the non-theme-hook-specific preprocess and * process functions for them would incur a noticeable performance penalty. * - * There are two special variables that these preprocess and process 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 or process function. - * * @param $hook * The name of the theme hook to call. If the name contains a * double-underscore ('__') and there isn't an implementation for the full @@ -957,6 +946,28 @@ function theme($hook, $variables = array()) { $variables += array($info['render element'] => array()); } + // Invoke template suggestion hooks. + $suggestions = array(); + if (!empty($info['template suggestion functions'])) { + foreach ($info['template suggestion functions'] as $funcname) { + $suggestions += $funcname($variables); + } + } + + // Check if each suggestion exists in the theme registry, and if so, + // use it instead of the hook that theme() was called with. This + // allows the preprocess/process step to route to a more specific + // theme hook. For example, a function may call theme('node', ...), + // but a a module can add 'node__article' as a suggestion via + // hook_template_suggestions_HOOK, 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 processors, if any. The processors 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 processors of @@ -973,13 +984,11 @@ function theme($hook, $variables = array()) { } } if (isset($base_hook_info['preprocess functions']) || isset($base_hook_info['process functions'])) { - $variables['theme_hook_suggestion'] = $hook; $hook = $base_hook; $info = $base_hook_info; } } if (isset($info['preprocess functions']) || isset($info['process functions'])) { - $variables['theme_hook_suggestions'] = array(); foreach (array('preprocess functions', 'process functions') as $phase) { if (!empty($info[$phase])) { foreach ($info[$phase] as $processor_function) { @@ -991,31 +1000,6 @@ function theme($hook, $variables = array()) { } } } - // If the preprocess/process 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/process 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. @@ -2495,11 +2479,6 @@ function template_preprocess_html(&$variables) { foreach ($elements as $name => $element) { drupal_add_html_head($element, $name); } - - // Populate the page template suggestions. - if ($suggestions = theme_get_suggestions(arg(), 'html')) { - $variables['theme_hook_suggestions'] = $suggestions; - } } /** @@ -2557,11 +2536,6 @@ function template_preprocess_page(&$variables) { if ($node = menu_get_object()) { $variables['node'] = $node; } - - // Populate the page template suggestions. - if ($suggestions = theme_get_suggestions(arg(), 'page')) { - $variables['theme_hook_suggestions'] = $suggestions; - } } /** @@ -2619,8 +2593,8 @@ function template_process_html(&$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 + * This is typically called for adding to the 'hook_template_suggestions_HOOK' + * or 'attributes' class key variables from within preprocess functions, when * wanting to base the additional suggestions on the path of the current page. * * @param $args @@ -2635,7 +2609,7 @@ function template_process_html(&$variables) { * * @return * An array of suggestions, suitable for adding to - * $variables['theme_hook_suggestions'] within a preprocess function or to + * hook_template_suggestions_HOOK or to * $variables['attributes']['class'] if the suggestions represent extra CSS classes. */ function theme_get_suggestions($args, $base, $delimiter = '__') { @@ -2780,12 +2754,6 @@ function template_preprocess_maintenance_page(&$variables) { $variables['attributes']['class'][] = 'one-sidebar'; $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'; - } } /** @@ -2817,5 +2785,4 @@ function template_preprocess_region(&$variables) { $variables['region'] = $variables['elements']['#region']; $variables['attributes']['class'][] = drupal_region_class($variables['region']); - $variables['theme_hook_suggestions'][] = 'region__' . $variables['region']; } diff --git a/core/modules/block/block.module b/core/modules/block/block.module index 2592137..71e7f31 100644 --- a/core/modules/block/block.module +++ b/core/modules/block/block.module @@ -968,6 +968,28 @@ function block_rebuild() { } /** + * Implements hook_template_suggestions_HOOK(). + */ +function block_template_suggestions_block($variables) { + $suggestions = array(); + $block = $variables['elements']['#block']; + + $suggestions[] = 'block__' . $block->region; + $suggestions[] = 'block__' . $block->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. + $suggestions[] = 'block__' . $block->module . '__' . strtr($block->delta, '-', '_'); + + return $suggestions; +} + +/** * Processes variables for block.tpl.php. * * Prepares the values passed to the theme_block function to be passed @@ -1003,18 +1025,6 @@ function template_preprocess_block(&$variables) { // Add default class for block content. $variables['content_attributes']['class'][] = 'content'; - $variables['theme_hook_suggestions'][] = 'block__' . $variables['block']->region; - $variables['theme_hook_suggestions'][] = 'block__' . $variables['block']->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. - $variables['theme_hook_suggestions'][] = 'block__' . $variables['block']->module . '__' . strtr($variables['block']->delta, '-', '_'); - // Create a valid HTML ID and make sure it is unique. $variables['block_html_id'] = drupal_html_id('block-' . $variables['block']->module . '-' . $variables['block']->delta); } diff --git a/core/modules/field/field.module b/core/modules/field/field.module index 8157971..af05631 100644 --- a/core/modules/field/field.module +++ b/core/modules/field/field.module @@ -1042,6 +1042,21 @@ function field_extract_bundle($entity_type, $bundle) { } /** + * Implements hook_template_suggestions_HOOK(). + */ +function field_template_suggestions_field($variables) { + $element = $variables['element']; + + // Add specific suggestions that can override the default implementation. + return array( + 'field__' . $element['#field_type'], + 'field__' . $element['#field_name'], + 'field__' . $element['#bundle'], + 'field__' . $element['#field_name'] . '__' . $element['#bundle'], + ); +} + +/** * Theme preprocess function for theme_field() and field.tpl.php. * * @see theme_field() @@ -1087,14 +1102,6 @@ function template_preprocess_field(&$variables, $hook) { if ($element['#label_display'] == 'inline') { $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['#bundle'], - 'field__' . $element['#field_name'] . '__' . $element['#bundle'], - ); } /** diff --git a/core/modules/forum/forum.module b/core/modules/forum/forum.module index 30b1e6b..e1fed21 100644 --- a/core/modules/forum/forum.module +++ b/core/modules/forum/forum.module @@ -1036,6 +1036,31 @@ function forum_get_topics($tid, $sortby, $forum_per_page) { } /** + * Implements hook_template_suggestions_HOOK(). + */ +function forum_template_suggestions_forums($variables) { + $suggestions = 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']) { + $suggestions[] = 'forums__containers'; + $suggestions[] = 'forums__' . $variables['tid']; + $suggestions[] = 'forums__containers__' . $variables['tid']; + } + elseif (!$variables['forums'] && $variables['topics']) { + $suggestions[] = 'forums__topics'; + $suggestions[] = 'forums__' . $variables['tid']; + $suggestions[] = 'forums__topics__' . $variables['tid']; + } + else { + $suggestions[] = 'forums__' . $variables['tid']; + } + + return $suggestions; +} + +/** * Implements hook_preprocess_HOOK() for block.tpl.php. */ function forum_preprocess_block(&$variables) { @@ -1106,23 +1131,6 @@ function template_preprocess_forums(&$variables) { else { $variables['topics'] = ''; } - - // 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 { drupal_set_title(t('No forums defined')); diff --git a/core/modules/node/node.module b/core/modules/node/node.module index 349aab5..0ee758f 100644 --- a/core/modules/node/node.module +++ b/core/modules/node/node.module @@ -281,6 +281,17 @@ function node_field_display_node_alter(&$display, $context) { } /** + * Implements hook_template_suggestions_HOOK(). + */ +function node_template_suggestions_node($variables) { + return array( + 'node__' . $variables['nid'], + 'node__' . $variables['type'] . '__' . $variables['langcode'], + 'node__' . $variables['type'], + ); +} + +/** * Entity uri callback. * * @param Drupal\node\Node $node @@ -1383,10 +1394,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->type; - $variables['theme_hook_suggestions'][] = 'node__' . $node->nid; } /** diff --git a/core/modules/poll/poll.module b/core/modules/poll/poll.module index 4ab2cb8..5e5eec4 100644 --- a/core/modules/poll/poll.module +++ b/core/modules/poll/poll.module @@ -776,6 +776,20 @@ function poll_vote($form, &$form_state) { } /** + * Implements hook_template_suggestions_HOOK(). + */ +function poll_template_suggestions_poll_vote($variables) { + $suggestions = array(); + + // When rendering as a block, suggest a separate template. + if ($variables['form']['#block']) { + $suggestions[] = 'poll_vote__block'; + } + + return $suggestions; +} + +/** * Implements hook_preprocess_HOOK() for block.tpl.php. */ function poll_preprocess_block(&$variables) { @@ -796,9 +810,6 @@ function template_preprocess_poll_vote(&$variables) { $variables['vote'] = drupal_render($form['vote']); $variables['rest'] = drupal_render_children($form); $variables['block'] = $form['#block']; - if ($variables['block']) { - $variables['theme_hook_suggestions'][] = 'poll_vote__block'; - } } /** diff --git a/core/modules/search/search.module b/core/modules/search/search.module index 7613465..2fb7c84 100644 --- a/core/modules/search/search.module +++ b/core/modules/search/search.module @@ -156,6 +156,20 @@ function search_block_view($delta = '') { } /** + * Implements hook_template_suggestions_HOOK(). + */ +function search_template_suggestions_search_result($variables) { + return array('search_result__' . $variables['module']); +} + +/** + * Implements hook_template_suggestions_HOOK(). + */ +function search_template_suggestions_search_results($variables) { + return array('search_results__' . $variables['module']); +} + +/** * Implements hook_preprocess_HOOK() for block.tpl.php. */ function search_preprocess_block(&$variables) { diff --git a/core/modules/search/search.pages.inc b/core/modules/search/search.pages.inc index 337fe85..ae5ca73 100644 --- a/core/modules/search/search.pages.inc +++ b/core/modules/search/search.pages.inc @@ -91,7 +91,6 @@ function template_preprocess_search_results(&$variables) { $variables['search_results'] .= theme('search_result', array('result' => $result, 'module' => $variables['module'])); } $variables['pager'] = theme('pager', array('tags' => NULL)); - $variables['theme_hook_suggestions'][] = 'search_results__' . $variables['module']; } /** @@ -132,7 +131,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['module']; } /** diff --git a/core/modules/system/system.module b/core/modules/system/system.module index de7241f..6982556 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -1162,6 +1162,38 @@ function system_menu() { } /** + * Implements hook_template_suggestions_HOOK(). + */ +function system_template_suggestions_html($variables) { + return theme_get_suggestions(arg(), 'html'); +} + +/** + * Implements hook_template_suggestions_HOOK(). + */ +function system_template_suggestions_page($variables) { + return theme_get_suggestions(arg(), 'page'); +} + +/** + * Implements hook_template_suggestions_HOOK(). + */ +function system_template_suggestions_maintenance_page($variables) { + // 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']) { + return array('maintenance_page__offline'); + } +} + +/** + * Implements hook_template_suggestions_HOOK(). + */ +function system_template_suggestions_region($variables) { + return array('region__' . $variables['region']); +} + +/** * Page callback; Execute cron tasks. * * @see system_cron_access(). diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module index a772e02..3b01155 100644 --- a/core/modules/taxonomy/taxonomy.module +++ b/core/modules/taxonomy/taxonomy.module @@ -622,6 +622,19 @@ function taxonomy_term_view(Term $term, $view_mode = 'full', $langcode = NULL) { } /** + * Implements hook_template_suggestions_HOOK(). + */ +function taxonomy_template_suggestions_taxonomy_term($variables) { + $suggestions = array(); + $term = $variables['term']; + + $suggestions[] = 'taxonomy_term__' . $term->vocabulary_machine_name; + $suggestions[] = 'taxonomy_term__' . $term->tid; + + return $suggestions; +} + +/** * Process variables for taxonomy-term.tpl.php. */ function template_preprocess_taxonomy_term(&$variables) { @@ -652,9 +665,6 @@ function template_preprocess_taxonomy_term(&$variables) { // Gather classes, and clean up name so there are no underscores. $vocabulary_name_css = str_replace('_', '-', $term->vocabulary_machine_name); $variables['attributes']['class'][] = 'vocabulary-' . $vocabulary_name_css; - - $variables['theme_hook_suggestions'][] = 'taxonomy_term__' . $term->vocabulary_machine_name; - $variables['theme_hook_suggestions'][] = 'taxonomy_term__' . $term->tid; } /**