From c3e6b5f1fd03995fcd70014e789bc3c1533fcf62 Mon Sep 17 00:00:00 2001 From: Kristiaan Van den Eynde Date: Fri, 21 Sep 2018 12:07:33 +0200 Subject: [PATCH] Issue #2651974 by kristiaanvandeneynde: field_ui_entity_operation() cannot respect route parameters because of incorrectly named routes --- core/lib/Drupal/Core/Entity/Entity.php | 2 +- core/lib/Drupal/Core/Entity/EntityType.php | 17 +++++- core/modules/field_ui/field_ui.module | 38 +++++++++---- core/modules/field_ui/field_ui.services.yml | 4 ++ .../src/Routing/DeprecatedRouteMatcher.php | 55 +++++++++++++++++++ .../field_ui/src/Routing/RouteSubscriber.php | 15 ++++- .../Listeners/DeprecationListenerTrait.php | 1 + 7 files changed, 119 insertions(+), 13 deletions(-) create mode 100644 core/modules/field_ui/src/Routing/DeprecatedRouteMatcher.php diff --git a/core/lib/Drupal/Core/Entity/Entity.php b/core/lib/Drupal/Core/Entity/Entity.php index 84991bc609..7e4592a5db 100644 --- a/core/lib/Drupal/Core/Entity/Entity.php +++ b/core/lib/Drupal/Core/Entity/Entity.php @@ -318,7 +318,7 @@ protected function urlRouteParameters($rel) { // The entity ID is needed as a route parameter. $uri_route_parameters[$this->getEntityTypeId()] = $this->id(); } - if ($rel === 'add-form' && ($this->getEntityType()->hasKey('bundle'))) { + if (($rel === 'add-form' || strpos('drupal:field-ui-', $rel) === 0) && ($this->getEntityType()->hasKey('bundle'))) { $parameter_name = $this->getEntityType()->getBundleEntityType() ?: $this->getEntityType()->getKey('bundle'); $uri_route_parameters[$parameter_name] = $this->bundle(); } diff --git a/core/lib/Drupal/Core/Entity/EntityType.php b/core/lib/Drupal/Core/Entity/EntityType.php index c6f320fd6e..fc3edcbfaa 100644 --- a/core/lib/Drupal/Core/Entity/EntityType.php +++ b/core/lib/Drupal/Core/Entity/EntityType.php @@ -241,7 +241,22 @@ class EntityType extends PluginDefinition implements EntityTypeInterface { protected $group_label; /** - * The route name used by field UI to attach its management pages. + * The path used by field UI to create its management pages routes with. + * + * @todo From Drupal 9.0.0 onwards, this should be used to detect whether an + * entity type supports Field UI. + * + * @var string + */ + protected $field_ui_base_path; + + /** + * The route name used by field UI to attach its management pages local tasks. + * + * The path for this route should be the field_ui_base_path value. + * + * @todo From Drupal 9.0.0 onwards, this should only be used to detect whether + * an entity type supports Field UI local tasks (tabs). * * @var string */ diff --git a/core/modules/field_ui/field_ui.module b/core/modules/field_ui/field_ui.module index 79b3f151a5..a6a7fa1b57 100644 --- a/core/modules/field_ui/field_ui.module +++ b/core/modules/field_ui/field_ui.module @@ -10,7 +10,6 @@ use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Entity\EntityViewModeInterface; use Drupal\Core\Entity\EntityFormModeInterface; -use Drupal\Core\Url; use Drupal\field_ui\FieldUI; use Drupal\field_ui\Plugin\Derivative\FieldUiLocalTask; @@ -105,6 +104,31 @@ function field_ui_entity_type_build(array &$entity_types) { $view_mode->setLinkTemplate('collection', '/admin/structure/display-modes/view'); } +/** + * Implements hook_entity_type_alter(). + */ +function field_ui_entity_type_alter(array &$entity_types) { + /** @var $entity_types \Drupal\Core\Entity\EntityTypeInterface[] */ + foreach ($entity_types as $entity_type) { + // Add manage fields and display link relations and templates if this entity + // type has field UI enabled. + $base_path = $entity_type->get('field_ui_base_path'); + if (!$base_path) { + continue; + } + + // If the entity type uses another entity type as its bundle, we need to + // set the Field UI link templates on the bundle entity type instead. + $target_entity_type = ($bundle_entity = $entity_type->getBundleEntityType()) + ? $entity_types[$bundle_entity] + : $entity_type; + + $target_entity_type->setLinkTemplate('drupal:field-ui-fields', $base_path . '/fields'); + $target_entity_type->setLinkTemplate('drupal:field-ui-form-display', $base_path . '/form-display'); + $target_entity_type->setLinkTemplate('drupal:field-ui-display', $base_path . '/display'); + } +} + /** * Implements hook_entity_bundle_create(). */ @@ -148,27 +172,21 @@ function field_ui_entity_operation(EntityInterface $entity) { $operations['manage-fields'] = [ 'title' => t('Manage fields'), 'weight' => 15, - 'url' => Url::fromRoute("entity.{$bundle_of}.field_ui_fields", [ - $entity->getEntityTypeId() => $entity->id(), - ]), + 'url' => $entity->toUrl('drupal:field-ui-fields'), ]; } if ($account->hasPermission('administer ' . $bundle_of . ' form display')) { $operations['manage-form-display'] = [ 'title' => t('Manage form display'), 'weight' => 20, - 'url' => Url::fromRoute("entity.entity_form_display.{$bundle_of}.default", [ - $entity->getEntityTypeId() => $entity->id(), - ]), + 'url' => $entity->toUrl('drupal:field-ui-form-display'), ]; } if ($account->hasPermission('administer ' . $bundle_of . ' display')) { $operations['manage-display'] = [ 'title' => t('Manage display'), 'weight' => 25, - 'url' => Url::fromRoute("entity.entity_view_display.{$bundle_of}.default", [ - $entity->getEntityTypeId() => $entity->id(), - ]), + 'url' => $entity->toUrl('drupal:field-ui-display'), ]; } } diff --git a/core/modules/field_ui/field_ui.services.yml b/core/modules/field_ui/field_ui.services.yml index f6744eb1ba..ab62ed7a72 100644 --- a/core/modules/field_ui/field_ui.services.yml +++ b/core/modules/field_ui/field_ui.services.yml @@ -19,3 +19,7 @@ services: arguments: ['@entity.manager'] tags: - { name: access_check, applies_to: _field_ui_form_mode_access } + field_ui.deprecated_route_matcher: + class: Drupal\field_ui\Routing\DeprecatedRouteMatcher + tags: + - { name: route_filter } diff --git a/core/modules/field_ui/src/Routing/DeprecatedRouteMatcher.php b/core/modules/field_ui/src/Routing/DeprecatedRouteMatcher.php new file mode 100644 index 0000000000..9aa29402ce --- /dev/null +++ b/core/modules/field_ui/src/Routing/DeprecatedRouteMatcher.php @@ -0,0 +1,55 @@ + $route) { + if ($entity_type_id = $route->getDefault('entity_type_id')) { + if ($this->isBackwardsIncompatibleRouteName($route_name, $entity_type_id)) { + $collection->remove($route_name); + } + } + } + if (count($collection)) { + return $collection; + } + throw new NotAcceptableHttpException('No route remaining after filtering out backwards incompatible Field UI routes.'); + } + + /** + * Checks whether a route name is a newer, backwards incompatible version. + * + * @param string $route_name + * The route name to check. + * @param string $entity_type_id + * The entity type ID used by the route. + * + * @return bool + * Whether the route name is the newer version. + */ + protected function isBackwardsIncompatibleRouteName($route_name, $entity_type_id) { + $backwards_incompatible = [ + "entity.{$entity_type_id}.field_ui_form_display", + "entity.{$entity_type_id}.field_ui_display", + ]; + return in_array($route_name, $backwards_incompatible, TRUE); + } + +} diff --git a/core/modules/field_ui/src/Routing/RouteSubscriber.php b/core/modules/field_ui/src/Routing/RouteSubscriber.php index b08e7f8fe5..576b414577 100644 --- a/core/modules/field_ui/src/Routing/RouteSubscriber.php +++ b/core/modules/field_ui/src/Routing/RouteSubscriber.php @@ -40,7 +40,10 @@ protected function alterRoutes(RouteCollection $collection) { if (!$entity_route = $collection->get($route_name)) { continue; } - $path = $entity_route->getPath(); + // @deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. + // Should consistently check for the field_ui_base_path entity type key + // instead of the base route starting from Drupal 9.0.0 onwards. + $path = $entity_type->get('field_ui_base_path') ?: $entity_route->getPath(); $options = $entity_route->getOptions(); if ($bundle_entity_type = $entity_type->getBundleEntityType()) { @@ -119,7 +122,12 @@ protected function alterRoutes(RouteCollection $collection) { ['_field_ui_form_mode_access' => 'administer ' . $entity_type_id . ' form display'], $options ); + // @deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. + // Use entity.{$entity_type_id}.field_ui_form_display instead. The old + // name is kept to maintain backwards compatibility and will be selected + // over the newer one in Drupal\field_ui\Routing\DeprecatedRouteMatcher. $collection->add("entity.entity_form_display.{$entity_type_id}.default", $route); + $collection->add("entity.{$entity_type_id}.field_ui_form_display", $route); $route = new Route( "$path/form-display/{form_mode_name}", @@ -142,7 +150,12 @@ protected function alterRoutes(RouteCollection $collection) { ['_field_ui_view_mode_access' => 'administer ' . $entity_type_id . ' display'], $options ); + // @deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. + // Use entity.{$entity_type_id}.field_ui_display instead. The old route + // name is kept to maintain backwards compatibility and will be selected + // over the newer one in Drupal\field_ui\Routing\DeprecatedRouteMatcher. $collection->add("entity.entity_view_display.{$entity_type_id}.default", $route); + $collection->add("entity.{$entity_type_id}.field_ui_display", $route); $route = new Route( "$path/display/{view_mode_name}", diff --git a/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php b/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php index db8312a4f1..f7d7b39610 100644 --- a/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php +++ b/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php @@ -149,6 +149,7 @@ public static function getSkippedDeprecations() { 'The Symfony\Component\HttpFoundation\Session\Storage\Handler\WriteCheckSessionHandler class is deprecated since Symfony 3.4 and will be removed in 4.0. Implement `SessionUpdateTimestampHandlerInterface` or extend `AbstractSessionHandler` instead.', 'The "session_handler.write_check" service relies on the deprecated "Symfony\Component\HttpFoundation\Session\Storage\Handler\WriteCheckSessionHandler" class. It should either be deprecated or its implementation upgraded.', 'Not setting the strict option of the Choice constraint to true is deprecated since Symfony 3.4 and will throw an exception in 4.0.', + 'The "field_ui.deprecated_route_matcher" service relies on the deprecated "Drupal\field_ui\Routing\DeprecatedRouteMatcher" class. It should either be deprecated or its implementation upgraded.', ]; } -- 2.17.1