annotate core/modules/system/src/Plugin/Block/SystemMenuBlock.php @ 19:fa3358dc1485 tip

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