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\EntityTypeInterface;
|
||||
use Drupal\Core\Field\BaseFieldDefinition;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\link\LinkItemInterface;
|
||||
use Drupal\menu_link_content\MenuLinkContentInterface;
|
||||
|
||||
|
|
@ -19,12 +18,14 @@ 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",
|
||||
|
|
@ -179,6 +180,19 @@ class MenuLinkContent extends ContentEntityBase implements MenuLinkContentInterf
|
|||
$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}
|
||||
*/
|
||||
|
|
@ -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).'))
|
||||
->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')
|
||||
->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.'))
|
||||
|
|
@ -361,4 +379,19 @@ class MenuLinkContent extends ContentEntityBase implements MenuLinkContentInterf
|
|||
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();
|
||||
|
||||
/**
|
||||
* 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