diff --git a/menu_attributes.module b/menu_attributes.module index 2ac428c..3dc93f7 100644 --- a/menu_attributes.module +++ b/menu_attributes.module @@ -6,9 +6,11 @@ * attributes for the menu link */ +use Drupal\Component\Plugin\Exception\PluginNotFoundException; use Drupal\Component\Utility\Unicode; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Menu\MenuLinkInterface; use Drupal\Core\Render\Element; use Drupal\Core\Url; @@ -89,7 +91,10 @@ function menu_attributes_menu_attribute_info() { ]; $info['accesskey'] = [ 'label' => t('Access Key'), - 'description' => t('Specifies a keyboard shortcut to access this link.', [':accesskey' => Url::fromUri('http://en.wikipedia.org/wiki/Access_keys')]), + 'description' => t( + 'Specifies a keyboard shortcut to access this link.', + [':url' => 'http://en.wikipedia.org/wiki/Access_keys'] + ), 'form' => [ '#maxlength' => 1, '#size' => 1, @@ -111,7 +116,6 @@ function menu_attributes_get_menu_attribute_info() { $info += [ 'form' => [], 'enabled' => $config->get('attribute_enable.' . $attribute), - '#default_value' => $config->get('attribute_enable.' . $attribute), ]; $info['form'] += [ '#type' => 'textfield', @@ -133,8 +137,13 @@ function menu_attributes_get_menu_attribute_info() { * @see _menu_attributes_form_alter() * @see menu_attributes_form_menu_edit_item_submit() */ -function menu_attributes_form_menu_link_edit_alter(array &$form) { - _menu_attributes_form_alter($form, $form, $form); +function menu_attributes_form_menu_link_edit_alter(array &$form, FormStateInterface $form_state, $form_id) { + // Although the form itself can be altered to show the attribute fields, there + // is not currently a proper way to save the attributes without bypassing core's + // current functionality. + // @see https://www.drupal.org/node/2656534 & https://www.drupal.org/node/2660486 + + //_menu_attributes_form_alter($form, $form_state, $form_id); } /** @@ -145,8 +154,9 @@ function menu_attributes_form_menu_link_edit_alter(array &$form) { * @see _menu_attributes_form_alter() * @see menu_attributes_form_menu_edit_item_submit() */ -function menu_attributes_form_menu_link_content_form_alter(array &$form) { - _menu_attributes_form_alter($form, $form, $form); +function menu_attributes_form_menu_link_content_form_alter(array &$form, FormStateInterface $form_state, $form_id) { + $menu_link = $form_state->getFormObject()->getEntity(); + _menu_attributes_form_alter($form, $form_state, $menu_link); } /** @@ -171,14 +181,7 @@ function menu_attributes_form_node_form_alter(&$form, FormStateInterface $form_s * @param $item * The optional existing menu item for context. */ -function _menu_attributes_form_alter(array &$form, array $item = [], array &$complete_form) { - $form['options']['#tree'] = TRUE; - $form['options']['#weight'] = 50; - - // Unset the previous value so that the new values get saved. - unset($form['options']['#value']['attributes']); - unset($form['options']['#value']['item_attributes']); - +function _menu_attributes_form_alter(array &$form, FormStateInterface $form_state, $menu_link) { $form['options'][MENU_ATTRIBUTES_LINK] = [ '#type' => 'details', '#title' => t('Menu link attributes'), @@ -196,6 +199,10 @@ function _menu_attributes_form_alter(array &$form, array $item = [], array &$com ]; $attributes = menu_attributes_get_menu_attribute_info(); + $menu_link_options = $menu_link->link->first()->options ?: []; + $menu_attributes[MENU_ATTRIBUTES_LINK] = isset($menu_link_options[MENU_ATTRIBUTES_LINK]) ? $menu_link_options[MENU_ATTRIBUTES_LINK] : []; + $menu_attributes[MENU_ATTRIBUTES_ITEM] = isset($menu_link_options[MENU_ATTRIBUTES_ITEM]) ? $menu_link_options[MENU_ATTRIBUTES_ITEM] : []; + foreach ($attributes as $attribute => $info) { // If no scope is set, this attribute should be available to both link // and item. @@ -205,60 +212,27 @@ function _menu_attributes_form_alter(array &$form, array $item = [], array &$com // Define fields for each scope. foreach ($info['scope'] as $group) { - // Merge in the proper default value. - if (isset($item['options'][$group][$attribute])) { - // If the menu link already has this attribute, use it. - $info['form']['#default_value'] = $item['options'][$group][$attribute]; - // Convert the classes array to a string for the form. - if ($attribute == 'class' && is_array($info['form']['#default_value'])) { - $info['form']['#default_value'] = implode(' ', $info['form']['#default_value']); - } - } - elseif ($item['mlid']) { - // If this is an existing link, use the raw default (usually empty). - $info['form']['#default_value'] = $info['default']; + if (isset($menu_attributes[$group][$attribute])) { + $info['form']['#default_value'] = $menu_attributes[$group][$attribute]; } - $form['options'][$group][$attribute] = $info['form'] + ['#access' => $info['enabled']]; - } - } - // Add form values for the reset of $item['options'] and - // $item['options']['attributes'] so the values will carry over during save. - foreach ($item['options'] as $key => $value) { - if ($key !== 'attributes' && !isset($form['options'][$key])) { - $form['options'][$key] = [ - '#type' => 'value', - '#value' => $value, - ]; - } - } - foreach ([MENU_ATTRIBUTES_LINK, MENU_ATTRIBUTES_ITEM] as $group) { - if (isset($item['options'][$group])) { - foreach ($item['options'][$group] as $key => $value) { - if (!isset($form['options'][$group][$key])) { - $form['options'][$group][$key] = [ - '#type' => 'value', - '#value' => $value, - ]; - } - } + $form['options'][$group][$attribute] = $info['form'] + [ + '#access' => $info['enabled'], + ]; } } // Hide the 'description' field since we will be using our own 'title' field. if (isset($form['description'])) { $form['description']['#access'] = FALSE; - - // Because this form uses a special $form['description'] field which is - // really the 'title' attribute, we need to add special pre-submit handling - // to ensure our field gets saved as the title attribute. - array_unshift($complete_form['#submit'], '_menu_attributes_form_submit'); } // Restrict access to the new form elements. $has_visible_children = (bool) Element::getVisibleChildren($form['options']['attributes']); $user_has_access = \Drupal::currentUser()->hasPermission('administer menu attributes'); $form['options']['attributes']['#access'] = ($has_visible_children && $user_has_access); + + $form['actions']['submit']['#submit'][] = '_menu_attributes_form_submit'; } /** @@ -268,14 +242,14 @@ function _menu_attributes_form_alter(array &$form, array $item = [], array &$com * will get properly saved. */ function _menu_attributes_form_submit($form, FormStateInterface $form_state) { - $values = $form_state->getValues(); + $menu_link = $form_state->getFormObject()->getEntity(); + $options = array( + MENU_ATTRIBUTES_LINK => $form_state->getValue(MENU_ATTRIBUTES_LINK), + MENU_ATTRIBUTES_ITEM => $form_state->getValue(MENU_ATTRIBUTES_ITEM), + ); - if (isset($values['menu']['options']['attributes']['title'])) { - $values['menu']['description'] = $values['menu']['options']['attributes']['title']; - } - elseif (isset($values['options']['attributes']['title'])) { - $values['description'] = $values['options']['attributes']['title']; - } + $menu_link->link->first()->options = $options; + $menu_link->save(); } /** @@ -309,3 +283,73 @@ function menu_attributes_preprocess_menu_link(&$variables) { unset($options['item_attributes']); } } + +/** + * Implements hook_preprocess_menu(). + */ +function menu_attributes_preprocess_menu(&$variables) { + foreach ($variables['items'] as &$item) { + $menu_attributes = menu_attributes_get_attributes($item['original_link']); + $menu_link_attributes = empty($menu_attributes[MENU_ATTRIBUTES_LINK]) ? [] + : array_filter($menu_attributes[MENU_ATTRIBUTES_LINK]); + $menu_item_attributes = empty($menu_attributes[MENU_ATTRIBUTES_ITEM]) ? [] + : array_filter($menu_attributes[MENU_ATTRIBUTES_ITEM]); + + if (count($menu_link_attributes)) { + $url_attributes = $item['url']->getOption('attributes') ?: []; + $attributes = array_merge($url_attributes, $menu_link_attributes); + $item['url']->setOption('attributes', $attributes); + } + + if (count($menu_item_attributes)) { + foreach ($menu_item_attributes as $attribute => $info) { + $item['attributes']->setAttribute($attribute, $info); + } + } + } +} + +/** + * Get menu link attributes. + * + * @param \Drupal\Core\Menu\MenuLinkInterface $menu_link_content_plugin + * + * @return array + */ +function menu_attributes_get_attributes(MenuLinkInterface $menu_link_content_plugin) { + $attributes = []; + + if (!$menu_link_content_plugin instanceof \Drupal\menu_link_content\Plugin\Menu\MenuLinkContent) { + return $attributes; + } + + try { + $plugin_id = $menu_link_content_plugin->getPluginId(); + } + catch (PluginNotFoundException $e) { + return $attributes; + } + + if (strpos($plugin_id, ':') === FALSE) { + return $attributes; + } + + list($entity_type, $uuid) = explode(':', $plugin_id, 2); + $entity = \Drupal::entityManager()->loadEntityByUuid($entity_type, $uuid); + + if ($entity) { + $options = $entity->link->first()->options; + $attributes[MENU_ATTRIBUTES_LINK] = isset($options[MENU_ATTRIBUTES_LINK]) ? $options[MENU_ATTRIBUTES_LINK] : []; + $attributes[MENU_ATTRIBUTES_ITEM] = isset($options[MENU_ATTRIBUTES_ITEM]) ? $options[MENU_ATTRIBUTES_ITEM] : []; + + // Class attribute needs special handling because it's stored as an array. + if (isset($attributes[MENU_ATTRIBUTES_LINK]['class'])) { + $attributes[MENU_ATTRIBUTES_LINK]['class'] = explode(' ', $attributes[MENU_ATTRIBUTES_LINK]['class']); + } + if (isset($attributes[MENU_ATTRIBUTES_ITEM]['class'])) { + $attributes[MENU_ATTRIBUTES_ITEM]['class'] = explode(' ', $attributes[MENU_ATTRIBUTES_ITEM]['class']); + } + } + + return $attributes; +}