3 namespace Drupal\views_ui;
5 use Drupal\Core\Entity\EntityForm;
6 use Drupal\Core\Form\FormStateInterface;
7 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
8 use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
11 * Base form for Views forms.
13 abstract class ViewFormBase extends EntityForm {
16 * The name of the display used by the form.
25 public function init(FormStateInterface $form_state) {
26 parent::init($form_state);
28 // @todo Remove the need for this.
29 $form_state->loadInclude('views_ui', 'inc', 'admin');
30 $form_state->set('view', $this->entity);
36 public function buildForm(array $form, FormStateInterface $form_state, $display_id = NULL) {
37 if (isset($display_id) && $form_state->has('display_id') && ($display_id !== $form_state->get('display_id'))) {
38 throw new \InvalidArgumentException('Mismatch between $form_state->get(\'display_id\') and $display_id.');
40 $this->displayID = $form_state->has('display_id') ? $form_state->get('display_id') : $display_id;
41 return parent::buildForm($form, $form_state);
47 protected function prepareEntity() {
48 // Determine the displays available for editing.
49 if ($tabs = $this->getDisplayTabs($this->entity)) {
50 if (empty($this->displayID)) {
51 // If a display isn't specified, use the first one after sorting by
53 uasort($tabs, 'Drupal\Component\Utility\SortArray::sortByWeightProperty');
54 foreach ($tabs as $id => $tab) {
55 if (!isset($tab['#access']) || $tab['#access']) {
56 $this->displayID = $id;
61 // If a display is specified, but we don't have access to it, return
62 // an access denied page.
63 if ($this->displayID && !isset($tabs[$this->displayID])) {
64 throw new NotFoundHttpException();
66 elseif ($this->displayID && (isset($tabs[$this->displayID]['#access']) && !$tabs[$this->displayID]['#access'])) {
67 throw new AccessDeniedHttpException();
71 elseif ($this->displayID) {
72 throw new NotFoundHttpException();
77 * Adds tabs for navigating across Displays when editing a View.
79 * This function can be called from hook_menu_local_tasks_alter() to implement
80 * these tabs as secondary local tasks, or it can be called from elsewhere if
81 * having them as secondary local tasks isn't desired. The caller is responsible
82 * for setting the active tab's #active property to TRUE.
85 * The display_id which is edited on the current request.
87 public function getDisplayTabs(ViewUI $view) {
88 $executable = $view->getExecutable();
89 $executable->initDisplay();
90 $display_id = $this->displayID;
93 // Create a tab for each display.
94 foreach ($view->get('display') as $id => $display) {
95 // Get an instance of the display plugin, to make sure it will work in the
97 $display_plugin = $executable->displayHandlers->get($id);
98 if (empty($display_plugin)) {
103 '#theme' => 'menu_local_task',
104 '#weight' => $display['position'],
106 'title' => $this->getDisplayLabel($view, $id),
107 'localized_options' => [],
108 'url' => $view->urlInfo('edit-display-form')->setRouteParameter('display_id', $id),
111 if (!empty($display['deleted'])) {
112 $tabs[$id]['#link']['localized_options']['attributes']['class'][] = 'views-display-deleted-link';
114 if (isset($display['display_options']['enabled']) && !$display['display_options']['enabled']) {
115 $tabs[$id]['#link']['localized_options']['attributes']['class'][] = 'views-display-disabled-link';
119 // If the default display isn't supposed to be shown, don't display its tab, unless it's the only display.
120 if ((!$this->isDefaultDisplayShown($view) && $display_id != 'default') && count($tabs) > 1) {
121 $tabs['default']['#access'] = FALSE;
124 // Mark the display tab as red to show validation errors.
125 $errors = $executable->validate();
126 foreach ($view->get('display') as $id => $display) {
127 if (!empty($errors[$id])) {
128 // Always show the tab.
129 $tabs[$id]['#access'] = TRUE;
130 // Add a class to mark the error and a title to make a hover tip.
131 $tabs[$id]['#link']['localized_options']['attributes']['class'][] = 'error';
132 $tabs[$id]['#link']['localized_options']['attributes']['title'] = $this->t('This display has one or more validation errors.');
140 * Controls whether or not the default display should have its own tab on edit.
142 public function isDefaultDisplayShown(ViewUI $view) {
143 // Always show the default display for advanced users who prefer that mode.
144 $advanced_mode = \Drupal::config('views.settings')->get('ui.show.master_display');
145 // For other users, show the default display only if there are no others, and
146 // hide it if there's at least one "real" display.
147 $additional_displays = (count($view->getExecutable()->displayHandlers) == 1);
149 return $advanced_mode || $additional_displays;
153 * Placeholder function for overriding $display['display_title'].
155 * @todo Remove this function once editing the display title is possible.
157 public function getDisplayLabel(ViewUI $view, $display_id, $check_changed = TRUE) {
158 $display = $view->get('display');
159 $title = $display_id == 'default' ? $this->t('Master') : $display[$display_id]['display_title'];
160 $title = views_ui_truncate($title, 25);
162 if ($check_changed && !empty($view->changed_display[$display_id])) {
164 $title = $title . $changed;