Version 1
[yaffs-website] / web / core / lib / Drupal / Core / Menu / MenuParentFormSelector.php
diff --git a/web/core/lib/Drupal/Core/Menu/MenuParentFormSelector.php b/web/core/lib/Drupal/Core/Menu/MenuParentFormSelector.php
new file mode 100644 (file)
index 0000000..8add3b0
--- /dev/null
@@ -0,0 +1,194 @@
+<?php
+
+namespace Drupal\Core\Menu;
+
+use Drupal\Core\Cache\CacheableMetadata;
+use Drupal\Core\Entity\EntityManagerInterface;
+use Drupal\Component\Utility\Unicode;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\Core\StringTranslation\TranslationInterface;
+
+/**
+ * Default implementation of the menu parent form selector service.
+ *
+ * The form selector is a list of all appropriate menu links.
+ */
+class MenuParentFormSelector implements MenuParentFormSelectorInterface {
+  use StringTranslationTrait;
+
+  /**
+   * The menu link tree service.
+   *
+   * @var \Drupal\Core\Menu\MenuLinkTreeInterface
+   */
+  protected $menuLinkTree;
+
+  /**
+   * The entity manager.
+   *
+   * @var \Drupal\Core\Entity\EntityManagerInterface
+   */
+  protected $entityManager;
+
+  /**
+   * Constructs a \Drupal\Core\Menu\MenuParentFormSelector
+   *
+   * @param \Drupal\Core\Menu\MenuLinkTreeInterface $menu_link_tree
+   *   The menu link tree service.
+   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
+   *   The entity manager.
+   * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
+   *   The string translation service.
+   */
+  public function __construct(MenuLinkTreeInterface $menu_link_tree, EntityManagerInterface $entity_manager, TranslationInterface $string_translation) {
+    $this->menuLinkTree = $menu_link_tree;
+    $this->entityManager = $entity_manager;
+    $this->stringTranslation = $string_translation;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getParentSelectOptions($id = '', array $menus = NULL, CacheableMetadata &$cacheability = NULL) {
+    if (!isset($menus)) {
+      $menus = $this->getMenuOptions();
+    }
+
+    $options = [];
+    $depth_limit = $this->getParentDepthLimit($id);
+    foreach ($menus as $menu_name => $menu_title) {
+      $options[$menu_name . ':'] = '<' . $menu_title . '>';
+
+      $parameters = new MenuTreeParameters();
+      $parameters->setMaxDepth($depth_limit);
+      $tree = $this->menuLinkTree->load($menu_name, $parameters);
+      $manipulators = [
+        ['callable' => 'menu.default_tree_manipulators:checkNodeAccess'],
+        ['callable' => 'menu.default_tree_manipulators:checkAccess'],
+        ['callable' => 'menu.default_tree_manipulators:generateIndexAndSort'],
+      ];
+      $tree = $this->menuLinkTree->transform($tree, $manipulators);
+      $this->parentSelectOptionsTreeWalk($tree, $menu_name, '--', $options, $id, $depth_limit, $cacheability);
+    }
+    return $options;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function parentSelectElement($menu_parent, $id = '', array $menus = NULL) {
+    $options_cacheability = new CacheableMetadata();
+    $options = $this->getParentSelectOptions($id, $menus, $options_cacheability);
+    // If no options were found, there is nothing to select.
+    if ($options) {
+      $element = [
+        '#type' => 'select',
+        '#options' => $options,
+      ];
+      if (!isset($options[$menu_parent])) {
+        // The requested menu parent cannot be found in the menu anymore. Try
+        // setting it to the top level in the current menu.
+        list($menu_name, $parent) = explode(':', $menu_parent, 2);
+        $menu_parent = $menu_name . ':';
+      }
+      if (isset($options[$menu_parent])) {
+        // Only provide the default value if it is valid among the options.
+        $element += ['#default_value' => $menu_parent];
+      }
+      $options_cacheability->applyTo($element);
+      return $element;
+    }
+    return [];
+  }
+
+  /**
+   * Returns the maximum depth of the possible parents of the menu link.
+   *
+   * @param string $id
+   *   The menu link plugin ID or an empty value for a new link.
+   *
+   * @return int
+   *   The depth related to the depth of the given menu link.
+   */
+  protected function getParentDepthLimit($id) {
+    if ($id) {
+      $limit = $this->menuLinkTree->maxDepth() - $this->menuLinkTree->getSubtreeHeight($id);
+    }
+    else {
+      $limit = $this->menuLinkTree->maxDepth() - 1;
+    }
+    return $limit;
+  }
+
+  /**
+   * Iterates over all items in the tree to prepare the parents select options.
+   *
+   * @param \Drupal\Core\Menu\MenuLinkTreeElement[] $tree
+   *   The menu tree.
+   * @param string $menu_name
+   *   The menu name.
+   * @param string $indent
+   *   The indentation string used for the label.
+   * @param array $options
+   *   The select options.
+   * @param string $exclude
+   *   An excluded menu link.
+   * @param int $depth_limit
+   *   The maximum depth of menu links considered for the select options.
+   * @param \Drupal\Core\Cache\CacheableMetadata|null &$cacheability
+   *   The object to add cacheability metadata to, if not NULL.
+   */
+  protected function parentSelectOptionsTreeWalk(array $tree, $menu_name, $indent, array &$options, $exclude, $depth_limit, CacheableMetadata &$cacheability = NULL) {
+    foreach ($tree as $element) {
+      if ($element->depth > $depth_limit) {
+        // Don't iterate through any links on this level.
+        break;
+      }
+
+      // Collect the cacheability metadata of the access result, as well as the
+      // link.
+      if ($cacheability) {
+        $cacheability = $cacheability
+          ->merge(CacheableMetadata::createFromObject($element->access))
+          ->merge(CacheableMetadata::createFromObject($element->link));
+      }
+
+      // Only show accessible links.
+      if (!$element->access->isAllowed()) {
+        continue;
+      }
+
+      $link = $element->link;
+      if ($link->getPluginId() != $exclude) {
+        $title = $indent . ' ' . Unicode::truncate($link->getTitle(), 30, TRUE, FALSE);
+        if (!$link->isEnabled()) {
+          $title .= ' (' . $this->t('disabled') . ')';
+        }
+        $options[$menu_name . ':' . $link->getPluginId()] = $title;
+        if (!empty($element->subtree)) {
+          $this->parentSelectOptionsTreeWalk($element->subtree, $menu_name, $indent . '--', $options, $exclude, $depth_limit, $cacheability);
+        }
+      }
+    }
+  }
+
+  /**
+   * Gets a list of menu names for use as options.
+   *
+   * @param array $menu_names
+   *   (optional) Array of menu names to limit the options, or NULL to load all.
+   *
+   * @return array
+   *   Keys are menu names (ids) values are the menu labels.
+   */
+  protected function getMenuOptions(array $menu_names = NULL) {
+    $menus = $this->entityManager->getStorage('menu')->loadMultiple($menu_names);
+    $options = [];
+    /** @var \Drupal\system\MenuInterface[] $menus */
+    foreach ($menus as $menu) {
+      $options[$menu->id()] = $menu->label();
+    }
+    return $options;
+  }
+
+}