views_ui.module 13.5 KB
Newer Older
merlinofchaos's avatar
merlinofchaos committed
1 2 3 4 5 6 7
<?php

/**
 * @file
 * Provide structure for the administrative interface to Views.
 */

8
use Drupal\Core\Routing\RouteMatchInterface;
9
use Drupal\Core\Url;
10
use Drupal\views\ViewExecutable;
11
use Drupal\views\Analyzer;
12

13 14 15
/**
 * Implements hook_help().
 */
16
function views_ui_help($route_name, RouteMatchInterface $route_match) {
17 18
  switch ($route_name) {
    case 'help.page.views_ui':
19 20
      $output = '';
      $output .= '<h3>' . t('About') . '</h3>';
21
      $output .= '<p>' . t('The Views UI module provides an interface for managing views for the <a href=":views">Views module</a>. For more information, see the <a href=":handbook">online documentation for the Views UI module</a>.', [':views' => \Drupal::url('help.page', ['name' => 'views']), ':handbook' => 'https://www.drupal.org/documentation/modules/views_ui']) . '</p>';
22 23
      $output .= '<h3>' . t('Uses') . '</h3>';
      $output .= '<dl>';
24
      $output .= '<dt>' . t('Creating and managing views') . '</dt>';
25
      $output .= '<dd>' . t('Views can be created from the <a href=":list">Views list page</a> by using the "Add view" action. Existing views can be managed from the <a href=":list">Views list page</a> by locating the view in the "Enabled" or "Disabled" list and selecting the desired operation action, for example "Edit".', [':list' => \Drupal::url('entity.view.collection', ['name' => 'views_ui'])]) . '</dd>';
26
      $output .= '<dt>' . t('Enabling and disabling views') . '<dt>';
27
      $output .= '<dd>' . t('Views can be enabled or disabled from the <a href=":list">Views list page</a>. To enable a view, find the view within the "Disabled" list and select the "Enable" operation. To disable a view find the view within the "Enabled" list and select the "Disable" operation.', [':list' => \Drupal::url('entity.view.collection', ['name' => 'views_ui'])]) . '</dd>';
28
      $output .= '<dt>' . t('Exporting and importing views') . '</dt>';
29
      $output .= '<dd>' . t('Views can be exported and imported as configuration files by using the <a href=":config">Configuration Manager module</a>.', [':config' => (\Drupal::moduleHandler()->moduleExists('config')) ? \Drupal::url('help.page', ['name' => 'config']) : '#']) . '</dd>';
30 31 32 33 34
      $output .= '</dl>';
      return $output;
  }
}

35
/**
36
 * Implements hook_entity_type_build().
37
 */
38 39 40
function views_ui_entity_type_build(array &$entity_types) {
  /** @var $entity_types \Drupal\Core\Entity\EntityTypeInterface[] */
  $entity_types['view']
41 42 43
    ->setFormClass('edit', 'Drupal\views_ui\ViewEditForm')
    ->setFormClass('add', 'Drupal\views_ui\ViewAddForm')
    ->setFormClass('preview', 'Drupal\views_ui\ViewPreviewForm')
44
    ->setFormClass('duplicate', 'Drupal\views_ui\ViewDuplicateForm')
45
    ->setFormClass('delete', 'Drupal\Core\Entity\EntityDeleteForm')
46
    ->setFormClass('break_lock', 'Drupal\views_ui\Form\BreakLockForm')
47
    ->setListBuilderClass('Drupal\views_ui\ViewListBuilder')
48 49 50 51 52 53 54
    ->setLinkTemplate('edit-form', '/admin/structure/views/view/{view}')
    ->setLinkTemplate('edit-display-form', '/admin/structure/views/view/{view}/edit/{display_id}')
    ->setLinkTemplate('preview-form', '/admin/structure/views/view/{view}/preview/{display_id}')
    ->setLinkTemplate('duplicate-form', '/admin/structure/views/view/{view}/duplicate')
    ->setLinkTemplate('delete-form', '/admin/structure/views/view/{view}/delete')
    ->setLinkTemplate('enable', '/admin/structure/views/view/{view}/enable')
    ->setLinkTemplate('disable', '/admin/structure/views/view/{view}/disable')
55 56
    ->setLinkTemplate('break-lock-form', '/admin/structure/views/view/{view}/break-lock')
    ->setLinkTemplate('collection', '/admin/structure/views');
57 58
}

merlinofchaos's avatar
merlinofchaos committed
59 60 61 62
/**
 * Implements hook_theme().
 */
