fffe084c7252afb353b8c3d71c1474f2bf7fb70d
[yaffs-website] / web / core / modules / views / src / Plugin / views / exposed_form / ExposedFormPluginBase.php
1 <?php
2
3 namespace Drupal\views\Plugin\views\exposed_form;
4
5 use Drupal\Component\Utility\Html;
6 use Drupal\Core\Cache\Cache;
7 use Drupal\Core\Cache\CacheableDependencyInterface;
8 use Drupal\Core\Form\FormState;
9 use Drupal\Core\Form\FormStateInterface;
10 use Drupal\views\Plugin\views\PluginBase;
11
12 /**
13  * Base class for Views exposed filter form plugins.
14  *
15  * @ingroup views_exposed_form_plugins
16  */
17 abstract class ExposedFormPluginBase extends PluginBase implements CacheableDependencyInterface, ExposedFormPluginInterface {
18
19   /**
20    * {@inheritdoc}
21    */
22   protected $usesOptions = TRUE;
23
24   /**
25    * {@inheritdoc}
26    */
27   protected function defineOptions() {
28     $options = parent::defineOptions();
29     $options['submit_button'] = ['default' => $this->t('Apply')];
30     $options['reset_button'] = ['default' => FALSE];
31     $options['reset_button_label'] = ['default' => $this->t('Reset')];
32     $options['exposed_sorts_label'] = ['default' => $this->t('Sort by')];
33     $options['expose_sort_order'] = ['default' => TRUE];
34     $options['sort_asc_label'] = ['default' => $this->t('Asc')];
35     $options['sort_desc_label'] = ['default' => $this->t('Desc')];
36     return $options;
37   }
38
39   /**
40    * {@inheritdoc}
41    */
42   public function buildOptionsForm(&$form, FormStateInterface $form_state) {
43     parent::buildOptionsForm($form, $form_state);
44     $form['submit_button'] = [
45       '#type' => 'textfield',
46       '#title' => $this->t('Submit button text'),
47       '#default_value' => $this->options['submit_button'],
48       '#required' => TRUE,
49     ];
50
51     $form['reset_button'] = [
52       '#type' => 'checkbox',
53       '#title' => $this->t('Include reset button (resets all applied exposed filters)'),
54       '#default_value' => $this->options['reset_button'],
55     ];
56
57     $form['reset_button_label'] = [
58      '#type' => 'textfield',
59       '#title' => $this->t('Reset button label'),
60       '#description' => $this->t('Text to display in the reset button of the exposed form.'),
61       '#default_value' => $this->options['reset_button_label'],
62       '#required' => TRUE,
63       '#states' => [
64         'invisible' => [
65           'input[name="exposed_form_options[reset_button]"]' => ['checked' => FALSE],
66         ],
67       ],
68     ];
69
70     $form['exposed_sorts_label'] = [
71       '#type' => 'textfield',
72       '#title' => $this->t('Exposed sorts label'),
73       '#default_value' => $this->options['exposed_sorts_label'],
74       '#required' => TRUE,
75     ];
76
77     $form['expose_sort_order'] = [
78       '#type' => 'checkbox',
79       '#title' => $this->t('Allow people to choose the sort order'),
80       '#description' => $this->t('If sort order is not exposed, the sort criteria settings for each sort will determine its order.'),
81       '#default_value' => $this->options['expose_sort_order'],
82     ];
83
84     $form['sort_asc_label'] = [
85       '#type' => 'textfield',
86       '#title' => $this->t('Label for ascending sort'),
87       '#default_value' => $this->options['sort_asc_label'],
88       '#required' => TRUE,
89       '#states' => [
90         'visible' => [
91           'input[name="exposed_form_options[expose_sort_order]"]' => ['checked' => TRUE],
92         ],
93       ],
94     ];
95
96     $form['sort_desc_label'] = [
97       '#type' => 'textfield',
98       '#title' => $this->t('Label for descending sort'),
99       '#default_value' => $this->options['sort_desc_label'],
100       '#required' => TRUE,
101       '#states' => [
102         'visible' => [
103           'input[name="exposed_form_options[expose_sort_order]"]' => ['checked' => TRUE],
104         ],
105       ],
106     ];
107   }
108
109   /**
110    * {@inheritdoc}
111    */
112   public function renderExposedForm($block = FALSE) {
113     // Deal with any exposed filters we may have, before building.
114     $form_state = (new FormState())
115       ->setStorage([
116         'view' => $this->view,
117         'display' => &$this->view->display_handler->display,
118         'rerender' => TRUE,
119       ])
120       ->setMethod('get')
121       ->setAlwaysProcess()
122       ->disableRedirect();
123
124     // Some types of displays (eg. attachments) may wish to use the exposed
125     // filters of their parent displays instead of showing an additional
126     // exposed filter form for the attachment as well as that for the parent.
127     if (!$this->view->display_handler->displaysExposed() || (!$block && $this->view->display_handler->getOption('exposed_block'))) {
128       $form_state->set('rerender', NULL);
129     }
130
131     if (!empty($this->ajax)) {
132       $form_state->set('ajax', TRUE);
133     }
134
135     $form = \Drupal::formBuilder()->buildForm('\Drupal\views\Form\ViewsExposedForm', $form_state);
136     $errors = $form_state->getErrors();
137
138     // If the exposed form had errors, do not build the view.
139     if (!empty($errors)) {
140       $this->view->build_info['abort'] = TRUE;
141     }
142
143     if (!$this->view->display_handler->displaysExposed() || (!$block && $this->view->display_handler->getOption('exposed_block'))) {
144       return [];
145     }
146     else {
147       return $form;
148     }
149   }
150
151   /**
152    * {@inheritdoc}
153    */
154   public function query() {
155     $view = $this->view;
156     $exposed_data = isset($view->exposed_data) ? $view->exposed_data : [];
157     $sort_by = isset($exposed_data['sort_by']) ? $exposed_data['sort_by'] : NULL;
158     if (!empty($sort_by)) {
159       // Make sure the original order of sorts is preserved
160       // (e.g. a sticky sort is often first)
161       if (isset($view->sort[$sort_by])) {
162         $view->query->orderby = [];
163         foreach ($view->sort as $key => $sort) {
164           if (!$sort->isExposed()) {
165             $sort->query();
166           }
167           elseif ($key == $sort_by) {
168             if (isset($exposed_data['sort_order']) && in_array($exposed_data['sort_order'], ['ASC', 'DESC'])) {
169               $sort->options['order'] = $exposed_data['sort_order'];
170             }
171             $sort->setRelationship();
172             $sort->query();
173           }
174         }
175       }
176     }
177   }
178
179   /**
180    * {@inheritdoc}
181    */
182   public function preRender($values) { }
183
184   /**
185    * {@inheritdoc}
186    */
187   public function postRender(&$output) { }
188
189   /**
190    * {@inheritdoc}
191    */
192   public function preExecute() { }
193
194   /**
195    * {@inheritdoc}
196    */
197   public function postExecute() { }
198
199   /**
200    * {@inheritdoc}
201    */
202   public function exposedFormAlter(&$form, FormStateInterface $form_state) {
203     if (!empty($this->options['submit_button'])) {
204       $form['actions']['submit']['#value'] = $this->options['submit_button'];
205     }
206
207     // Check if there is exposed sorts for this view
208     $exposed_sorts = [];
209     foreach ($this->view->sort as $id => $handler) {
210       if ($handler->canExpose() && $handler->isExposed()) {
211         $exposed_sorts[$id] = Html::escape($handler->options['expose']['label']);
212       }
213     }
214
215     if (count($exposed_sorts)) {
216       $form['sort_by'] = [
217         '#type' => 'select',
218         '#options' => $exposed_sorts,
219         '#title' => $this->options['exposed_sorts_label'],
220       ];
221       $sort_order = [
222         'ASC' => $this->options['sort_asc_label'],
223         'DESC' => $this->options['sort_desc_label'],
224       ];
225       $user_input = $form_state->getUserInput();
226       if (isset($user_input['sort_by']) && isset($this->view->sort[$user_input['sort_by']])) {
227         $default_sort_order = $this->view->sort[$user_input['sort_by']]->options['order'];
228       }
229       else {
230         $first_sort = reset($this->view->sort);
231         $default_sort_order = $first_sort->options['order'];
232       }
233
234       if (!isset($user_input['sort_by'])) {
235         $keys = array_keys($exposed_sorts);
236         $user_input['sort_by'] = array_shift($keys);
237         $form_state->setUserInput($user_input);
238       }
239
240       if ($this->options['expose_sort_order']) {
241         $form['sort_order'] = [
242           '#type' => 'select',
243           '#options' => $sort_order,
244           '#title' => $this->t('Order', [], ['context' => 'Sort order']),
245           '#default_value' => $default_sort_order,
246         ];
247       }
248       $form['submit']['#weight'] = 10;
249     }
250
251     if (!empty($this->options['reset_button'])) {
252       $form['actions']['reset'] = [
253         '#value' => $this->options['reset_button_label'],
254         '#type' => 'submit',
255         '#weight' => 10,
256       ];
257
258       // Get an array of exposed filters, keyed by identifier option.
259       $exposed_filters = [];
260       foreach ($this->view->filter as $id => $handler) {
261         if ($handler->canExpose() && $handler->isExposed() && !empty($handler->options['expose']['identifier'])) {
262           $exposed_filters[$handler->options['expose']['identifier']] = $id;
263         }
264       }
265       $all_exposed = array_merge($exposed_sorts, $exposed_filters);
266
267       // Set the access to FALSE if there is no exposed input.
268       if (!array_intersect_key($all_exposed, $this->view->getExposedInput())) {
269         $form['actions']['reset']['#access'] = FALSE;
270       }
271     }
272
273     $pager = $this->view->display_handler->getPlugin('pager');
274     if ($pager) {
275       $pager->exposedFormAlter($form, $form_state);
276       $form_state->set('pager_plugin', $pager);
277     }
278   }
279
280   /**
281    * {@inheritdoc}
282    */
283   public function exposedFormValidate(&$form, FormStateInterface $form_state) {
284     if ($pager_plugin = $form_state->get('pager_plugin')) {
285       $pager_plugin->exposedFormValidate($form, $form_state);
286     }
287   }
288
289   /**
290    * {@inheritdoc}
291    */
292   public function exposedFormSubmit(&$form, FormStateInterface $form_state, &$exclude) {
293     if (!$form_state->isValueEmpty('op') && $form_state->getValue('op') == $this->options['reset_button_label']) {
294       $this->resetForm($form, $form_state);
295     }
296     if ($pager_plugin = $form_state->get('pager_plugin')) {
297       $pager_plugin->exposedFormSubmit($form, $form_state, $exclude);
298       $exclude[] = 'pager_plugin';
299     }
300   }
301
302   /**
303    * Resets all the states of the exposed form.
304    *
305    * This method is called when the "Reset" button is triggered. Clears
306    * user inputs, stored session, and the form state.
307    *
308    * @param array $form
309    *   An associative array containing the structure of the form.
310    * @param \Drupal\Core\Form\FormStateInterface $form_state
311    *   The current state of the form.
312    */
313   public function resetForm(&$form, FormStateInterface $form_state) {
314     // _SESSION is not defined for users who are not logged in.
315
316     // If filters are not overridden, store the 'remember' settings on the
317     // default display. If they are, store them on this display. This way,
318     // multiple displays in the same view can share the same filters and
319     // remember settings.
320     $display_id = ($this->view->display_handler->isDefaulted('filters')) ? 'default' : $this->view->current_display;
321
322     if (isset($_SESSION['views'][$this->view->storage->id()][$display_id])) {
323       unset($_SESSION['views'][$this->view->storage->id()][$display_id]);
324     }
325
326     // Set the form to allow redirect.
327     if (empty($this->view->live_preview) && !\Drupal::request()->isXmlHttpRequest()) {
328       $form_state->disableRedirect(FALSE);
329     }
330     else {
331       $form_state->setRebuild();
332       $this->view->exposed_data = [];
333     }
334
335     $form_state->setRedirect('<current>');
336     $form_state->setValues([]);
337   }
338
339   /**
340    * {@inheritdoc}
341    */
342   public function getCacheMaxAge() {
343     return Cache::PERMANENT;
344   }
345
346   /**
347    * {@inheritdoc}
348    */
349   public function getCacheContexts() {
350     $contexts = [];
351     if ($this->options['expose_sort_order']) {
352       // The sort order query arg is just important in case there is a exposed
353       // sort order.
354       $has_exposed_sort_handler = FALSE;
355       /** @var \Drupal\views\Plugin\views\sort\SortPluginBase $sort_handler */
356       foreach ($this->displayHandler->getHandlers('sort') as $sort_handler) {
357         if ($sort_handler->isExposed()) {
358           $has_exposed_sort_handler = TRUE;
359         }
360       }
361
362       if ($has_exposed_sort_handler) {
363         $contexts[] = 'url.query_args:sort_order';
364       }
365     }
366
367     // Merge in cache contexts for all exposed filters to prevent display of
368     // cached forms.
369     foreach ($this->displayHandler->getHandlers('filter') as $filter_hander) {
370       if ($filter_hander->isExposed()) {
371         $contexts = Cache::mergeContexts($contexts, $filter_hander->getCacheContexts());
372       }
373     }
374
375     return $contexts;
376   }
377
378   /**
379    * {@inheritdoc}
380    */
381   public function getCacheTags() {
382     return [];
383   }
384
385 }