1<?php
2
3/**
4 * @file
5 * Preprocessors and theme functions for the Views UI.
6 */
7
8use Drupal\Component\Render\FormattableMarkup;
9use Drupal\Core\Form\FormState;
10use Drupal\Core\Render\Element;
11use Drupal\Core\Render\Element\Checkboxes;
12use Drupal\Core\Render\Element\Radios;
13use Drupal\Core\Url;
14use Drupal\Core\Template\Attribute;
15
16/**
17 * Prepares variables for Views UI display tab setting templates.
18 *
19 * Default template: views-ui-display-tab-setting.html.twig.
20 *
21 * @param array $variables
22 *   An associative array containing:
23 *   - link: The setting's primary link.
24 *   - settings_links: An array of links for this setting.
25 *   - defaulted: A boolean indicating the setting is in its default state.
26 *   - overridden: A boolean indicating the setting has been overridden from
27 *     the default.
28 *   - description: The setting's description.
29 *   - description_separator: A boolean indicating a separator colon should be
30 *     appended to the setting's description.
31 */
32function template_preprocess_views_ui_display_tab_setting(&$variables) {
33  // Put the primary link to the left side.
34  array_unshift($variables['settings_links'], $variables['link']);
35
36  if (!empty($variables['overridden'])) {
37    $variables['attributes']['title'][] = t('Overridden');
38  }
39
40  // Append a colon to the description, if requested.
41  if ($variables['description'] && $variables['description_separator']) {
42    $variables['description'] .= t(':');
43  }
44}
45
46/**
47 * Prepares variables for Views UI view listing templates.
48 *
49 * Default template: views-ui-view-listing-table.html.twig.
50 *
51 * @param array $variables
52 *   An associative array containing:
53 *   - headers: An associative array containing the headers for the view
54 *     listing table.
55 *   - rows: An associative array containing the rows data for the view
56 *     listing table.
57 */
58function template_preprocess_views_ui_views_listing_table(&$variables) {
59  // Convert the attributes to valid attribute objects.
60  foreach ($variables['headers'] as $key => $header) {
61    $variables['headers'][$key]['attributes'] = new Attribute($header['#attributes']);
62  }
63
64  if (!empty($variables['rows'])) {
65    foreach ($variables['rows'] as $key => $row) {
66      $variables['rows'][$key]['attributes'] = new Attribute($row['#attributes']);
67    }
68  }
69}
70
71/**
72 * Prepares variables for Views UI display tab bucket templates.
73 *
74 * Default template: views-ui-display-tab-bucket.html.twig.
75 *
76 * @param array $variables
77 *   An associative array containing:
78 *   - element: An associative array containing the properties of the element.
79 *     Properties used: #name, #overridden, #children, #title, #actions.
80 */
81function template_preprocess_views_ui_display_tab_bucket(&$variables) {
82  $element = $variables['element'];
83
84  if (!empty($element['#overridden'])) {
85    $variables['attributes']['title'][] = t('Overridden');
86  }
87
88  $variables['name'] = isset($element['#name']) ? $element['#name'] : NULL;
89  $variables['overridden'] = isset($element['#overridden']) ? $element['#overridden'] : NULL;
90  $variables['content'] = $element['#children'];
91  $variables['title'] = $element['#title'];
92  $variables['actions'] = !empty($element['#actions']) ? $element['#actions'] : [];
93}
94
95/**
96 * Prepares variables for Views UI build group filter form templates.
97 *
98 * Default template: views-ui-build-group-filter-form.html.twig.
99 *
100 * @param array $variables
101 *   An associative array containing:
102 *   - form: A render element representing the form.
103 */
104function template_preprocess_views_ui_build_group_filter_form(&$variables) {
105  $form = $variables['form'];
106
107  // Prepare table of options.
108  $header = [
109    t('Default'),
110    t('Weight'),
111    t('Label'),
112    t('Operator'),
113    t('Value'),
114    t('Operations'),
115  ];
116
117  // Prepare default selectors.
118  $form_state = new FormState();
119  $form['default_group'] = Radios::processRadios($form['default_group'], $form_state, $form);
120  $form['default_group_multiple'] = Checkboxes::processCheckboxes($form['default_group_multiple'], $form_state, $form);
121  $form['default_group']['All']['#title'] = '';
122
123  $rows[] = [
124    ['data' => $form['default_group']['All']],
125    '',
126    [
127      'data' => \Drupal::config('views.settings')->get('ui.exposed_filter_any_label') == 'old_any' ? t('&lt;Any&gt;') : t('- Any -'),
128      'colspan' => 4,
129      'class' => ['class' => 'any-default-radios-row'],
130    ],
131  ];
132  // Remove the 'All' default_group form element because it's added to the
133  // table row.
134  unset($variables['form']['default_group']['All']);
135
136  foreach (Element::children($form['group_items']) as $group_id) {
137    $form['group_items'][$group_id]['value']['#title'] = '';
138    $default = [
139      $form['default_group'][$group_id],
140      $form['default_group_multiple'][$group_id],
141    ];
142    // Remove these fields from the form since they are moved into the table.
143    unset($variables['form']['default_group'][$group_id]);
144    unset($variables['form']['default_group_multiple'][$group_id]);
145
146    $link = [
147      '#type' => 'link',
148      '#url' => Url::fromRoute('<none>', [], [
149        'attributes' => [
150          'id' => 'views-remove-link-' . $group_id,
151          'class' => [
152            'views-hidden',
153            'views-button-remove',
154            'views-groups-remove-link',
155            'views-remove-link',
156          ],
157          'alt' => t('Remove this item'),
158          'title' => t('Remove this item'),
159        ],
160      ]),
161      '#title' => new FormattableMarkup('<span>@text</span>', ['@text' => t('Remove')]),
162    ];
163    $remove = [$form['group_items'][$group_id]['remove'], $link];
164    $data = [
165      'default' => ['data' => $default],
166      'weight' => ['data' => $form['group_items'][$group_id]['weight']],
167      'title' => ['data' => $form['group_items'][$group_id]['title']],
168      'operator' => ['data' => $form['group_items'][$group_id]['operator']],
169      'value' => ['data' => $form['group_items'][$group_id]['value']],
170      'remove' => ['data' => $remove],
171    ];
172    $rows[] = ['data' => $data, 'id' => 'views-row-' . $group_id, 'class' => ['draggable']];
173  }
174  $variables['table'] = [
175    '#type' => 'table',
176    '#header' => $header,
177    '#rows' => $rows,
178    '#attributes' => [
179      'class' => ['views-filter-groups'],
180      'id' => 'views-filter-groups',
181    ],
182    '#tabledrag' => [
183      [
184        'action' => 'order',
185        'relationship' => 'sibling',
186        'group' => 'weight',
187      ],
188    ],
189  ];
190
191  // Hide fields used in table.
192  unset($variables['form']['group_items']);
193}
194
195/**
196 * Prepares variables for Views UI rearrange filter form templates.
197 *
198 * Default template: views-ui-rearrange-filter-form.html.twig.
199 *
200 * @param array $variables
201 *   An associative array containing:
202 *   - form: A render element representing the form.
203 */
204function template_preprocess_views_ui_rearrange_filter_form(&$variables) {
205  $form = &$variables['form'];
206  $rows = $ungroupable_rows = [];
207  // Enable grouping only if > 1 group.
208  $variables['grouping'] = count(array_keys($form['#group_options'])) > 1;
209
210  foreach ($form['#group_renders'] as $group_id => $contents) {
211    // Header row for the group.
212    if ($group_id !== 'ungroupable') {
213      // Set up tabledrag so that it changes the group dropdown when rows are
214      // dragged between groups.
215      $options = [
216        'table_id' => 'views-rearrange-filters',
217        'action' => 'match',
218        'relationship' => 'sibling',
219        'group' => 'views-group-select',
220        'subgroup' => 'views-group-select-' . $group_id,
221      ];
222      drupal_attach_tabledrag($form['override'], $options);
223
224      // Title row, spanning all columns.
225      $row = [];
226      // Add a cell to the first row, containing the group operator.
227      $row[] = [
228        'class' => ['group', 'group-operator', 'container-inline'],
229        'data' => $form['filter_groups']['groups'][$group_id],
230        'rowspan' => max([2, count($contents) + 1]),
231      ];
232      // Title.
233      $row[] = [
234        'class' => ['group', 'group-title'],
235        'data' => [
236          '#prefix' => '<span>',
237          '#markup' => $form['#group_options'][$group_id],
238          '#suffix' => '</span>',
239        ],
240        'colspan' => 4,
241      ];
242      $rows[] = [
243        'class' => ['views-group-title'],
244        'data' => $row,
245        'id' => 'views-group-title-' . $group_id,
246      ];
247
248      // Row which will only appear if the group has nothing in it.
249      $row = [];
250      $class = 'group-' . (count($contents) ? 'populated' : 'empty');
251      $instructions = '<span>' . t('No filters have been added.') . '</span> <span class="js-only">' . t('Drag to add filters.') . '</span>';
252      // When JavaScript is enabled, the button for removing the group (if it's
253      // present) should be hidden, since it will be replaced by a link on the
254      // client side.
255      if (!empty($form['remove_groups'][$group_id]['#type']) && $form['remove_groups'][$group_id]['#type'] == 'submit') {
256        $form['remove_groups'][$group_id]['#attributes']['class'][] = 'js-hide';
257      }
258      $row[] = [
259        'colspan' => 5,
260        'data' => [
261          ['#markup' => $instructions],
262          $form['remove_groups'][$group_id],
263        ],
264      ];
265      $rows[] = [
266        'class' => [
267          'group-message',
268          'group-' . $group_id . '-message',
269          $class,
270        ],
271        'data' => $row,
272        'id' => 'views-group-' . $group_id,
273      ];
274    }
275
276    foreach ($contents as $id) {
277      if (isset($form['filters'][$id]['name'])) {
278        $row = [];
279        $row[]['data'] = $form['filters'][$id]['name'];
280        $form['filters'][$id]['weight']['#attributes']['class'] = ['weight'];
281        $row[]['data'] = $form['filters'][$id]['weight'];
282        $form['filters'][$id]['group']['#attributes']['class'] = ['views-group-select views-group-select-' . $group_id];
283        $row[]['data'] = $form['filters'][$id]['group'];
284        $form['filters'][$id]['removed']['#attributes']['class'][] = 'js-hide';
285
286        $remove_link = [
287          '#type' => 'link',
288          '#url' => Url::fromRoute('<none>'),
289          '#title' => new FormattableMarkup('<span>@text</span>', ['@text' => t('Remove')]),
290          '#weight' => '1',
291          '#options' => [
292            'attributes' => [
293              'id' => 'views-remove-link-' . $id,
294              'class' => [
295                'views-hidden',
296                'views-button-remove',
297                'views-groups-remove-link',
298                'views-remove-link',
299              ],
300              'alt' => t('Remove this item'),
301              'title' => t('Remove this item'),
302            ],
303          ],
304        ];
305        $row[]['data'] = [
306          $form['filters'][$id]['removed'],
307          $remove_link,
308        ];
309
310        $row = [
311          'data' => $row,
312          'class' => ['draggable'],
313          'id' => 'views-row-' . $id,
314        ];
315
316        if ($group_id !== 'ungroupable') {
317          $rows[] = $row;
318        }
319        else {
320          $ungroupable_rows[] = $row;
321        }
322      }
323    }
324  }
325
326  if (!$variables['grouping']) {
327    $form['filter_groups']['groups'][0]['#title'] = t('Operator');
328  }
329
330  if (!empty($ungroupable_rows)) {
331    $header = [
332      t('Ungroupable filters'),
333      t('Weight'),
334      [
335        'data' => t('Group'),
336        'class' => ['views-hide-label'],
337      ],
338      [
339        'data' => t('Remove'),
340        'class' => ['views-hide-label'],
341      ],
342    ];
343    $variables['ungroupable_table'] = [
344      '#type' => 'table',
345      '#header' => $header,
346      '#rows' => $ungroupable_rows,
347      '#attributes' => [
348        'id' => 'views-rearrange-filters-ungroupable',
349        'class' => ['arrange'],
350      ],
351      '#tabledrag' => [
352        [
353          'action' => 'order',
354          'relationship' => 'sibling',
355          'group' => 'weight',
356        ],
357      ],
358    ];
359  }
360
361  if (empty($rows)) {
362    $rows[] = [['data' => t('No fields available.'), 'colspan' => '2']];
363  }
364
365  // Set up tabledrag so that the weights are changed when rows are dragged.
366  $variables['table'] = [
367    '#type' => 'table',
368    '#rows' => $rows,
369    '#attributes' => [
370      'id' => 'views-rearrange-filters',
371      'class' => ['arrange'],
372    ],
373    '#tabledrag' => [
374      [
375        'action' => 'order',
376        'relationship' => 'sibling',
377        'group' => 'weight',
378      ],
379    ],
380  ];
381
382  // When JavaScript is enabled, the button for adding a new group should be
383  // hidden, since it will be replaced by a link on the client side.
384  $form['actions']['add_group']['#attributes']['class'][] = 'js-hide';
385
386}
387
388/**
389 * Prepares variables for style plugin table templates.
390 *
391 * Default template: views-ui-style-plugin-table.html.twig.
392 *
393 * @param array $variables
394 *   An associative array containing:
395 *   - form: A render element representing the form.
396 */
397function template_preprocess_views_ui_style_plugin_table(&$variables) {
398  $form = $variables['form'];
399
400  $header = [
401    t('Field'),
402    t('Column'),
403    t('Align'),
404    t('Separator'),
405    [
406      'data' => t('Sortable'),
407      'align' => 'center',
408    ],
409    [
410      'data' => t('Default order'),
411      'align' => 'center',
412    ],
413    [
414      'data' => t('Default sort'),
415      'align' => 'center',
416    ],
417    [
418      'data' => t('Hide empty column'),
419      'align' => 'center',
420    ],
421    [
422      'data' => t('Responsive'),
423      'align' => 'center',
424    ],
425  ];
426  $rows = [];
427  foreach (Element::children($form['columns']) as $id) {
428    $row = [];
429    $row[]['data'] = $form['info'][$id]['name'];
430    $row[]['data'] = $form['columns'][$id];
431    $row[]['data'] = $form['info'][$id]['align'];
432    $row[]['data'] = $form['info'][$id]['separator'];
433
434    if (!empty($form['info'][$id]['sortable'])) {
435      $row[] = [
436        'data' => $form['info'][$id]['sortable'],
437        'align' => 'center',
438      ];
439      $row[] = [
440        'data' => $form['info'][$id]['default_sort_order'],
441        'align' => 'center',
442      ];
443      $row[] = [
444        'data' => $form['default'][$id],
445        'align' => 'center',
446      ];
447    }
448    else {
449      $row[] = '';
450      $row[] = '';
451      $row[] = '';
452    }
453    $row[] = [
454      'data' => $form['info'][$id]['empty_column'],
455      'align' => 'center',
456    ];
457    $row[] = [
458      'data' => $form['info'][$id]['responsive'],
459      'align' => 'center',
460    ];
461    $rows[] = $row;
462  }
463
464  // Add the special 'None' row.
465  $rows[] = [['data' => t('None'), 'colspan' => 6], ['align' => 'center', 'data' => $form['default'][-1]], ['colspan' => 2]];
466
467  // Unset elements from the form array that are used to build the table so that
468  // they are not rendered twice.
469  unset($form['default']);
470  unset($form['info']);
471  unset($form['columns']);
472
473  $variables['table'] = [
474    '#type' => 'table',
475    '#theme' => 'table__views_ui_style_plugin_table',
476    '#header' => $header,
477    '#rows' => $rows,
478  ];
479  $variables['form'] = $form;
480}
481
482/**
483 * Prepares variables for views UI view preview section templates.
484 *
485 * Default template: views-ui-view-preview-section.html.twig.
486 *
487 * @param array $variables
488 *   An associative array containing:
489 *   - view: The view object.
490 *   - section: The section name of a View (e.g. title, rows or pager).
491 */
492function template_preprocess_views_ui_view_preview_section(&$variables) {
493  switch ($variables['section']) {
494    case 'title':
495      $variables['title'] = t('Title');
496      $links = views_ui_view_preview_section_display_category_links($variables['view'], 'title', $variables['title']);
497      break;
498
499    case 'header':
500      $variables['title'] = t('Header');
501      $links = views_ui_view_preview_section_handler_links($variables['view'], $variables['section']);
502      break;
503
504    case 'empty':
505      $variables['title'] = t('No results behavior');
506      $links = views_ui_view_preview_section_handler_links($variables['view'], $variables['section']);
507      break;
508
509    case 'exposed':
510      // @todo Sorts can be exposed too, so we may need a better title.
511      $variables['title'] = t('Exposed Filters');
512      $links = views_ui_view_preview_section_display_category_links($variables['view'], 'exposed_form_options', $variables['title']);
513      break;
514
515    case 'rows':
516      // @todo The title needs to depend on what is being viewed.
517      $variables['title'] = t('Content');
518      $links = views_ui_view_preview_section_rows_links($variables['view']);
519      break;
520
521    case 'pager':
522      $variables['title'] = t('Pager');
523      $links = views_ui_view_preview_section_display_category_links($variables['view'], 'pager_options', $variables['title']);
524      break;
525
526    case 'more':
527      $variables['title'] = t('More');
528      $links = views_ui_view_preview_section_display_category_links($variables['view'], 'use_more', $variables['title']);
529      break;
530
531    case 'footer':
532      $variables['title'] = t('Footer');
533      $links = views_ui_view_preview_section_handler_links($variables['view'], $variables['section']);
534      break;
535
536    case 'attachment_before':
537      // @todo: Add links to the attachment configuration page.
538      $variables['title'] = t('Attachment before');
539      break;
540
541    case 'attachment_after':
542      // @todo: Add links to the attachment configuration page.
543      $variables['title'] = t('Attachment after');
544      break;
545  }
546
547  if (isset($links)) {
548    $build = [
549      '#theme' => 'links__contextual',
550      '#links' => $links,
551      '#attributes' => ['class' => ['contextual-links']],
552      '#attached' => [
553        'library' => ['contextual/drupal.contextual-links'],
554      ],
555    ];
556    $variables['links'] = $build;
557  }
558}
559