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('<Any>') : 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