Mercurial > hg > isophonics-drupal-site
comparison core/lib/Drupal/Core/Menu/MenuLinkTree.php @ 0:4c8ae668cc8c
Initial import (non-working)
author | Chris Cannam |
---|---|
date | Wed, 29 Nov 2017 16:09:58 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4c8ae668cc8c |
---|---|
1 <?php | |
2 | |
3 namespace Drupal\Core\Menu; | |
4 | |
5 use Drupal\Component\Utility\NestedArray; | |
6 use Drupal\Core\Access\AccessResultInterface; | |
7 use Drupal\Core\Cache\CacheableMetadata; | |
8 use Drupal\Core\Controller\ControllerResolverInterface; | |
9 use Drupal\Core\Routing\RouteProviderInterface; | |
10 use Drupal\Core\Template\Attribute; | |
11 | |
12 /** | |
13 * Implements the loading, transforming and rendering of menu link trees. | |
14 */ | |
15 class MenuLinkTree implements MenuLinkTreeInterface { | |
16 | |
17 /** | |
18 * The menu link tree storage. | |
19 * | |
20 * @var \Drupal\Core\Menu\MenuTreeStorageInterface | |
21 */ | |
22 protected $treeStorage; | |
23 | |
24 /** | |
25 * The menu link plugin manager. | |
26 * | |
27 * @var \Drupal\Core\Menu\MenuLinkManagerInterface | |
28 */ | |
29 protected $menuLinkManager; | |
30 | |
31 /** | |
32 * The route provider to load routes by name. | |
33 * | |
34 * @var \Drupal\Core\Routing\RouteProviderInterface | |
35 */ | |
36 protected $routeProvider; | |
37 | |
38 /** | |
39 * The active menu trail service. | |
40 * | |
41 * @var \Drupal\Core\Menu\MenuActiveTrailInterface | |
42 */ | |
43 protected $menuActiveTrail; | |
44 | |
45 /** | |
46 * The controller resolver. | |
47 * | |
48 * @var \Drupal\Core\Controller\ControllerResolverInterface | |
49 */ | |
50 protected $controllerResolver; | |
51 | |
52 /** | |
53 * Constructs a \Drupal\Core\Menu\MenuLinkTree object. | |
54 * | |
55 * @param \Drupal\Core\Menu\MenuTreeStorageInterface $tree_storage | |
56 * The menu link tree storage. | |
57 * @param \Drupal\Core\Menu\MenuLinkManagerInterface $menu_link_manager | |
58 * The menu link plugin manager. | |
59 * @param \Drupal\Core\Routing\RouteProviderInterface $route_provider | |
60 * The route provider to load routes by name. | |
61 * @param \Drupal\Core\Menu\MenuActiveTrailInterface $menu_active_trail | |
62 * The active menu trail service. | |
63 * @param \Drupal\Core\Controller\ControllerResolverInterface $controller_resolver | |
64 * The controller resolver. | |
65 */ | |
66 public function __construct(MenuTreeStorageInterface $tree_storage, MenuLinkManagerInterface $menu_link_manager, RouteProviderInterface $route_provider, MenuActiveTrailInterface $menu_active_trail, ControllerResolverInterface $controller_resolver) { | |
67 $this->treeStorage = $tree_storage; | |
68 $this->menuLinkManager = $menu_link_manager; | |
69 $this->routeProvider = $route_provider; | |
70 $this->menuActiveTrail = $menu_active_trail; | |
71 $this->controllerResolver = $controller_resolver; | |
72 } | |
73 | |
74 /** | |
75 * {@inheritdoc} | |
76 */ | |
77 public function getCurrentRouteMenuTreeParameters($menu_name) { | |
78 $active_trail = $this->menuActiveTrail->getActiveTrailIds($menu_name); | |
79 | |
80 $parameters = new MenuTreeParameters(); | |
81 $parameters->setActiveTrail($active_trail) | |
82 // We want links in the active trail to be expanded. | |
83 ->addExpandedParents($active_trail) | |
84 // We marked the links in the active trail to be expanded, but we also | |
85 // want their descendants that have the "expanded" flag enabled to be | |
86 // expanded. | |
87 ->addExpandedParents($this->treeStorage->getExpanded($menu_name, $active_trail)); | |
88 | |
89 return $parameters; | |
90 } | |
91 | |
92 /** | |
93 * {@inheritdoc} | |
94 */ | |
95 public function load($menu_name, MenuTreeParameters $parameters) { | |
96 $data = $this->treeStorage->loadTreeData($menu_name, $parameters); | |
97 // Pre-load all the route objects in the tree for access checks. | |
98 if ($data['route_names']) { | |
99 $this->routeProvider->getRoutesByNames($data['route_names']); | |
100 } | |
101 return $this->createInstances($data['tree']); | |
102 } | |
103 | |
104 /** | |
105 * Returns a tree containing of MenuLinkTreeElement based upon tree data. | |
106 * | |
107 * This method converts the tree representation as array coming from the tree | |
108 * storage to a tree containing a list of MenuLinkTreeElement[]. | |
109 * | |
110 * @param array $data_tree | |
111 * The tree data coming from the menu tree storage. | |
112 * | |
113 * @return \Drupal\Core\Menu\MenuLinkTreeElement[] | |
114 * An array containing the elements of a menu tree. | |
115 */ | |
116 protected function createInstances(array $data_tree) { | |
117 $tree = []; | |
118 foreach ($data_tree as $key => $element) { | |
119 $subtree = $this->createInstances($element['subtree']); | |
120 // Build a MenuLinkTreeElement out of the menu tree link definition: | |
121 // transform the tree link definition into a link definition and store | |
122 // tree metadata. | |
123 $tree[$key] = new MenuLinkTreeElement( | |
124 $this->menuLinkManager->createInstance($element['definition']['id']), | |
125 (bool) $element['has_children'], | |
126 (int) $element['depth'], | |
127 (bool) $element['in_active_trail'], | |
128 $subtree | |
129 ); | |
130 } | |
131 return $tree; | |
132 } | |
133 | |
134 /** | |
135 * {@inheritdoc} | |
136 */ | |
137 public function transform(array $tree, array $manipulators) { | |
138 foreach ($manipulators as $manipulator) { | |
139 $callable = $manipulator['callable']; | |
140 $callable = $this->controllerResolver->getControllerFromDefinition($callable); | |
141 // Prepare the arguments for the menu tree manipulator callable; the first | |
142 // argument is always the menu link tree. | |
143 if (isset($manipulator['args'])) { | |
144 array_unshift($manipulator['args'], $tree); | |
145 $tree = call_user_func_array($callable, $manipulator['args']); | |
146 } | |
147 else { | |
148 $tree = call_user_func($callable, $tree); | |
149 } | |
150 } | |
151 return $tree; | |
152 } | |
153 | |
154 /** | |
155 * {@inheritdoc} | |
156 */ | |
157 public function build(array $tree) { | |
158 $tree_access_cacheability = new CacheableMetadata(); | |
159 $tree_link_cacheability = new CacheableMetadata(); | |
160 $items = $this->buildItems($tree, $tree_access_cacheability, $tree_link_cacheability); | |
161 | |
162 $build = []; | |
163 | |
164 // Apply the tree-wide gathered access cacheability metadata and link | |
165 // cacheability metadata to the render array. This ensures that the | |
166 // rendered menu is varied by the cache contexts that the access results | |
167 // and (dynamic) links depended upon, and invalidated by the cache tags | |
168 // that may change the values of the access results and links. | |
169 $tree_cacheability = $tree_access_cacheability->merge($tree_link_cacheability); | |
170 $tree_cacheability->applyTo($build); | |
171 | |
172 if ($items) { | |
173 // Make sure drupal_render() does not re-order the links. | |
174 $build['#sorted'] = TRUE; | |
175 // Get the menu name from the last link. | |
176 $item = end($items); | |
177 $link = $item['original_link']; | |
178 $menu_name = $link->getMenuName(); | |
179 // Add the theme wrapper for outer markup. | |
180 // Allow menu-specific theme overrides. | |
181 $build['#theme'] = 'menu__' . strtr($menu_name, '-', '_'); | |
182 $build['#menu_name'] = $menu_name; | |
183 $build['#items'] = $items; | |
184 // Set cache tag. | |
185 $build['#cache']['tags'][] = 'config:system.menu.' . $menu_name; | |
186 } | |
187 | |
188 return $build; | |
189 } | |
190 | |
191 /** | |
192 * Builds the #items property for a menu tree's renderable array. | |
193 * | |
194 * Helper function for ::build(). | |
195 * | |
196 * @param \Drupal\Core\Menu\MenuLinkTreeElement[] $tree | |
197 * A data structure representing the tree, as returned from | |
198 * MenuLinkTreeInterface::load(). | |
199 * @param \Drupal\Core\Cache\CacheableMetadata &$tree_access_cacheability | |
200 * Internal use only. The aggregated cacheability metadata for the access | |
201 * results across the entire tree. Used when rendering the root level. | |
202 * @param \Drupal\Core\Cache\CacheableMetadata &$tree_link_cacheability | |
203 * Internal use only. The aggregated cacheability metadata for the menu | |
204 * links across the entire tree. Used when rendering the root level. | |
205 * | |
206 * @return array | |
207 * The value to use for the #items property of a renderable menu. | |
208 * | |
209 * @throws \DomainException | |
210 */ | |
211 protected function buildItems(array $tree, CacheableMetadata &$tree_access_cacheability, CacheableMetadata &$tree_link_cacheability) { | |
212 $items = []; | |
213 | |
214 foreach ($tree as $data) { | |
215 /** @var \Drupal\Core\Menu\MenuLinkInterface $link */ | |
216 $link = $data->link; | |
217 // Generally we only deal with visible links, but just in case. | |
218 if (!$link->isEnabled()) { | |
219 continue; | |
220 } | |
221 | |
222 if ($data->access !== NULL && !$data->access instanceof AccessResultInterface) { | |
223 throw new \DomainException('MenuLinkTreeElement::access must be either NULL or an AccessResultInterface object.'); | |
224 } | |
225 | |
226 // Gather the access cacheability of every item in the menu link tree, | |
227 // including inaccessible items. This allows us to render cache the menu | |
228 // tree, yet still automatically vary the rendered menu by the same cache | |
229 // contexts that the access results vary by. | |
230 // However, if $data->access is not an AccessResultInterface object, this | |
231 // will still render the menu link, because this method does not want to | |
232 // require access checking to be able to render a menu tree. | |
233 if ($data->access instanceof AccessResultInterface) { | |
234 $tree_access_cacheability = $tree_access_cacheability->merge(CacheableMetadata::createFromObject($data->access)); | |
235 } | |
236 | |
237 // Gather the cacheability of every item in the menu link tree. Some links | |
238 // may be dynamic: they may have a dynamic text (e.g. a "Hi, <user>" link | |
239 // text, which would vary by 'user' cache context), or a dynamic route | |
240 // name or route parameters. | |
241 $tree_link_cacheability = $tree_link_cacheability->merge(CacheableMetadata::createFromObject($data->link)); | |
242 | |
243 // Only render accessible links. | |
244 if ($data->access instanceof AccessResultInterface && !$data->access->isAllowed()) { | |
245 continue; | |
246 } | |
247 $element = []; | |
248 | |
249 // Set a variable for the <li> tag. Only set 'expanded' to true if the | |
250 // link also has visible children within the current tree. | |
251 $element['is_expanded'] = FALSE; | |
252 $element['is_collapsed'] = FALSE; | |
253 if ($data->hasChildren && !empty($data->subtree)) { | |
254 $element['is_expanded'] = TRUE; | |
255 } | |
256 elseif ($data->hasChildren) { | |
257 $element['is_collapsed'] = TRUE; | |
258 } | |
259 // Set a helper variable to indicate whether the link is in the active | |
260 // trail. | |
261 $element['in_active_trail'] = FALSE; | |
262 if ($data->inActiveTrail) { | |
263 $element['in_active_trail'] = TRUE; | |
264 } | |
265 | |
266 // Note: links are rendered in the menu.html.twig template; and they | |
267 // automatically bubble their associated cacheability metadata. | |
268 $element['attributes'] = new Attribute(); | |
269 $element['title'] = $link->getTitle(); | |
270 $element['url'] = $link->getUrlObject(); | |
271 $element['url']->setOption('set_active_class', TRUE); | |
272 $element['below'] = $data->subtree ? $this->buildItems($data->subtree, $tree_access_cacheability, $tree_link_cacheability) : []; | |
273 if (isset($data->options)) { | |
274 $element['url']->setOptions(NestedArray::mergeDeep($element['url']->getOptions(), $data->options)); | |
275 } | |
276 $element['original_link'] = $link; | |
277 // Index using the link's unique ID. | |
278 $items[$link->getPluginId()] = $element; | |
279 } | |
280 | |
281 return $items; | |
282 } | |
283 | |
284 /** | |
285 * {@inheritdoc} | |
286 */ | |
287 public function maxDepth() { | |
288 return $this->treeStorage->maxDepth(); | |
289 } | |
290 | |
291 /** | |
292 * {@inheritdoc} | |
293 */ | |
294 public function getSubtreeHeight($id) { | |
295 return $this->treeStorage->getSubtreeHeight($id); | |
296 } | |
297 | |
298 /** | |
299 * {@inheritdoc} | |
300 */ | |
301 public function getExpanded($menu_name, array $parents) { | |
302 return $this->treeStorage->getExpanded($menu_name, $parents); | |
303 } | |
304 | |
305 } |