views_ui.module 13.7 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\Component\Utility\Unicode;
9
use Drupal\Core\Routing\RouteMatchInterface;
10
use Drupal\Core\Url;
11
use Drupal\views\Views;
12
use Drupal\views\ViewExecutable;
13
use Drupal\views_ui\ViewUI;
14
use Drupal\views\Analyzer;
15 16
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\ReplaceCommand;
17
use Drupal\Component\Utility\Xss;
18

19 20 21
/**
 * Implements hook_help().
 */
22
function views_ui_help($route_name, RouteMatchInterface $route_match) {
23 24
  switch ($route_name) {
    case 'help.page.views_ui':
25 26 27 28 29 30
      $output = '';
      $output .= '<h3>' . t('About') . '</h3>';
      $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>.', array('@views' => \Drupal::url('help.page', array('name' => 'views')), '@handbook' => 'https://drupal.org/documentation/modules/views_ui')) . '</p>';
      $output .= '<h3>' . t('Uses') . '</h3>';
      $output .= '<dl>';
      $output .= '<dt>' . t('Creating and managing views.') . '</dt>';
31
      $output .= '<dd>' . t('Views can be created from the <a href="@list">Views list page</a> by using the "Add new 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".', array('@list' => \Drupal::url('entity.view.collection', array('name' => 'views_ui')))) . '</dd>';
32
      $output .= '<dt>' . t('Enabling and disabling views.') . '<dt>';
33
      $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.', array('@list' => \Drupal::url('entity.view.collection', array('name' => 'views_ui')))) . '</dd>';
34 35 36 37 38 39 40 41 42
      $output .= '<dt>' . t('Exporting and importing views.') . '</dt>';
      $output .= '<dd>' . t('Views can be exported and imported as configuration files by using the <a href="@config">Configuration Manager module</a>.', array('@config' => \Drupal::url('help.page', array('name' => 'config')))) . '</dd>';
      $output .= '<dt>' . t('Theming views.') . '</dt>';
      $output .= '<dd>' . t('The template files used by views can be overridden from a custom theme. When editing a view, you can see the templates that are used and alternatives for overriding them by clicking on the "Templates" link, found in the Advanced &gt; Other section under "Output".') . '</dd>';
      $output .= '</dl>';
      return $output;
  }
}

43
/**
44
 * Implements hook_entity_type_build().
45
 */
46 47 48
function views_ui_entity_type_build(array &$entity_types) {
  /** @var $entity_types \Drupal\Core\Entity\EntityTypeInterface[] */
  $entity_types['view']
49 50 51
    ->setFormClass('edit', 'Drupal\views_ui\ViewEditForm')
    ->setFormClass('add', 'Drupal\views_ui\ViewAddForm')
    ->setFormClass('preview', 'Drupal\views_ui\ViewPreviewForm')
52
    ->setFormClass('duplicate', 'Drupal\views_ui\ViewDuplicateForm')
53
    ->setFormClass('delete', 'Drupal\Core\Entity\EntityDeleteForm')
54
    ->setFormClass('break_lock', 'Drupal\views_ui\Form\BreakLockForm')
55
    ->setListBuilderClass('Drupal\views_ui\ViewListBuilder')
56 57 58 59 60 61 62
    ->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')
63 64
    ->setLinkTemplate('break-lock-form', '/admin/structure/views/view/{view}/break-lock')
    ->setLinkTemplate('collection', '/admin/structure/views');
65 66
}

merlinofchaos's avatar
merlinofchaos committed
67 68 69 70 71 72 73 74
/**
 * Implements hook_theme().
 */
function views_ui_theme() {
  return array(
    // edit a view
    'views_ui_display_tab_setting' => array(
      'variables' => array('description' => '', 'link' => '', 'settings_links' => array(), 'overridden' => FALSE, 'defaulted' => FALSE, 'description_separator' => TRUE, 'class' => array()),
75
      'file' => 'views_ui.theme.inc',
merlinofchaos's avatar
merlinofchaos committed
76 77 78
    ),
    'views_ui_display_tab_bucket' => array(
      'render element' => 'element',
79
      'file' => 'views_ui.theme.inc',
merlinofchaos's avatar
merlinofchaos committed
80 81 82
    ),
    'views_ui_rearrange_filter_form' => array(
      'render element' => 'form',
83
      'file' => 'views_ui.theme.inc',
merlinofchaos's avatar
merlinofchaos committed
84 85 86
    ),
    'views_ui_expose_filter_form' => array(
      'render element' => 'form',
87
      'file' => 'views_ui.theme.inc',
merlinofchaos's avatar
merlinofchaos committed
88 89 90 91
    ),

    // list views
    'views_ui_view_info' => array(
92
      'variables' => array('view' => NULL, 'displays' => NULL),
93
      'file' => 'views_ui.theme.inc',
merlinofchaos's avatar
merlinofchaos committed
94 95
    ),

96 97 98
    // Group of filters.
    'views_ui_build_group_filter_form' => array(
      'render element' => 'form',
99
      'file' => 'views_ui.theme.inc',
100
      'function' => 'theme_views_ui_build_group_filter_form',
101 102
    ),

merlinofchaos's avatar
merlinofchaos committed
103 104 105
    // On behalf of a plugin
    'views_ui_style_plugin_table' => array(
      'render element' => 'form',
106
      'file' => 'views_ui.theme.inc',
merlinofchaos's avatar
merlinofchaos committed
107 108 109 110 111
    ),

    // When previewing a view.
    'views_ui_view_preview_section' => array(
      'variables' => array('view' => NULL, 'section' => NULL, 'content' => NULL, 'links' => ''),
112
      'file' => 'views_ui.theme.inc',
merlinofchaos's avatar
merlinofchaos committed
113 114 115 116
    ),

    // Generic container wrapper, to use instead of theme_container when an id
    // is not desired.
117
    'views_ui_container' => array(
118
      'variables' => array('children' => NULL, 'attributes' => array()),
119
      'file' => 'views_ui.theme.inc',
merlinofchaos's avatar
merlinofchaos committed
120 121 122 123 124
    ),
  );
}

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

  // Render title for the admin preview.
  if (!empty($view->live_preview)) {
    $variables['title'] = Xss::filterAdmin($view->getTitle());
  }

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

/**
 * 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.
 */
162 163 164
function views_ui_view_preview_section_handler_links(ViewExecutable $view, $type, $title = FALSE) {
  $display = $view->display_handler->display;
  $handlers = $view->display_handler->getHandlers($type);
merlinofchaos's avatar
merlinofchaos committed
165 166
  $links = array();

167
  $types = ViewExecutable::getHandlerTypes();
merlinofchaos's avatar
merlinofchaos committed
168 169 170 171 172 173 174
  if ($title) {
    $links[$type . '-title'] = array(
      'title' => $types[$type]['title'],
    );
  }

  foreach ($handlers as $id => $handler) {
175
    $field_name = $handler->adminLabel(TRUE);
merlinofchaos's avatar
merlinofchaos committed
176 177
    $links[$type . '-edit-' . $id] = array(
      'title' => t('Edit @section', array('@section' => $field_name)),
178
      'url' => Url::fromRoute('views_ui.form_handler', ['js' => 'nojs', 'view' => $view->storage->id(), 'display_id' => $display['id'], 'type' => $type, 'id' => $id]),
merlinofchaos's avatar
merlinofchaos committed
179 180 181 182 183
      'attributes' => array('class' => array('views-ajax-link')),
    );
  }
  $links[$type . '-add'] = array(
    'title' => t('Add new'),
184
    'url' => Url::fromRoute('views_ui.form_add_handler', ['js' => 'nojs', 'view' => $view->storage->id(), 'display_id' => $display['id'], 'type' => $type]),
merlinofchaos's avatar
merlinofchaos committed
185 186 187 188 189 190 191 192 193
    'attributes' => array('class' => array('views-ajax-link')),
  );

  return $links;
}

/**
 * Returns a link to editing a certain display setting.
 */
194
function views_ui_view_preview_section_display_category_links(ViewExecutable $view, $type, $title) {
merlinofchaos's avatar
merlinofchaos committed
195 196 197 198
  $display = $view->display_handler->display;
  $links = array(
    $type . '-edit' => array(
      'title' => t('Edit @section', array('@section' => $title)),
199
      'url' => Url::fromRoute('views_ui.form_display', ['js' => 'nojs', 'view' => $view->storage->id(), 'display_id' => $display['id'], 'type' => $type]),
merlinofchaos's avatar
merlinofchaos committed
200 201 202 203 204 205 206 207 208 209
      'attributes' => array('class' => array('views-ajax-link')),
    ),
  );

  return $links;
}

/**
 * Returns all contextual links for the main content part of the view.
 */
210
function views_ui_view_preview_section_rows_links(ViewExecutable $view) {
merlinofchaos's avatar
merlinofchaos committed
211 212 213 214 215 216 217 218 219 220 221
  $links = array();
  $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;
}

/**
222
 * Implements hook_views_plugins_display_alter().
merlinofchaos's avatar
merlinofchaos committed
223
 */
224
function views_ui_views_plugins_display_alter(&$plugins) {
merlinofchaos's avatar
merlinofchaos committed
225
  // Attach contextual links to each display plugin. The links will point to
226
  // paths underneath "admin/structure/views/view/{$view->id()}" (i.e., paths
merlinofchaos's avatar
merlinofchaos committed
227
  // for editing and performing other contextual actions on the view).
228
  foreach ($plugins as &$display) {
229 230
    $display['contextual links']['entity.view.edit_form'] = array(
      'route_name' => 'entity.view.edit_form',
231
      'route_parameters_names' => array('view' => 'id'),
merlinofchaos's avatar
merlinofchaos committed
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
    );
  }
}

/**
 * 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()) {
    $element['#links'] = array();
  }
  // 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.
248 249
  elseif (!empty($element['#links']['entityviewedit-form'])) {
    $display_id = $items['entity.view.edit_form']['metadata']['display_id'];
250 251
    $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
252 253 254 255 256 257 258 259 260 261 262 263 264 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
  }
}

/**
 * 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() {
  views_ui_contextual_links_suppress(((int) views_ui_contextual_links_suppress())+1);
}

/**
 * Decrements the views_ui_contextual_links_suppress() static variable.
 *
 * @see views_ui_contextual_links_suppress_push()
 */
function views_ui_contextual_links_suppress_pop() {
  views_ui_contextual_links_suppress(((int) views_ui_contextual_links_suppress())-1);
}

293 294 295 296 297 298 299
/**
 * 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.
 */
300
function views_ui_views_analyze(ViewExecutable $view) {
301 302 303 304 305 306 307 308 309 310 311 312 313
  $ret = array();
  // 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')) {
314
      $normal_path = \Drupal::service('path.alias_manager')->getPathByAlias($path);
315
      if ($path != $normal_path) {
316
        $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.', array('%display' => $display->display['display_title'])), 'warning');
317 318 319 320 321 322 323
      }
    }
  }

  return $ret;
}

merlinofchaos's avatar
merlinofchaos committed
324 325 326 327 328 329
/**
 * Truncate strings to a set length and provide a ... if they truncated.
 *
 * This is often used in the UI to ensure long strings fit.
 */
function views_ui_truncate($string, $length) {
330
  if (Unicode::strlen($string) > $length) {
331
    $string = Unicode::substr($string, 0, $length);
merlinofchaos's avatar
merlinofchaos committed
332 333 334 335 336
    $string .= '...';
  }

  return $string;
}