From 04f662ff72c3030615e7e454eeada7277a72e360 Mon Sep 17 00:00:00 2001
From: webchick <webchick@24967.no-reply.drupal.org>
Date: Mon, 7 Oct 2013 12:13:29 -0700
Subject: [PATCH] Issue #2027115 by dawehner, tim.plunkett: Allow views to
 override existing routing items.

---
 .../Core/EventSubscriber/AccessSubscriber.php |   2 +-
 .../lib/Drupal/Core/Routing/RouteCompiler.php |   2 +-
 .../rest/Plugin/views/display/RestExport.php  |  12 +-
 .../Drupal/rest/Tests/CollectRoutesTest.php   |   8 +
 .../lib/Drupal/user/Controller/UserAdmin.php  | 162 +++++++++++++
 core/modules/user/user.admin.inc              |  85 -------
 core/modules/user/user.local_actions.yml      |   5 +
 core/modules/user/user.module                 |  14 +-
 core/modules/user/user.routing.yml            |   7 +
 .../views/EventSubscriber/RouteSubscriber.php | 147 +++++++++++-
 .../views/display/DisplayRouterInterface.php  |  17 +-
 .../Plugin/views/display/PathPluginBase.php   | 120 +++++++++-
 .../views/Tests/Plugin/DisplayPageTest.php    |   2 +-
 .../EventSubscriber/RouteSubscriberTest.php   | 224 ++++++++++++++++++
 .../Plugin/display/PathPluginBaseTest.php     | 187 +++++++++++++++
 core/modules/views/views.module               |   3 +
 core/modules/views/views.services.yml         |   3 +-
 .../views_ui/Tests/ViewListControllerTest.php |   4 +-
 18 files changed, 886 insertions(+), 118 deletions(-)
 create mode 100644 core/modules/user/lib/Drupal/user/Controller/UserAdmin.php
 create mode 100644 core/modules/user/user.local_actions.yml
 create mode 100644 core/modules/views/tests/Drupal/views/Tests/EventSubscriber/RouteSubscriberTest.php
 create mode 100644 core/modules/views/tests/Drupal/views/Tests/Plugin/display/PathPluginBaseTest.php

