Issue #2417423 by dawehner, kgoel, pwolanin, larowlan: Re-process the user-entered-paths for custom menu links when there is a menu rebuild
parent
24bfa759c7
commit
a2ad34879c
|
|
@ -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
|
||||||
|
|
@ -11,7 +11,6 @@ use Drupal\Core\Entity\ContentEntityBase;
|
||||||
use Drupal\Core\Entity\EntityStorageInterface;
|
use Drupal\Core\Entity\EntityStorageInterface;
|
||||||
use Drupal\Core\Entity\EntityTypeInterface;
|
use Drupal\Core\Entity\EntityTypeInterface;
|
||||||
use Drupal\Core\Field\BaseFieldDefinition;
|
use Drupal\Core\Field\BaseFieldDefinition;
|
||||||
use Drupal\Core\Url;
|
|
||||||
use Drupal\link\LinkItemInterface;
|
use Drupal\link\LinkItemInterface;
|
||||||
use Drupal\menu_link_content\MenuLinkContentInterface;
|
use Drupal\menu_link_content\MenuLinkContentInterface;
|
||||||
|
|
||||||
|
|
@ -19,12 +18,14 @@ use Drupal\menu_link_content\MenuLinkContentInterface;
|
||||||
* Defines the menu link content entity class.
|
* Defines the menu link content entity class.
|
||||||
*
|
*
|
||||||
* @property \Drupal\link\LinkItemInterface link
|
* @property \Drupal\link\LinkItemInterface link
|
||||||
|
* @property \Drupal\Core\Field\FieldItemList rediscover
|
||||||
*
|
*
|
||||||
* @ContentEntityType(
|
* @ContentEntityType(
|
||||||
* id = "menu_link_content",
|
* id = "menu_link_content",
|
||||||
* label = @Translation("Custom menu link"),
|
* label = @Translation("Custom menu link"),
|
||||||
* handlers = {
|
* handlers = {
|
||||||
* "storage" = "Drupal\Core\Entity\Sql\SqlContentEntityStorage",
|
* "storage" = "Drupal\Core\Entity\Sql\SqlContentEntityStorage",
|
||||||
|
* "storage_schema" = "Drupal\menu_link_content\MenuLinkContentStorageSchema",
|
||||||
* "access" = "Drupal\menu_link_content\MenuLinkContentAccessControlHandler",
|
* "access" = "Drupal\menu_link_content\MenuLinkContentAccessControlHandler",
|
||||||
* "form" = {
|
* "form" = {
|
||||||
* "default" = "Drupal\menu_link_content\Form\MenuLinkContentForm",
|
* "default" = "Drupal\menu_link_content\Form\MenuLinkContentForm",
|
||||||
|
|
@ -179,6 +180,19 @@ class MenuLinkContent extends ContentEntityBase implements MenuLinkContentInterf
|
||||||
$values += ['bundle' => 'menu_link_content'];
|
$values += ['bundle' => 'menu_link_content'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function preSave(EntityStorageInterface $storage) {
|
||||||
|
parent::preSave($storage);
|
||||||
|
|
||||||
|
if (parse_url($this->link->uri, PHP_URL_SCHEME) === 'user-path') {
|
||||||
|
$this->setRequiresRediscovery(TRUE);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$this->setRequiresRediscovery(FALSE);
|
||||||
|
}
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
|
|
@ -297,6 +311,10 @@ class MenuLinkContent extends ContentEntityBase implements MenuLinkContentInterf
|
||||||
->setDescription(t('A flag to indicate if the link points to a full URL starting with a protocol, like http:// (1 = external, 0 = internal).'))
|
->setDescription(t('A flag to indicate if the link points to a full URL starting with a protocol, like http:// (1 = external, 0 = internal).'))
|
||||||
->setSetting('default_value', FALSE);
|
->setSetting('default_value', FALSE);
|
||||||
|
|
||||||
|
$fields['rediscover'] = BaseFieldDefinition::create('boolean')
|
||||||
|
->setLabel(t('Indicates whether the menu link should be rediscovered'))
|
||||||
|
->setSetting('default_value', FALSE);
|
||||||
|
|
||||||
$fields['weight'] = BaseFieldDefinition::create('integer')
|
$fields['weight'] = BaseFieldDefinition::create('integer')
|
||||||
->setLabel(t('Weight'))
|
->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.'))
|
->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.'))
|
||||||
|
|
@ -361,4 +379,19 @@ class MenuLinkContent extends ContentEntityBase implements MenuLinkContentInterf
|
||||||
return $fields;
|
return $fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function requiresRediscovery() {
|
||||||
|
return $this->get('rediscover')->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function setRequiresRediscovery($rediscovery) {
|
||||||
|
$this->set('rediscover', $rediscovery);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -102,4 +102,35 @@ interface MenuLinkContentInterface extends ContentEntityInterface, EntityChanged
|
||||||
*/
|
*/
|
||||||
public function getPluginDefinition();
|
public function getPluginDefinition();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the menu link requires rediscovery.
|
||||||
|
*
|
||||||
|
* If a menu-link points to a user-supplied path such as /blog then the route
|
||||||
|
* this resolves to needs to be rediscovered as the module or route providing
|
||||||
|
* a given path might change over time.
|
||||||
|
*
|
||||||
|
* For example: at the time a menu-link is created, the /blog path might be
|
||||||
|
* provided by a route in Views module, but later this path may be served by
|
||||||
|
* the Panels module. Flagging a link as requiring rediscovery ensures that if
|
||||||
|
* the route that provides a user-entered path changes over time, the link is
|
||||||
|
* flexible enough to update to reflect these changes.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* TRUE if the menu link requires rediscovery during route rebuilding.
|
||||||
|
*/
|
||||||
|
public function requiresRediscovery();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flags a link as requiring rediscovery.
|
||||||
|
*
|
||||||
|
* @param bool $rediscovery
|
||||||
|
* Whether or not the link requires rediscovery.
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
* The instance on which the method was called.
|
||||||
|
*
|
||||||
|
* @see \Drupal\menu_link_content\MenuLinkContentInterface::requiresRediscovery()
|
||||||
|
*/
|
||||||
|
public function setRequiresRediscovery($rediscovery);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Contains \Drupal\menu_link_content\MenuLinkContentStorageSchema.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Drupal\menu_link_content;
|
||||||
|
|
||||||
|
use Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema;
|
||||||
|
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines the menu_link_content schema handler.
|
||||||
|
*/
|
||||||
|
class MenuLinkContentStorageSchema extends SqlContentEntityStorageSchema {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function getSharedTableFieldSchema(FieldStorageDefinitionInterface $storage_definition, $table_name, array $column_mapping) {
|
||||||
|
$schema = parent::getSharedTableFieldSchema($storage_definition, $table_name, $column_mapping);
|
||||||
|
$field_name = $storage_definition->getName();
|
||||||
|
|
||||||
|
if ($table_name == 'menu_link_content') {
|
||||||
|
switch ($field_name) {
|
||||||
|
case 'rediscover':
|
||||||
|
$this->addSharedTableFieldIndex($storage_definition, $schema, TRUE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $schema;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,91 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Contains \Drupal\menu_link_content\Plugin\Deriver\MenuLinkContentDeriver.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Drupal\menu_link_content\Plugin\Deriver;
|
||||||
|
|
||||||
|
use Drupal\Component\Plugin\Derivative\DeriverBase;
|
||||||
|
use Drupal\Core\Entity\EntityManagerInterface;
|
||||||
|
use Drupal\Core\Entity\Query\QueryFactory;
|
||||||
|
use Drupal\Core\Menu\MenuLinkManagerInterface;
|
||||||
|
use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
|
||||||
|
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides a deriver for user entered paths of menu links.
|
||||||
|
*
|
||||||
|
* The assumption is that the number of manually entered menu links are lower
|
||||||
|
* compared to entity referenced ones.
|
||||||
|
*/
|
||||||
|
class MenuLinkContentDeriver extends DeriverBase implements ContainerDeriverInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The query factory.
|
||||||
|
*
|
||||||
|
* @var \Drupal\Core\Entity\Query\QueryFactory
|
||||||
|
*/
|
||||||
|
protected $queryFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The entity manager.
|
||||||
|
*
|
||||||
|
* @var \Drupal\Core\Entity\EntityManagerInterface
|
||||||
|
*/
|
||||||
|
protected $entityManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The menu link manager.
|
||||||
|
*
|
||||||
|
* @var \Drupal\Core\Menu\MenuLinkManagerInterface
|
||||||
|
*/
|
||||||
|
protected $menuLinkManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a MenuLinkContentDeriver instance.
|
||||||
|
*
|
||||||
|
* @param \Drupal\Core\Entity\Query\QueryFactory $query_factory
|
||||||
|
* The query factory.
|
||||||
|
*
|
||||||
|
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
|
||||||
|
* The entity manager.
|
||||||
|
* @param \Drupal\Core\Menu\MenuLinkManagerInterface $menu_link_manager
|
||||||
|
* The menu link manager.
|
||||||
|
*/
|
||||||
|
public function __construct(QueryFactory $query_factory, EntityManagerInterface $entity_manager, MenuLinkManagerInterface $menu_link_manager) {
|
||||||
|
$this->queryFactory = $query_factory;
|
||||||
|
$this->entityManager = $entity_manager;
|
||||||
|
$this->menuLinkManager = $menu_link_manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public static function create(ContainerInterface $container, $base_plugin_id) {
|
||||||
|
return new static(
|
||||||
|
$container->get('entity.query'),
|
||||||
|
$container->get('entity.manager'),
|
||||||
|
$container->get('plugin.manager.menu.link')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getDerivativeDefinitions($base_plugin_definition) {
|
||||||
|
// Get all custom menu links which should be rediscovered.
|
||||||
|
$entity_ids = $this->queryFactory->get('menu_link_content')
|
||||||
|
->condition('rediscover', TRUE)
|
||||||
|
->execute();
|
||||||
|
$plugin_definitions = [];
|
||||||
|
$menu_link_content_entities = $this->entityManager->getStorage('menu_link_content')->loadMultiple($entity_ids);
|
||||||
|
/** @var \Drupal\menu_link_content\MenuLinkContentInterface $menu_link_content */
|
||||||
|
foreach ($menu_link_content_entities as $menu_link_content) {
|
||||||
|
$plugin_definitions[$menu_link_content->uuid()] = $menu_link_content->getPluginDefinition();
|
||||||
|
}
|
||||||
|
return $plugin_definitions;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Contains \Drupal\menu_link_content\Tests\MenuLinkContentDeriverTest.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Drupal\menu_link_content\Tests;
|
||||||
|
|
||||||
|
use Drupal\Core\Menu\MenuTreeParameters;
|
||||||
|
use Drupal\menu_link_content\Entity\MenuLinkContent;
|
||||||
|
use Drupal\simpletest\KernelTestBase;
|
||||||
|
use Symfony\Component\Routing\Route;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the menu link content deriver.
|
||||||
|
*
|
||||||
|
* @group menu_link_content
|
||||||
|
*/
|
||||||
|
class MenuLinkContentDeriverTest extends KernelTestBase {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public static $modules = ['menu_link_content', 'link', 'system', 'menu_link_content_dynamic_route'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function setUp() {
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->installEntitySchema('menu_link_content');
|
||||||
|
$this->installSchema('system', 'router');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the rediscovering.
|
||||||
|
*/
|
||||||
|
public function testRediscover() {
|
||||||
|
\Drupal::state()->set('menu_link_content_dynamic_route.routes', [
|
||||||
|
'route_name_1' => new Route('/example-path'),
|
||||||
|
]);
|
||||||
|
\Drupal::service('router.builder')->rebuild();
|
||||||
|
|
||||||
|
// Set up a custom menu link pointing to a specific path.
|
||||||
|
MenuLinkContent::create([
|
||||||
|
'title' => 'Example',
|
||||||
|
'link' => [['uri' => 'user-path:example-path']],
|
||||||
|
'menu_name' => 'tools',
|
||||||
|
])->save();
|
||||||
|
$menu_tree = \Drupal::menuTree()->load('tools', new MenuTreeParameters());
|
||||||
|
$this->assertEqual(1, count($menu_tree));
|
||||||
|
/** @var \Drupal\Core\Menu\MenuLinkTreeElement $tree_element */
|
||||||
|
$tree_element = reset($menu_tree);
|
||||||
|
$this->assertEqual('route_name_1', $tree_element->link->getRouteName());
|
||||||
|
|
||||||
|
// Change the underlying route and trigger the rediscovering.
|
||||||
|
\Drupal::state()->set('menu_link_content_dynamic_route.routes', [
|
||||||
|
'route_name_2' => new Route('/example-path'),
|
||||||
|
]);
|
||||||
|
\Drupal::service('router.builder')->rebuild();
|
||||||
|
|
||||||
|
// Ensure that the new route name / parameters are captured by the tree.
|
||||||
|
$menu_tree = \Drupal::menuTree()->load('tools', new MenuTreeParameters());
|
||||||
|
$this->assertEqual(1, count($menu_tree));
|
||||||
|
/** @var \Drupal\Core\Menu\MenuLinkTreeElement $tree_element */
|
||||||
|
$tree_element = reset($menu_tree);
|
||||||
|
$this->assertEqual('route_name_2', $tree_element->link->getRouteName());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
name: 'Menu link content dynamic route'
|
||||||
|
type: module
|
||||||
|
core: 8.x
|
||||||
|
hidden: true
|
||||||
|
dependencies:
|
||||||
|
- menu_link_content
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
route_callbacks:
|
||||||
|
- 'Drupal\menu_link_content_dynamic_route\Routes::dynamic'
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Contains \Drupal\menu_link_content_dynamic_route\Routes.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Drupal\menu_link_content_dynamic_route;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides dynamic routes for test purposes.
|
||||||
|
*/
|
||||||
|
class Routes {
|
||||||
|
|
||||||
|
public function dynamic() {
|
||||||
|
return \Drupal::state()->get('menu_link_content_dynamic_route.routes', []);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue