views_ui.module 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. <?php
  2. /**
  3. * @file
  4. * Provide structure for the administrative interface to Views.
  5. */
  6. use Drupal\Component\Utility\Unicode;
  7. use Drupal\Core\Routing\RouteMatchInterface;
  8. use Drupal\Core\Url;
  9. use Drupal\views\ViewExecutable;
  10. use Drupal\views\Analyzer;
  11. /**
  12. * Implements hook_help().
  13. */
  14. function views_ui_help($route_name, RouteMatchInterface $route_match) {
  15. switch ($route_name) {
  16. case 'help.page.views_ui':
  17. $output = '';
  18. $output .= '<h3>' . t('About') . '</h3>';
  19. $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>';
  20. $output .= '<h3>' . t('Uses') . '</h3>';
  21. $output .= '<dl>';
  22. $output .= '<dt>' . t('Creating and managing views') . '</dt>';
  23. $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>';
  24. $output .= '<dt>' . t('Enabling and disabling views') . '<dt>';
  25. $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>';
  26. $output .= '<dt>' . t('Exporting and importing views') . '</dt>';
  27. $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>';
  28. $output .= '</dl>';
  29. return $output;
  30. }
  31. }
  32. /**
  33. * Implements hook_entity_type_build().
  34. */
  35. function views_ui_entity_type_build(array &$entity_types) {
  36. /** @var $entity_types \Drupal\Core\Entity\EntityTypeInterface[] */
  37. $entity_types['view']
  38. ->setFormClass('edit', 'Drupal\views_ui\ViewEditForm')
  39. ->setFormClass('add', 'Drupal\views_ui\ViewAddForm')
  40. ->setFormClass('preview', 'Drupal\views_ui\ViewPreviewForm')
  41. ->setFormClass('duplicate', 'Drupal\views_ui\ViewDuplicateForm')
  42. ->setFormClass('delete', 'Drupal\Core\Entity\EntityDeleteForm')
  43. ->setFormClass('break_lock', 'Drupal\views_ui\Form\BreakLockForm')
  44. ->setListBuilderClass('Drupal\views_ui\ViewListBuilder')
  45. ->setLinkTemplate('edit-form', '/admin/structure/views/view/{view}')
  46. ->setLinkTemplate('edit-display-form', '/admin/structure/views/view/{view}/edit/{display_id}')
  47. ->setLinkTemplate('preview-form', '/admin/structure/views/view/{view}/preview/{display_id}')
  48. ->setLinkTemplate('duplicate-form', '/admin/structure/views/view/{view}/duplicate')
  49. ->setLinkTemplate('delete-form', '/admin/structure/views/view/{view}/delete')
  50. ->setLinkTemplate('enable', '/admin/structure/views/view/{view}/enable')
  51. ->setLinkTemplate('disable', '/admin/structure/views/view/{view}/disable')
  52. ->setLinkTemplate('break-lock-form', '/admin/structure/views/view/{view}/break-lock')
  53. ->setLinkTemplate('collection', '/admin/structure/views');
  54. }
  55. /**
  56. * Implements hook_theme().
  57. */
  58. function views_ui_theme() {
  59. return [
  60. // edit a view
  61. 'views_ui_display_tab_setting' => [
  62. 'variables' => ['description' => '', 'link' => '', 'settings_links' => [], 'overridden' => FALSE, 'defaulted' => FALSE, 'description_separator' => TRUE, 'class' => []],
  63. 'file' => 'views_ui.theme.inc',
  64. ],
  65. 'views_ui_display_tab_bucket' => [
  66. 'render element' => 'element',
  67. 'file' => 'views_ui.theme.inc',
  68. ],
  69. 'views_ui_rearrange_filter_form' => [
  70. 'render element' => 'form',
  71. 'file' => 'views_ui.theme.inc',
  72. ],
  73. 'views_ui_expose_filter_form' => [
  74. 'render element' => 'form',
  75. 'file' => 'views_ui.theme.inc',
  76. ],
  77. // Legacy theme hook for displaying views info.
  78. 'views_ui_view_info' => [
  79. 'variables' => ['view' => NULL, 'displays' => NULL],
  80. 'file' => 'views_ui.theme.inc',
  81. ],
  82. // List views.
  83. 'views_ui_views_listing_table' => [
  84. 'variables' => [
  85. 'headers' => NULL,
  86. 'rows' => NULL,
  87. 'attributes' => [],
  88. ],
  89. 'file' => 'views_ui.theme.inc',
  90. ],
  91. 'views_ui_view_displays_list' => [
  92. 'variables' => ['displays' => []],
  93. ],
  94. // Group of filters.
  95. 'views_ui_build_group_filter_form' => [
  96. 'render element' => 'form',
  97. 'file' => 'views_ui.theme.inc',
  98. ],
  99. // On behalf of a plugin
  100. 'views_ui_style_plugin_table' => [
  101. 'render element' => 'form',
  102. 'file' => 'views_ui.theme.inc',
  103. ],
  104. // When previewing a view.
  105. 'views_ui_view_preview_section' => [
  106. 'variables' => ['view' => NULL, 'section' => NULL, 'content' => NULL, 'links' => ''],
  107. 'file' => 'views_ui.theme.inc',
  108. ],
  109. // Generic container wrapper, to use instead of theme_container when an id
  110. // is not desired.
  111. 'views_ui_container' => [
  112. 'variables' => ['children' => NULL, 'attributes' => []],
  113. 'file' => 'views_ui.theme.inc',
  114. ],
  115. ];
  116. }
  117. /**
  118. * Implements hook_preprocess_HOOK() for views templates.
  119. */
  120. function views_ui_preprocess_views_view(&$variables) {
  121. $view = $variables['view'];
  122. // Render title for the admin preview.
  123. if (!empty($view->live_preview)) {
  124. $variables['title'] = [
  125. '#markup' => $view->getTitle()
  126. ];
  127. }
  128. if (!empty($view->live_preview) && \Drupal::moduleHandler()->moduleExists('contextual')) {
  129. $view->setShowAdminLinks(FALSE);
  130. foreach (['title', 'header', 'exposed', 'rows', 'pager', 'more', 'footer', 'empty', 'attachment_after', 'attachment_before'] as $section) {
  131. if (!empty($variables[$section])) {
  132. $variables[$section] = [
  133. '#theme' => 'views_ui_view_preview_section',
  134. '#view' => $view,
  135. '#section' => $section,
  136. '#content' => $variables[$section],
  137. '#theme_wrappers' => ['views_ui_container'],
  138. '#attributes' => ['class' => ['contextual-region']],
  139. ];
  140. }
  141. }
  142. }
  143. }
  144. /**
  145. * Implements hook_theme_suggestions_HOOK().
  146. */
  147. function views_ui_theme_suggestions_views_ui_view_preview_section(array $variables) {
  148. return ['views_ui_view_preview_section__' . $variables['section']];
  149. }
  150. /**
  151. * Returns contextual links for each handler of a certain section.
  152. *
  153. * @TODO
  154. * Bring in relationships
  155. * Refactor this function to use much stuff of views_ui_edit_form_get_bucket.
  156. *
  157. * @param $title
  158. * Add a bolded title of this section.
  159. */
  160. function views_ui_view_preview_section_handler_links(ViewExecutable $view, $type, $title = FALSE) {
  161. $display = $view->display_handler->display;
  162. $handlers = $view->display_handler->getHandlers($type);
  163. $links = [];
  164. $types = ViewExecutable::getHandlerTypes();
  165. if ($title) {
  166. $links[$type . '-title'] = [
  167. 'title' => $types[$type]['title'],
  168. ];
  169. }
  170. foreach ($handlers as $id => $handler) {
  171. $field_name = $handler->adminLabel(TRUE);
  172. $links[$type . '-edit-' . $id] = [
  173. 'title' => t('Edit @section', ['@section' => $field_name]),
  174. 'url' => Url::fromRoute('views_ui.form_handler', ['js' => 'nojs', 'view' => $view->storage->id(), 'display_id' => $display['id'], 'type' => $type, 'id' => $id]),
  175. 'attributes' => ['class' => ['views-ajax-link']],
  176. ];
  177. }
  178. $links[$type . '-add'] = [
  179. 'title' => t('Add new'),
  180. 'url' => Url::fromRoute('views_ui.form_add_handler', ['js' => 'nojs', 'view' => $view->storage->id(), 'display_id' => $display['id'], 'type' => $type]),
  181. 'attributes' => ['class' => ['views-ajax-link']],
  182. ];
  183. return $links;
  184. }
  185. /**
  186. * Returns a link to editing a certain display setting.
  187. */
  188. function views_ui_view_preview_section_display_category_links(ViewExecutable $view, $type, $title) {
  189. $display = $view->display_handler->display;
  190. $links = [
  191. $type . '-edit' => [
  192. 'title' => t('Edit @section', ['@section' => $title]),
  193. 'url' => Url::fromRoute('views_ui.form_display', ['js' => 'nojs', 'view' => $view->storage->id(), 'display_id' => $display['id'], 'type' => $type]),
  194. 'attributes' => ['class' => ['views-ajax-link']],
  195. ],
  196. ];
  197. return $links;
  198. }
  199. /**
  200. * Returns all contextual links for the main content part of the view.
  201. */
  202. function views_ui_view_preview_section_rows_links(ViewExecutable $view) {
  203. $links = [];
  204. $links = array_merge($links, views_ui_view_preview_section_handler_links($view, 'filter', TRUE));
  205. $links = array_merge($links, views_ui_view_preview_section_handler_links($view, 'field', TRUE));
  206. $links = array_merge($links, views_ui_view_preview_section_handler_links($view, 'sort', TRUE));
  207. $links = array_merge($links, views_ui_view_preview_section_handler_links($view, 'argument', TRUE));
  208. $links = array_merge($links, views_ui_view_preview_section_handler_links($view, 'relationship', TRUE));
  209. return $links;
  210. }
  211. /**
  212. * Implements hook_views_plugins_display_alter().
  213. */
  214. function views_ui_views_plugins_display_alter(&$plugins) {
  215. // Attach contextual links to each display plugin. The links will point to
  216. // paths underneath "admin/structure/views/view/{$view->id()}" (i.e., paths
  217. // for editing and performing other contextual actions on the view).
  218. foreach ($plugins as &$display) {
  219. $display['contextual links']['entity.view.edit_form'] = [
  220. 'route_name' => 'entity.view.edit_form',
  221. 'route_parameters_names' => ['view' => 'id'],
  222. ];
  223. }
  224. }
  225. /**
  226. * Implements hook_contextual_links_view_alter().
  227. */
  228. function views_ui_contextual_links_view_alter(&$element, $items) {
  229. // Remove contextual links from being rendered, when so desired, such as
  230. // within a View preview.
  231. if (views_ui_contextual_links_suppress()) {
  232. $element['#links'] = [];
  233. }
  234. // Append the display ID to the Views UI edit links, so that clicking on the
  235. // contextual link takes you directly to the correct display tab on the edit
  236. // screen.
  237. elseif (!empty($element['#links']['entityviewedit-form'])) {
  238. $display_id = $items['entity.view.edit_form']['metadata']['display_id'];
  239. $route_parameters = $element['#links']['entityviewedit-form']['url']->getRouteParameters() + ['display_id' => $display_id];
  240. $element['#links']['entityviewedit-form']['url'] = Url::fromRoute('entity.view.edit_display_form', $route_parameters);
  241. }
  242. }
  243. /**
  244. * Sets a static variable for controlling whether contextual links are rendered.
  245. *
  246. * @see views_ui_contextual_links_view_alter()
  247. */
  248. function views_ui_contextual_links_suppress($set = NULL) {
  249. $suppress = &drupal_static(__FUNCTION__);
  250. if (isset($set)) {
  251. $suppress = $set;
  252. }
  253. return $suppress;
  254. }
  255. /**
  256. * Increments the views_ui_contextual_links_suppress() static variable.
  257. *
  258. * When this function is added to the #pre_render of an element, and
  259. * 'views_ui_contextual_links_suppress_pop' is added to the #post_render of the
  260. * same element, then all contextual links within the element and its
  261. * descendants are suppressed from being rendered. This is used, for example,
  262. * during a View preview, when it is not desired for nodes in the Views result
  263. * to have contextual links.
  264. *
  265. * @see views_ui_contextual_links_suppress_pop()
  266. */
  267. function views_ui_contextual_links_suppress_push() {
  268. views_ui_contextual_links_suppress(((int) views_ui_contextual_links_suppress()) + 1);
  269. }
  270. /**
  271. * Decrements the views_ui_contextual_links_suppress() static variable.
  272. *
  273. * @see views_ui_contextual_links_suppress_push()
  274. */
  275. function views_ui_contextual_links_suppress_pop() {
  276. views_ui_contextual_links_suppress(((int) views_ui_contextual_links_suppress()) - 1);
  277. }
  278. /**
  279. * Implements hook_views_analyze().
  280. *
  281. * This is the basic views analysis that checks for very minimal problems.
  282. * There are other analysis tools in core specific sections, such as
  283. * node.views.inc as well.
  284. */
  285. function views_ui_views_analyze(ViewExecutable $view) {
  286. $ret = [];
  287. // Check for something other than the default display:
  288. if (count($view->displayHandlers) < 2) {
  289. $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');
  290. }
  291. // You can give a page display the same path as an alias existing in the
  292. // system, so the alias will not work anymore. Report this to the user,
  293. // because he probably wanted something else.
  294. foreach ($view->displayHandlers as $display) {
  295. if (empty($display)) {
  296. continue;
  297. }
  298. if ($display->hasPath() && $path = $display->getOption('path')) {
  299. $normal_path = \Drupal::service('path.alias_manager')->getPathByAlias($path);
  300. if ($path != $normal_path) {
  301. $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');
  302. }
  303. }
  304. }
  305. return $ret;
  306. }
  307. /**
  308. * Truncate strings to a set length and provide a '...' if they truncated.
  309. *
  310. * This is often used in the UI to ensure long strings fit.
  311. */
  312. function views_ui_truncate($string, $length) {
  313. if (Unicode::strlen($string) > $length) {
  314. $string = Unicode::substr($string, 0, $length);
  315. $string .= '...';
  316. }
  317. return $string;
  318. }