Version 1
[yaffs-website] / web / core / modules / system / src / Plugin / Block / SystemMenuBlock.php
diff --git a/web/core/modules/system/src/Plugin/Block/SystemMenuBlock.php b/web/core/modules/system/src/Plugin/Block/SystemMenuBlock.php
new file mode 100644 (file)
index 0000000..c142c86
--- /dev/null
@@ -0,0 +1,196 @@
+<?php
+
+namespace Drupal\system\Plugin\Block;
+
+use Drupal\Core\Block\BlockBase;
+use Drupal\Core\Cache\Cache;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Menu\MenuActiveTrailInterface;
+use Drupal\Core\Menu\MenuLinkTreeInterface;
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Provides a generic Menu block.
+ *
+ * @Block(
+ *   id = "system_menu_block",
+ *   admin_label = @Translation("Menu"),
+ *   category = @Translation("Menus"),
+ *   deriver = "Drupal\system\Plugin\Derivative\SystemMenuBlock"
+ * )
+ */
+class SystemMenuBlock extends BlockBase implements ContainerFactoryPluginInterface {
+
+  /**
+   * The menu link tree service.
+   *
+   * @var \Drupal\Core\Menu\MenuLinkTreeInterface
+   */
+  protected $menuTree;
+
+  /**
+   * The active menu trail service.
+   *
+   * @var \Drupal\Core\Menu\MenuActiveTrailInterface
+   */
+  protected $menuActiveTrail;
+
+  /**
+   * Constructs a new SystemMenuBlock.
+   *
+   * @param array $configuration
+   *   A configuration array containing information about the plugin instance.
+   * @param string $plugin_id
+   *   The plugin_id for the plugin instance.
+   * @param array $plugin_definition
+   *   The plugin implementation definition.
+   * @param \Drupal\Core\Menu\MenuLinkTreeInterface $menu_tree
+   *   The menu tree service.
+   * @param \Drupal\Core\Menu\MenuActiveTrailInterface $menu_active_trail
+   *   The active menu trail service.
+   */
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, MenuLinkTreeInterface $menu_tree, MenuActiveTrailInterface $menu_active_trail) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+    $this->menuTree = $menu_tree;
+    $this->menuActiveTrail = $menu_active_trail;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $container->get('menu.link_tree'),
+      $container->get('menu.active_trail')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function blockForm($form, FormStateInterface $form_state) {
+    $config = $this->configuration;
+
+    $defaults = $this->defaultConfiguration();
+    $form['menu_levels'] = [
+      '#type' => 'details',
+      '#title' => $this->t('Menu levels'),
+      // Open if not set to defaults.
+      '#open' => $defaults['level'] !== $config['level'] || $defaults['depth'] !== $config['depth'],
+      '#process' => [[get_class(), 'processMenuLevelParents']],
+    ];
+
+    $options = range(0, $this->menuTree->maxDepth());
+    unset($options[0]);
+
+    $form['menu_levels']['level'] = [
+      '#type' => 'select',
+      '#title' => $this->t('Initial visibility level'),
+      '#default_value' => $config['level'],
+      '#options' => $options,
+      '#description' => $this->t('The menu is only visible if the menu item for the current page is at this level or below it. Use level 1 to always display this menu.'),
+      '#required' => TRUE,
+    ];
+
+    $options[0] = $this->t('Unlimited');
+
+    $form['menu_levels']['depth'] = [
+      '#type' => 'select',
+      '#title' => $this->t('Number of levels to display'),
+      '#default_value' => $config['depth'],
+      '#options' => $options,
+      '#description' => $this->t('This maximum number includes the initial level.'),
+      '#required' => TRUE,
+    ];
+
+    return $form;
+  }
+
+  /**
+   * Form API callback: Processes the menu_levels field element.
+   *
+   * Adjusts the #parents of menu_levels to save its children at the top level.
+   */
+  public static function processMenuLevelParents(&$element, FormStateInterface $form_state, &$complete_form) {
+    array_pop($element['#parents']);
+    return $element;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function blockSubmit($form, FormStateInterface $form_state) {
+    $this->configuration['level'] = $form_state->getValue('level');
+    $this->configuration['depth'] = $form_state->getValue('depth');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function build() {
+    $menu_name = $this->getDerivativeId();
+    $parameters = $this->menuTree->getCurrentRouteMenuTreeParameters($menu_name);
+
+    // Adjust the menu tree parameters based on the block's configuration.
+    $level = $this->configuration['level'];
+    $depth = $this->configuration['depth'];
+    $parameters->setMinDepth($level);
+    // When the depth is configured to zero, there is no depth limit. When depth
+    // is non-zero, it indicates the number of levels that must be displayed.
+    // Hence this is a relative depth that we must convert to an actual
+    // (absolute) depth, that may never exceed the maximum depth.
+    if ($depth > 0) {
+      $parameters->setMaxDepth(min($level + $depth - 1, $this->menuTree->maxDepth()));
+    }
+
+    $tree = $this->menuTree->load($menu_name, $parameters);
+    $manipulators = [
+      ['callable' => 'menu.default_tree_manipulators:checkAccess'],
+      ['callable' => 'menu.default_tree_manipulators:generateIndexAndSort'],
+    ];
+    $tree = $this->menuTree->transform($tree, $manipulators);
+    return $this->menuTree->build($tree);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function defaultConfiguration() {
+    return [
+      'level' => 1,
+      'depth' => 0,
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCacheTags() {
+    // Even when the menu block renders to the empty string for a user, we want
+    // the cache tag for this menu to be set: whenever the menu is changed, this
+    // menu block must also be re-rendered for that user, because maybe a menu
+    // link that is accessible for that user has been added.
+    $cache_tags = parent::getCacheTags();
+    $cache_tags[] = 'config:system.menu.' . $this->getDerivativeId();
+    return $cache_tags;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCacheContexts() {
+    // ::build() uses MenuLinkTreeInterface::getCurrentRouteMenuTreeParameters()
+    // to generate menu tree parameters, and those take the active menu trail
+    // into account. Therefore, we must vary the rendered menu by the active
+    // trail of the rendered menu.
+    // Additional cache contexts, e.g. those that determine link text or
+    // accessibility of a menu, will be bubbled automatically.
+    $menu_name = $this->getDerivativeId();
+    return Cache::mergeContexts(parent::getCacheContexts(), ['route.menu_active_trails:' . $menu_name]);
+  }
+
+}