Move into nested docroot
This commit is contained in:
parent
83a0d3a149
commit
c8b70abde9
13405 changed files with 0 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
web/core/modules/menu_link_content/menu_link_content.install
Normal file
18
web/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
|
83
web/core/modules/menu_link_content/menu_link_content.module
Normal file
83
web/core/modules/menu_link_content/menu_link_content.module
Normal file
|
@ -0,0 +1,83 @@
|
|||
<?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']);
|
||||
}
|
|
@ -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,56 @@
|
|||
id: d6_menu_links
|
||||
label: Menu links
|
||||
migration_tags:
|
||||
- Drupal 6
|
||||
source:
|
||||
plugin: menu_link
|
||||
process:
|
||||
id: mlid
|
||||
title: link_title
|
||||
description: description
|
||||
menu_name:
|
||||
-
|
||||
plugin: migration
|
||||
# The menu migration is in the system module.
|
||||
migration: d6_menu
|
||||
source: menu_name
|
||||
-
|
||||
plugin: skip_on_empty
|
||||
method: row
|
||||
-
|
||||
plugin: static_map
|
||||
map:
|
||||
management: admin
|
||||
bypass: true
|
||||
'link/uri':
|
||||
plugin: link_uri
|
||||
source:
|
||||
- link_path
|
||||
'link/options': options
|
||||
route:
|
||||
plugin: route
|
||||
source:
|
||||
- link_path
|
||||
- options
|
||||
route_name: '@route/route_name'
|
||||
route_parameters: '@route/route_parameters'
|
||||
url: '@route/url'
|
||||
options: '@route/options'
|
||||
external: external
|
||||
weight: weight
|
||||
expanded: expanded
|
||||
enabled: enabled
|
||||
parent:
|
||||
plugin: menu_link_parent
|
||||
source:
|
||||
- plid
|
||||
- '@menu_name'
|
||||
- parent_link_path
|
||||
changed: updated
|
||||
destination:
|
||||
plugin: entity:menu_link_content
|
||||
default_bundle: menu_link_content
|
||||
no_stub: true
|
||||
migration_dependencies:
|
||||
required:
|
||||
- d6_menu
|
|
@ -0,0 +1,52 @@
|
|||
id: d7_menu_links
|
||||
label: Menu links
|
||||
migration_tags:
|
||||
- Drupal 7
|
||||
source:
|
||||
plugin: menu_link
|
||||
constants:
|
||||
bundle: menu_link_content
|
||||
process:
|
||||
id: mlid
|
||||
bundle: 'constants/bundle'
|
||||
title: link_title
|
||||
description: description
|
||||
menu_name:
|
||||
-
|
||||
plugin: migration
|
||||
migration: d7_menu
|
||||
source: menu_name
|
||||
-
|
||||
plugin: skip_on_empty
|
||||
method: row
|
||||
'link/uri':
|
||||
plugin: link_uri
|
||||
source:
|
||||
- link_path
|
||||
'link/options': options
|
||||
route:
|
||||
plugin: route
|
||||
source:
|
||||
- link_path
|
||||
- options
|
||||
route_name: '@route/route_name'
|
||||
route_parameters: '@route/route_parameters'
|
||||
url: '@route/url'
|
||||
options: '@route/options'
|
||||
external: external
|
||||
weight: weight
|
||||
expanded: expanded
|
||||
enabled: enabled
|
||||
parent:
|
||||
plugin: menu_link_parent
|
||||
source:
|
||||
- plid
|
||||
- '@menu_name'
|
||||
- parent_link_path
|
||||
changed: updated
|
||||
destination:
|
||||
plugin: entity:menu_link_content
|
||||
no_stub: true
|
||||
migration_dependencies:
|
||||
required:
|
||||
- d7_menu
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,382 @@
|
|||
<?php
|
||||
|
||||
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() {
|
||||
// Cast the parent ID to a string, only an empty string means no parent,
|
||||
// NULL keeps the existing parent.
|
||||
return (string) $this->get('parent')->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getWeight() {
|
||||
return (int) $this->get('weight')->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.
|
||||
$definition = $this->getPluginDefinition();
|
||||
// Even when $update is FALSE, for top level links it is possible the link
|
||||
// already is in the storage because of the getPluginDefinition() call
|
||||
// above, see https://www.drupal.org/node/2605684#comment-10515450 for the
|
||||
// call chain. Because of this the $update flag is ignored and only the
|
||||
// existence of the definition (equals to being in the tree storage) is
|
||||
// checked.
|
||||
if ($menu_link_manager->getDefinition($this->getPluginId(), FALSE)) {
|
||||
// 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(), $definition, FALSE);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$menu_link_manager->addDefinition($this->getPluginId(), $definition);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@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) {
|
||||
/** @var \Drupal\Core\Field\BaseFieldDefinition[] $fields */
|
||||
$fields = parent::baseFieldDefinitions($entity_type);
|
||||
|
||||
$fields['id']->setLabel(t('Entity ID'))
|
||||
->setDescription(t('The entity ID for this menu link content entity.'));
|
||||
|
||||
$fields['uuid']->setDescription(t('The content menu link UUID.'));
|
||||
|
||||
$fields['langcode']->setDescription(t('The menu link language code.'));
|
||||
|
||||
$fields['bundle']
|
||||
->setDescription(t('The content menu link bundle.'))
|
||||
->setSetting('max_length', EntityTypeInterface::BUNDLE_MAX_LENGTH)
|
||||
->setSetting('is_ascii', 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)
|
||||
->setSetting('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)
|
||||
->setSetting('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['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,37 @@
|
|||
<?php
|
||||
|
||||
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() {
|
||||
if ($this->moduleHandler->moduleExists('menu_ui')) {
|
||||
return new Url('entity.menu.edit_form', array('menu' => $this->entity->getMenuName()));
|
||||
}
|
||||
return $this->entity->urlInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@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()));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
<?php
|
||||
|
||||
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,78 @@
|
|||
<?php
|
||||
|
||||
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, AccountInterface $account) {
|
||||
switch ($operation) {
|
||||
case 'view':
|
||||
// There is no direct viewing of a menu link, but still for purposes of
|
||||
// content_translation we need a generic way to check access.
|
||||
return AccessResult::allowedIfHasPermission($account, 'administer menu');
|
||||
|
||||
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()->addCacheableDependency($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()->addCacheableDependency($entity);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
<?php
|
||||
|
||||
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,31 @@
|
|||
<?php
|
||||
|
||||
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,85 @@
|
|||
<?php
|
||||
|
||||
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,244 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\menu_link_content\Plugin\Menu;
|
||||
|
||||
use Drupal\Component\Plugin\Exception\PluginException;
|
||||
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("Entity not found through the menu link plugin definition and could not fallback on 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();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\menu_link_content\Plugin\migrate\process;
|
||||
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\migrate\MigrateExecutableInterface;
|
||||
use Drupal\migrate\ProcessPluginBase;
|
||||
use Drupal\migrate\Row;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Processes a link path into an 'internal:' or 'entity:' URI.
|
||||
*
|
||||
* @MigrateProcessPlugin(
|
||||
* id = "link_uri"
|
||||
* )
|
||||
*/
|
||||
class LinkUri extends ProcessPluginBase implements ContainerFactoryPluginInterface {
|
||||
|
||||
/**
|
||||
* The entity type manager, used to fetch entity link templates.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* Constructs a LinkUri object.
|
||||
*
|
||||
* @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\EntityTypeManagerInterface $entity_type_manager
|
||||
* The entity type manager, used to fetch entity link templates.
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition);
|
||||
$this->entityTypeManager = $entity_type_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_type.manager')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
|
||||
list($path) = $value;
|
||||
$path = ltrim($path, '/');
|
||||
|
||||
if (parse_url($path, PHP_URL_SCHEME) === NULL) {
|
||||
if ($path == '<front>') {
|
||||
$path = '';
|
||||
}
|
||||
$path = 'internal:/' . $path;
|
||||
|
||||
// Convert entity URIs to the entity scheme, if the path matches a route
|
||||
// of the form "entity.$entity_type_id.canonical".
|
||||
// @see \Drupal\Core\Url::fromEntityUri()
|
||||
$url = Url::fromUri($path);
|
||||
if ($url->isRouted()) {
|
||||
$route_name = $url->getRouteName();
|
||||
foreach (array_keys($this->entityTypeManager->getDefinitions()) as $entity_type_id) {
|
||||
if ($route_name == "entity.$entity_type_id.canonical" && isset($url->getRouteParameters()[$entity_type_id])) {
|
||||
return "entity:$entity_type_id/" . $url->getRouteParameters()[$entity_type_id];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $path;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\menu_link_content\Plugin\migrate\process\d6;
|
||||
|
||||
use \Drupal\menu_link_content\Plugin\migrate\process\LinkUri as RealLinkUri;
|
||||
|
||||
/**
|
||||
* Processes a link path into an 'internal:' or 'entity:' URI.
|
||||
*
|
||||
* @deprecated in Drupal 8.2.0, will be removed before Drupal 9.0.0. Use
|
||||
* \Drupal\menu_link_content\Plugin\migrate\process\LinkUri instead.
|
||||
*/
|
||||
class LinkUri extends RealLinkUri {}
|
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\menu_link_content\Plugin\migrate\process\d7;
|
||||
|
||||
use Drupal\menu_link_content\Plugin\migrate\process\LinkUri;
|
||||
|
||||
/**
|
||||
* Processes an internal uri into an 'internal:' or 'entity:' URI.
|
||||
*
|
||||
* @deprecated in Drupal 8.2.0, will be removed before Drupal 9.0.0. Use
|
||||
* \Drupal\menu_link_content\Plugin\migrate\process\LinkUri instead.
|
||||
*/
|
||||
class InternalUri extends LinkUri {}
|
|
@ -0,0 +1,91 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\menu_link_content\Plugin\migrate\source;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
|
||||
use Drupal\migrate\Row;
|
||||
|
||||
/**
|
||||
* Drupal menu link source from database.
|
||||
*
|
||||
* @MigrateSource(
|
||||
* id = "menu_link",
|
||||
* )
|
||||
*/
|
||||
class MenuLink extends DrupalSqlBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function query() {
|
||||
$query = $this->select('menu_links', 'ml')
|
||||
->fields('ml');
|
||||
$and = $query->andConditionGroup()
|
||||
->condition('ml.module', 'menu')
|
||||
->condition('ml.router_path', ['admin/build/menu-customize/%', 'admin/structure/menu/manage/%'], 'NOT IN');
|
||||
$condition = $query->orConditionGroup()
|
||||
->condition('ml.customized', 1)
|
||||
->condition($and);
|
||||
$query->condition($condition);
|
||||
$query->leftJoin('menu_links', 'pl', 'ml.plid = pl.mlid');
|
||||
$query->addField('pl', 'link_path', 'parent_link_path');
|
||||
$query->orderBy('ml.depth');
|
||||
$query->orderby('ml.mlid');
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fields() {
|
||||
return array(
|
||||
'menu_name' => t("The menu name. All links with the same menu name (such as 'navigation') are part of the same menu."),
|
||||
'mlid' => t('The menu link ID (mlid) is the integer primary key.'),
|
||||
'plid' => t('The parent link ID (plid) is the mlid of the link above in the hierarchy, or zero if the link is at the top level in its menu.'),
|
||||
'link_path' => t('The Drupal path or external path this link points to.'),
|
||||
'router_path' => t('For links corresponding to a Drupal path (external = 0), this connects the link to a {menu_router}.path for joins.'),
|
||||
'link_title' => t('The text displayed for the link, which may be modified by a title callback stored in {menu_router}.'),
|
||||
'options' => t('A serialized array of options to set on the URL, such as a query string or HTML attributes.'),
|
||||
'module' => t('The name of the module that generated this link.'),
|
||||
'hidden' => t('A flag for whether the link should be rendered in menus. (1 = a disabled menu item that may be shown on admin screens, -1 = a menu callback, 0 = a normal, visible link)'),
|
||||
'external' => t('A flag to indicate if the link points to a full URL starting with a protocol, like http:// (1 = external, 0 = internal).'),
|
||||
'has_children' => t('Flag indicating whether any links have this link as a parent (1 = children exist, 0 = no children).'),
|
||||
'expanded' => 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)'),
|
||||
'weight' => t('Link weight among links in the same menu at the same depth.'),
|
||||
'depth' => t('The depth relative to the top level. A link with plid == 0 will have depth == 1.'),
|
||||
'customized' => t('A flag to indicate that the user has manually created or edited the link (1 = customized, 0 = not customized).'),
|
||||
'p1' => t('The first mlid in the materialized path. If N = depth, then pN must equal the mlid. If depth > 1 then p(N-1) must equal the plid. All pX where X > depth must equal zero. The columns p1 .. p9 are also called the parents.'),
|
||||
'p2' => t('The second mlid in the materialized path. See p1.'),
|
||||
'p3' => t('The third mlid in the materialized path. See p1.'),
|
||||
'p4' => t('The fourth mlid in the materialized path. See p1.'),
|
||||
'p5' => t('The fifth mlid in the materialized path. See p1.'),
|
||||
'p6' => t('The sixth mlid in the materialized path. See p1.'),
|
||||
'p7' => t('The seventh mlid in the materialized path. See p1.'),
|
||||
'p8' => t('The eighth mlid in the materialized path. See p1.'),
|
||||
'p9' => t('The ninth mlid in the materialized path. See p1.'),
|
||||
'updated' => t('Flag that indicates that this link was generated during the update from Drupal 5.'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function prepareRow(Row $row) {
|
||||
$row->setSourceProperty('options', unserialize($row->getSourceProperty('options')));
|
||||
$row->setSourceProperty('enabled', !$row->getSourceProperty('hidden'));
|
||||
$row->setSourceProperty('description', Unicode::truncate($row->getSourceProperty('options/attributes/title'), 255));
|
||||
|
||||
return parent::prepareRow($row);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIds() {
|
||||
$ids['mlid']['type'] = 'integer';
|
||||
$ids['mlid']['alias'] = 'ml';
|
||||
return $ids;
|
||||
}
|
||||
|
||||
}
|
228
web/core/modules/menu_link_content/src/Tests/LinksTest.php
Normal file
228
web/core/modules/menu_link_content/src/Tests/LinksTest.php
Normal file
|
@ -0,0 +1,228 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\menu_link_content\Tests;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\menu_link_content\Entity\MenuLinkContent;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\system\Entity\Menu;
|
||||
|
||||
/**
|
||||
* 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');
|
||||
|
||||
Menu::create(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 = MenuLinkContent::create($parent);
|
||||
$link->save();
|
||||
$links['parent'] = $link->getPluginId();
|
||||
|
||||
$child_1 = $base_options + array(
|
||||
'link' => ['uri' => 'internal:/menu-test/hierarchy/parent/child'],
|
||||
'parent' => $links['parent'],
|
||||
);
|
||||
$link = MenuLinkContent::create($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 = MenuLinkContent::create($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 = MenuLinkContent::create($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 = MenuLinkContent::create($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 = MenuLinkContent::create($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);
|
||||
|
||||
// Try changing the parent at the entity level.
|
||||
$definition = $this->menuLinkManager->getDefinition($links['child-1-2']);
|
||||
$entity = MenuLinkContent::load($definition['metadata']['entity_id']);
|
||||
$entity->parent->value = '';
|
||||
$entity->save();
|
||||
|
||||
$expected_hierarchy = array(
|
||||
'parent' => '',
|
||||
'child-1-1' => 'parent',
|
||||
'child-1-2' => '',
|
||||
'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,65 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\menu_link_content\Tests;
|
||||
|
||||
use Drupal\menu_link_content\Entity\MenuLinkContent;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\system\Entity\Menu;
|
||||
|
||||
/**
|
||||
* Tests the menu link content delete UI.
|
||||
*
|
||||
* @group Menu
|
||||
*/
|
||||
class MenuLinkContentDeleteFormTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['menu_link_content'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$web_user = $this->drupalCreateUser(['administer menu']);
|
||||
$this->drupalLogin($web_user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the MenuLinkContentDeleteForm class.
|
||||
*/
|
||||
public function testMenuLinkContentDeleteForm() {
|
||||
// Add new menu item.
|
||||
$this->drupalPostForm(
|
||||
'admin/structure/menu/manage/admin/add',
|
||||
[
|
||||
'title[0][value]' => t('Front page'),
|
||||
'link[0][uri]' => '<front>',
|
||||
],
|
||||
t('Save')
|
||||
);
|
||||
$this->assertText(t('The menu link has been saved.'));
|
||||
|
||||
$menu_link = MenuLinkContent::load(1);
|
||||
$this->drupalGet($menu_link->urlInfo('delete-form'));
|
||||
$this->assertRaw(t('Are you sure you want to delete the custom menu link %name?', ['%name' => $menu_link->label()]));
|
||||
$this->assertLink(t('Cancel'));
|
||||
// Make sure cancel link points to link edit
|
||||
$this->assertLinkByHref($menu_link->url('edit-form'));
|
||||
|
||||
\Drupal::service('module_installer')->install(['menu_ui']);
|
||||
\Drupal::service('router.builder')->rebuild();
|
||||
|
||||
// Make sure cancel URL points to menu_ui route now.
|
||||
$this->drupalGet($menu_link->urlInfo('delete-form'));
|
||||
$menu = Menu::load($menu_link->getMenuName());
|
||||
$this->assertLinkByHref($menu->url('edit-form'));
|
||||
$this->drupalPostForm(NULL, [], t('Delete'));
|
||||
$this->assertRaw(t('The menu link %title has been deleted.', ['%title' => $menu_link->label()]));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
|
||||
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',
|
||||
);
|
||||
|
||||
/**
|
||||
* {@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,123 @@
|
|||
<?php
|
||||
|
||||
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 {
|
||||
|
||||
/**
|
||||
* {inheritdoc}
|
||||
*/
|
||||
protected $defaultCacheContexts = ['languages:language_interface', 'session', 'theme', 'url.path', 'url.query_args', 'user.permissions', 'user.roles:authenticated'];
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
$storage = $this->container->get('entity_type.manager')
|
||||
->getStorage($this->entityTypeId);
|
||||
$storage->resetCache([$this->entityId]);
|
||||
$entity = $storage->load($this->entityId);
|
||||
$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,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,14 @@
|
|||
<?php
|
||||
|
||||
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'
|
|
@ -0,0 +1,143 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\menu_link_content\Kernel;
|
||||
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Core\Menu\MenuTreeParameters;
|
||||
use Drupal\Core\Render\BubbleableMetadata;
|
||||
use Drupal\menu_link_content\Entity\MenuLinkContent;
|
||||
use Drupal\KernelTests\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 bubbleable 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');
|
||||
|
||||
// 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 bubbleable metadata of menu links' outbound route/path processing.
|
||||
*/
|
||||
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 BubbleableMetadata())
|
||||
->setCacheMaxAge(Cache::PERMANENT)
|
||||
->setCacheTags(['config:system.menu.tools'])
|
||||
->setCacheContexts(['languages:' . LanguageInterface::TYPE_INTERFACE, 'theme', 'user.permissions']);
|
||||
|
||||
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 BubbleableMetadata())->setCacheContexts(['route']),
|
||||
],
|
||||
// \Drupal\Core\Access\RouteProcessorCsrf: placeholder.
|
||||
[
|
||||
'uri' => 'route:outbound_processing_test.route.csrf',
|
||||
'cacheability' => (new BubbleableMetadata())->setCacheContexts(['session'])->setAttachments(['placeholders' => []]),
|
||||
],
|
||||
// \Drupal\Core\PathProcessor\PathProcessorFront: permanently cacheable.
|
||||
[
|
||||
'uri' => 'internal:/',
|
||||
'cacheability' => (new BubbleableMetadata()),
|
||||
],
|
||||
// \Drupal\url_alter_test\PathProcessorTest: user entity's cache tags.
|
||||
[
|
||||
'uri' => 'internal:/user/1',
|
||||
'cacheability' => (new BubbleableMetadata())->setCacheTags(User::load(1)->getCacheTags()),
|
||||
],
|
||||
[
|
||||
'uri' => 'internal:/user/2',
|
||||
'cacheability' => (new BubbleableMetadata())->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, BubbleableMetadata::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 BubbleableMetadata();
|
||||
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, BubbleableMetadata::createFromRenderArray($build));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\menu_link_content\Kernel;
|
||||
|
||||
use Drupal\Core\Menu\MenuTreeParameters;
|
||||
use Drupal\Core\StringTranslation\TranslatableMarkup;
|
||||
use Drupal\menu_link_content\Entity\MenuLinkContent;
|
||||
use Drupal\KernelTests\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');
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
$parent = MenuLinkContent::create([
|
||||
'title' => '<script>alert("Welcome to the discovered jungle!")</script>',
|
||||
'link' => [['uri' => 'internal:/example-path']],
|
||||
'menu_name' => 'tools',
|
||||
]);
|
||||
$parent->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());
|
||||
$title = $tree_element->link->getTitle();
|
||||
$this->assertFalse($title instanceof TranslatableMarkup);
|
||||
$this->assertIdentical('<script>alert("Welcome to the discovered jungle!")</script>', $title);
|
||||
|
||||
// Create a hierarchy.
|
||||
\Drupal::state()->set('menu_link_content_dynamic_route.routes', [
|
||||
'route_name_1' => new Route('/example-path'),
|
||||
'route_name_2' => new Route('/example-path/child'),
|
||||
]);
|
||||
$child = MenuLinkContent::create([
|
||||
'title' => 'Child',
|
||||
'link' => [['uri' => 'entity:/example-path/child']],
|
||||
'menu_name' => 'tools',
|
||||
'parent' => 'menu_link_content:' . $parent->uuid(),
|
||||
]);
|
||||
$child->save();
|
||||
$parent->set('link', [['uri' => 'entity:/example-path']]);
|
||||
$parent->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->assertTrue($tree_element->hasChildren);
|
||||
$this->assertEqual(1, count($tree_element->subtree));
|
||||
|
||||
// Edit child element link to use 'internal' instead of 'entity'.
|
||||
$child->set('link', [['uri' => 'internal:/example-path/child']]);
|
||||
$child->save();
|
||||
\Drupal::service('plugin.manager.menu.link')->rebuild();
|
||||
|
||||
$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->assertTrue($tree_element->hasChildren);
|
||||
$this->assertEqual(1, count($tree_element->subtree));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\menu_link_content\Kernel\Migrate;
|
||||
|
||||
use Drupal\Tests\migrate_drupal\Kernel\MigrateDrupalTestBase;
|
||||
use Drupal\migrate_drupal\Tests\StubTestTrait;
|
||||
|
||||
/**
|
||||
* Test stub creation for menu link content entities.
|
||||
*
|
||||
* @group menu_link_content
|
||||
*/
|
||||
class MigrateMenuLinkContentStubTest extends MigrateDrupalTestBase {
|
||||
|
||||
use StubTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['menu_link_content', 'link'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installEntitySchema('menu_link_content');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests creation of menu link content stubs.
|
||||
*/
|
||||
public function testStub() {
|
||||
$this->performStubTest('menu_link_content');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\menu_link_content\Kernel\Migrate\d6;
|
||||
|
||||
use Drupal\menu_link_content\Entity\MenuLinkContent;
|
||||
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
|
||||
|
||||
/**
|
||||
* Menu link migration.
|
||||
*
|
||||
* @group migrate_drupal_6
|
||||
*/
|
||||
class MigrateMenuLinkTest extends MigrateDrupal6TestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = array('menu_ui', 'menu_link_content');
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installEntitySchema('menu_link_content');
|
||||
$this->executeMigrations(['d6_menu', 'd6_menu_links']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests migration of menu links.
|
||||
*/
|
||||
public function testMenuLinks() {
|
||||
$menu_link = MenuLinkContent::load(138);
|
||||
$this->assertIdentical('Test 1', $menu_link->getTitle());
|
||||
$this->assertIdentical('secondary-links', $menu_link->getMenuName());
|
||||
$this->assertIdentical('Test menu link 1', $menu_link->getDescription());
|
||||
$this->assertIdentical(TRUE, $menu_link->isEnabled());
|
||||
$this->assertIdentical(FALSE, $menu_link->isExpanded());
|
||||
$this->assertIdentical(['attributes' => ['title' => 'Test menu link 1']], $menu_link->link->options);
|
||||
$this->assertIdentical('internal:/user/login', $menu_link->link->uri);
|
||||
$this->assertIdentical(-50, $menu_link->getWeight());
|
||||
|
||||
$menu_link = MenuLinkContent::load(139);
|
||||
$this->assertIdentical('Test 2', $menu_link->getTitle());
|
||||
$this->assertIdentical('secondary-links', $menu_link->getMenuName());
|
||||
$this->assertIdentical('Test menu link 2', $menu_link->getDescription());
|
||||
$this->assertIdentical(TRUE, $menu_link->isEnabled());
|
||||
$this->assertIdentical(TRUE, $menu_link->isExpanded());
|
||||
$this->assertIdentical(['query' => 'foo=bar', 'attributes' => ['title' => 'Test menu link 2']], $menu_link->link->options);
|
||||
$this->assertIdentical('internal:/admin', $menu_link->link->uri);
|
||||
$this->assertIdentical(-49, $menu_link->getWeight());
|
||||
|
||||
$menu_link = MenuLinkContent::load(140);
|
||||
$this->assertIdentical('Drupal.org', $menu_link->getTitle());
|
||||
$this->assertIdentical('secondary-links', $menu_link->getMenuName());
|
||||
$this->assertIdentical(NULL, $menu_link->getDescription());
|
||||
$this->assertIdentical(TRUE, $menu_link->isEnabled());
|
||||
$this->assertIdentical(FALSE, $menu_link->isExpanded());
|
||||
$this->assertIdentical(['attributes' => ['title' => '']], $menu_link->link->options);
|
||||
$this->assertIdentical('https://www.drupal.org', $menu_link->link->uri);
|
||||
$this->assertIdentical(-50, $menu_link->getWeight());
|
||||
|
||||
// assert that missing title attributes don't stop or break migration.
|
||||
$menu_link = MenuLinkContent::load(393);
|
||||
$this->assertIdentical('Test 3', $menu_link->getTitle());
|
||||
$this->assertIdentical('secondary-links', $menu_link->getMenuName());
|
||||
$this->assertIdentical(NULL, $menu_link->getDescription());
|
||||
$this->assertIdentical(TRUE, $menu_link->isEnabled());
|
||||
$this->assertIdentical(FALSE, $menu_link->isExpanded());
|
||||
$this->assertIdentical([], $menu_link->link->options);
|
||||
$this->assertIdentical('internal:/user/login', $menu_link->link->uri);
|
||||
$this->assertIdentical(-47, $menu_link->getWeight());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\menu_link_content\Kernel\Migrate\d7;
|
||||
|
||||
use Drupal\Core\Menu\MenuTreeParameters;
|
||||
use Drupal\menu_link_content\Entity\MenuLinkContent;
|
||||
use Drupal\menu_link_content\MenuLinkContentInterface;
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase;
|
||||
|
||||
/**
|
||||
* Menu link migration.
|
||||
*
|
||||
* @group menu_link_content
|
||||
*/
|
||||
class MigrateMenuLinkTest extends MigrateDrupal7TestBase {
|
||||
const MENU_NAME = 'menu-test-menu';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = array('link', 'menu_ui', 'menu_link_content', 'node');
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installEntitySchema('menu_link_content');
|
||||
$this->installEntitySchema('node');
|
||||
$node = Node::create([
|
||||
'nid' => 3,
|
||||
'title' => 'node link test',
|
||||
'type' => 'article',
|
||||
]);
|
||||
$node->save();
|
||||
$this->executeMigrations(['d7_menu', 'd7_menu_links']);
|
||||
\Drupal::service('router.builder')->rebuild();
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts various aspects of a menu link entity.
|
||||
*
|
||||
* @param string $id
|
||||
* The link ID.
|
||||
* @param string $title
|
||||
* The expected title of the link.
|
||||
* @param string $menu
|
||||
* The expected ID of the menu to which the link will belong.
|
||||
* @param string $description
|
||||
* The link's expected description.
|
||||
* @param bool $enabled
|
||||
* Whether the link is enabled.
|
||||
* @param bool $expanded
|
||||
* Whether the link is expanded
|
||||
* @param array $attributes
|
||||
* Additional attributes the link is expected to have.
|
||||
* @param string $uri
|
||||
* The expected URI of the link.
|
||||
* @param int $weight
|
||||
* The expected weight of the link.
|
||||
*
|
||||
* @return \Drupal\menu_link_content\MenuLinkContentInterface
|
||||
* The menu link content.
|
||||
*/
|
||||
protected function assertEntity($id, $title, $menu, $description, $enabled, $expanded, array $attributes, $uri, $weight) {
|
||||
/** @var \Drupal\menu_link_content\MenuLinkContentInterface $menu_link */
|
||||
$menu_link = MenuLinkContent::load($id);
|
||||
$this->assertTrue($menu_link instanceof MenuLinkContentInterface);
|
||||
$this->assertSame($title, $menu_link->getTitle());
|
||||
$this->assertSame($menu, $menu_link->getMenuName());
|
||||
// The migration sets the description of the link to the value of the
|
||||
// 'title' attribute. Bit strange, but there you go.
|
||||
$this->assertSame($description, $menu_link->getDescription());
|
||||
$this->assertSame($enabled, $menu_link->isEnabled());
|
||||
$this->assertSame($expanded, $menu_link->isExpanded());
|
||||
$this->assertSame($attributes, $menu_link->link->options);
|
||||
$this->assertSame($uri, $menu_link->link->uri);
|
||||
$this->assertSame($weight, $menu_link->getWeight());
|
||||
return $menu_link;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests migration of menu links.
|
||||
*/
|
||||
public function testMenuLinks() {
|
||||
$this->assertEntity(469, 'Bing', static::MENU_NAME, 'Bing', TRUE, FALSE, ['attributes' => ['title' => 'Bing']], 'http://bing.com', 0);
|
||||
$this->assertEntity(467, 'Google', static::MENU_NAME, 'Google', TRUE, FALSE, ['attributes' => ['title' => 'Google']], 'http://google.com', 0);
|
||||
$this->assertEntity(468, 'Yahoo', static::MENU_NAME, 'Yahoo', TRUE, FALSE, ['attributes' => ['title' => 'Yahoo']], 'http://yahoo.com', 0);
|
||||
// Tests migrating an external link with an undefined title attribute.
|
||||
$this->assertEntity(470, 'Ask', static::MENU_NAME, NULL, TRUE, FALSE, [], 'http://ask.com', 0);
|
||||
$this->assertEntity(245, 'Home', 'main', NULL, TRUE, FALSE, [], 'internal:/', 0);
|
||||
$this->assertEntity(478, 'custom link test', 'admin', NULL, TRUE, FALSE, ['attributes' => ['title' => '']], 'internal:/admin/content/book', 0);
|
||||
$this->assertEntity(479, 'node link test', 'tools', 'node 3', TRUE, FALSE, ['attributes' => ['title' => 'node 3']], 'entity:node/3', 3);
|
||||
|
||||
$menu_link_tree_service = \Drupal::service('menu.link_tree');
|
||||
$parameters = new MenuTreeParameters();
|
||||
$tree = $menu_link_tree_service->load(static::MENU_NAME, $parameters);
|
||||
$this->assertCount(2, $tree);
|
||||
$children = 0;
|
||||
$google_found = FALSE;
|
||||
foreach ($tree as $menu_link_tree_element) {
|
||||
$children += $menu_link_tree_element->hasChildren;
|
||||
if ($menu_link_tree_element->link->getUrlObject()->toString() == 'http://bing.com') {
|
||||
$this->assertEquals(reset($menu_link_tree_element->subtree)->link->getUrlObject()->toString(), 'http://google.com');
|
||||
$google_found = TRUE;
|
||||
}
|
||||
}
|
||||
$this->assertEquals(1, $children);
|
||||
$this->assertTrue($google_found);
|
||||
// Now find the custom link under a system link.
|
||||
$parameters->root = 'system.admin_structure';
|
||||
$tree = $menu_link_tree_service->load(static::MENU_NAME, $parameters);
|
||||
$found = FALSE;
|
||||
foreach ($tree as $menu_link_tree_element) {
|
||||
$this->assertTrue($menu_link_tree_element->link->getUrlObject()->toString());
|
||||
if ($menu_link_tree_element->link->getTitle() == 'custom link test') {
|
||||
$found = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$this->assertTrue($found);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\menu_link_content\Kernel;
|
||||
|
||||
use Drupal\Core\DependencyInjection\ContainerBuilder;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Core\Menu\MenuTreeParameters;
|
||||
use Drupal\menu_link_content\Entity\MenuLinkContent;
|
||||
use Drupal\KernelTests\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');
|
||||
|
||||
// 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 register(ContainerBuilder $container) {
|
||||
parent::register($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,225 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\menu_link_content\Kernel\Plugin\migrate\source;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Tests\migrate\Kernel\MigrateSqlSourceTestBase;
|
||||
|
||||
/**
|
||||
* Tests the menu link source plugin.
|
||||
*
|
||||
* @covers \Drupal\menu_link_content\Plugin\migrate\source\MenuLink
|
||||
*
|
||||
* @group menu_link_content
|
||||
*/
|
||||
class MenuLinkTest extends MigrateSqlSourceTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['menu_link_content', 'migrate_drupal'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function providerSource() {
|
||||
$tests = [];
|
||||
|
||||
// The source data.
|
||||
$tests[0]['source_data']['menu_links'] = [
|
||||
[
|
||||
// Customized menu link, provided by system module.
|
||||
'menu_name' => 'menu-test-menu',
|
||||
'mlid' => 140,
|
||||
'plid' => 0,
|
||||
'link_path' => 'admin/config/system/cron',
|
||||
'router_path' => 'admin/config/system/cron',
|
||||
'link_title' => 'Cron',
|
||||
'options' => array(),
|
||||
'module' => 'system',
|
||||
'hidden' => 0,
|
||||
'external' => 0,
|
||||
'has_children' => 0,
|
||||
'expanded' => 0,
|
||||
'weight' => 0,
|
||||
'depth' => 0,
|
||||
'customized' => 1,
|
||||
'p1' => '0',
|
||||
'p2' => '0',
|
||||
'p3' => '0',
|
||||
'p4' => '0',
|
||||
'p5' => '0',
|
||||
'p6' => '0',
|
||||
'p7' => '0',
|
||||
'p8' => '0',
|
||||
'p9' => '0',
|
||||
'updated' => '0',
|
||||
'description' => '',
|
||||
],
|
||||
[
|
||||
// D6 customized menu link, provided by menu module.
|
||||
'menu_name' => 'menu-test-menu',
|
||||
'mlid' => 141,
|
||||
'plid' => 0,
|
||||
'link_path' => 'node/141',
|
||||
'router_path' => 'node/%',
|
||||
'link_title' => 'Node 141',
|
||||
'options' => array(),
|
||||
'module' => 'menu',
|
||||
'hidden' => 0,
|
||||
'external' => 0,
|
||||
'has_children' => 0,
|
||||
'expanded' => 0,
|
||||
'weight' => 0,
|
||||
'depth' => 0,
|
||||
'customized' => 1,
|
||||
'p1' => '0',
|
||||
'p2' => '0',
|
||||
'p3' => '0',
|
||||
'p4' => '0',
|
||||
'p5' => '0',
|
||||
'p6' => '0',
|
||||
'p7' => '0',
|
||||
'p8' => '0',
|
||||
'p9' => '0',
|
||||
'updated' => '0',
|
||||
'description' => '',
|
||||
],
|
||||
[
|
||||
// D6 non-customized menu link, provided by menu module.
|
||||
'menu_name' => 'menu-test-menu',
|
||||
'mlid' => 142,
|
||||
'plid' => 0,
|
||||
'link_path' => 'node/142',
|
||||
'router_path' => 'node/%',
|
||||
'link_title' => 'Node 142',
|
||||
'options' => array(),
|
||||
'module' => 'menu',
|
||||
'hidden' => 0,
|
||||
'external' => 0,
|
||||
'has_children' => 0,
|
||||
'expanded' => 0,
|
||||
'weight' => 0,
|
||||
'depth' => 0,
|
||||
'customized' => 0,
|
||||
'p1' => '0',
|
||||
'p2' => '0',
|
||||
'p3' => '0',
|
||||
'p4' => '0',
|
||||
'p5' => '0',
|
||||
'p6' => '0',
|
||||
'p7' => '0',
|
||||
'p8' => '0',
|
||||
'p9' => '0',
|
||||
'updated' => '0',
|
||||
'description' => '',
|
||||
],
|
||||
[
|
||||
'menu_name' => 'menu-test-menu',
|
||||
'mlid' => 138,
|
||||
'plid' => 0,
|
||||
'link_path' => 'admin',
|
||||
'router_path' => 'admin',
|
||||
'link_title' => 'Test 1',
|
||||
'options' => array('attributes' => array('title' => 'Test menu link 1')),
|
||||
'module' => 'menu',
|
||||
'hidden' => 0,
|
||||
'external' => 0,
|
||||
'has_children' => 1,
|
||||
'expanded' => 0,
|
||||
'weight' => 15,
|
||||
'depth' => 1,
|
||||
'customized' => 1,
|
||||
'p1' => '138',
|
||||
'p2' => '0',
|
||||
'p3' => '0',
|
||||
'p4' => '0',
|
||||
'p5' => '0',
|
||||
'p6' => '0',
|
||||
'p7' => '0',
|
||||
'p8' => '0',
|
||||
'p9' => '0',
|
||||
'updated' => '0',
|
||||
'description' => 'Test menu link 1',
|
||||
],
|
||||
[
|
||||
'menu_name' => 'menu-test-menu',
|
||||
'mlid' => 139,
|
||||
'plid' => 138,
|
||||
'link_path' => 'admin/modules',
|
||||
'router_path' => 'admin/modules',
|
||||
'link_title' => 'Test 2',
|
||||
'options' => array('attributes' => array('title' => 'Test menu link 2')),
|
||||
'module' => 'menu',
|
||||
'hidden' => 0,
|
||||
'external' => 0,
|
||||
'has_children' => 0,
|
||||
'expanded' => 0,
|
||||
'weight' => 12,
|
||||
'depth' => 2,
|
||||
'customized' => 1,
|
||||
'p1' => '138',
|
||||
'p2' => '139',
|
||||
'p3' => '0',
|
||||
'p4' => '0',
|
||||
'p5' => '0',
|
||||
'p6' => '0',
|
||||
'p7' => '0',
|
||||
'p8' => '0',
|
||||
'p9' => '0',
|
||||
'updated' => '0',
|
||||
'description' => 'Test menu link 2',
|
||||
],
|
||||
[
|
||||
'menu_name' => 'menu-user',
|
||||
'mlid' => 143,
|
||||
'plid' => 0,
|
||||
'link_path' => 'admin/build/menu-customize/navigation',
|
||||
'router_path' => 'admin/build/menu-customize/%',
|
||||
'link_title' => 'Navigation',
|
||||
'options' => array(),
|
||||
'module' => 'menu',
|
||||
'hidden' => 0,
|
||||
'external' => 0,
|
||||
'has_children' => 0,
|
||||
'expanded' => 0,
|
||||
'weight' => 0,
|
||||
'depth' => 0,
|
||||
'customized' => 0,
|
||||
'p1' => '0',
|
||||
'p2' => '0',
|
||||
'p3' => '0',
|
||||
'p4' => '0',
|
||||
'p5' => '0',
|
||||
'p6' => '0',
|
||||
'p7' => '0',
|
||||
'p8' => '0',
|
||||
'p9' => '0',
|
||||
'updated' => '0',
|
||||
'description' => '',
|
||||
],
|
||||
];
|
||||
|
||||
// Add long link title attributes to source data.
|
||||
$title = $this->getRandomGenerator()->string('500');
|
||||
$tests[0]['source_data']['menu_links'][0]['options']['attributes']['title'] = $title;
|
||||
|
||||
// Build the expected results.
|
||||
$expected = $tests[0]['source_data']['menu_links'];
|
||||
|
||||
// Add long link title attributes to expected results.
|
||||
$expected[0]['description'] = Unicode::truncate($title, 255);
|
||||
|
||||
// Don't expect D6 menu link to a custom menu, provided by menu module.
|
||||
unset($expected[5]);
|
||||
|
||||
array_walk($tests[0]['source_data']['menu_links'], function (&$row) {
|
||||
$row['options'] = serialize($row['options']);
|
||||
});
|
||||
|
||||
$tests[0]['expected_data'] = $expected;
|
||||
|
||||
return $tests;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\menu_link_content\Unit\Plugin\migrate\process;
|
||||
|
||||
use Drupal\Core\DependencyInjection\ContainerBuilder;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\menu_link_content\Plugin\migrate\process\LinkUri;
|
||||
use Drupal\migrate\MigrateExecutableInterface;
|
||||
use Drupal\migrate\Row;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Path\PathValidator;
|
||||
|
||||
/**
|
||||
* Tests \Drupal\menu_link_content\Plugin\migrate\process\LinkUri.
|
||||
*
|
||||
* @group menu_link_content
|
||||
*
|
||||
* @coversDefaultClass \Drupal\menu_link_content\Plugin\migrate\process\LinkUri
|
||||
*/
|
||||
class LinkUriTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* The entity type manager prophecy used in the test.
|
||||
*
|
||||
* @var \Prophecy\Prophecy\ProphecyInterface|\Drupal\Core\Entity\EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* The 'link_uri' process plugin being tested.
|
||||
*
|
||||
* @var \Drupal\menu_link_content\Plugin\migrate\process\LinkUri
|
||||
*/
|
||||
protected $processPlugin;
|
||||
|
||||
/**
|
||||
* The path validator prophecy used in the test.
|
||||
*
|
||||
* @var \Drupal\Core\Path\PathValidator
|
||||
*/
|
||||
protected $pathValidator;
|
||||
|
||||
/**
|
||||
* The fake entity type ID used in the test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $entityTypeId = 'the_entity_type_id';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->entityTypeManager = $this->prophesize(EntityTypeManagerInterface::class);
|
||||
$this->entityTypeManager->getDefinitions()->willReturn([$this->entityTypeId => '']);
|
||||
$this->processPlugin = new LinkUri([], 'link_uri', [], $this->entityTypeManager->reveal());
|
||||
|
||||
// Url::fromInternalUri() accesses the path validator from the global
|
||||
// container.
|
||||
// @see \Drupal\Core\Url::fromInternalUri()
|
||||
$this->pathValidator = $this->prophesize(PathValidator::class);
|
||||
$container = new ContainerBuilder();
|
||||
$container->set('path.validator', $this->pathValidator->reveal());
|
||||
\Drupal::setContainer($container);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests LinkUri::transform().
|
||||
*
|
||||
* @param array $value
|
||||
* The value to pass to LinkUri::transform().
|
||||
* @param string $expected
|
||||
* The expected return value of LinkUri::transform().
|
||||
* @param \Drupal\Core\Url $url
|
||||
* (optional) The URL that the path validator prophecy will return.
|
||||
*
|
||||
* @dataProvider providerTestTransform
|
||||
*
|
||||
* @covers ::transform
|
||||
*/
|
||||
public function testTransform(array $value, $expected, Url $url = NULL) {
|
||||
$migrate_executable = $this->prophesize(MigrateExecutableInterface::class);
|
||||
$row = $this->prophesize(Row::class);
|
||||
|
||||
if ($url) {
|
||||
$this->pathValidator->getUrlIfValidWithoutAccessCheck(reset($value))->willReturn($url);
|
||||
}
|
||||
|
||||
$actual = $this->processPlugin->transform($value, $migrate_executable->reveal(), $row->reveal(), 'link/uri');
|
||||
$this->assertEquals($expected, $actual);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides test cases for LinkUriTest::testTransform().
|
||||
*
|
||||
* @return array
|
||||
* An array of test cases, each which the following values:
|
||||
* - The value array to pass to LinkUri::transform().
|
||||
* - The expected path returned by LinkUri::transform().
|
||||
* - (optional) A URL object that the path validator prophecy will return.
|
||||
*/
|
||||
public function providerTestTransform() {
|
||||
$tests = [];
|
||||
|
||||
$value = ['http://example.com'];
|
||||
$expected = 'http://example.com';
|
||||
$tests['with_scheme'] = [$value, $expected];
|
||||
|
||||
$value = ['/test'];
|
||||
$expected = 'internal:/test';
|
||||
$tests['leading_slash'] = [$value, $expected];
|
||||
|
||||
$value = ['test'];
|
||||
$expected = 'internal:/test';
|
||||
$tests['without_scheme'] = [$value, $expected];
|
||||
|
||||
$value = ['<front>'];
|
||||
$expected = 'internal:/';
|
||||
$tests['front'] = [$value, $expected];
|
||||
|
||||
$url = Url::fromRoute('route_name');
|
||||
$tests['with_route'] = [$value, $expected, $url];
|
||||
|
||||
$url = Url::fromRoute('entity.not_an_entity_type_id.canonical');
|
||||
$tests['without_entity_type'] = [$value, $expected, $url];
|
||||
|
||||
$url = Url::fromRoute('entity.the_entity_type_id.canonical');
|
||||
$tests['without_route_parameter'] = [$value, $expected, $url];
|
||||
|
||||
$url = Url::fromRoute('entity.the_entity_type_id.canonical', ['the_entity_type_id' => 'the_entity_id']);
|
||||
$expected = 'entity:the_entity_type_id/the_entity_id';
|
||||
$tests['entity_path'] = [$value, $expected, $url];
|
||||
|
||||
return $tests;
|
||||
}
|
||||
|
||||
}
|
Reference in a new issue