3 namespace Drupal\views_ui;
5 use Drupal\Component\Plugin\PluginManagerInterface;
6 use Drupal\Core\Entity\EntityInterface;
7 use Drupal\Core\Config\Entity\ConfigEntityListBuilder;
8 use Drupal\Core\Entity\EntityStorageInterface;
9 use Drupal\Core\Entity\EntityTypeInterface;
11 use Symfony\Component\DependencyInjection\ContainerInterface;
12 use Symfony\Component\HttpKernel\Exception\NotAcceptableHttpException;
15 * Defines a class to build a listing of view entities.
17 * @see \Drupal\views\Entity\View
19 class ViewListBuilder extends ConfigEntityListBuilder {
22 * The views display plugin manager to use.
24 * @var \Drupal\Component\Plugin\PluginManagerInterface
26 protected $displayManager;
36 public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
39 $container->get('entity.manager')->getStorage($entity_type->id()),
40 $container->get('plugin.manager.views.display')
45 * Constructs a new ViewListBuilder object.
47 * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
48 * The entity type definition.
49 * @param \Drupal\Core\Entity\EntityStorageInterface $storage
50 * The entity storage class.
51 * @param \Drupal\Component\Plugin\PluginManagerInterface $display_manager
52 * The views display plugin manager to use.
54 public function __construct(EntityTypeInterface $entity_type, EntityStorageInterface $storage, PluginManagerInterface $display_manager) {
55 parent::__construct($entity_type, $storage);
57 $this->displayManager = $display_manager;
58 // This list builder uses client-side filters which requires all entities to
59 // be listed, disable the pager.
60 // @todo https://www.drupal.org/node/2536826 change the filtering to support
68 public function load() {
73 foreach (parent::load() as $entity) {
74 if ($entity->status()) {
75 $entities['enabled'][] = $entity;
78 $entities['disabled'][] = $entity;
87 public function buildRow(EntityInterface $view) {
88 $row = parent::buildRow($view);
93 '#plain_text' => $view->label(),
98 '#plain_text' => $view->id(),
103 '#plain_text' => $view->get('description'),
108 '#theme' => 'views_ui_view_displays_list',
109 '#displays' => $this->getDisplaysList($view),
112 'operations' => $row['operations'],
115 'class' => [$view->status() ? 'views-ui-list-enabled' : 'views-ui-list-disabled'],
123 public function buildHeader() {
126 'data' => $this->t('View name'),
128 'class' => ['views-ui-name'],
132 'data' => $this->t('Machine name'),
134 'class' => ['views-ui-machine-name'],
138 'data' => $this->t('Description'),
140 'class' => ['views-ui-description'],
144 'data' => $this->t('Displays'),
146 'class' => ['views-ui-displays'],
150 'data' => $this->t('Operations'),
152 'class' => ['views-ui-operations'],
161 public function getDefaultOperations(EntityInterface $entity) {
162 $operations = parent::getDefaultOperations($entity);
164 if ($entity->hasLinkTemplate('duplicate-form')) {
165 $operations['duplicate'] = [
166 'title' => $this->t('Duplicate'),
168 'url' => $entity->urlInfo('duplicate-form'),
172 // Add AJAX functionality to enable/disable operations.
173 foreach (['enable', 'disable'] as $op) {
174 if (isset($operations[$op])) {
175 $operations[$op]['url'] = $entity->urlInfo($op);
176 // Enable and disable operations should use AJAX.
177 $operations[$op]['attributes']['class'][] = 'use-ajax';
181 // ajax.js focuses automatically on the data-drupal-selector element. When
182 // enabling the view again, focusing on the disable link doesn't work, as it
183 // is hidden. We assign data-drupal-selector to every link, so it focuses
185 foreach ($operations as &$operation) {
186 $operation['attributes']['data-drupal-selector'] = 'views-listing-' . $entity->id();
195 public function render() {
196 $entities = $this->load();
197 $list['#type'] = 'container';
198 $list['#attributes']['id'] = 'views-entity-list';
200 $list['#attached']['library'][] = 'core/drupal.ajax';
201 $list['#attached']['library'][] = 'views_ui/views_ui.listing';
204 '#type' => 'container',
206 'class' => ['table-filter', 'js-show'],
210 $list['filters']['text'] = [
212 '#title' => $this->t('Filter'),
213 '#title_display' => 'invisible',
215 '#placeholder' => $this->t('Filter by view name, machine name, description, or display path'),
217 'class' => ['views-filter-text'],
218 'data-table' => '.views-listing-table',
219 'autocomplete' => 'off',
220 'title' => $this->t('Enter a part of the view name, machine name, description, or display path to filter by.'),
224 $list['enabled']['heading']['#markup'] = '<h2>' . $this->t('Enabled', [], ['context' => 'Plural']) . '</h2>';
225 $list['disabled']['heading']['#markup'] = '<h2>' . $this->t('Disabled', [], ['context' => 'Plural']) . '</h2>';
226 foreach (['enabled', 'disabled'] as $status) {
227 $list[$status]['#type'] = 'container';
228 $list[$status]['#attributes'] = ['class' => ['views-list-section', $status]];
229 $list[$status]['table'] = [
230 '#theme' => 'views_ui_views_listing_table',
231 '#headers' => $this->buildHeader(),
232 '#attributes' => ['class' => ['views-listing-table', $status]],
234 foreach ($entities[$status] as $entity) {
235 $list[$status]['table']['#rows'][$entity->id()] = $this->buildRow($entity);
238 // @todo Use a placeholder for the entity label if this is abstracted to
239 // other entity types.
240 $list['enabled']['table']['#empty'] = $this->t('There are no enabled views.');
241 $list['disabled']['table']['#empty'] = $this->t('There are no disabled views.');
247 * Gets a list of displays included in the view.
249 * @param \Drupal\Core\Entity\EntityInterface $view
250 * The view entity instance to get a list of displays for.
253 * An array of display types that this view includes.
255 protected function getDisplaysList(EntityInterface $view) {
258 $executable = $view->getExecutable();
259 $executable->initDisplay();
260 foreach ($executable->displayHandlers as $display) {
261 $rendered_path = FALSE;
262 $definition = $display->getPluginDefinition();
263 if (!empty($definition['admin'])) {
264 if ($display->hasPath()) {
265 $path = $display->getPath();
266 if ($view->status() && strpos($path, '%') === FALSE) {
267 // Wrap this in a try/catch as trying to generate links to some
268 // routes may throw a NotAcceptableHttpException if they do not
269 // respond to HTML, such as RESTExports.
271 // @todo Views should expect and store a leading /. See:
272 // https://www.drupal.org/node/2423913
273 $rendered_path = \Drupal::l('/' . $path, Url::fromUserInput('/' . $path));
275 catch (NotAcceptableHttpException $e) {
276 $rendered_path = '/' . $path;
280 $rendered_path = '/' . $path;
284 'display' => $definition['admin'],
285 'path' => $rendered_path,