Issue #2301313 by pwolanin, dawehner, Wim Leers, effulgentsia, kgoel, YesCT, glide: MenuLinkNG part3 (no conversions): MenuLinkContent UI.

8.0.x
Alex Pott 2014-07-21 21:03:47 +01:00
parent 49f4fddbf3
commit ef4e932d71
14 changed files with 1120 additions and 22 deletions

View File

@ -0,0 +1 @@
definitions: []

View File

@ -0,0 +1,27 @@
menu_link.static.overrides:
type: mapping
label: 'Menu link overrides'
mapping:
definitions:
type: sequence
label: Definitions
sequence:
- type: mapping
label: Definition
mapping:
menu_name:
type: string
label: 'Menu name'
parent:
type: string
label: 'Parent'
weight:
type: integer
label: 'Weight'
expanded:
type: boolean
label: 'Expanded'
hidden:
type: boolean
label: 'Hidden'

View File

@ -301,6 +301,11 @@ services:
parent: default_plugin_manager
plugin.cache_clearer:
class: Drupal\Core\Plugin\CachedDiscoveryClearer
paramconverter.menu_link:
class: Drupal\Core\ParamConverter\MenuLinkPluginConverter
tags:
- { name: paramconverter }
arguments: ['@plugin.manager.menu.link']
menu.tree_storage:
class: Drupal\Core\Menu\MenuTreeStorage
arguments: ['@database', '@cache.menu', 'menu_tree']

View File

