taxonomy_menu_ui.module in Taxonomy Menu UI 8
Add ability to create menu links for taxonomy terms.
File
taxonomy_menu_ui.moduleView source
<?php
/**
* @file
* Add ability to create menu links for taxonomy terms.
*/
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\menu_link_content\Entity\MenuLinkContent;
use Drupal\taxonomy\VocabularyInterface;
use Drupal\taxonomy\TermInterface;
use Drupal\taxonomy\Entity\Vocabulary;
/**
* Helper function to create or update a menu link for a taxonomy term.
*
* @param \Drupal\taxonomy\TermInterface $term
* Term entity.
* @param array $values
* Values for the menu link.
*/
function _menu_ui_taxonomy_term_save(TermInterface $term, array $values) {
/** @var \Drupal\menu_link_content\MenuLinkContentInterface $entity */
if (!empty($values['entity_id'])) {
$entity = MenuLinkContent::load($values['entity_id']);
if ($entity
->isTranslatable()) {
if (!$entity
->hasTranslation($term
->language()
->getId())) {
$entity = $entity
->addTranslation($term
->language()
->getId(), $entity
->toArray());
}
else {
$entity = $entity
->getTranslation($term
->language()
->getId());
}
}
}
else {
// Create a new menu_link_content entity.
$entity = MenuLinkContent::create([
'link' => [
'uri' => 'internal:/taxonomy/term/' . $term
->id(),
],
'langcode' => $term
->language()
->getId(),
]);
$entity->enabled->value = 1;
}
$entity->title->value = trim($values['title']);
$entity->description->value = trim($values['description']);
$entity->menu_name->value = $values['menu_name'];
$entity->parent->value = $values['parent'];
$entity->weight->value = isset($values['weight']) ? $values['weight'] : 0;
$entity
->save();
}
/**
* Returns the definition for a menu link for the given term.
*
* @param \Drupal\taxonomy\TermInterface $term
* The term entity.
*
* @return array
* An array that contains default values for the menu link form.
*/
function taxonomy_menu_ui_get_menu_link_defaults(TermInterface $term) {
// Prepare the definition for the edit form.
/** @var \Drupal\taxonomy\VocabularyInterface $vocabulary */
$vid = $term
->bundle();
$vocabulary = Vocabulary::load($vid);
$menu_name = strtok($vocabulary
->getThirdPartySetting('menu_ui', 'parent', 'main:'), ':');
$defaults = FALSE;
if ($term
->id()) {
$id = FALSE;
// Give priority to the default menu.
$vocabulary_menus = $vocabulary
->getThirdPartySetting('menu_ui', 'available_menus', [
'main',
]);
if (in_array($menu_name, $vocabulary_menus)) {
$query = \Drupal::entityQuery('menu_link_content')
->condition('link.uri', 'taxonomy/term/' . $term
->id())
->condition('menu_name', $menu_name)
->sort('id', 'ASC')
->range(0, 1);
$result = $query
->execute();
$id = !empty($result) ? reset($result) : FALSE;
}
// Check all allowed menus if a link does not exist in the default menu.
if (!$id && !empty($vocabulary_menus)) {
$query = \Drupal::entityQuery('menu_link_content')
->condition('link.uri', 'internal:/taxonomy/term/' . $term
->id())
->condition('menu_name', array_values($vocabulary_menus), 'IN')
->sort('id', 'ASC')
->range(0, 1);
$result = $query
->execute();
$id = !empty($result) ? reset($result) : FALSE;
}
if ($id) {
$menu_link = MenuLinkContent::load($id);
$menu_link = \Drupal::service('entity.repository')
->getTranslationFromContext($menu_link);
$defaults = [
'entity_id' => $menu_link
->id(),
'id' => $menu_link
->getPluginId(),
'title' => $menu_link
->getTitle(),
'title_max_length' => $menu_link
->getFieldDefinitions()['title']
->getSetting('max_length'),
'description' => $menu_link
->getDescription(),
'menu_name' => $menu_link
->getMenuName(),
'parent' => $menu_link
->getParentId(),
'weight' => $menu_link
->getWeight(),
];
}
}
if (!$defaults) {
// Get the default max_length of a menu link title from the base field
// definition.
$field_definitions = \Drupal::service('entity_field.manager')
->getBaseFieldDefinitions('menu_link_content');
$max_length = $field_definitions['title']
->getSetting('max_length');
$defaults = [
'entity_id' => 0,
'id' => '',
'title' => '',
'title_max_length' => $max_length,
'description' => '',
'menu_name' => $menu_name,
'parent' => '',
'weight' => 0,
];
}
return $defaults;
}
/**
* Implements hook_form_BASE_FORM_ID_alter() for \Drupal\taxonomy\TermForm.
*
* Adds menu item fields to the taxonomy term form.
*
* @see taxonomy_menu_ui_form_taxonomy_term_form_submit()
*/
function taxonomy_menu_ui_form_taxonomy_term_form_alter(&$form, FormStateInterface $form_state) {
// Generate a list of possible parents
// (not including this link or descendants).
// @todo This must be handled in a #process handler.
$term = $form_state
->getFormObject()
->getEntity();
$defaults = taxonomy_menu_ui_get_menu_link_defaults($term);
/** @var \Drupal\taxonomy\VocabularyInterface $vocabulary */
$vid = $term
->bundle();
$vocabulary = Vocabulary::load($vid);
/** @var \Drupal\Core\Menu\MenuParentFormSelectorInterface $menu_parent_selector */
$menu_parent_selector = \Drupal::service('menu.parent_form_selector');
$menu_names = menu_ui_get_menus();
$vocabulary_menus = $vocabulary
->getThirdPartySetting('menu_ui', 'available_menus', [
'main',
]);
$available_menus = [];
foreach ($vocabulary_menus as $menu) {
$available_menus[$menu] = $menu_names[$menu];
}
if ($defaults['id']) {
$default = $defaults['menu_name'] . ':' . $defaults['parent'];
}
else {
$default = $vocabulary
->getThirdPartySetting('menu_ui', 'parent', 'main:');
}
$parent_element = $menu_parent_selector
->parentSelectElement($default, $defaults['id'], $available_menus);
// If no possible parent menu items were found, there is nothing to display.
if (empty($parent_element)) {
return;
}
$current_user = \Drupal::currentUser();
$access = $current_user
->hasPermission('administer menu');
// Menu admin per menu integration.
if (!$access && \Drupal::moduleHandler()
->moduleExists('menu_admin_per_menu')) {
foreach (array_keys($available_menus) as $available_menu_id) {
if ($access = $current_user
->hasPermission('administer ' . $available_menu_id . ' menu items')) {
break;
}
}
}
$form['menu'] = [
'#type' => 'details',
'#title' => t('Menu settings'),
'#access' => $access,
'#open' => (bool) $defaults['id'],
'#group' => 'advanced',
'#attached' => [
'library' => [
'menu_ui/drupal.menu_ui',
],
],
'#tree' => TRUE,
'#weight' => 10,
'#attributes' => [
'class' => [
'menu-link-form',
],
],
];
$form['menu']['enabled'] = [
'#type' => 'checkbox',
'#title' => t('Provide a menu link'),
'#default_value' => (int) (bool) $defaults['id'],
];
$form['menu']['link'] = [
'#type' => 'container',
'#parents' => [
'menu',
],
'#states' => [
'invisible' => [
'input[name="menu[enabled]"]' => [
'checked' => FALSE,
],
],
],
];
// Populate the element with the link data.
foreach ([
'id',
'entity_id',
] as $key) {
$form['menu']['link'][$key] = [
'#type' => 'value',
'#value' => $defaults[$key],
];
}
$form['menu']['link']['title'] = [
'#type' => 'textfield',
'#title' => t('Menu link title'),
'#default_value' => $defaults['title'],
'#maxlength' => $defaults['title_max_length'],
];
$form['menu']['link']['description'] = [
'#type' => 'textarea',
'#title' => t('Description'),
'#default_value' => $defaults['description'],
'#rows' => 1,
'#description' => t('Shown when hovering over the menu link.'),
];
$form['menu']['link']['menu_parent'] = $parent_element;
$form['menu']['link']['menu_parent']['#title'] = t('Parent item');
$form['menu']['link']['menu_parent']['#attributes']['class'][] = 'menu-parent-select';
$form['menu']['link']['weight'] = [
'#type' => 'number',
'#title' => t('Weight'),
'#default_value' => $defaults['weight'],
'#description' => t('Menu links with lower weights are displayed before links with higher weights.'),
];
foreach (array_keys($form['actions']) as $action) {
if ($action != 'preview' && isset($form['actions'][$action]['#type']) && $form['actions'][$action]['#type'] === 'submit') {
$form['actions'][$action]['#submit'][] = 'taxonomy_menu_ui_form_taxonomy_term_form_submit';
}
}
$form['#entity_builders'][] = 'taxonomy_menu_ui_taxonomy_term_builder';
}
/**
* Entity form builder to add the menu information to the taxonomy term.
*/
function taxonomy_menu_ui_taxonomy_term_builder($entity_type, TermInterface $entity, &$form, FormStateInterface $form_state) {
$entity->menu = $form_state
->getValue('menu');
}
/**
* Form submission handler for menu item field on the texonomy term form.
*
* @see taxonomy_menu_ui_form_taxonomy_term_form_alter()
*/
function taxonomy_menu_ui_form_taxonomy_term_form_submit($form, FormStateInterface $form_state) {
$term = $form_state
->getFormObject()
->getEntity();
if (!$form_state
->isValueEmpty('menu')) {
$values = $form_state
->getValue('menu');
if (empty($values['enabled'])) {
if ($values['entity_id']) {
$entity = MenuLinkContent::load($values['entity_id']);
$entity
->delete();
}
}
elseif (trim($values['title'])) {
// Decompose the selected menu parent option into 'menu_name' and
// 'parent', if the form used the default parent selection widget.
if (!empty($values['menu_parent'])) {
list($menu_name, $parent) = explode(':', $values['menu_parent'], 2);
$values['menu_name'] = $menu_name;
$values['parent'] = $parent;
}
_menu_ui_taxonomy_term_save($term, $values);
}
}
}
/**
* Implements hook_form_FORM_ID_alter() for \Drupal\taxonomy\VocabularyForm.
*
* Adds menu options to the taxonomy vocabulary form.
*
* @see VocabularyForm::form()
* @see taxonomy_menu_ui_form_taxonomy_vocabulary_form_submit()
*/
function taxonomy_menu_ui_form_taxonomy_vocabulary_form_alter(&$form, FormStateInterface $form_state) {
/** @var \Drupal\Core\Menu\MenuParentFormSelectorInterface $menu_parent_selector */
$menu_parent_selector = \Drupal::service('menu.parent_form_selector');
$menu_options = menu_ui_get_menus();
/** @var \Drupal\taxonomy\VocabularyInterface $vocabulary */
$vocabulary = $form_state
->getFormObject()
->getEntity();
$form['menu'] = [
'#type' => 'details',
'#title' => t('Menu settings'),
'#attached' => [
'library' => [
'menu_ui/drupal.menu_ui.admin',
],
],
'#group' => 'additional_settings',
];
$form['menu']['menu_options'] = [
'#type' => 'checkboxes',
'#title' => t('Available menus'),
'#default_value' => $vocabulary
->getThirdPartySetting('menu_ui', 'available_menus', [
'main',
]),
'#options' => $menu_options,
'#description' => t('The menus available to place links in for this content type.'),
];
// @todo See if we can avoid pre-loading all options by changing the form or
// using a #process callback. https://www.drupal.org/node/2310319
// To avoid an 'illegal option' error after saving the form we have to load
// all available menu parents. Otherwise, it is not possible to dynamically
// add options to the list using ajax.
$options_cacheability = new CacheableMetadata();
$options = $menu_parent_selector
->getParentSelectOptions('', NULL, $options_cacheability);
$form['menu']['menu_parent'] = [
'#type' => 'select',
'#title' => t('Default parent item'),
'#default_value' => $vocabulary
->getThirdPartySetting('menu_ui', 'parent', 'main:'),
'#options' => $options,
'#description' => t('Choose the menu item to be the default parent for a new link in the content authoring form.'),
'#attributes' => [
'class' => [
'menu-title-select',
],
],
];
$options_cacheability
->applyTo($form['menu']['menu_parent']);
$form['#validate'][] = 'taxonomy_menu_ui_form_taxonomy_vocabulary_form_validate';
$form['#entity_builders'][] = 'taxonomy_menu_ui_form_taxonomy_vocabulary_form_builder';
}
/**
* Validate handler for forms with menu options.
*
* @see taxonomy_menu_ui_form_taxonomy_vocabulary_form_alter()
*/
function taxonomy_menu_ui_form_taxonomy_vocabulary_form_validate(&$form, FormStateInterface $form_state) {
$available_menus = array_filter($form_state
->getValue('menu_options'));
// If there is at least one menu allowed, the selected item should be in
// one of them.
if (count($available_menus)) {
$menu_item_id_parts = explode(':', $form_state
->getValue('menu_parent'));
if (!in_array($menu_item_id_parts[0], $available_menus)) {
$form_state
->setErrorByName('menu_parent', t('The selected menu item is not under one of the selected menus.'));
}
}
else {
$form_state
->setValue('menu_parent', '');
}
}
/**
* Entity builder for the taxonomy vocabulary form with menu options.
*
* @see taxonomy_menu_ui_form_taxonomy_vocabulary_form_alter()
*/
function taxonomy_menu_ui_form_taxonomy_vocabulary_form_builder($entity_type, VocabularyInterface $vocabulary, &$form, FormStateInterface $form_state) {
$vocabulary
->setThirdPartySetting('menu_ui', 'available_menus', array_values(array_filter($form_state
->getValue('menu_options'))));
$vocabulary
->setThirdPartySetting('menu_ui', 'parent', $form_state
->getValue('menu_parent'));
}
/**
* Implements hook_entity_extra_field_info().
*
* Add extra fields for each taxonomy vocabulary to show Menu settings.
*/
function taxonomy_menu_ui_entity_extra_field_info() {
$extra = [];
/** @var \Drupal\Core\Entity\EntityInterface $bundle */
foreach (Vocabulary::loadMultiple() as $bundle) {
$extra['taxonomy_term'][$bundle
->id()]['form']['menu'] = [
'label' => t('Menu settings'),
'description' => t('Field for menu settings'),
'weight' => 10,
];
}
return $extra;
}
/**
* Implements hook_ENTITY_TYPE_translation_delete().
*
* Remove translation from the menu.
*/
function taxonomy_menu_ui_taxonomy_term_translation_delete(EntityInterface $entity) {
$language_id = $entity
->language()
->getId();
$menus = \Drupal::entityTypeManager()
->getStorage('menu_link_content')
->loadByProperties([
'link__uri' => 'internal:/taxonomy/term/' . $entity
->id(),
]);
foreach ($menus as $menu) {
if ($menu
->hasTranslation($language_id)) {
$menu
->removeTranslation($language_id);
$menu
->save();
}
}
}