<?php /** * @file * Provide structure for the administrative interface to Views. */ use Drupal\Component\Utility\Unicode; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Url; use Drupal\views\Views; use Drupal\views\ViewExecutable; use Drupal\views_ui\ViewUI; use Drupal\views\Analyzer; use Drupal\Core\Ajax\AjaxResponse; use Drupal\Core\Ajax\ReplaceCommand; use Drupal\Component\Utility\Xss; /** * Implements hook_help(). */ function views_ui_help($route_name, RouteMatchInterface $route_match) { switch ($route_name) { case 'help.page.views_ui': $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://www.drupal.org/documentation/modules/views_ui')) . '</p>'; $output .= '<h3>' . t('Uses') . '</h3>'; $output .= '<dl>'; $output .= '<dt>' . t('Creating and managing views.') . '</dt>'; $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>'; $output .= '<dt>' . t('Enabling and disabling views.') . '<dt>'; $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>'; $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::moduleHandler()->moduleExists('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 > Other section under "Output".') . '</dd>'; $output .= '</dl>'; return $output; } } /** * Implements hook_entity_type_build(). */ function views_ui_entity_type_build(array &$entity_types) { /** @var $entity_types \Drupal\Core\Entity\EntityTypeInterface[] */ $entity_types['view'] ->setFormClass('edit', 'Drupal\views_ui\ViewEditForm') ->setFormClass('add', 'Drupal\views_ui\ViewAddForm') ->setFormClass('preview', 'Drupal\views_ui\ViewPreviewForm') ->setFormClass('duplicate', 'Drupal\views_ui\ViewDuplicateForm') ->setFormClass('delete', 'Drupal\Core\Entity\EntityDeleteForm') ->setFormClass('break_lock', 'Drupal\views_ui\Form\BreakLockForm') ->setListBuilderClass('Drupal\views_ui\ViewListBuilder') ->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') ->setLinkTemplate('break-lock-form', '/admin/structure/views/view/{view}/break-lock') ->setLinkTemplate('collection', '/admin/structure/views'); } /** * 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()), 'file' => 'views_ui.theme.inc', ), 'views_ui_display_tab_bucket' => array( 'render element' => 'element', 'file' => 'views_ui.theme.inc', ), 'views_ui_rearrange_filter_form' => array( 'render element' => 'form', 'file' => 'views_ui.theme.inc', ), 'views_ui_expose_filter_form' => array( 'render element' => 'form', 'file' => 'views_ui.theme.inc', ), // list views 'views_ui_view_info' => array( 'variables' => array('view' => NULL, 'displays' => NULL), 'file' => 'views_ui.theme.inc', ), // Group of filters. 'views_ui_build_group_filter_form' => array( 'render element' => 'form', 'file' => 'views_ui.theme.inc', 'function' => 'theme_views_ui_build_group_filter_form', ), // On behalf of a plugin 'views_ui_style_plugin_table' => array( 'render element' => 'form', 'file' => 'views_ui.theme.inc', ), // When previewing a view. 'views_ui_view_preview_section' => array( 'variables' => array('view' => NULL, 'section' => NULL, 'content' => NULL, 'links' => ''), 'file' => 'views_ui.theme.inc', ), // Generic container wrapper, to use instead of theme_container when an id // is not desired. 'views_ui_container' => array( 'variables' => array('children' => NULL, 'attributes' => array()), 'file' => 'views_ui.theme.inc', ), ); } /** * Implements hook_preprocess_HOOK() for views templates. */ function views_ui_preprocess_views_view(&$variables) { $view = $variables['view']; // Render title for the admin preview. if (!empty($view->live_preview)) { $variables['title'] = Xss::filterAdmin($view->getTitle()); } if (!empty($view->live_preview) && \Drupal::moduleHandler()->moduleExists('contextual')) { $view->setShowAdminLinks(FALSE); foreach (array('title', 'header', 'exposed', 'rows', 'pager', 'more', 'footer', 'empty', 'attachment_after', 'attachment_before') as $section) { if (!empty($variables[$section])) { $variables[$section] = array( '#theme' => 'views_ui_view_preview_section', '#view' => $view, '#section' => $section, '#content' => $variables[$section], '#theme_wrappers' => array('views_ui_container'), '#attributes' => array('class' => array('contextual-region')), ); } } } } /** * 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. */ function views_ui_view_preview_section_handler_links(ViewExecutable $view, $type, $title = FALSE) { $display = $view->display_handler->display; $handlers = $view->display_handler->getHandlers($type); $links = array(); $types = ViewExecutable::getHandlerTypes(); if ($title) { $links[$type . '-title'] = array( 'title' => $types[$type]['title'], ); } foreach ($handlers as $id => $handler) { $field_name = $handler->adminLabel(TRUE); $links[$type . '-edit-' . $id] = array( 'title' => t('Edit @section', array('@section' => $field_name)), 'url' => Url::fromRoute('views_ui.form_handler', ['js' => 'nojs', 'view' => $view->storage->id(), 'display_id' => $display['id'], 'type' => $type, 'id' => $id]), 'attributes' => array('class' => array('views-ajax-link')), ); } $links[$type . '-add'] = array( 'title' => t('Add new'), 'url' => Url::fromRoute('views_ui.form_add_handler', ['js' => 'nojs', 'view' => $view->storage->id(), 'display_id' => $display['id'], 'type' => $type]), 'attributes' => array('class' => array('views-ajax-link')), ); return $links; } /** * Returns a link to editing a certain display setting. */ function views_ui_view_preview_section_display_category_links(ViewExecutable $view, $type, $title) { $display = $view->display_handler->display; $links = array( $type . '-edit' => array( 'title' => t('Edit @section', array('@section' => $title)), 'url' => Url::fromRoute('views_ui.form_display', ['js' => 'nojs', 'view' => $view->storage->id(), 'display_id' => $display['id'], 'type' => $type]), 'attributes' => array('class' => array('views-ajax-link')), ), ); return $links; } /** * Returns all contextual links for the main content part of the view. */ function views_ui_view_preview_section_rows_links(ViewExecutable $view) { $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; } /** * Implements hook_views_plugins_display_alter(). */ function views_ui_views_plugins_display_alter(&$plugins) { // Attach contextual links to each display plugin. The links will point to // paths underneath "admin/structure/views/view/{$view->id()}" (i.e., paths // for editing and performing other contextual actions on the view). foreach ($plugins as &$display) { $display['contextual links']['entity.view.edit_form'] = array( 'route_name' => 'entity.view.edit_form', 'route_parameters_names' => array('view' => 'id'), ); } } /** * 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. elseif (!empty($element['#links']['entityviewedit-form'])) { $display_id = $items['entity.view.edit_form']['metadata']['display_id']; $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); } } /** * 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); } /** * 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. */ function views_ui_views_analyze(ViewExecutable $view) { $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')) { $normal_path = \Drupal::service('path.alias_manager')->getPathByAlias($path); if ($path != $normal_path) { $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'); } } } return $ret; } /** * 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) { if (Unicode::strlen($string) > $length) { $string = Unicode::substr($string, 0, $length); $string .= '...'; } return $string; }