Drupal 8.0.0 beta 12. More info: https://www.drupal.org/node/2514176
This commit is contained in:
commit
9921556621
13277 changed files with 1459781 additions and 0 deletions
|
@ -0,0 +1,8 @@
|
|||
name: 'Custom Menu Links'
|
||||
type: module
|
||||
description: 'Allows administrators to create custom menu links.'
|
||||
package: Core
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- link
|
18
core/modules/menu_link_content/menu_link_content.install
Normal file
18
core/modules/menu_link_content/menu_link_content.install
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Install, update and uninstall functions for the menu_link_content module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_install().
|
||||
*/
|
||||
function menu_link_content_install() {
|
||||
// Add a higher weight so that menu_link_content_path_update() is called after
|
||||
// system_path_update() clears the path alias cache.
|
||||
// @todo remove this when the cache clearing is moved to path module or if
|
||||
// caching is removed for path aliases due to
|
||||
// https://www.drupal.org/node/1965074
|
||||
module_set_weight('menu_link_content', 1);
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
menu_link_content:
|
||||
class: \Drupal\menu_link_content\Plugin\Menu\MenuLinkContent
|
||||
form_class: \Drupal\menu_link_content\Form\MenuLinkContentForm
|
||||
deriver: \Drupal\menu_link_content\Plugin\Deriver\MenuLinkContentDeriver
|
|
@ -0,0 +1,4 @@
|
|||
entity.menu_link_content.canonical:
|
||||
route_name: entity.menu_link_content.canonical
|
||||
base_route: entity.menu_link_content.canonical
|
||||
title: Edit
|
84
core/modules/menu_link_content/menu_link_content.module
Normal file
84
core/modules/menu_link_content/menu_link_content.module
Normal file
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Allows administrators to create custom menu links.
|
||||
*/
|
||||
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\system\MenuInterface;
|
||||
|
||||
/**
|
||||
* Implements hook_help().
|
||||
*/
|
||||
function menu_link_content_help($route_name, RouteMatchInterface $route_match) {
|
||||
switch ($route_name) {
|
||||
case 'help.page.menu_link_content':
|
||||
$output = '';
|
||||
$output .= '<h3>' . t('About') . '</h3>';
|
||||
$output .= '<p>' . t('The Custom Menu Links module allows users to create menu links. These links can be translated if multiple languages are used for the site.');
|
||||
if (\Drupal::moduleHandler()->moduleExists('menu_ui')) {
|
||||
$output .= ' ' . t('It is required by the Menu UI module, which provides an interface for managing menus and menu links. For more information, see the <a href="!menu-help">Menu UI module help page</a> and the <a href="!drupal-org-help">online documentation for the Custom Menu Links module</a>.', array('!menu-help' => \Drupal::url('help.page', array('name' => 'menu_ui')), '!drupal-org-help' => 'https://www.drupal.org/documentation/modules/menu_link'));
|
||||
}
|
||||
else {
|
||||
$output .= ' ' . t('For more information, see the <a href="!drupal-org-help">online documentation for the Custom Menu Links module</a>. If you enable the Menu UI module, it provides an interface for managing menus and menu links.', array('!drupal-org-help' => 'https://www.drupal.org/documentation/modules/menu_link'));
|
||||
}
|
||||
$output .= '</p>';
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_menu_delete().
|
||||
*/
|
||||
function menu_link_content_menu_delete(MenuInterface $menu) {
|
||||
$storage = \Drupal::entityManager()->getStorage('menu_link_content');
|
||||
$menu_links = $storage->loadByProperties(array('menu_name' => $menu->id()));
|
||||
$storage->delete($menu_links);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_path_insert().
|
||||
*/
|
||||
function menu_link_content_path_insert($path) {
|
||||
_menu_link_content_update_path_alias($path['alias']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to update plugin definition using internal scheme.
|
||||
*
|
||||
* @param string $path
|
||||
* The path alias.
|
||||
*
|
||||
*/
|
||||
function _menu_link_content_update_path_alias($path) {
|
||||
/** @var \Drupal\Core\Menu\MenuLinkManagerInterface $menu_link_manager */
|
||||
$menu_link_manager = \Drupal::service('plugin.manager.menu.link');
|
||||
/** @var \Drupal\menu_link_content\MenuLinkContentInterface[] $entities */
|
||||
$entities = \Drupal::entityManager()
|
||||
->getStorage('menu_link_content')
|
||||
->loadByProperties(['link.uri' => 'internal:' . $path]);
|
||||
foreach ($entities as $menu_link) {
|
||||
$menu_link_manager->updateDefinition($menu_link->getPluginId(), $menu_link->getPluginDefinition(), FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_path_update().
|
||||
*/
|
||||
function menu_link_content_path_update($path) {
|
||||
if ($path['alias'] != $path['original']['alias']) {
|
||||
_menu_link_content_update_path_alias($path['alias']);
|
||||
_menu_link_content_update_path_alias($path['original']['alias']);
|
||||
}
|
||||
elseif ($path['source'] != $path['original']['source']) {
|
||||
_menu_link_content_update_path_alias($path['alias']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_path_delete().
|
||||
*/
|
||||
function menu_link_content_path_delete($path) {
|
||||
_menu_link_content_update_path_alias($path['alias']);
|
||||
}
|
31
core/modules/menu_link_content/menu_link_content.routing.yml
Normal file
31
core/modules/menu_link_content/menu_link_content.routing.yml
Normal file
|
@ -0,0 +1,31 @@
|
|||
entity.menu.add_link_form:
|
||||
path: '/admin/structure/menu/manage/{menu}/add'
|
||||
defaults:
|
||||
_controller: '\Drupal\menu_link_content\Controller\MenuController::addLink'
|
||||
_title: 'Add menu link'
|
||||
requirements:
|
||||
_entity_create_access: 'menu_link_content'
|
||||
|
||||
entity.menu_link_content.canonical:
|
||||
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'
|
||||
|
||||
entity.menu_link_content.edit_form:
|
||||
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'
|
||||
|
||||
entity.menu_link_content.delete_form:
|
||||
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'
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
404
core/modules/menu_link_content/src/Entity/MenuLinkContent.php
Normal file
404
core/modules/menu_link_content/src/Entity/MenuLinkContent.php
Normal file
|
@ -0,0 +1,404 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\menu_link_content\Entity\MenuLinkContent.
|
||||
*/
|
||||
|
||||
namespace Drupal\menu_link_content\Entity;
|
||||
|
||||
use Drupal\Core\Entity\ContentEntityBase;
|
||||
use Drupal\Core\Entity\EntityChangedTrait;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Field\BaseFieldDefinition;
|
||||
use Drupal\link\LinkItemInterface;
|
||||
use Drupal\menu_link_content\MenuLinkContentInterface;
|
||||
|
||||
/**
|
||||
* Defines the menu link content entity class.
|
||||
*
|
||||
* @property \Drupal\link\LinkItemInterface link
|
||||
* @property \Drupal\Core\Field\FieldItemList rediscover
|
||||
*
|
||||
* @ContentEntityType(
|
||||
* id = "menu_link_content",
|
||||
* label = @Translation("Custom menu link"),
|
||||
* handlers = {
|
||||
* "storage" = "Drupal\Core\Entity\Sql\SqlContentEntityStorage",
|
||||
* "storage_schema" = "Drupal\menu_link_content\MenuLinkContentStorageSchema",
|
||||
* "access" = "Drupal\menu_link_content\MenuLinkContentAccessControlHandler",
|
||||
* "form" = {
|
||||
* "default" = "Drupal\menu_link_content\Form\MenuLinkContentForm",
|
||||
* "delete" = "Drupal\menu_link_content\Form\MenuLinkContentDeleteForm"
|
||||
* }
|
||||
* },
|
||||
* admin_permission = "administer menu",
|
||||
* base_table = "menu_link_content",
|
||||
* data_table = "menu_link_content_data",
|
||||
* translatable = TRUE,
|
||||
* entity_keys = {
|
||||
* "id" = "id",
|
||||
* "label" = "title",
|
||||
* "langcode" = "langcode",
|
||||
* "uuid" = "uuid",
|
||||
* "bundle" = "bundle"
|
||||
* },
|
||||
* links = {
|
||||
* "canonical" = "/admin/structure/menu/item/{menu_link_content}/edit",
|
||||
* "edit-form" = "/admin/structure/menu/item/{menu_link_content}/edit",
|
||||
* "delete-form" = "/admin/structure/menu/item/{menu_link_content}/delete",
|
||||
* }
|
||||
* )
|
||||
*/
|
||||
class MenuLinkContent extends ContentEntityBase implements MenuLinkContentInterface {
|
||||
|
||||
use EntityChangedTrait;
|
||||
|
||||
/**
|
||||
* A flag for whether this entity is wrapped in a plugin instance.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $insidePlugin = FALSE;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setInsidePlugin() {
|
||||
$this->insidePlugin = TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTitle() {
|
||||
return $this->get('title')->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getUrlObject() {
|
||||
return $this->link->first()->getUrl();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getMenuName() {
|
||||
return $this->get('menu_name')->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDescription() {
|
||||
return $this->get('description')->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPluginId() {
|
||||
return 'menu_link_content:' . $this->uuid();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isEnabled() {
|
||||
return (bool) $this->get('enabled')->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isExpanded() {
|
||||
return (bool) $this->get('expanded')->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getParentId() {
|
||||
return $this->get('parent')->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getWeight() {
|
||||
return (int) $this->get('weight')->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getChangedTime() {
|
||||
return $this->get('changed')->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPluginDefinition() {
|
||||
$definition = array();
|
||||
$definition['class'] = 'Drupal\menu_link_content\Plugin\Menu\MenuLinkContent';
|
||||
$definition['menu_name'] = $this->getMenuName();
|
||||
|
||||
if ($url_object = $this->getUrlObject()) {
|
||||
$definition['url'] = NULL;
|
||||
$definition['route_name'] = NULL;
|
||||
$definition['route_parameters'] = [];
|
||||
if (!$url_object->isRouted()) {
|
||||
$definition['url'] = $url_object->getUri();
|
||||
}
|
||||
else {
|
||||
$definition['route_name'] = $url_object->getRouteName();
|
||||
$definition['route_parameters'] = $url_object->getRouteParameters();
|
||||
}
|
||||
$definition['options'] = $url_object->getOptions();
|
||||
}
|
||||
|
||||
$definition['title'] = $this->getTitle();
|
||||
$definition['description'] = $this->getDescription();
|
||||
$definition['weight'] = $this->getWeight();
|
||||
$definition['id'] = $this->getPluginId();
|
||||
$definition['metadata'] = array('entity_id' => $this->id());
|
||||
$definition['form_class'] = '\Drupal\menu_link_content\Form\MenuLinkContentForm';
|
||||
$definition['enabled'] = $this->isEnabled() ? 1 : 0;
|
||||
$definition['expanded'] = $this->isExpanded() ? 1 : 0;
|
||||
$definition['provider'] = 'menu_link_content';
|
||||
$definition['discovered'] = 0;
|
||||
$definition['parent'] = $this->getParentId();
|
||||
|
||||
return $definition;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function preCreate(EntityStorageInterface $storage, array &$values) {
|
||||
$values += ['bundle' => 'menu_link_content'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function preSave(EntityStorageInterface $storage) {
|
||||
parent::preSave($storage);
|
||||
|
||||
if (parse_url($this->link->uri, PHP_URL_SCHEME) === 'internal') {
|
||||
$this->setRequiresRediscovery(TRUE);
|
||||
}
|
||||
else {
|
||||
$this->setRequiresRediscovery(FALSE);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function postSave(EntityStorageInterface $storage, $update = TRUE) {
|
||||
parent::postSave($storage, $update);
|
||||
|
||||
/** @var \Drupal\Core\Menu\MenuLinkManagerInterface $menu_link_manager */
|
||||
$menu_link_manager = \Drupal::service('plugin.manager.menu.link');
|
||||
|
||||
// The menu link can just be updated if there is already an menu link entry
|
||||
// on both entity and menu link plugin level.
|
||||
if ($update && $menu_link_manager->getDefinition($this->getPluginId())) {
|
||||
// When the entity is saved via a plugin instance, we should not call
|
||||
// the menu tree manager to update the definition a second time.
|
||||
if (!$this->insidePlugin) {
|
||||
$menu_link_manager->updateDefinition($this->getPluginId(), $this->getPluginDefinition(), FALSE);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$menu_link_manager->addDefinition($this->getPluginId(), $this->getPluginDefinition());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function preDelete(EntityStorageInterface $storage, array $entities) {
|
||||
parent::preDelete($storage, $entities);
|
||||
|
||||
/** @var \Drupal\Core\Menu\MenuLinkManagerInterface $menu_link_manager */
|
||||
$menu_link_manager = \Drupal::service('plugin.manager.menu.link');
|
||||
|
||||
foreach ($entities as $menu_link) {
|
||||
/** @var \Drupal\menu_link_content\Entity\MenuLinkContent $menu_link */
|
||||
$menu_link_manager->removeDefinition($menu_link->getPluginId(), FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
|
||||
$fields['id'] = BaseFieldDefinition::create('integer')
|
||||
->setLabel(t('Entity ID'))
|
||||
->setDescription(t('The entity ID for this menu link content entity.'))
|
||||
->setReadOnly(TRUE)
|
||||
->setSetting('unsigned', TRUE);
|
||||
|
||||
$fields['uuid'] = BaseFieldDefinition::create('uuid')
|
||||
->setLabel(t('UUID'))
|
||||
->setDescription(t('The content menu link UUID.'))
|
||||
->setReadOnly(TRUE);
|
||||
|
||||
$fields['bundle'] = BaseFieldDefinition::create('string')
|
||||
->setLabel(t('Bundle'))
|
||||
->setDescription(t('The content menu link bundle.'))
|
||||
->setSetting('max_length', EntityTypeInterface::BUNDLE_MAX_LENGTH)
|
||||
->setSetting('is_ascii', TRUE)
|
||||
->setReadOnly(TRUE);
|
||||
|
||||
$fields['title'] = BaseFieldDefinition::create('string')
|
||||
->setLabel(t('Menu link title'))
|
||||
->setDescription(t('The text to be used for this link in the menu.'))
|
||||
->setRequired(TRUE)
|
||||
->setTranslatable(TRUE)
|
||||
->setSettings(array(
|
||||
'max_length' => 255,
|
||||
))
|
||||
->setDisplayOptions('view', array(
|
||||
'label' => 'hidden',
|
||||
'type' => 'string',
|
||||
'weight' => -5,
|
||||
))
|
||||
->setDisplayOptions('form', array(
|
||||
'type' => 'string_textfield',
|
||||
'weight' => -5,
|
||||
))
|
||||
->setDisplayConfigurable('form', TRUE);
|
||||
|
||||
$fields['description'] = BaseFieldDefinition::create('string')
|
||||
->setLabel(t('Description'))
|
||||
->setDescription(t('Shown when hovering over the menu link.'))
|
||||
->setTranslatable(TRUE)
|
||||
->setSettings(array(
|
||||
'max_length' => 255,
|
||||
))
|
||||
->setDisplayOptions('view', array(
|
||||
'label' => 'hidden',
|
||||
'type' => 'string',
|
||||
'weight' => 0,
|
||||
))
|
||||
->setDisplayOptions('form', array(
|
||||
'type' => 'string_textfield',
|
||||
'weight' => 0,
|
||||
));
|
||||
|
||||
$fields['menu_name'] = BaseFieldDefinition::create('string')
|
||||
->setLabel(t('Menu name'))
|
||||
->setDescription(t('The menu name. All links with the same menu name (such as "tools") are part of the same menu.'))
|
||||
->setDefaultValue('tools')
|
||||
->setSetting('is_ascii', TRUE);
|
||||
|
||||
$fields['link'] = BaseFieldDefinition::create('link')
|
||||
->setLabel(t('Link'))
|
||||
->setDescription(t('The location this menu link points to.'))
|
||||
->setRequired(TRUE)
|
||||
->setSettings(array(
|
||||
'link_type' => LinkItemInterface::LINK_GENERIC,
|
||||
'title' => DRUPAL_DISABLED,
|
||||
))
|
||||
->setDisplayOptions('form', array(
|
||||
'type' => 'link_default',
|
||||
'weight' => -2,
|
||||
));
|
||||
|
||||
$fields['external'] = BaseFieldDefinition::create('boolean')
|
||||
->setLabel(t('External'))
|
||||
->setDescription(t('A flag to indicate if the link points to a full URL starting with a protocol, like http:// (1 = external, 0 = internal).'))
|
||||
->setDefaultValue(FALSE);
|
||||
|
||||
$fields['rediscover'] = BaseFieldDefinition::create('boolean')
|
||||
->setLabel(t('Indicates whether the menu link should be rediscovered'))
|
||||
->setDefaultValue(FALSE);
|
||||
|
||||
$fields['weight'] = BaseFieldDefinition::create('integer')
|
||||
->setLabel(t('Weight'))
|
||||
->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.'))
|
||||
->setDefaultValue(0)
|
||||
->setDisplayOptions('view', array(
|
||||
'label' => 'hidden',
|
||||
'type' => 'integer',
|
||||
'weight' => 0,
|
||||
))
|
||||
->setDisplayOptions('form', array(
|
||||
'type' => 'number',
|
||||
'weight' => 20,
|
||||
));
|
||||
|
||||
$fields['expanded'] = BaseFieldDefinition::create('boolean')
|
||||
->setLabel(t('Show as expanded'))
|
||||
->setDescription(t('If selected and this menu link has children, the menu will always appear expanded.'))
|
||||
->setDefaultValue(FALSE)
|
||||
->setDisplayOptions('view', array(
|
||||
'label' => 'hidden',
|
||||
'type' => 'boolean',
|
||||
'weight' => 0,
|
||||
))
|
||||
->setDisplayOptions('form', array(
|
||||
'settings' => array('display_label' => TRUE),
|
||||
'weight' => 0,
|
||||
));
|
||||
|
||||
$fields['enabled'] = BaseFieldDefinition::create('boolean')
|
||||
->setLabel(t('Enabled'))
|
||||
->setDescription(t('A flag for whether the link should be enabled in menus or hidden.'))
|
||||
->setDefaultValue(TRUE)
|
||||
->setDisplayOptions('view', array(
|
||||
'label' => 'hidden',
|
||||
'type' => 'boolean',
|
||||
'weight' => 0,
|
||||
))
|
||||
->setDisplayOptions('form', array(
|
||||
'settings' => array('display_label' => TRUE),
|
||||
'weight' => -1,
|
||||
));
|
||||
|
||||
$fields['langcode'] = BaseFieldDefinition::create('language')
|
||||
->setLabel(t('Language'))
|
||||
->setDescription(t('The menu link language code.'))
|
||||
->setTranslatable(TRUE)
|
||||
->setDisplayOptions('view', array(
|
||||
'type' => 'hidden',
|
||||
))
|
||||
->setDisplayOptions('form', array(
|
||||
'type' => 'language_select',
|
||||
'weight' => 2,
|
||||
));
|
||||
|
||||
$fields['parent'] = BaseFieldDefinition::create('string')
|
||||
->setLabel(t('Parent plugin ID'))
|
||||
->setDescription(t('The ID of the parent menu link plugin, or empty string when at the top level of the hierarchy.'));
|
||||
|
||||
$fields['changed'] = BaseFieldDefinition::create('changed')
|
||||
->setLabel(t('Changed'))
|
||||
->setDescription(t('The time that the menu link was last edited.'))
|
||||
->setTranslatable(TRUE);
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function requiresRediscovery() {
|
||||
return $this->get('rediscover')->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setRequiresRediscovery($rediscovery) {
|
||||
$this->set('rediscover', $rediscovery);
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\menu_link_content\Form\MenuLinkContentDeleteForm.
|
||||
*/
|
||||
|
||||
namespace Drupal\menu_link_content\Form;
|
||||
|
||||
use Drupal\Core\Entity\ContentEntityDeleteForm;
|
||||
use Drupal\Core\Url;
|
||||
|
||||
/**
|
||||
* Provides a delete form for content menu links.
|
||||
*/
|
||||
class MenuLinkContentDeleteForm extends ContentEntityDeleteForm {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCancelUrl() {
|
||||
return new Url('entity.menu.edit_form', array('menu' => $this->entity->getMenuName()));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getRedirectUrl() {
|
||||
return $this->getCancelUrl();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getDeletionMessage() {
|
||||
return $this->t('The menu link %title has been deleted.', array('%title' => $this->entity->label()));
|
||||
}
|
||||
|
||||
}
|
140
core/modules/menu_link_content/src/Form/MenuLinkContentForm.php
Normal file
140
core/modules/menu_link_content/src/Form/MenuLinkContentForm.php
Normal file
|
@ -0,0 +1,140 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\menu_link_content\Form\MenuLinkContentForm.
|
||||
*/
|
||||
|
||||
namespace Drupal\menu_link_content\Form;
|
||||
|
||||
use Drupal\Core\Entity\ContentEntityForm;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Language\LanguageManagerInterface;
|
||||
use Drupal\Core\Menu\MenuParentFormSelectorInterface;
|
||||
use Drupal\Core\Path\PathValidatorInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides a form to add/update content menu links.
|
||||
*/
|
||||
class MenuLinkContentForm extends ContentEntityForm {
|
||||
|
||||
/**
|
||||
* The content menu link.
|
||||
*
|
||||
* @var \Drupal\menu_link_content\MenuLinkContentInterface
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* The parent form selector service.
|
||||
*
|
||||
* @var \Drupal\Core\Menu\MenuParentFormSelectorInterface
|
||||
*/
|
||||
protected $menuParentSelector;
|
||||
|
||||
/**
|
||||
* The path validator.
|
||||
*
|
||||
* @var \Drupal\Core\Path\PathValidatorInterface
|
||||
*/
|
||||
protected $pathValidator;
|
||||
|
||||
/**
|
||||
* 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\Language\LanguageManagerInterface $language_manager
|
||||
* The language manager.
|
||||
* @param \Drupal\Core\Path\PathValidatorInterface $path_validator
|
||||
* The path validator.
|
||||
*/
|
||||
public function __construct(EntityManagerInterface $entity_manager, MenuParentFormSelectorInterface $menu_parent_selector, LanguageManagerInterface $language_manager, PathValidatorInterface $path_validator) {
|
||||
parent::__construct($entity_manager, $language_manager);
|
||||
$this->menuParentSelector = $menu_parent_selector;
|
||||
$this->pathValidator = $path_validator;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('entity.manager'),
|
||||
$container->get('menu.parent_form_selector'),
|
||||
$container->get('language_manager'),
|
||||
$container->get('path.validator')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function form(array $form, FormStateInterface $form_state) {
|
||||
$form = parent::form($form, $form_state);
|
||||
|
||||
$default = $this->entity->getMenuName() . ':' . $this->entity->getParentId();
|
||||
$id = $this->entity->isNew() ? '' : $this->entity->getPluginId();
|
||||
$form['menu_parent'] = $this->menuParentSelector->parentSelectElement($default, $id);
|
||||
$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, FormStateInterface $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 buildEntity(array $form, FormStateInterface $form_state) {
|
||||
/** @var \Drupal\menu_link_content\MenuLinkContentInterface $entity */
|
||||
$entity = parent::buildEntity($form, $form_state);
|
||||
|
||||
list($menu_name, $parent) = explode(':', $form_state->getValue('menu_parent'), 2);
|
||||
|
||||
$entity->parent->value = $parent;
|
||||
$entity->menu_name->value = $menu_name;
|
||||
$entity->enabled->value = (!$form_state->isValueEmpty(array('enabled', 'value')));
|
||||
$entity->expanded->value = (!$form_state->isValueEmpty(array('expanded', 'value')));
|
||||
|
||||
return $entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save(array $form, FormStateInterface $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->setRedirect(
|
||||
'entity.menu_link_content.canonical',
|
||||
array('menu_link_content' => $menu_link->id())
|
||||
);
|
||||
}
|
||||
else {
|
||||
drupal_set_message($this->t('There was an error saving the menu link.'), 'error');
|
||||
$form_state->setRebuild();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\menu_link_content\MenuLinkContentAccessControlHandler.
|
||||
*/
|
||||
|
||||
namespace Drupal\menu_link_content;
|
||||
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Access\AccessManagerInterface;
|
||||
use Drupal\Core\Entity\EntityAccessControlHandler;
|
||||
use Drupal\Core\Entity\EntityHandlerInterface;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Defines the access control handler for the user entity type.
|
||||
*/
|
||||
class MenuLinkContentAccessControlHandler extends EntityAccessControlHandler implements EntityHandlerInterface {
|
||||
|
||||
/**
|
||||
* The access manager to check routes by name.
|
||||
*
|
||||
* @var \Drupal\Core\Access\AccessManagerInterface
|
||||
*/
|
||||
protected $accessManager;
|
||||
|
||||
/**
|
||||
* Creates a new MenuLinkContentAccessControlHandler.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
|
||||
* The entity type definition.
|
||||
* @param \Drupal\Core\Access\AccessManagerInterface $access_manager
|
||||
* The access manager to check routes by name.
|
||||
*/
|
||||
public function __construct(EntityTypeInterface $entity_type, AccessManagerInterface $access_manager) {
|
||||
parent::__construct($entity_type);
|
||||
|
||||
$this->accessManager = $access_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
|
||||
return new static($entity_type, $container->get('access_manager'));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function checkAccess(EntityInterface $entity, $operation, $langcode, AccountInterface $account) {
|
||||
switch ($operation) {
|
||||
case 'view':
|
||||
// There is no direct view.
|
||||
return AccessResult::neutral();
|
||||
|
||||
case 'update':
|
||||
if (!$account->hasPermission('administer menu')) {
|
||||
return AccessResult::neutral()->cachePerPermissions();
|
||||
}
|
||||
else {
|
||||
// If there is a URL, this is an external link so always accessible.
|
||||
$access = AccessResult::allowed()->cachePerPermissions()->cacheUntilEntityChanges($entity);
|
||||
/** @var \Drupal\menu_link_content\MenuLinkContentInterface $entity */
|
||||
// We allow access, but only if the link is accessible as well.
|
||||
if (($url_object = $entity->getUrlObject()) && $url_object->isRouted()) {
|
||||
$link_access = $this->accessManager->checkNamedRoute($url_object->getRouteName(), $url_object->getRouteParameters(), $account, TRUE);
|
||||
$access = $access->andIf($link_access);
|
||||
}
|
||||
return $access;
|
||||
}
|
||||
|
||||
case 'delete':
|
||||
return AccessResult::allowedIf(!$entity->isNew() && $account->hasPermission('administer menu'))->cachePerPermissions()->cacheUntilEntityChanges($entity);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
136
core/modules/menu_link_content/src/MenuLinkContentInterface.php
Normal file
136
core/modules/menu_link_content/src/MenuLinkContentInterface.php
Normal file
|
@ -0,0 +1,136 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\menu_link_content\MenuLinkContentInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\menu_link_content;
|
||||
|
||||
use Drupal\Core\Entity\EntityChangedInterface;
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
|
||||
/**
|
||||
* Defines an interface for custom menu links.
|
||||
*/
|
||||
interface MenuLinkContentInterface extends ContentEntityInterface, EntityChangedInterface {
|
||||
|
||||
/**
|
||||
* Flags this instance as being wrapped in a menu link plugin instance.
|
||||
*/
|
||||
public function setInsidePlugin();
|
||||
|
||||
/**
|
||||
* Gets the title of the menu link.
|
||||
*
|
||||
* @return string
|
||||
* The title of the link.
|
||||
*/
|
||||
public function getTitle();
|
||||
|
||||
/**
|
||||
* Gets the url object pointing to the URL of the menu link content entity.
|
||||
*
|
||||
* @return \Drupal\Core\Url
|
||||
* A Url object instance.
|
||||
*/
|
||||
public function getUrlObject();
|
||||
|
||||
/**
|
||||
* Gets the menu name of the custom menu link.
|
||||
*
|
||||
* @return string
|
||||
* The menu ID.
|
||||
*/
|
||||
public function getMenuName();
|
||||
|
||||
/**
|
||||
* Gets the description of the menu link for the UI.
|
||||
*
|
||||
* @return string
|
||||
* The description to use on admin pages or as a title attribute.
|
||||
*/
|
||||
public function getDescription();
|
||||
|
||||
/**
|
||||
* Gets the menu plugin ID associated with this entity.
|
||||
*
|
||||
* @return string
|
||||
* The plugin ID.
|
||||
*/
|
||||
public function getPluginId();
|
||||
|
||||
/**
|
||||
* Returns whether the menu link is marked as enabled.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if is enabled, otherwise FALSE.
|
||||
*/
|
||||
public function isEnabled();
|
||||
|
||||
/**
|
||||
* Returns whether the menu link is marked as always expanded.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE for expanded, FALSE otherwise.
|
||||
*/
|
||||
public function isExpanded();
|
||||
|
||||
/**
|
||||
* Gets the plugin ID of the parent menu link.
|
||||
*
|
||||
* @return string
|
||||
* A plugin ID, or empty string if this link is at the top level.
|
||||
*/
|
||||
public function getParentId();
|
||||
|
||||
/**
|
||||
* Returns the weight of the menu link content entity.
|
||||
*
|
||||
* @return int
|
||||
* A weight for use when ordering links.
|
||||
*/
|
||||
public function getWeight();
|
||||
|
||||
/**
|
||||
* Builds up the menu link plugin definition for this entity.
|
||||
*
|
||||
* @return array
|
||||
* The plugin definition corresponding to this entity.
|
||||
*
|
||||
* @see \Drupal\Core\Menu\MenuLinkTree::$defaults
|
||||
*/
|
||||
public function getPluginDefinition();
|
||||
|
||||
/**
|
||||
* Returns whether the menu link requires rediscovery.
|
||||
*
|
||||
* If a menu-link points to a user-supplied path such as /blog then the route
|
||||
* this resolves to needs to be rediscovered as the module or route providing
|
||||
* a given path might change over time.
|
||||
*
|
||||
* For example: at the time a menu-link is created, the /blog path might be
|
||||
* provided by a route in Views module, but later this path may be served by
|
||||
* the Panels module. Flagging a link as requiring rediscovery ensures that if
|
||||
* the route that provides a user-entered path changes over time, the link is
|
||||
* flexible enough to update to reflect these changes.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the menu link requires rediscovery during route rebuilding.
|
||||
*/
|
||||
public function requiresRediscovery();
|
||||
|
||||
/**
|
||||
* Flags a link as requiring rediscovery.
|
||||
*
|
||||
* @param bool $rediscovery
|
||||
* Whether or not the link requires rediscovery.
|
||||
*
|
||||
* @return $this
|
||||
* The instance on which the method was called.
|
||||
*
|
||||
* @see \Drupal\menu_link_content\MenuLinkContentInterface::requiresRediscovery()
|
||||
*/
|
||||
public function setRequiresRediscovery($rediscovery);
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\menu_link_content\MenuLinkContentStorageSchema.
|
||||
*/
|
||||
|
||||
namespace Drupal\menu_link_content;
|
||||
|
||||
use Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
|
||||
/**
|
||||
* Defines the menu_link_content schema handler.
|
||||
*/
|
||||
class MenuLinkContentStorageSchema extends SqlContentEntityStorageSchema {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getSharedTableFieldSchema(FieldStorageDefinitionInterface $storage_definition, $table_name, array $column_mapping) {
|
||||
$schema = parent::getSharedTableFieldSchema($storage_definition, $table_name, $column_mapping);
|
||||
$field_name = $storage_definition->getName();
|
||||
|
||||
if ($table_name == 'menu_link_content') {
|
||||
switch ($field_name) {
|
||||
case 'rediscover':
|
||||
$this->addSharedTableFieldIndex($storage_definition, $schema, TRUE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $schema;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\menu_link_content\Plugin\Deriver\MenuLinkContentDeriver.
|
||||
*/
|
||||
|
||||
namespace Drupal\menu_link_content\Plugin\Deriver;
|
||||
|
||||
use Drupal\Component\Plugin\Derivative\DeriverBase;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Entity\Query\QueryFactory;
|
||||
use Drupal\Core\Menu\MenuLinkManagerInterface;
|
||||
use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides a deriver for user entered paths of menu links.
|
||||
*
|
||||
* The assumption is that the number of manually entered menu links are lower
|
||||
* compared to entity referenced ones.
|
||||
*/
|
||||
class MenuLinkContentDeriver extends DeriverBase implements ContainerDeriverInterface {
|
||||
|
||||
/**
|
||||
* The query factory.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\Query\QueryFactory
|
||||
*/
|
||||
protected $queryFactory;
|
||||
|
||||
/**
|
||||
* The entity manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityManagerInterface
|
||||
*/
|
||||
protected $entityManager;
|
||||
|
||||
/**
|
||||
* The menu link manager.
|
||||
*
|
||||
* @var \Drupal\Core\Menu\MenuLinkManagerInterface
|
||||
*/
|
||||
protected $menuLinkManager;
|
||||
|
||||
/**
|
||||
* Constructs a MenuLinkContentDeriver instance.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\Query\QueryFactory $query_factory
|
||||
* The query factory.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
|
||||
* The entity manager.
|
||||
* @param \Drupal\Core\Menu\MenuLinkManagerInterface $menu_link_manager
|
||||
* The menu link manager.
|
||||
*/
|
||||
public function __construct(QueryFactory $query_factory, EntityManagerInterface $entity_manager, MenuLinkManagerInterface $menu_link_manager) {
|
||||
$this->queryFactory = $query_factory;
|
||||
$this->entityManager = $entity_manager;
|
||||
$this->menuLinkManager = $menu_link_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, $base_plugin_id) {
|
||||
return new static(
|
||||
$container->get('entity.query'),
|
||||
$container->get('entity.manager'),
|
||||
$container->get('plugin.manager.menu.link')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDerivativeDefinitions($base_plugin_definition) {
|
||||
// Get all custom menu links which should be rediscovered.
|
||||
$entity_ids = $this->queryFactory->get('menu_link_content')
|
||||
->condition('rediscover', TRUE)
|
||||
->execute();
|
||||
$plugin_definitions = [];
|
||||
$menu_link_content_entities = $this->entityManager->getStorage('menu_link_content')->loadMultiple($entity_ids);
|
||||
/** @var \Drupal\menu_link_content\MenuLinkContentInterface $menu_link_content */
|
||||
foreach ($menu_link_content_entities as $menu_link_content) {
|
||||
$plugin_definitions[$menu_link_content->uuid()] = $menu_link_content->getPluginDefinition();
|
||||
}
|
||||
return $plugin_definitions;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,250 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\menu_link_content\Plugin\Menu\MenuLinkContent.
|
||||
*/
|
||||
|
||||
namespace Drupal\menu_link_content\Plugin\Menu;
|
||||
|
||||
use Drupal\Component\Plugin\Exception\PluginException;
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Language\LanguageManagerInterface;
|
||||
use Drupal\Core\Menu\MenuLinkBase;
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides the menu link plugin for content menu links.
|
||||
*/
|
||||
class MenuLinkContent extends MenuLinkBase implements ContainerFactoryPluginInterface {
|
||||
|
||||
/**
|
||||
* Entities IDs to load.
|
||||
*
|
||||
* It is an array of entity IDs keyed by entity IDs.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $entityIdsToLoad = array();
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $overrideAllowed = array(
|
||||
'menu_name' => 1,
|
||||
'parent' => 1,
|
||||
'weight' => 1,
|
||||
'expanded' => 1,
|
||||
'enabled' => 1,
|
||||
'title' => 1,
|
||||
'description' => 1,
|
||||
'route_name' => 1,
|
||||
'route_parameters' => 1,
|
||||
'url' => 1,
|
||||
'options' => 1,
|
||||
);
|
||||
|
||||
/**
|
||||
* The menu link content entity connected to this plugin instance.
|
||||
*
|
||||
* @var \Drupal\menu_link_content\MenuLinkContentInterface
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* The entity manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityManagerInterface
|
||||
*/
|
||||
protected $entityManager;
|
||||
|
||||
/**
|
||||
* The language manager.
|
||||
*
|
||||
* @var \Drupal\Core\Language\LanguageManagerInterface
|
||||
*/
|
||||
protected $languageManager;
|
||||
|
||||
/**
|
||||
* Constructs a new MenuLinkContent.
|
||||
*
|
||||
* @param array $configuration
|
||||
* A configuration array containing information about the plugin instance.
|
||||
* @param string $plugin_id
|
||||
* The plugin_id for the plugin instance.
|
||||
* @param mixed $plugin_definition
|
||||
* The plugin implementation definition.
|
||||
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
|
||||
* The entity manager.
|
||||
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
|
||||
* The language manager.
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityManagerInterface $entity_manager, LanguageManagerInterface $language_manager) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition);
|
||||
|
||||
if (!empty($this->pluginDefinition['metadata']['entity_id'])) {
|
||||
$entity_id = $this->pluginDefinition['metadata']['entity_id'];
|
||||
// Builds a list of entity IDs to take advantage of the more efficient
|
||||
// EntityStorageInterface::loadMultiple() in getEntity() at render time.
|
||||
static::$entityIdsToLoad[$entity_id] = $entity_id;
|
||||
}
|
||||
|
||||
$this->entityManager = $entity_manager;
|
||||
$this->languageManager = $language_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static(
|
||||
$configuration,
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
$container->get('entity.manager'),
|
||||
$container->get('language_manager')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the entity associated with this menu link.
|
||||
*
|
||||
* @return \Drupal\menu_link_content\MenuLinkContentInterface
|
||||
* The menu link content entity.
|
||||
*
|
||||
* @throws \Drupal\Component\Plugin\Exception\PluginException
|
||||
* If the entity ID and UUID are both invalid or missing.
|
||||
*/
|
||||
protected function getEntity() {
|
||||
if (empty($this->entity)) {
|
||||
$entity = NULL;
|
||||
$storage = $this->entityManager->getStorage('menu_link_content');
|
||||
if (!empty($this->pluginDefinition['metadata']['entity_id'])) {
|
||||
$entity_id = $this->pluginDefinition['metadata']['entity_id'];
|
||||
// Make sure the current ID is in the list, since each plugin empties
|
||||
// the list after calling loadMultiple(). Note that the list may include
|
||||
// multiple IDs added earlier in each plugin's constructor.
|
||||
static::$entityIdsToLoad[$entity_id] = $entity_id;
|
||||
$entities = $storage->loadMultiple(array_values(static::$entityIdsToLoad));
|
||||
$entity = isset($entities[$entity_id]) ? $entities[$entity_id] : NULL;
|
||||
static::$entityIdsToLoad = array();
|
||||
}
|
||||
if (!$entity) {
|
||||
// Fallback to the loading by the UUID.
|
||||
$uuid = $this->getUuid();
|
||||
$loaded_entities = $storage->loadByProperties(array('uuid' => $uuid));
|
||||
$entity = reset($loaded_entities);
|
||||
}
|
||||
if (!$entity) {
|
||||
throw new PluginException(SafeMarkup::format('Entity not found through the menu link plugin definition and could not fallback on UUID @uuid', array('@uuid' => $uuid)));
|
||||
}
|
||||
// Clone the entity object to avoid tampering with the static cache.
|
||||
$this->entity = clone $entity;
|
||||
$the_entity = $this->entityManager->getTranslationFromContext($this->entity);
|
||||
/** @var \Drupal\menu_link_content\MenuLinkContentInterface $the_entity */
|
||||
$this->entity = $the_entity;
|
||||
$this->entity->setInsidePlugin();
|
||||
}
|
||||
return $this->entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTitle() {
|
||||
// We only need to get the title from the actual entity if it may be a
|
||||
// translation based on the current language context. This can only happen
|
||||
// if the site is configured to be multilingual.
|
||||
if ($this->languageManager->isMultilingual()) {
|
||||
return $this->getEntity()->getTitle();
|
||||
}
|
||||
return $this->pluginDefinition['title'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDescription() {
|
||||
// We only need to get the description from the actual entity if it may be a
|
||||
// translation based on the current language context. This can only happen
|
||||
// if the site is configured to be multilingual.
|
||||
if ($this->languageManager->isMultilingual()) {
|
||||
return $this->getEntity()->getDescription();
|
||||
}
|
||||
return $this->pluginDefinition['description'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDeleteRoute() {
|
||||
return $this->getEntity()->urlInfo('delete-form');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getEditRoute() {
|
||||
return $this->getEntity()->urlInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTranslateRoute() {
|
||||
return $this->getEntity()->urlInfo('drupal:content-translation-overview');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the unique ID representing the menu link.
|
||||
*
|
||||
* @return string
|
||||
* The menu link ID.
|
||||
*/
|
||||
protected function getUuid() {
|
||||
$this->getDerivativeId();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function updateLink(array $new_definition_values, $persist) {
|
||||
// Filter the list of updates to only those that are allowed.
|
||||
$overrides = array_intersect_key($new_definition_values, $this->overrideAllowed);
|
||||
// Update the definition.
|
||||
$this->pluginDefinition = $overrides + $this->getPluginDefinition();
|
||||
if ($persist) {
|
||||
$entity = $this->getEntity();
|
||||
foreach ($overrides as $key => $value) {
|
||||
$entity->{$key}->value = $value;
|
||||
}
|
||||
$this->entityManager->getStorage('menu_link_content')->save($entity);
|
||||
}
|
||||
|
||||
return $this->pluginDefinition;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isDeletable() {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isTranslatable() {
|
||||
return $this->getEntity()->isTranslatable();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function deleteLink() {
|
||||
$this->getEntity()->delete();
|
||||
}
|
||||
|
||||
}
|
218
core/modules/menu_link_content/src/Tests/LinksTest.php
Normal file
218
core/modules/menu_link_content/src/Tests/LinksTest.php
Normal file
|
@ -0,0 +1,218 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\menu_link_content\Tests\LinksTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\menu_link_content\Tests;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\locale\TranslationString;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests handling of menu links hierarchies.
|
||||
*
|
||||
* @group Menu
|
||||
*/
|
||||
class LinksTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('router_test', 'menu_link_content');
|
||||
|
||||
/**
|
||||
* The menu link plugin manager.
|
||||
*
|
||||
* @var \Drupal\Core\Menu\MenuLinkManagerInterface $menuLinkManager
|
||||
*/
|
||||
protected $menuLinkManager;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->menuLinkManager = \Drupal::service('plugin.manager.menu.link');
|
||||
|
||||
entity_create('menu', array(
|
||||
'id' => 'menu_test',
|
||||
'label' => 'Test menu',
|
||||
'description' => 'Description text',
|
||||
))->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a simple hierarchy of links.
|
||||
*/
|
||||
function createLinkHierarchy($module = 'menu_test') {
|
||||
// First remove all the menu links in the menu.
|
||||
$this->menuLinkManager->deleteLinksInMenu('menu_test');
|
||||
|
||||
// Then create a simple link hierarchy:
|
||||
// - parent
|
||||
// - child-1
|
||||
// - child-1-1
|
||||
// - child-1-2
|
||||
// - child-2
|
||||
$base_options = array(
|
||||
'title' => 'Menu link test',
|
||||
'provider' => $module,
|
||||
'menu_name' => 'menu_test',
|
||||
);
|
||||
|
||||
$parent = $base_options + array(
|
||||
'link' => ['uri' => 'internal:/menu-test/hierarchy/parent'],
|
||||
);
|
||||
$link = entity_create('menu_link_content', $parent);
|
||||
$link->save();
|
||||
$links['parent'] = $link->getPluginId();
|
||||
|
||||
$child_1 = $base_options + array(
|
||||
'link' => ['uri' => 'internal:/menu-test/hierarchy/parent/child'],
|
||||
'parent' => $links['parent'],
|
||||
);
|
||||
$link = entity_create('menu_link_content', $child_1);
|
||||
$link->save();
|
||||
$links['child-1'] = $link->getPluginId();
|
||||
|
||||
$child_1_1 = $base_options + array(
|
||||
'link' => ['uri' => 'internal:/menu-test/hierarchy/parent/child2/child'],
|
||||
'parent' => $links['child-1'],
|
||||
);
|
||||
$link = entity_create('menu_link_content', $child_1_1);
|
||||
$link->save();
|
||||
$links['child-1-1'] = $link->getPluginId();
|
||||
|
||||
$child_1_2 = $base_options + array(
|
||||
'link' => ['uri' => 'internal:/menu-test/hierarchy/parent/child2/child'],
|
||||
'parent' => $links['child-1'],
|
||||
);
|
||||
$link = entity_create('menu_link_content', $child_1_2);
|
||||
$link->save();
|
||||
$links['child-1-2'] = $link->getPluginId();
|
||||
|
||||
$child_2 = $base_options + array(
|
||||
'link' => ['uri' => 'internal:/menu-test/hierarchy/parent/child'],
|
||||
'parent' => $links['parent'],
|
||||
);
|
||||
$link = entity_create('menu_link_content', $child_2);
|
||||
$link->save();
|
||||
$links['child-2'] = $link->getPluginId();
|
||||
|
||||
return $links;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that at set of links is properly parented.
|
||||
*/
|
||||
function assertMenuLinkParents($links, $expected_hierarchy) {
|
||||
foreach ($expected_hierarchy as $id => $parent) {
|
||||
/* @var \Drupal\Core\Menu\MenuLinkInterface $menu_link_plugin */
|
||||
$menu_link_plugin = $this->menuLinkManager->createInstance($links[$id]);
|
||||
$expected_parent = isset($links[$parent]) ? $links[$parent] : '';
|
||||
|
||||
$this->assertEqual($menu_link_plugin->getParent(), $expected_parent, SafeMarkup::format('Menu link %id has parent of %parent, expected %expected_parent.', array('%id' => $id, '%parent' => $menu_link_plugin->getParent(), '%expected_parent' => $expected_parent)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that a link entity's created timestamp is set.
|
||||
*/
|
||||
public function testCreateLink() {
|
||||
$options = array(
|
||||
'menu_name' => 'menu_test',
|
||||
'bundle' => 'menu_link_content',
|
||||
'link' => [['uri' => 'internal:/']],
|
||||
);
|
||||
$link = entity_create('menu_link_content', $options);
|
||||
$link->save();
|
||||
// Make sure the changed timestamp is set.
|
||||
$this->assertEqual($link->getChangedTime(), REQUEST_TIME, 'Creating a menu link sets the "changed" timestamp.');
|
||||
$options = array(
|
||||
'title' => 'Test Link',
|
||||
);
|
||||
$link->link->options = $options;
|
||||
$link->changed->value = 0;
|
||||
$link->save();
|
||||
// Make sure the changed timestamp is updated.
|
||||
$this->assertEqual($link->getChangedTime(), REQUEST_TIME, 'Changing a menu link sets "changed" timestamp.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test automatic reparenting of menu links.
|
||||
*/
|
||||
function testMenuLinkReparenting($module = 'menu_test') {
|
||||
// Check the initial hierarchy.
|
||||
$links = $this->createLinkHierarchy($module);
|
||||
|
||||
$expected_hierarchy = array(
|
||||
'parent' => '',
|
||||
'child-1' => 'parent',
|
||||
'child-1-1' => 'child-1',
|
||||
'child-1-2' => 'child-1',
|
||||
'child-2' => 'parent',
|
||||
);
|
||||
$this->assertMenuLinkParents($links, $expected_hierarchy);
|
||||
|
||||
// Start over, and move child-1 under child-2, and check that all the
|
||||
// children of child-1 have been moved too.
|
||||
$links = $this->createLinkHierarchy($module);
|
||||
/* @var \Drupal\Core\Menu\MenuLinkInterface $menu_link_plugin */
|
||||
$this->menuLinkManager->updateDefinition($links['child-1'], array('parent' => $links['child-2']));
|
||||
// Verify that the entity was updated too.
|
||||
$menu_link_plugin = $this->menuLinkManager->createInstance($links['child-1']);
|
||||
$entity = \Drupal::entityManager()->loadEntityByUuid('menu_link_content', $menu_link_plugin->getDerivativeId());
|
||||
$this->assertEqual($entity->getParentId(), $links['child-2']);
|
||||
|
||||
$expected_hierarchy = array(
|
||||
'parent' => '',
|
||||
'child-1' => 'child-2',
|
||||
'child-1-1' => 'child-1',
|
||||
'child-1-2' => 'child-1',
|
||||
'child-2' => 'parent',
|
||||
);
|
||||
$this->assertMenuLinkParents($links, $expected_hierarchy);
|
||||
|
||||
// Start over, and delete child-1, and check that the children of child-1
|
||||
// have been reassigned to the parent.
|
||||
$links = $this->createLinkHierarchy($module);
|
||||
$this->menuLinkManager->removeDefinition($links['child-1']);
|
||||
|
||||
$expected_hierarchy = array(
|
||||
'parent' => FALSE,
|
||||
'child-1-1' => 'parent',
|
||||
'child-1-2' => 'parent',
|
||||
'child-2' => 'parent',
|
||||
);
|
||||
$this->assertMenuLinkParents($links, $expected_hierarchy);
|
||||
|
||||
// @todo Figure out what makes sense to test in terms of automatic
|
||||
// re-parenting. https://www.drupal.org/node/2309531
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests uninstalling a module providing default links.
|
||||
*/
|
||||
public function testModuleUninstalledMenuLinks() {
|
||||
\Drupal::service('module_installer')->install(array('menu_test'));
|
||||
\Drupal::service('router.builder')->rebuild();
|
||||
\Drupal::service('plugin.manager.menu.link')->rebuild();
|
||||
$menu_links = $this->menuLinkManager->loadLinksByRoute('menu_test.menu_test');
|
||||
$this->assertEqual(count($menu_links), 1);
|
||||
$menu_link = reset($menu_links);
|
||||
$this->assertEqual($menu_link->getPluginId(), 'menu_test');
|
||||
|
||||
// Uninstall the module and ensure the menu link got removed.
|
||||
\Drupal::service('module_installer')->uninstall(array('menu_test'));
|
||||
\Drupal::service('plugin.manager.menu.link')->rebuild();
|
||||
$menu_links = $this->menuLinkManager->loadLinksByRoute('menu_test.menu_test');
|
||||
$this->assertEqual(count($menu_links), 0);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,149 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\menu_link_content\Tests\MenuLinkContentCacheabilityBubblingTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\menu_link_content\Tests;
|
||||
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Core\Menu\MenuTreeParameters;
|
||||
use Drupal\menu_link_content\Entity\MenuLinkContent;
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
use Drupal\user\Entity\User;
|
||||
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Routing\Route;
|
||||
|
||||
/**
|
||||
* Ensures that rendered menu links bubble the necessary cacheability metadata
|
||||
* for outbound path/route processing.
|
||||
*
|
||||
* @group menu_link_content
|
||||
*/
|
||||
class MenuLinkContentCacheabilityBubblingTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['menu_link_content', 'system', 'link', 'outbound_processing_test', 'url_alter_test', 'user'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installEntitySchema('menu_link_content');
|
||||
$this->installEntitySchema('user');
|
||||
$this->installSchema('system', ['url_alias', 'router']);
|
||||
|
||||
// Ensure that the weight of module_link_content is higher than system.
|
||||
// @see menu_link_content_install()
|
||||
module_set_weight('menu_link_content', 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests bubbling of menu links' outbound route/path processing cacheability.
|
||||
*/
|
||||
public function testOutboundPathAndRouteProcessing() {
|
||||
\Drupal::service('router.builder')->rebuild();
|
||||
|
||||
$request_stack = \Drupal::requestStack();
|
||||
/** @var \Symfony\Component\Routing\RequestContext $request_context */
|
||||
$request_context = \Drupal::service('router.request_context');
|
||||
|
||||
$request = Request::create('/');
|
||||
$request->attributes->set(RouteObjectInterface::ROUTE_NAME, '<front>');
|
||||
$request->attributes->set(RouteObjectInterface::ROUTE_OBJECT, new Route('/'));
|
||||
$request_stack->push($request);
|
||||
$request_context->fromRequest($request);
|
||||
|
||||
$menu_tree = \Drupal::menuTree();
|
||||
$renderer = \Drupal::service('renderer');
|
||||
|
||||
|
||||
$default_menu_cacheability = (new CacheableMetadata())
|
||||
->setCacheMaxAge(Cache::PERMANENT)
|
||||
->setCacheTags(['config:system.menu.tools'])
|
||||
->setCacheContexts(['languages:' . LanguageInterface::TYPE_INTERFACE, 'theme']);
|
||||
|
||||
User::create(['uid' => 1, 'name' => $this->randomString()])->save();
|
||||
User::create(['uid' => 2, 'name' => $this->randomString()])->save();
|
||||
|
||||
// Five test cases, four asserting one outbound path/route processor, and
|
||||
// together covering one of each:
|
||||
// - no cacheability metadata,
|
||||
// - a cache context,
|
||||
// - a cache tag,
|
||||
// - a cache max-age.
|
||||
// Plus an additional test case to verify that multiple links adding
|
||||
// cacheability metadata of the same type is working (two links with cache
|
||||
// tags).
|
||||
$test_cases = [
|
||||
// \Drupal\Core\RouteProcessor\RouteProcessorCurrent: 'route' cache context.
|
||||
[
|
||||
'uri' => 'route:<current>',
|
||||
'cacheability' => (new CacheableMetadata())->setCacheContexts(['route']),
|
||||
],
|
||||
// \Drupal\Core\Access\RouteProcessorCsrf: max-age = 0.
|
||||
[
|
||||
'uri' => 'route:outbound_processing_test.route.csrf',
|
||||
'cacheability' => (new CacheableMetadata())->setCacheMaxAge(0),
|
||||
],
|
||||
// \Drupal\Core\PathProcessor\PathProcessorFront: permanently cacheable.
|
||||
[
|
||||
'uri' => 'internal:/',
|
||||
'cacheability' => (new CacheableMetadata()),
|
||||
],
|
||||
// \Drupal\url_alter_test\PathProcessorTest: user entity's cache tags.
|
||||
[
|
||||
'uri' => 'internal:/user/1',
|
||||
'cacheability' => (new CacheableMetadata())->setCacheTags(User::load(1)->getCacheTags()),
|
||||
],
|
||||
[
|
||||
'uri' => 'internal:/user/2',
|
||||
'cacheability' => (new CacheableMetadata())->setCacheTags(User::load(2)->getCacheTags()),
|
||||
],
|
||||
];
|
||||
|
||||
// Test each expectation individually.
|
||||
foreach ($test_cases as $expectation) {
|
||||
$menu_link_content = MenuLinkContent::create([
|
||||
'link' => ['uri' => $expectation['uri']],
|
||||
'menu_name' => 'tools',
|
||||
]);
|
||||
$menu_link_content->save();
|
||||
$tree = $menu_tree->load('tools', new MenuTreeParameters());
|
||||
$build = $menu_tree->build($tree);
|
||||
$renderer->renderRoot($build);
|
||||
|
||||
$expected_cacheability = $default_menu_cacheability->merge($expectation['cacheability']);
|
||||
$this->assertEqual($expected_cacheability, CacheableMetadata::createFromRenderArray($build));
|
||||
|
||||
$menu_link_content->delete();
|
||||
}
|
||||
|
||||
// Now test them all together in one menu: the rendered menu's cacheability
|
||||
// metadata should be the combination of the cacheability of all links, and
|
||||
// thus of all tested outbound path & route processors.
|
||||
$expected_cacheability = new CacheableMetadata();
|
||||
foreach ($test_cases as $expectation) {
|
||||
$menu_link_content = MenuLinkContent::create([
|
||||
'link' => ['uri' => $expectation['uri']],
|
||||
'menu_name' => 'tools',
|
||||
]);
|
||||
$menu_link_content->save();
|
||||
$expected_cacheability = $expected_cacheability->merge($expectation['cacheability']);
|
||||
}
|
||||
$tree = $menu_tree->load('tools', new MenuTreeParameters());
|
||||
$build = $menu_tree->build($tree);
|
||||
$renderer->renderRoot($build);
|
||||
$expected_cacheability = $expected_cacheability->merge($default_menu_cacheability);
|
||||
$this->assertEqual($expected_cacheability, CacheableMetadata::createFromRenderArray($build));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\menu_link_content\Tests\MenuLinkContentDeriverTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\menu_link_content\Tests;
|
||||
|
||||
use Drupal\Core\Menu\MenuTreeParameters;
|
||||
use Drupal\menu_link_content\Entity\MenuLinkContent;
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
use Symfony\Component\Routing\Route;
|
||||
|
||||
/**
|
||||
* Tests the menu link content deriver.
|
||||
*
|
||||
* @group menu_link_content
|
||||
*/
|
||||
class MenuLinkContentDeriverTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['menu_link_content', 'link', 'system', 'menu_link_content_dynamic_route'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installEntitySchema('menu_link_content');
|
||||
$this->installSchema('system', 'router');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the rediscovering.
|
||||
*/
|
||||
public function testRediscover() {
|
||||
\Drupal::state()->set('menu_link_content_dynamic_route.routes', [
|
||||
'route_name_1' => new Route('/example-path'),
|
||||
]);
|
||||
\Drupal::service('router.builder')->rebuild();
|
||||
|
||||
// Set up a custom menu link pointing to a specific path.
|
||||
MenuLinkContent::create([
|
||||
'title' => 'Example',
|
||||
'link' => [['uri' => 'internal:/example-path']],
|
||||
'menu_name' => 'tools',
|
||||
])->save();
|
||||
$menu_tree = \Drupal::menuTree()->load('tools', new MenuTreeParameters());
|
||||
$this->assertEqual(1, count($menu_tree));
|
||||
/** @var \Drupal\Core\Menu\MenuLinkTreeElement $tree_element */
|
||||
$tree_element = reset($menu_tree);
|
||||
$this->assertEqual('route_name_1', $tree_element->link->getRouteName());
|
||||
|
||||
// Change the underlying route and trigger the rediscovering.
|
||||
\Drupal::state()->set('menu_link_content_dynamic_route.routes', [
|
||||
'route_name_2' => new Route('/example-path'),
|
||||
]);
|
||||
\Drupal::service('router.builder')->rebuild();
|
||||
|
||||
// Ensure that the new route name / parameters are captured by the tree.
|
||||
$menu_tree = \Drupal::menuTree()->load('tools', new MenuTreeParameters());
|
||||
$this->assertEqual(1, count($menu_tree));
|
||||
/** @var \Drupal\Core\Menu\MenuLinkTreeElement $tree_element */
|
||||
$tree_element = reset($menu_tree);
|
||||
$this->assertEqual('route_name_2', $tree_element->link->getRouteName());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\menu_link_content\Tests\MenuLinkContentFormTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\menu_link_content\Tests;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests the menu link content UI.
|
||||
*
|
||||
* @group Menu
|
||||
*/
|
||||
class MenuLinkContentFormTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array(
|
||||
'menu_link_content',
|
||||
'menu_ui',
|
||||
);
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$web_user = $this->drupalCreateUser(array('administer menu'));
|
||||
$this->drupalLogin($web_user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the MenuLinkContentForm class.
|
||||
*/
|
||||
public function testMenuLinkContentForm() {
|
||||
$this->drupalGet('admin/structure/menu/manage/admin/add');
|
||||
$element = $this->xpath('//select[@id = :id]/option[@selected]', array(':id' => 'edit-menu-parent'));
|
||||
$this->assertTrue($element, 'A default menu parent was found.');
|
||||
$this->assertEqual('admin:', $element[0]['value'], '<Administration> menu is the parent.');
|
||||
|
||||
$this->drupalPostForm(
|
||||
NULL,
|
||||
array(
|
||||
'title[0][value]' => t('Front page'),
|
||||
'link[0][uri]' => '<front>',
|
||||
),
|
||||
t('Save')
|
||||
);
|
||||
$this->assertText(t('The menu link has been saved.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests validation for the MenuLinkContentForm class.
|
||||
*/
|
||||
public function testMenuLinkContentFormValidation() {
|
||||
$this->drupalGet('admin/structure/menu/manage/admin/add');
|
||||
$this->drupalPostForm(
|
||||
NULL,
|
||||
array(
|
||||
'title[0][value]' => t('Test page'),
|
||||
'link[0][uri]' => '<test>',
|
||||
),
|
||||
t('Save')
|
||||
);
|
||||
$this->assertText(t('Manually entered paths should start with /, ? or #.'));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\menu_link_content\Tests\MenuLinkContentTranslationUITest.
|
||||
*/
|
||||
|
||||
namespace Drupal\menu_link_content\Tests;
|
||||
|
||||
use Drupal\content_translation\Tests\ContentTranslationUITestBase;
|
||||
use Drupal\menu_link_content\Entity\MenuLinkContent;
|
||||
|
||||
/**
|
||||
* Tests the menu link content translation UI.
|
||||
*
|
||||
* @group Menu
|
||||
*/
|
||||
class MenuLinkContentTranslationUITest extends ContentTranslationUITestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array(
|
||||
'language',
|
||||
'content_translation',
|
||||
'menu_link_content',
|
||||
'menu_ui',
|
||||
);
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
$this->entityTypeId = 'menu_link_content';
|
||||
$this->bundle = 'menu_link_content';
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getTranslatorPermissions() {
|
||||
return array_merge(parent::getTranslatorPermissions(), array('administer menu'));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getAdministratorPermissions() {
|
||||
return array_merge(parent::getAdministratorPermissions(), array('administer themes', 'view the administration theme'));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createEntity($values, $langcode, $bundle_name = NULL) {
|
||||
$values['menu_name'] = 'tools';
|
||||
$values['link']['uri'] = 'internal:/admin/structure/menu';
|
||||
$values['title'] = 'Test title';
|
||||
|
||||
return parent::createEntity($values, $langcode, $bundle_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that a translate link can be found on the menu edit form.
|
||||
*/
|
||||
public function testTranslationLinkOnMenuEditForm() {
|
||||
$this->drupalGet('admin/structure/menu/manage/tools');
|
||||
$this->assertNoLink(t('Translate'));
|
||||
|
||||
$menu_link_content = MenuLinkContent::create(['menu_name' => 'tools', 'link' => ['uri' => 'internal:/admin/structure/menu']]);
|
||||
$menu_link_content->save();
|
||||
$this->drupalGet('admin/structure/menu/manage/tools');
|
||||
$this->assertLink(t('Translate'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that translation page inherits admin status of edit page.
|
||||
*/
|
||||
function testTranslationLinkTheme() {
|
||||
$this->drupalLogin($this->administrator);
|
||||
$entityId = $this->createEntity(array(), 'en');
|
||||
|
||||
// Set up Seven as the admin theme to test.
|
||||
$this->container->get('theme_handler')->install(array('seven'));
|
||||
$edit = array();
|
||||
$edit['admin_theme'] = 'seven';
|
||||
$this->drupalPostForm('admin/appearance', $edit, t('Save configuration'));
|
||||
$this->drupalGet('admin/structure/menu/item/' . $entityId . '/edit');
|
||||
$this->assertRaw('core/themes/seven/css/base/elements.css', 'Edit uses admin theme.');
|
||||
$this->drupalGet('admin/structure/menu/item/' . $entityId . '/edit/translations');
|
||||
$this->assertRaw('core/themes/seven/css/base/elements.css', 'Translation uses admin theme as well.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doTestTranslationEdit() {
|
||||
$entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
|
||||
$languages = $this->container->get('language_manager')->getLanguages();
|
||||
|
||||
foreach ($this->langcodes as $langcode) {
|
||||
// We only want to test the title for non-english translations.
|
||||
if ($langcode != 'en') {
|
||||
$options = array('language' => $languages[$langcode]);
|
||||
$url = $entity->urlInfo('edit-form', $options);
|
||||
$this->drupalGet($url);
|
||||
|
||||
$title = t('@title [%language translation]', array(
|
||||
'@title' => $entity->getTranslation($langcode)->label(),
|
||||
'%language' => $languages[$langcode]->getName(),
|
||||
));
|
||||
$this->assertRaw($title);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\menu_link_content\Tests\PathAliasMenuLinkContentTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\menu_link_content\Tests;
|
||||
|
||||
use Drupal\Core\DependencyInjection\ContainerBuilder;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Core\Menu\MenuTreeParameters;
|
||||
use Drupal\menu_link_content\Entity\MenuLinkContent;
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Ensures that the menu tree adapts to path alias changes.
|
||||
*
|
||||
* @group menu_link_content
|
||||
*/
|
||||
class PathAliasMenuLinkContentTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['menu_link_content', 'system', 'link', 'test_page_test'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installEntitySchema('menu_link_content');
|
||||
$this->installSchema('system', ['url_alias', 'router']);
|
||||
|
||||
// Ensure that the weight of module_link_content is higher than system.
|
||||
// @see menu_link_content_install()
|
||||
module_set_weight('menu_link_content', 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function containerBuild(ContainerBuilder $container) {
|
||||
parent::containerBuild($container);
|
||||
|
||||
$definition = $container->getDefinition('path_processor_alias');
|
||||
$definition
|
||||
->addTag('path_processor_inbound', ['priority' => 100]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tests the path aliasing changing.
|
||||
*/
|
||||
public function testPathAliasChange() {
|
||||
\Drupal::service('router.builder')->rebuild();
|
||||
|
||||
/** @var \Drupal\Core\Path\AliasStorageInterface $path_alias_storage */
|
||||
$path_alias_storage = \Drupal::service('path.alias_storage');
|
||||
$alias = $path_alias_storage->save('/test-page', '/my-blog');
|
||||
$pid = $alias['pid'];
|
||||
|
||||
$menu_link_content = MenuLinkContent::create([
|
||||
'title' => 'Menu title',
|
||||
'link' => ['uri' => 'internal:/my-blog'],
|
||||
'menu_name' => 'tools',
|
||||
]);
|
||||
$menu_link_content->save();
|
||||
|
||||
$tree = \Drupal::menuTree()->load('tools', new MenuTreeParameters());
|
||||
$this->assertEqual('test_page_test.test_page', $tree[$menu_link_content->getPluginId()]->link->getPluginDefinition()['route_name']);
|
||||
|
||||
// Saving an alias should clear the alias manager cache.
|
||||
$path_alias_storage->save('/test-render-title', '/my-blog', LanguageInterface::LANGCODE_NOT_SPECIFIED, $pid);
|
||||
|
||||
$tree = \Drupal::menuTree()->load('tools', new MenuTreeParameters());
|
||||
$this->assertEqual('test_page_test.render_title', $tree[$menu_link_content->getPluginId()]->link->getPluginDefinition()['route_name']);
|
||||
|
||||
// Delete the alias.
|
||||
$path_alias_storage->delete(['pid' => $pid]);
|
||||
$tree = \Drupal::menuTree()->load('tools', new MenuTreeParameters());
|
||||
$this->assertTrue(isset($tree[$menu_link_content->getPluginId()]));
|
||||
$this->assertEqual('', $tree[$menu_link_content->getPluginId()]->link->getRouteName());
|
||||
// Verify the plugin now references a path that does not match any route.
|
||||
$this->assertEqual('base:my-blog', $tree[$menu_link_content->getPluginId()]->link->getUrlObject()->getUri());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
name: 'Menu link content dynamic route'
|
||||
type: module
|
||||
core: 8.x
|
||||
hidden: true
|
||||
dependencies:
|
||||
- menu_link_content
|
|
@ -0,0 +1,2 @@
|
|||
route_callbacks:
|
||||
- 'Drupal\menu_link_content_dynamic_route\Routes::dynamic'
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\menu_link_content_dynamic_route\Routes.
|
||||
*/
|
||||
|
||||
namespace Drupal\menu_link_content_dynamic_route;
|
||||
|
||||
/**
|
||||
* Provides dynamic routes for test purposes.
|
||||
*/
|
||||
class Routes {
|
||||
|
||||
public function dynamic() {
|
||||
return \Drupal::state()->get('menu_link_content_dynamic_route.routes', []);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
name: 'Outbound route/path processing'
|
||||
type: module
|
||||
core: 8.x
|
||||
hidden: true
|
|
@ -0,0 +1,5 @@
|
|||
outbound_processing_test.route.csrf:
|
||||
path: '/outbound_processing_test/route/csrf'
|
||||
requirements:
|
||||
_access: 'TRUE'
|
||||
_csrf_token: 'TRUE'
|
Reference in a new issue