MenuParentFormSelector.php 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. <?php
  2. namespace Drupal\Core\Menu;
  3. use Drupal\Core\Cache\CacheableMetadata;
  4. use Drupal\Component\Utility\Unicode;
  5. use Drupal\Core\DependencyInjection\DeprecatedServicePropertyTrait;
  6. use Drupal\Core\Entity\EntityManagerInterface;
  7. use Drupal\Core\Entity\EntityTypeManagerInterface;
  8. use Drupal\Core\StringTranslation\StringTranslationTrait;
  9. use Drupal\Core\StringTranslation\TranslationInterface;
  10. /**
  11. * Default implementation of the menu parent form selector service.
  12. *
  13. * The form selector is a list of all appropriate menu links.
  14. */
  15. class MenuParentFormSelector implements MenuParentFormSelectorInterface {
  16. use StringTranslationTrait;
  17. use DeprecatedServicePropertyTrait;
  18. /**
  19. * {@inheritdoc}
  20. */
  21. protected $deprecatedProperties = ['entityManager' => 'entity.manager'];
  22. /**
  23. * The menu link tree service.
  24. *
  25. * @var \Drupal\Core\Menu\MenuLinkTreeInterface
  26. */
  27. protected $menuLinkTree;
  28. /**
  29. * The entity type manager service.
  30. *
  31. * @var \Drupal\Core\Entity\EntityTypeManagerInterface
  32. */
  33. protected $entityTypeManager;
  34. /**
  35. * Constructs a \Drupal\Core\Menu\MenuParentFormSelector
  36. *
  37. * @param \Drupal\Core\Menu\MenuLinkTreeInterface $menu_link_tree
  38. * The menu link tree service.
  39. * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
  40. * The entity type manager service.
  41. * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
  42. * The string translation service.
  43. */
  44. public function __construct(MenuLinkTreeInterface $menu_link_tree, EntityTypeManagerInterface $entity_type_manager, TranslationInterface $string_translation) {
  45. $this->menuLinkTree = $menu_link_tree;
  46. if ($entity_type_manager instanceof EntityManagerInterface) {
  47. @trigger_error('Passing the entity.manager service to MenuParentFormSelector::__construct() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. Pass the new dependencies instead. See https://www.drupal.org/node/2549139.', E_USER_DEPRECATED);
  48. $this->entityTypeManager = \Drupal::entityTypeManager();
  49. }
  50. else {
  51. $this->entityTypeManager = $entity_type_manager;
  52. }
  53. $this->stringTranslation = $string_translation;
  54. }
  55. /**
  56. * {@inheritdoc}
  57. */
  58. public function getParentSelectOptions($id = '', array $menus = NULL, CacheableMetadata &$cacheability = NULL) {
  59. if (!isset($menus)) {
  60. $menus = $this->getMenuOptions();
  61. }
  62. $options = [];
  63. $depth_limit = $this->getParentDepthLimit($id);
  64. foreach ($menus as $menu_name => $menu_title) {
  65. $options[$menu_name . ':'] = '<' . $menu_title . '>';
  66. $parameters = new MenuTreeParameters();
  67. $parameters->setMaxDepth($depth_limit);
  68. $tree = $this->menuLinkTree->load($menu_name, $parameters);
  69. $manipulators = [
  70. ['callable' => 'menu.default_tree_manipulators:checkNodeAccess'],
  71. ['callable' => 'menu.default_tree_manipulators:checkAccess'],
  72. ['callable' => 'menu.default_tree_manipulators:generateIndexAndSort'],
  73. ];
  74. $tree = $this->menuLinkTree->transform($tree, $manipulators);
  75. $this->parentSelectOptionsTreeWalk($tree, $menu_name, '--', $options, $id, $depth_limit, $cacheability);
  76. }
  77. return $options;
  78. }
  79. /**
  80. * {@inheritdoc}
  81. */
  82. public function parentSelectElement($menu_parent, $id = '', array $menus = NULL) {
  83. $options_cacheability = new CacheableMetadata();
  84. $options = $this->getParentSelectOptions($id, $menus, $options_cacheability);
  85. // If no options were found, there is nothing to select.
  86. if ($options) {
  87. $element = [
  88. '#type' => 'select',
  89. '#options' => $options,
  90. ];
  91. if (!isset($options[$menu_parent])) {
  92. // The requested menu parent cannot be found in the menu anymore. Try
  93. // setting it to the top level in the current menu.
  94. list($menu_name, $parent) = explode(':', $menu_parent, 2);
  95. $menu_parent = $menu_name . ':';
  96. }
  97. if (isset($options[$menu_parent])) {
  98. // Only provide the default value if it is valid among the options.
  99. $element += ['#default_value' => $menu_parent];
  100. }
  101. $options_cacheability->applyTo($element);
  102. return $element;
  103. }
  104. return [];
  105. }
  106. /**
  107. * Returns the maximum depth of the possible parents of the menu link.
  108. *
  109. * @param string $id
  110. * The menu link plugin ID or an empty value for a new link.
  111. *
  112. * @return int
  113. * The depth related to the depth of the given menu link.
  114. */
  115. protected function getParentDepthLimit($id) {
  116. if ($id) {
  117. $limit = $this->menuLinkTree->maxDepth() - $this->menuLinkTree->getSubtreeHeight($id);
  118. }
  119. else {
  120. $limit = $this->menuLinkTree->maxDepth() - 1;
  121. }
  122. return $limit;
  123. }
  124. /**
  125. * Iterates over all items in the tree to prepare the parents select options.
  126. *
  127. * @param \Drupal\Core\Menu\MenuLinkTreeElement[] $tree
  128. * The menu tree.
  129. * @param string $menu_name
  130. * The menu name.
  131. * @param string $indent
  132. * The indentation string used for the label.
  133. * @param array $options
  134. * The select options.
  135. * @param string $exclude
  136. * An excluded menu link.
  137. * @param int $depth_limit
  138. * The maximum depth of menu links considered for the select options.
  139. * @param \Drupal\Core\Cache\CacheableMetadata|null &$cacheability
  140. * The object to add cacheability metadata to, if not NULL.
  141. */
  142. protected function parentSelectOptionsTreeWalk(array $tree, $menu_name, $indent, array &$options, $exclude, $depth_limit, CacheableMetadata &$cacheability = NULL) {
  143. foreach ($tree as $element) {
  144. if ($element->depth > $depth_limit) {
  145. // Don't iterate through any links on this level.
  146. break;
  147. }
  148. // Collect the cacheability metadata of the access result, as well as the
  149. // link.
  150. if ($cacheability) {
  151. $cacheability = $cacheability
  152. ->merge(CacheableMetadata::createFromObject($element->access))
  153. ->merge(CacheableMetadata::createFromObject($element->link));
  154. }
  155. // Only show accessible links.
  156. if (!$element->access->isAllowed()) {
  157. continue;
  158. }
  159. $link = $element->link;
  160. if ($link->getPluginId() != $exclude) {
  161. $title = $indent . ' ' . Unicode::truncate($link->getTitle(), 30, TRUE, FALSE);
  162. if (!$link->isEnabled()) {
  163. $title .= ' (' . $this->t('disabled') . ')';
  164. }
  165. $options[$menu_name . ':' . $link->getPluginId()] = $title;
  166. if (!empty($element->subtree)) {
  167. $this->parentSelectOptionsTreeWalk($element->subtree, $menu_name, $indent . '--', $options, $exclude, $depth_limit, $cacheability);
  168. }
  169. }
  170. }
  171. }
  172. /**
  173. * Gets a list of menu names for use as options.
  174. *
  175. * @param array $menu_names
  176. * (optional) Array of menu names to limit the options, or NULL to load all.
  177. *
  178. * @return array
  179. * Keys are menu names (ids) values are the menu labels.
  180. */
  181. protected function getMenuOptions(array $menu_names = NULL) {
  182. $menus = $this->entityTypeManager->getStorage('menu')->loadMultiple($menu_names);
  183. $options = [];
  184. /** @var \Drupal\system\MenuInterface[] $menus */
  185. foreach ($menus as $menu) {
  186. $options[$menu->id()] = $menu->label();
  187. }
  188. return $options;
  189. }
  190. }