annotate core/modules/system/src/Plugin/Block/SystemMenuBlock.php @ 14:1fec387a4317

Update Drupal core to 8.5.2 via Composer
author Chris Cannam
date Mon, 23 Apr 2018 09:46:53 +0100
parents 4c8ae668cc8c
children af1871eacc83
rev   line source
Chris@0 1 <?php
Chris@0 2
Chris@0 3 namespace Drupal\system\Plugin\Block;
Chris@0 4
Chris@0 5 use Drupal\Core\Block\BlockBase;
Chris@0 6 use Drupal\Core\Cache\Cache;
Chris@0 7 use Drupal\Core\Form\FormStateInterface;
Chris@0 8 use Drupal\Core\Menu\MenuLinkTreeInterface;
Chris@0 9 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
Chris@0 10 use Symfony\Component\DependencyInjection\ContainerInterface;
Chris@0 11
Chris@0 12 /**
Chris@0 13 * Provides a generic Menu block.
Chris@0 14 *
Chris@0 15 * @Block(
Chris@0 16 * id = "system_menu_block",
Chris@0 17 * admin_label = @Translation("Menu"),
Chris@0 18 * category = @Translation("Menus"),
Chris@14 19 * deriver = "Drupal\system\Plugin\Derivative\SystemMenuBlock",
Chris@14 20 * forms = {
Chris@14 21 * "settings_tray" = "\Drupal\system\Form\SystemMenuOffCanvasForm",
Chris@14 22 * },
Chris@0 23 * )
Chris@0 24 */
Chris@0 25 class SystemMenuBlock extends BlockBase implements ContainerFactoryPluginInterface {
Chris@0 26
Chris@0 27 /**
Chris@0 28 * The menu link tree service.
Chris@0 29 *
Chris@0 30 * @var \Drupal\Core\Menu\MenuLinkTreeInterface
Chris@0 31 */
Chris@0 32 protected $menuTree;
Chris@0 33
Chris@0 34 /**
Chris@0 35 * Constructs a new SystemMenuBlock.
Chris@0 36 *
Chris@0 37 * @param array $configuration
Chris@0 38 * A configuration array containing information about the plugin instance.
Chris@0 39 * @param string $plugin_id
Chris@0 40 * The plugin_id for the plugin instance.
Chris@0 41 * @param array $plugin_definition
Chris@0 42 * The plugin implementation definition.
Chris@0 43 * @param \Drupal\Core\Menu\MenuLinkTreeInterface $menu_tree
Chris@0 44 * The menu tree service.
Chris@0 45 */
Chris@14 46 public function __construct(array $configuration, $plugin_id, $plugin_definition, MenuLinkTreeInterface $menu_tree) {
Chris@0 47 parent::__construct($configuration, $plugin_id, $plugin_definition);
Chris@0 48 $this->menuTree = $menu_tree;
Chris@0 49 }
Chris@0 50
Chris@0 51 /**
Chris@0 52 * {@inheritdoc}
Chris@0 53 */
Chris@0 54 public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
Chris@0 55 return new static(
Chris@0 56 $configuration,
Chris@0 57 $plugin_id,
Chris@0 58 $plugin_definition,
Chris@14 59 $container->get('menu.link_tree')
Chris@0 60 );
Chris@0 61 }
Chris@0 62
Chris@0 63 /**
Chris@0 64 * {@inheritdoc}
Chris@0 65 */
Chris@0 66 public function blockForm($form, FormStateInterface $form_state) {
Chris@0 67 $config = $this->configuration;
Chris@0 68
Chris@0 69 $defaults = $this->defaultConfiguration();
Chris@0 70 $form['menu_levels'] = [
Chris@0 71 '#type' => 'details',
Chris@0 72 '#title' => $this->t('Menu levels'),
Chris@0 73 // Open if not set to defaults.
Chris@0 74 '#open' => $defaults['level'] !== $config['level'] || $defaults['depth'] !== $config['depth'],
Chris@0 75 '#process' => [[get_class(), 'processMenuLevelParents']],
Chris@0 76 ];
Chris@0 77
Chris@0 78 $options = range(0, $this->menuTree->maxDepth());
Chris@0 79 unset($options[0]);
Chris@0 80
Chris@0 81 $form['menu_levels']['level'] = [
Chris@0 82 '#type' => 'select',
Chris@0 83 '#title' => $this->t('Initial visibility level'),
Chris@0 84 '#default_value' => $config['level'],
Chris@0 85 '#options' => $options,
Chris@0 86 '#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.'),
Chris@0 87 '#required' => TRUE,
Chris@0 88 ];
Chris@0 89
Chris@0 90 $options[0] = $this->t('Unlimited');
Chris@0 91
Chris@0 92 $form['menu_levels']['depth'] = [
Chris@0 93 '#type' => 'select',
Chris@0 94 '#title' => $this->t('Number of levels to display'),
Chris@0 95 '#default_value' => $config['depth'],
Chris@0 96 '#options' => $options,
Chris@0 97 '#description' => $this->t('This maximum number includes the initial level.'),
Chris@0 98 '#required' => TRUE,
Chris@0 99 ];
Chris@0 100
Chris@0 101 return $form;
Chris@0 102 }
Chris@0 103
Chris@0 104 /**
Chris@0 105 * Form API callback: Processes the menu_levels field element.
Chris@0 106 *
Chris@0 107 * Adjusts the #parents of menu_levels to save its children at the top level.
Chris@0 108 */
Chris@0 109 public static function processMenuLevelParents(&$element, FormStateInterface $form_state, &$complete_form) {
Chris@0 110 array_pop($element['#parents']);
Chris@0 111 return $element;
Chris@0 112 }
Chris@0 113
Chris@0 114 /**
Chris@0 115 * {@inheritdoc}
Chris@0 116 */
Chris@0 117 public function blockSubmit($form, FormStateInterface $form_state) {
Chris@0 118 $this->configuration['level'] = $form_state->getValue('level');
Chris@0 119 $this->configuration['depth'] = $form_state->getValue('depth');
Chris@0 120 }
Chris@0 121
Chris@0 122 /**
Chris@0 123 * {@inheritdoc}
Chris@0 124 */
Chris@0 125 public function build() {
Chris@0 126 $menu_name = $this->getDerivativeId();
Chris@0 127 $parameters = $this->menuTree->getCurrentRouteMenuTreeParameters($menu_name);
Chris@0 128
Chris@0 129 // Adjust the menu tree parameters based on the block's configuration.
Chris@0 130 $level = $this->configuration['level'];
Chris@0 131 $depth = $this->configuration['depth'];
Chris@0 132 $parameters->setMinDepth($level);
Chris@0 133 // When the depth is configured to zero, there is no depth limit. When depth
Chris@0 134 // is non-zero, it indicates the number of levels that must be displayed.
Chris@0 135 // Hence this is a relative depth that we must convert to an actual
Chris@0 136 // (absolute) depth, that may never exceed the maximum depth.
Chris@0 137 if ($depth > 0) {
Chris@0 138 $parameters->setMaxDepth(min($level + $depth - 1, $this->menuTree->maxDepth()));
Chris@0 139 }
Chris@0 140
Chris@0 141 // For menu blocks with start level greater than 1, only show menu items
Chris@0 142 // from the current active trail. Adjust the root according to the current
Chris@0 143 // position in the menu in order to determine if we can show the subtree.
Chris@0 144 if ($level > 1) {
Chris@0 145 if (count($parameters->activeTrail) >= $level) {
Chris@0 146 // Active trail array is child-first. Reverse it, and pull the new menu
Chris@0 147 // root based on the parent of the configured start level.
Chris@0 148 $menu_trail_ids = array_reverse(array_values($parameters->activeTrail));
Chris@0 149 $menu_root = $menu_trail_ids[$level - 1];
Chris@0 150 $parameters->setRoot($menu_root)->setMinDepth(1);
Chris@0 151 if ($depth > 0) {
Chris@0 152 $parameters->setMaxDepth(min($level - 1 + $depth - 1, $this->menuTree->maxDepth()));
Chris@0 153 }
Chris@0 154 }
Chris@0 155 else {
Chris@0 156 return [];
Chris@0 157 }
Chris@0 158 }
Chris@0 159
Chris@0 160 $tree = $this->menuTree->load($menu_name, $parameters);
Chris@0 161 $manipulators = [
Chris@0 162 ['callable' => 'menu.default_tree_manipulators:checkAccess'],
Chris@0 163 ['callable' => 'menu.default_tree_manipulators:generateIndexAndSort'],
Chris@0 164 ];
Chris@0 165 $tree = $this->menuTree->transform($tree, $manipulators);
Chris@0 166 return $this->menuTree->build($tree);
Chris@0 167 }
Chris@0 168
Chris@0 169 /**
Chris@0 170 * {@inheritdoc}
Chris@0 171 */
Chris@0 172 public function defaultConfiguration() {
Chris@0 173 return [
Chris@0 174 'level' => 1,
Chris@0 175 'depth' => 0,
Chris@0 176 ];
Chris@0 177 }
Chris@0 178
Chris@0 179 /**
Chris@0 180 * {@inheritdoc}
Chris@0 181 */
Chris@0 182 public function getCacheTags() {
Chris@0 183 // Even when the menu block renders to the empty string for a user, we want
Chris@0 184 // the cache tag for this menu to be set: whenever the menu is changed, this
Chris@0 185 // menu block must also be re-rendered for that user, because maybe a menu
Chris@0 186 // link that is accessible for that user has been added.
Chris@0 187 $cache_tags = parent::getCacheTags();
Chris@0 188 $cache_tags[] = 'config:system.menu.' . $this->getDerivativeId();
Chris@0 189 return $cache_tags;
Chris@0 190 }
Chris@0 191
Chris@0 192 /**
Chris@0 193 * {@inheritdoc}
Chris@0 194 */
Chris@0 195 public function getCacheContexts() {
Chris@0 196 // ::build() uses MenuLinkTreeInterface::getCurrentRouteMenuTreeParameters()
Chris@0 197 // to generate menu tree parameters, and those take the active menu trail
Chris@0 198 // into account. Therefore, we must vary the rendered menu by the active
Chris@0 199 // trail of the rendered menu.
Chris@0 200 // Additional cache contexts, e.g. those that determine link text or
Chris@0 201 // accessibility of a menu, will be bubbled automatically.
Chris@0 202 $menu_name = $this->getDerivativeId();
Chris@0 203 return Cache::mergeContexts(parent::getCacheContexts(), ['route.menu_active_trails:' . $menu_name]);
Chris@0 204 }
Chris@0 205
Chris@0 206 }