function views_ui_theme() {
63
  return [
merlinofchaos's avatar
merlinofchaos committed
64
    // edit a view
65 66
    'views_ui_display_tab_setting' => [
      'variables' => ['description' => '', 'link' => '', 'settings_links' => [], 'overridden' => FALSE, 'defaulted' => FALSE, 'description_separator' => TRUE, 'class' => []],
67
      'file' => 'views_ui.theme.inc',
68 69
    ],
    'views_ui_display_tab_bucket' => [
merlinofchaos's avatar
merlinofchaos committed
70
      'render element' => 'element',
71
      'file' => 'views_ui.theme.inc',
72 73
    ],
    'views_ui_rearrange_filter_form' => [
merlinofchaos's avatar
merlinofchaos committed
74
      'render element' => 'form',
75
      'file' => 'views_ui.theme.inc',
76 77
    ],
    'views_ui_expose_filter_form' => [
merlinofchaos's avatar
merlinofchaos committed
78
      'render element' => 'form',
79
      'file' => 'views_ui.theme.inc',
80
    ],
merlinofchaos's avatar
merlinofchaos committed
81

82
    // Legacy theme hook for displaying views info.
83 84
    'views_ui_view_info' => [
      'variables' => ['view' => NULL, 'displays' => NULL],
85
      'file' => 'views_ui.theme.inc',
86
    ],
merlinofchaos's avatar
merlinofchaos committed
87

88
    // List views.
89 90
    'views_ui_views_listing_table' => [
      'variables' => [
91 92
        'headers' => NULL,
        'rows' => NULL,
93 94
        'attributes' => [],
      ],
95
      'file' => 'views_ui.theme.inc',
96 97 98 99
    ],
    'views_ui_view_displays_list' => [
      'variables' => ['displays' => []],
    ],
100

101
    // Group of filters.
102
    'views_ui_build_group_filter_form' => [
103
      'render element' => 'form',
104
      'file' => 'views_ui.theme.inc',
105
    ],
106

merlinofchaos's avatar
merlinofchaos committed
107
    // On behalf of a plugin
108
    'views_ui_style_plugin_table' => [
merlinofchaos's avatar
merlinofchaos committed
109
      'render element' => 'form',
110
      'file' => 'views_ui.theme.inc',
111
    ],
merlinofchaos's avatar
merlinofchaos committed
112 113

    // When previewing a view.
114 115
    'views_ui_view_preview_section' => [
      'variables' => ['view' => NULL, 'section' => NULL, 'content' => NULL, 'links' => ''],
116
      'file' => 'views_ui.theme.inc',
117
    ],
merlinofchaos's avatar
merlinofchaos committed
118 119 120

    // Generic container wrapper, to use instead of theme_container when an id
    // is not desired.
121 122
    'views_ui_container' => [
      'variables' => ['children' => NULL, 'attributes' => []],
123
      'file' => 'views_ui.theme.inc',
124 125
    ],
  ];
merlinofchaos's avatar
merlinofchaos committed
126 127 128
}

/**
129
 * Implements hook_preprocess_HOOK() for views templates.
merlinofchaos's avatar
merlinofchaos committed
130
 */
131 132
function views_ui_preprocess_views_view(&$variables) {
  $view = $variables['view'];
133 134 135

  // Render title for the admin preview.
  if (!empty($view->live_preview)) {
136
    $variables['title'] = [
137
      '#markup' => $view->getTitle(),
138
    ];
139 140
  }

141
  if (!empty($view->live_preview) && \Drupal::moduleHandler()->moduleExists('contextual')) {
142
    $view->setShowAdminLinks(FALSE);
143
    foreach (['title', 'header', 'exposed', 'rows', 'pager', 'more', 'footer', 'empty', 'attachment_after', 'attachment_before'] as $section) {
144
      if (!empty($variables[$section])) {
145
        $variables[$section] = [
merlinofchaos's avatar
merlinofchaos committed
146 147 148
          '#theme' => 'views_ui_view_preview_section',
          '#view' => $view,
          '#section' => $section,
149
          '#content' => $variables[$section],
150 151 152
          '#theme_wrappers' => ['views_ui_container'],
          '#attributes' => ['class' => ['contextual-region']],
        ];
merlinofchaos's avatar
merlinofchaos committed
153 154 155 156 157
      }
    }
  }
}

158 159 160 161 162 163 164
/**
 * Implements hook_theme_suggestions_HOOK().
 */
function views_ui_theme_suggestions_views_ui_view_preview_section(array $variables) {
  return ['views_ui_view_preview_section__' . $variables['section']];
}

