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