diff --git a/src/Plugin/Block/MenuBlock.php b/src/Plugin/Block/MenuBlock.php index 87b6ca6..2479c0e 100644 --- a/src/Plugin/Block/MenuBlock.php +++ b/src/Plugin/Block/MenuBlock.php @@ -5,6 +5,11 @@ namespace Drupal\menu_block\Plugin\Block; use Drupal\Core\Form\FormStateInterface; use Drupal\system\Entity\Menu; use Drupal\system\Plugin\Block\SystemMenuBlock; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Drupal\Core\Entity\EntityTypeManagerInterface; +use Drupal\Core\Menu\MenuActiveTrailInterface; +use Drupal\Core\Menu\MenuLinkTreeInterface; +use Drupal\Core\Menu\MenuTreeParameters; /** * Provides an extended Menu block. @@ -21,6 +26,62 @@ use Drupal\system\Plugin\Block\SystemMenuBlock; */ class MenuBlock extends SystemMenuBlock { + /** + * Constant definition options for block label type. + */ + const LABEL_BLOCK = 'block'; + const LABEL_MENU = 'menu'; + const LABEL_ACTIVE_ITEM = 'active_item'; + const LABEL_PARENT = 'parent'; + const LABEL_ROOT = 'root'; + const LABEL_FIXED_PARENT = 'fixed_parent'; + + /** + * Entity type manager. + * + * @var \Drupal\Core\Entity\EntityTypeManagerInterface + */ + protected $entityTypeManager; + + /** + * The active menu trail service. + * + * @var \Drupal\Core\Menu\MenuActiveTrailInterface + */ + protected $menuActiveTrail; + + /** + * {@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'), + $container->get('entity_type.manager') + ); + } + + /** + * {@inheritdoc} + */ + public function __construct(array $configuration, $plugin_id, array $plugin_definition, MenuLinkTreeInterface $menu_tree, MenuActiveTrailInterface $active_trail, EntityTypeManagerInterface $entity_type_manager) { + parent::__construct($configuration, $plugin_id, $plugin_definition, $menu_tree); + $this->menuActiveTrail = $active_trail; + $this->entityTypeManager = $entity_type_manager; + } + + /** + * {@inheritdoc} + */ + public function getConfiguration() { + $label = $this->getBlockLabel() ?: $this->label(); + $this->setConfigurationValue('label', $label); + return $this->configuration; + } + /** * {@inheritdoc} */ @@ -57,6 +118,26 @@ class MenuBlock extends SystemMenuBlock { '#description' => $this->t('Alter the options in “Menu levels” to be relative to the fixed parent item. The block will only contain children of the selected menu link.'), ]; + $form['advanced']['label_type'] = [ + '#type' => 'select', + '#title' => $this->t('Use as title'), + '#description' => $this->t('Replace the block title with an item from the menu.'), + '#options' => [ + self::LABEL_BLOCK => $this->t('Block title'), + self::LABEL_MENU => $this->t('Menu title'), + self::LABEL_FIXED_PARENT => $this->t("Fixed parent item's title"), + self::LABEL_ACTIVE_ITEM => $this->t("Active item's title"), + self::LABEL_PARENT => $this->t("Active trail's parent title"), + self::LABEL_ROOT => $this->t("Active trail's root title"), + ], + '#default_value' => $config['label_type'], + '#states' => [ + 'visible' => [ + ':input[name="settings[label_display]"]' => ['checked' => TRUE], + ], + ], + ]; + $form['style'] = [ '#type' => 'details', '#title' => $this->t('HTML and style options'), @@ -107,6 +188,7 @@ class MenuBlock extends SystemMenuBlock { $this->configuration['expand'] = $form_state->getValue('expand'); $this->configuration['parent'] = $form_state->getValue('parent'); $this->configuration['suggestion'] = $form_state->getValue('suggestion'); + $this->configuration['label_type'] = $form_state->getValue('label_type'); } /** @@ -216,6 +298,7 @@ class MenuBlock extends SystemMenuBlock { 'expand' => 0, 'parent' => $this->getDerivativeId() . ':', 'suggestion' => strtr($this->getDerivativeId(), '-', '_'), + 'label_type' => self::LABEL_BLOCK, ]; } @@ -229,4 +312,157 @@ class MenuBlock extends SystemMenuBlock { return FALSE; } + /** + * Get the configured block label. + * + * @return string + * The configured label. + */ + public function getBlockLabel() { + switch ($this->configuration['label_type']) { + case self::LABEL_MENU: + return $this->getMenuTitle(); + + case self::LABEL_ACTIVE_ITEM: + return $this->getActiveItemTitle(); + + case self::LABEL_PARENT: + return $this->getActiveTrailParentTitle(); + + case self::LABEL_ROOT: + return $this->getActiveTrailRootTitle(); + + case self::LABEL_FIXED_PARENT: + return $this->getFixedParentItemTitle(); + + default: + return $this->label(); + } + } + + /** + * Get the label of the configured menu. + * + * @return string|null + * Menu label or null if no menu exists. + */ + protected function getMenuTitle() { + try { + $menu = $this->entityTypeManager->getStorage('menu') + ->load($this->getDerivativeId()); + } + catch (\Exception $e) { + return NULL; + } + + return $menu ? $menu->label() : NULL; + } + + /** + * @return string + */ + protected function getFixedParentItemTitle() { + $parent = $this->configuration['parent']; + + if ($parent) { + $fixed_parent_menu_link_id = str_replace($this->getDerivativeId() . ':', '', $parent); + return $this->getLinkTitleFromLink($fixed_parent_menu_link_id); + } + } + + /** + * Get the active menu item's title. + * + * @return string + * Current menu item title. + */ + protected function getActiveItemTitle() { + $active_trail_ids = $this->getDerivativeActiveTrailIds(); + if ($active_trail_ids) { + return $this->getLinkTitleFromLink(reset($active_trail_ids)); + } + } + + /** + * Get the current menu item's parent menu title. + * + * @return string + * The menu item title. + */ + protected function getActiveTrailParentTitle() { + $active_trail_ids = $this->getDerivativeActiveTrailIds(); + + if ($active_trail_ids) { + if (count($active_trail_ids) === 1) { + return $this->getActiveItemTitle(); + } + return $this->getLinkTitleFromLink(next($active_trail_ids)); + } + } + + /** + * Get the current menu item's root menu item title. + * + * @return string + * The menu item title. + */ + protected function getActiveTrailRootTitle() { + $active_trail_ids = $this->getDerivativeActiveTrailIds(); + + if ($active_trail_ids) { + return $this->getLinkTitleFromLink(end($active_trail_ids)); + } + } + + /** + * Get an array of the active trail menu link items. + * + * @return array + * The active trail. + */ + protected function getDerivativeActiveTrailIds() { + $menu_id = $this->getDerivativeId(); + return array_filter($this->menuActiveTrail->getActiveTrailIds($menu_id)); + } + + /** + * Given a menu item ID, get that item's title. + * + * @param string $link_id + * Menu Item ID. + * + * @return string + * The menu item title. + */ + protected function getLinkTitleFromLink($link_id) { + $parameters = new MenuTreeParameters(); + $menu = $this->menuTree->load($this->getDerivativeId(), $parameters); + if ($link = $this->findLinkInTree($menu, $link_id)) { + return $link->link->getTitle(); + } + } + + /** + * Find and return the menu link item from the menu tree. + * + * @param array $menu_tree + * Associative array containing the menu link tree data. + * @param string $link_id + * Menu link id to find. + * + * @return \Drupal\Core\Menu\MenuLinkTreeElement + * The link element from the given menu tree. + */ + protected function findLinkInTree(array $menu_tree, $link_id) { + if (isset($menu_tree[$link_id])) { + return $menu_tree[$link_id]; + } + /** @var \Drupal\Core\Menu\MenuLinkTreeElement $link */ + foreach ($menu_tree as $link) { + if ($link = $this->findLinkInTree($link->subtree, $link_id)) { + return $link; + } + } + } + }