@ -0,0 +1,215 @@
<?php
/**
* @file
* Contains \Drupal\Core\Menu\Form\MenuLinkDefaultForm.
*/
namespace Drupal\Core\Menu\Form;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Menu\MenuLinkInterface;
use Drupal\Core\Menu\MenuLinkManagerInterface;
use Drupal\Core\Menu\MenuParentFormSelectorInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\StringTranslation\TranslationInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides an edit form for static menu links.
*
* @see \Drupal\Core\Menu\MenuLinkDefault
*/
class MenuLinkDefaultForm implements MenuLinkFormInterface, ContainerInjectionInterface {
use StringTranslationTrait;
/**
* The edited menu link.
*
* @var \Drupal\Core\Menu\MenuLinkInterface
*/
protected $menuLink;
/**
* The menu link manager.
*
* @var \Drupal\Core\Menu\MenuLinkManagerInterface
*/
protected $menuLinkManager;
/**
* The parent form selector service.
*
* @var \Drupal\Core\Menu\MenuParentFormSelectorInterface
*/
protected $menuParentSelector;
/**
* The module handler service.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/**
* The module data from system_get_info().
*
* @var array
*/
protected $moduleData;
/**
* Constructs a new \Drupal\Core\Menu\Form\MenuLinkDefaultForm.
*
* @param \Drupal\Core\Menu\MenuLinkManagerInterface $menu_link_manager
* The menu link manager.
* @param \Drupal\Core\Menu\MenuParentFormSelectorInterface $menu_parent_selector
* The menu parent form selector service.
* @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
* The string translation.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler;
*/
public function __construct(MenuLinkManagerInterface $menu_link_manager, MenuParentFormSelectorInterface $menu_parent_selector, TranslationInterface $string_translation, ModuleHandlerInterface $module_handler) {
$this->menuLinkManager = $menu_link_manager;
$this->menuParentSelector = $menu_parent_selector;
$this->stringTranslation = $string_translation;
$this->moduleHandler = $module_handler;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('plugin.manager.menu.link'),
$container->get('menu.parent_form_selector'),
$container->get('string_translation'),
$container->get('module_handler')
);
}
/**
* {@inheritdoc}
*/
public function setMenuLinkInstance(MenuLinkInterface $menu_link) {
$this->menuLink = $menu_link;
}
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, array &$form_state) {
$form['#title'] = $this->t('Edit menu link %title', array('%title' => $this->menuLink->getTitle()));
$provider = $this->menuLink->getProvider();
$form['info'] = array(
'#type' => 'item',
'#title' => $this->t('This link is provided by the @name module. The title and path cannot be edited.', array('@name' => $this->getModuleName($provider))),
);
$link = array(
'#type' => 'link',
'#title' => $this->menuLink->getTitle(),
) + $this->menuLink->getUrlObject()->toRenderArray();
$form['path'] = array(
'link' => $link,
'#type' => 'item',
'#title' => $this->t('Link'),
);
$form['enabled'] = array(
'#type' => 'checkbox',
'#title' => $this->t('Enable menu link'),
'#description' => $this->t('Menu links that are not enabled will not be listed in any menu.'),
'#default_value' => !$this->menuLink->isHidden(),
);
$form['expanded'] = array(
'#type' => 'checkbox',
'#title' => t('Show as expanded'),
'#description' => $this->t('If selected and this menu link has children, the menu will always appear expanded.'),
'#default_value' => $this->menuLink->isExpanded(),
);
$menu_parent = $this->menuLink->getMenuName() . ':' . $this->menuLink->getParent();
$form['menu_parent'] = $this->menuParentSelector->parentSelectElement($menu_parent, $this->menuLink->getPluginId());
$form['menu_parent']['#title'] = $this->t('Parent link');
$form['menu_parent']['#description'] = $this->t('The maximum depth for a link and all its children is fixed. Some menu links may not be available as parents if selecting them would exceed this limit.');
$form['menu_parent']['#attributes']['class'][] = 'menu-title-select';
$delta = max(abs($this->menuLink->getWeight()), 50);
$form['weight'] = array(
'#type' => 'number',
'#min' => -$delta,
'#max' => $delta,
'#default_value' => $this->menuLink->getWeight(),
'#title' => $this->t('Weight'),
'#description' => $this->t('Link weight among links in the same menu at the same depth. In the menu, the links with high weight will sink and links with a low weight will be positioned nearer the top.'),
);
return $form;
}
/**
* {@inheritdoc}
*/
public function extractFormValues(array &$form, array &$form_state) {
$new_definition = array();
$new_definition['hidden'] = $form_state['values']['enabled'] ? 0 : 1;
$new_definition['weight'] = (int) $form_state['values']['weight'];
$new_definition['expanded'] = $form_state['values']['expanded'] ? 1 : 0;
list($menu_name, $parent) = explode(':', $form_state['values']['menu_parent'], 2);
if (!empty($menu_name)) {
$new_definition['menu_name'] = $menu_name;
}
if (isset($parent)) {
$new_definition['parent'] = $parent;
}
return $new_definition;
}
/**
* {@inheritdoc}
*/
public function validateConfigurationForm(array &$form, array &$form_state) {
}
/**
* {@inheritdoc}
*/
public function submitConfigurationForm(array &$form, array &$form_state) {
$new_definition = $this->extractFormValues($form, $form_state);
return $this->menuLinkManager->updateDefinition($this->menuLink->getPluginId(), $new_definition);
}
/**
* Gets the name of the module.
*
* @param string $module
* The machine name of a module.
*
* @todo This function is horrible, but core has nothing better until we add a
* a method to the ModuleHandler that handles this nicely.
* https://drupal.org/node/2281989
*
* @return string
* The human-readable, localized module name, or the machine name passed in
* if no matching module is found.
*/
protected function getModuleName($module) {
// Gather module data.
if (!isset($this->moduleData)) {
$this->moduleData = system_get_info('module');
}
// If the module exists, return its human-readable name.
if (isset($this->moduleData[$module])) {
return $this->t($this->moduleData[$module]['name']);
}
// Otherwise, return the machine name.
return $module;
}
}

View File

@ -0,0 +1,44 @@
<?php
/**
* @file
* Contains \Drupal\Core\Menu\Form\MenuLinkFormInterface.
*/
namespace Drupal\Core\Menu\Form;
use Drupal\Core\Menu\MenuLinkInterface;
use Drupal\Core\Plugin\PluginFormInterface;
/**
* Defines an interface for edit forms of menu links.
*
* All menu link plugins use the same interface for their configuration or
* editing form, but the implementations may differ.
*
* @see \Drupal\Core\Menu\MenuLinkInterface::getFormClass()
*/
interface MenuLinkFormInterface extends PluginFormInterface {
/**
* Injects the menu link plugin instance.
*
* @param \Drupal\Core\Menu\MenuLinkInterface $menu_link
* A menu link plugin instance.
*/
public function setMenuLinkInstance(MenuLinkInterface $menu_link);
/**
* Extracts a plugin definition from form values.
*
* @param array $form
* An associative array containing the structure of the form.
* @param array $form_state
* An associative array containing the current state of the form.
*
* @return array
* The new plugin definition values taken from the form values.
*/
public function extractFormValues(array &$form, array &$form_state);
}

