Chris@0
|
1 <?php
|
Chris@0
|
2
|
Chris@0
|
3 /**
|
Chris@0
|
4 * @file
|
Chris@0
|
5 * Preprocessors and helper functions to make theming easier.
|
Chris@0
|
6 */
|
Chris@0
|
7
|
Chris@0
|
8 use Drupal\Component\Utility\Html;
|
Chris@0
|
9 use Drupal\Component\Utility\Xss;
|
Chris@0
|
10 use Drupal\Core\Template\Attribute;
|
Chris@0
|
11 use Drupal\Core\Url;
|
Chris@18
|
12 use Drupal\Core\Utility\TableSort;
|
Chris@0
|
13
|
Chris@0
|
14 /**
|
Chris@0
|
15 * Prepares variables for view templates.
|
Chris@0
|
16 *
|
Chris@0
|
17 * Default template: views-view.html.twig.
|
Chris@0
|
18 *
|
Chris@0
|
19 * @param array $variables
|
Chris@0
|
20 * An associative array containing:
|
Chris@0
|
21 * - view: The ViewExecutable object.
|
Chris@0
|
22 */
|
Chris@0
|
23 function template_preprocess_views_view(&$variables) {
|
Chris@0
|
24 $view = $variables['view'];
|
Chris@0
|
25 $id = $view->storage->id();
|
Chris@0
|
26
|
Chris@0
|
27 $variables['css_name'] = Html::cleanCssIdentifier($id);
|
Chris@0
|
28 $variables['id'] = $id;
|
Chris@0
|
29 $variables['display_id'] = $view->current_display;
|
Chris@0
|
30 // Override the title to be empty by default. For example, if viewing a page
|
Chris@0
|
31 // view, 'title' will already be populated in $variables. This can still be
|
Chris@0
|
32 // overridden to use a title when needed. See views_ui_preprocess_views_view()
|
Chris@0
|
33 // for an example of this.
|
Chris@0
|
34 $variables['title'] = '';
|
Chris@0
|
35
|
Chris@0
|
36 $css_class = $view->display_handler->getOption('css_class');
|
Chris@0
|
37 if (!empty($css_class)) {
|
Chris@17
|
38 // Views uses its own sanitization method. This is preserved to keep
|
Chris@17
|
39 // backwards compatibility.
|
Chris@17
|
40 // @todo https://www.drupal.org/project/drupal/issues/2977950 Decide what to
|
Chris@17
|
41 // do with the backwards compatibility layer.
|
Chris@17
|
42 $bc_classes = explode(' ', preg_replace('/[^a-zA-Z0-9- ]/', '-', $css_class));
|
Chris@17
|
43 // Sanitize the classes using the classes using the proper API.
|
Chris@17
|
44 $sanitized_classes = array_map('\Drupal\Component\Utility\Html::cleanCssIdentifier', explode(' ', $css_class));
|
Chris@17
|
45 $view_classes = array_unique(array_merge($bc_classes, $sanitized_classes));
|
Chris@17
|
46 // Merge the view display classes into any existing classes if they exist.
|
Chris@17
|
47 $variables['attributes']['class'] = !empty($variables['attributes']['class']) ? array_merge($variables['attributes']['class'], $view_classes) : $view_classes;
|
Chris@17
|
48 $variables['css_class'] = implode(' ', $view_classes);
|
Chris@0
|
49 }
|
Chris@0
|
50
|
Chris@0
|
51 // contextual_preprocess() only works on render elements, and since this theme
|
Chris@0
|
52 // hook is not for a render element, contextual_preprocess() falls back to the
|
Chris@0
|
53 // first argument and checks if that is a render element. The first element is
|
Chris@0
|
54 // view_array. However, view_array does not get set anywhere, but since we do
|
Chris@0
|
55 // have access to the View object, we can also access the View object's
|
Chris@0
|
56 // element, which is a render element that does have #contextual_links set if
|
Chris@0
|
57 // the display supports it. Doing this allows contextual_preprocess() to
|
Chris@0
|
58 // access this theme hook's render element, and therefore allows this template
|
Chris@0
|
59 // to have contextual links.
|
Chris@0
|
60 // @see views_theme()
|
Chris@0
|
61 $variables['view_array'] = $variables['view']->element;
|
Chris@0
|
62
|
Chris@0
|
63 // Attachments are always updated with the outer view, never by themselves,
|
Chris@0
|
64 // so they do not have dom ids.
|
Chris@0
|
65 if (empty($view->is_attachment)) {
|
Chris@0
|
66 // Our JavaScript needs to have some means to find the HTML belonging to
|
Chris@0
|
67 // this view.
|
Chris@0
|
68 //
|
Chris@0
|
69 // It is true that the DIV wrapper has classes denoting the name of the view
|
Chris@0
|
70 // and its display ID, but this is not enough to unequivocally match a view
|
Chris@0
|
71 // with its HTML, because one view may appear several times on the page. So
|
Chris@0
|
72 // we set up a hash with the current time, $dom_id, to issue a "unique"
|
Chris@0
|
73 // identifier for each view. This identifier is written to both
|
Chris@0
|
74 // drupalSettings and the DIV wrapper.
|
Chris@0
|
75 $variables['dom_id'] = $view->dom_id;
|
Chris@0
|
76 }
|
Chris@0
|
77 }
|
Chris@0
|
78
|
Chris@0
|
79 /**
|
Chris@0
|
80 * Prepares variables for views fields templates.
|
Chris@0
|
81 *
|
Chris@0
|
82 * Default template: views-view-fields.html.twig.
|
Chris@0
|
83 *
|
Chris@0
|
84 * @param array $variables
|
Chris@0
|
85 * An associative array containing:
|
Chris@0
|
86 * - view: The view object.
|
Chris@0
|
87 * - options: An array of options. Each option contains:
|
Chris@0
|
88 * - inline: An array that contains the fields that are to be
|
Chris@0
|
89 * displayed inline.
|
Chris@0
|
90 * - default_field_elements: If default field wrapper
|
Chris@0
|
91 * elements are to be provided.
|
Chris@0
|
92 * - hide_empty: Whether the field is to be hidden if empty.
|
Chris@0
|
93 * - element_default_classes: If the default classes are to be added.
|
Chris@0
|
94 * - separator: A string to be placed between inline fields to keep them
|
Chris@0
|
95 * visually distinct.
|
Chris@0
|
96 * - row: An array containing information about the current row.
|
Chris@0
|
97 */
|
Chris@0
|
98 function template_preprocess_views_view_fields(&$variables) {
|
Chris@0
|
99 $view = $variables['view'];
|
Chris@0
|
100
|
Chris@0
|
101 // Loop through the fields for this view.
|
Chris@0
|
102 $previous_inline = FALSE;
|
Chris@0
|
103 // Ensure it's at least an empty array.
|
Chris@0
|
104 $variables['fields'] = [];
|
Chris@0
|
105 /** @var \Drupal\views\ResultRow $row */
|
Chris@0
|
106 $row = $variables['row'];
|
Chris@0
|
107 foreach ($view->field as $id => $field) {
|
Chris@0
|
108 // render this even if set to exclude so it can be used elsewhere.
|
Chris@0
|
109 $field_output = $view->style_plugin->getField($row->index, $id);
|
Chris@0
|
110 $empty = $field->isValueEmpty($field_output, $field->options['empty_zero']);
|
Chris@0
|
111 if (empty($field->options['exclude']) && (!$empty || (empty($field->options['hide_empty']) && empty($variables['options']['hide_empty'])))) {
|
Chris@0
|
112 $object = new stdClass();
|
Chris@0
|
113 $object->handler = $view->field[$id];
|
Chris@0
|
114 $object->inline = !empty($variables['options']['inline'][$id]);
|
Chris@0
|
115 // Set up default value of the flag that indicates whether to display a
|
Chris@0
|
116 // colon after the label.
|
Chris@0
|
117 $object->has_label_colon = FALSE;
|
Chris@0
|
118
|
Chris@0
|
119 $object->element_type = $object->handler->elementType(TRUE, !$variables['options']['default_field_elements'], $object->inline);
|
Chris@0
|
120 if ($object->element_type) {
|
Chris@0
|
121 $attributes = [];
|
Chris@0
|
122 if ($object->handler->options['element_default_classes']) {
|
Chris@0
|
123 $attributes['class'][] = 'field-content';
|
Chris@0
|
124 }
|
Chris@0
|
125
|
Chris@0
|
126 if ($classes = $object->handler->elementClasses($row->index)) {
|
Chris@0
|
127 $attributes['class'][] = $classes;
|
Chris@0
|
128 }
|
Chris@0
|
129 $object->element_attributes = new Attribute($attributes);
|
Chris@0
|
130 }
|
Chris@0
|
131
|
Chris@0
|
132 $object->content = $field_output;
|
Chris@0
|
133 if (isset($view->field[$id]->field_alias) && isset($row->{$view->field[$id]->field_alias})) {
|
Chris@0
|
134 $object->raw = $row->{$view->field[$id]->field_alias};
|
Chris@0
|
135 }
|
Chris@0
|
136 else {
|
Chris@0
|
137 // Make sure it exists to reduce NOTICE.
|
Chris@0
|
138 $object->raw = NULL;
|
Chris@0
|
139 }
|
Chris@0
|
140
|
Chris@0
|
141 if (!empty($variables['options']['separator']) && $previous_inline && $object->inline && $object->content) {
|
Chris@0
|
142 $object->separator = Xss::filterAdmin($variables['options']['separator']);
|
Chris@0
|
143 }
|
Chris@0
|
144
|
Chris@0
|
145 $object->class = Html::cleanCssIdentifier($id);
|
Chris@0
|
146
|
Chris@0
|
147 $previous_inline = $object->inline;
|
Chris@0
|
148 // Set up field wrapper element.
|
Chris@0
|
149 $object->wrapper_element = $object->handler->elementWrapperType(TRUE, TRUE);
|
Chris@0
|
150 if ($object->wrapper_element === '' && $variables['options']['default_field_elements']) {
|
Chris@0
|
151 $object->wrapper_element = $object->inline ? 'span' : 'div';
|
Chris@0
|
152 }
|
Chris@0
|
153
|
Chris@0
|
154 // Set up field wrapper attributes if field wrapper was set.
|
Chris@0
|
155 if ($object->wrapper_element) {
|
Chris@0
|
156 $attributes = [];
|
Chris@0
|
157 if ($object->handler->options['element_default_classes']) {
|
Chris@0
|
158 $attributes['class'][] = 'views-field';
|
Chris@0
|
159 $attributes['class'][] = 'views-field-' . $object->class;
|
Chris@0
|
160 }
|
Chris@0
|
161
|
Chris@0
|
162 if ($classes = $object->handler->elementWrapperClasses($row->index)) {
|
Chris@0
|
163 $attributes['class'][] = $classes;
|
Chris@0
|
164 }
|
Chris@0
|
165 $object->wrapper_attributes = new Attribute($attributes);
|
Chris@0
|
166 }
|
Chris@0
|
167
|
Chris@0
|
168 // Set up field label
|
Chris@0
|
169 $object->label = $view->field[$id]->label();
|
Chris@0
|
170
|
Chris@0
|
171 // Set up field label wrapper and its attributes.
|
Chris@0
|
172 if ($object->label) {
|
Chris@0
|
173 // Add a colon in a label suffix.
|
Chris@0
|
174 if ($object->handler->options['element_label_colon']) {
|
Chris@0
|
175 $object->label_suffix = ': ';
|
Chris@0
|
176 $object->has_label_colon = TRUE;
|
Chris@0
|
177 }
|
Chris@0
|
178
|
Chris@0
|
179 // Set up label HTML element.
|
Chris@0
|
180 $object->label_element = $object->handler->elementLabelType(TRUE, !$variables['options']['default_field_elements']);
|
Chris@0
|
181
|
Chris@0
|
182 // Set up label attributes.
|
Chris@0
|
183 if ($object->label_element) {
|
Chris@0
|
184 $attributes = [];
|
Chris@0
|
185 if ($object->handler->options['element_default_classes']) {
|
Chris@0
|
186 $attributes['class'][] = 'views-label';
|
Chris@0
|
187 $attributes['class'][] = 'views-label-' . $object->class;
|
Chris@0
|
188 }
|
Chris@0
|
189
|
Chris@0
|
190 // Set up field label.
|
Chris@0
|
191 $element_label_class = $object->handler->elementLabelClasses($row->index);
|
Chris@0
|
192 if ($element_label_class) {
|
Chris@0
|
193 $attributes['class'][] = $element_label_class;
|
Chris@0
|
194 }
|
Chris@0
|
195 $object->label_attributes = new Attribute($attributes);
|
Chris@0
|
196 }
|
Chris@0
|
197 }
|
Chris@0
|
198
|
Chris@0
|
199 $variables['fields'][$id] = $object;
|
Chris@0
|
200 }
|
Chris@0
|
201 }
|
Chris@0
|
202
|
Chris@0
|
203 }
|
Chris@0
|
204
|
Chris@0
|
205 /**
|
Chris@0
|
206 * Prepares variables for views single grouping templates.
|
Chris@0
|
207 *
|
Chris@0
|
208 * Default template: views-view-grouping.html.twig.
|
Chris@0
|
209 *
|
Chris@0
|
210 * @param array $variables
|
Chris@0
|
211 * An associative array containing:
|
Chris@0
|
212 * - view: The view object.
|
Chris@0
|
213 * - rows: The rows returned from the view.
|
Chris@0
|
214 * - grouping_level: Integer indicating the hierarchical level of the
|
Chris@0
|
215 * grouping.
|
Chris@0
|
216 * - content: The content to be grouped.
|
Chris@0
|
217 * - title: The group heading.
|
Chris@0
|
218 */
|
Chris@0
|
219 function template_preprocess_views_view_grouping(&$variables) {
|
Chris@0
|
220 $variables['content'] = $variables['view']->style_plugin->renderGroupingSets($variables['rows'], $variables['grouping_level']);
|
Chris@0
|
221 }
|
Chris@0
|
222
|
Chris@0
|
223 /**
|
Chris@0
|
224 * Prepares variables for views field templates.
|
Chris@0
|
225 *
|
Chris@0
|
226 * Default template: views-view-field.html.twig.
|
Chris@0
|
227 *
|
Chris@0
|
228 * @param array $variables
|
Chris@0
|
229 * An associative array containing:
|
Chris@0
|
230 * - field: The field handler object for the current field.
|
Chris@0
|
231 * - row: Object representing the raw result of the SQL query for the current
|
Chris@0
|
232 * field.
|
Chris@0
|
233 * - view: Instance of the ViewExecutable object for the parent view.
|
Chris@0
|
234 */
|
Chris@0
|
235 function template_preprocess_views_view_field(&$variables) {
|
Chris@0
|
236 $variables['output'] = $variables['field']->advancedRender($variables['row']);
|
Chris@0
|
237 }
|
Chris@0
|
238
|
Chris@0
|
239 /**
|
Chris@0
|
240 * Prepares variables for views summary templates.
|
Chris@0
|
241 *
|
Chris@0
|
242 * The summary prints a single record from a row, with fields.
|
Chris@0
|
243 *
|
Chris@0
|
244 * Default template: views-view-summary.html.twig.
|
Chris@0
|
245 *
|
Chris@0
|
246 * @param array $variables
|
Chris@0
|
247 * An associative array containing:
|
Chris@0
|
248 * - view: A ViewExecutable object.
|
Chris@0
|
249 * - rows: The raw row data.
|
Chris@0
|
250 */
|
Chris@0
|
251 function template_preprocess_views_view_summary(&$variables) {
|
Chris@0
|
252 /** @var \Drupal\views\ViewExecutable $view */
|
Chris@0
|
253 $view = $variables['view'];
|
Chris@0
|
254 $argument = $view->argument[$view->build_info['summary_level']];
|
Chris@0
|
255
|
Chris@0
|
256 $url_options = [];
|
Chris@0
|
257
|
Chris@0
|
258 if (!empty($view->exposed_raw_input)) {
|
Chris@0
|
259 $url_options['query'] = $view->exposed_raw_input;
|
Chris@0
|
260 }
|
Chris@0
|
261
|
Chris@0
|
262 $active_urls = [
|
Chris@0
|
263 // Force system path.
|
Chris@18
|
264 Url::fromRoute('<current>', [], ['alias' => TRUE])->toString(),
|
Chris@0
|
265 // Force system path.
|
Chris@0
|
266 Url::fromRouteMatch(\Drupal::routeMatch())->setOption('alias', TRUE)->toString(),
|
Chris@0
|
267 // Could be an alias.
|
Chris@18
|
268 Url::fromRoute('<current>')->toString(),
|
Chris@0
|
269 // Could be an alias.
|
Chris@0
|
270 Url::fromRouteMatch(\Drupal::routeMatch())->toString(),
|
Chris@0
|
271 ];
|
Chris@0
|
272 $active_urls = array_combine($active_urls, $active_urls);
|
Chris@0
|
273
|
Chris@0
|
274 // Collect all arguments foreach row, to be able to alter them for example
|
Chris@0
|
275 // by the validator. This is not done per single argument value, because this
|
Chris@0
|
276 // could cause performance problems.
|
Chris@0
|
277 $row_args = [];
|
Chris@0
|
278
|
Chris@0
|
279 foreach ($variables['rows'] as $id => $row) {
|
Chris@0
|
280 $row_args[$id] = $argument->summaryArgument($row);
|
Chris@0
|
281 }
|
Chris@0
|
282 $argument->processSummaryArguments($row_args);
|
Chris@0
|
283
|
Chris@0
|
284 foreach ($variables['rows'] as $id => $row) {
|
Chris@0
|
285 $variables['rows'][$id]->attributes = [];
|
Chris@0
|
286 $variables['rows'][$id]->link = $argument->summaryName($row);
|
Chris@0
|
287 $args = $view->args;
|
Chris@0
|
288 $args[$argument->position] = $row_args[$id];
|
Chris@0
|
289
|
Chris@0
|
290 if (!empty($argument->options['summary_options']['base_path'])) {
|
Chris@0
|
291 $base_path = $argument->options['summary_options']['base_path'];
|
Chris@0
|
292 $tokens = $view->getDisplay()->getArgumentsTokens();
|
Chris@0
|
293 $base_path = $argument->globalTokenReplace($base_path, $tokens);
|
Chris@0
|
294 // @todo Views should expect and store a leading /. See:
|
Chris@0
|
295 // https://www.drupal.org/node/2423913
|
Chris@0
|
296 $url = Url::fromUserInput('/' . $base_path);
|
Chris@0
|
297 try {
|
Chris@0
|
298 /** @var \Symfony\Component\Routing\Route $route */
|
Chris@0
|
299 $route_name = $url->getRouteName();
|
Chris@0
|
300 $route = \Drupal::service('router.route_provider')->getRouteByName($route_name);
|
Chris@0
|
301
|
Chris@0
|
302 $route_variables = $route->compile()->getVariables();
|
Chris@0
|
303 $parameters = $url->getRouteParameters();
|
Chris@0
|
304
|
Chris@0
|
305 foreach ($route_variables as $variable_name) {
|
Chris@0
|
306 $parameters[$variable_name] = array_shift($args);
|
Chris@0
|
307 }
|
Chris@0
|
308
|
Chris@0
|
309 $url->setRouteParameters($parameters);
|
Chris@0
|
310 }
|
Chris@0
|
311 catch (Exception $e) {
|
Chris@0
|
312 // If the given route doesn't exist, default to <front>
|
Chris@0
|
313 $url = Url::fromRoute('<front>');
|
Chris@0
|
314 }
|
Chris@0
|
315 }
|
Chris@0
|
316 else {
|
Chris@0
|
317 $url = $view->getUrl($args)->setOptions($url_options);
|
Chris@0
|
318 }
|
Chris@0
|
319 $variables['rows'][$id]->url = $url->toString();
|
Chris@0
|
320 $variables['rows'][$id]->count = intval($row->{$argument->count_alias});
|
Chris@0
|
321 $variables['rows'][$id]->attributes = new Attribute($variables['rows'][$id]->attributes);
|
Chris@0
|
322 $variables['rows'][$id]->active = isset($active_urls[$variables['rows'][$id]->url]);
|
Chris@0
|
323 }
|
Chris@0
|
324 }
|
Chris@0
|
325
|
Chris@0
|
326 /**
|
Chris@0
|
327 * Prepares variables for unformatted summary view templates.
|
Chris@0
|
328 *
|
Chris@0
|
329 * Default template: views-view-summary-unformatted.html.twig.
|
Chris@0
|
330 *
|
Chris@0
|
331 * @param array $variables
|
Chris@0
|
332 * An associative array containing:
|
Chris@0
|
333 * - view: A ViewExecutable object.
|
Chris@0
|
334 * - rows: The raw row data.
|
Chris@0
|
335 * - options: An array of options. Each option contains:
|
Chris@0
|
336 * - separator: A string to be placed between inline fields to keep them
|
Chris@0
|
337 * visually distinct.
|
Chris@0
|
338 */
|
Chris@0
|
339 function template_preprocess_views_view_summary_unformatted(&$variables) {
|
Chris@0
|
340 /** @var \Drupal\views\ViewExecutable $view */
|
Chris@0
|
341 $view = $variables['view'];
|
Chris@0
|
342 $argument = $view->argument[$view->build_info['summary_level']];
|
Chris@0
|
343
|
Chris@0
|
344 $url_options = [];
|
Chris@0
|
345
|
Chris@0
|
346 if (!empty($view->exposed_raw_input)) {
|
Chris@0
|
347 $url_options['query'] = $view->exposed_raw_input;
|
Chris@0
|
348 }
|
Chris@0
|
349
|
Chris@0
|
350 $count = 0;
|
Chris@0
|
351 $active_urls = [
|
Chris@0
|
352 // Force system path.
|
Chris@18
|
353 Url::fromRoute('<current>', [], ['alias' => TRUE])->toString(),
|
Chris@0
|
354 // Could be an alias.
|
Chris@18
|
355 Url::fromRoute('<current>')->toString(),
|
Chris@0
|
356 ];
|
Chris@0
|
357 $active_urls = array_combine($active_urls, $active_urls);
|
Chris@0
|
358
|
Chris@0
|
359 // Collect all arguments for each row, to be able to alter them for example
|
Chris@0
|
360 // by the validator. This is not done per single argument value, because
|
Chris@0
|
361 // this could cause performance problems.
|
Chris@0
|
362 $row_args = [];
|
Chris@0
|
363 foreach ($variables['rows'] as $id => $row) {
|
Chris@0
|
364 $row_args[$id] = $argument->summaryArgument($row);
|
Chris@0
|
365 }
|
Chris@0
|
366 $argument->processSummaryArguments($row_args);
|
Chris@0
|
367
|
Chris@0
|
368 foreach ($variables['rows'] as $id => $row) {
|
Chris@0
|
369 // Only false on first time.
|
Chris@0
|
370 if ($count++) {
|
Chris@0
|
371 $variables['rows'][$id]->separator = Xss::filterAdmin($variables['options']['separator']);
|
Chris@0
|
372 }
|
Chris@0
|
373 $variables['rows'][$id]->attributes = [];
|
Chris@0
|
374 $variables['rows'][$id]->link = $argument->summaryName($row);
|
Chris@0
|
375 $args = $view->args;
|
Chris@0
|
376 $args[$argument->position] = $row_args[$id];
|
Chris@0
|
377
|
Chris@0
|
378 if (!empty($argument->options['summary_options']['base_path'])) {
|
Chris@0
|
379 $base_path = $argument->options['summary_options']['base_path'];
|
Chris@0
|
380 $tokens = $view->getDisplay()->getArgumentsTokens();
|
Chris@0
|
381 $base_path = $argument->globalTokenReplace($base_path, $tokens);
|
Chris@0
|
382 // @todo Views should expect and store a leading /. See:
|
Chris@0
|
383 // https://www.drupal.org/node/2423913
|
Chris@0
|
384 $url = Url::fromUserInput('/' . $base_path);
|
Chris@0
|
385 try {
|
Chris@0
|
386 /** @var \Symfony\Component\Routing\Route $route */
|
Chris@0
|
387 $route = \Drupal::service('router.route_provider')->getRouteByName($url->getRouteName());
|
Chris@0
|
388 $route_variables = $route->compile()->getVariables();
|
Chris@0
|
389 $parameters = $url->getRouteParameters();
|
Chris@0
|
390
|
Chris@0
|
391 foreach ($route_variables as $variable_name) {
|
Chris@0
|
392 $parameters[$variable_name] = array_shift($args);
|
Chris@0
|
393 }
|
Chris@0
|
394
|
Chris@0
|
395 $url->setRouteParameters($parameters);
|
Chris@0
|
396 }
|
Chris@0
|
397 catch (Exception $e) {
|
Chris@0
|
398 // If the given route doesn't exist, default to <front>
|
Chris@0
|
399 $url = Url::fromRoute('<front>');
|
Chris@0
|
400 }
|
Chris@0
|
401 }
|
Chris@0
|
402 else {
|
Chris@0
|
403 $url = $view->getUrl($args)->setOptions($url_options);
|
Chris@0
|
404 }
|
Chris@0
|
405 $variables['rows'][$id]->url = $url->toString();
|
Chris@0
|
406 $variables['rows'][$id]->count = intval($row->{$argument->count_alias});
|
Chris@0
|
407 $variables['rows'][$id]->active = isset($active_urls[$variables['rows'][$id]->url]);
|
Chris@0
|
408 $variables['rows'][$id]->attributes = new Attribute($variables['rows'][$id]->attributes);
|
Chris@0
|
409 }
|
Chris@0
|
410 }
|
Chris@0
|
411
|
Chris@0
|
412 /**
|
Chris@0
|
413 * Prepares variables for views table templates.
|
Chris@0
|
414 *
|
Chris@0
|
415 * Default template: views-view-table.html.twig.
|
Chris@0
|
416 *
|
Chris@0
|
417 * @param array $variables
|
Chris@0
|
418 * An associative array containing:
|
Chris@0
|
419 * - view: A ViewExecutable object.
|
Chris@0
|
420 * - rows: The raw row data.
|
Chris@0
|
421 */
|
Chris@0
|
422 function template_preprocess_views_view_table(&$variables) {
|
Chris@0
|
423 $view = $variables['view'];
|
Chris@0
|
424
|
Chris@0
|
425 // We need the raw data for this grouping, which is passed in
|
Chris@0
|
426 // as $variables['rows'].
|
Chris@0
|
427 // However, the template also needs to use for the rendered fields. We
|
Chris@0
|
428 // therefore swap the raw data out to a new variable and reset $variables['rows']
|
Chris@0
|
429 // so that it can get rebuilt.
|
Chris@0
|
430 // Store rows so that they may be used by further preprocess functions.
|
Chris@0
|
431 $result = $variables['result'] = $variables['rows'];
|
Chris@0
|
432 $variables['rows'] = [];
|
Chris@0
|
433 $variables['header'] = [];
|
Chris@0
|
434
|
Chris@0
|
435 $options = $view->style_plugin->options;
|
Chris@0
|
436 $handler = $view->style_plugin;
|
Chris@0
|
437
|
Chris@0
|
438 $fields = &$view->field;
|
Chris@0
|
439 $columns = $handler->sanitizeColumns($options['columns'], $fields);
|
Chris@0
|
440
|
Chris@0
|
441 $active = !empty($handler->active) ? $handler->active : '';
|
Chris@0
|
442 $order = !empty($handler->order) ? $handler->order : 'asc';
|
Chris@0
|
443
|
Chris@0
|
444 // A boolean variable which stores whether the table has a responsive class.
|
Chris@0
|
445 $responsive = FALSE;
|
Chris@0
|
446
|
Chris@0
|
447 // For the actual site we want to not render full URLs, because this would
|
Chris@0
|
448 // make pagers cacheable per URL, which is problematic in blocks, for example.
|
Chris@0
|
449 // For the actual live preview though the javascript relies on properly
|
Chris@0
|
450 // working URLs.
|
Chris@0
|
451 $route_name = !empty($view->live_preview) ? '<current>' : '<none>';
|
Chris@0
|
452
|
Chris@18
|
453 $query = TableSort::getQueryParameters(\Drupal::request());
|
Chris@0
|
454 if (isset($view->exposed_raw_input)) {
|
Chris@0
|
455 $query += $view->exposed_raw_input;
|
Chris@0
|
456 }
|
Chris@0
|
457
|
Chris@0
|
458 // A boolean to store whether the table's header has any labels.
|
Chris@0
|
459 $has_header_labels = FALSE;
|
Chris@0
|
460 foreach ($columns as $field => $column) {
|
Chris@0
|
461 // Create a second variable so we can easily find what fields we have and
|
Chris@0
|
462 // what the CSS classes should be.
|
Chris@0
|
463 $variables['fields'][$field] = Html::cleanCssIdentifier($field);
|
Chris@0
|
464 if ($active == $field) {
|
Chris@0
|
465 $variables['fields'][$field] .= ' is-active';
|
Chris@0
|
466 }
|
Chris@0
|
467
|
Chris@0
|
468 // Render the header labels.
|
Chris@0
|
469 if ($field == $column && empty($fields[$field]->options['exclude'])) {
|
Chris@0
|
470 $label = !empty($fields[$field]) ? $fields[$field]->label() : '';
|
Chris@0
|
471 if (empty($options['info'][$field]['sortable']) || !$fields[$field]->clickSortable()) {
|
Chris@0
|
472 $variables['header'][$field]['content'] = $label;
|
Chris@0
|
473 }
|
Chris@0
|
474 else {
|
Chris@0
|
475 $initial = !empty($options['info'][$field]['default_sort_order']) ? $options['info'][$field]['default_sort_order'] : 'asc';
|
Chris@0
|
476
|
Chris@0
|
477 if ($active == $field) {
|
Chris@0
|
478 $initial = ($order == 'asc') ? 'desc' : 'asc';
|
Chris@0
|
479 }
|
Chris@0
|
480
|
Chris@0
|
481 $title = t('sort by @s', ['@s' => $label]);
|
Chris@0
|
482 if ($active == $field) {
|
Chris@0
|
483 $variables['header'][$field]['sort_indicator'] = [
|
Chris@0
|
484 '#theme' => 'tablesort_indicator',
|
Chris@0
|
485 '#style' => $initial,
|
Chris@0
|
486 ];
|
Chris@0
|
487 }
|
Chris@0
|
488
|
Chris@0
|
489 $query['order'] = $field;
|
Chris@0
|
490 $query['sort'] = $initial;
|
Chris@0
|
491 $link_options = [
|
Chris@0
|
492 'query' => $query,
|
Chris@0
|
493 ];
|
Chris@0
|
494 $url = new Url($route_name, [], $link_options);
|
Chris@0
|
495 $variables['header'][$field]['url'] = $url->toString();
|
Chris@0
|
496 $variables['header'][$field]['content'] = $label;
|
Chris@0
|
497 $variables['header'][$field]['title'] = $title;
|
Chris@0
|
498 }
|
Chris@0
|
499
|
Chris@0
|
500 $variables['header'][$field]['default_classes'] = $fields[$field]->options['element_default_classes'];
|
Chris@0
|
501 // Set up the header label class.
|
Chris@0
|
502 $variables['header'][$field]['attributes'] = [];
|
Chris@0
|
503 $class = $fields[$field]->elementLabelClasses(0);
|
Chris@0
|
504 if ($class) {
|
Chris@0
|
505 $variables['header'][$field]['attributes']['class'][] = $class;
|
Chris@0
|
506 }
|
Chris@0
|
507 // Add responsive header classes.
|
Chris@0
|
508 if (!empty($options['info'][$field]['responsive'])) {
|
Chris@0
|
509 $variables['header'][$field]['attributes']['class'][] = $options['info'][$field]['responsive'];
|
Chris@0
|
510 $responsive = TRUE;
|
Chris@0
|
511 }
|
Chris@0
|
512 // Add a CSS align class to each field if one was set.
|
Chris@0
|
513 if (!empty($options['info'][$field]['align'])) {
|
Chris@0
|
514 $variables['header'][$field]['attributes']['class'][] = Html::cleanCssIdentifier($options['info'][$field]['align']);
|
Chris@0
|
515 }
|
Chris@0
|
516 // Add a header label wrapper if one was selected.
|
Chris@0
|
517 if ($variables['header'][$field]['content']) {
|
Chris@0
|
518 $element_label_type = $fields[$field]->elementLabelType(TRUE, TRUE);
|
Chris@0
|
519 if ($element_label_type) {
|
Chris@0
|
520 $variables['header'][$field]['wrapper_element'] = $element_label_type;
|
Chris@0
|
521 }
|
Chris@0
|
522 // Improves accessibility of complex tables.
|
Chris@0
|
523 $variables['header'][$field]['attributes']['id'] = Html::getUniqueId('view-' . $field . '-table-column');
|
Chris@0
|
524 }
|
Chris@0
|
525 // Check if header label is not empty.
|
Chris@0
|
526 if (!empty($variables['header'][$field]['content'])) {
|
Chris@0
|
527 $has_header_labels = TRUE;
|
Chris@0
|
528 }
|
Chris@0
|
529
|
Chris@0
|
530 $variables['header'][$field]['attributes'] = new Attribute($variables['header'][$field]['attributes']);
|
Chris@0
|
531 }
|
Chris@0
|
532
|
Chris@0
|
533 // Add a CSS align class to each field if one was set.
|
Chris@0
|
534 if (!empty($options['info'][$field]['align'])) {
|
Chris@0
|
535 $variables['fields'][$field] .= ' ' . Html::cleanCssIdentifier($options['info'][$field]['align']);
|
Chris@0
|
536 }
|
Chris@0
|
537
|
Chris@0
|
538 // Render each field into its appropriate column.
|
Chris@0
|
539 foreach ($result as $num => $row) {
|
Chris@0
|
540
|
Chris@0
|
541 // Skip building the attributes and content if the field is to be excluded
|
Chris@0
|
542 // from the display.
|
Chris@0
|
543 if (!empty($fields[$field]->options['exclude'])) {
|
Chris@0
|
544 continue;
|
Chris@0
|
545 }
|
Chris@0
|
546
|
Chris@0
|
547 // Reference to the column in the loop to make the code easier to read.
|
Chris@0
|
548 $column_reference =& $variables['rows'][$num]['columns'][$column];
|
Chris@0
|
549
|
Chris@0
|
550 $column_reference['default_classes'] = $fields[$field]->options['element_default_classes'];
|
Chris@0
|
551
|
Chris@0
|
552 // Set the field key to the column so it can be used for adding classes
|
Chris@0
|
553 // in a template.
|
Chris@0
|
554 $column_reference['fields'][] = $variables['fields'][$field];
|
Chris@0
|
555
|
Chris@0
|
556 // Add field classes.
|
Chris@0
|
557 if (!isset($column_reference['attributes'])) {
|
Chris@0
|
558 $column_reference['attributes'] = [];
|
Chris@0
|
559 }
|
Chris@0
|
560
|
Chris@0
|
561 if ($classes = $fields[$field]->elementClasses($num)) {
|
Chris@0
|
562 $column_reference['attributes']['class'][] = $classes;
|
Chris@0
|
563 }
|
Chris@0
|
564
|
Chris@0
|
565 // Add responsive header classes.
|
Chris@0
|
566 if (!empty($options['info'][$field]['responsive'])) {
|
Chris@0
|
567 $column_reference['attributes']['class'][] = $options['info'][$field]['responsive'];
|
Chris@0
|
568 }
|
Chris@0
|
569
|
Chris@0
|
570 // Improves accessibility of complex tables.
|
Chris@0
|
571 if (isset($variables['header'][$field]['attributes']['id'])) {
|
Chris@0
|
572 $column_reference['attributes']['headers'] = [$variables['header'][$field]['attributes']['id']];
|
Chris@0
|
573 }
|
Chris@0
|
574
|
Chris@0
|
575 if (!empty($fields[$field])) {
|
Chris@0
|
576 $field_output = $handler->getField($num, $field);
|
Chris@0
|
577 $column_reference['wrapper_element'] = $fields[$field]->elementType(TRUE, TRUE);
|
Chris@0
|
578 if (!isset($column_reference['content'])) {
|
Chris@0
|
579 $column_reference['content'] = [];
|
Chris@0
|
580 }
|
Chris@0
|
581
|
Chris@0
|
582 // Only bother with separators and stuff if the field shows up.
|
Chris@0
|
583 // Place the field into the column, along with an optional separator.
|
Chris@0
|
584 if (trim($field_output) != '') {
|
Chris@0
|
585 if (!empty($column_reference['content']) && !empty($options['info'][$column]['separator'])) {
|
Chris@0
|
586 $column_reference['content'][] = [
|
Chris@0
|
587 'separator' => ['#markup' => $options['info'][$column]['separator']],
|
Chris@17
|
588 'field_output' => ['#markup' => $field_output],
|
Chris@0
|
589 ];
|
Chris@0
|
590 }
|
Chris@0
|
591 else {
|
Chris@0
|
592 $column_reference['content'][] = [
|
Chris@17
|
593 'field_output' => ['#markup' => $field_output],
|
Chris@0
|
594 ];
|
Chris@0
|
595 }
|
Chris@0
|
596 }
|
Chris@0
|
597 }
|
Chris@0
|
598 $column_reference['attributes'] = new Attribute($column_reference['attributes']);
|
Chris@0
|
599 }
|
Chris@0
|
600
|
Chris@0
|
601 // Remove columns if the "empty_column" option is checked and the
|
Chris@0
|
602 // field is empty.
|
Chris@0
|
603 if (!empty($options['info'][$field]['empty_column'])) {
|
Chris@0
|
604 $empty = TRUE;
|
Chris@0
|
605 foreach ($variables['rows'] as $columns) {
|
Chris@0
|
606 $empty &= empty($columns['columns'][$column]['content']);
|
Chris@0
|
607 }
|
Chris@0
|
608 if ($empty) {
|
Chris@0
|
609 foreach ($variables['rows'] as &$column_items) {
|
Chris@0
|
610 unset($column_items['columns'][$column]);
|
Chris@0
|
611 }
|
Chris@0
|
612 unset($variables['header'][$column]);
|
Chris@0
|
613 }
|
Chris@0
|
614 }
|
Chris@0
|
615 }
|
Chris@0
|
616
|
Chris@0
|
617 // Hide table header if all labels are empty.
|
Chris@0
|
618 if (!$has_header_labels) {
|
Chris@0
|
619 $variables['header'] = [];
|
Chris@0
|
620 }
|
Chris@0
|
621
|
Chris@0
|
622 foreach ($variables['rows'] as $num => $row) {
|
Chris@0
|
623 $variables['rows'][$num]['attributes'] = [];
|
Chris@0
|
624 if ($row_class = $handler->getRowClass($num)) {
|
Chris@0
|
625 $variables['rows'][$num]['attributes']['class'][] = $row_class;
|
Chris@0
|
626 }
|
Chris@0
|
627 $variables['rows'][$num]['attributes'] = new Attribute($variables['rows'][$num]['attributes']);
|
Chris@0
|
628 }
|
Chris@0
|
629
|
Chris@0
|
630 if (empty($variables['rows']) && !empty($options['empty_table'])) {
|
Chris@0
|
631 $build = $view->display_handler->renderArea('empty');
|
Chris@0
|
632 $variables['rows'][0]['columns'][0]['content'][0]['field_output'] = $build;
|
Chris@0
|
633 $variables['rows'][0]['attributes'] = new Attribute(['class' => ['odd']]);
|
Chris@0
|
634 // Calculate the amounts of rows with output.
|
Chris@0
|
635 $variables['rows'][0]['columns'][0]['attributes'] = new Attribute([
|
Chris@0
|
636 'colspan' => count($variables['header']),
|
Chris@0
|
637 'class' => ['views-empty'],
|
Chris@0
|
638 ]);
|
Chris@0
|
639 }
|
Chris@0
|
640
|
Chris@0
|
641 $variables['sticky'] = FALSE;
|
Chris@0
|
642 if (!empty($options['sticky'])) {
|
Chris@0
|
643 $variables['view']->element['#attached']['library'][] = 'core/drupal.tableheader';
|
Chris@0
|
644 $variables['sticky'] = TRUE;
|
Chris@0
|
645 }
|
Chris@0
|
646
|
Chris@0
|
647 // Add the caption to the list if set.
|
Chris@0
|
648 if (!empty($handler->options['caption'])) {
|
Chris@0
|
649 $variables['caption'] = ['#markup' => $handler->options['caption']];
|
Chris@0
|
650 $variables['caption_needed'] = TRUE;
|
Chris@0
|
651 }
|
Chris@0
|
652 elseif (!empty($variables['title'])) {
|
Chris@0
|
653 $variables['caption'] = ['#markup' => $variables['title']];
|
Chris@0
|
654 $variables['caption_needed'] = TRUE;
|
Chris@0
|
655 }
|
Chris@0
|
656 else {
|
Chris@0
|
657 $variables['caption'] = '';
|
Chris@0
|
658 $variables['caption_needed'] = FALSE;
|
Chris@0
|
659 }
|
Chris@0
|
660
|
Chris@0
|
661 $variables['summary'] = $handler->options['summary'];
|
Chris@0
|
662 $variables['description'] = $handler->options['description'];
|
Chris@0
|
663 $variables['caption_needed'] |= !empty($variables['summary']) || !empty($variables['description']);
|
Chris@0
|
664
|
Chris@0
|
665 $variables['responsive'] = FALSE;
|
Chris@0
|
666 // If the table has headers and it should react responsively to columns hidden
|
Chris@0
|
667 // with the classes represented by the constants RESPONSIVE_PRIORITY_MEDIUM
|
Chris@0
|
668 // and RESPONSIVE_PRIORITY_LOW, add the tableresponsive behaviors.
|
Chris@0
|
669 if (isset($variables['header']) && $responsive) {
|
Chris@0
|
670 $variables['view']->element['#attached']['library'][] = 'core/drupal.tableresponsive';
|
Chris@0
|
671 // Add 'responsive-enabled' class to the table to identify it for JS.
|
Chris@0
|
672 // This is needed to target tables constructed by this function.
|
Chris@0
|
673 $variables['responsive'] = TRUE;
|
Chris@0
|
674 }
|
Chris@0
|
675 }
|
Chris@0
|
676
|
Chris@0
|
677 /**
|
Chris@0
|
678 * Prepares variables for views grid style templates.
|
Chris@0
|
679 *
|
Chris@0
|
680 * Default template: views-view-grid.html.twig.
|
Chris@0
|
681 *
|
Chris@0
|
682 * @param array $variables
|
Chris@0
|
683 * An associative array containing:
|
Chris@0
|
684 * - view: The view object.
|
Chris@0
|
685 * - rows: An array of row items. Each row is an array of content.
|
Chris@0
|
686 */
|
Chris@0
|
687 function template_preprocess_views_view_grid(&$variables) {
|
Chris@0
|
688 $options = $variables['options'] = $variables['view']->style_plugin->options;
|
Chris@0
|
689 $horizontal = ($options['alignment'] === 'horizontal');
|
Chris@0
|
690
|
Chris@0
|
691 $col = 0;
|
Chris@0
|
692 $row = 0;
|
Chris@0
|
693 $items = [];
|
Chris@0
|
694 $remainders = count($variables['rows']) % $options['columns'];
|
Chris@0
|
695 $num_rows = floor(count($variables['rows']) / $options['columns']);
|
Chris@0
|
696
|
Chris@0
|
697 // Iterate over each rendered views result row.
|
Chris@0
|
698 foreach ($variables['rows'] as $result_index => $item) {
|
Chris@0
|
699
|
Chris@0
|
700 // Add the item.
|
Chris@0
|
701 if ($horizontal) {
|
Chris@0
|
702 $items[$row]['content'][$col]['content'] = $item;
|
Chris@0
|
703 }
|
Chris@0
|
704 else {
|
Chris@0
|
705 $items[$col]['content'][$row]['content'] = $item;
|
Chris@0
|
706 }
|
Chris@0
|
707
|
Chris@0
|
708 // Create attributes for rows.
|
Chris@0
|
709 if (!$horizontal || ($horizontal && empty($items[$row]['attributes']))) {
|
Chris@0
|
710 $row_attributes = ['class' => []];
|
Chris@0
|
711 // Add custom row classes.
|
Chris@0
|
712 $row_class = array_filter(explode(' ', $variables['view']->style_plugin->getCustomClass($result_index, 'row')));
|
Chris@0
|
713 if (!empty($row_class)) {
|
Chris@0
|
714 $row_attributes['class'] = array_merge($row_attributes['class'], $row_class);
|
Chris@0
|
715 }
|
Chris@0
|
716 // Add row attributes to the item.
|
Chris@0
|
717 if ($horizontal) {
|
Chris@0
|
718 $items[$row]['attributes'] = new Attribute($row_attributes);
|
Chris@0
|
719 }
|
Chris@0
|
720 else {
|
Chris@0
|
721 $items[$col]['content'][$row]['attributes'] = new Attribute($row_attributes);
|
Chris@0
|
722 }
|
Chris@0
|
723 }
|
Chris@0
|
724
|
Chris@0
|
725 // Create attributes for columns.
|
Chris@0
|
726 if ($horizontal || (!$horizontal && empty($items[$col]['attributes']))) {
|
Chris@0
|
727 $col_attributes = ['class' => []];
|
Chris@0
|
728 // Add default views column classes.
|
Chris@0
|
729 // Add custom column classes.
|
Chris@0
|
730 $col_class = array_filter(explode(' ', $variables['view']->style_plugin->getCustomClass($result_index, 'col')));
|
Chris@0
|
731 if (!empty($col_class)) {
|
Chris@0
|
732 $col_attributes['class'] = array_merge($col_attributes['class'], $col_class);
|
Chris@0
|
733 }
|
Chris@0
|
734 // Add automatic width for columns.
|
Chris@0
|
735 if ($options['automatic_width']) {
|
Chris@0
|
736 $col_attributes['style'] = 'width: ' . (100 / $options['columns']) . '%;';
|
Chris@0
|
737 }
|
Chris@0
|
738 // Add column attributes to the item.
|
Chris@0
|
739 if ($horizontal) {
|
Chris@0
|
740 $items[$row]['content'][$col]['attributes'] = new Attribute($col_attributes);
|
Chris@0
|
741 }
|
Chris@0
|
742 else {
|
Chris@0
|
743 $items[$col]['attributes'] = new Attribute($col_attributes);
|
Chris@0
|
744 }
|
Chris@0
|
745 }
|
Chris@0
|
746
|
Chris@0
|
747 // Increase, decrease or reset appropriate integers.
|
Chris@0
|
748 if ($horizontal) {
|
Chris@0
|
749 if ($col == 0 && $col != ($options['columns'] - 1)) {
|
Chris@0
|
750 $col++;
|
Chris@0
|
751 }
|
Chris@0
|
752 elseif ($col >= ($options['columns'] - 1)) {
|
Chris@0
|
753 $col = 0;
|
Chris@0
|
754 $row++;
|
Chris@0
|
755 }
|
Chris@0
|
756 else {
|
Chris@0
|
757 $col++;
|
Chris@0
|
758 }
|
Chris@0
|
759 }
|
Chris@0
|
760 else {
|
Chris@0
|
761 $row++;
|
Chris@0
|
762 if (!$remainders && $row == $num_rows) {
|
Chris@0
|
763 $row = 0;
|
Chris@0
|
764 $col++;
|
Chris@0
|
765 }
|
Chris@0
|
766 elseif ($remainders && $row == $num_rows + 1) {
|
Chris@0
|
767 $row = 0;
|
Chris@0
|
768 $col++;
|
Chris@0
|
769 $remainders--;
|
Chris@0
|
770 }
|
Chris@0
|
771 }
|
Chris@0
|
772 }
|
Chris@0
|
773
|
Chris@0
|
774 // Add items to the variables array.
|
Chris@0
|
775 $variables['items'] = $items;
|
Chris@0
|
776 }
|
Chris@0
|
777
|
Chris@0
|
778 /**
|
Chris@0
|
779 * Prepares variables for views unformatted rows templates.
|
Chris@0
|
780 *
|
Chris@0
|
781 * Default template: views-view-unformatted.html.twig.
|
Chris@0
|
782 *
|
Chris@0
|
783 * @param array $variables
|
Chris@0
|
784 * An associative array containing:
|
Chris@0
|
785 * - view: The view object.
|
Chris@0
|
786 * - rows: An array of row items. Each row is an array of content.
|
Chris@0
|
787 */
|
Chris@0
|
788 function template_preprocess_views_view_unformatted(&$variables) {
|
Chris@0
|
789 $view = $variables['view'];
|
Chris@0
|
790 $rows = $variables['rows'];
|
Chris@0
|
791 $style = $view->style_plugin;
|
Chris@0
|
792 $options = $style->options;
|
Chris@0
|
793
|
Chris@0
|
794 $variables['default_row_class'] = !empty($options['default_row_class']);
|
Chris@0
|
795 foreach ($rows as $id => $row) {
|
Chris@0
|
796 $variables['rows'][$id] = [];
|
Chris@0
|
797 $variables['rows'][$id]['content'] = $row;
|
Chris@0
|
798 $variables['rows'][$id]['attributes'] = new Attribute();
|
Chris@0
|
799 if ($row_class = $view->style_plugin->getRowClass($id)) {
|
Chris@0
|
800 $variables['rows'][$id]['attributes']->addClass($row_class);
|
Chris@0
|
801 }
|
Chris@0
|
802 }
|
Chris@0
|
803 }
|
Chris@0
|
804
|
Chris@0
|
805 /**
|
Chris@0
|
806 * Prepares variables for Views HTML list templates.
|
Chris@0
|
807 *
|
Chris@0
|
808 * Default template: views-view-list.html.twig.
|
Chris@0
|
809 *
|
Chris@0
|
810 * @param array $variables
|
Chris@0
|
811 * An associative array containing:
|
Chris@0
|
812 * - view: A View object.
|
Chris@0
|
813 */
|
Chris@0
|
814 function template_preprocess_views_view_list(&$variables) {
|
Chris@17
|
815 $handler = $variables['view']->style_plugin;
|
Chris@0
|
816
|
Chris@0
|
817 // Fetch classes from handler options.
|
Chris@0
|
818 if ($handler->options['class']) {
|
Chris@0
|
819 $class = explode(' ', $handler->options['class']);
|
Chris@0
|
820 $class = array_map('\Drupal\Component\Utility\Html::cleanCssIdentifier', $class);
|
Chris@0
|
821
|
Chris@0
|
822 // Initialize a new attribute class for $class.
|
Chris@0
|
823 $variables['list']['attributes'] = new Attribute(['class' => $class]);
|
Chris@0
|
824 }
|
Chris@0
|
825
|
Chris@0
|
826 // Fetch wrapper classes from handler options.
|
Chris@0
|
827 if ($handler->options['wrapper_class']) {
|
Chris@0
|
828 $wrapper_class = explode(' ', $handler->options['wrapper_class']);
|
Chris@0
|
829 $variables['attributes']['class'] = array_map('\Drupal\Component\Utility\Html::cleanCssIdentifier', $wrapper_class);
|
Chris@0
|
830 }
|
Chris@0
|
831
|
Chris@0
|
832 $variables['list']['type'] = $handler->options['type'];
|
Chris@0
|
833
|
Chris@0
|
834 template_preprocess_views_view_unformatted($variables);
|
Chris@0
|
835 }
|
Chris@0
|
836
|
Chris@0
|
837 /**
|
Chris@0
|
838 * Prepares variables for RSS feed templates.
|
Chris@0
|
839 *
|
Chris@0
|
840 * Default template: views-view-rss.html.twig.
|
Chris@0
|
841 *
|
Chris@0
|
842 * @param array $variables
|
Chris@0
|
843 * An associative array containing:
|
Chris@0
|
844 * - view: A ViewExecutable object.
|
Chris@0
|
845 * - rows: The raw row data.
|
Chris@0
|
846 */
|
Chris@0
|
847 function template_preprocess_views_view_rss(&$variables) {
|
Chris@17
|
848 $view = $variables['view'];
|
Chris@0
|
849 $items = $variables['rows'];
|
Chris@0
|
850 $style = $view->style_plugin;
|
Chris@0
|
851
|
Chris@0
|
852 $config = \Drupal::config('system.site');
|
Chris@0
|
853
|
Chris@0
|
854 // The RSS 2.0 "spec" doesn't indicate HTML can be used in the description.
|
Chris@0
|
855 // We strip all HTML tags, but need to prevent double encoding from properly
|
Chris@0
|
856 // escaped source data (such as & becoming &amp;).
|
Chris@0
|
857 $variables['description'] = Html::decodeEntities(strip_tags($style->getDescription()));
|
Chris@0
|
858
|
Chris@0
|
859 if ($view->display_handler->getOption('sitename_title')) {
|
Chris@0
|
860 $title = $config->get('name');
|
Chris@0
|
861 if ($slogan = $config->get('slogan')) {
|
Chris@0
|
862 $title .= ' - ' . $slogan;
|
Chris@0
|
863 }
|
Chris@0
|
864 }
|
Chris@0
|
865 else {
|
Chris@0
|
866 $title = $view->getTitle();
|
Chris@0
|
867 }
|
Chris@0
|
868 $variables['title'] = $title;
|
Chris@0
|
869
|
Chris@0
|
870 // Figure out which display which has a path we're using for this feed. If
|
Chris@0
|
871 // there isn't one, use the global $base_url
|
Chris@0
|
872 $link_display_id = $view->display_handler->getLinkDisplay();
|
Chris@0
|
873 /** @var \Drupal\views\Plugin\views\display\DisplayPluginBase $display */
|
Chris@0
|
874 if ($link_display_id && ($display = $view->displayHandlers->get($link_display_id)) && $display->isEnabled()) {
|
Chris@0
|
875 $url = $view->getUrl(NULL, $link_display_id);
|
Chris@0
|
876 }
|
Chris@0
|
877
|
Chris@0
|
878 /** @var \Drupal\Core\Url $url */
|
Chris@0
|
879 if (!empty($url)) {
|
Chris@0
|
880 $url_options = ['absolute' => TRUE];
|
Chris@0
|
881 if (!empty($view->exposed_raw_input)) {
|
Chris@0
|
882 $url_options['query'] = $view->exposed_raw_input;
|
Chris@0
|
883 }
|
Chris@0
|
884
|
Chris@0
|
885 // Compare the link to the default home page; if it's the default home page,
|
Chris@0
|
886 // just use $base_url.
|
Chris@0
|
887 $url_string = $url->setOptions($url_options)->toString();
|
Chris@0
|
888 if ($url_string === Url::fromUserInput($config->get('page.front'))->toString()) {
|
Chris@0
|
889 $url_string = Url::fromRoute('<front>')->setAbsolute()->toString();
|
Chris@0
|
890 }
|
Chris@0
|
891
|
Chris@0
|
892 $variables['link'] = $url_string;
|
Chris@0
|
893 }
|
Chris@0
|
894
|
Chris@0
|
895 $variables['langcode'] = \Drupal::languageManager()->getCurrentLanguage()->getId();
|
Chris@0
|
896 $variables['namespaces'] = new Attribute($style->namespaces);
|
Chris@0
|
897 $variables['items'] = $items;
|
Chris@0
|
898 $variables['channel_elements'] = \Drupal::service('renderer')->render($style->channel_elements);
|
Chris@0
|
899
|
Chris@0
|
900 // During live preview we don't want to output the header since the contents
|
Chris@0
|
901 // of the feed are being displayed inside a normal HTML page.
|
Chris@0
|
902 if (empty($variables['view']->live_preview)) {
|
Chris@0
|
903 $variables['view']->getResponse()->headers->set('Content-Type', 'application/rss+xml; charset=utf-8');
|
Chris@0
|
904 }
|
Chris@0
|
905 }
|
Chris@0
|
906
|
Chris@0
|
907 /**
|
Chris@0
|
908 * Prepares variables for views RSS item templates.
|
Chris@0
|
909 *
|
Chris@0
|
910 * Default template: views-view-row-rss.html.twig.
|
Chris@0
|
911 *
|
Chris@0
|
912 * @param array $variables
|
Chris@0
|
913 * An associative array containing:
|
Chris@0
|
914 * - row: The raw results rows.
|
Chris@0
|
915 */
|
Chris@0
|
916 function template_preprocess_views_view_row_rss(&$variables) {
|
Chris@0
|
917 $item = $variables['row'];
|
Chris@0
|
918 $variables['title'] = $item->title;
|
Chris@0
|
919 $variables['link'] = $item->link;
|
Chris@0
|
920
|
Chris@0
|
921 // The description is the only place where we should find HTML.
|
Chris@0
|
922 // @see https://validator.w3.org/feed/docs/rss2.html#hrelementsOfLtitemgt
|
Chris@0
|
923 // If we have a render array, render it here and pass the result to the
|
Chris@0
|
924 // template, letting Twig autoescape it.
|
Chris@0
|
925 if (isset($item->description) && is_array($item->description)) {
|
Chris@0
|
926 $variables['description'] = (string) \Drupal::service('renderer')->render($item->description);
|
Chris@0
|
927 }
|
Chris@0
|
928
|
Chris@0
|
929 $variables['item_elements'] = [];
|
Chris@0
|
930 foreach ($item->elements as $element) {
|
Chris@0
|
931 if (isset($element['attributes']) && is_array($element['attributes'])) {
|
Chris@0
|
932 $element['attributes'] = new Attribute($element['attributes']);
|
Chris@0
|
933 }
|
Chris@0
|
934 $variables['item_elements'][] = $element;
|
Chris@0
|
935 }
|
Chris@0
|
936 }
|
Chris@0
|
937
|
Chris@0
|
938 /**
|
Chris@0
|
939 * Prepares variables for OPML feed templates.
|
Chris@0
|
940 *
|
Chris@0
|
941 * Default template: views-view-opml.html.twig.
|
Chris@0
|
942 *
|
Chris@0
|
943 * @param array $variables
|
Chris@0
|
944 * An associative array containing:
|
Chris@0
|
945 * - view: A ViewExecutable object.
|
Chris@0
|
946 * - rows: The raw row data.
|
Chris@0
|
947 */
|
Chris@0
|
948 function template_preprocess_views_view_opml(&$variables) {
|
Chris@17
|
949 $view = $variables['view'];
|
Chris@0
|
950 $items = $variables['rows'];
|
Chris@0
|
951
|
Chris@0
|
952 $config = \Drupal::config('system.site');
|
Chris@0
|
953
|
Chris@0
|
954 if ($view->display_handler->getOption('sitename_title')) {
|
Chris@0
|
955 $title = $config->get('name');
|
Chris@0
|
956 if ($slogan = $config->get('slogan')) {
|
Chris@0
|
957 $title .= ' - ' . $slogan;
|
Chris@0
|
958 }
|
Chris@0
|
959 }
|
Chris@0
|
960 else {
|
Chris@0
|
961 $title = $view->getTitle();
|
Chris@0
|
962 }
|
Chris@0
|
963 $variables['title'] = $title;
|
Chris@0
|
964 $variables['items'] = $items;
|
Chris@0
|
965 $variables['updated'] = gmdate(DATE_RFC2822, REQUEST_TIME);
|
Chris@0
|
966
|
Chris@0
|
967 // During live preview we don't want to output the header since the contents
|
Chris@0
|
968 // of the feed are being displayed inside a normal HTML page.
|
Chris@0
|
969 if (empty($variables['view']->live_preview)) {
|
Chris@0
|
970 $variables['view']->getResponse()->headers->set('Content-Type', 'text/xml; charset=utf-8');
|
Chris@0
|
971 }
|
Chris@0
|
972 }
|
Chris@0
|
973
|
Chris@0
|
974 /**
|
Chris@0
|
975 * Prepares variables for views OPML item templates.
|
Chris@0
|
976 *
|
Chris@0
|
977 * Default template: views-view-row-opml.html.twig.
|
Chris@0
|
978 *
|
Chris@0
|
979 * @param array $variables
|
Chris@0
|
980 * An associative array containing:
|
Chris@0
|
981 * - row: The raw results rows.
|
Chris@0
|
982 */
|
Chris@0
|
983 function template_preprocess_views_view_row_opml(&$variables) {
|
Chris@0
|
984 $item = $variables['row'];
|
Chris@0
|
985
|
Chris@0
|
986 $variables['attributes'] = new Attribute($item);
|
Chris@0
|
987 }
|
Chris@0
|
988
|
Chris@0
|
989 /**
|
Chris@0
|
990 * Prepares variables for views exposed form templates.
|
Chris@0
|
991 *
|
Chris@0
|
992 * Default template: views-exposed-form.html.twig.
|
Chris@0
|
993 *
|
Chris@0
|
994 * @param array $variables
|
Chris@0
|
995 * An associative array containing:
|
Chris@0
|
996 * - form: A render element representing the form.
|
Chris@0
|
997 */
|
Chris@0
|
998 function template_preprocess_views_exposed_form(&$variables) {
|
Chris@0
|
999 $form = &$variables['form'];
|
Chris@0
|
1000
|
Chris@0
|
1001 if (!empty($form['q'])) {
|
Chris@0
|
1002 $variables['q'] = $form['q'];
|
Chris@0
|
1003 }
|
Chris@0
|
1004
|
Chris@0
|
1005 foreach ($form['#info'] as $info) {
|
Chris@0
|
1006 if (!empty($info['label'])) {
|
Chris@0
|
1007 $form[$info['value']]['#title'] = $info['label'];
|
Chris@0
|
1008 }
|
Chris@0
|
1009 if (!empty($info['description'])) {
|
Chris@0
|
1010 $form[$info['value']]['#description'] = $info['description'];
|
Chris@0
|
1011 }
|
Chris@0
|
1012 }
|
Chris@0
|
1013 }
|
Chris@0
|
1014
|
Chris@0
|
1015 /**
|
Chris@0
|
1016 * Prepares variables for views mini-pager templates.
|
Chris@0
|
1017 *
|
Chris@0
|
1018 * Default template: views-mini-pager.html.twig.
|
Chris@0
|
1019 *
|
Chris@0
|
1020 * @param array $variables
|
Chris@0
|
1021 * An associative array containing:
|
Chris@0
|
1022 * - tags: Provides link text for the next/previous links.
|
Chris@0
|
1023 * - element: The pager's id.
|
Chris@0
|
1024 * - parameters: Any extra GET parameters that should be retained, such as
|
Chris@0
|
1025 * exposed input.
|
Chris@0
|
1026 */
|
Chris@0
|
1027 function template_preprocess_views_mini_pager(&$variables) {
|
Chris@0
|
1028 global $pager_page_array, $pager_total;
|
Chris@0
|
1029
|
Chris@0
|
1030 $tags = &$variables['tags'];
|
Chris@0
|
1031 $element = $variables['element'];
|
Chris@0
|
1032 $parameters = $variables['parameters'];
|
Chris@0
|
1033
|
Chris@0
|
1034 // Current is the page we are currently paged to.
|
Chris@0
|
1035 $variables['items']['current'] = $pager_page_array[$element] + 1;
|
Chris@0
|
1036
|
Chris@0
|
1037 if ($pager_total[$element] > 1 && $pager_page_array[$element] > 0) {
|
Chris@0
|
1038 $options = [
|
Chris@0
|
1039 'query' => pager_query_add_page($parameters, $element, $pager_page_array[$element] - 1),
|
Chris@0
|
1040 ];
|
Chris@18
|
1041 $variables['items']['previous']['href'] = Url::fromRoute('<current>', [], $options)->toString();
|
Chris@0
|
1042 if (isset($tags[1])) {
|
Chris@0
|
1043 $variables['items']['previous']['text'] = $tags[1];
|
Chris@0
|
1044 }
|
Chris@0
|
1045 $variables['items']['previous']['attributes'] = new Attribute();
|
Chris@0
|
1046 }
|
Chris@0
|
1047
|
Chris@0
|
1048 if ($pager_page_array[$element] < ($pager_total[$element] - 1)) {
|
Chris@0
|
1049 $options = [
|
Chris@0
|
1050 'query' => pager_query_add_page($parameters, $element, $pager_page_array[$element] + 1),
|
Chris@0
|
1051 ];
|
Chris@18
|
1052 $variables['items']['next']['href'] = Url::fromRoute('<current>', [], $options)->toString();
|
Chris@0
|
1053 if (isset($tags[3])) {
|
Chris@0
|
1054 $variables['items']['next']['text'] = $tags[3];
|
Chris@0
|
1055 }
|
Chris@0
|
1056 $variables['items']['next']['attributes'] = new Attribute();
|
Chris@0
|
1057 }
|
Chris@0
|
1058
|
Chris@0
|
1059 // This is based on the entire current query string. We need to ensure
|
Chris@0
|
1060 // cacheability is affected accordingly.
|
Chris@0
|
1061 $variables['#cache']['contexts'][] = 'url.query_args';
|
Chris@0
|
1062 }
|
Chris@0
|
1063
|
Chris@0
|
1064 /**
|
Chris@0
|
1065 * @defgroup views_templates Views template files
|
Chris@0
|
1066 * @{
|
Chris@0
|
1067 * Describes various views templates & overriding options.
|
Chris@0
|
1068 *
|
Chris@0
|
1069 * All views templates can be overridden with a variety of names, using
|
Chris@0
|
1070 * the view, the display ID of the view, the display type of the view,
|
Chris@0
|
1071 * or some combination thereof.
|
Chris@0
|
1072 *
|
Chris@0
|
1073 * For each view, there will be a minimum of two templates used. The first
|
Chris@0
|
1074 * is used for all views: views-view.html.twig.
|
Chris@0
|
1075 *
|
Chris@0
|
1076 * The second template is determined by the style selected for the view. Note
|
Chris@0
|
1077 * that certain aspects of the view can also change which style is used; for
|
Chris@0
|
1078 * example, arguments which provide a summary view might change the style to
|
Chris@0
|
1079 * one of the special summary styles.
|
Chris@0
|
1080 *
|
Chris@0
|
1081 * The default style for all views is views-view-unformatted.html.twig.
|
Chris@0
|
1082 *
|
Chris@0
|
1083 * Many styles will then farm out the actual display of each row to a row
|
Chris@0
|
1084 * style; the default row style is views-view-fields.html.twig.
|
Chris@0
|
1085 *
|
Chris@0
|
1086 * Here is an example of all the templates that will be tried in the following
|
Chris@0
|
1087 * case:
|
Chris@0
|
1088 *
|
Chris@0
|
1089 * View, named foobar. Style: unformatted. Row style: Fields. Display: Page.
|
Chris@0
|
1090 *
|
Chris@0
|
1091 * - views-view--foobar--page.html.twig
|
Chris@0
|
1092 * - views-view--page.html.twig
|
Chris@0
|
1093 * - views-view--foobar.html.twig
|
Chris@0
|
1094 * - views-view.html.twig
|
Chris@0
|
1095 *
|
Chris@0
|
1096 * - views-view-unformatted--foobar--page.html.twig
|
Chris@0
|
1097 * - views-view-unformatted--page.html.twig
|
Chris@0
|
1098 * - views-view-unformatted--foobar.html.twig
|
Chris@0
|
1099 * - views-view-unformatted.html.twig
|
Chris@0
|
1100 *
|
Chris@0
|
1101 * - views-view-fields--foobar--page.html.twig
|
Chris@0
|
1102 * - views-view-fields--page.html.twig
|
Chris@0
|
1103 * - views-view-fields--foobar.html.twig
|
Chris@0
|
1104 * - views-view-fields.html.twig
|
Chris@0
|
1105 *
|
Chris@0
|
1106 * Important! When adding a new template to your theme, be sure to flush the
|
Chris@0
|
1107 * theme registry cache!
|
Chris@0
|
1108 *
|
Chris@0
|
1109 * @ingroup views_overview
|
Chris@0
|
1110 * @see \Drupal\views\ViewExecutable::buildThemeFunctions()
|
Chris@0
|
1111 * @}
|
Chris@0
|
1112 */
|