Chris@0
|
1 <?php
|
Chris@0
|
2
|
Chris@0
|
3 namespace Drupal\menu_ui;
|
Chris@0
|
4
|
Chris@0
|
5 use Drupal\Component\Utility\NestedArray;
|
Chris@0
|
6 use Drupal\Core\Cache\CacheableMetadata;
|
Chris@0
|
7 use Drupal\Core\Entity\EntityForm;
|
Chris@0
|
8 use Drupal\Core\Form\FormStateInterface;
|
Chris@0
|
9 use Drupal\Core\Language\LanguageInterface;
|
Chris@0
|
10 use Drupal\Core\Link;
|
Chris@0
|
11 use Drupal\Core\Menu\MenuLinkManagerInterface;
|
Chris@0
|
12 use Drupal\Core\Menu\MenuLinkTreeElement;
|
Chris@0
|
13 use Drupal\Core\Menu\MenuLinkTreeInterface;
|
Chris@0
|
14 use Drupal\Core\Menu\MenuTreeParameters;
|
Chris@0
|
15 use Drupal\Core\Render\Element;
|
Chris@0
|
16 use Drupal\Core\Url;
|
Chris@0
|
17 use Drupal\Core\Utility\LinkGeneratorInterface;
|
Chris@0
|
18 use Symfony\Component\DependencyInjection\ContainerInterface;
|
Chris@0
|
19
|
Chris@0
|
20 /**
|
Chris@0
|
21 * Base form for menu edit forms.
|
Chris@0
|
22 *
|
Chris@0
|
23 * @internal
|
Chris@0
|
24 */
|
Chris@0
|
25 class MenuForm extends EntityForm {
|
Chris@0
|
26
|
Chris@0
|
27 /**
|
Chris@0
|
28 * The menu link manager.
|
Chris@0
|
29 *
|
Chris@0
|
30 * @var \Drupal\Core\Menu\MenuLinkManagerInterface
|
Chris@0
|
31 */
|
Chris@0
|
32 protected $menuLinkManager;
|
Chris@0
|
33
|
Chris@0
|
34 /**
|
Chris@0
|
35 * The menu tree service.
|
Chris@0
|
36 *
|
Chris@0
|
37 * @var \Drupal\Core\Menu\MenuLinkTreeInterface
|
Chris@0
|
38 */
|
Chris@0
|
39 protected $menuTree;
|
Chris@0
|
40
|
Chris@0
|
41 /**
|
Chris@0
|
42 * The link generator.
|
Chris@0
|
43 *
|
Chris@0
|
44 * @var \Drupal\Core\Utility\LinkGeneratorInterface
|
Chris@0
|
45 */
|
Chris@0
|
46 protected $linkGenerator;
|
Chris@0
|
47
|
Chris@0
|
48 /**
|
Chris@0
|
49 * The overview tree form.
|
Chris@0
|
50 *
|
Chris@0
|
51 * @var array
|
Chris@0
|
52 */
|
Chris@0
|
53 protected $overviewTreeForm = ['#tree' => TRUE];
|
Chris@0
|
54
|
Chris@0
|
55 /**
|
Chris@0
|
56 * Constructs a MenuForm object.
|
Chris@0
|
57 *
|
Chris@0
|
58 * @param \Drupal\Core\Menu\MenuLinkManagerInterface $menu_link_manager
|
Chris@0
|
59 * The menu link manager.
|
Chris@0
|
60 * @param \Drupal\Core\Menu\MenuLinkTreeInterface $menu_tree
|
Chris@0
|
61 * The menu tree service.
|
Chris@0
|
62 * @param \Drupal\Core\Utility\LinkGeneratorInterface $link_generator
|
Chris@0
|
63 * The link generator.
|
Chris@0
|
64 */
|
Chris@0
|
65 public function __construct(MenuLinkManagerInterface $menu_link_manager, MenuLinkTreeInterface $menu_tree, LinkGeneratorInterface $link_generator) {
|
Chris@0
|
66 $this->menuLinkManager = $menu_link_manager;
|
Chris@0
|
67 $this->menuTree = $menu_tree;
|
Chris@0
|
68 $this->linkGenerator = $link_generator;
|
Chris@0
|
69 }
|
Chris@0
|
70
|
Chris@0
|
71 /**
|
Chris@0
|
72 * {@inheritdoc}
|
Chris@0
|
73 */
|
Chris@0
|
74 public static function create(ContainerInterface $container) {
|
Chris@0
|
75 return new static(
|
Chris@0
|
76 $container->get('plugin.manager.menu.link'),
|
Chris@0
|
77 $container->get('menu.link_tree'),
|
Chris@0
|
78 $container->get('link_generator')
|
Chris@0
|
79 );
|
Chris@0
|
80 }
|
Chris@0
|
81
|
Chris@0
|
82 /**
|
Chris@0
|
83 * {@inheritdoc}
|
Chris@0
|
84 */
|
Chris@0
|
85 public function form(array $form, FormStateInterface $form_state) {
|
Chris@0
|
86 $menu = $this->entity;
|
Chris@0
|
87
|
Chris@0
|
88 if ($this->operation == 'edit') {
|
Chris@0
|
89 $form['#title'] = $this->t('Edit menu %label', ['%label' => $menu->label()]);
|
Chris@0
|
90 }
|
Chris@0
|
91
|
Chris@0
|
92 $form['label'] = [
|
Chris@0
|
93 '#type' => 'textfield',
|
Chris@0
|
94 '#title' => $this->t('Title'),
|
Chris@0
|
95 '#default_value' => $menu->label(),
|
Chris@0
|
96 '#required' => TRUE,
|
Chris@0
|
97 ];
|
Chris@0
|
98 $form['id'] = [
|
Chris@0
|
99 '#type' => 'machine_name',
|
Chris@0
|
100 '#title' => $this->t('Menu name'),
|
Chris@0
|
101 '#default_value' => $menu->id(),
|
Chris@0
|
102 '#maxlength' => MENU_MAX_MENU_NAME_LENGTH_UI,
|
Chris@0
|
103 '#description' => $this->t('A unique name to construct the URL for the menu. It must only contain lowercase letters, numbers and hyphens.'),
|
Chris@0
|
104 '#machine_name' => [
|
Chris@0
|
105 'exists' => [$this, 'menuNameExists'],
|
Chris@0
|
106 'source' => ['label'],
|
Chris@0
|
107 'replace_pattern' => '[^a-z0-9-]+',
|
Chris@0
|
108 'replace' => '-',
|
Chris@0
|
109 ],
|
Chris@0
|
110 // A menu's machine name cannot be changed.
|
Chris@0
|
111 '#disabled' => !$menu->isNew() || $menu->isLocked(),
|
Chris@0
|
112 ];
|
Chris@0
|
113 $form['description'] = [
|
Chris@0
|
114 '#type' => 'textfield',
|
Chris@0
|
115 '#title' => t('Administrative summary'),
|
Chris@0
|
116 '#maxlength' => 512,
|
Chris@0
|
117 '#default_value' => $menu->getDescription(),
|
Chris@0
|
118 ];
|
Chris@0
|
119
|
Chris@0
|
120 $form['langcode'] = [
|
Chris@0
|
121 '#type' => 'language_select',
|
Chris@0
|
122 '#title' => t('Menu language'),
|
Chris@0
|
123 '#languages' => LanguageInterface::STATE_ALL,
|
Chris@0
|
124 '#default_value' => $menu->language()->getId(),
|
Chris@0
|
125 ];
|
Chris@0
|
126
|
Chris@0
|
127 // Add menu links administration form for existing menus.
|
Chris@0
|
128 if (!$menu->isNew() || $menu->isLocked()) {
|
Chris@0
|
129 // Form API supports constructing and validating self-contained sections
|
Chris@0
|
130 // within forms, but does not allow handling the form section's submission
|
Chris@0
|
131 // equally separated yet. Therefore, we use a $form_state key to point to
|
Chris@0
|
132 // the parents of the form section.
|
Chris@0
|
133 // @see self::submitOverviewForm()
|
Chris@0
|
134 $form_state->set('menu_overview_form_parents', ['links']);
|
Chris@0
|
135 $form['links'] = [];
|
Chris@0
|
136 $form['links'] = $this->buildOverviewForm($form['links'], $form_state);
|
Chris@0
|
137 }
|
Chris@0
|
138
|
Chris@0
|
139 return parent::form($form, $form_state);
|
Chris@0
|
140 }
|
Chris@0
|
141
|
Chris@0
|
142 /**
|
Chris@0
|
143 * Returns whether a menu name already exists.
|
Chris@0
|
144 *
|
Chris@0
|
145 * @param string $value
|
Chris@0
|
146 * The name of the menu.
|
Chris@0
|
147 *
|
Chris@0
|
148 * @return bool
|
Chris@0
|
149 * Returns TRUE if the menu already exists, FALSE otherwise.
|
Chris@0
|
150 */
|
Chris@0
|
151 public function menuNameExists($value) {
|
Chris@0
|
152 // Check first to see if a menu with this ID exists.
|
Chris@0
|
153 if ($this->entityTypeManager->getStorage('menu')->getQuery()->condition('id', $value)->range(0, 1)->count()->execute()) {
|
Chris@0
|
154 return TRUE;
|
Chris@0
|
155 }
|
Chris@0
|
156
|
Chris@0
|
157 // Check for a link assigned to this menu.
|
Chris@0
|
158 return $this->menuLinkManager->menuNameInUse($value);
|
Chris@0
|
159 }
|
Chris@0
|
160
|
Chris@0
|
161 /**
|
Chris@0
|
162 * {@inheritdoc}
|
Chris@0
|
163 */
|
Chris@0
|
164 public function save(array $form, FormStateInterface $form_state) {
|
Chris@0
|
165 $menu = $this->entity;
|
Chris@0
|
166 $status = $menu->save();
|
Chris@0
|
167 $edit_link = $this->entity->link($this->t('Edit'));
|
Chris@0
|
168 if ($status == SAVED_UPDATED) {
|
Chris@0
|
169 drupal_set_message($this->t('Menu %label has been updated.', ['%label' => $menu->label()]));
|
Chris@0
|
170 $this->logger('menu')->notice('Menu %label has been updated.', ['%label' => $menu->label(), 'link' => $edit_link]);
|
Chris@0
|
171 }
|
Chris@0
|
172 else {
|
Chris@0
|
173 drupal_set_message($this->t('Menu %label has been added.', ['%label' => $menu->label()]));
|
Chris@0
|
174 $this->logger('menu')->notice('Menu %label has been added.', ['%label' => $menu->label(), 'link' => $edit_link]);
|
Chris@0
|
175 }
|
Chris@0
|
176
|
Chris@0
|
177 $form_state->setRedirectUrl($this->entity->urlInfo('edit-form'));
|
Chris@0
|
178 }
|
Chris@0
|
179
|
Chris@0
|
180 /**
|
Chris@0
|
181 * {@inheritdoc}
|
Chris@0
|
182 */
|
Chris@0
|
183 public function submitForm(array &$form, FormStateInterface $form_state) {
|
Chris@0
|
184 parent::submitForm($form, $form_state);
|
Chris@0
|
185
|
Chris@0
|
186 if (!$this->entity->isNew() || $this->entity->isLocked()) {
|
Chris@0
|
187 $this->submitOverviewForm($form, $form_state);
|
Chris@0
|
188 }
|
Chris@0
|
189 }
|
Chris@0
|
190
|
Chris@0
|
191 /**
|
Chris@0
|
192 * Form constructor to edit an entire menu tree at once.
|
Chris@0
|
193 *
|
Chris@0
|
194 * Shows for one menu the menu links accessible to the current user and
|
Chris@0
|
195 * relevant operations.
|
Chris@0
|
196 *
|
Chris@0
|
197 * This form constructor can be integrated as a section into another form. It
|
Chris@0
|
198 * relies on the following keys in $form_state:
|
Chris@0
|
199 * - menu: A menu entity.
|
Chris@0
|
200 * - menu_overview_form_parents: An array containing the parent keys to this
|
Chris@0
|
201 * form.
|
Chris@0
|
202 * Forms integrating this section should call menu_overview_form_submit() from
|
Chris@0
|
203 * their form submit handler.
|
Chris@0
|
204 */
|
Chris@0
|
205 protected function buildOverviewForm(array &$form, FormStateInterface $form_state) {
|
Chris@0
|
206 // Ensure that menu_overview_form_submit() knows the parents of this form
|
Chris@0
|
207 // section.
|
Chris@0
|
208 if (!$form_state->has('menu_overview_form_parents')) {
|
Chris@0
|
209 $form_state->set('menu_overview_form_parents', []);
|
Chris@0
|
210 }
|
Chris@0
|
211
|
Chris@0
|
212 $form['#attached']['library'][] = 'menu_ui/drupal.menu_ui.adminforms';
|
Chris@0
|
213
|
Chris@0
|
214 $tree = $this->menuTree->load($this->entity->id(), new MenuTreeParameters());
|
Chris@0
|
215
|
Chris@0
|
216 // We indicate that a menu administrator is running the menu access check.
|
Chris@0
|
217 $this->getRequest()->attributes->set('_menu_admin', TRUE);
|
Chris@0
|
218 $manipulators = [
|
Chris@0
|
219 ['callable' => 'menu.default_tree_manipulators:checkAccess'],
|
Chris@0
|
220 ['callable' => 'menu.default_tree_manipulators:generateIndexAndSort'],
|
Chris@0
|
221 ];
|
Chris@0
|
222 $tree = $this->menuTree->transform($tree, $manipulators);
|
Chris@0
|
223 $this->getRequest()->attributes->set('_menu_admin', FALSE);
|
Chris@0
|
224
|
Chris@0
|
225 // Determine the delta; the number of weights to be made available.
|
Chris@0
|
226 $count = function (array $tree) {
|
Chris@0
|
227 $sum = function ($carry, MenuLinkTreeElement $item) {
|
Chris@0
|
228 return $carry + $item->count();
|
Chris@0
|
229 };
|
Chris@0
|
230 return array_reduce($tree, $sum);
|
Chris@0
|
231 };
|
Chris@0
|
232 $delta = max($count($tree), 50);
|
Chris@0
|
233
|
Chris@0
|
234 $form['links'] = [
|
Chris@0
|
235 '#type' => 'table',
|
Chris@0
|
236 '#theme' => 'table__menu_overview',
|
Chris@0
|
237 '#header' => [
|
Chris@0
|
238 $this->t('Menu link'),
|
Chris@0
|
239 [
|
Chris@0
|
240 'data' => $this->t('Enabled'),
|
Chris@0
|
241 'class' => ['checkbox'],
|
Chris@0
|
242 ],
|
Chris@0
|
243 $this->t('Weight'),
|
Chris@0
|
244 [
|
Chris@0
|
245 'data' => $this->t('Operations'),
|
Chris@0
|
246 'colspan' => 3,
|
Chris@0
|
247 ],
|
Chris@0
|
248 ],
|
Chris@0
|
249 '#attributes' => [
|
Chris@0
|
250 'id' => 'menu-overview',
|
Chris@0
|
251 ],
|
Chris@0
|
252 '#tabledrag' => [
|
Chris@0
|
253 [
|
Chris@0
|
254 'action' => 'match',
|
Chris@0
|
255 'relationship' => 'parent',
|
Chris@0
|
256 'group' => 'menu-parent',
|
Chris@0
|
257 'subgroup' => 'menu-parent',
|
Chris@0
|
258 'source' => 'menu-id',
|
Chris@0
|
259 'hidden' => TRUE,
|
Chris@0
|
260 'limit' => \Drupal::menuTree()->maxDepth() - 1,
|
Chris@0
|
261 ],
|
Chris@0
|
262 [
|
Chris@0
|
263 'action' => 'order',
|
Chris@0
|
264 'relationship' => 'sibling',
|
Chris@0
|
265 'group' => 'menu-weight',
|
Chris@0
|
266 ],
|
Chris@0
|
267 ],
|
Chris@0
|
268 ];
|
Chris@0
|
269
|
Chris@0
|
270 $form['links']['#empty'] = $this->t('There are no menu links yet. <a href=":url">Add link</a>.', [
|
Chris@0
|
271 ':url' => $this->url('entity.menu.add_link_form', ['menu' => $this->entity->id()], [
|
Chris@0
|
272 'query' => ['destination' => $this->entity->url('edit-form')],
|
Chris@0
|
273 ]),
|
Chris@0
|
274 ]);
|
Chris@0
|
275 $links = $this->buildOverviewTreeForm($tree, $delta);
|
Chris@0
|
276 foreach (Element::children($links) as $id) {
|
Chris@0
|
277 if (isset($links[$id]['#item'])) {
|
Chris@0
|
278 $element = $links[$id];
|
Chris@0
|
279
|
Chris@0
|
280 $form['links'][$id]['#item'] = $element['#item'];
|
Chris@0
|
281
|
Chris@0
|
282 // TableDrag: Mark the table row as draggable.
|
Chris@0
|
283 $form['links'][$id]['#attributes'] = $element['#attributes'];
|
Chris@0
|
284 $form['links'][$id]['#attributes']['class'][] = 'draggable';
|
Chris@0
|
285
|
Chris@0
|
286 // TableDrag: Sort the table row according to its existing/configured weight.
|
Chris@0
|
287 $form['links'][$id]['#weight'] = $element['#item']->link->getWeight();
|
Chris@0
|
288
|
Chris@0
|
289 // Add special classes to be used for tabledrag.js.
|
Chris@0
|
290 $element['parent']['#attributes']['class'] = ['menu-parent'];
|
Chris@0
|
291 $element['weight']['#attributes']['class'] = ['menu-weight'];
|
Chris@0
|
292 $element['id']['#attributes']['class'] = ['menu-id'];
|
Chris@0
|
293
|
Chris@0
|
294 $form['links'][$id]['title'] = [
|
Chris@0
|
295 [
|
Chris@0
|
296 '#theme' => 'indentation',
|
Chris@0
|
297 '#size' => $element['#item']->depth - 1,
|
Chris@0
|
298 ],
|
Chris@0
|
299 $element['title'],
|
Chris@0
|
300 ];
|
Chris@0
|
301 $form['links'][$id]['enabled'] = $element['enabled'];
|
Chris@0
|
302 $form['links'][$id]['enabled']['#wrapper_attributes']['class'] = ['checkbox', 'menu-enabled'];
|
Chris@0
|
303
|
Chris@0
|
304 $form['links'][$id]['weight'] = $element['weight'];
|
Chris@0
|
305
|
Chris@0
|
306 // Operations (dropbutton) column.
|
Chris@0
|
307 $form['links'][$id]['operations'] = $element['operations'];
|
Chris@0
|
308
|
Chris@0
|
309 $form['links'][$id]['id'] = $element['id'];
|
Chris@0
|
310 $form['links'][$id]['parent'] = $element['parent'];
|
Chris@0
|
311 }
|
Chris@0
|
312 }
|
Chris@0
|
313
|
Chris@0
|
314 return $form;
|
Chris@0
|
315 }
|
Chris@0
|
316
|
Chris@0
|
317 /**
|
Chris@0
|
318 * Recursive helper function for buildOverviewForm().
|
Chris@0
|
319 *
|
Chris@0
|
320 * @param \Drupal\Core\Menu\MenuLinkTreeElement[] $tree
|
Chris@0
|
321 * The tree retrieved by \Drupal\Core\Menu\MenuLinkTreeInterface::load().
|
Chris@0
|
322 * @param int $delta
|
Chris@0
|
323 * The default number of menu items used in the menu weight selector is 50.
|
Chris@0
|
324 *
|
Chris@0
|
325 * @return array
|
Chris@0
|
326 * The overview tree form.
|
Chris@0
|
327 */
|
Chris@0
|
328 protected function buildOverviewTreeForm($tree, $delta) {
|
Chris@0
|
329 $form = &$this->overviewTreeForm;
|
Chris@0
|
330 $tree_access_cacheability = new CacheableMetadata();
|
Chris@0
|
331 foreach ($tree as $element) {
|
Chris@0
|
332 $tree_access_cacheability = $tree_access_cacheability->merge(CacheableMetadata::createFromObject($element->access));
|
Chris@0
|
333
|
Chris@0
|
334 // Only render accessible links.
|
Chris@0
|
335 if (!$element->access->isAllowed()) {
|
Chris@0
|
336 continue;
|
Chris@0
|
337 }
|
Chris@0
|
338
|
Chris@0
|
339 /** @var \Drupal\Core\Menu\MenuLinkInterface $link */
|
Chris@0
|
340 $link = $element->link;
|
Chris@0
|
341 if ($link) {
|
Chris@0
|
342 $id = 'menu_plugin_id:' . $link->getPluginId();
|
Chris@0
|
343 $form[$id]['#item'] = $element;
|
Chris@0
|
344 $form[$id]['#attributes'] = $link->isEnabled() ? ['class' => ['menu-enabled']] : ['class' => ['menu-disabled']];
|
Chris@0
|
345 $form[$id]['title'] = Link::fromTextAndUrl($link->getTitle(), $link->getUrlObject())->toRenderable();
|
Chris@0
|
346 if (!$link->isEnabled()) {
|
Chris@0
|
347 $form[$id]['title']['#suffix'] = ' (' . $this->t('disabled') . ')';
|
Chris@0
|
348 }
|
Chris@0
|
349 // @todo Remove this in https://www.drupal.org/node/2568785.
|
Chris@0
|
350 elseif ($id === 'menu_plugin_id:user.logout') {
|
Chris@0
|
351 $form[$id]['title']['#suffix'] = ' (' . $this->t('<q>Log in</q> for anonymous users') . ')';
|
Chris@0
|
352 }
|
Chris@0
|
353 // @todo Remove this in https://www.drupal.org/node/2568785.
|
Chris@0
|
354 elseif (($url = $link->getUrlObject()) && $url->isRouted() && $url->getRouteName() == 'user.page') {
|
Chris@0
|
355 $form[$id]['title']['#suffix'] = ' (' . $this->t('logged in users only') . ')';
|
Chris@0
|
356 }
|
Chris@0
|
357
|
Chris@0
|
358 $form[$id]['enabled'] = [
|
Chris@0
|
359 '#type' => 'checkbox',
|
Chris@0
|
360 '#title' => $this->t('Enable @title menu link', ['@title' => $link->getTitle()]),
|
Chris@0
|
361 '#title_display' => 'invisible',
|
Chris@0
|
362 '#default_value' => $link->isEnabled(),
|
Chris@0
|
363 ];
|
Chris@0
|
364 $form[$id]['weight'] = [
|
Chris@0
|
365 '#type' => 'weight',
|
Chris@0
|
366 '#delta' => $delta,
|
Chris@0
|
367 '#default_value' => $link->getWeight(),
|
Chris@0
|
368 '#title' => $this->t('Weight for @title', ['@title' => $link->getTitle()]),
|
Chris@0
|
369 '#title_display' => 'invisible',
|
Chris@0
|
370 ];
|
Chris@0
|
371 $form[$id]['id'] = [
|
Chris@0
|
372 '#type' => 'hidden',
|
Chris@0
|
373 '#value' => $link->getPluginId(),
|
Chris@0
|
374 ];
|
Chris@0
|
375 $form[$id]['parent'] = [
|
Chris@0
|
376 '#type' => 'hidden',
|
Chris@0
|
377 '#default_value' => $link->getParent(),
|
Chris@0
|
378 ];
|
Chris@0
|
379 // Build a list of operations.
|
Chris@0
|
380 $operations = [];
|
Chris@0
|
381 $operations['edit'] = [
|
Chris@0
|
382 'title' => $this->t('Edit'),
|
Chris@0
|
383 ];
|
Chris@0
|
384 // Allow for a custom edit link per plugin.
|
Chris@0
|
385 $edit_route = $link->getEditRoute();
|
Chris@0
|
386 if ($edit_route) {
|
Chris@0
|
387 $operations['edit']['url'] = $edit_route;
|
Chris@0
|
388 // Bring the user back to the menu overview.
|
Chris@0
|
389 $operations['edit']['query'] = $this->getDestinationArray();
|
Chris@0
|
390 }
|
Chris@0
|
391 else {
|
Chris@0
|
392 // Fall back to the standard edit link.
|
Chris@0
|
393 $operations['edit'] += [
|
Chris@0
|
394 'url' => Url::fromRoute('menu_ui.link_edit', ['menu_link_plugin' => $link->getPluginId()]),
|
Chris@0
|
395 ];
|
Chris@0
|
396 }
|
Chris@0
|
397 // Links can either be reset or deleted, not both.
|
Chris@0
|
398 if ($link->isResettable()) {
|
Chris@0
|
399 $operations['reset'] = [
|
Chris@0
|
400 'title' => $this->t('Reset'),
|
Chris@0
|
401 'url' => Url::fromRoute('menu_ui.link_reset', ['menu_link_plugin' => $link->getPluginId()]),
|
Chris@0
|
402 ];
|
Chris@0
|
403 }
|
Chris@0
|
404 elseif ($delete_link = $link->getDeleteRoute()) {
|
Chris@0
|
405 $operations['delete']['url'] = $delete_link;
|
Chris@0
|
406 $operations['delete']['query'] = $this->getDestinationArray();
|
Chris@0
|
407 $operations['delete']['title'] = $this->t('Delete');
|
Chris@0
|
408 }
|
Chris@0
|
409 if ($link->isTranslatable()) {
|
Chris@0
|
410 $operations['translate'] = [
|
Chris@0
|
411 'title' => $this->t('Translate'),
|
Chris@0
|
412 'url' => $link->getTranslateRoute(),
|
Chris@0
|
413 ];
|
Chris@0
|
414 }
|
Chris@0
|
415 $form[$id]['operations'] = [
|
Chris@0
|
416 '#type' => 'operations',
|
Chris@0
|
417 '#links' => $operations,
|
Chris@0
|
418 ];
|
Chris@0
|
419 }
|
Chris@0
|
420
|
Chris@0
|
421 if ($element->subtree) {
|
Chris@0
|
422 $this->buildOverviewTreeForm($element->subtree, $delta);
|
Chris@0
|
423 }
|
Chris@0
|
424 }
|
Chris@0
|
425
|
Chris@0
|
426 $tree_access_cacheability
|
Chris@0
|
427 ->merge(CacheableMetadata::createFromRenderArray($form))
|
Chris@0
|
428 ->applyTo($form);
|
Chris@0
|
429
|
Chris@0
|
430 return $form;
|
Chris@0
|
431 }
|
Chris@0
|
432
|
Chris@0
|
433 /**
|
Chris@0
|
434 * Submit handler for the menu overview form.
|
Chris@0
|
435 *
|
Chris@0
|
436 * This function takes great care in saving parent items first, then items
|
Chris@0
|
437 * underneath them. Saving items in the incorrect order can break the tree.
|
Chris@0
|
438 */
|
Chris@0
|
439 protected function submitOverviewForm(array $complete_form, FormStateInterface $form_state) {
|
Chris@0
|
440 // Form API supports constructing and validating self-contained sections
|
Chris@0
|
441 // within forms, but does not allow to handle the form section's submission
|
Chris@0
|
442 // equally separated yet. Therefore, we use a $form_state key to point to
|
Chris@0
|
443 // the parents of the form section.
|
Chris@0
|
444 $parents = $form_state->get('menu_overview_form_parents');
|
Chris@0
|
445 $input = NestedArray::getValue($form_state->getUserInput(), $parents);
|
Chris@0
|
446 $form = &NestedArray::getValue($complete_form, $parents);
|
Chris@0
|
447
|
Chris@0
|
448 // When dealing with saving menu items, the order in which these items are
|
Chris@0
|
449 // saved is critical. If a changed child item is saved before its parent,
|
Chris@0
|
450 // the child item could be saved with an invalid path past its immediate
|
Chris@0
|
451 // parent. To prevent this, save items in the form in the same order they
|
Chris@0
|
452 // are sent, ensuring parents are saved first, then their children.
|
Chris@0
|
453 // See https://www.drupal.org/node/181126#comment-632270.
|
Chris@0
|
454 $order = is_array($input) ? array_flip(array_keys($input)) : [];
|
Chris@0
|
455 // Update our original form with the new order.
|
Chris@0
|
456 $form = array_intersect_key(array_merge($order, $form), $form);
|
Chris@0
|
457
|
Chris@0
|
458 $fields = ['weight', 'parent', 'enabled'];
|
Chris@0
|
459 $form_links = $form['links'];
|
Chris@0
|
460 foreach (Element::children($form_links) as $id) {
|
Chris@0
|
461 if (isset($form_links[$id]['#item'])) {
|
Chris@0
|
462 $element = $form_links[$id];
|
Chris@0
|
463 $updated_values = [];
|
Chris@0
|
464 // Update any fields that have changed in this menu item.
|
Chris@0
|
465 foreach ($fields as $field) {
|
Chris@0
|
466 if ($element[$field]['#value'] != $element[$field]['#default_value']) {
|
Chris@0
|
467 $updated_values[$field] = $element[$field]['#value'];
|
Chris@0
|
468 }
|
Chris@0
|
469 }
|
Chris@0
|
470 if ($updated_values) {
|
Chris@0
|
471 // Use the ID from the actual plugin instance since the hidden value
|
Chris@0
|
472 // in the form could be tampered with.
|
Chris@0
|
473 $this->menuLinkManager->updateDefinition($element['#item']->link->getPLuginId(), $updated_values);
|
Chris@0
|
474 }
|
Chris@0
|
475 }
|
Chris@0
|
476 }
|
Chris@0
|
477 }
|
Chris@0
|
478
|
Chris@0
|
479 }
|