Issue #2417423 by dawehner, kgoel, pwolanin, larowlan: Re-process the user-entered-paths for custom menu links when there is a menu rebuild

8.0.x
webchick 2015-02-01 18:40:36 -08:00
parent 24bfa759c7
commit a2ad34879c
9 changed files with 295 additions and 1 deletions

View File

@ -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

View File

@ -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;
}
} }

View File

@ -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);
} }

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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());
}
}

View File

@ -0,0 +1,6 @@
name: 'Menu link content dynamic route'
type: module
core: 8.x
hidden: true
dependencies:
- menu_link_content

View File

@ -0,0 +1,2 @@
route_callbacks:
- 'Drupal\menu_link_content_dynamic_route\Routes::dynamic'

View File

@ -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', []);
}
}