merlinofchaos's avatar
merlinofchaos committed
165 166 167 168 169 170 171 172 173 174
/**
 * Returns contextual links for each handler of a certain section.
 *
 * @TODO
 *   Bring in relationships
 *   Refactor this function to use much stuff of views_ui_edit_form_get_bucket.
 *
 * @param $title
 *   Add a bolded title of this section.
 */
175 176 177
function views_ui_view_preview_section_handler_links(ViewExecutable $view, $type, $title = FALSE) {
  $display = $view->display_handler->display;
  $handlers = $view->display_handler->getHandlers($type);
178
  $links = [];
merlinofchaos's avatar
merlinofchaos committed
179

180
  $types = ViewExecutable::getHandlerTypes();
merlinofchaos's avatar
merlinofchaos committed
181
  if ($title) {
182
    $links[$type . '-title'] = [
merlinofchaos's avatar
merlinofchaos committed
183
      'title' => $types[$type]['title'],
184
    ];
merlinofchaos's avatar
merlinofchaos committed
185 186 187
  }

  foreach ($handlers as $id => $handler) {
188
    $field_name = $handler->adminLabel(TRUE);
189 190
    $links[$type . '-edit-' . $id] = [
      'title' => t('Edit @section', ['@section' => $field_name]),
191
      'url' => Url::fromRoute('views_ui.form_handler', ['js' => 'nojs', 'view' => $view->storage->id(), 'display_id' => $display['id'], 'type' => $type, 'id' => $id]),
192 193
      'attributes' => ['class' => ['views-ajax-link']],
    ];
merlinofchaos's avatar
merlinofchaos committed
194
  }
195
  $links[$type . '-add'] = [
merlinofchaos's avatar
merlinofchaos committed
196
    'title' => t('Add new'),
197
    'url' => Url::fromRoute('views_ui.form_add_handler', ['js' => 'nojs', 'view' => $view->storage->id(), 'display_id' => $display['id'], 'type' => $type]),
198 199
    'attributes' => ['class' => ['views-ajax-link']],
  ];
merlinofchaos's avatar
merlinofchaos committed
200 201 202 203 204 205 206

  return $links;
}

/**
 * Returns a link to editing a certain display setting.
 */
207
function views_ui_view_preview_section_display_category_links(ViewExecutable $view, $type, $title) {
merlinofchaos's avatar
merlinofchaos committed
208
  $display = $view->display_handler->display;
209 210 211
  $links = [
    $type . '-edit' => [
      'title' => t('Edit @section', ['@section' => $title]),
212
      'url' => Url::fromRoute('views_ui.form_display', ['js' => 'nojs', 'view' => $view->storage->id(), 'display_id' => $display['id'], 'type' => $type]),
213 214 215
      'attributes' => ['class' => ['views-ajax-link']],
    ],
  ];
merlinofchaos's avatar
merlinofchaos committed
216 217 218 219 220 221 222

  return $links;
}

/**
 * Returns all contextual links for the main content part of the view.
 */
223
function views_ui_view_preview_section_rows_links(ViewExecutable $view) {
224
  $links = [];
merlinofchaos's avatar
merlinofchaos committed
225 226 227 228 229 230 231 232 233 234
  $links = array_merge($links, views_ui_view_preview_section_handler_links($view, 'filter', TRUE));
  $links = array_merge($links, views_ui_view_preview_section_handler_links($view, 'field', TRUE));
  $links = array_merge($links, views_ui_view_preview_section_handler_links($view, 'sort', TRUE));
  $links = array_merge($links, views_ui_view_preview_section_handler_links($view, 'argument', TRUE));
  $links = array_merge($links, views_ui_view_preview_section_handler_links($view, 'relationship', TRUE));

  return $links;
}

/**
235
 * Implements hook_views_plugins_display_alter().
merlinofchaos's avatar
merlinofchaos committed
236
 */
237
function views_ui_views_plugins_display_alter(&$plugins) {
merlinofchaos's avatar
merlinofchaos committed
238
  // Attach contextual links to each display plugin. The links will point to
239
  // paths underneath "admin/structure/views/view/{$view->id()}" (i.e., paths
merlinofchaos's avatar
merlinofchaos committed
240
  // for editing and performing other contextual actions on the view).
241
  foreach ($plugins as &$display) {
242
    $display['contextual links']['entity.view.edit_form'] = [
243
      'route_name' => 'entity.view.edit_form',
244 245
      'route_parameters_names' => ['view' => 'id'],
    ];
merlinofchaos's avatar
merlinofchaos committed
246 247 248 249 250 251 252 253 254 255
  }
}

