23409ec56da04d8a8b72dc6b5732ed943006aa74
[yaffs-website] / web / core / modules / views / src / Plugin / views / display / Page.php
1 <?php
2
3 namespace Drupal\views\Plugin\views\display;
4
5 use Drupal\Component\Utility\Xss;
6 use Drupal\Core\Entity\EntityStorageInterface;
7 use Drupal\Core\Form\FormStateInterface;
8 use Drupal\Core\State\StateInterface;
9 use Drupal\Core\Routing\RouteProviderInterface;
10 use Symfony\Component\DependencyInjection\ContainerInterface;
11 use Symfony\Component\Routing\Route;
12
13 /**
14  * The plugin that handles a full page.
15  *
16  * @ingroup views_display_plugins
17  *
18  * @ViewsDisplay(
19  *   id = "page",
20  *   title = @Translation("Page"),
21  *   help = @Translation("Display the view as a page, with a URL and menu links."),
22  *   uses_menu_links = TRUE,
23  *   uses_route = TRUE,
24  *   contextual_links_locations = {"page"},
25  *   theme = "views_view",
26  *   admin = @Translation("Page")
27  * )
28  */
29 class Page extends PathPluginBase {
30
31   /**
32    * The current page render array.
33    *
34    * @var array
35    */
36   protected static $pageRenderArray;
37
38   /**
39    * Whether the display allows attachments.
40    *
41    * @var bool
42    */
43   protected $usesAttachments = TRUE;
44
45   /**
46    * The menu storage.
47    *
48    * @var \Drupal\Core\Entity\EntityStorageInterface
49    */
50   protected $menuStorage;
51
52   /**
53    * Constructs a Page object.
54    *
55    * @param array $configuration
56    *   A configuration array containing information about the plugin instance.
57    * @param string $plugin_id
58    *   The plugin_id for the plugin instance.
59    * @param mixed $plugin_definition
60    *   The plugin implementation definition.
61    * @param \Drupal\Core\Routing\RouteProviderInterface $route_provider
62    *   The route provider.
63    * @param \Drupal\Core\State\StateInterface $state
64    *   The state key value store.
65    * @param \Drupal\Core\Entity\EntityStorageInterface $menu_storage
66    *   The menu storage.
67    */
68   public function __construct(array $configuration, $plugin_id, $plugin_definition, RouteProviderInterface $route_provider, StateInterface $state, EntityStorageInterface $menu_storage) {
69     parent::__construct($configuration, $plugin_id, $plugin_definition, $route_provider, $state);
70     $this->menuStorage = $menu_storage;
71   }
72
73   /**
74    * {@inheritdoc}
75    */
76   public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
77     return new static(
78       $configuration,
79       $plugin_id,
80       $plugin_definition,
81       $container->get('router.route_provider'),
82       $container->get('state'),
83       $container->get('entity.manager')->getStorage('menu')
84     );
85   }
86
87   /**
88    * {@inheritdoc}
89    */
90   protected function getRoute($view_id, $display_id) {
91     $route = parent::getRoute($view_id, $display_id);
92
93     // Explicitly set HTML as the format for Page displays.
94     $route->setRequirement('_format', 'html');
95
96     return $route;
97   }
98
99   /**
100    * Sets the current page views render array.
101    *
102    * @param array $element
103    *   (optional) A render array. If not specified the previous element is
104    *   returned.
105    *
106    * @return array
107    *   The page render array.
108    */
109   public static function &setPageRenderArray(array &$element = NULL) {
110     if (isset($element)) {
111       static::$pageRenderArray = &$element;
112     }
113
114     return static::$pageRenderArray;
115   }
116
117   /**
118    * Gets the current views page render array.
119    *
120    * @return array
121    *   The page render array.
122    */
123   public static function &getPageRenderArray() {
124     return static::$pageRenderArray;
125   }
126
127   /**
128    * {@inheritdoc}
129    */
130   protected function defineOptions() {
131     $options = parent::defineOptions();
132
133     $options['menu'] = [
134       'contains' => [
135         'type' => ['default' => 'none'],
136         'title' => ['default' => ''],
137         'description' => ['default' => ''],
138         'weight' => ['default' => 0],
139         'enabled' => ['default' => TRUE],
140         'menu_name' => ['default' => 'main'],
141         'parent' => ['default' => ''],
142         'context' => ['default' => ''],
143         'expanded' => ['default' => FALSE],
144       ],
145     ];
146     $options['tab_options'] = [
147       'contains' => [
148         'type' => ['default' => 'none'],
149         'title' => ['default' => ''],
150         'description' => ['default' => ''],
151         'weight' => ['default' => 0],
152       ],
153     ];
154
155     return $options;
156   }
157
158   /**
159    * {@inheritdoc}
160    */
161   public static function buildBasicRenderable($view_id, $display_id, array $args = [], Route $route = NULL) {
162     $build = parent::buildBasicRenderable($view_id, $display_id, $args);
163
164     if ($route) {
165       $build['#view_id'] = $route->getDefault('view_id');
166       $build['#view_display_plugin_id'] = $route->getOption('_view_display_plugin_id');
167       $build['#view_display_show_admin_links'] = $route->getOption('_view_display_show_admin_links');
168     }
169     else {
170       throw new \BadFunctionCallException('Missing route parameters.');
171     }
172
173     return $build;
174   }
175
176   /**
177    * {@inheritdoc}
178    */
179   public function execute() {
180     parent::execute();
181
182     // And now render the view.
183     $render = $this->view->render();
184
185     // First execute the view so it's possible to get tokens for the title.
186     // And the title, which is much easier.
187     // @todo Figure out how to support custom response objects. Maybe for pages
188     //   it should be dropped.
189     if (is_array($render)) {
190       $render += [
191         '#title' => ['#markup' => $this->view->getTitle(), '#allowed_tags' => Xss::getHtmlTagList()],
192       ];
193     }
194     return $render;
195   }
196
197   /**
198    * {@inheritdoc}
199    */
200   public function optionsSummary(&$categories, &$options) {
201     parent::optionsSummary($categories, $options);
202
203     $menu = $this->getOption('menu');
204     if (!is_array($menu)) {
205       $menu = ['type' => 'none'];
206     }
207     switch ($menu['type']) {
208       case 'none':
209       default:
210         $menu_str = $this->t('No menu');
211         break;
212       case 'normal':
213         $menu_str = $this->t('Normal: @title', ['@title' => $menu['title']]);
214         break;
215       case 'tab':
216       case 'default tab':
217         $menu_str = $this->t('Tab: @title', ['@title' => $menu['title']]);
218         break;
219     }
220
221     $options['menu'] = [
222       'category' => 'page',
223       'title' => $this->t('Menu'),
224       'value' => views_ui_truncate($menu_str, 24),
225     ];
226
227     // This adds a 'Settings' link to the style_options setting if the style
228     // has options.
229     if ($menu['type'] == 'default tab') {
230       $options['menu']['setting'] = $this->t('Parent menu item');
231       $options['menu']['links']['tab_options'] = $this->t('Change settings for the parent menu');
232     }
233   }
234
235   /**
236    * {@inheritdoc}
237    */
238   public function buildOptionsForm(&$form, FormStateInterface $form_state) {
239     parent::buildOptionsForm($form, $form_state);
240
241     switch ($form_state->get('section')) {
242       case 'menu':
243         $form['#title'] .= $this->t('Menu item entry');
244         $form['menu'] = [
245           '#prefix' => '<div class="clearfix">',
246           '#suffix' => '</div>',
247           '#tree' => TRUE,
248         ];
249         $menu = $this->getOption('menu');
250         if (empty($menu)) {
251           $menu = ['type' => 'none', 'title' => '', 'weight' => 0, 'expanded' => FALSE];
252         }
253         $form['menu']['type'] = [
254           '#prefix' => '<div class="views-left-30">',
255           '#suffix' => '</div>',
256           '#title' => $this->t('Type'),
257           '#type' => 'radios',
258           '#options' => [
259             'none' => $this->t('No menu entry'),
260             'normal' => $this->t('Normal menu entry'),
261             'tab' => $this->t('Menu tab'),
262             'default tab' => $this->t('Default menu tab'),
263           ],
264           '#default_value' => $menu['type'],
265         ];
266
267         $form['menu']['title'] = [
268           '#prefix' => '<div class="views-left-50">',
269           '#title' => $this->t('Menu link title'),
270           '#type' => 'textfield',
271           '#default_value' => $menu['title'],
272           '#states' => [
273             'visible' => [
274               [
275                 ':input[name="menu[type]"]' => ['value' => 'normal'],
276               ],
277               [
278                 ':input[name="menu[type]"]' => ['value' => 'tab'],
279               ],
280               [
281                 ':input[name="menu[type]"]' => ['value' => 'default tab'],
282               ],
283             ],
284           ],
285         ];
286         $form['menu']['description'] = [
287           '#title' => $this->t('Description'),
288           '#type' => 'textfield',
289           '#default_value' => $menu['description'],
290           '#description' => $this->t("Shown when hovering over the menu link."),
291           '#states' => [
292             'visible' => [
293               [
294                 ':input[name="menu[type]"]' => ['value' => 'normal'],
295               ],
296               [
297                 ':input[name="menu[type]"]' => ['value' => 'tab'],
298               ],
299               [
300                 ':input[name="menu[type]"]' => ['value' => 'default tab'],
301               ],
302             ],
303           ],
304         ];
305         $form['menu']['expanded'] = [
306           '#title' => $this->t('Show as expanded'),
307           '#type' => 'checkbox',
308           '#default_value' => !empty($menu['expanded']),
309           '#description' => $this->t('If selected and this menu link has children, the menu will always appear expanded.'),
310         ];
311
312         // Only display the parent selector if Menu UI module is enabled.
313         $menu_parent = $menu['menu_name'] . ':' . $menu['parent'];
314         if (\Drupal::moduleHandler()->moduleExists('menu_ui')) {
315           $menu_link = 'views_view:views.' . $form_state->get('view')->id() . '.' . $form_state->get('display_id');
316           $form['menu']['parent'] = \Drupal::service('menu.parent_form_selector')->parentSelectElement($menu_parent, $menu_link);
317           $form['menu']['parent'] += [
318             '#title' => $this->t('Parent'),
319             '#description' => $this->t('The maximum depth for a link and all its children is fixed. Some menu links may not be available as parents if selecting them would exceed this limit.'),
320             '#attributes' => ['class' => ['menu-title-select']],
321             '#states' => [
322               'visible' => [
323                 [
324                   ':input[name="menu[type]"]' => ['value' => 'normal'],
325                 ],
326                 [
327                   ':input[name="menu[type]"]' => ['value' => 'tab'],
328                 ],
329               ],
330             ],
331           ];
332         }
333         else {
334           $form['menu']['parent'] = [
335             '#type' => 'value',
336             '#value' => $menu_parent,
337           ];
338           $form['menu']['markup'] = [
339             '#markup' => $this->t('Menu selection requires the activation of Menu UI module.'),
340           ];
341         }
342         $form['menu']['weight'] = [
343           '#title' => $this->t('Weight'),
344           '#type' => 'textfield',
345           '#default_value' => isset($menu['weight']) ? $menu['weight'] : 0,
346           '#description' => $this->t('In the menu, the heavier links will sink and the lighter links will be positioned nearer the top.'),
347           '#states' => [
348             'visible' => [
349               [
350                 ':input[name="menu[type]"]' => ['value' => 'normal'],
351               ],
352               [
353                 ':input[name="menu[type]"]' => ['value' => 'tab'],
354               ],
355               [
356                 ':input[name="menu[type]"]' => ['value' => 'default tab'],
357               ],
358             ],
359           ],
360         ];
361         $form['menu']['context'] = [
362           '#title' => $this->t('Context'),
363           '#suffix' => '</div>',
364           '#type' => 'checkbox',
365           '#default_value' => !empty($menu['context']),
366           '#description' => $this->t('Displays the link in contextual links'),
367           '#states' => [
368             'visible' => [
369               ':input[name="menu[type]"]' => ['value' => 'tab'],
370             ],
371           ],
372         ];
373         break;
374       case 'tab_options':
375         $form['#title'] .= $this->t('Default tab options');
376         $tab_options = $this->getOption('tab_options');
377         if (empty($tab_options)) {
378           $tab_options = ['type' => 'none', 'title' => '', 'weight' => 0];
379         }
380
381         $form['tab_markup'] = [
382           '#markup' => '<div class="js-form-item form-item description">' . $this->t('When providing a menu item as a tab, Drupal needs to know what the parent menu item of that tab will be. Sometimes the parent will already exist, but other times you will need to have one created. The path of a parent item will always be the same path with the last part left off. i.e, if the path to this view is <em>foo/bar/baz</em>, the parent path would be <em>foo/bar</em>.') . '</div>',
383         ];
384
385         $form['tab_options'] = [
386           '#prefix' => '<div class="clearfix">',
387           '#suffix' => '</div>',
388           '#tree' => TRUE,
389         ];
390         $form['tab_options']['type'] = [
391           '#prefix' => '<div class="views-left-25">',
392           '#suffix' => '</div>',
393           '#title' => $this->t('Parent menu item'),
394           '#type' => 'radios',
395           '#options' => ['none' => $this->t('Already exists'), 'normal' => $this->t('Normal menu item'), 'tab' => $this->t('Menu tab')],
396           '#default_value' => $tab_options['type'],
397         ];
398         $form['tab_options']['title'] = [
399           '#prefix' => '<div class="views-left-75">',
400           '#title' => $this->t('Title'),
401           '#type' => 'textfield',
402           '#default_value' => $tab_options['title'],
403           '#description' => $this->t('If creating a parent menu item, enter the title of the item.'),
404           '#states' => [
405             'visible' => [
406               [
407                 ':input[name="tab_options[type]"]' => ['value' => 'normal'],
408               ],
409               [
410                 ':input[name="tab_options[type]"]' => ['value' => 'tab'],
411               ],
412             ],
413           ],
414         ];
415         $form['tab_options']['description'] = [
416           '#title' => $this->t('Description'),
417           '#type' => 'textfield',
418           '#default_value' => $tab_options['description'],
419           '#description' => $this->t('If creating a parent menu item, enter the description of the item.'),
420           '#states' => [
421             'visible' => [
422               [
423                 ':input[name="tab_options[type]"]' => ['value' => 'normal'],
424               ],
425               [
426                 ':input[name="tab_options[type]"]' => ['value' => 'tab'],
427               ],
428             ],
429           ],
430         ];
431         $form['tab_options']['weight'] = [
432           '#suffix' => '</div>',
433           '#title' => $this->t('Tab weight'),
434           '#type' => 'textfield',
435           '#default_value' => $tab_options['weight'],
436           '#size' => 5,
437           '#description' => $this->t('If the parent menu item is a tab, enter the weight of the tab. Heavier tabs will sink and the lighter tabs will be positioned nearer to the first menu item.'),
438           '#states' => [
439             'visible' => [
440               ':input[name="tab_options[type]"]' => ['value' => 'tab'],
441             ],
442           ],
443         ];
444         break;
445     }
446   }
447
448   /**
449    * {@inheritdoc}
450    */
451   public function validateOptionsForm(&$form, FormStateInterface $form_state) {
452     parent::validateOptionsForm($form, $form_state);
453
454     if ($form_state->get('section') == 'menu') {
455       $path = $this->getOption('path');
456       $menu_type = $form_state->getValue(['menu', 'type']);
457       if ($menu_type == 'normal' && strpos($path, '%') !== FALSE) {
458         $form_state->setError($form['menu']['type'], $this->t('Views cannot create normal menu items for paths with a % in them.'));
459       }
460
461       if ($menu_type == 'default tab' || $menu_type == 'tab') {
462         $bits = explode('/', $path);
463         $last = array_pop($bits);
464         if ($last == '%') {
465           $form_state->setError($form['menu']['type'], $this->t('A display whose path ends with a % cannot be a tab.'));
466         }
467       }
468
469       if ($menu_type != 'none' && $form_state->isValueEmpty(['menu', 'title'])) {
470         $form_state->setError($form['menu']['title'], $this->t('Title is required for this menu type.'));
471       }
472     }
473   }
474
475   /**
476    * {@inheritdoc}
477    */
478   public function submitOptionsForm(&$form, FormStateInterface $form_state) {
479     parent::submitOptionsForm($form, $form_state);
480
481     switch ($form_state->get('section')) {
482       case 'menu':
483         $menu = $form_state->getValue('menu');
484         list($menu['menu_name'], $menu['parent']) = explode(':', $menu['parent'], 2);
485         $this->setOption('menu', $menu);
486         // send ajax form to options page if we use it.
487         if ($form_state->getValue(['menu', 'type']) == 'default tab') {
488           $form_state->get('view')->addFormToStack('display', $this->display['id'], 'tab_options');
489         }
490         break;
491       case 'tab_options':
492         $this->setOption('tab_options', $form_state->getValue('tab_options'));
493         break;
494     }
495   }
496
497   /**
498    * {@inheritdoc}
499    */
500   public function validate() {
501     $errors = parent::validate();
502
503     $menu = $this->getOption('menu');
504     if (!empty($menu['type']) && $menu['type'] != 'none' && empty($menu['title'])) {
505       $errors[] = $this->t('Display @display is set to use a menu but the menu link text is not set.', ['@display' => $this->display['display_title']]);
506     }
507
508     if ($menu['type'] == 'default tab') {
509       $tab_options = $this->getOption('tab_options');
510       if (!empty($tab_options['type']) && $tab_options['type'] != 'none' && empty($tab_options['title'])) {
511         $errors[] = $this->t('Display @display is set to use a parent menu but the parent menu link text is not set.', ['@display' => $this->display['display_title']]);
512       }
513     }
514
515     return $errors;
516   }
517
518   /**
519    * {@inheritdoc}
520    */
521   public function getArgumentText() {
522     return [
523       'filter value not present' => $this->t('When the filter value is <em>NOT</em> in the URL'),
524       'filter value present' => $this->t('When the filter value <em>IS</em> in the URL or a default is provided'),
525       'description' => $this->t('The contextual filter values are provided by the URL.'),
526     ];
527   }
528
529   /**
530    * {@inheritdoc}
531    */
532   public function getPagerText() {
533     return [
534       'items per page title' => $this->t('Items per page'),
535       'items per page description' => $this->t('Enter 0 for no limit.'),
536     ];
537   }
538
539   /**
540    * {@inheritdoc}
541    */
542   public function calculateDependencies() {
543     $dependencies = parent::calculateDependencies();
544
545     $menu = $this->getOption('menu');
546     if ($menu['type'] === 'normal' && ($menu_entity = $this->menuStorage->load($menu['menu_name']))) {
547       $dependencies[$menu_entity->getConfigDependencyKey()][] = $menu_entity->getConfigDependencyName();
548     }
549
550     return $dependencies;
551   }
552
553 }