Chris@0
|
1 <?php
|
Chris@0
|
2
|
Chris@0
|
3 /**
|
Chris@0
|
4 * @file
|
Chris@0
|
5 * Handles integration of Twig templates with the Drupal theme system.
|
Chris@0
|
6 */
|
Chris@0
|
7
|
Chris@0
|
8 use Drupal\Component\Utility\Html;
|
Chris@0
|
9 use Drupal\Core\Render\Markup;
|
Chris@0
|
10 use Drupal\Core\Extension\Extension;
|
Chris@0
|
11
|
Chris@0
|
12 /**
|
Chris@0
|
13 * Implements hook_theme().
|
Chris@0
|
14 */
|
Chris@0
|
15 function twig_theme($existing, $type, $theme, $path) {
|
Chris@0
|
16 $templates = drupal_find_theme_functions($existing, [$theme]);
|
Chris@0
|
17 $templates += drupal_find_theme_templates($existing, '.html.twig', $path);
|
Chris@0
|
18 return $templates;
|
Chris@0
|
19 }
|
Chris@0
|
20
|
Chris@0
|
21 /**
|
Chris@0
|
22 * Implements hook_extension().
|
Chris@0
|
23 */
|
Chris@0
|
24 function twig_extension() {
|
Chris@0
|
25 return '.html.twig';
|
Chris@0
|
26 }
|
Chris@0
|
27
|
Chris@0
|
28 /**
|
Chris@0
|
29 * Includes .theme file from themes.
|
Chris@0
|
30 *
|
Chris@0
|
31 * @param \Drupal\Core\Extension\Extension $theme
|
Chris@0
|
32 * The theme extension object.
|
Chris@0
|
33 */
|
Chris@0
|
34 function twig_init(Extension $theme) {
|
Chris@0
|
35 $theme->load();
|
Chris@0
|
36 }
|
Chris@0
|
37
|
Chris@0
|
38 /**
|
Chris@0
|
39 * Implements hook_render_template().
|
Chris@0
|
40 *
|
Chris@0
|
41 * Renders a Twig template.
|
Chris@0
|
42 *
|
Chris@0
|
43 * If the Twig debug setting is enabled, HTML comments including the theme hook
|
Chris@0
|
44 * and template file name suggestions will surround the template markup.
|
Chris@0
|
45 *
|
Chris@0
|
46 * @param string $template_file
|
Chris@0
|
47 * The file name of the template to render.
|
Chris@0
|
48 * @param array $variables
|
Chris@0
|
49 * A keyed array of variables that will appear in the output.
|
Chris@0
|
50 *
|
Chris@0
|
51 * @return string|\Drupal\Component\Render\MarkupInterface
|
Chris@0
|
52 * The output generated by the template, plus any debug information.
|
Chris@0
|
53 */
|
Chris@0
|
54 function twig_render_template($template_file, array $variables) {
|
Chris@0
|
55 /** @var \Twig_Environment $twig_service */
|
Chris@0
|
56 $twig_service = \Drupal::service('twig');
|
Chris@0
|
57 $output = [
|
Chris@0
|
58 'debug_prefix' => '',
|
Chris@0
|
59 'debug_info' => '',
|
Chris@0
|
60 'rendered_markup' => '',
|
Chris@0
|
61 'debug_suffix' => '',
|
Chris@0
|
62 ];
|
Chris@0
|
63 try {
|
Chris@0
|
64 $output['rendered_markup'] = $twig_service->loadTemplate($template_file)->render($variables);
|
Chris@0
|
65 }
|
Chris@0
|
66 catch (\Twig_Error_Runtime $e) {
|
Chris@0
|
67 // In case there is a previous exception, re-throw the previous exception,
|
Chris@0
|
68 // so that the original exception is shown, rather than
|
Chris@0
|
69 // \Twig_Template::displayWithErrorHandling()'s exception.
|
Chris@0
|
70 $previous_exception = $e->getPrevious();
|
Chris@0
|
71 if ($previous_exception) {
|
Chris@0
|
72 throw $previous_exception;
|
Chris@0
|
73 }
|
Chris@0
|
74 throw $e;
|
Chris@0
|
75 }
|
Chris@0
|
76 if ($twig_service->isDebug()) {
|
Chris@0
|
77 $output['debug_prefix'] .= "\n\n<!-- THEME DEBUG -->";
|
Chris@0
|
78 $output['debug_prefix'] .= "\n<!-- THEME HOOK: '" . Html::escape($variables['theme_hook_original']) . "' -->";
|
Chris@0
|
79 // If there are theme suggestions, reverse the array so more specific
|
Chris@0
|
80 // suggestions are shown first.
|
Chris@0
|
81 if (!empty($variables['theme_hook_suggestions'])) {
|
Chris@0
|
82 $variables['theme_hook_suggestions'] = array_reverse($variables['theme_hook_suggestions']);
|
Chris@0
|
83 }
|
Chris@0
|
84 // Add debug output for directly called suggestions like
|
Chris@0
|
85 // '#theme' => 'comment__node__article'.
|
Chris@0
|
86 if (strpos($variables['theme_hook_original'], '__') !== FALSE) {
|
Chris@0
|
87 $derived_suggestions[] = $hook = $variables['theme_hook_original'];
|
Chris@0
|
88 while ($pos = strrpos($hook, '__')) {
|
Chris@0
|
89 $hook = substr($hook, 0, $pos);
|
Chris@0
|
90 $derived_suggestions[] = $hook;
|
Chris@0
|
91 }
|
Chris@0
|
92 // Get the value of the base hook (last derived suggestion) and append it
|
Chris@0
|
93 // to the end of all theme suggestions.
|
Chris@0
|
94 $base_hook = array_pop($derived_suggestions);
|
Chris@0
|
95 $variables['theme_hook_suggestions'] = array_merge($derived_suggestions, $variables['theme_hook_suggestions']);
|
Chris@0
|
96 $variables['theme_hook_suggestions'][] = $base_hook;
|
Chris@0
|
97 }
|
Chris@0
|
98 if (!empty($variables['theme_hook_suggestions'])) {
|
Chris@0
|
99 $extension = twig_extension();
|
Chris@0
|
100 $current_template = basename($template_file);
|
Chris@0
|
101 $suggestions = $variables['theme_hook_suggestions'];
|
Chris@0
|
102 // Only add the original theme hook if it wasn't a directly called
|
Chris@0
|
103 // suggestion.
|
Chris@0
|
104 if (strpos($variables['theme_hook_original'], '__') === FALSE) {
|
Chris@0
|
105 $suggestions[] = $variables['theme_hook_original'];
|
Chris@0
|
106 }
|
Chris@0
|
107 foreach ($suggestions as &$suggestion) {
|
Chris@0
|
108 $template = strtr($suggestion, '_', '-') . $extension;
|
Chris@0
|
109 $prefix = ($template == $current_template) ? 'x' : '*';
|
Chris@0
|
110 $suggestion = $prefix . ' ' . $template;
|
Chris@0
|
111 }
|
Chris@0
|
112 $output['debug_info'] .= "\n<!-- FILE NAME SUGGESTIONS:\n " . Html::escape(implode("\n ", $suggestions)) . "\n-->";
|
Chris@0
|
113 }
|
Chris@0
|
114 $output['debug_info'] .= "\n<!-- BEGIN OUTPUT from '" . Html::escape($template_file) . "' -->\n";
|
Chris@0
|
115 $output['debug_suffix'] .= "\n<!-- END OUTPUT from '" . Html::escape($template_file) . "' -->\n\n";
|
Chris@0
|
116 }
|
Chris@0
|
117 // This output has already been rendered and is therefore considered safe.
|
Chris@0
|
118 return Markup::create(implode('', $output));
|
Chris@0
|
119 }
|
Chris@0
|
120
|
Chris@0
|
121 /**
|
Chris@0
|
122 * Removes child elements from a copy of the original array.
|
Chris@0
|
123 *
|
Chris@0
|
124 * Creates a copy of the renderable array and removes child elements by key
|
Chris@0
|
125 * specified through filter's arguments. The copy can be printed without these
|
Chris@0
|
126 * elements. The original renderable array is still available and can be used
|
Chris@0
|
127 * to print child elements in their entirety in the twig template.
|
Chris@0
|
128 *
|
Chris@0
|
129 * @param array|object $element
|
Chris@0
|
130 * The parent renderable array to exclude the child items.
|
Chris@0
|
131 * @param string[] ...
|
Chris@0
|
132 * The string keys of $element to prevent printing.
|
Chris@0
|
133 *
|
Chris@0
|
134 * @return array
|
Chris@0
|
135 * The filtered renderable array.
|
Chris@0
|
136 */
|
Chris@0
|
137 function twig_without($element) {
|
Chris@0
|
138 if ($element instanceof ArrayAccess) {
|
Chris@0
|
139 $filtered_element = clone $element;
|
Chris@0
|
140 }
|
Chris@0
|
141 else {
|
Chris@0
|
142 $filtered_element = $element;
|
Chris@0
|
143 }
|
Chris@0
|
144 $args = func_get_args();
|
Chris@0
|
145 unset($args[0]);
|
Chris@0
|
146 foreach ($args as $arg) {
|
Chris@0
|
147 if (isset($filtered_element[$arg])) {
|
Chris@0
|
148 unset($filtered_element[$arg]);
|
Chris@0
|
149 }
|
Chris@0
|
150 }
|
Chris@0
|
151 return $filtered_element;
|
Chris@0
|
152 }
|