View File

@ -0,0 +1,58 @@
<?php
/**
* @file
* Contains \Drupal\Core\ParamConverter\MenuLinkPluginConverter.
*/
namespace Drupal\Core\ParamConverter;
use Drupal\Core\Menu\MenuLinkManagerInterface;
use Drupal\Component\Plugin\Exception\PluginException;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Route;
/**
* Parameter converter for upcasting entity ids to full objects.
*/
class MenuLinkPluginConverter implements ParamConverterInterface {
/**
* Plugin manager which creates the instance from the value.
*
* @var \Drupal\Core\Menu\MenuLinkManagerInterface
*/
protected $menuLinkManager;
/**
* Constructs a new MenuLinkPluginConverter.
*
* @param \Drupal\Core\Menu\MenuLinkManagerInterface $menu_link_manager
* The menu link plugin manager.
*/
public function __construct(MenuLinkManagerInterface $menu_link_manager) {
$this->menuLinkManager = $menu_link_manager;
}
/**
* {@inheritdoc}
*/
public function convert($value, $definition, $name, array $defaults, Request $request) {
if ($value) {
try {
return $this->menuLinkManager->createInstance($value);
}
catch (PluginException $e) {
// Suppress the error.
}
}
}
/**
* {@inheritdoc}
*/
public function applies($definition, $name, Route $route) {
return (!empty($definition['type']) && $definition['type'] === 'menu_link_plugin');
}
}

View File

@ -0,0 +1,4 @@
menu_link_content.link_edit:
route_name: menu_link_content.link_edit
base_route: menu_link_content.link_edit
title: Edit

View File

@ -0,0 +1,23 @@
menu_link_content.link_add:
path: '/admin/structure/menu/manage/{menu}/add'
defaults:
_content: '\Drupal\menu_link_content\Controller\MenuController::addLink'
_title: 'Add menu link'
requirements:
_entity_create_access: 'menu_link_content'
menu_link_content.link_edit:
path: '/admin/structure/menu/item/{menu_link_content}/edit'
defaults:
_entity_form: 'menu_link_content.default'
_title: 'Edit menu link'
requirements:
_entity_access: 'menu_link_content.update'
menu_link_content.link_delete:
path: '/admin/structure/menu/item/{menu_link_content}/delete'
defaults:
_entity_form: 'menu_link_content.delete'
_title: 'Delete menu link'
requirements:
_entity_access: 'menu_link_content.delete'

View File

@ -0,0 +1,37 @@
<?php
/**
* @file
* Contains \Drupal\menu_link_content\Controller\MenuController.
*/
namespace Drupal\menu_link_content\Controller;
use Drupal\Core\Controller\ControllerBase;
use Drupal\system\MenuInterface;
/**
* Defines a route controller for a form for menu link content entity creation.
*/
class MenuController extends ControllerBase {
/**
* Provides the menu link creation form.
*
* @param \Drupal\system\MenuInterface $menu
* An entity representing a custom menu.
*
* @return array
* Returns the menu link creation form.
*/
public function addLink(MenuInterface $menu) {
$menu_link = $this->entityManager()->getStorage('menu_link_content')->create(array(
'id' => '',
'parent' => '',
'menu_name' => $menu->id(),
'bundle' => 'menu_link_content',
));
return $this->entityFormBuilder()->getForm($menu_link);
}
}

View File

