From cd635205008dae172e8a79bbd48bc103684c1abe Mon Sep 17 00:00:00 2001
From: Alex Pott <alex.a.pott@googlemail.com>
Date: Sun, 8 Jun 2014 17:49:56 -0500
Subject: [PATCH] Issue #2085571 by Xano, dawehner: Fixed admin/content should
 not depend on node.module.

---
 core/modules/comment/comment.local_tasks.yml  |  2 +-
 core/modules/comment/comment.menu_links.yml   |  2 +-
 core/modules/comment/comment.module           | 13 ---
 core/modules/node/node.local_actions.yml      |  2 +-
 core/modules/node/node.local_tasks.yml        |  4 -
 core/modules/node/node.menu_links.yml         |  6 --
 core/modules/node/node.module                 |  2 +-
 core/modules/node/node.routing.yml            |  7 --
 core/modules/node/node.services.yml           |  4 +
 core/modules/node/src/Form/DeleteMultiple.php |  2 +-
 .../node/src/Routing/RouteSubscriber.php      | 37 ++++++++
 .../src/Controller/SystemController.php       |  9 +-
 .../src/Plugin/Derivative/ThemeLocalTask.php  | 33 +++++++-
 core/modules/system/system.local_tasks.yml    |  5 ++
 core/modules/system/system.menu_links.yml     |  6 ++
 core/modules/system/system.routing.yml        | 10 +++
 .../Tests/Menu/SystemLocalTasksTest.php       | 84 +++++++++++++++++++
 .../Core/Menu/LocalTaskIntegrationTest.php    | 11 +++
 18 files changed, 199 insertions(+), 40 deletions(-)
 create mode 100644 core/modules/node/src/Routing/RouteSubscriber.php
 create mode 100644 core/modules/system/tests/Drupal/system/Tests/Menu/SystemLocalTasksTest.php

diff --git a/core/modules/comment/comment.local_tasks.yml b/core/modules/comment/comment.local_tasks.yml
index 091321bf9455..8826d1d70040 100644
--- a/core/modules/comment/comment.local_tasks.yml
+++ b/core/modules/comment/comment.local_tasks.yml
@@ -16,7 +16,7 @@ comment.confirm_delete_tab:
 comment.admin:
   title: Comments
   route_name: comment.admin
-  base_route: node.content_overview
+  base_route: system.admin_content
 
 comment.admin_new:
   title: 'Published comments'
diff --git a/core/modules/comment/comment.menu_links.yml b/core/modules/comment/comment.menu_links.yml
index 7d715fe18561..7f0af7805aa9 100644
--- a/core/modules/comment/comment.menu_links.yml
+++ b/core/modules/comment/comment.menu_links.yml
@@ -1,7 +1,7 @@
 comment.admin:
   title: Comments
   route_name: comment.admin
-  parent: system.admin
+  parent: system.admin_content
   description: 'List and edit site comments and the comment approval queue.'
 comment.bundle_list:
   title: 'Comment forms'
diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module
index fce19d937417..8fc55372d585 100644
--- a/core/modules/comment/comment.module
+++ b/core/modules/comment/comment.module
@@ -183,19 +183,6 @@ function comment_theme() {
   );
 }
 
