From fbe932254fbec4dd3700f9b40a68441712a9e70c Mon Sep 17 00:00:00 2001
From: Nathaniel Catchpole <catch@35733.no-reply.drupal.org>
Date: Wed, 20 Nov 2013 11:37:11 +0000
Subject: [PATCH] Issue #2111823 by amateescu, dawehner: Convert field_ui /
 Entity local tasks to YAML definitions.

---
 .../Core/Entity/Annotation/EntityType.php     |   9 +-
 core/lib/Drupal/Core/Entity/EntityManager.php |   9 +-
 .../Core/Entity/EntityManagerInterface.php    |   5 +
 .../custom_block/Entity/CustomBlock.php       |   7 +-
 .../lib/Drupal/comment/Entity/Comment.php     |   4 +-
 .../comment/Tests/CommentNonNodeTest.php      |  12 +-
 .../ConfigFieldInstanceMapper.php             |   7 +-
 core/modules/contact/contact.local_tasks.yml  |   4 +
 core/modules/contact/contact.module           |   4 -
 .../lib/Drupal/contact/Entity/Message.php     |   5 +-
 .../modules/field_ui/field_ui.local_tasks.yml |   3 +
 core/modules/field_ui/field_ui.module         | 104 +-------
 core/modules/field_ui/field_ui.services.yml   |   4 +-
 .../field_ui/Access/FormModeAccessCheck.php   |  36 ++-
 .../field_ui/Access/ViewModeAccessCheck.php   |  34 ++-
 .../lib/Drupal/field_ui/DisplayOverview.php   |  11 +
 .../Drupal/field_ui/DisplayOverviewBase.php   |   6 +-
 .../Drupal/field_ui/Form/FieldDeleteForm.php  |   7 +-
 .../Drupal/field_ui/Form/FieldEditForm.php    |   7 +-
 .../field_ui/Form/FieldInstanceEditForm.php   |  10 +-
 .../Drupal/field_ui/FormDisplayOverview.php   |  11 +
 .../lib/Drupal/field_ui/OverviewBase.php      |  10 +-
 .../Plugin/Derivative/FieldUiLocalTask.php    | 234 ++++++++++++++++++
 .../field_ui/Routing/RouteSubscriber.php      |  82 ++++--
 .../Entity/FieldUITestNoBundle.php            |   3 +-
 .../node/lib/Drupal/node/Entity/Node.php      |   5 +-
 core/modules/node/node.local_tasks.yml        |   4 +
 core/modules/node/node.module                 |  10 -
 .../Controller/EntityTestController.php       |   7 +
 .../Drupal/entity_test/Entity/EntityTest.php  |   4 +-
 .../entity_test/Entity/EntityTestMul.php      |   4 +-
 .../entity_test/Routing/RouteSubscriber.php   |   7 +
 .../lib/Drupal/taxonomy/Entity/Term.php       |   5 +-
 .../modules/taxonomy/taxonomy.local_tasks.yml |   9 +
 core/modules/taxonomy/taxonomy.module         |   8 -
 .../user/lib/Drupal/user/Entity/User.php      |   4 +-
 36 files changed, 475 insertions(+), 220 deletions(-)
 create mode 100644 core/modules/contact/contact.local_tasks.yml
 create mode 100644 core/modules/field_ui/lib/Drupal/field_ui/Plugin/Derivative/FieldUiLocalTask.php

diff --git a/core/lib/Drupal/Core/Entity/Annotation/EntityType.php b/core/lib/Drupal/Core/Entity/Annotation/EntityType.php
index 604bf5dc358f..3dbbb39d5583 100644
--- a/core/lib/Drupal/Core/Entity/Annotation/EntityType.php
+++ b/core/lib/Drupal/Core/Entity/Annotation/EntityType.php
@@ -225,16 +225,11 @@ class EntityType extends Plugin {
   public $bundle_keys;
 
   /**
-   * The base router path for the entity type's field administration page.
-   *
-   * If the entity type has a bundle, include {bundle} in the path.
-   *
-   * For example, the node entity type specifies
-   * "admin/structure/types/manage/{bundle}" as its base field admin path.
+   * The name of the entity type which provides bundles.
    *
    * @var string (optional)
    */
-  public $route_base_path;
+  public $bundle_entity_type = 'bundle';
 
   /**
    * Link templates using the URI template syntax.
diff --git a/core/lib/Drupal/Core/Entity/EntityManager.php b/core/lib/Drupal/Core/Entity/EntityManager.php
index 5fc2d16f0983..221d4ee3842f 100644
--- a/core/lib/Drupal/Core/Entity/EntityManager.php
+++ b/core/lib/Drupal/Core/Entity/EntityManager.php
@@ -301,9 +301,9 @@ public function getAdminPath($entity_type, $bundle) {
     $admin_path = '';
     $entity_info = $this->getDefinition($entity_type);
     // Check for an entity type's admin base path.
-    if (isset($entity_info['route_base_path'])) {
-      // Replace any dynamic 'bundle' portion of the path with the actual bundle.
-      $admin_path = str_replace('{bundle}', $bundle, $entity_info['route_base_path']);
+    if (isset($entity_info['links']['admin-form'])) {
+      $route_parameters[$entity_info['bundle_entity_type']] = $bundle;
+      $admin_path = \Drupal::urlGenerator()->getPathFromRoute($entity_info['links']['admin-form'], $route_parameters);
     }
 
     return $admin_path;
@@ -313,10 +313,11 @@ public function getAdminPath($entity_type, $bundle) {
    * {@inheritdoc}
    */
   public function getAdminRouteInfo($entity_type, $bundle) {
+    $entity_info = $this->getDefinition($entity_type);
     return array(
       'route_name' => "field_ui.overview_$entity_type",
       'route_parameters' => array(
-        'bundle' => $bundle,
+        $entity_info['bundle_entity_type'] => $bundle,
       )
     );
   }
diff --git a/core/lib/Drupal/Core/Entity/EntityManagerInterface.php b/core/lib/Drupal/Core/Entity/EntityManagerInterface.php
index d561f530e602..e89966aa50b3 100644
--- a/core/lib/Drupal/Core/Entity/EntityManagerInterface.php
+++ b/core/lib/Drupal/Core/Entity/EntityManagerInterface.php
@@ -164,6 +164,11 @@ public function getViewBuilder($entity_type);
    *
    * @return string
    *   The administration path for an entity type bundle, if it exists.
+   *
+   * @deprecated since version 8.0
+   *   System paths should not be used - use route names and parameters.
+   *
+   * @see self::getAdminRouteInfo()
    */
   public function getAdminPath($entity_type, $bundle);
 
diff --git a/core/modules/block/custom_block/lib/Drupal/custom_block/Entity/CustomBlock.php b/core/modules/block/custom_block/lib/Drupal/custom_block/Entity/CustomBlock.php
index a83352b2265c..055bfeaf4646 100644
--- a/core/modules/block/custom_block/lib/Drupal/custom_block/Entity/CustomBlock.php
+++ b/core/modules/block/custom_block/lib/Drupal/custom_block/Entity/CustomBlock.php
@@ -36,10 +36,10 @@
  *   admin_permission = "administer blocks",
  *   base_table = "custom_block",
  *   revision_table = "custom_block_revision",
- *   route_base_path = "admin/structure/block/custom-blocks/manage/{bundle}",
  *   links = {
  *     "canonical" = "custom_block.edit",
- *     "edit-form" = "custom_block.edit"
+ *     "edit-form" = "custom_block.edit",
+ *     "admin-form" = "custom_block.type_edit"
  *   },
  *   fieldable = TRUE,
  *   translatable = TRUE,
@@ -52,7 +52,8 @@
  *   },
  *   bundle_keys = {
  *     "bundle" = "type"
- *   }
+ *   },
+ *   bundle_entity_type = "custom_block_type"
  * )
  */
 class CustomBlock extends ContentEntityBase implements CustomBlockInterface {
diff --git a/core/modules/comment/lib/Drupal/comment/Entity/Comment.php b/core/modules/comment/lib/Drupal/comment/Entity/Comment.php
index 209d08b6b0ab..cf96a591e18a 100644
--- a/core/modules/comment/lib/Drupal/comment/Entity/Comment.php
+++ b/core/modules/comment/lib/Drupal/comment/Entity/Comment.php
@@ -36,7 +36,6 @@
  *   fieldable = TRUE,
  *   translatable = TRUE,
  *   render_cache = FALSE,
- *   route_base_path = "admin/structure/comments/manage/{bundle}",
  *   entity_keys = {
  *     "id" = "cid",
  *     "bundle" = "field_id",
@@ -48,7 +47,8 @@
  *   },
  *   links = {
  *     "canonical" = "comment.permalink",
- *     "edit-form" = "comment.edit_page"
+ *     "edit-form" = "comment.edit_page",
+ *     "admin-form" = "comment.bundle"
  *   }
  * )
  */
diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentNonNodeTest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentNonNodeTest.php
index 80e4855a2fd8..f7cfd761c35d 100644
--- a/core/modules/comment/lib/Drupal/comment/Tests/CommentNonNodeTest.php
+++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentNonNodeTest.php
@@ -229,11 +229,11 @@ function testCommentFunctionality() {
     ));
     $this->drupalLogin($limited_user);
     // Test that default field exists.
-    $this->drupalGet('admin/structure/entity-test/manage/entity_test/fields');
+    $this->drupalGet('entity_test/structure/entity_test/fields');
     $this->assertText(t('Comment settings'));
-    $this->assertLinkByHref('admin/structure/entity-test/manage/entity_test/fields/entity_test.entity_test.comment');
+    $this->assertLinkByHref('entity_test/structure/entity_test/fields/entity_test.entity_test.comment');
     // Test widget hidden option is not visible when there's no comments.
-    $this->drupalGet('admin/structure/entity-test/manage/entity_test/entity-test/fields/entity_test.entity_test.comment');
+    $this->drupalGet('entity_test/structure/entity_test/entity-test/fields/entity_test.entity_test.comment');
     $this->assertNoField('edit-default-value-input-comment-und-0-status-0');
 
     $this->drupalLogin($this->admin_user);
@@ -343,20 +343,20 @@ function testCommentFunctionality() {
       'administer entity_test content',
     ));
     $this->drupalLogin($limited_user);
-    $this->drupalGet('admin/structure/entity-test/manage/entity_test/fields/entity_test.entity_test.comment');
+    $this->drupalGet('entity_test/structure/entity_test/fields/entity_test.entity_test.comment');
     $this->assertNoFieldChecked('edit-default-value-input-comment-0-status-0');
     $this->assertNoFieldChecked('edit-default-value-input-comment-0-status-1');
     $this->assertFieldChecked('edit-default-value-input-comment-0-status-2');
     // Test comment option change in field settings.
     $edit = array('default_value_input[comment][0][status]' => COMMENT_CLOSED);
     $this->drupalPostForm(NULL, $edit, t('Save settings'));
-    $this->drupalGet('admin/structure/entity-test/manage/entity_test/fields/entity_test.entity_test.comment');
+    $this->drupalGet('entity_test/structure/entity_test/fields/entity_test.entity_test.comment');
     $this->assertNoFieldChecked('edit-default-value-input-comment-0-status-0');
     $this->assertFieldChecked('edit-default-value-input-comment-0-status-1');
     $this->assertNoFieldChecked('edit-default-value-input-comment-0-status-2');
 
     // Add a new comment field.
