improvements-2.x-dev/modules/improvements_menu/improvements_menu.module
modules/improvements_menu/improvements_menu.module
<?php
use Drupal\block\BlockInterface;
use Drupal\block\Entity\Block;
use Drupal\Core\Block\BlockPluginInterface;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Menu\MenuLinkInterface;
use Drupal\menu_link_content\Form\MenuLinkContentForm;
use Drupal\menu_link_content\MenuLinkContentInterface;
use Drupal\menu_ui\Form\MenuLinkEditForm;
use Drupal\system\Entity\Menu;
use Drupal\system\Plugin\Block\SystemMenuBlock;
require_once __DIR__ . '/improvements_menu.link_advanced_settings.inc';
/**
* Implements hook_form_FORM_ID_alter(): menu_add_form.
*/
function improvements_menu_form_menu_add_form_alter(array &$form, FormStateInterface $form_state): void {
// Add autofocus to menu name input
if (isset($form['label'])) {
$form['label']['#attributes']['autofocus'] = TRUE;
}
}
/**
* Implements hook_form_FORM_ID_alter(): menu_edit_form.
*/
function improvements_menu_form_menu_edit_form_alter(array &$form, FormStateInterface $form_state): void {
improvements_menu_link_advanced_form_menu_edit_form_alter($form, $form_state);
}
/**
* Implements hook_form_FORM_ID_alter(): menu_link_content_form.
*
* @see \Drupal\menu_link_content\Form\MenuLinkContentForm::form()
*/
function improvements_menu_form_menu_link_content_form_alter(array &$form, FormStateInterface $form_state): void {
$menu_link_form_object = $form_state->getFormObject(); /** @var MenuLinkContentForm $menu_link_form_object */
$menu_link = $menu_link_form_object->getEntity(); /** @var MenuLinkContentInterface $menu_link */
// Add autofocus to menu link title
if (isset($form['title']['widget'][0]['value'])) {
$form['title']['widget'][0]['value']['#attributes']['autofocus'] = 'autofocus';
}
// Remove "required" flag from link uri
if (isset($form['link']['widget'][0]['uri'])) {
$form['link']['widget'][0]['uri']['#required'] = FALSE;
}
// Clear "weight" field on new link
if ($menu_link->isNew() && isset($form['weight']['widget'][0]['value'])) {
$form['weight']['widget'][0]['value']['#default_value'] = '';
}
$form['#entity_builders'][] = 'improvements_menu_link_content_entity_builder';
$form['actions']['submit']['#submit'][] = 'improvements_menu_link_form_submit';
$form['#pre_render'][] = '\Drupal\improvements_menu\ImprovementsMenuTrustedCallbacks::menuLinkContentFormPreRender';
improvements_menu_link_advanced_settings_form_alter($form, $form_state);
}
/**
* Implements hook_form_FORM_ID_alter(): menu_link_edit.
*
* @see \Drupal\menu_ui\Form\MenuLinkEditForm::buildForm()
*/
function improvements_menu_form_menu_link_edit_alter(array &$form, FormStateInterface $form_state): void {
improvements_menu_link_advanced_settings_form_alter($form, $form_state);
$form['#submit'][] = 'improvements_menu_link_form_submit';
}
/**
* Menu link form submit callback.
*/
function improvements_menu_link_form_submit(array &$form, FormStateInterface $form_state): void {
// Redirect to menu edit form
if (!\Drupal::request()->query->has('destination')) {
$menu_link = $form_state->get('menu_link'); /** @var MenuLinkContentInterface|MenuLinkInterface $menu_link */
$form_state->setRedirect('entity.menu.edit_form', ['menu' => $menu_link->getMenuName()]);
}
}
/**
* Menu link entity builder.
*/
function improvements_menu_link_content_entity_builder(string $entity_type, MenuLinkContentInterface $menu_link, array &$form, FormStateInterface $form_state): void {
// Set default url
if (!$menu_link->get('link')->uri) {
$menu_link->get('link')->uri = 'route:<none>';
}
// Calculate menu link weight
if ($form_state->getValue('weight')[0]['value'] === '') {
$query = \Drupal::database()->select('menu_link_content_data', 'l');
$query->addExpression('MAX(l.weight)');
$query->condition('l.menu_name', $menu_link->getMenuName());
$max_weight = $query->execute()->fetchField();
$menu_link->set('weight', $max_weight + 1);
}
}
/**
* Preprocess function for menu.html.twig.
*/
function improvements_menu_preprocess_menu(array &$vars): void {
// Set menu_name variable if not exists
if (!isset($vars['menu_name'])) {
$first_menu_item = reset($vars['items']);
if (isset($first_menu_item['original_link'])) {
$first_menu_link = $first_menu_item['original_link']; /** @var MenuLinkContentInterface|MenuLinkInterface $first_menu_link */
$vars['menu_name'] = $first_menu_link->getMenuName();
}
}
// Process menu items
if (!empty($vars['menu_name']) && ($menu = Menu::load($vars['menu_name']))) {
$menu_links_advanced_options = $menu->getThirdPartySetting('improvements', 'links_options', []);
improvements_menu_preprocess_menu_items($vars, $vars['items'], $menu_links_advanced_options);
}
}
/**
* Preprocess menu items.
*
* @see improvements_menu_preprocess_menu()
*/
function improvements_menu_preprocess_menu_items(array &$menu_vars, array &$menu_items, array $menu_links_advanced_options): void {
foreach ($menu_items as $key => &$menu_item) {
$menu_link = $menu_item['original_link'] ?? NULL; /** @var MenuLinkContentInterface|MenuLinkInterface|NULL $menu_link */
if ($menu_link) {
// Set below text
$item_link_definition = $menu_link->getPluginDefinition();
if (!empty($item_link_definition['below_text'])) {
$menu_item['below_text'] = $item_link_definition['below_text'];
}
// Advanced settings
improvements_menu_link_advanced_settings_preprocess_menu_item($menu_item, $menu_vars, $menu_links_advanced_options);
if (isset($menu_item['access']) && $menu_item['access'] == FALSE) {
unset($menu_items[$key]);
continue;
}
// Processing child items
if ($menu_item['below']) {
improvements_menu_preprocess_menu_items($menu_vars, $menu_item['below'], $menu_links_advanced_options);
}
}
}
}
/**
* Implements hook_form_FORM_ID_alter(): block_form.
*
* @see \Drupal\block\BlockForm
*/
function improvements_menu_form_block_form_alter(array &$form, FormStateInterface $form_state): void {
$block_config = $form_state->getFormObject()->getEntity(); /** @var BlockInterface $block_config */
$block_plugin = $block_config->getPlugin();
if ($block_plugin instanceof SystemMenuBlock) {
$items_limitation_settings = $block_config->getThirdPartySetting('improvements', 'items_limitation');
$menu_parent_selector = \Drupal::service('menu.parent_form_selector');
$menu_name = $block_plugin->getDerivativeId();
$form['items_limitation'] = [
'#type' => 'details',
'#title' => t('Items limitation'),
'#weight' => 0, // Place element after "Menu levels"
];
$form['items_limitation']['method'] = [
'#type' => 'radios',
'#title' => t('Method'),
'#options' => [
'exclude_selected' => t('Show items without selected'),
'exclude_non_selected' => t('Show only selected items'),
],
'#default_value' => $items_limitation_settings['method'] ?? 'exclude_selected',
];
$options_cacheability = new CacheableMetadata();
$parent_select_options = $menu_parent_selector->getParentSelectOptions('', [$menu_name => $menu_name], $options_cacheability);
$options = [];
foreach ($parent_select_options as $option_key => $option_text) {
[, $item_id] = explode(':', $option_key, 2);
if ($item_id) {
$options[$item_id] = substr($option_text, 2);
}
}
$form['items_limitation']['items'] = [
'#type' => 'select',
'#title' => t('Items'),
'#options' => $options,
'#multiple' => TRUE,
'#size' => 15,
'#default_value' => $items_limitation_settings['items'] ?? [],
];
$form['tweak_cache'] = [
'#type' => 'checkbox',
'#title' => t('Tweak cache'),
'#description' => t('Delete @cache_context cache context.', ['@cache_context' => 'route.menu_active_trails']),
'#default_value' => $block_config->getThirdPartySetting('improvements', 'tweak_cache'),
];
$form['#entity_builders'][] = 'improvements_menu_block_form_entity_builder';
}
}
/**
* Menu block form entity builder.
*/
function improvements_menu_block_form_entity_builder(string $entity_type, BlockInterface $block_config, array &$form, FormStateInterface $form_state): void {
$items_limitation = [
'method' => $form_state->getValue(['items_limitation', 'method']),
'items' => array_values($form_state->getValue(['items_limitation', 'items'])),
];
$block_config->setThirdPartySetting('improvements', 'items_limitation', $items_limitation);
$block_config->setThirdPartySetting('improvements', 'tweak_cache', (bool)$form_state->getValue('tweak_cache'));
}
/**
* Implements hook_block_build_BASE_BLOCK_ID_alter(): system_menu_block.
*/
function improvements_menu_block_build_system_menu_block_alter(array &$build, BlockPluginInterface $block_plugin): void {
$block_config_id = $build['#cache']['keys'][2];
$block_config = Block::load($block_config_id); /** @var BlockInterface $block_config */
// Tweak menu block caching
if ($block_config->getThirdPartySetting('improvements', 'tweak_cache')) {
$build['#cache']['contexts'] = array_diff($build['#cache']['contexts'], ['route.menu_active_trails:' . $block_plugin->getDerivativeId()]);
}
}
/**
* Preprocess function for block--system-menu-block.html.twig.
*
* @see improvements_menu_preprocess_block()
*/
function improvements_menu_preprocess_block__system_menu_block(array &$vars): void {
/** @see improvements_block_view_alter() */
$block_config_entity = $vars['elements']['#block_config']; /** @var BlockInterface $block_config_entity */
// Items limitation
$menu_items_limitation_settings = $block_config_entity->getThirdPartySetting('improvements', 'items_limitation');
if (!empty($menu_items_limitation_settings['items'])) {
improvements_menu_exclude_items($vars['content']['#items'], $menu_items_limitation_settings['method'], $menu_items_limitation_settings['items']);
}
// Tweak menu block caching
if ($block_config_entity->getThirdPartySetting('improvements', 'tweak_cache')) {
improvements_menu_unset_active_trail($vars['content']['#items']);
}
}
/**
* Exclude menu items.
*/
function improvements_menu_exclude_items(array &$items, string $method, array $selected_items): void {
if ($method == 'exclude_selected') {
foreach ($selected_items as $selected_item) {
if (array_key_exists($selected_item, $items)) {
unset($items[$selected_item]);
}
}
}
elseif ($method == 'exclude_non_selected') {
$items = array_intersect_key($items, array_fill_keys($selected_items, NULL));
}
// Processing child items
foreach ($items as $key => &$item) {
if (!empty($item['below'])) {
improvements_menu_exclude_items($item['below'], $method, $selected_items);
}
}
}
/**
* Set in_active_trail=FALSE to all items.
*/
function improvements_menu_unset_active_trail(array &$items): void {
foreach ($items as $key => &$item) {
$items[$key]['in_active_trail'] = FALSE;
// Processing child items
if (!empty($item['below'])) {
improvements_menu_unset_active_trail($item['below']);
}
}
}
/**
* Implements hook_entity_base_field_info_alter().
*
* @param BaseFieldDefinition[] $fields
* @param EntityTypeInterface $entity_type
*
* @see MenuLinkContent::baseFieldDefinitions()
*/
function improvements_menu_entity_base_field_info_alter(array &$fields, EntityTypeInterface $entity_type): void {
if ($entity_type->id() == 'menu_link_content') {
$fields['changed']->setLabel(t('Date of change'));
}
}