/**
 * Implements hook_contextual_links_view_alter().
 */
function views_ui_contextual_links_view_alter(&$element, $items) {
  // Remove contextual links from being rendered, when so desired, such as
  // within a View preview.
  if (views_ui_contextual_links_suppress()) {
256
    $element['#links'] = [];
merlinofchaos's avatar
merlinofchaos committed
257 258 259 260
  }
  // Append the display ID to the Views UI edit links, so that clicking on the
  // contextual link takes you directly to the correct display tab on the edit
  // screen.
261 262
  elseif (!empty($element['#links']['entityviewedit-form'])) {
    $display_id = $items['entity.view.edit_form']['metadata']['display_id'];
263 264
    $route_parameters = $element['#links']['entityviewedit-form']['url']->getRouteParameters() + ['display_id' => $display_id];
    $element['#links']['entityviewedit-form']['url'] = Url::fromRoute('entity.view.edit_display_form', $route_parameters);
merlinofchaos's avatar
merlinofchaos committed
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
  }
}

/**
 * Sets a static variable for controlling whether contextual links are rendered.
 *
 * @see views_ui_contextual_links_view_alter()
 */
function views_ui_contextual_links_suppress($set = NULL) {
  $suppress = &drupal_static(__FUNCTION__);
  if (isset($set)) {
    $suppress = $set;
  }
  return $suppress;
}

/**
 * Increments the views_ui_contextual_links_suppress() static variable.
 *
 * When this function is added to the #pre_render of an element, and
 * 'views_ui_contextual_links_suppress_pop' is added to the #post_render of the
 * same element, then all contextual links within the element and its
 * descendants are suppressed from being rendered. This is used, for example,
 * during a View preview, when it is not desired for nodes in the Views result
 * to have contextual links.
 *
 * @see views_ui_contextual_links_suppress_pop()
 */
function views_ui_contextual_links_suppress_push() {
294
  views_ui_contextual_links_suppress(((int) views_ui_contextual_links_suppress()) + 1);
merlinofchaos's avatar
merlinofchaos committed
295 296 297 298 299 300 301 302
}

/**
 * Decrements the views_ui_contextual_links_suppress() static variable.
 *
 * @see views_ui_contextual_links_suppress_push()
 */
function views_ui_contextual_links_suppress_pop() {
303
  views_ui_contextual_links_suppress(((int) views_ui_contextual_links_suppress()) - 1);
merlinofchaos's avatar
merlinofchaos committed
304 305
}

306 307 308 309 310 311 312
/**
 * Implements hook_views_analyze().
 *
 * This is the basic views analysis that checks for very minimal problems.
 * There are other analysis tools in core specific sections, such as
 * node.views.inc as well.
 */
313
function views_ui_views_analyze(ViewExecutable $view) {
314
  $ret = [];
315 316 317 318 319 320 321 322 323 324 325 326
  // Check for something other than the default display:
  if (count($view->displayHandlers) < 2) {
    $ret[] = Analyzer::formatMessage(t('This view has only a default display and therefore will not be placed anywhere on your site; perhaps you want to add a page or a block display.'), 'warning');
  }
  // You can give a page display the same path as an alias existing in the
  // system, so the alias will not work anymore. Report this to the user,
  // because he probably wanted something else.
  foreach ($view->displayHandlers as $display) {
    if (empty($display)) {
      continue;
    }
    if ($display->hasPath() && $path = $display->getOption('path')) {
327
      $normal_path = \Drupal::service('path.alias_manager')->getPathByAlias($path);
328
      if ($path != $normal_path) {
329
        $ret[] = Analyzer::formatMessage(t('You have configured display %display with a path which is an path alias as well. This might lead to unwanted effects so better use an internal path.', ['%display' => $display->display['display_title']]), 'warning');
330 331 332 333 334 335 336
      }
    }
  }

  return $ret;
}

merlinofchaos's avatar
merlinofchaos committed
337
/**
338
 * Truncate strings to a set length and provide a '...' if they truncated.
merlinofchaos's avatar
merlinofchaos committed
339 340 341 342
 *
 * This is often used in the UI to ensure long strings fit.
 */
function views_ui_truncate($string, $length) {
343 344
  if (mb_strlen($string) > $length) {
    $string = mb_substr($string, 0, $length);
merlinofchaos's avatar
merlinofchaos committed
345 346 347 348 349
    $string .= '...';
  }

  return $string;
}