From a2ad34879c5fba1e6b30bd9c475886ec23be2a94 Mon Sep 17 00:00:00 2001 From: webchick Date: Sun, 1 Feb 2015 18:40:36 -0800 Subject: [PATCH] Issue #2417423 by dawehner, kgoel, pwolanin, larowlan: Re-process the user-entered-paths for custom menu links when there is a menu rebuild --- .../menu_link_content.links.menu.yml | 4 + .../src/Entity/MenuLinkContent.php | 35 ++++++- .../src/MenuLinkContentInterface.php | 31 +++++++ .../src/MenuLinkContentStorageSchema.php | 36 ++++++++ .../Plugin/Deriver/MenuLinkContentDeriver.php | 91 +++++++++++++++++++ .../src/Tests/MenuLinkContentDeriverTest.php | 72 +++++++++++++++ .../menu_link_content_dynamic_route.info.yml | 6 ++ ...enu_link_content_dynamic_route.routing.yml | 2 + .../src/Routes.php | 19 ++++ 9 files changed, 295 insertions(+), 1 deletion(-) create mode 100644 core/modules/menu_link_content/menu_link_content.links.menu.yml create mode 100644 core/modules/menu_link_content/src/MenuLinkContentStorageSchema.php create mode 100644 core/modules/menu_link_content/src/Plugin/Deriver/MenuLinkContentDeriver.php create mode 100644 core/modules/menu_link_content/src/Tests/MenuLinkContentDeriverTest.php create mode 100644 core/modules/menu_link_content/tests/menu_link_content_dynamic_route/menu_link_content_dynamic_route.info.yml create mode 100644 core/modules/menu_link_content/tests/menu_link_content_dynamic_route/menu_link_content_dynamic_route.routing.yml create mode 100644 core/modules/menu_link_content/tests/menu_link_content_dynamic_route/src/Routes.php diff --git a/core/modules/menu_link_content/menu_link_content.links.menu.yml b/core/modules/menu_link_content/menu_link_content.links.menu.yml new file mode 100644 index 000000000000..9f4fed206ca1 --- /dev/null +++ b/core/modules/menu_link_content/menu_link_content.links.menu.yml @@ -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 diff --git a/core/modules/menu_link_content/src/Entity/MenuLinkContent.php b/core/modules/menu_link_content/src/Entity/MenuLinkContent.php index c9a7fca96094..3ae1f4426b25 100644 --- a/core/modules/menu_link_content/src/Entity/MenuLinkContent.php +++ b/core/modules/menu_link_content/src/Entity/MenuLinkContent.php @@ -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; + } + } diff --git a/core/modules/menu_link_content/src/MenuLinkContentInterface.php b/core/modules/menu_link_content/src/MenuLinkContentInterface.php index 650b3d297da8..d2b825474908 100644 --- a/core/modules/menu_link_content/src/MenuLinkContentInterface.php +++ b/core/modules/menu_link_content/src/MenuLinkContentInterface.php @@ -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); + } diff --git a/core/modules/menu_link_content/src/MenuLinkContentStorageSchema.php b/core/modules/menu_link_content/src/MenuLinkContentStorageSchema.php new file mode 100644 index 000000000000..96d39b93f77c --- /dev/null +++ b/core/modules/menu_link_content/src/MenuLinkContentStorageSchema.php @@ -0,0 +1,36 @@ +getName(); + + if ($table_name == 'menu_link_content') { + switch ($field_name) { + case 'rediscover': + $this->addSharedTableFieldIndex($storage_definition, $schema, TRUE); + break; + } + } + + return $schema; + } + +} diff --git a/core/modules/menu_link_content/src/Plugin/Deriver/MenuLinkContentDeriver.php b/core/modules/menu_link_content/src/Plugin/Deriver/MenuLinkContentDeriver.php new file mode 100644 index 000000000000..76c2d330b2a1 --- /dev/null +++ b/core/modules/menu_link_content/src/Plugin/Deriver/MenuLinkContentDeriver.php @@ -0,0 +1,91 @@ +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; + } + +} diff --git a/core/modules/menu_link_content/src/Tests/MenuLinkContentDeriverTest.php b/core/modules/menu_link_content/src/Tests/MenuLinkContentDeriverTest.php new file mode 100644 index 000000000000..d18a0923fcbf --- /dev/null +++ b/core/modules/menu_link_content/src/Tests/MenuLinkContentDeriverTest.php @@ -0,0 +1,72 @@ +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()); + } + +} diff --git a/core/modules/menu_link_content/tests/menu_link_content_dynamic_route/menu_link_content_dynamic_route.info.yml b/core/modules/menu_link_content/tests/menu_link_content_dynamic_route/menu_link_content_dynamic_route.info.yml new file mode 100644 index 000000000000..fa9a0fb53bbd --- /dev/null +++ b/core/modules/menu_link_content/tests/menu_link_content_dynamic_route/menu_link_content_dynamic_route.info.yml @@ -0,0 +1,6 @@ +name: 'Menu link content dynamic route' +type: module +core: 8.x +hidden: true +dependencies: + - menu_link_content diff --git a/core/modules/menu_link_content/tests/menu_link_content_dynamic_route/menu_link_content_dynamic_route.routing.yml b/core/modules/menu_link_content/tests/menu_link_content_dynamic_route/menu_link_content_dynamic_route.routing.yml new file mode 100644 index 000000000000..959cb09e7f6a --- /dev/null +++ b/core/modules/menu_link_content/tests/menu_link_content_dynamic_route/menu_link_content_dynamic_route.routing.yml @@ -0,0 +1,2 @@ +route_callbacks: + - 'Drupal\menu_link_content_dynamic_route\Routes::dynamic' diff --git a/core/modules/menu_link_content/tests/menu_link_content_dynamic_route/src/Routes.php b/core/modules/menu_link_content/tests/menu_link_content_dynamic_route/src/Routes.php new file mode 100644 index 000000000000..979790f180ac --- /dev/null +++ b/core/modules/menu_link_content/tests/menu_link_content_dynamic_route/src/Routes.php @@ -0,0 +1,19 @@ +get('menu_link_content_dynamic_route.routes', []); + } + +}