Skip to content
Snippets Groups Projects
Unverified Commit 0ac4b763 authored by Lauri Timmanee's avatar Lauri Timmanee
Browse files

Issue #3343198 by andy-blum, murilohp, smustgrave, safetypin: Improve...

Issue #3343198 by andy-blum, murilohp, smustgrave, safetypin: Improve documentation of hook_theme_suggestions_HOOK_alter()
parent 22a39eb4
Branches
Tags
29 merge requests!12227Issue #3181946 by jonmcl, mglaman,!54479.5.x SF update,!5014Issue #3071143: Table Render Array Example Is Incorrect,!4868Issue #1428520: Improve menu parent link selection,!4289Issue #1344552 by marcingy, Niklas Fiekas, Ravi.J, aleevas, Eduardo Morales...,!4114Issue #2707291: Disable body-level scrolling when a dialog is open as a modal,!4100Issue #3249600: Add support for PHP 8.1 Enums as allowed values for list_* data types,!3630Issue #2815301 by Chi, DanielVeza, kostyashupenko, smustgrave: Allow to create...,!3600Issue #3344629: Passing null to parameter #1 ($haystack) of type string is deprecated,!2378Issue #2875033: Optimize joins and table selection in SQL entity query implementation,!2334Issue #3228209: Add hasRole() method to AccountInterface,!2062Issue #3246454: Add weekly granularity to views date sort,!1591Issue #3199697: Add JSON:API Translation experimental module,!1484Exposed filters get values from URL when Ajax is on,!1255Issue #3238922: Refactor (if feasible) uses of the jQuery serialize function to use vanillaJS,!1162Issue #3100350: Unable to save '/' root path alias,!1105Issue #3025039: New non translatable field on translatable content throws error,!1073issue #3191727: Focus states on mobile second level navigation items fixed,!10223132456: Fix issue where views instances are emptied before an ajax request is complete,!925Issue #2339235: Remove taxonomy hard dependency on node module,!877Issue #2708101: Default value for link text is not saved,!872Draft: Issue #3221319: Race condition when creating menu links and editing content deletes menu links,!844Resolve #3036010 "Updaters",!617Issue #3043725: Provide a Entity Handler for user cancelation,!579Issue #2230909: Simple decimals fail to pass validation,!560Move callback classRemove outside of the loop,!555Issue #3202493,!485Sets the autocomplete attribute for username/password input field on login form.,!30Issue #3182188: Updates composer usage to point at ./vendor/bin/composer
......@@ -645,6 +645,8 @@ function hook_theme_suggestions_HOOK(array $variables) {
* hook_theme_suggestions_HOOK_alter(). So, for each module or theme, the more
* general hooks are called first followed by the more specific.
*
* New suggestions must begin with the value of HOOK, followed by two underscores to be discoverable.
*
* In the following example, we provide an alternative template suggestion to
* node and taxonomy term templates based on the user being logged in.
* @code
......@@ -690,11 +692,27 @@ function hook_theme_suggestions_alter(array &$suggestions, array $variables, $ho
* hook called (in this case 'node__article') is available in
* $variables['theme_hook_original'].
*
* New suggestions must begin with the value of HOOK, followed by two underscores to be discoverable.
* For example, consider the below suggestions from hook_theme_suggestions_node_alter:
* - node__article is valid
* - node__article__custom_template is valid
* - node--article is invalid
* - article__custom_template is invalid
*
* Implementations of this hook must be placed in *.module or *.theme files, or
* must otherwise make sure that the hook implementation is available at
* any given time.
*
* @todo Add @code sample.
* In the following example, we provide an alternative template suggestion to
* node templates based on the user being logged in.
* @code
* function MYMODULE_theme_suggestions_node_alter(array &$suggestions, array $variables) {
* if (\Drupal::currentUser()->isAuthenticated()) {
* $suggestions[] = 'node__logged_in';
* }
* }
*
* @endcode
*
* @param array $suggestions
* An array of theme suggestions.
......
......@@ -55,6 +55,7 @@ public function testTwigDebugMarkup() {
$this->assertStringContainsString("THEME HOOK: 'node'", $output, 'Theme call information found.');
$this->assertStringContainsString('* node--1--full' . $extension . PHP_EOL . ' x node--1' . $extension . PHP_EOL . ' * node--page--full' . $extension . PHP_EOL . ' * node--page' . $extension . PHP_EOL . ' * node--full' . $extension . PHP_EOL . ' * node' . $extension, $output, 'Suggested template files found in order and node ID specific template shown as current template.');
$this->assertStringContainsString(Html::escape('node--<script type="text/javascript">alert(\'yo\');</script>'), (string) $output);
$this->assertStringContainsString('<!-- INVALID FILE NAME SUGGESTIONS:' . PHP_EOL . ' See https://api.drupal.org/api/drupal/core!lib!Drupal!Core!Render!theme.api.php/function/hook_theme_suggestions_alter' . PHP_EOL . ' invalid_theme_suggestions' . PHP_EOL . '-->', $output, 'Twig debug markup found invalid suggestions.');
$template_filename = $templates['node__1']['path'] . '/' . $templates['node__1']['template'] . $extension;
$this->assertStringContainsString("BEGIN OUTPUT from '$template_filename'", $output, 'Full path to current template file found.');
......
......@@ -68,6 +68,15 @@ function test_theme_theme_suggestions_theme_test_suggestions_alter(array &$sugge
array_unshift($suggestions, 'theme_test_suggestions__' . 'theme_override');
}
/**
* Implements hook_theme_suggestions_HOOK_alter().
*/
function test_theme_theme_suggestions_node_alter(array &$suggestions, array $variables) {
// Add an invalid suggestion to be tested.
$suggestions[] = 'invalid_theme_suggestions';
\Drupal::messenger()->addStatus(__FUNCTION__ . '() executed.');
}
/**
* Implements hook_theme_registry_alter().
*/
......
......@@ -93,12 +93,27 @@ function twig_render_template($template_file, array $variables) {
if (strpos($variables['theme_hook_original'], '__') === FALSE) {
$suggestions[] = $variables['theme_hook_original'];
}
foreach ($suggestions as &$suggestion) {
$invalid_suggestions = [];
$base_hook = $base_hook ?? $variables['theme_hook_original'];
foreach ($suggestions as $key => &$suggestion) {
// Valid suggestions are $base_hook, $base_hook__*, and contain no hyphens.
if (($suggestion !== $base_hook && !str_starts_with($suggestion, $base_hook . '__')) || str_contains($suggestion, '-')) {
$invalid_suggestions[] = $suggestion;
unset($suggestions[$key]);
continue;
}
$template = strtr($suggestion, '_', '-') . $extension;
$prefix = ($template == $current_template) ? 'x' : '*';
$suggestion = $prefix . ' ' . $template;
}
$output['debug_info'] .= "\n<!-- FILE NAME SUGGESTIONS:\n " . Html::escape(implode("\n ", $suggestions)) . "\n-->";
if (!empty($invalid_suggestions)) {
$output['debug_info'] .= "\n<!-- INVALID FILE NAME SUGGESTIONS:";
$output['debug_info'] .= "\n See https://api.drupal.org/api/drupal/core!lib!Drupal!Core!Render!theme.api.php/function/hook_theme_suggestions_alter";
$output['debug_info'] .= "\n " . Html::escape(implode("\n ", $invalid_suggestions));
$output['debug_info'] .= "\n-->";
}
}
$output['debug_info'] .= "\n<!-- BEGIN OUTPUT from '" . Html::escape($template_file) . "' -->\n";
$output['debug_suffix'] .= "\n<!-- END OUTPUT from '" . Html::escape($template_file) . "' -->\n\n";
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment