Security update for Core, with self-updated composer
[yaffs-website] / web / core / modules / system / src / Plugin / Block / SystemMenuBlock.php
1 <?php
2
3 namespace Drupal\system\Plugin\Block;
4
5 use Drupal\Core\Block\BlockBase;
6 use Drupal\Core\Cache\Cache;
7 use Drupal\Core\Form\FormStateInterface;
8 use Drupal\Core\Menu\MenuActiveTrailInterface;
9 use Drupal\Core\Menu\MenuLinkTreeInterface;
10 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
11 use Symfony\Component\DependencyInjection\ContainerInterface;
12
13 /**
14  * Provides a generic Menu block.
15  *
16  * @Block(
17  *   id = "system_menu_block",
18  *   admin_label = @Translation("Menu"),
19  *   category = @Translation("Menus"),
20  *   deriver = "Drupal\system\Plugin\Derivative\SystemMenuBlock"
21  * )
22  */
23 class SystemMenuBlock extends BlockBase implements ContainerFactoryPluginInterface {
24
25   /**
26    * The menu link tree service.
27    *
28    * @var \Drupal\Core\Menu\MenuLinkTreeInterface
29    */
30   protected $menuTree;
31
32   /**
33    * The active menu trail service.
34    *
35    * @var \Drupal\Core\Menu\MenuActiveTrailInterface
36    */
37   protected $menuActiveTrail;
38
39   /**
40    * Constructs a new SystemMenuBlock.
41    *
42    * @param array $configuration
43    *   A configuration array containing information about the plugin instance.
44    * @param string $plugin_id
45    *   The plugin_id for the plugin instance.
46    * @param array $plugin_definition
47    *   The plugin implementation definition.
48    * @param \Drupal\Core\Menu\MenuLinkTreeInterface $menu_tree
49    *   The menu tree service.
50    * @param \Drupal\Core\Menu\MenuActiveTrailInterface $menu_active_trail
51    *   The active menu trail service.
52    */
53   public function __construct(array $configuration, $plugin_id, $plugin_definition, MenuLinkTreeInterface $menu_tree, MenuActiveTrailInterface $menu_active_trail) {
54     parent::__construct($configuration, $plugin_id, $plugin_definition);
55     $this->menuTree = $menu_tree;
56     $this->menuActiveTrail = $menu_active_trail;
57   }
58
59   /**
60    * {@inheritdoc}
61    */
62   public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
63     return new static(
64       $configuration,
65       $plugin_id,
66       $plugin_definition,
67       $container->get('menu.link_tree'),
68       $container->get('menu.active_trail')
69     );
70   }
71
72   /**
73    * {@inheritdoc}
74    */
75   public function blockForm($form, FormStateInterface $form_state) {
76     $config = $this->configuration;
77
78     $defaults = $this->defaultConfiguration();
79     $form['menu_levels'] = [
80       '#type' => 'details',
81       '#title' => $this->t('Menu levels'),
82       // Open if not set to defaults.
83       '#open' => $defaults['level'] !== $config['level'] || $defaults['depth'] !== $config['depth'],
84       '#process' => [[get_class(), 'processMenuLevelParents']],
85     ];
86
87     $options = range(0, $this->menuTree->maxDepth());
88     unset($options[0]);
89
90     $form['menu_levels']['level'] = [
91       '#type' => 'select',
92       '#title' => $this->t('Initial visibility level'),
93       '#default_value' => $config['level'],
94       '#options' => $options,
95       '#description' => $this->t('The menu is only visible if the menu item for the current page is at this level or below it. Use level 1 to always display this menu.'),
96       '#required' => TRUE,
97     ];
98
99     $options[0] = $this->t('Unlimited');
100
101     $form['menu_levels']['depth'] = [
102       '#type' => 'select',
103       '#title' => $this->t('Number of levels to display'),
104       '#default_value' => $config['depth'],
105       '#options' => $options,
106       '#description' => $this->t('This maximum number includes the initial level.'),
107       '#required' => TRUE,
108     ];
109
110     return $form;
111   }
112
113   /**
114    * Form API callback: Processes the menu_levels field element.
115    *
116    * Adjusts the #parents of menu_levels to save its children at the top level.
117    */
118   public static function processMenuLevelParents(&$element, FormStateInterface $form_state, &$complete_form) {
119     array_pop($element['#parents']);
120     return $element;
121   }
122
123   /**
124    * {@inheritdoc}
125    */
126   public function blockSubmit($form, FormStateInterface $form_state) {
127     $this->configuration['level'] = $form_state->getValue('level');
128     $this->configuration['depth'] = $form_state->getValue('depth');
129   }
130
131   /**
132    * {@inheritdoc}
133    */
134   public function build() {
135     $menu_name = $this->getDerivativeId();
136     $parameters = $this->menuTree->getCurrentRouteMenuTreeParameters($menu_name);
137
138     // Adjust the menu tree parameters based on the block's configuration.
139     $level = $this->configuration['level'];
140     $depth = $this->configuration['depth'];
141     $parameters->setMinDepth($level);
142     // When the depth is configured to zero, there is no depth limit. When depth
143     // is non-zero, it indicates the number of levels that must be displayed.
144     // Hence this is a relative depth that we must convert to an actual
145     // (absolute) depth, that may never exceed the maximum depth.
146     if ($depth > 0) {
147       $parameters->setMaxDepth(min($level + $depth - 1, $this->menuTree->maxDepth()));
148     }
149
150     // For menu blocks with start level greater than 1, only show menu items
151     // from the current active trail. Adjust the root according to the current
152     // position in the menu in order to determine if we can show the subtree.
153     if ($level > 1) {
154       if (count($parameters->activeTrail) >= $level) {
155         // Active trail array is child-first. Reverse it, and pull the new menu
156         // root based on the parent of the configured start level.
157         $menu_trail_ids = array_reverse(array_values($parameters->activeTrail));
158         $menu_root = $menu_trail_ids[$level - 1];
159         $parameters->setRoot($menu_root)->setMinDepth(1);
160         if ($depth > 0) {
161           $parameters->setMaxDepth(min($level - 1 + $depth - 1, $this->menuTree->maxDepth()));
162         }
163       }
164       else {
165         return [];
166       }
167     }
168
169     $tree = $this->menuTree->load($menu_name, $parameters);
170     $manipulators = [
171       ['callable' => 'menu.default_tree_manipulators:checkAccess'],
172       ['callable' => 'menu.default_tree_manipulators:generateIndexAndSort'],
173     ];
174     $tree = $this->menuTree->transform($tree, $manipulators);
175     return $this->menuTree->build($tree);
176   }
177
178   /**
179    * {@inheritdoc}
180    */
181   public function defaultConfiguration() {
182     return [
183       'level' => 1,
184       'depth' => 0,
185     ];
186   }
187
188   /**
189    * {@inheritdoc}
190    */
191   public function getCacheTags() {
192     // Even when the menu block renders to the empty string for a user, we want
193     // the cache tag for this menu to be set: whenever the menu is changed, this
194     // menu block must also be re-rendered for that user, because maybe a menu
195     // link that is accessible for that user has been added.
196     $cache_tags = parent::getCacheTags();
197     $cache_tags[] = 'config:system.menu.' . $this->getDerivativeId();
198     return $cache_tags;
199   }
200
201   /**
202    * {@inheritdoc}
203    */
204   public function getCacheContexts() {
205     // ::build() uses MenuLinkTreeInterface::getCurrentRouteMenuTreeParameters()
206     // to generate menu tree parameters, and those take the active menu trail
207     // into account. Therefore, we must vary the rendered menu by the active
208     // trail of the rendered menu.
209     // Additional cache contexts, e.g. those that determine link text or
210     // accessibility of a menu, will be bubbled automatically.
211     $menu_name = $this->getDerivativeId();
212     return Cache::mergeContexts(parent::getCacheContexts(), ['route.menu_active_trails:' . $menu_name]);
213   }
214
215 }