-    $this->drupalGet('admin/structure/entity-test/manage/entity_test/fields');
+    $this->drupalGet('entity_test/structure/entity_test/fields');
     $edit = array(
       'fields[_add_new_field][label]' => 'Foobar',
       'fields[_add_new_field][field_name]' => 'foobar',
diff --git a/core/modules/config_translation/lib/Drupal/config_translation/ConfigFieldInstanceMapper.php b/core/modules/config_translation/lib/Drupal/config_translation/ConfigFieldInstanceMapper.php
index ce4f1be4f0bf..be497e4ab2bb 100644
--- a/core/modules/config_translation/lib/Drupal/config_translation/ConfigFieldInstanceMapper.php
+++ b/core/modules/config_translation/lib/Drupal/config_translation/ConfigFieldInstanceMapper.php
@@ -23,13 +23,10 @@ class ConfigFieldInstanceMapper extends ConfigEntityMapper {
    */
   public function getBaseRouteParameters() {
     $parameters = parent::getBaseRouteParameters();
-    // @todo All core content entity path placeholders can be fully filled in
-    //   with an additional {bundle} value in their paths, but a more
-    //   predictable solution would be ideal. See
-    //   https://drupal.org/node/2134871
+    $base_entity_info = $this->entityManager->getDefinition($this->pluginDefinition['base_entity_type']);
     // @todo Field instances have no method to return the bundle the instance is
     //   attached to. See https://drupal.org/node/2134861
-    $parameters['bundle'] = $this->entity->bundle;
+    $parameters[$base_entity_info['bundle_entity_type']] = $this->entity->bundle;
     return $parameters;
   }
 
diff --git a/core/modules/contact/contact.local_tasks.yml b/core/modules/contact/contact.local_tasks.yml
new file mode 100644
index 000000000000..91bb65d36359
--- /dev/null
+++ b/core/modules/contact/contact.local_tasks.yml
@@ -0,0 +1,4 @@
+contact.category_edit:
+  title: 'Edit'
+  route_name: contact.category_edit
+  tab_root_id: contact.category_edit
diff --git a/core/modules/contact/contact.module b/core/modules/contact/contact.module
index 66a80ece3a27..58e343e70779 100644
--- a/core/modules/contact/contact.module
+++ b/core/modules/contact/contact.module
@@ -67,10 +67,6 @@ function contact_menu() {
     'title' => 'Edit contact category',
     'route_name' => 'contact.category_edit',
   );
-  $items['admin/structure/contact/manage/%contact_category/edit'] = array(
-    'title' => 'Edit',
-    'type' => MENU_DEFAULT_LOCAL_TASK,
-  );
 
   $items['contact'] = array(
     'title' => 'Contact',
diff --git a/core/modules/contact/lib/Drupal/contact/Entity/Message.php b/core/modules/contact/lib/Drupal/contact/Entity/Message.php
index 9e424fa2c4b0..aaaa64ee5eaa 100644
--- a/core/modules/contact/lib/Drupal/contact/Entity/Message.php
+++ b/core/modules/contact/lib/Drupal/contact/Entity/Message.php
@@ -26,10 +26,13 @@
  *   entity_keys = {
  *     "bundle" = "category"
  *   },
- *   route_base_path = "admin/structure/contact/manage/{bundle}",
+ *   bundle_entity_type = "contact_category",
  *   fieldable = TRUE,
  *   bundle_keys = {
  *     "bundle" = "id"
+ *   },
+ *   links = {
+ *     "admin-form" = "contact.category_edit"
  *   }
  * )
  */
diff --git a/core/modules/field_ui/field_ui.local_tasks.yml b/core/modules/field_ui/field_ui.local_tasks.yml
index e690491757fb..1330c2dc07c8 100644
--- a/core/modules/field_ui/field_ui.local_tasks.yml
+++ b/core/modules/field_ui/field_ui.local_tasks.yml
@@ -2,3 +2,6 @@ field_ui.list:
   title: Entities
   route_name: field_ui.list
   tab_root_id: field_ui.list
+field_ui.fields:
+  class: \Drupal\Core\Menu\LocalTaskDefault
+  derivative: \Drupal\field_ui\Plugin\Derivative\FieldUiLocalTask
diff --git a/core/modules/field_ui/field_ui.module b/core/modules/field_ui/field_ui.module
index f9d90c6c4867..eefc19987505 100644
--- a/core/modules/field_ui/field_ui.module
+++ b/core/modules/field_ui/field_ui.module
@@ -7,6 +7,7 @@
 
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\entity\EntityViewModeInterface;
+use Drupal\field_ui\Plugin\Derivative\FieldUiLocalTask;
 
 /**
  * Implements hook_help().
@@ -61,94 +62,6 @@ function field_ui_menu() {
     'type' => MENU_NORMAL_ITEM,
   );
 
-  // Create tabs for all possible bundles.
-  foreach (entity_get_info() as $entity_type => $entity_info) {
-    if ($entity_info['fieldable'] && isset($entity_info['route_base_path'])) {
-      // Extract path information from the entity type.
-      $path = $entity_info['route_base_path'];
-      $default_path = preg_replace('/{' . DRUPAL_PHP_FUNCTION_PATTERN . '}/', '%', $path);
-      // This is the position of the %field_ui_instance placeholder in the
-      // items below.
-      $field_position = count(explode('/', $path)) + 1;
-
-      $items["$path/fields"] = array(
-        'title' => 'Manage fields',
-        'type' => MENU_LOCAL_TASK,
-        'route_name' => "field_ui.overview_$entity_type",
-        'weight' => 1,
-      );
-      $items["$path/fields/%"] = array(
-        'title callback' => 'entity_page_label',
-        'title arguments' => array($field_position),
-        'route_name' => "field_ui.instance_edit_$entity_type",
-      );
-      $items["$default_path/fields/%/edit"] = array(
-        'title' => 'Edit',
-        'type' => MENU_DEFAULT_LOCAL_TASK,
-      );
-      $items["$path/fields/%/field"] = array(
-        'title' => 'Field settings',
-        'type' => MENU_LOCAL_TASK,
-        'route_name' => "field_ui.field_edit_$entity_type",
-      );
-      $items["$path/fields/%/delete"] = array(
-        'title' => 'Delete',
-        'type' => MENU_VISIBLE_IN_BREADCRUMB,
-        'route_name' => "field_ui.delete_$entity_type",
-        'weight' => 10,
-      );
-
-      // 'Manage form display' tab.
-      $items["$path/form-display"] = array(
-        'title' => 'Manage form display',
-        'type' => MENU_LOCAL_TASK,
-        'route_name' => "field_ui.form_display_overview_$entity_type",
-        'weight' => 2,
-      );
-
-      // 'Manage display' tab.
-      $items["$path/display"] = array(
-        'title' => 'Manage display',
-        'type' => MENU_LOCAL_TASK,
-        'route_name' => "field_ui.display_overview_$entity_type",
-        'weight' => 3,
-      );
-
-      // View and form modes secondary tabs.
-      // The same base $path for the menu item (with a placeholder) can be
-      // used for all bundles of a given entity type; but depending on
-      // administrator settings, each bundle has a different set of view
-      // modes available for customisation. So we define menu items for all
-      // view modes, and use a route requirement to determine which ones are
-      // actually visible for a given bundle.
-      $items["$default_path/form-display/default"] = array(
-        'title' => t('Default'),
-        'type' => MENU_DEFAULT_LOCAL_TASK,
-      );
-      $items["$default_path/display/default"] = array(
-        'title' => t('Default'),
-        'type' => MENU_DEFAULT_LOCAL_TASK,
-      );
-      $weight = 0;
-      foreach (entity_get_form_modes($entity_type) as $form_mode => $form_mode_info) {
-        $items["$path/form-display/$form_mode"] = array(
-          'title' => $form_mode_info['label'],
-          'type' => MENU_LOCAL_TASK,
-          'weight' => $weight++,
-          'route_name' => "field_ui.form_display_overview_$entity_type" . '_' . $form_mode,
-        );
-      }
-      $weight = 0;
-      foreach (entity_get_view_modes($entity_type) as $view_mode => $view_mode_info) {
-        $items["$path/display/$view_mode"] = array(
-          'title' => $view_mode_info['label'],
-          'type' => MENU_LOCAL_TASK,
-          'weight' => $weight++,
-          'route_name' => "field_ui.display_overview_$entity_type" . '_' . $view_mode,
-        );
-      }
-    }
-  }
   return $items;
 }
 
@@ -334,12 +247,7 @@ function field_ui_entity_operation_alter(array &$operations, EntityInterface $en
  */
 function field_ui_form_node_type_form_submit($form, &$form_state) {
   if ($form_state['triggering_element']['#parents'][0] === 'save_continue') {
-    $form_state['redirect_route'] = array(
-      'route_name' => 'field_ui.overview_node',
-      'route_parameters' => array(
-        'bundle' => $form_state['values']['type'],
-      ),
-    );
+    $form_state['redirect_route'] = \Drupal::entityManager()->getAdminRouteInfo('node', $form_state['values']['type']);
   }
 }
 
@@ -462,3 +370,11 @@ function theme_field_ui_table($variables) {
   return drupal_render($table);
 }
 
+/**
+ * Implements hook_local_tasks_alter().
+ */
+function field_ui_local_tasks_alter(&$local_tasks) {
+  $container = \Drupal::getContainer();
+  $local_task = FieldUiLocalTask::create($container, 'field_ui.fields');
+  $local_task->alterLocalTasks($local_tasks);
+}
diff --git a/core/modules/field_ui/field_ui.services.yml b/core/modules/field_ui/field_ui.services.yml
index 2ab3051a1a16..10752b099168 100644
--- a/core/modules/field_ui/field_ui.services.yml
+++ b/core/modules/field_ui/field_ui.services.yml
@@ -1,14 +1,16 @@
 services:
   field_ui.subscriber:
     class: Drupal\field_ui\Routing\RouteSubscriber
-    arguments: ['@entity.manager']
+    arguments: ['@entity.manager', '@router.route_provider']
     tags:
      - { name: event_subscriber }
   access_check.field_ui.view_mode:
     class: Drupal\field_ui\Access\ViewModeAccessCheck
+    arguments: ['@entity.manager']
     tags:
      - { name: access_check }
   access_check.field_ui.form_mode:
     class: Drupal\field_ui\Access\FormModeAccessCheck
+    arguments: ['@entity.manager']
     tags:
      - { name: access_check }
diff --git a/core/modules/field_ui/lib/Drupal/field_ui/Access/FormModeAccessCheck.php b/core/modules/field_ui/lib/Drupal/field_ui/Access/FormModeAccessCheck.php
index 92e3790ba0f6..3322c9edc2f0 100644
--- a/core/modules/field_ui/lib/Drupal/field_ui/Access/FormModeAccessCheck.php
+++ b/core/modules/field_ui/lib/Drupal/field_ui/Access/FormModeAccessCheck.php
@@ -8,6 +8,7 @@
 namespace Drupal\field_ui\Access;
 
 use Drupal\Core\Access\StaticAccessCheckInterface;
+use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Session\AccountInterface;
 use Symfony\Component\Routing\Route;
 use Symfony\Component\HttpFoundation\Request;
@@ -17,6 +18,23 @@
  */
 class FormModeAccessCheck implements StaticAccessCheckInterface {
 
+  /**
+   * The entity manager.
+   *
+   * @var \Drupal\Core\Entity\EntityManagerInterface
+   */
+  protected $entityManager;
+
+  /**
+   * Creates a new FormModeAccessCheck.
+   *
+   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
+   *   The entity manager.
+   */
+  public function __construct(EntityManagerInterface $entity_manager) {
+    $this->entityManager = $entity_manager;
+  }
+
   /**
    * {@inheritdoc}
    */
@@ -29,17 +47,19 @@ public function appliesTo() {
    */
   public function access(Route $route, Request $request, AccountInterface $account) {
     if ($entity_type = $request->attributes->get('entity_type')) {
-      $bundle = $request->attributes->get('bundle');
-      $form_mode = $request->attributes->get('mode');
+      $form_mode = $request->attributes->get('form_mode_name');
 
-      if ($form_mode == 'default') {
-        $visibility = TRUE;
+      if (!($bundle = $request->attributes->get('bundle'))) {
+        $entity_info = $this->entityManager->getDefinition($entity_type);
+        $bundle = $request->attributes->get('_raw_variables')->get($entity_info['bundle_entity_type']);
       }
-      elseif ($entity_form_display = entity_load('entity_form_display', $entity_type . '.' . $bundle . '.' . $form_mode)) {
-        $visibility = $entity_form_display->status();
+
+      $visibility = FALSE;
+      if (!$form_mode || $form_mode == 'default') {
+        $visibility = TRUE;
       }
-      else {
-        $visibility = FALSE;
+      elseif ($entity_display = $this->entityManager->getStorageController('entity_form_display')->load($entity_type . '.' . $bundle . '.' . $form_mode)) {
+        $visibility = $entity_display->status();
       }
 
       if ($visibility) {
diff --git a/core/modules/field_ui/lib/Drupal/field_ui/Access/ViewModeAccessCheck.php b/core/modules/field_ui/lib/Drupal/field_ui/Access/ViewModeAccessCheck.php
index 2fe350b23684..5e69daf86478 100644
--- a/core/modules/field_ui/lib/Drupal/field_ui/Access/ViewModeAccessCheck.php
+++ b/core/modules/field_ui/lib/Drupal/field_ui/Access/ViewModeAccessCheck.php
@@ -8,6 +8,7 @@
 namespace Drupal\field_ui\Access;
 
 use Drupal\Core\Access\StaticAccessCheckInterface;
+use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Session\AccountInterface;
 use Symfony\Component\Routing\Route;
 use Symfony\Component\HttpFoundation\Request;
@@ -17,6 +18,23 @@
  */
 class ViewModeAccessCheck implements StaticAccessCheckInterface {
 
+  /**
+   * The entity manager.
+   *
+   * @var \Drupal\Core\Entity\EntityManagerInterface
+   */
+  protected $entityManager;
+
+  /**
+   * Creates a new ViewModeAccessCheck.
+   *
+   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
+   *   The entity manager.
+   */
+  public function __construct(EntityManagerInterface $entity_manager) {
+    $this->entityManager = $entity_manager;
+  }
+
   /**
    * {@inheritdoc}
    */
@@ -29,18 +47,20 @@ public function appliesTo() {
    */
   public function access(Route $route, Request $request, AccountInterface $account) {
     if ($entity_type = $request->attributes->get('entity_type')) {
-      $bundle = $request->attributes->get('bundle');
-      $view_mode = $request->attributes->get('mode');
+      $view_mode = $request->attributes->get('view_mode_name');
 
-      if ($view_mode == 'default') {
+      if (!($bundle = $request->attributes->get('bundle'))) {
+        $entity_info = $this->entityManager->getDefinition($entity_type);
+        $bundle = $request->attributes->get('_raw_variables')->get($entity_info['bundle_entity_type']);
+      }
+
+      $visibility = FALSE;
+      if (!$view_mode || $view_mode == 'default') {
         $visibility = TRUE;
       }
-      elseif ($entity_display = entity_load('entity_display', $entity_type . '.' . $bundle . '.' . $view_mode)) {
+      elseif ($entity_display = $this->entityManager->getStorageController('entity_display')->load($entity_type . '.' . $bundle . '.' . $view_mode)) {
         $visibility = $entity_display->status();
       }
-      else {
-        $visibility = FALSE;
-      }
 
       if ($visibility) {
         $permission = $route->getRequirement('_field_ui_view_mode_access');
diff --git a/core/modules/field_ui/lib/Drupal/field_ui/DisplayOverview.php b/core/modules/field_ui/lib/Drupal/field_ui/DisplayOverview.php
index 0d68e77a2712..870df32f70f6 100644
--- a/core/modules/field_ui/lib/Drupal/field_ui/DisplayOverview.php
+++ b/core/modules/field_ui/lib/Drupal/field_ui/DisplayOverview.php
@@ -35,6 +35,17 @@ public function getFormId() {
     return 'field_ui_display_overview_form';
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, array &$form_state, $entity_type = NULL, $bundle = NULL) {
+    if ($this->getRequest()->attributes->has('view_mode_name')) {
+      $this->mode = $this->getRequest()->attributes->get('view_mode_name');
+    }
+
+    return parent::buildForm($form, $form_state, $entity_type, $bundle);
+  }
+
   /**
    * {@inheritdoc}
    */
diff --git a/core/modules/field_ui/lib/Drupal/field_ui/DisplayOverviewBase.php b/core/modules/field_ui/lib/Drupal/field_ui/DisplayOverviewBase.php
index c25bd4330985..06a4bdcee17f 100644
--- a/core/modules/field_ui/lib/Drupal/field_ui/DisplayOverviewBase.php
+++ b/core/modules/field_ui/lib/Drupal/field_ui/DisplayOverviewBase.php
@@ -82,10 +82,12 @@ public function getRegions() {
   /**
    * {@inheritdoc}
    */
-  public function buildForm(array $form, array &$form_state, $entity_type = NULL, $bundle = NULL, $mode = NULL) {
+  public function buildForm(array $form, array &$form_state, $entity_type = NULL, $bundle = NULL) {
     parent::buildForm($form, $form_state, $entity_type, $bundle);
 
-    $this->mode = (isset($mode) ? $mode : 'default');
+    if (empty($this->mode)) {
+      $this->mode = 'default';
+    }
 
     // Gather type information.
     $instances = field_info_instances($this->entity_type, $this->bundle);
diff --git a/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldDeleteForm.php b/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldDeleteForm.php
index 7e37efb086c7..f51256bce64c 100644
--- a/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldDeleteForm.php
+++ b/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldDeleteForm.php
@@ -79,12 +79,7 @@ public function submit(array $form, array &$form_state) {
       drupal_set_message($this->t('There was a problem removing the %field from the %type content type.', array('%field' => $this->entity->label(), '%type' => $bundle_label)), 'error');
     }
 
-    $form_state['redirect_route'] = array(
-      'route_name' => 'field_ui.overview_' . $this->entity->entity_type,
-      'route_parameters' => array(
-        'bundle' => $this->entity->bundle,
-      )
-    );
+    $form_state['redirect_route'] = $this->entityManager->getAdminRouteInfo($this->entity->entity_type, $this->entity->bundle);
 
     // Fields are purged on cron. However field module prevents disabling modules
     // when field types they provided are used in a field until it is fully
diff --git a/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldEditForm.php b/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldEditForm.php
index 062dd40884b8..8d9ce26a7960 100644
--- a/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldEditForm.php
+++ b/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldEditForm.php
@@ -206,12 +206,7 @@ public function submitForm(array &$form, array &$form_state) {
         $form_state['redirect'] = $next_destination;
       }
       else {
-        $form_state['redirect_route'] = array(
-          'route_name' => 'field_ui.overview_' . $this->instance->entity_type,
-          'route_parameters' => array(
-            'bundle' => $this->instance->bundle,
-          )
-        );
+        $form_state['redirect_route'] = $this->entityManager->getAdminRouteInfo($this->instance->entity_type, $this->instance->bundle);
       }
     }
     catch (\Exception $e) {
diff --git a/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldInstanceEditForm.php b/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldInstanceEditForm.php
index 7b5074527117..8a7650ff36fc 100644
--- a/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldInstanceEditForm.php
+++ b/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldInstanceEditForm.php
@@ -193,12 +193,7 @@ public function submitForm(array &$form, array &$form_state) {
       $form_state['redirect'] = $next_destination;
     }
     else {
-      $form_state['redirect_route'] = array(
-        'route_name' => 'field_ui.overview_' . $this->instance->entity_type,
-        'route_parameters' => array(
-          'bundle' => $this->instance->bundle,
-        )
-      );
+      $form_state['redirect_route'] = $this->entityManager->getAdminRouteInfo($this->instance->entity_type, $this->instance->bundle);
     }
   }
 
@@ -212,10 +207,11 @@ public function delete(array &$form, array &$form_state) {
       $destination = drupal_get_destination();
       $request->query->remove('destination');
     }
+    $entity_info = $this->entityManager->getDefinition($this->instance->entity_type);
     $form_state['redirect_route'] = array(
       'route_name' => 'field_ui.delete_' . $this->instance->entity_type,
       'route_parameters' => array(
-        'bundle' => $this->instance->bundle,
+        $entity_info['bundle_entity_type'] => $this->instance->bundle,
         'field_instance' => $this->instance->id(),
       ),
       'options' => array(
diff --git a/core/modules/field_ui/lib/Drupal/field_ui/FormDisplayOverview.php b/core/modules/field_ui/lib/Drupal/field_ui/FormDisplayOverview.php
index a1e8d8ba22df..0c4877c7103f 100644
--- a/core/modules/field_ui/lib/Drupal/field_ui/FormDisplayOverview.php
+++ b/core/modules/field_ui/lib/Drupal/field_ui/FormDisplayOverview.php
@@ -35,6 +35,17 @@ public function getFormId() {
     return 'field_ui_form_display_overview_form';
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, array &$form_state, $entity_type = NULL, $bundle = NULL) {
+    if ($this->getRequest()->attributes->has('form_mode_name')) {
+      $this->mode = $this->getRequest()->attributes->get('form_mode_name');
+    }
+
+    return parent::buildForm($form, $form_state, $entity_type, $bundle);
+  }
+
   /**
    * {@inheritdoc}
    */
diff --git a/core/modules/field_ui/lib/Drupal/field_ui/OverviewBase.php b/core/modules/field_ui/lib/Drupal/field_ui/OverviewBase.php
index c7db86e44e01..aad6599b8d50 100644
--- a/core/modules/field_ui/lib/Drupal/field_ui/OverviewBase.php
+++ b/core/modules/field_ui/lib/Drupal/field_ui/OverviewBase.php
@@ -74,10 +74,16 @@ public static function create(ContainerInterface $container) {
    * {@inheritdoc}
    */
   public function buildForm(array $form, array &$form_state, $entity_type = NULL, $bundle = NULL) {
-    $entity_info = $this->entityManager->getDefinition($entity_type);
+    if (!isset($form_state['bundle'])) {
+      if (!$bundle) {
+        $entity_info = $this->entityManager->getDefinition($entity_type);
+        $bundle = $this->getRequest()->attributes->get('_raw_variables')->get($entity_info['bundle_entity_type']);
+      }
+      $form_state['bundle'] = $bundle;
+    }
 
     $this->entity_type = $entity_type;
-    $this->bundle = $bundle;
+    $this->bundle = $form_state['bundle'];
     $this->adminPath = $this->entityManager->getAdminPath($this->entity_type, $this->bundle);
 
     // When displaying the form, make sure the list of fields is up-to-date.
diff --git a/core/modules/field_ui/lib/Drupal/field_ui/Plugin/Derivative/FieldUiLocalTask.php b/core/modules/field_ui/lib/Drupal/field_ui/Plugin/Derivative/FieldUiLocalTask.php
new file mode 100644
index 000000000000..a75a495efde4
--- /dev/null
+++ b/core/modules/field_ui/lib/Drupal/field_ui/Plugin/Derivative/FieldUiLocalTask.php
@@ -0,0 +1,234 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\field_ui\Plugin\Derivative\FieldUiLocalTask.
+ */
+
+namespace Drupal\field_ui\Plugin\Derivative;
+
+use Drupal\Component\Plugin\Derivative\DerivativeBase;
+use Drupal\Core\Entity\EntityManagerInterface;
+use Drupal\Core\Plugin\Discovery\ContainerDerivativeInterface;
+use Drupal\Core\Routing\RouteProviderInterface;
+use Drupal\Core\StringTranslation\TranslationInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Provides local task definitions for all entity bundles.
+ */
+class FieldUiLocalTask extends DerivativeBase implements ContainerDerivativeInterface {
+
+  /**
+   * The route provider.
+   *
+   * @var \Drupal\Core\Routing\RouteProviderInterface
+   */
+  protected $routeProvider;
+
+  /**
+   * The entity manager
+   *
+   * @var \Drupal\Core\Entity\EntityManagerInterface
+   */
+  protected $entityManager;
+
+  /**
+   * The translation manager service.
+   *
+   * @var \Drupal\Core\StringTranslation\TranslationInterface
+   */
+  protected $translationManager;
+
+  /**
+   * Creates an FieldUiLocalTask object.
+   *
+   * @param \Drupal\Core\Routing\RouteProviderInterface $route_provider
+   *   The route provider.
+   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
+   *   The entity manager.
+   * @param \Drupal\Core\StringTranslation\TranslationInterface $translation_manager
+   *   The translation manager.
+   */
+  public function __construct(RouteProviderInterface $route_provider, EntityManagerInterface $entity_manager, TranslationInterface $translation_manager) {
+    $this->routeProvider = $route_provider;
+    $this->entityManager = $entity_manager;
+    $this->translationManager = $translation_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, $base_plugin_id) {
+    return new static(
+      $container->get('router.route_provider'),
+      $container->get('entity.manager'),
+      $container->get('string_translation')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDerivativeDefinitions(array $base_plugin_definition) {
+    $this->derivatives = array();
+
+    foreach ($this->entityManager->getDefinitions() as $entity_type => $entity_info) {
+      if ($entity_info['fieldable'] && isset($entity_info['links']['admin-form'])) {
+        $this->derivatives["overview_$entity_type"] = array(
+          'route_name' => "field_ui.overview_$entity_type",
+          'weight' => 1,
+          'title' => $this->t('Manage fields'),
+          'tab_root_id' => "field_ui.fields:overview_$entity_type",
+        );
+
+        // 'Manage form display' tab.
+        $this->derivatives["form_display_overview_$entity_type"] = array(
+          'route_name' => "field_ui.form_display_overview_$entity_type",
+          'weight' => 2,
+          'title' => $this->t('Manage form display'),
+          'tab_root_id' => "field_ui.fields:overview_$entity_type",
+        );
+
+        // 'Manage display' tab.
+        $this->derivatives["display_overview_$entity_type"] = array(
+          'route_name' => "field_ui.display_overview_$entity_type",
+          'weight' => 3,
+          'title' => $this->t('Manage display'),
+          'tab_root_id' => "field_ui.fields:overview_$entity_type",
+        );
+
+        // Field instance edit tab.
+        $this->derivatives["instance_edit_$entity_type"] = array(
+          'route_name' => "field_ui.instance_edit_$entity_type",
+          'title' => $this->t('Edit'),
+          'tab_root_id' => "field_ui.fields:instance_edit_$entity_type",
+        );
+
+        // Field settings tab.
+        $this->derivatives["field_edit_$entity_type"] = array(
+          'route_name' => "field_ui.field_edit_$entity_type",
+          'title' => $this->t('Field settings'),
+          'tab_root_id' => "field_ui.fields:instance_edit_$entity_type",
+        );
+
+        // View and form modes secondary tabs.
+        // The same base $path for the menu item (with a placeholder) can be
+        // used for all bundles of a given entity type; but depending on
+        // administrator settings, each bundle has a different set of view
+        // modes available for customisation. So we define menu items for all
+        // view modes, and use a route requirement to determine which ones are
+        // actually visible for a given bundle.
+        $this->derivatives['field_form_display_default_' . $entity_type] = array(
+          'title' => 'Default',
+          'route_name' => "field_ui.form_display_overview_$entity_type",
+          'tab_root_id' => "field_ui.fields:overview_$entity_type",
+          'tab_parent_id' => "field_ui.fields:form_display_overview_$entity_type",
+        );
+        $this->derivatives['field_display_default_' . $entity_type] = array(
+          'title' => 'Default',
+          'route_name' => "field_ui.display_overview_$entity_type",
+          'tab_root_id' => "field_ui.fields:overview_$entity_type",
+          'tab_parent_id' => "field_ui.fields:display_overview_$entity_type",
+        );
+
+        // One local task for each form mode.
+        $weight = 0;
+        foreach (entity_get_form_modes($entity_type) as $form_mode => $form_mode_info) {
+          $this->derivatives['field_form_display_' . $form_mode . '_' . $entity_type] = array(
+            'title' => $form_mode_info['label'],
+            'route_name' => "field_ui.form_display_overview_form_mode_$entity_type",
+            'route_parameters' => array(
+              'form_mode_name' => $form_mode,
+            ),
+            'tab_root_id' => "field_ui.fields:overview_$entity_type",
+            'tab_parent_id' => "field_ui.fields:form_display_overview_$entity_type",
+            'weight' => $weight++,
+          );
+        }
+
+        // One local task for each view mode.
+        $weight = 0;
+        foreach (entity_get_view_modes($entity_type) as $view_mode => $form_mode_info) {
+          $this->derivatives['field_display_' . $view_mode . '_' . $entity_type] = array(
+            'title' => $form_mode_info['label'],
+            'route_name' => "field_ui.display_overview_view_mode_$entity_type",
+            'route_parameters' => array(
+              'view_mode_name' => $view_mode,
+            ),
+            'tab_root_id' => "field_ui.fields:overview_$entity_type",
+            'tab_parent_id' => "field_ui.fields:display_overview_$entity_type",
+            'weight' => $weight++,
+          );
+        }
+      }
+    }
+
+    foreach ($this->derivatives as &$entry) {
+      $entry += $base_plugin_definition;
+    }
+
+    return $this->derivatives;
+  }
+
+  /**
+   * Alters the tab_root_id definition for field_ui local tasks.
+   *
+   * @param array $local_tasks
+   *   An array of local tasks plugin definitions, keyed by plugin ID.
+   */
+  public function alterLocalTasks(&$local_tasks) {
+    foreach ($this->entityManager->getDefinitions() as $entity_type => $entity_info) {
+      if ($entity_info['fieldable'] && isset($entity_info['links']['admin-form'])) {
+        if ($parent_task = $this->getPluginIdFromRoute($entity_info['links']['admin-form'], $local_tasks)) {
+          $local_tasks["field_ui.fields:overview_$entity_type"]['tab_root_id'] = $parent_task;
+          $local_tasks["field_ui.fields:form_display_overview_$entity_type"]['tab_root_id'] = $parent_task;
+          $local_tasks["field_ui.fields:display_overview_$entity_type"]['tab_root_id'] = $parent_task;
+          $local_tasks["field_ui.fields:field_form_display_default_$entity_type"]['tab_root_id'] = $parent_task;
+          $local_tasks["field_ui.fields:field_display_default_$entity_type"]['tab_root_id'] = $parent_task;
+
+          foreach (entity_get_form_modes($entity_type) as $form_mode => $form_mode_info) {
+            $local_tasks['field_ui.fields:field_form_display_' . $form_mode . '_' . $entity_type]['tab_root_id'] = $parent_task;
+          }
+
+          foreach (entity_get_view_modes($entity_type) as $view_mode => $form_mode_info) {
+            $local_tasks['field_ui.fields:field_display_' . $view_mode . '_' . $entity_type]['tab_root_id'] = $parent_task;
+          }
+        }
+      }
+    }
+  }
+
+  /**
+   * Finds the local task ID of a route given the route name.
+   *
+   * @param string $route_name
+   *   The route name.
+   * @param array $local_tasks
+   *   An array of all local task definitions.
+   *
+   * @return string|null
+   *   Returns the local task ID of the given route or NULL if none is found.
+   */
+  protected function getPluginIdFromRoute($route_name, &$local_tasks) {
+    $local_task_id = NULL;
+    foreach ($local_tasks as $plugin_id => $local_task) {
+      if ($local_task['route_name'] == $route_name) {
+        $local_task_id = $plugin_id;
+        break;
+      }
+    }
+
+    return $local_task_id;
+  }
+
+  /**
+   * Translates a string to the current language or to a given language.
+   *
+   * See the t() documentation for details.
+   */
+  protected function t($string, array $args = array(), array $options = array()) {
+    return $this->translationManager->translate($string, $args, $options);
+  }
+
+}
diff --git a/core/modules/field_ui/lib/Drupal/field_ui/Routing/RouteSubscriber.php b/core/modules/field_ui/lib/Drupal/field_ui/Routing/RouteSubscriber.php
index ff49c182dbf2..048630a2b5c9 100644
--- a/core/modules/field_ui/lib/Drupal/field_ui/Routing/RouteSubscriber.php
+++ b/core/modules/field_ui/lib/Drupal/field_ui/Routing/RouteSubscriber.php
@@ -8,7 +8,10 @@
 namespace Drupal\field_ui\Routing;
 
 use Drupal\Core\Entity\EntityManagerInterface;
+use Drupal\Core\Routing\RouteProviderInterface;
 use Drupal\Core\Routing\RouteSubscriberBase;
+use Drupal\Core\Routing\RoutingEvents;
+use Symfony\Component\Routing\Exception\RouteNotFoundException;
 use Symfony\Component\Routing\Route;
 use Symfony\Component\Routing\RouteCollection;
 
@@ -24,14 +27,24 @@ class RouteSubscriber extends RouteSubscriberBase {
    */
   protected $manager;
 
+  /**
+   * The route provider.
+   *
+   * @var \Drupal\Core\Routing\RouteProviderInterface
+   */
+  protected $routeProvider;
+
   /**
    * Constructs a RouteSubscriber object.
    *
    * @param \Drupal\Core\Entity\EntityManagerInterface $manager
    *   The entity type manager.
+   * @param \Drupal\Core\Routing\RouteProviderInterface $route_provider
+   *   The route provider.
    */
-  public function __construct(EntityManagerInterface $manager) {
+  public function __construct(EntityManagerInterface $manager, RouteProviderInterface $route_provider) {
     $this->manager = $manager;
+    $this->routeProvider = $route_provider;
   }
 
   /**
@@ -40,8 +53,20 @@ public function __construct(EntityManagerInterface $manager) {
   protected function routes(RouteCollection $collection) {
     foreach ($this->manager->getDefinitions() as $entity_type => $entity_info) {
       $defaults = array();
-      if ($entity_info['fieldable'] && isset($entity_info['route_base_path'])) {
-        $path = $entity_info['route_base_path'];
+      if ($entity_info['fieldable'] && isset($entity_info['links']['admin-form'])) {
+        // First try to get the route from the dynamic_routes collection.
+        if (!$entity_route = $collection->get($entity_info['links']['admin-form'])) {
+          // Then try to get the route from the route provider itself, checking
+          // all previous collections.
+          try {
+            $entity_route = $this->routeProvider->getRouteByName($entity_info['links']['admin-form']);
+          }
+          // If the route was not found, skip this entity type.
+          catch (RouteNotFoundException $e) {
+            continue;
+          }
+        }
+        $path = $entity_route->getPath();
 
         $route = new Route(
           "$path/fields/{field_instance}",
@@ -85,20 +110,19 @@ protected function routes(RouteCollection $collection) {
             '_form' => '\Drupal\field_ui\FormDisplayOverview',
             '_title' => 'Manage form display',
           ) + $defaults,
-          array('_permission' => 'administer ' . $entity_type . ' form display')
+          array('_field_ui_form_mode_access' => 'administer ' . $entity_type . ' form display')
         );
         $collection->add("field_ui.form_display_overview_$entity_type", $route);
 
-        foreach (entity_get_form_modes($entity_type) as $form_mode => $form_mode_info) {
-          $route = new Route(
-            "$path/form-display/$form_mode",
-            array(
-              '_form' => '\Drupal\field_ui\FormDisplayOverview',
-              'mode' => $form_mode,
-            ) + $defaults,
-            array('_field_ui_form_mode_access' => 'administer ' . $entity_type . ' form display'));
-          $collection->add("field_ui.form_display_overview_$entity_type" . '_'. $form_mode, $route);
-        }
+        $route = new Route(
+          "$path/form-display/{form_mode_name}",
+          array(
+            '_form' => '\Drupal\field_ui\FormDisplayOverview',
+            'form_mode_name' => NULL,
+          ) + $defaults,
+          array('_field_ui_form_mode_access' => 'administer ' . $entity_type . ' form display')
+        );
+        $collection->add("field_ui.form_display_overview_form_mode_$entity_type", $route);
 
         $route = new Route(
           "$path/display",
@@ -106,22 +130,30 @@ protected function routes(RouteCollection $collection) {
             '_form' => '\Drupal\field_ui\DisplayOverview',
             '_title' => 'Manage display',
           ) + $defaults,
-          array('_permission' => 'administer ' . $entity_type . ' display')
+          array('_field_ui_view_mode_access' => 'administer ' . $entity_type . ' display')
         );
         $collection->add("field_ui.display_overview_$entity_type", $route);
 
-        foreach (entity_get_view_modes($entity_type) as $view_mode => $view_mode_info) {
-          $route = new Route(
-            "$path/display/$view_mode",
-            array(
-              '_form' => '\Drupal\field_ui\DisplayOverview',
-              'mode' => $view_mode,
-            ) + $defaults,
-            array('_field_ui_view_mode_access' => 'administer ' . $entity_type . ' display'));
-          $collection->add("field_ui.display_overview_$entity_type" . '_' . $view_mode, $route);
-        }
+        $route = new Route(
+          "$path/display/{view_mode_name}",
+          array(
+            '_form' => '\Drupal\field_ui\DisplayOverview',
+            'view_mode_name' => NULL,
+          ) + $defaults,
+          array('_field_ui_view_mode_access' => 'administer ' . $entity_type . ' display')
+        );
+        $collection->add("field_ui.display_overview_view_mode_$entity_type", $route);
       }
     }
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public static function getSubscribedEvents() {
+    $events = parent::getSubscribedEvents();
+    $events[RoutingEvents::DYNAMIC] = array('onDynamicRoutes', -100);
+    return $events;
+  }
+
 }
diff --git a/core/modules/field_ui/tests/modules/field_ui_test/lib/Drupal/field_ui_test/Entity/FieldUITestNoBundle.php b/core/modules/field_ui/tests/modules/field_ui_test/lib/Drupal/field_ui_test/Entity/FieldUITestNoBundle.php
index e6841c5571f1..ede77992dafe 100644
--- a/core/modules/field_ui/tests/modules/field_ui_test/lib/Drupal/field_ui_test/Entity/FieldUITestNoBundle.php
+++ b/core/modules/field_ui/tests/modules/field_ui_test/lib/Drupal/field_ui_test/Entity/FieldUITestNoBundle.php
@@ -20,8 +20,7 @@
  *   controllers = {
  *     "storage" = "Drupal\Core\Entity\DatabaseStorageController"
  *   },
- *   fieldable = TRUE,
- *   route_base_path = "field-ui-test-no-bundle/manage"
+ *   fieldable = TRUE
  * )
  */
 class FieldUITestNoBundle extends Entity {
diff --git a/core/modules/node/lib/Drupal/node/Entity/Node.php b/core/modules/node/lib/Drupal/node/Entity/Node.php
index f67d15b7bf19..dddecb3c11d7 100644
--- a/core/modules/node/lib/Drupal/node/Entity/Node.php
+++ b/core/modules/node/lib/Drupal/node/Entity/Node.php
@@ -49,12 +49,13 @@
  *   bundle_keys = {
  *     "bundle" = "type"
  *   },
- *   route_base_path = "admin/structure/types/manage/{bundle}",
+ *   bundle_entity_type = "node_type",
  *   permission_granularity = "bundle",
  *   links = {
  *     "canonical" = "node.view",
  *     "edit-form" = "node.page_edit",
- *     "version-history" = "node.revision_overview"
+ *     "version-history" = "node.revision_overview",
+ *     "admin-form" = "node.type_edit"
  *   }
  * )
  */
diff --git a/core/modules/node/node.local_tasks.yml b/core/modules/node/node.local_tasks.yml
index 24d9ef22c134..ecf629850a16 100644
--- a/core/modules/node/node.local_tasks.yml
+++ b/core/modules/node/node.local_tasks.yml
@@ -16,3 +16,7 @@ node.revision_overview:
   tab_root_id: node.view
   title: 'Revisions'
   weight: 20
+node.type_edit:
+  title: 'Edit'
+  route_name: node.type_edit
+  tab_root_id: node.type_edit
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index 79c7b4455a23..a860f3fee706 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -949,16 +949,6 @@ function node_menu() {
     'title' => 'List',
     'type' => MENU_DEFAULT_LOCAL_TASK,
   );
-  $items['admin/structure/types/manage/%node_type'] = array(
-    'title' => 'Edit content type',
-    'title callback' => 'entity_page_label',
-    'title arguments' => array(4),
-    'route_name' => 'node.type_edit',
-  );
-  $items['admin/structure/types/manage/%node_type/edit'] = array(
-    'title' => 'Edit',
-    'type' => MENU_DEFAULT_LOCAL_TASK,
-  );
   $items['node/add'] = array(
     'title' => 'Add content',
     'route_name' => 'node.add_page',
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Controller/EntityTestController.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Controller/EntityTestController.php
index ad22414e3586..a6a42eef7e6b 100644
--- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Controller/EntityTestController.php
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Controller/EntityTestController.php
@@ -30,4 +30,11 @@ public function testEdit(Request $request) {
     return entity_test_edit($entity);
   }
 
+  /**
+   * Returns an empty page.
+   */
+  public function testAdmin() {
+    return '';
+  }
+
 }
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTest.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTest.php
index 5ba70914cbfd..b4ad49b81e11 100644
--- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTest.php
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTest.php
@@ -37,10 +37,10 @@
  *     "bundle" = "type",
  *     "label" = "name"
  *   },
- *   route_base_path = "admin/structure/entity-test/manage/{bundle}",
  *   links = {
  *     "canonical" = "entity_test.render",
- *     "edit-form" = "entity_test.edit_entity_test"
+ *     "edit-form" = "entity_test.edit_entity_test",
+ *     "admin-form" = "entity_test.admin_entity_test"
  *   }
  * )
  */
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestMul.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestMul.php
index c3f9212c08b1..e73ff5e7ef64 100644
--- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestMul.php
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestMul.php
@@ -36,10 +36,10 @@
  *     "bundle" = "type",
  *     "label" = "name"
  *   },
- *   route_base_path = "entity_test_mul/structure/{bundle}",
  *   links = {
  *     "canonical" = "entity_test.edit_entity_test_mul",
- *     "edit-form" = "entity_test.edit_entity_test_mul"
+ *     "edit-form" = "entity_test.edit_entity_test_mul",
+ *     "admin-form" = "entity_test.admin_entity_test_mul"
  *   }
  * )
  */
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Routing/RouteSubscriber.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Routing/RouteSubscriber.php
index 0379d37705da..91a43cda4993 100644
--- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Routing/RouteSubscriber.php
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Routing/RouteSubscriber.php
@@ -39,6 +39,13 @@ protected function routes(RouteCollection $collection) {
         ))
       );
       $collection->add("entity_test.edit_$entity_type", $route);
+
+      $route = new Route(
+        "$entity_type/structure/{bundle}",
+        array('_content' => '\Drupal\entity_test\Controller\EntityTestController::testAdmin'),
+        array('_permission' => 'administer entity_test content')
+      );
+      $collection->add("entity_test.admin_$entity_type", $route);
     }
   }
 
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Entity/Term.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Entity/Term.php
index 0b7c3d64859b..a091d1a18a62 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Entity/Term.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Entity/Term.php
@@ -44,11 +44,12 @@
  *   bundle_keys = {
  *     "bundle" = "vid"
  *   },
+ *   bundle_entity_type = "taxonomy_vocabulary",
  *   links = {
  *     "canonical" = "taxonomy.term_page",
- *     "edit-form" = "taxonomy.term_edit"
+ *     "edit-form" = "taxonomy.term_edit",
+ *     "admin-form" = "taxonomy.overview_terms"
  *   },
- *   route_base_path = "admin/structure/taxonomy/manage/{bundle}",
  *   permission_granularity = "bundle"
  * )
  */
diff --git a/core/modules/taxonomy/taxonomy.local_tasks.yml b/core/modules/taxonomy/taxonomy.local_tasks.yml
index a04379e3a1e8..cdcb4eaccf9f 100644
--- a/core/modules/taxonomy/taxonomy.local_tasks.yml
+++ b/core/modules/taxonomy/taxonomy.local_tasks.yml
@@ -7,3 +7,12 @@ taxonomy.term_edit:
   title: 'Edit'
   route_name: taxonomy.term_edit
   tab_root_id: taxonomy.term_page
+
+taxonomy.overview_terms:
+  title: 'List'
+  route_name: taxonomy.overview_terms
+  tab_root_id: taxonomy.overview_terms
+taxonomy.vocabulary_edit:
+  title: 'Edit'
+  route_name: taxonomy.vocabulary_edit
+  tab_root_id: taxonomy.overview_terms
diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module
index 72222aa79283..c5f61530c47c 100644
--- a/core/modules/taxonomy/taxonomy.module
+++ b/core/modules/taxonomy/taxonomy.module
@@ -240,14 +240,6 @@ function taxonomy_menu() {
     'description' => 'Manage tagging, categorization, and classification of your content.',
     'route_name' => 'taxonomy.vocabulary_list',
   );
-  $items['admin/structure/taxonomy/list'] = array(
-    'title' => 'List',
-    'type' => MENU_DEFAULT_LOCAL_TASK,
-  );
-  $items['admin/structure/taxonomy/add'] = array(
-    'route_name' => 'taxonomy.vocabulary_add',
-    'type' => MENU_SIBLING_LOCAL_TASK,
-  );
 
   $items['taxonomy/term/%taxonomy_term'] = array(
     'title' => 'Taxonomy term',
diff --git a/core/modules/user/lib/Drupal/user/Entity/User.php b/core/modules/user/lib/Drupal/user/Entity/User.php
index fe90bfc74a88..c667d207cc0a 100644
--- a/core/modules/user/lib/Drupal/user/Entity/User.php
+++ b/core/modules/user/lib/Drupal/user/Entity/User.php
@@ -32,7 +32,6 @@
  *   admin_permission = "administer user",
  *   base_table = "users",
  *   uri_callback = "user_uri",
- *   route_base_path = "admin/config/people/accounts",
  *   label_callback = "user_label",
  *   fieldable = TRUE,
  *   translatable = TRUE,
@@ -42,7 +41,8 @@
  *   },
  *   links = {
  *     "canonical" = "user.view",
- *     "edit-form" = "user.edit"
+ *     "edit-form" = "user.edit",
+ *     "admin-form" = "user.account_settings"
  *   }
  * )
  */
-- 
GitLab