Commit 4fbb1a1b authored by webchick's avatar webchick

Issue #2160735 by Cottser, aspilicious: Add hook_theme_suggestions_alter().

parent cdd39859
......@@ -627,8 +627,14 @@ function theme($hook, $variables = array()) {
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);
// Invoke hook_theme_suggestions_alter() and
// hook_theme_suggestions_HOOK_alter().
$hooks = array(
'theme_suggestions',
'theme_suggestions_' . $base_theme_hook,
);
\Drupal::moduleHandler()->alter($hooks, $suggestions, $variables, $base_theme_hook);
// 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
......
......@@ -51,25 +51,46 @@ function testTemplateSuggestions() {
$this->assertText('Template overridden based on suggestion provided by the module declaring the theme hook.');
}
/**
* Tests hook_theme_suggestions_alter().
*/
function testGeneralSuggestionsAlter() {
$this->drupalGet('theme-test/general-suggestion-alter');
$this->assertText('Original template for testing hook_theme_suggestions_alter().');
// Enable test_theme and test that themes can alter template suggestions.
config('system.theme')
->set('default', 'test_theme')
->save();
$this->drupalGet('theme-test/general-suggestion-alter');
$this->assertText('Template overridden based on new theme suggestion provided by the test_theme theme via hook_theme_suggestions_alter().');
// Enable the theme_suggestions_test module to test modules implementing
// suggestions alter hooks.
\Drupal::moduleHandler()->install(array('theme_suggestions_test'));
$this->drupalGet('theme-test/general-suggestion-alter');
$this->assertText('Template overridden based on new theme suggestion provided by a module via hook_theme_suggestions_alter().');
}
/**
* Tests that theme suggestion alter hooks work for templates.
*/
function testTemplateSuggestionsAlter() {
$this->drupalGet('theme-test/suggestion-alter');
$this->assertText('Original template.');
$this->assertText('Original template for testing hook_theme_suggestions_HOOK_alter().');
// 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.');
$this->assertText('Template overridden based on new theme suggestion provided by the test_theme theme via hook_theme_suggestions_HOOK_alter().');
// 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.');
$this->assertText('Template overridden based on new theme suggestion provided by a module via hook_theme_suggestions_HOOK_alter().');
}
/**
......@@ -117,4 +138,34 @@ function testThemeFunctionSuggestionsAlter() {
$this->assertText('Theme function overridden based on new theme suggestion provided by a module.');
}
/**
* Tests execution order of theme suggestion alter hooks.
*
* hook_theme_suggestions_alter() should fire before
* hook_theme_suggestions_HOOK_alter() within an extension (module or theme).
*/
function testExecutionOrder() {
// Enable our test theme and module.
config('system.theme')
->set('default', 'test_theme')
->save();
\Drupal::moduleHandler()->install(array('theme_suggestions_test'));
// Send two requests so that we get all the messages we've set via
// drupal_set_message().
$this->drupalGet('theme-test/suggestion-alter');
// Ensure that the order is first by extension, then for a given extension,
// the hook-specific one after the generic one.
$expected = array(
'theme_suggestions_test_theme_suggestions_alter() executed.',
'theme_suggestions_test_theme_suggestions_theme_test_suggestions_alter() executed.',
'theme_test_theme_suggestions_alter() executed.',
'theme_test_theme_suggestions_theme_test_suggestions_alter() executed.',
'test_theme_theme_suggestions_alter() executed.',
'test_theme_theme_suggestions_theme_test_suggestions_alter() executed.',
);
$content = preg_replace('/\s+/', ' ', filter_xss($this->content, array()));
$this->assert(strpos($content, implode(' ', $expected)) !== FALSE, 'Suggestion alter hooks executed in the expected order.');
}
}
......@@ -5,10 +5,21 @@
* Support module for testing theme suggestions.
*/
/**
* Implements hook_theme_suggestions_alter().
*/
function theme_suggestions_test_theme_suggestions_alter(array &$suggestions, array $variables, $hook) {
drupal_set_message(__FUNCTION__ . '() executed.');
if ($hook == 'theme_test_general_suggestions') {
$suggestions[] = $hook . '__module_override';
}
}
/**
* Implements hook_theme_suggestions_HOOK_alter().
*/
function theme_suggestions_test_theme_suggestions_theme_test_suggestions_alter(array &$suggestions, array $variables) {
drupal_set_message(__FUNCTION__ . '() executed.');
$suggestions[] = 'theme_test_suggestions__' . 'module_override';
}
......
......@@ -106,6 +106,13 @@ function suggestionAlter() {
return array('#theme' => 'theme_test_suggestions');
}
/**
* Menu callback for testing hook_theme_suggestions_alter().
*/
function generalSuggestionAlter() {
return array('#theme' => 'theme_test_general_suggestions');
}
/**
* Menu callback for testing suggestion alter hooks with specific suggestions.
*/
......
{# Output for Theme API test #}
Original template for testing hook_theme_suggestions_alter().
{# Output for Theme API test #}
Original template.
Original template for testing hook_theme_suggestions_HOOK_alter().
......@@ -26,6 +26,10 @@ function theme_test_theme($existing, $type, $theme, $path) {
'template' => 'theme-test-suggestions',
'variables' => array(),
);
$items['theme_test_general_suggestions'] = array(
'template' => 'theme-test-general-suggestions',
'variables' => array(),
);
$items['theme_test_function_suggestions'] = array(
'variables' => array(),
);
......@@ -152,3 +156,17 @@ function theme_theme_test_function_suggestions($variables) {
function theme_test_theme_suggestions_theme_test_suggestion_provided(array $variables) {
return array('theme_test_suggestion_provided__' . 'foo');
}
/**
* Implements hook_theme_suggestions_alter().
*/
function theme_test_theme_suggestions_alter(array &$suggestions, array $variables, $hook) {
drupal_set_message(__FUNCTION__ . '() executed.');
}
/**
* Implements hook_theme_suggestions_HOOK_alter().
*/
function theme_test_theme_suggestions_theme_test_suggestions_alter(array &$suggestions, array $variables) {
drupal_set_message(__FUNCTION__ . '() executed.');
}
......@@ -65,6 +65,13 @@ suggestion_alter:
requirements:
_permission: 'access content'
theme_test.general_suggestion_alter:
path: '/theme-test/general-suggestion-alter'
defaults:
_content: '\Drupal\theme_test\ThemeTestController::generalSuggestionAlter'
requirements:
_permission: 'access content'
suggestion_provided:
path: '/theme-test/suggestion-provided'
defaults:
......
{# Output for Theme API test #}
Template overridden based on new theme suggestion provided by a module via hook_theme_suggestions_alter().
{# Output for Theme API test #}
Template overridden based on new theme suggestion provided by the test_theme theme via hook_theme_suggestions_alter().
{# Output for Theme API test #}
Template overridden based on new theme suggestion provided by a module.
Template overridden based on new theme suggestion provided by a module via hook_theme_suggestions_HOOK_alter().
{# Output for Theme API test #}
Template overridden based on new theme suggestion provided by the test_theme theme.
Template overridden based on new theme suggestion provided by the test_theme theme via hook_theme_suggestions_HOOK_alter().
......@@ -30,10 +30,25 @@ function test_theme_theme_test_alter_alter(&$data) {
$data = 'test_theme_theme_test_alter_alter was invoked';
}
/**
* Implements hook_theme_suggestions_alter().
*/
function test_theme_theme_suggestions_alter(array &$suggestions, array $variables, $hook) {
drupal_set_message(__FUNCTION__ . '() executed.');
// 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.
if ($hook == 'theme_test_general_suggestions') {
array_unshift($suggestions, 'theme_test_general_suggestions__' . 'theme_override');
}
}
/**
* Implements hook_theme_suggestions_HOOK_alter().
*/
function test_theme_theme_suggestions_theme_test_suggestions_alter(array &$suggestions, array $variables) {
drupal_set_message(__FUNCTION__ . '() executed.');
// 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
......
......@@ -189,6 +189,55 @@ function hook_theme_suggestions_HOOK(array $variables) {
return $suggestions;
}
/**
* Alters named suggestions for all theme hooks.
*
* This hook is invoked for all theme hooks, if you are targeting a specific
* theme hook it's best to use hook_theme_suggestions_HOOK_alter().
*
* The call order is as follows: all existing suggestion alter functions are
* called for module A, then all for module B, etc., followed by all for any
* base theme(s), and finally for the active theme. The order is
* determined by system weight, then by extension (module or theme) name.
*
* Within each module or theme, suggestion alter hooks are called in the
* following order: first, hook_theme_suggestions_alter(); second,
* hook_theme_suggestions_HOOK_alter(). So, for each module or theme, the more
* general hooks are called first followed by the more specific.
*
* In the following example, we provide an alternative template suggestion to
* node and taxonomy term templates based on the user being logged in.
* @code
* function MYMODULE_theme_suggestions_alter(array &$suggestions, array $variables, $hook) {
* if (\Drupal::currentUser()->isAuthenticated() && in_array($hook, array('node', 'taxonomy_term'))) {
* $suggestions[] = $hook . '__' . 'logged_in';
* }
* }
*
* @endcode
*
* @param array $suggestions
* An array of alternate, more specific names for template files or theme
* functions.
* @param array $variables
* An array of variables passed to the theme hook. Note that this hook is
* invoked before any variable preprocessing.
* @param string $hook
* The base hook name. For example, if '#theme' => 'node__article' is called,
* then $hook will be 'node', not 'node__article'. The specific hook called
* (in this case 'node__article') is available in
* $variables['theme_hook_original'].
*
* @return array
* An array of theme suggestions.
*
* @see hook_theme_suggestions_HOOK_alter()
*/
function hook_theme_suggestions_alter(array &$suggestions, array $variables, $hook) {
// Add an interface-language specific suggestion to all theme hooks.
$suggestions[] = $hook . '__' . \Drupal::languageManager()->getLanguage()->id;
}
/**
* Alters named suggestions for a specific theme hook.
*
......@@ -210,6 +259,7 @@ function hook_theme_suggestions_HOOK(array $variables) {
* An array of variables passed to the theme hook. Note that this hook is
* invoked before any preprocessing.
*
* @see hook_theme_suggestions_alter()
* @see hook_theme_suggestions_HOOK()
*/
function hook_theme_suggestions_HOOK_alter(array &$suggestions, array $variables) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment