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