diff --git a/core/lib/Drupal/Core/EventSubscriber/AccessSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/AccessSubscriber.php
index 6f18839269d1..33e7274ea651 100644
--- a/core/lib/Drupal/Core/EventSubscriber/AccessSubscriber.php
+++ b/core/lib/Drupal/Core/EventSubscriber/AccessSubscriber.php
@@ -71,7 +71,7 @@ public function onRoutingRouteAlterSetAccessCheck(RouteBuildEvent $event) {
   static function getSubscribedEvents() {
     $events[KernelEvents::REQUEST][] = array('onKernelRequestAccessCheck', 30);
     // Setting very low priority to ensure access checks are run after alters.
-    $events[RoutingEvents::ALTER][] = array('onRoutingRouteAlterSetAccessCheck', 0);
+    $events[RoutingEvents::ALTER][] = array('onRoutingRouteAlterSetAccessCheck', -50);
 
     return $events;
   }
diff --git a/core/lib/Drupal/Core/Routing/RouteCompiler.php b/core/lib/Drupal/Core/Routing/RouteCompiler.php
index 771669a14d04..3f3af5a3a7c3 100644
--- a/core/lib/Drupal/Core/Routing/RouteCompiler.php
+++ b/core/lib/Drupal/Core/Routing/RouteCompiler.php
@@ -124,7 +124,7 @@ public static function getFit($path) {
    * @return string
    *   The path string, stripped of placeholders that have default values.
    */
-  protected static function getPathWithoutDefaults(Route $route) {
+  public static function getPathWithoutDefaults(Route $route) {
     $path = $route->getPath();
     $defaults = $route->getDefaults();
 
diff --git a/core/modules/rest/lib/Drupal/rest/Plugin/views/display/RestExport.php b/core/modules/rest/lib/Drupal/rest/Plugin/views/display/RestExport.php
index b8d7f3bc9195..19bc077a72eb 100644
--- a/core/modules/rest/lib/Drupal/rest/Plugin/views/display/RestExport.php
+++ b/core/modules/rest/lib/Drupal/rest/Plugin/views/display/RestExport.php
@@ -8,6 +8,8 @@
 namespace Drupal\rest\Plugin\views\display;
 
 
+use Drupal\Core\KeyValueStore\KeyValueStoreInterface;
+use Drupal\Core\Routing\RouteProviderInterface;
 use Drupal\views\Annotation\ViewsDisplay;
 use Drupal\Core\Annotation\Translation;
 use Drupal\Core\ContentNegotiation;
@@ -95,13 +97,17 @@ class RestExport extends PathPluginBase {
    *   The plugin_id for the plugin instance.
    * @param array $plugin_definition
    *   The plugin implementation definition.
+   * @param \Drupal\Core\Routing\RouteProviderInterface $route_provider
+   *   The route provider
+   * @param \Drupal\Core\KeyValueStore\KeyValueStoreInterface $state
+   *   The state key value store.
    * @param \Drupal\Core\ContentNegotiation $content_negotiation
    *   The content negotiation library.
    * @param \Symfony\Component\HttpFoundation\Request $request
    *   The request object.
    */
-  public function __construct(array $configuration, $plugin_id, array $plugin_definition, ContentNegotiation $content_negotiation, Request $request) {
-    parent::__construct($configuration, $plugin_id, $plugin_definition);
+  public function __construct(array $configuration, $plugin_id, array $plugin_definition, RouteProviderInterface $route_provider, KeyValueStoreInterface $state, ContentNegotiation $content_negotiation, Request $request) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition, $route_provider, $state);
     $this->contentNegotiation = $content_negotiation;
     $this->request = $request;
   }
@@ -114,6 +120,8 @@ public static function create(ContainerInterface $container, array $configuratio
       $configuration,
       $plugin_id,
       $plugin_definition,
+      $container->get('router.route_provider'),
+      $container->get('state'),
       $container->get('content_negotiation'),
       $container->get('request')
     );
diff --git a/core/modules/rest/tests/Drupal/rest/Tests/CollectRoutesTest.php b/core/modules/rest/tests/Drupal/rest/Tests/CollectRoutesTest.php
index 7f27170d3d6e..ae4bb4339846 100644
--- a/core/modules/rest/tests/Drupal/rest/Tests/CollectRoutesTest.php
+++ b/core/modules/rest/tests/Drupal/rest/Tests/CollectRoutesTest.php
@@ -77,6 +77,14 @@ protected function setUp() {
       ->getMock();
     $container->set('plugin.manager.views.access', $access_manager);
 
+    $route_provider = $this->getMockBuilder('\Drupal\Core\Routing\RouteProviderInterface')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $container->set('router.route_provider', $route_provider);
+
+    $state = $this->getMock('\Drupal\Core\KeyValueStore\KeyValueStoreInterface');
+    $container->set('state', $state);
+
     $style_manager = $this->getMockBuilder('\Drupal\views\Plugin\ViewsPluginManager')
       ->disableOriginalConstructor()
       ->getMock();
diff --git a/core/modules/user/lib/Drupal/user/Controller/UserAdmin.php b/core/modules/user/lib/Drupal/user/Controller/UserAdmin.php
new file mode 100644
index 000000000000..c5da47753957
--- /dev/null
+++ b/core/modules/user/lib/Drupal/user/Controller/UserAdmin.php
@@ -0,0 +1,162 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\user\Controller\UserAdmin.
+ */
+
+namespace Drupal\user\Controller;
+
+use Drupal\Core\Controller\ControllerBase;
+use Drupal\Core\Database\Connection;
+use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
+use Drupal\Core\Entity\Query\QueryInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\user\UserStorageControllerInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Provides a user administrative listing.
+ *
+ * @todo Convert this to a entity list controller once table sort is supported.
+ */
+class UserAdmin extends ControllerBase implements ContainerInjectionInterface {
+
+  /**
+   * The database connection.
+   *
+   * @var \Drupal\Core\Database\Connection
+   */
+  protected $connection;
+
+  /**
+   * The module handler.
+   *
+   * @var \Drupal\Core\Extension\ModuleHandlerInterface
+   */
+  protected $moduleHandler;
+
+  /**
+   * The user storage controller.
+   *
+   * @var \Drupal\user\UserStorageControllerInterface
+   */
+  protected $storageController;
+
+  /**
+   * The entity query.
+   *
+   * @var \Drupal\Core\Entity\Query\QueryInterface
+   */
+  protected $entityQuery;
+
+  /**
+   * Constructs a new UserAdmin object.
+   *
+   * @param \Drupal\Core\Database\Connection $connection
+   *   The database connection.
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   The module handler.
+   * @param \Drupal\user\UserStorageControllerInterface $storage_controller
+   *   The user storage controller.
+   * @param \Drupal\Core\Entity\Query\QueryInterface $entity_query
+   *   The entity query.
+   */
+  public function __construct(Connection $connection, ModuleHandlerInterface $module_handler, UserStorageControllerInterface $storage_controller, QueryInterface $entity_query) {
+    $this->connection = $connection;
+    $this->moduleHandler = $module_handler;
+    $this->storageController = $storage_controller;
+    $this->entityQuery = $entity_query;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('database'),
+      $container->get('module_handler'),
+      $container->get('entity.manager')->getStorageController('user'),
+      $container->get('entity.query')->get('user')
+    );
+  }
+
+  /**
+   * User administrative listing.
+   *
+   * @return array
+   *   A render array as expected by drupal_render().
+   */
+  public function userList() {
+    $header = array(
+      'username' => array('data' => $this->t('Username'), 'field' => 'name', 'specifier' => 'name'),
+      'status' => array('data' => $this->t('Status'), 'field' => 'status', 'specifier' => 'status', 'class' => array(RESPONSIVE_PRIORITY_LOW)),
+      'roles' => array('data' => $this->t('Roles'), 'class' => array(RESPONSIVE_PRIORITY_LOW)),
+      'member_for' => array('data' => $this->t('Member for'), 'field' => 'created', 'specifier' => 'created', 'sort' => 'desc', 'class' => array(RESPONSIVE_PRIORITY_LOW)),
+      'access' => array('data' => $this->t('Last access'), 'field' => 'access', 'specifier' => 'access', 'class' => array(RESPONSIVE_PRIORITY_LOW)),
+      'operations' => $this->t('Operations'),
+    );
+
+    $this->entityQuery->condition('uid', 0, '<>');
+    $this->entityQuery->pager(50);
+    $this->entityQuery->tableSort($header);
+    $uids = $this->entityQuery->execute();
+    $accounts = $this->storageController->loadMultiple($uids);
+
+    $destination = drupal_get_destination();
+    $status = array($this->t('blocked'), $this->t('active'));
+    $roles = array_map('\Drupal\Component\Utility\String::checkPlain', user_role_names(TRUE));
+    unset($roles[DRUPAL_AUTHENTICATED_RID]);
+    $options = array();
+    foreach ($accounts as $account) {
+      $users_roles = array();
+      foreach ($account->getRoles() as $role) {
+        if (isset($roles[$role])) {
+          $users_roles[] = $roles[$role];
+        }
+      }
+      asort($users_roles);
+      $options[$account->id()]['username']['data'] = array(
+        '#theme' => 'username',
+        '#account' => $account,
+      );
+      $options[$account->id()]['status'] = $status[$account->isActive()];
+      $options[$account->id()]['roles']['data'] = array(
+        '#theme' => 'item_list',
+        '#items' => $users_roles,
+      );
+      $options[$account->id()]['member_for'] = format_interval(REQUEST_TIME - $account->getCreatedTime());
+      $options[$account->id()]['access'] = $account->access ? $this->t('@time ago', array('@time' => format_interval(REQUEST_TIME - $account->getLastAccessedTime()))) : t('never');
+      $links = array();
+      $links['edit'] = array(
+        'title' => $this->t('Edit'),
+        'href' => 'user/' . $account->id() . '/edit',
+        'query' => $destination,
+      );
+      if ($this->moduleHandler->invoke('content_translation', 'translate_access', array($account))) {
+        $links['translate'] = array(
+          'title' => $this->t('Translate'),
+          'href' => 'user/' . $account->id() . '/translations',
+          'query' => $destination,
+        );
+      }
+      $options[$account->id()]['operations']['data'] = array(
+        '#type' => 'operations',
+        '#links' => $links,
+      );
+    }
+
+    $build['accounts'] = array(
+      '#theme' => 'table',
+      '#header' => $header,
+      '#rows' => $options,
+      '#empty' => $this->t('No people available.'),
+    );
+    $build['pager'] = array(
+      '#theme' =>'pager',
+    );
+
+    return $build;
+  }
+
+}
diff --git a/core/modules/user/user.admin.inc b/core/modules/user/user.admin.inc
index 4caec4352217..9a6ca2a8edc4 100644
--- a/core/modules/user/user.admin.inc
+++ b/core/modules/user/user.admin.inc
@@ -5,91 +5,6 @@
  * Admin page callback file for the user module.
  */
 
-/**
- * Page callback: User administration page.
- */
-function user_admin_account() {
-  $header = array(
-    'username' => array('data' => t('Username'), 'field' => 'u.name'),
-    'status' => array('data' => t('Status'), 'field' => 'u.status', 'class' => array(RESPONSIVE_PRIORITY_LOW)),
-    'roles' => array('data' => t('Roles'), 'class' => array(RESPONSIVE_PRIORITY_LOW)),
-    'member_for' => array('data' => t('Member for'), 'field' => 'u.created', 'sort' => 'desc', 'class' => array(RESPONSIVE_PRIORITY_LOW)),
-    'access' => array('data' => t('Last access'), 'field' => 'u.access', 'class' => array(RESPONSIVE_PRIORITY_LOW)),
-    'operations' => t('Operations'),
-  );
-
-  $query = db_select('users', 'u');
-  $query->condition('u.uid', 0, '<>');
-
-  $count_query = clone $query;
-  $count_query->addExpression('COUNT(u.uid)');
-
-  $query = $query
-    ->extend('Drupal\Core\Database\Query\PagerSelectExtender')
-    ->extend('Drupal\Core\Database\Query\TableSortExtender');
-  $query
-    ->fields('u', array('uid'))
-    ->limit(50)
-    ->orderByHeader($header)
-    ->setCountQuery($count_query);
-  $uids = $query->execute()
-    ->fetchCol('uid');
-
-  $destination = drupal_get_destination();
-  $accounts = user_load_multiple($uids);
-
-  foreach ($accounts as $account) {
-    $users_roles = $account->getRoles();
-    unset($users_roles[0]);
-    asort($users_roles);
-    $username = array(
-      '#theme' => 'username',
-      '#account' => $account,
-    );
-    $item_list = array(
-      '#theme' => 'item_list',
-      '#items' => $users_roles,
-    );
-    $options[$account->id()] = array(
-      'username' => drupal_render($username),
-      'status' => $account->isActive() ? t('active') : t('blocked'),
-      'roles' => drupal_render($item_list),
-      'member_for' => format_interval(REQUEST_TIME - $account->getCreatedTime()),
-      'access' => $account->getLastAccessedTime() ? t('@time ago', array('@time' => format_interval(REQUEST_TIME - $account->getLastAccessedTime()))) : t('never'),
-    );
-    $links = array();
-    $links['edit'] = array(
-      'title' => t('Edit'),
-      'href' => 'user/' . $account->id() . '/edit',
-      'query' => $destination,
-    );
-    if (module_invoke('content_translation', 'translate_access', $account)) {
-      $links['translate'] = array(
-        'title' => t('Translate'),
-        'href' => 'user/' . $account->id() . '/translations',
-        'query' => $destination,
-      );
-    }
-    $options[$account->id()]['operations']['data'] = array(
-      '#type' => 'operations',
-      '#links' => $links,
-    );
-
-  }
-
-  $form['accounts'] = array(
-    '#theme' => 'table',
-    '#header' => $header,
-    '#rows' => $options,
-    '#empty' => t('No people available.'),
-  );
-  $form['pager'] = array(
-    '#theme' =>'pager',
-  );
-
-  return $form;
-}
-
 /**
  * Returns HTML for an individual permission description.
  *
diff --git a/core/modules/user/user.local_actions.yml b/core/modules/user/user.local_actions.yml
new file mode 100644
index 000000000000..9a5777254ade
--- /dev/null
+++ b/core/modules/user/user.local_actions.yml
@@ -0,0 +1,5 @@
+user_admin_create:
+  route_name: user.admin_create
+  title: 'Add user'
+  appears_on:
+    - user.admin_account
diff --git a/core/modules/user/user.module b/core/modules/user/user.module
index f233607292a6..aa2b2e8d2897 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -778,11 +778,13 @@ function user_menu() {
   $items['admin/people'] = array(
     'title' => 'People',
     'description' => 'Manage user accounts, roles, and permissions.',
-    'page callback' => 'user_admin_account',
-    'access arguments' => array('administer users'),
+    'route_name' => 'user.admin_account',
     'position' => 'left',
     'weight' => -4,
-    'file' => 'user.admin.inc',
+  );
+  $items['admin/people/list'] = array(
+    'title' => 'List',
+    'type' => MENU_DEFAULT_LOCAL_TASK,
   );
   // Permissions and role forms.
   $items['admin/people/permissions'] = array(
@@ -812,12 +814,6 @@ function user_menu() {
     'context' => MENU_CONTEXT_INLINE,
   );
 
-  $items['admin/people/create'] = array(
-    'title' => 'Add user',
-    'route_name' => 'user.admin_create',
-    'type' => MENU_LOCAL_ACTION,
-  );
-
   // Administration pages.
   $items['admin/config/people'] = array(
     'title' => 'People',
diff --git a/core/modules/user/user.routing.yml b/core/modules/user/user.routing.yml
index d220da77685f..169343bb4b6e 100644
--- a/core/modules/user/user.routing.yml
+++ b/core/modules/user/user.routing.yml
@@ -40,6 +40,13 @@ user.account_settings:
   requirements:
     _permission: 'administer account settings'
 
+user.admin_account:
+  path: '/admin/people'
+  defaults:
+    _controller: '\Drupal\user\Controller\UserAdmin::userList'
+  requirements:
+    _permission: 'administer users'
+
 user.admin_create:
   path: '/admin/people/create'
   defaults:
diff --git a/core/modules/views/lib/Drupal/views/EventSubscriber/RouteSubscriber.php b/core/modules/views/lib/Drupal/views/EventSubscriber/RouteSubscriber.php
index e3dbde33487b..2ab8cdbd660d 100644
--- a/core/modules/views/lib/Drupal/views/EventSubscriber/RouteSubscriber.php
+++ b/core/modules/views/lib/Drupal/views/EventSubscriber/RouteSubscriber.php
@@ -7,24 +7,104 @@
 
 namespace Drupal\views\EventSubscriber;
 
+use Drupal\Component\Utility\MapArray;
+use Drupal\Component\Utility\NestedArray;
+use Drupal\Core\DestructableInterface;
+use Drupal\Core\Entity\EntityManager;
+use Drupal\Core\KeyValueStore\KeyValueStoreInterface;
 use Drupal\Core\Routing\RouteBuildEvent;
 use Drupal\Core\Routing\RoutingEvents;
 use Drupal\views\Plugin\views\display\DisplayRouterInterface;
+use Drupal\views\ViewExecutable;
 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 
 /**
  * Builds up the routes of all views.
+ *
+ * The general idea is to execute first all alter hooks to determine which
+ * routes are overridden by views. This information is used to determine which
+ * views have to be added by views in the dynamic event.
+ *
+ * @see \Drupal\views\Plugin\views\display\PathPluginBase
  */
-class RouteSubscriber implements EventSubscriberInterface {
+class RouteSubscriber implements EventSubscriberInterface, DestructableInterface {
+
+  /**
+   * Stores a list of view,display IDs which haven't be used in the alter event.
+   *
+   * @var array
+   */
+  protected $viewsDisplayPairs;
+
+  /**
+   * The view storage controller.
+   *
+   * @var \Drupal\Core\Entity\EntityStorageControllerInterface
+   */
+  protected $viewStorageController;
+
+  /**
+   * The state key value store.
+   *
+   * @var \Drupal\Core\KeyValueStore\KeyValueStoreInterface
+   */
+  protected $state;
+
+  /**
+   * Stores an array of route names keyed by view_id.display_id.
+   *
+   * @var array
+   */
+  protected $viewRouteNames = array();
+
+  /**
+   * Constructs a \Drupal\views\EventSubscriber\RouteSubscriber instance.
+   *
+   * @param \Drupal\Core\Entity\EntityManager $entity_manager
+   *   The entity manager.
+   * @param \Drupal\Core\KeyValueStore\KeyValueStoreInterface $state
+   *   The state key value store.
+   */
+  public function __construct(EntityManager $entity_manager, KeyValueStoreInterface $state) {
+    $this->viewStorageController = $entity_manager->getStorageController('view');
+    $this->state = $state;
+  }
+
+  /**
+   * Resets the internal state of the route subscriber.
+   */
+  public function reset() {
+    $this->viewsDisplayPairs = NULL;
+  }
 
   /**
    * {@inheritdoc}
    */
   public static function getSubscribedEvents() {
     $events[RoutingEvents::DYNAMIC] = 'dynamicRoutes';
+    $events[RoutingEvents::ALTER] = 'alterRoutes';
     return $events;
   }
 
+  /**
+   * Gets all the views and display IDs using a route.
+   */
+  protected function getViewsDisplayIDsWithRoute() {
+    if (!isset($this->viewsDisplayPairs)) {
+      $this->viewsDisplayPairs = array();
+
+      // @todo Convert this method to some service.
+      $views = $this->getApplicableViews();
+      foreach ($views as $data) {
+        list($view, $display_id) = $data;
+        $id = $view->storage->id();
+        $this->viewsDisplayPairs[] = $id . '.' . $display_id;
+      }
+      $this->viewsDisplayPairs = MapArray::copyValuesToKeys($this->viewsDisplayPairs);
+    }
+    return $this->viewsDisplayPairs;
+  }
+
   /**
    * Adds routes defined by all views.
    *
@@ -34,16 +114,67 @@ public static function getSubscribedEvents() {
   public function dynamicRoutes(RouteBuildEvent $event) {
     $collection = $event->getRouteCollection();
 
-    $views = views_get_applicable_views('uses_route');
-    foreach ($views as $data) {
-      list($view, $display_id) = $data;
-      if ($view->setDisplay($display_id) && $display = $view->displayHandlers->get($display_id)) {
-        if ($display instanceof DisplayRouterInterface) {
-          $display->collectRoutes($collection);
+    foreach ($this->getViewsDisplayIDsWithRoute() as $pair) {
+      list($view_id, $display_id) = explode('.', $pair);
+      $view = $this->viewStorageController->load($view_id);
+      // @todo This should have an executable factory injected.
+      if (($view = $view->getExecutable()) && $view instanceof ViewExecutable) {
+        if ($view->setDisplay($display_id) && $display = $view->displayHandlers->get($display_id)) {
+          if ($display instanceof DisplayRouterInterface) {
+            $view_route_names = (array) $display->collectRoutes($collection);
+
+            $this->viewRouteNames += $view_route_names;
+          }
         }
+        $view->destroy();
       }
-      $view->destroy();
     }
+
+    $this->state->set('views.view_route_names', $this->viewRouteNames);
+  }
+
+  /**
+   * Alters existing routes.
+   *
+   * @param \Drupal\Core\Routing\RouteBuildEvent $event
+   *   The route building event.
+   */
+  public function alterRoutes(RouteBuildEvent $event) {
+    foreach ($this->getViewsDisplayIDsWithRoute() as $pair) {
+      list($view_id, $display_id) = explode('.', $pair);
+      $view = $this->viewStorageController->load($view_id);
+      // @todo This should have an executable factory injected.
+      if (($view = $view->getExecutable()) && $view instanceof ViewExecutable) {
+        if ($view->setDisplay($display_id) && $display = $view->displayHandlers->get($display_id)) {
+          if ($display instanceof DisplayRouterInterface) {
+            // If the display returns TRUE a route item was found, so it does not
+            // have to be added.
+            $view_route_names = $display->alterRoutes($event->getRouteCollection());
+            $this->viewRouteNames += $view_route_names;
+            foreach ($view_route_names as $id_display => $route_name) {
+              unset($this->viewsDisplayPairs[$id_display]);
+            }
+          }
+        }
+        $view->destroy();
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function destruct() {
+    $this->state->set('views.view_route_names', $this->viewRouteNames);
+  }
+
+  /**
+   * Returns all views/display combinations with routes.
+   *
+   * @see views_get_applicable_views()
+   */
+  protected function getApplicableViews() {
+    return views_get_applicable_views('uses_route');
   }
 
 }
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayRouterInterface.php b/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayRouterInterface.php
index 68cbe688ee02..7c305176cd0e 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayRouterInterface.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayRouterInterface.php
@@ -7,14 +7,14 @@
 
 namespace Drupal\views\Plugin\views\display;
 
+use Symfony\Component\Routing\RouteCollection;
+
 /**
  * Defines an interface for displays that can collect routes.
  *
  * In addition to implementing the interface, specify 'uses_routes' in the
  * plugin definition.
  */
-use Symfony\Component\Routing\RouteCollection;
-
 interface DisplayRouterInterface {
 
   /**
@@ -25,4 +25,17 @@ interface DisplayRouterInterface {
    */
   public function collectRoutes(RouteCollection $collection);
 
+  /**
+   * Alters a collection of routes and replaces definitions to the view.
+   *
+   * Most of the collections won't have the needed route, so by the return value
+   * the method can specify to break the search.
+   *
+   * @param \Symfony\Component\Routing\RouteCollection $collection
+   *
+   * @return array
+   *   Returns a list of "$view_id.$display_id" elements which got overridden.
+   */
+  public function alterRoutes(RouteCollection $collection);
+
 }
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/display/PathPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/display/PathPluginBase.php
index 79795d217f86..731f6043104d 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/display/PathPluginBase.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/display/PathPluginBase.php
@@ -7,8 +7,12 @@
 
 namespace Drupal\views\Plugin\views\display;
 
+use Drupal\Core\KeyValueStore\KeyValueStoreInterface;
+use Drupal\Core\Routing\RouteCompiler;
+use Drupal\Core\Routing\RouteProviderInterface;
 use Drupal\views\Views;
 
+use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
 use Symfony\Component\Routing\Route;
@@ -16,9 +20,59 @@
 
 /**
  * The base display plugin for path/callbacks. This is used for pages and feeds.
+ *
+ * @see \Drupal\views\EventSubscriber\RouteSubscriber
  */
 abstract class PathPluginBase extends DisplayPluginBase implements DisplayRouterInterface {
 
+  /**
+   * The route provider.
+   *
+   * @var \Drupal\Core\Routing\RouteProviderInterface
+   */
+  protected $routeProvider;
+
+  /**
+   * The state key value store.
+   *
+   * @var \Drupal\Core\KeyValueStore\KeyValueStoreInterface
+   */
+  protected $state;
+
+  /**
+   * Constructs a PathPluginBase object.
+   *
+   * @param array $configuration
+   *   A configuration array containing information about the plugin instance.
+   * @param string $plugin_id
+   *   The plugin_id for the plugin instance.
+   * @param array $plugin_definition
+   *   The plugin implementation definition.
+   * @param \Drupal\Core\Routing\RouteProviderInterface $route_provider
+   *   The route provider.
+   * @param \Drupal\Core\KeyValueStore\KeyValueStoreInterface $state
+   *   The state key value store.
+   */
+  public function __construct(array $configuration, $plugin_id, array $plugin_definition, RouteProviderInterface $route_provider, KeyValueStoreInterface $state) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+
+    $this->routeProvider = $route_provider;
+    $this->state = $state;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, array $plugin_definition) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $container->get('router.route_provider'),
+      $container->get('state')
+    );
+  }
+
   /**
    * Overrides \Drupal\views\Plugin\views\display\DisplayPluginBase::hasPath().
    */
@@ -60,12 +114,17 @@ protected function defineOptions() {
   }
 
   /**
-   * {@inheritdoc}
+   * Generates a route entry for a given view and display.
+   *
+   * @param string $view_id
+   *   The ID of the view.
+   * @param string $display_id
+   *   The current display ID.
+   *
+   * @return \Symfony\Component\Routing\Route
+   *   The route for the view.
    */
-  public function collectRoutes(RouteCollection $collection) {
-    $view_id = $this->view->storage->id();
-    $display_id = $this->display['id'];
-
+  protected function getRoute($view_id, $display_id) {
     $defaults = array(
       '_controller' => 'Drupal\views\Routing\ViewPageController::handle',
       'view_id' => $view_id,
@@ -80,7 +139,7 @@ public function collectRoutes(RouteCollection $collection) {
     $arg_counter = 0;
 
     $this->view->initHandlers();
-    $view_arguments = $this->view->argument;
+    $view_arguments = (array) $this->view->argument;
 
     $argument_ids = array_keys($view_arguments);
     $total_arguments = count($argument_ids);
@@ -126,8 +185,50 @@ public function collectRoutes(RouteCollection $collection) {
       $access_plugin = Views::pluginManager('access')->createInstance('none');
     }
     $access_plugin->alterRouteDefinition($route);
+    return $route;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function collectRoutes(RouteCollection $collection) {
+    $view_id = $this->view->storage->id();
+    $display_id = $this->display['id'];
+
+    $route = $this->getRoute($view_id, $display_id);
 
     $collection->add("view.$view_id.$display_id", $route);
+    return array("$view_id.$display_id" => "view.$view_id.$display_id");
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function alterRoutes(RouteCollection $collection) {
+    $view_route_names = array();
+    $view_path = $this->getPath();
+    foreach ($collection->all() as $name => $route) {
+      // Find all paths which match the path of the current display..
+      $route_path = RouteCompiler::getPathWithoutDefaults($route);
+      $route_path = RouteCompiler::getPatternOutline($route_path);
+      // Ensure that we don't override a route which is already controlled by
+      // views.
+      if (!$route->hasDefault('view_id') && ('/' . $view_path == $route_path)) {
+        // @todo Figure out whether we need to merge some settings (like
+        // requirements).
+
+        // Replace the existing route with a new one based on views.
+        $collection->remove($name);
+
+        $view_id = $this->view->storage->id();
+        $display_id = $this->display['id'];
+        $route = $this->getRoute($view_id, $display_id);
+        $collection->add($name, $route);
+        $view_route_names[$view_id . '.' . $display_id] = $name;
+      }
+    }
+
+    return $view_route_names;
   }
 
   /**
@@ -157,9 +258,14 @@ public function executeHookMenu($callbacks) {
 
     $path = implode('/', $bits);
 
+    $view_route_names = $this->state->get('views.view_route_names') ?: array();
+
     if ($path) {
+      // Some views might override existing paths, so we have to set the route
+      // name based upon the altering.
+      $view_id_display =  "{$this->view->storage->id()}.{$this->display['id']}";
       $items[$path] = array(
-        'route_name' => "view.{$this->view->storage->id()}.{$this->display['id']}",
+        'route_name' => isset($view_route_names[$view_id_display]) ? $view_route_names[$view_id_display] : "view.$view_id_display",
         // Identify URL embedded arguments and correlate them to a handler.
         'load arguments'  => array($this->view->storage->id(), $this->display['id'], '%index'),
       );
diff --git a/core/modules/views/lib/Drupal/views/Tests/Plugin/DisplayPageTest.php b/core/modules/views/lib/Drupal/views/Tests/Plugin/DisplayPageTest.php
index 7944e0597f93..f4209432bf48 100644
--- a/core/modules/views/lib/Drupal/views/Tests/Plugin/DisplayPageTest.php
+++ b/core/modules/views/lib/Drupal/views/Tests/Plugin/DisplayPageTest.php
@@ -93,7 +93,7 @@ public function testPageResponses() {
    * Checks that the router items are properly registered
    */
   public function testPageRouterItems() {
-    $subscriber = new RouteSubscriber();
+    $subscriber = new RouteSubscriber($this->container->get('entity.manager'), $this->container->get('state'));
     $collection = new RouteCollection();
     $subscriber->dynamicRoutes(new RouteBuildEvent($collection, 'dynamic_routes'));
 
diff --git a/core/modules/views/tests/Drupal/views/Tests/EventSubscriber/RouteSubscriberTest.php b/core/modules/views/tests/Drupal/views/Tests/EventSubscriber/RouteSubscriberTest.php
new file mode 100644
index 000000000000..a6a6d7cdc432
--- /dev/null
+++ b/core/modules/views/tests/Drupal/views/Tests/EventSubscriber/RouteSubscriberTest.php
@@ -0,0 +1,224 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\views\Tests\EventSubscriber\RouteSubscriberTest.
+ */
+
+namespace Drupal\views\Tests\EventSubscriber;
+
+use Drupal\Core\Routing\RouteBuildEvent;
+use Drupal\Tests\UnitTestCase;
+use Drupal\views\EventSubscriber\RouteSubscriber;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RouteCollection;
+
+/**
+ * Tests the views route subscriber.
+ *
+ * @see \Drupal\views\EventSubscriber\RouteSubscriber
+ */
+class RouteSubscriberTest extends UnitTestCase {
+
+  /**
+   * The mocked entity manager.
+   *
+   * @var \Drupal\Core\Entity\EntityManager|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $entityManager;
+
+  /**
+   * The mocked view storage controller.
+   *
+   * @var \Drupal\views\ViewStorageController|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $viewStorageController;
+
+  /**
+   * The tested views route subscriber.
+   *
+   * @var \Drupal\views\EventSubscriber\RouteSubscriber|\Drupal\views\Tests\EventSubscriber\TestRouteSubscriber
+   */
+  protected $routeSubscriber;
+
+  /**
+   * The mocked key value storage.
+   *
+   * @var \Drupal\Core\KeyValueStore\KeyValueStoreInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $state;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Views route subscriber',
+      'description' => 'Tests the views route subscriber.',
+      'group' => 'Views plugins',
+    );
+  }
+
+  protected function setUp() {
+    $this->entityManager = $this->getMockBuilder('\Drupal\Core\Entity\EntityManager')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $this->viewStorageController = $this->getMockBuilder('\Drupal\views\ViewStorageController')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $this->entityManager->expects($this->any())
+      ->method('getStorageController')
+      ->with('view')
+      ->will($this->returnValue($this->viewStorageController));
+    $this->state = $this->getMock('\Drupal\Core\KeyValueStore\KeyValueStoreInterface');
+    $this->routeSubscriber = new TestRouteSubscriber($this->entityManager, $this->state);
+  }
+
+  /**
+   * Tests the dynamicRoutes method.
+   *
+   * @see \Drupal\views\EventSubscriber\RouteSubscriber::dynamicRoutes()
+   */
+  public function testDynamicRoutes() {
+    $collection = new RouteCollection();
+    $route_event = new RouteBuildEvent($collection, 'views');
+
+    list($view, $executable, $display_1, $display_2) = $this->setupMocks();
+
+    $display_1->expects($this->once())
+      ->method('collectRoutes')
+      ->will($this->returnValue(array('test_id.page_1' => 'views.test_id.page_1')));
+    $display_2->expects($this->once())
+      ->method('collectRoutes')
+      ->will($this->returnValue(array('test_id.page_2' => 'views.test_id.page_2')));
+
+    $this->assertNull($this->routeSubscriber->dynamicRoutes($route_event));
+
+    $this->state->expects($this->once())
+      ->method('set')
+      ->with('views.view_route_names', array('test_id.page_1' => 'views.test_id.page_1', 'test_id.page_2' => 'views.test_id.page_2'));
+    $this->routeSubscriber->destruct();
+  }
+
+  /**
+   * Tests the alterRoutes method.
+   *
+   * @see \Drupal\views\EventSubscriber\RouteSubscriber::alterRoutes()
+   */
+  public function testAlterRoutes() {
+    $collection = new RouteCollection();
+    $collection->add('test_route', new Route('test_route', array('_controller' => 'Drupal\Tests\Core\Controller\TestController')));
+    $route_2 = new Route('test_route/example', array('_controller' => 'Drupal\Tests\Core\Controller\TestController'));
+    $collection->add('test_route_2', $route_2);
+
+    $route_event = new RouteBuildEvent($collection, 'views');
+
+    list($view, $executable, $display_1, $display_2) = $this->setupMocks();
+
+    // The page_1 display overrides an existing route, so the dynamicRoutes
+    // should only call the second display.
+    $display_1->expects($this->once())
+      ->method('alterRoutes')
+      ->will($this->returnValue(array('test_id.page_1' => 'test_route')));
+    $display_1->expects($this->never())
+      ->method('collectRoutes');
+
+    $display_2->expects($this->once())
+      ->method('alterRoutes')
+      ->will($this->returnValue(array()));
+    $display_2->expects($this->once())
+      ->method('collectRoutes')
+      ->will($this->returnValue(array('test_id.page_2' => 'views.test_id.page_2')));
+
+    $this->assertNull($this->routeSubscriber->alterRoutes($route_event));
+
+    // Ensure that after the alterRoutes the collectRoutes method is just called
+    // once (not for page_1 anymore).
+
+    $this->assertNull($this->routeSubscriber->dynamicRoutes($route_event));
+
+    $this->state->expects($this->once())
+      ->method('set')
+      ->with('views.view_route_names', array('test_id.page_1' => 'test_route', 'test_id.page_2' => 'views.test_id.page_2'));
+    $this->routeSubscriber->destruct();
+  }
+
+  /**
+   * Sets up mocks of Views objects needed for testing.
+   *
+   * @return array
+   *   An array of Views mocks, including the executable, the view entity, and
+   *   two display plugins.
+   */
+  protected function setupMocks() {
+    $executable = $this->getMockBuilder('Drupal\views\ViewExecutable')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $view = $this->getMockBuilder('Drupal\views\Entity\View')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $this->viewStorageController->expects($this->any())
+      ->method('load')
+      ->will($this->returnValue($view));
+
+    $view->expects($this->any())
+      ->method('getExecutable')
+      ->will($this->returnValue($executable));
+    $view->expects($this->any())
+      ->method('id')
+      ->will($this->returnValue('test_id'));
+    $executable->storage = $view;
+
+    $executable->expects($this->any())
+      ->method('setDisplay')
+      ->will($this->returnValueMap(array(
+        array('page_1', TRUE),
+        array('page_2', TRUE),
+        array('page_3', FALSE),
+      )));
+
+    // Ensure that only the first two displays are actually called.
+    $display_1 = $this->getMock('Drupal\views\Plugin\views\display\DisplayRouterInterface');
+    $display_2 = $this->getMock('Drupal\views\Plugin\views\display\DisplayRouterInterface');
+
+    $display_bag = $this->getMockBuilder('Drupal\views\DisplayBag')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $display_bag->expects($this->any())
+      ->method('get')
+      ->will($this->returnValueMap(array(
+        array('page_1', $display_1),
+        array('page_2', $display_2),
+      )));
+    $executable->displayHandlers = $display_bag;
+
+    $this->routeSubscriber->applicableViews = array();
+    $this->routeSubscriber->applicableViews[] = array($executable, 'page_1');
+    $this->routeSubscriber->applicableViews[] = array($executable, 'page_2');
+    $this->routeSubscriber->applicableViews[] = array($executable, 'page_3');
+
+    return array($executable, $view, $display_1, $display_2);
+  }
+
+}
+
+/**
+ * Provides a test route subscriber.
+ */
+class TestRouteSubscriber extends RouteSubscriber {
+
+  /**
+   * The applicable views.
+   *
+   * @var array
+   */
+  public $applicableViews;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getApplicableViews() {
+    return $this->applicableViews;
+  }
+
+}
diff --git a/core/modules/views/tests/Drupal/views/Tests/Plugin/display/PathPluginBaseTest.php b/core/modules/views/tests/Drupal/views/Tests/Plugin/display/PathPluginBaseTest.php
new file mode 100644
index 000000000000..e7f15a8d0978
--- /dev/null
+++ b/core/modules/views/tests/Drupal/views/Tests/Plugin/display/PathPluginBaseTest.php
@@ -0,0 +1,187 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\views\Tests\Plugin\display\PathPluginBaseTest.
+ */
+
+namespace Drupal\views\Tests\Plugin\display {
+
+use Drupal\Core\DependencyInjection\ContainerBuilder;
+use Drupal\Tests\UnitTestCase;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RouteCollection;
+
+/**
+ * Tests the abstract base class for path based display plugins.
+ *
+ * @see \Drupal\views\Plugin\views\display\PathPluginBase
+ */
+class PathPluginBaseTest extends UnitTestCase {
+
+  /**
+   * The route provider that should be used.
+   *
+   * @var \Drupal\Core\Routing\RouteProviderInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $routeProvider;
+
+  /**
+   * The tested path plugin base.
+   *
+   * @var \Drupal\views\Plugin\views\display\PathPluginBase
+   */
+  protected $pathPlugin;
+
+  /**
+   * The mocked views access plugin manager.
+   *
+   * @var \Drupal\views\Plugin\ViewsPluginManager|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $accessPluginManager;
+
+  /**
+   * The mocked key value storage.
+   *
+   * @var \Drupal\Core\KeyValueStore\KeyValueStoreInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $state;
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Display: Path plugin base.',
+      'description' => 'Tests the abstract base class for path based display plugins.',
+      'group' => 'Views Plugins',
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->routeProvider = $this->getMock('Drupal\Core\Routing\RouteProviderInterface');
+    $this->state = $this->getMock('\Drupal\Core\KeyValueStore\KeyValueStoreInterface');
+    $this->pathPlugin = $this->getMockBuilder('Drupal\views\Plugin\views\display\PathPluginBase')
+      ->setConstructorArgs(array(array(), 'path_base', array(), $this->routeProvider, $this->state))
+      ->setMethods(NULL)
+      ->getMock();
+    $this->setupAccessPluginManager();
+  }
+
+  /**
+   * Setup access plugin manager in a Drupal class.
+   */
+  public function setupAccessPluginManager() {
+    $this->accessPluginManager = $this->getMockBuilder('\Drupal\views\Plugin\ViewsPluginManager')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $container = new ContainerBuilder();
+    $container->set('plugin.manager.views.access', $this->accessPluginManager);
+    \Drupal::setContainer($container);
+  }
+
+  /**
+   * Tests the collectRoutes method.
+   *
+   * @see \Drupal\views\Plugin\views\display\PathPluginBase::collectRoutes()
+   */
+  public function testCollectRoutes() {
+    list($view) = $this->setupViewExecutableAccessPlugin();
+
+    $display = array();
+    $display['display_plugin'] = 'page';
+    $display['id'] = 'page_1';
+    $display['display_options'] = array(
+      'path' => 'test_route',
+    );
+    $this->pathPlugin->initDisplay($view, $display);
+
+    $collection = new RouteCollection();
+    $result = $this->pathPlugin->collectRoutes($collection);
+    $this->assertEquals(array('test_id.page_1' => 'view.test_id.page_1'), $result);
+
+    $route = $collection->get('view.test_id.page_1');
+    $this->assertTrue($route instanceof Route);
+    $this->assertEquals('test_id', $route->getDefault('view_id'));
+    $this->assertEquals('page_1', $route->getDefault('display_id'));
+
+  }
+
+  /**
+   * Tests the alter route method.
+   */
+  public function testAlterRoute() {
+    $collection = new RouteCollection();
+    $collection->add('test_route', new Route('test_route', array('_controller' => 'Drupal\Tests\Core\Controller\TestController')));
+    $route_2 = new Route('test_route/example', array('_controller' => 'Drupal\Tests\Core\Controller\TestController'));
+    $collection->add('test_route_2', $route_2);
+
+    list($view) = $this->setupViewExecutableAccessPlugin();
+
+    $display = array();
+    $display['display_plugin'] = 'page';
+    $display['id'] = 'page_1';
+    $display['display_options'] = array(
+      'path' => 'test_route',
+    );
+    $this->pathPlugin->initDisplay($view, $display);
+
+    $view_route_names = $this->pathPlugin->alterRoutes($collection);
+    $this->assertEquals(array('test_id.page_1' => 'test_route'), $view_route_names);
+
+    // Ensure that the test_route is overridden.
+    $route = $collection->get('test_route');
+    $this->assertTrue($route instanceof Route);
+    $this->assertEquals('test_id', $route->getDefault('view_id'));
+    $this->assertEquals('page_1', $route->getDefault('display_id'));
+
+    // Ensure that the test_route_2 is not overridden.
+    $route = $collection->get('test_route_2');
+    $this->assertTrue($route instanceof Route);
+    $this->assertFalse($route->hasDefault('view_id'));
+    $this->assertFalse($route->hasDefault('display_id'));
+    $this->assertSame($collection->get('test_route_2'), $route_2);
+  }
+
+  /**
+   * Returns some mocked view entity, view executable, and access plugin.
+   */
+  protected function setupViewExecutableAccessPlugin() {
+    $view_entity = $this->getMockBuilder('Drupal\views\Entity\View')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $view_entity->expects($this->any())
+      ->method('id')
+      ->will($this->returnValue('test_id'));
+
+    $view = $this->getMockBuilder('Drupal\views\ViewExecutable')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $view->storage = $view_entity;
+
+    // Skip views options caching.
+    $view->editing = TRUE;
+
+    $access_plugin = $this->getMockBuilder('Drupal\views\Plugin\views\access\AccessPluginBase')
+      ->disableOriginalConstructor()
+      ->getMockForAbstractClass();
+    $this->accessPluginManager->expects($this->any())
+      ->method('createInstance')
+      ->will($this->returnValue($access_plugin));
+
+    return array($view, $view_entity, $access_plugin);
+  }
+
+}
+
+}
+
+namespace {
+  if (!function_exists('views_get_enabled_display_extenders')) {
+    function views_get_enabled_display_extenders() {
+      return array();
+    }
+  }
+}
diff --git a/core/modules/views/views.module b/core/modules/views/views.module
index 11af31632aa6..f8f9ff5e870a 100644
--- a/core/modules/views/views.module
+++ b/core/modules/views/views.module
@@ -706,6 +706,9 @@ function views_invalidate_cache() {
 
   $module_handler = \Drupal::moduleHandler();
 
+  // Reset the RouteSubscriber from views.
+  \Drupal::getContainer()->get('views.route_subscriber')->reset();
+
   // Set the router to be rebuild.
   // @todo Figure out why the cache rebuild is trigged but the route table
   //   does not exist yet.
diff --git a/core/modules/views/views.services.yml b/core/modules/views/views.services.yml
index c77a27ddb211..201780bf76c6 100644
--- a/core/modules/views/views.services.yml
+++ b/core/modules/views/views.services.yml
@@ -83,9 +83,10 @@ services:
     arguments: [views_results]
   views.route_subscriber:
     class: Drupal\views\EventSubscriber\RouteSubscriber
-    arguments: ['@config.factory']
+    arguments: ['@entity.manager', '@state']
     tags:
       - { name: 'event_subscriber' }
+      - { name: 'needs_destruction' }
   views.route_access_check:
     class: Drupal\views\ViewsAccessCheck
     tags:
diff --git a/core/modules/views_ui/tests/Drupal/views_ui/Tests/ViewListControllerTest.php b/core/modules/views_ui/tests/Drupal/views_ui/Tests/ViewListControllerTest.php
index ba82c0d19048..470568b0a373 100644
--- a/core/modules/views_ui/tests/Drupal/views_ui/Tests/ViewListControllerTest.php
+++ b/core/modules/views_ui/tests/Drupal/views_ui/Tests/ViewListControllerTest.php
@@ -77,9 +77,11 @@ public function testBuildRowEntityList() {
       array('initDisplay'),
       array(array(), 'default', $display_manager->getDefinition('default'))
     );
+    $route_provider = $this->getMock('Drupal\Core\Routing\RouteProviderInterface');
+    $state = $this->getMock('\Drupal\Core\KeyValueStore\KeyValueStoreInterface');
     $page_display = $this->getMock('Drupal\views\Plugin\views\display\Page',
       array('initDisplay', 'getPath'),
-      array(array(), 'default', $display_manager->getDefinition('page'))
+      array(array(), 'default', $display_manager->getDefinition('page'), $route_provider, $state)
     );
     $page_display->expects($this->any())
       ->method('getPath')
-- 
GitLab