@ -41,6 +41,7 @@ use Drupal\Core\Url;
* links = {
* "canonical" = "menu_link_content.link_edit",
* "edit-form" = "menu_link_content.link_edit",
* "delete-form" = "menu_link_content.link_delete",
* }
* )
*/
@ -336,30 +337,9 @@ class MenuLinkContent extends ContentEntityBase implements MenuLinkContentInterf
->setDescription(t('A flag to indicate if the link points to a full URL starting with a protocol, like http:// (1 = external, 0 = internal).'))
->setSetting('default_value', FALSE);
// The form widget doesn't work yet for core fields, so we skip the
// for display and manually create form elements for the boolean fields.
// @see https://drupal.org/node/2226493
// @see https://drupal.org/node/2150511
$fields['expanded'] = FieldDefinition::create('boolean')
->setLabel(t('Expanded'))
->setDescription(t('Flag for whether this link should be rendered as expanded in menus - expanded links always have their child links displayed, instead of only when the link is in the active trail (1 = expanded, 0 = not expanded).'))
->setSetting('default_value', FALSE)
->setDisplayOptions('view', array(
'label' => 'hidden',
'type' => 'boolean',
'weight' => 0,
));
// We manually create a form element for this, since the form logic is
// is inverted to show enabled.
$fields['hidden'] = FieldDefinition::create('boolean')
->setLabel(t('Hidden'))
->setDescription(t('A flag for whether the link should be hidden in menus or rendered normally.'))
->setSetting('default_value', FALSE);
$fields['weight'] = FieldDefinition::create('integer')
->setLabel(t('Weight'))
->setDescription(t('Link weight among links in the same menu at the same depth.'))
->setDescription(t('Link weight among links in the same menu at the same depth. In the menu, the links with high weight will sink and links with a low weight will be positioned nearer the top.'))
->setSetting('default_value', 0)
->setDisplayOptions('view', array(
'label' => 'hidden',
@ -368,9 +348,31 @@ class MenuLinkContent extends ContentEntityBase implements MenuLinkContentInterf
))
->setDisplayOptions('form', array(
'type' => 'integer',
'weight' => 20,
));
$fields['expanded'] = FieldDefinition::create('boolean')
->setLabel(t('Show as expanded'))
->setDescription(t('If selected and this menu link has children, the menu will always appear expanded.'))
->setSetting('default_value', FALSE)
->setDisplayOptions('view', array(
'label' => 'hidden',
'type' => 'boolean',
'weight' => 0,
))
->setDisplayOptions('form', array(
'settings' => array('display_label' => TRUE),
'weight' => 0,
));
// @todo We manually create a form element for this, since the form logic is
// is inverted to show enabled. Flip this to a status field and use the
// normal entity Boolean widget. https://www.drupal.org/node/2305707
$fields['hidden'] = FieldDefinition::create('boolean')
->setLabel(t('Hidden'))
->setDescription(t('A flag for whether the link should be hidden in menus or rendered normally.'))
->setSetting('default_value', FALSE);
$fields['langcode'] = FieldDefinition::create('language')
->setLabel(t('Language code'))
->setDescription(t('The node language code.'));

View File

@ -0,0 +1,78 @@
<?php
/**
* @file
* Contains \Drupal\menu_link_content\Form\MenuLinkContentDeleteForm.
*/
namespace Drupal\menu_link_content\Form;
use Drupal\Core\Entity\ContentEntityConfirmFormBase;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Drupal\Core\Url;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides a delete form for content menu links.
*/
class MenuLinkContentDeleteForm extends ContentEntityConfirmFormBase {
/**
* Logger channel.
*
* @var \Drupal\Core\Logger\LoggerChannelInterface
*/
protected $logger;
/**
* Constructs a MenuLinkContentDeleteForm object.
*
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
* The entity manager.
* @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $logger_factory
* The logger channel factory.
*/
public function __construct(EntityManagerInterface $entity_manager, LoggerChannelFactoryInterface $logger_factory) {
parent::__construct($entity_manager);
$this->logger = $logger_factory->get('menu');
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('entity.manager'),
$container->get('logger.factory')
);
}
/**
* {@inheritdoc}
*/
public function getQuestion() {
return $this->t('Are you sure you want to delete the custom menu link %item?', array('%item' => $this->entity->getTitle()));
}
/**
* {@inheritdoc}
*/
public function getCancelRoute() {
return new Url('menu_ui.menu_edit', array('menu' => $this->entity->getMenuName()));
}
/**
* {@inheritdoc}
*/
public function submit(array $form, array &$form_state) {
$t_args = array('%title' => $this->entity->getTitle());
$this->entity->delete();
drupal_set_message($this->t('The menu link %title has been deleted.', $t_args));
$this->logger->notice('Deleted menu link %title.', $t_args);
$form_state['redirect_route'] = array(
'route_name' => '<front>',
);
}
}

