menu_bootstrap_icon-1.x-dev/menu_bootstrap_icon.module
menu_bootstrap_icon.module
<?php
/**
* @file
* Entity Print Views module file.
*/
use Drupal\Component\Render\FormattableMarkup;
use Drupal\Component\Utility\Html;
use Drupal\Core\Form\FormStateInterface;
use Drupal\user\Entity\Role;
use Drupal\user\RoleInterface;
/**
* User for form add/edit.
*/
function menu_bootstrap_icon__form(&$form, $options = []) {
$config_factory = \Drupal::configFactory();
$config = $config_factory->getEditable('menu_bootstrap_icon.settings');
$form['bi_icon'] = [
'#type' => 'details',
'#title' => t('Bootstrap Icon'),
'#open' => TRUE,
];
$iconDefault = !empty($options['icon']) ? $options['icon'] : '';
$form['bi_icon']['icon'] = [
'#type' => 'textfield',
'#default_value' => !empty($options['icon']) ? $options['icon'] : '',
'#prefix' => '<div class="input-group"><span class="col-sm-1 col-form-label">' . t('Icon class') . '</span>',
'#suffix' => '<span class="input-group-text"><i class="icon-preview ' . $iconDefault . '"></i></span></div>',
'#attributes' => [
'class' => [
'iconpicker',
'w-auto',
],
],
];
$form['bi_icon']['icon_tag'] = [
'#type' => 'select',
'#title' => t('HTML tag'),
'#default_value' => !empty($options['icon_tag']) ? $options['icon_tag'] : 'i',
'#options' => [
'i' => 'i',
'span' => t('span'),
],
];
$form['bi_icon']['icon_appearance'] = [
'#type' => 'select',
'#title' => t('Appearance'),
'#default_value' => !empty($options['icon_appearance']) ? $options['icon_appearance'] : 'before',
'#options' => [
'before' => t('Before text'),
'after' => t('After text'),
'only' => t('Without text'),
],
];
if ($config->get('use_cdn')) {
$form['#attached']['library'][] = 'menu_bootstrap_icon/cdn';
}
$form['#attached']['library'][] = 'menu_bootstrap_icon/iconspicker';
$searchList = base_path() . \Drupal::service('extension.list.module')->getPath('menu_bootstrap_icon') . '/js/iconSearch.json';
$form['#attached']['drupalSettings']['menu_bootstrap_icon']['icons'] = $searchList;
}
/**
* Adds an optional role field to menu items.
*/
function menu_bootstrap_icon__role(&$form, $options = []) {
$form['menu_item_roles'] = [
'#type' => 'checkboxes',
'#title' => t('Menu Item Roles'),
'#description' => t('Set the roles allowed for this menu item.'),
'#options' => array_map(fn(RoleInterface $role) => Html::escape($role->label()), Role::loadMultiple()),
'#default_value' => $options['roles'] ?? [],
];
}
/**
* Implements hook_form_BASE_FORM_ID_alter().
*/
function menu_bootstrap_icon_form_menu_link_content_form_alter(&$form, FormStateInterface $form_state, $form_id) {
$menu_link = $form_state->getFormObject()->getEntity();
$options = $menu_link->link->first()->options ?: [];
menu_bootstrap_icon__form($form, $options);
menu_bootstrap_icon__role($form, $options);
$form['actions']['submit']['#submit'][] = 'menu_bootstrap_icon_menu_link_content_form_submit';
}
/**
* Implements hook_form_BASE_FORM_ID_alter().
*/
function menu_bootstrap_icon_form_menu_link_edit_alter(&$form, FormStateInterface $form_state, $form_id) {
$options = $form_state->getBuildInfo()['args'][0]->getOptions();
menu_bootstrap_icon__form($form, $options);
menu_bootstrap_icon__role($form, $options);
$form['#submit'][] = 'menu_bootstrap_icon_form_menu_link_edit_submit';
}
/**
* Process the submitted form.
*
* @param array $form
* Form Array.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* Form State Interface.
*
* @throws \Drupal\Core\Entity\EntityStorageException
*/
function menu_bootstrap_icon_menu_link_content_form_submit(array $form, FormStateInterface $form_state) {
$icon_field = $form_state->getValue('icon');
$icon_tag = $form_state->getValue('icon_tag');
$icon_appearance = $form_state->getValue('icon_appearance');
$roles = array_filter($form_state->getValue('menu_item_roles') ?? []);
$options = [
'icon' => !empty($icon_field) ? Html::escape($icon_field) : '',
'icon_tag' => !empty($icon_tag) ? Html::escape($icon_tag) : 'i',
'icon_appearance' => !empty($icon_appearance) ? Html::escape($icon_appearance) : 'before',
'roles' => !empty($roles) ? $roles : NULL,
];
/** @var \Drupal\menu_link_content\Entity\MenuLinkContent $menu_link */
$menu_link = $form_state->getFormObject()->getEntity();
$menu_link_options = $menu_link->link->first()->options;
$merged = array_merge($menu_link_options, $options);
$menu_link->link->first()->options = $merged;
$menu_link->save();
}
/**
* Process the submitted edit form.
*
* @param array $form
* Array Form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* Form Interface.
*/
function menu_bootstrap_icon_form_menu_link_edit_submit(array $form, FormStateInterface $form_state) {
$options = $form_state->getBuildInfo()['args'][0]->getOptions();
$menu_link_id = $form_state->getValue('menu_link_id');
$icon_field = $form_state->getValue('icon');
$icon_tag = $form_state->getValue('icon_tag');
$icon_appearance = $form_state->getValue('icon_appearance');
$options['icon'] = !empty($icon_field) ? Html::escape($icon_field) : '';
$options['icon_tag'] = !empty($icon_tag) ? Html::escape($icon_tag) : 'i';
$options['icon_appearance'] = !empty($icon_appearance) ? Html::escape($icon_appearance) : 'before';
$roles = array_filter($form_state->getValue('menu_item_roles') ?? []);
$options['roles'] = !empty($roles) ? $roles : NULL;
if (!empty($menu_link_id)) {
$query = \Drupal::database()->update('menu_tree');
$query->fields([
'options' => serialize($options),
]);
$query->condition('id', $menu_link_id);
$query->execute();
$config_factory = \Drupal::configFactory();
$config = $config_factory->getEditable('menu_bootstrap_icon.settings');
$icons = $config->get('menu_link_icons');
// Array key cannot contain dot in the config.
$config_key = str_replace('.', '_', $menu_link_id);
if (empty($options['icon'])) {
if (isset($icons[$config_key])) {
unset($icons[$config_key]);
}
}
else {
$icons[$config_key] = [
'icon' => $options['icon'],
'tag' => $options['icon_tag'],
'appearance' => $options['icon_appearance'],
'roles' => $options['roles'],
];
}
$config->set('menu_link_icons', $icons);
$config->save();
}
}
/**
* Implements hook_menu_links_discovered_alter().
*/
function menu_bootstrap_icon_menu_links_discovered_alter(&$links) {
// After clearing the site's cache, the options were cleared from the
// menu_tree database table (I'm not sure if this is a bug or normal
// behaviour)... but we need to re-apply icon on each menu link item.
$config = \Drupal::config('menu_bootstrap_icon.settings');
$icons = $config->get('menu_link_icons');
foreach ($links as $link_id => &$link) {
if (empty($link['id'])) {
continue;
}
// Array key cannot contain dot in the config.
$config_key = str_replace('.', '_', $link_id);
if (!empty($icons[$config_key])) {
$link['options']['icon_tag'] = !empty($icons[$config_key]['tag']) ? $icons[$config_key]['tag'] : 'i';
$link['options']['icon_appearance'] = !empty($icons[$config_key]['appearance']) ? $icons[$config_key]['appearance'] : 'before';
$link['options']['icon'] = !empty($icons[$config_key]['icon']) ? $icons[$config_key]['icon'] : $icons[$config_key];
}
}
}
/**
* Implements hook_link_alter().
*/
function menu_bootstrap_icon_link_alter(&$variables) {
if (!empty($variables['options']['icon']) && empty($variables['options']['already_processed'])) {
$class = $variables['options']['icon'];
$tag = !empty($variables['options']['icon_tag']) ? $variables['options']['icon_tag'] : 'i';
$appearance = !empty($variables['options']['icon_appearance']) ? $variables['options']['icon_appearance'] : 'before';
/** @var \Drupal\Core\Url $url */
$url = $variables['url'];
$is_link = ($url->isRouted() && ($url->getRouteName() == '<nolink>')) ? FALSE : TRUE;
switch ($appearance) {
case "only":
if ($is_link) {
$variables['options']['attributes']['aria-label'] = $variables['text'];
$variables['text'] = new FormattableMarkup('<' . $tag . ' class="' . $class . '" aria-hidden="true"></' . $tag . '>', []);
}
else {
$variables['text'] = new FormattableMarkup('<' . $tag . ' class="' . $class . '" aria-hidden="true" title="@title"></' . $tag . '><span class="sr-only">@title</span>', [
'@title' => $variables['text'],
]);
}
break;
case "after":
$variables['text'] = new FormattableMarkup('@title <' . $tag . ' class="' . $class . '" aria-hidden="true"></' . $tag . '>', [
'@title' => $variables['text'],
]);
break;
case "before":
default:
$variables['text'] = new FormattableMarkup('<' . $tag . ' class="' . $class . '" aria-hidden="true"></' . $tag . '> <span class="link-text">@title</span>', [
'@title' => $variables['text'],
]);
break;
}
$variables['options']['already_processed'] = TRUE;
}
$currentRoles = \Drupal::currentUser()->getRoles();
if (!in_array('administrator', $currentRoles) &&
!empty($variables["options"]["roles"]) &&
empty(array_intersect($currentRoles, array_keys($variables["options"]["roles"])))) {
$variables['access'] = FALSE;
$variables['options']['attributes']['class'][] = 'js-hide';
$variables['options']['attributes']['disabled'] = 'disabled';
}
}
/**
* Implements hook_preprocess_menu().
*/
function menu_bootstrap_icon_preprocess_menu(&$variables, $hook) {
if (!empty($variables['items'])) {
menu_bootstrap_icon__preprocess_menu($variables['items']);
}
}
/**
* {@inheritdoc}
*
* Process menu.
*/
function menu_bootstrap_icon__preprocess_menu(&$items) {
foreach ($items as &$item) {
if (empty($item['url'])) {
continue;
}
menu_bootstrap_icon__preprocess_menu_item($item);
if (!empty($item['below'])) {
menu_bootstrap_icon__preprocess_menu($item['below']);
}
}
}
/**
* {@inheritdoc}
*
* Process menu item.
*/
function menu_bootstrap_icon__preprocess_menu_item(&$item) {
/** @var \Drupal\Core\Url $url */
$url = $item['url'];
$options = $url->getOptions();
if (!empty($options['icon']) && empty($options['already_processed'])) {
$options['attributes']['class'][] = 'icon-link icon-link-hover';
$url->setOptions($options);
$class = $options['icon'];
$tag = !empty($options['icon_tag']) ? $options['icon_tag'] : 'i';
$appearance = !empty($options['icon_appearance']) ? $options['icon_appearance'] : 'before';
$is_link = ($url->isRouted() && ($url->getRouteName() == '<nolink>')) ? FALSE : TRUE;
switch ($appearance) {
case "only":
if ($is_link) {
$item['attributes']['aria-label'] = $item['title'];
$item['title'] = new FormattableMarkup('<' . $tag . ' class="' . $class . '" aria-hidden="true"></' . $tag . '>', []);
}
else {
$item['title'] = new FormattableMarkup('<' . $tag . ' class="' . $class . '" aria-hidden="true" title="@title"></' . $tag . '><span class="sr-only">@title</span>', [
'@title' => $item['title'],
]);
}
break;
case "after":
$item['title'] = new FormattableMarkup('<span class="link-text">@title</span> <' . $tag . ' class="' . $class . '" aria-hidden="true"></' . $tag . '>', [
'@title' => $item['title'],
]);
break;
case "before":
default:
$item['title'] = new FormattableMarkup('<' . $tag . ' class="' . $class . '" aria-hidden="true"></' . $tag . '> <span class="link-text">@title</span>', [
'@title' => $item['title'],
]);
break;
}
$item['url']->setOption('already_processed', TRUE);
}
}