-/**
- * Implements hook_menu_link_defaults_alter()
- */
-function comment_menu_link_defaults_alter(&$links) {
-  if (isset($links['node.content_overview'])) {
-    // Add comments to the description for admin/content if any.
-    $links['node.content_overview']['description'] = 'Administer content and comments.';
-  }
-  if (\Drupal::moduleHandler()->moduleExists('node')) {
-    $links['comment.admin']['parent'] = 'node.content_overview';
-  }
-}
-
 /**
  * Returns a menu title which includes the number of unapproved comments.
  *
diff --git a/core/modules/node/node.local_actions.yml b/core/modules/node/node.local_actions.yml
index af8a56d6c9d8..b7b1577512cf 100644
--- a/core/modules/node/node.local_actions.yml
+++ b/core/modules/node/node.local_actions.yml
@@ -7,4 +7,4 @@ node.add_page:
   route_name: node.add_page
   title: 'Add content'
   appears_on:
-    - node.content_overview
+    - system.admin_content
diff --git a/core/modules/node/node.local_tasks.yml b/core/modules/node/node.local_tasks.yml
index d3026e117c66..c61ad9064eac 100644
--- a/core/modules/node/node.local_tasks.yml
+++ b/core/modules/node/node.local_tasks.yml
@@ -11,10 +11,6 @@ node.delete_confirm:
   base_route: node.view
   title: Delete
   weight: 10
-node.content_overview:
-  title: Content
-  route_name: node.content_overview
-  base_route: node.content_overview
 node.revision_overview:
   route_name: node.revision_overview
   base_route: node.view
diff --git a/core/modules/node/node.menu_links.yml b/core/modules/node/node.menu_links.yml
index 50fd002e399a..a19749466297 100644
--- a/core/modules/node/node.menu_links.yml
+++ b/core/modules/node/node.menu_links.yml
@@ -1,9 +1,3 @@
-node.content_overview:
-  title: Content
-  route_name: node.content_overview
-  parent: system.admin
-  description: 'Find and manage content.'
-  weight: -10
 node.overview_types:
   title: 'Content types'
   parent: system.admin_structure
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index 32d32951c26d..475e797c7d1a 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -117,7 +117,7 @@ function node_help($route_name, Request $request) {
       $output .= '<dt>' . t('Creating custom content types') . '</dt>';
       $output .= '<dd>' . t('The Node module gives users with the <em>Administer content types</em> permission the ability to <a href="!content-new">create new content types</a> in addition to the default ones already configured. Creating custom content types allows you the flexibility to add <a href="!field">fields</a> and configure default settings that suit the differing needs of various site content.', array('!content-new' => \Drupal::url('node.type_add'), '!field' => \Drupal::url('help.page', array('name' => 'field')))) . '</dd>';
       $output .= '<dt>' . t('Administering content') . '</dt>';
-      $output .= '<dd>' . t('The <a href="!content">Content administration page</a> allows you to review and bulk manage your site content.', array('!content' => \Drupal::url('node.content_overview'))) . '</dd>';
+      $output .= '<dd>' . t('The <a href="!content">Content administration page</a> allows you to review and bulk manage your site content.', array('!content' => \Drupal::url('system.admin_content'))) . '</dd>';
       $output .= '<dt>' . t('Creating revisions') . '</dt>';
       $output .= '<dd>' . t('The Node module also enables you to create multiple versions of any content, and revert to older versions using the <em>Revision information</em> settings.') . '</dd>';
       $output .= '<dt>' . t('User permissions') . '</dt>';
diff --git a/core/modules/node/node.routing.yml b/core/modules/node/node.routing.yml
index 03ce3473bc2b..86278f2afff6 100644
--- a/core/modules/node/node.routing.yml
+++ b/core/modules/node/node.routing.yml
@@ -1,10 +1,3 @@
-node.content_overview:
-  path: '/admin/content'
-  defaults:
-    _title: 'Content'
-    _entity_list: 'node'
-  requirements:
-    _permission: 'access content overview'
 
 node.multiple_delete_confirm:
   path: '/admin/content/node/delete'
diff --git a/core/modules/node/node.services.yml b/core/modules/node/node.services.yml
index e211f7ba11bd..69cadb21f760 100644
--- a/core/modules/node/node.services.yml
+++ b/core/modules/node/node.services.yml
@@ -1,4 +1,8 @@
 services:
+  node.route_subscriber:
+    class: Drupal\node\Routing\RouteSubscriber
+    tags:
+      - { name: event_subscriber }
   node.grant_storage:
     class: Drupal\node\NodeGrantDatabaseStorage
     arguments: ['@database', '@module_handler']
diff --git a/core/modules/node/src/Form/DeleteMultiple.php b/core/modules/node/src/Form/DeleteMultiple.php
index fada9bbe559c..1e94e43c9bf4 100644
--- a/core/modules/node/src/Form/DeleteMultiple.php
+++ b/core/modules/node/src/Form/DeleteMultiple.php
@@ -125,7 +125,7 @@ public function submitForm(array &$form, array &$form_state) {
       drupal_set_message(format_plural($count, 'Deleted 1 post.', 'Deleted @count posts.'));
       Cache::invalidateTags(array('content' => TRUE));
     }
-    $form_state['redirect_route']['route_name'] = 'node.content_overview';
+    $form_state['redirect_route']['route_name'] = 'system.admin_content';
   }
 
 }
diff --git a/core/modules/node/src/Routing/RouteSubscriber.php b/core/modules/node/src/Routing/RouteSubscriber.php
new file mode 100644
index 000000000000..5c9dde629724
--- /dev/null
+++ b/core/modules/node/src/Routing/RouteSubscriber.php
@@ -0,0 +1,37 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\node\Routing\RouteSubscriber.
+ */
+
+namespace Drupal\node\Routing;
+
+use Drupal\Core\Routing\RouteSubscriberBase;
+use Symfony\Component\Routing\RouteCollection;
+
+/**
+ * Listens to the dynamic route events.
+ */
+class RouteSubscriber extends RouteSubscriberBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function alterRoutes(RouteCollection $collection) {
+    // As nodes are the primary type of content, the node listing should be
+    // easily available. In order to do that, override admin/content to show
+    // a node listing instead of the path's child links.
+    $route = $collection->get('system.admin_content');
+    if ($route) {
+      $route->setDefaults(array(
+        '_title' => 'Content',
+        '_entity_list' => 'node',
+      ));
+      $route->setRequirements(array(
+        '_permission' => 'access content overview',
+      ));
+    }
+  }
+
+}
diff --git a/core/modules/system/src/Controller/SystemController.php b/core/modules/system/src/Controller/SystemController.php
index 62272963d684..b9609687ca4a 100644
--- a/core/modules/system/src/Controller/SystemController.php
+++ b/core/modules/system/src/Controller/SystemController.php
@@ -94,18 +94,21 @@ public static function create(ContainerInterface $container) {
   /**
    * Provide the administration overview page.
    *
+   * @param string $path
+   *   The administrative path for which to display child links.
+   *
    * @return array
    *   A renderable array of the administration overview page.
    */
-  public function overview() {
+  public function overview($path) {
     // Check for status report errors.
     if ($this->systemManager->checkRequirements() && $this->currentUser()->hasPermission('administer site configuration')) {
       drupal_set_message($this->t('One or more problems were detected with your Drupal installation. Check the <a href="@status">status report</a> for more information.', array('@status' => url('admin/reports/status'))), 'error');
     }
     $blocks = array();
-    // Load all links on admin/config and menu links below it.
+    // Load all links on $path and menu links below it.
     $query = $this->queryFactory->get('menu_link')
-      ->condition('link_path', 'admin/config')
+      ->condition('link_path', $path)
       ->condition('module', 'system');
     $result = $query->execute();
     $menu_link_storage = $this->entityManager()->getStorage('menu_link');
diff --git a/core/modules/system/src/Plugin/Derivative/ThemeLocalTask.php b/core/modules/system/src/Plugin/Derivative/ThemeLocalTask.php
index da6910d1ef82..0fa1ebc7e6dc 100644
--- a/core/modules/system/src/Plugin/Derivative/ThemeLocalTask.php
+++ b/core/modules/system/src/Plugin/Derivative/ThemeLocalTask.php
@@ -8,17 +8,46 @@
 namespace Drupal\system\Plugin\Derivative;
 
 use Drupal\Component\Plugin\Derivative\DerivativeBase;
+use Drupal\Core\Extension\ThemeHandlerInterface;
+use Drupal\Core\Plugin\Discovery\ContainerDerivativeInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
  * Provides dynamic tabs based on active themes.
  */
-class ThemeLocalTask extends DerivativeBase {
+class ThemeLocalTask extends DerivativeBase implements ContainerDerivativeInterface {
+
+  /**
+   * The theme handler.
+   *
+   * @var \Drupal\Core\Extension\ThemeHandlerInterface
+   */
+  protected $themeHandler;
+
+  /**
+   * Constructs a new ThemeLocalTask instance.
+   *
+   * @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler
+   *   The theme handler.
+   */
+  public function __construct(ThemeHandlerInterface $theme_handler) {
+    $this->themeHandler = $theme_handler;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, $base_plugin_id) {
+    return new static(
+      $container->get('theme_handler')
+    );
+  }
 
   /**
    * {@inheritdoc}
    */
   public function getDerivativeDefinitions($base_plugin_definition) {
-    foreach (list_themes() as $theme_name => $theme) {
+    foreach ($this->themeHandler->listInfo() as $theme_name => $theme) {
       if ($theme->status) {
         $this->derivatives[$theme_name] = $base_plugin_definition;
         $this->derivatives[$theme_name]['title'] = $theme->info['name'];
diff --git a/core/modules/system/system.local_tasks.yml b/core/modules/system/system.local_tasks.yml
index 4d597ee708a9..ebc1d0fe1a4f 100644
--- a/core/modules/system/system.local_tasks.yml
+++ b/core/modules/system/system.local_tasks.yml
@@ -63,3 +63,8 @@ system.date_format_edit:
   title: 'Edit'
   route_name: system.date_format_edit
   base_route: system.date_format_edit
+
+system.admin_content:
+  title: Content
+  route_name: system.admin_content
+  base_route: system.admin_content
diff --git a/core/modules/system/system.menu_links.yml b/core/modules/system/system.menu_links.yml
index 4d883a7421e3..9b1f5e314406 100644
--- a/core/modules/system/system.menu_links.yml
+++ b/core/modules/system/system.menu_links.yml
@@ -3,6 +3,12 @@ system.admin:
   route_name: system.admin
   weight: 9
   menu_name: admin
+system.admin_content:
+  title: Content
+  description: 'Find and manage content.'
+  route_name: system.admin_content
+  parent: system.admin
+  weight: -10
 system.admin_structure:
   route_name: system.admin_structure
   parent: system.admin
diff --git a/core/modules/system/system.routing.yml b/core/modules/system/system.routing.yml
index 13019c2e0156..35e17f7bd2a4 100644
--- a/core/modules/system/system.routing.yml
+++ b/core/modules/system/system.routing.yml
@@ -382,6 +382,7 @@ system.admin_config:
   path: '/admin/config'
   defaults:
     _content: '\Drupal\system\Controller\SystemController::overview'
+    path: 'admin/config'
     _title: 'Configuration'
   requirements:
     _permission: 'access administration pages'
@@ -408,3 +409,12 @@ system.batch_page.json:
 
 system.update:
   path: '/core/update.php'
+
+system.admin_content:
+  path: '/admin/content'
+  defaults:
+    _content: '\Drupal\system\Controller\SystemController::overview'
+    path: 'admin/content'
+    _title: 'Content'
+  requirements:
+    _permission: 'access administration pages'
diff --git a/core/modules/system/tests/Drupal/system/Tests/Menu/SystemLocalTasksTest.php b/core/modules/system/tests/Drupal/system/Tests/Menu/SystemLocalTasksTest.php
new file mode 100644
index 000000000000..9cd7ebc4fc7b
--- /dev/null
+++ b/core/modules/system/tests/Drupal/system/Tests/Menu/SystemLocalTasksTest.php
@@ -0,0 +1,84 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\system\Tests\Menu\SystemLocalTasksTest.
+ */
+
+namespace Drupal\system\Tests\Menu;
+
+use Drupal\Core\Extension\Extension;
+use Drupal\Tests\Core\Menu\LocalTaskIntegrationTest;
+
+/**
+ * Tests existence of system local tasks.
+ *
+ * @group Drupal
+ * @group System
+ */
+class SystemLocalTasksTest extends LocalTaskIntegrationTest {
+
+  /**
+   * The mocked theme handler.
+   *
+   * @var \Drupal\Core\Extension\ThemeHandlerInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $themeHandler;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'System local tasks',
+      'description' => '',
+      'group' => 'System',
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setUp() {
+    parent::setUp();
+
+    $this->directoryList = array(
+      'system' => 'core/modules/system',
+    );
+
+    $this->themeHandler = $this->getMock('Drupal\Core\Extension\ThemeHandlerInterface');
+
+    $theme = new Extension('theme', DRUPAL_ROOT . '/core/themes/bartik', 'bartik.info.yml');
+    $theme->status = 1;
+    $theme->info = array('name' => 'bartik');
+    $this->themeHandler->expects($this->any())
+      ->method('listInfo')
+      ->will($this->returnValue(array(
+        'bartik' => $theme,
+      )));
+    $this->container->set('theme_handler', $this->themeHandler);
+  }
+
+  /**
+   * Tests local task existence.
+   *
+   * @dataProvider getSystemAdminRoutes
+   */
+  public function testSystemAdminLocalTasks($route, $expected) {
+    $this->assertLocalTasks($route, $expected);
+  }
+
+  /**
+   * Provides a list of routes to test.
+   */
+  public function getSystemAdminRoutes() {
+    return array(
+      array('system.admin_content', array(array('system.admin_content'))),
+      array('system.theme_settings_theme', array(
+        array('system.themes_page', 'system.theme_settings'),
+        array('system.theme_settings_global', 'system.theme_settings_theme:bartik'),
+      )),
+    );
+  }
+
+}
diff --git a/core/tests/Drupal/Tests/Core/Menu/LocalTaskIntegrationTest.php b/core/tests/Drupal/Tests/Core/Menu/LocalTaskIntegrationTest.php
index 0069d1504c19..234c1f3aad51 100644
--- a/core/tests/Drupal/Tests/Core/Menu/LocalTaskIntegrationTest.php
+++ b/core/tests/Drupal/Tests/Core/Menu/LocalTaskIntegrationTest.php
@@ -40,11 +40,22 @@ abstract class LocalTaskIntegrationTest extends UnitTestCase {
    */
   protected $moduleHandler;
 
+  /**
+   * The container.
+   *
+   * @var \Symfony\Component\DependencyInjection\ContainerBuilder
+   */
+  protected $container;
+
+  /**
+   * {@inheritdoc}
+   */
   protected function setUp() {
     $container = new ContainerBuilder();
     $config_factory = $this->getConfigFactoryStub(array());
     $container->set('config.factory', $config_factory);
     \Drupal::setContainer($container);
+    $this->container = $container;
   }
 
   /**
-- 
GitLab