View File

@ -0,0 +1,440 @@
<?php
/**
* @file
* Contains \Drupal\menu_link_content\Form\MenuLinkContentForm.
*/
namespace Drupal\menu_link_content\Form;
use Drupal\Component\Utility\UrlHelper;
use Drupal\Core\Access\AccessManager;
use Drupal\Core\Entity\ContentEntityForm;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Language\Language;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Menu\Form\MenuLinkFormInterface;
use Drupal\Core\Menu\MenuLinkInterface;
use Drupal\Core\Menu\MenuParentFormSelectorInterface;
use Drupal\Core\ParamConverter\ParamNotConvertedException;
use Drupal\Core\Path\AliasManagerInterface;
use Drupal\Core\Routing\MatchingRouteNotFoundException;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Url;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Routing\RequestContext;
/**
* Provides a form to add/update content menu links.
*
* Note: This is not only a content entity form, but also implements the
* MenuLinkFormInterface, so the same class can be used in places expecting a
* generic menu link plugin configuration form.
*/
class MenuLinkContentForm extends ContentEntityForm implements MenuLinkFormInterface {
/**
* The content menu link.
*
* @var \Drupal\menu_link_content\Entity\MenuLinkContentInterface
*/
protected $entity;
/**
* The parent form selector service.
*
* @var \Drupal\Core\Menu\MenuParentFormSelectorInterface
*/
protected $menuParentSelector;
/**
* The request context.
*
* @var \Symfony\Component\Routing\RequestContext
*/
protected $requestContext;
/**
* The language manager.
*
* @var \Drupal\Core\Language\LanguageManagerInterface
*/
protected $languageManager;
/**
* The access manager.
*
* @var \Drupal\Core\Access\AccessManager
*/
protected $accessManager;
/**
* The current user.
*
* @var \Drupal\Core\Session\AccountInterface
*/
protected $account;
/**
* Constructs a MenuLinkContentForm object.
*
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
* The entity manager.
* @param \Drupal\Core\Menu\MenuParentFormSelectorInterface $menu_parent_selector
* The menu parent form selector service.
* @param \Drupal\Core\Path\AliasManagerInterface $alias_manager
* The alias manager.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler;
* @param \Symfony\Component\Routing\RequestContext $request_context
* The request context.
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
* The language manager.
* @param \Drupal\Core\Access\AccessManager $access_manager
* The access manager.
* @param \Drupal\Core\Session\AccountInterface $account
* The current user.
*/
public function __construct(EntityManagerInterface $entity_manager, MenuParentFormSelectorInterface $menu_parent_selector, AliasManagerInterface $alias_manager, ModuleHandlerInterface $module_handler, RequestContext $request_context, LanguageManagerInterface $language_manager, AccessManager $access_manager, AccountInterface $account) {
parent::__construct($entity_manager, $language_manager);
$this->menuParentSelector = $menu_parent_selector;
$this->pathAliasManager = $alias_manager;
$this->moduleHandler = $module_handler;
$this->requestContext = $request_context;
$this->languageManager = $language_manager;
$this->accessManager = $access_manager;
$this->account = $account;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('entity.manager'),
$container->get('menu.parent_form_selector'),
$container->get('path.alias_manager'),
$container->get('module_handler'),
$container->get('router.request_context'),
$container->get('language_manager'),
$container->get('access_manager'),
$container->get('current_user')
);
}
/**
* {@inheritdoc}
*/
public function setMenuLinkInstance(MenuLinkInterface $menu_link) {
// Load the entity for the entity form. Loading by entity ID is much faster
// than loading by UUID, so use that ID if we have it.
$metadata = $menu_link->getMetaData();
if (!empty($metadata['entity_id'])) {
$this->entity = $this->entityManager->getStorage('menu_link_content')->load($metadata['entity_id']);
}
else {
// Fallback to the loading by UUID.
$links = $this->entityManager->getStorage('menu_link_content')->loadByProperties(array('uuid' => $menu_link->getDerivativeId()));
$this->entity = reset($links);
}
}
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, array &$form_state) {
return $this->buildEditForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function buildEditForm(array &$form, array &$form_state) {
$this->setOperation('default');
$this->init($form_state);
return $this->form($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function validateConfigurationForm(array &$form, array &$form_state) {
$this->doValidate($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function validateEditForm(array &$form, array &$form_state) {
$this->doValidate($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function submitConfigurationForm(array &$form, array &$form_state) {
return $this->submitEditForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function submitEditForm(array &$form, array &$form_state) {
// Remove button and internal Form API values from submitted values.
form_state_values_clean($form_state);
$this->entity = $this->buildEntity($form, $form_state);
$this->entity->save();
return $form_state;
}
/**
* Breaks up a user-entered URL or path into all the relevant parts.
*
* @param string $url
* The user-entered URL or path.
*
* @return array
* The extracted parts.
*/
protected function extractUrl($url) {
$extracted = UrlHelper::parse($url);
$external = UrlHelper::isExternal($url);
if ($external) {
$extracted['url'] = $extracted['path'];
$extracted['route_name'] = NULL;
$extracted['route_parameters'] = array();
}
else {
$extracted['url'] = '';
// If the path doesn't match a Drupal path, the route should end up empty.
$extracted['route_name'] = NULL;
$extracted['route_parameters'] = array();
try {
// Find the route_name.
$normal_path = $this->pathAliasManager->getPathByAlias($extracted['path']);
$url_obj = Url::createFromPath($normal_path);
$extracted['route_name'] = $url_obj->getRouteName();
$extracted['route_parameters'] = $url_obj->getRouteParameters();
}
catch (MatchingRouteNotFoundException $e) {
// The path doesn't match a Drupal path.
}
catch (ParamNotConvertedException $e) {
// A path like node/99 matched a route, but the route parameter was
// invalid (e.g. node with ID 99 does not exist).
}
}
return $extracted;
}
/**
* {@inheritdoc}
*/
public function extractFormValues(array &$form, array &$form_state) {
$new_definition = array();
$new_definition['expanded'] = !empty($form_state['values']['expanded']) ? 1 : 0;
$new_definition['hidden'] = empty($form_state['values']['enabled']) ? 1 : 0;
list($menu_name, $parent) = explode(':', $form_state['values']['menu_parent'], 2);
if (!empty($menu_name)) {
$new_definition['menu_name'] = $menu_name;
}
$new_definition['parent'] = isset($parent) ? $parent : '';
$extracted = $this->extractUrl($form_state['values']['url']);
$new_definition['url'] = $extracted['url'];
$new_definition['route_name'] = $extracted['route_name'];
$new_definition['route_parameters'] = $extracted['route_parameters'];
$new_definition['options'] = array();
if ($extracted['query']) {
$new_definition['options']['query'] = $extracted['query'];
}
if ($extracted['fragment']) {
$new_definition['options']['fragment'] = $extracted['fragment'];
}
$new_definition['title'] = $form_state['values']['title'][0]['value'];
$new_definition['description'] = $form_state['values']['description'][0]['value'];
$new_definition['weight'] = (int) $form_state['values']['weight'][0]['value'];
return $new_definition;
}
/**
* {@inheritdoc}
*/
public function form(array $form, array &$form_state) {
$form = parent::form($form, $form_state);
// We always show the internal path here.
/** @var \Drupal\Core\Url $url */
$url = $this->getEntity()->getUrlObject();
if ($url->isExternal()) {
$default_value = $url->toString();
}
elseif ($url->getRouteName() == '<front>') {
// The default route for new entities is <front>, but we just want an
// empty form field.
$default_value = $this->getEntity()->isNew() ? '' : '<front>';
}
else {
// @todo Url::getInternalPath() calls UrlGenerator::getPathFromRoute()
// which need a replacement since it is deprecated.
// https://www.drupal.org/node/2307061
$default_value = $url->getInternalPath();
// @todo Add a helper method to Url to render just the query string and
// fragment. https://www.drupal.org/node/2305013
$options = $url->getOptions();
if (isset($options['query'])) {
$default_value .= $options['query'] ? ('?' . UrlHelper::buildQuery($options['query'])) : '';
}
if (isset($options['fragment']) && $options['fragment'] !== '') {
$default_value .= '#' . $options['fragment'];
}
}
$form['url'] = array(
'#title' => $this->t('Link path'),
'#type' => 'textfield',
'#description' => $this->t('The path for this menu link. This can be an internal Drupal path such as %add-node or an external URL such as %drupal. Enter %front to link to the front page.', array('%front' => '<front>', '%add-node' => 'node/add', '%drupal' => 'http://drupal.org')),
'#default_value' => $default_value,
'#required' => TRUE,
'#weight' => -2,
);
$language_configuration = $this->moduleHandler->invoke('language', 'get_default_configuration', array('menu_link_content', 'menu_link_content'));
if ($this->entity->isNew()) {
$default_language = isset($language_configuration['langcode']) ? $language_configuration['langcode'] : $this->languageManager->getDefaultLanguage()->getId();
}
else {
$default_language = $this->entity->getUntranslated()->language()->getId();
}
$form['langcode'] = array(
'#title' => t('Language'),
'#type' => 'language_select',
'#default_value' => $default_language,
'#languages' => Language::STATE_ALL,
'#access' => !empty($language_configuration['language_show']),
);
$form['enabled'] = array(
'#type' => 'checkbox',
'#title' => $this->t('Enable menu link'),
'#description' => $this->t('Menu links that are not enabled will not be listed in any menu.'),
'#default_value' => !$this->entity->isHidden(),
'#weight' => 0,
);
$default = $this->entity->getMenuName() . ':' . $this->entity->getParentId();
$form['menu_parent'] = $this->menuParentSelector->parentSelectElement($default, $this->entity->getPluginId());
$form['menu_parent']['#weight'] = 10;
$form['menu_parent']['#title'] = $this->t('Parent link');
$form['menu_parent']['#description'] = $this->t('The maximum depth for a link and all its children is fixed. Some menu links may not be available as parents if selecting them would exceed this limit.');
$form['menu_parent']['#attributes']['class'][] = 'menu-title-select';
return $form;
}
/**
* {@inheritdoc}
*/
protected function actions(array $form, array &$form_state) {
$element = parent::actions($form, $form_state);
$element['submit']['#button_type'] = 'primary';
$element['delete']['#access'] = $this->entity->access('delete');
return $element;
}
/**
* {@inheritdoc}
*/
public function validate(array $form, array &$form_state) {
$this->doValidate($form, $form_state);
parent::validate($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function buildEntity(array $form, array &$form_state) {
/** @var \Drupal\menu_link_content\Entity\MenuLinkContentInterface $entity */
$entity = parent::buildEntity($form, $form_state);
$new_definition = $this->extractFormValues($form, $form_state);
$entity->parent->value = $new_definition['parent'];
$entity->menu_name->value = $new_definition['menu_name'];
$entity->hidden->value = (bool) $new_definition['hidden'];
$entity->expanded->value = $new_definition['expanded'];
$entity->url->value = $new_definition['url'];
$entity->route_name->value = $new_definition['route_name'];
$entity->setRouteParameters($new_definition['route_parameters']);
$entity->setOptions($new_definition['options']);
return $entity;
}
/**
* {@inheritdoc}
*/
public function save(array $form, array &$form_state) {
// The entity is rebuilt in parent::submit().
$menu_link = $this->entity;
$saved = $menu_link->save();
if ($saved) {
drupal_set_message($this->t('The menu link has been saved.'));
$form_state['redirect_route'] = array(
'route_name' => 'menu_link_content.link_edit',
'route_parameters' => array(
'menu_link_content' => $menu_link->id(),
),
);
}
else {
drupal_set_message($this->t('There was an error saving the menu link.'), 'error');
$form_state['rebuild'] = TRUE;
}
}
/**
* Validates the form, both on the menu link edit and content menu link form.
*
* $form is not currently used, but passed here to match the normal form
* validation method signature.
*
* @param array $form
* A nested array form elements comprising the form.
* @param array $form_state
* An associative array containing the current state of the form.
*/
protected function doValidate(array $form, array &$form_state) {
$extracted = $this->extractUrl($form_state['values']['url']);
// If both URL and route_name are empty, the entered value is not valid.
$valid = FALSE;
if ($extracted['url']) {
// This is an external link.
$valid = TRUE;
}
elseif ($extracted['route_name']) {
// Users are not allowed to add a link to a page they cannot access.
$valid = $this->accessManager->checkNamedRoute($extracted['route_name'], $extracted['route_parameters'], $this->account);
}
if (!$valid) {
$this->setFormError('url', $form_state, $this->t("The path '@link_path' is either invalid or you do not have access to it.", array('@link_path' => $form_state['values']['url'])));
}
elseif ($extracted['route_name']) {
// The user entered a Drupal path.
$normal_path = $this->pathAliasManager->getPathByAlias($extracted['path']);
if ($extracted['path'] != $normal_path) {
drupal_set_message($this->t('The menu system stores system paths only, but will use the URL alias for display. %link_path has been stored as %normal_path', array(
'%link_path' => $extracted['path'],
'%normal_path' => $normal_path,
)));
}
}
}
}

View File

@ -0,0 +1,59 @@
<?php
/**
* @file
* Contains \Drupal\menu_link_content\Tests\MenuLinkContentUITest.
*/
namespace Drupal\menu_link_content\Tests;
use Drupal\content_translation\Tests\ContentTranslationUITest;
/**
* Tests the menu link content UI.
*
* @group Menu
*/
class MenuLinkContentUITest extends ContentTranslationUITest {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array(
'language',
'content_translation',
'menu_link_content',
'menu_ui',
);
/**
* {@inheritdoc}
*/
public function setUp() {
$this->entityTypeId = 'menu_link_content';
$this->bundle = 'menu_link_content';
$this->fieldName = 'title';
parent::setUp();
}
/**
* {@inheritdoc}
*/
protected function getTranslatorPermissions() {
return array_merge(parent::getTranslatorPermissions(), array('administer menu'));
}
/**
* {@inheritdoc}
*/
protected function createEntity($values, $langcode, $bundle_name = NULL) {
$values['menu_name'] = 'tools';
$values['route_name'] = 'menu_ui.overview_page';
$values['title'] = 'Test title';
return parent::createEntity($values, $langcode, $bundle_name);
}
}

View File

@ -0,0 +1,105 @@
<?php
/**
* @file
* Contains \Drupal\menu_ui\Form\MenuLinkEditForm.
*/
namespace Drupal\menu_ui\Form;
use Drupal\Core\DependencyInjection\ClassResolverInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Menu\MenuLinkInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Defines a generic edit form for all menu link plugin types.
*
* The menu link plugin defines which class defines the corresponding form.
*
* @see \Drupal\Core\Menu\MenuLinkInterface::getFormClass()
*/
class MenuLinkEditForm extends FormBase {
/**
* The class resolver
*
* @var \Drupal\Core\DependencyInjection\ClassResolverInterface
*/
protected $classResolver;
/**
* Constructs a MenuLinkEditForm object.
*
* @param \Drupal\Core\DependencyInjection\ClassResolverInterface $class_resolver
* The class resolver.
*/
public function __construct(ClassResolverInterface $class_resolver) {
$this->classResolver = $class_resolver;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('class_resolver')
);
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'menu_link_edit';
}
/**
* {@inheritdoc}
*
* @param \Drupal\Core\Menu\MenuLinkInterface $menu_link_plugin
* The plugin instance to use for this form.
*/
public function buildForm(array $form, array &$form_state, MenuLinkInterface $menu_link_plugin = NULL) {
$form['menu_link_id'] = array(
'#type' => 'value',
'#value' => $menu_link_plugin->getPluginId(),
);
$class_name = $menu_link_plugin->getFormClass();
$form['#plugin_form'] = $this->classResolver->getInstanceFromDefinition($class_name);
$form['#plugin_form']->setMenuLinkInstance($menu_link_plugin);
$form += $form['#plugin_form']->buildConfigurationForm($form, $form_state);
$form['actions'] = array('#type' => 'actions');
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => $this->t('Save'),
'#button_type' => 'primary',
);
return $form;
}
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, array &$form_state) {
$form['#plugin_form']->validateConfigurationForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, array &$form_state) {
$link = $form['#plugin_form']->submitConfigurationForm($form, $form_state);
drupal_set_message($this->t('The menu link has been saved.'));
$form_state['redirect_route'] = array(
'route_name' => 'menu_ui.menu_edit',
'route_parameters' => array(
'menu' => $link->getMenuName(),
),
);
}
}