From 3ed75506489dc2cd8bfd62d6a2ce01ff9ce81cf3 Mon Sep 17 00:00:00 2001
From: Alex Pott <alex.a.pott@googlemail.com>
Date: Thu, 3 Oct 2019 09:15:53 +0100
Subject: [PATCH] Issue #3015650 by kim.pepper, andypost, Berdir, vacho,
 jibran, mikelutz, alexpott, larowlan, dpi: Remove tracker.pages.inc

---
 .../src/Controller/TrackerController.php      | 264 ++++++++++++++++++
 .../tracker/src/Controller/TrackerPage.php    |   7 +-
 .../src/Controller/TrackerUserRecent.php      |  26 +-
 .../tracker/src/Controller/TrackerUserTab.php |  15 +-
 .../tests/src/Kernel/TrackerLegacyTest.php    |  44 +++
 core/modules/tracker/tracker.pages.inc        | 139 +--------
 core/modules/tracker/tracker.routing.yml      |  10 +-
 7 files changed, 332 insertions(+), 173 deletions(-)
 create mode 100644 core/modules/tracker/src/Controller/TrackerController.php
 create mode 100644 core/modules/tracker/tests/src/Kernel/TrackerLegacyTest.php

diff --git a/core/modules/tracker/src/Controller/TrackerController.php b/core/modules/tracker/src/Controller/TrackerController.php
new file mode 100644
index 000000000000..aeb65755ee4a
--- /dev/null
+++ b/core/modules/tracker/src/Controller/TrackerController.php
@@ -0,0 +1,264 @@
+<?php
+
+namespace Drupal\tracker\Controller;
+
+use Drupal\comment\CommentStatisticsInterface;
+use Drupal\Core\Access\AccessResult;
+use Drupal\Core\Cache\CacheableMetadata;
+use Drupal\Core\Controller\ControllerBase;
+use Drupal\Core\Database\Connection;
+use Drupal\Core\Database\Query\PagerSelectExtender;
+use Drupal\Core\Datetime\DateFormatterInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\user\UserInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Controller for tracker pages.
+ */
+class TrackerController extends ControllerBase {
+
+  /**
+   * The database connection.
+   *
+   * @var \Drupal\Core\Database\Connection
+   */
+  protected $database;
+
+  /**
+   * The database replica connection.
+   *
+   * @var \Drupal\Core\Database\Connection
+   */
+  protected $databaseReplica;
+
+  /**
+   * The comment statistics.
+   *
+   * @var \Drupal\comment\CommentStatisticsInterface
+   */
+  protected $commentStatistics;
+
+  /**
+   * The date formatter.
+   *
+   * @var \Drupal\Core\Datetime\DateFormatterInterface
+   */
+  protected $dateFormatter;
+
+  /**
+   * The node storage.
+   *
+   * @var \Drupal\Core\Entity\EntityStorageInterface
+   */
+  protected $nodeStorage;
+
+  /**
+   * Constructs a TrackerController object.
+   *
+   * @param \Drupal\Core\Database\Connection $database
+   *   The database connection.
+   * @param \Drupal\Core\Database\Connection $databaseReplica
+   *   The database replica connection.
+   * @param \Drupal\comment\CommentStatisticsInterface $commentStatistics
+   *   The comment statistics.
+   * @param \Drupal\Core\Datetime\DateFormatterInterface $dateFormatter
+   *   The date formatter.
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
+   *   The entity type manager.
+   */
+  public function __construct(Connection $database, Connection $databaseReplica, CommentStatisticsInterface $commentStatistics, DateFormatterInterface $dateFormatter, EntityTypeManagerInterface $entityTypeManager) {
+    $this->database = $database;
+    $this->databaseReplica = $databaseReplica;
+    $this->commentStatistics = $commentStatistics;
+    $this->dateFormatter = $dateFormatter;
+    $this->entityTypeManager = $entityTypeManager;
+    $this->nodeStorage = $entityTypeManager->getStorage('node');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('database'),
+      $container->get('database.replica'),
+      $container->get('comment.statistics'),
+      $container->get('date.formatter'),
+      $container->get('entity_type.manager')
+    );
+  }
+
+  /**
+   * Title callback for the tracker.user_tab route.
+   *
+   * @param \Drupal\user\UserInterface $user
+   *   The user.
+   *
+   * @return string
+   *   The title.
+   */
+  public function getTitle(UserInterface $user) {
+    return $user->getDisplayName();
+  }
+
+  /**
+   * Checks access for the users recent content tracker page.
+   *
+   * @param \Drupal\user\UserInterface $user
+   *   The user being viewed.
+   * @param \Drupal\Core\Session\AccountInterface $account
+   *   The account viewing the page.
+   *
+   * @return \Drupal\Core\Access\AccessResult
+   *   The access result.
+   */
+  public function checkAccess(UserInterface $user, AccountInterface $account) {
+    return AccessResult::allowedIf($account->isAuthenticated() && $user->id() == $account->id())
+      ->cachePerUser();
+  }
+
+  /**
+   * Builds content for the tracker controllers.
+   *
+   * @param \Drupal\user\UserInterface|null $user
+   *   (optional) The user account.
+   *
+   * @return array
+   *   The render array.
+   */
+  public function buildContent(UserInterface $user = NULL) {
+    if ($user) {
+      $query = $this->database->select('tracker_user', 't')
+        ->extend(PagerSelectExtender::class)
+        ->addMetaData('base_table', 'tracker_user')
+        ->condition('t.uid', $user->id());
+    }
+    else {
+      $query = $this->databaseReplica->select('tracker_node', 't')
+        ->extend(PagerSelectExtender::class)
+        ->addMetaData('base_table', 'tracker_node');
+    }
+
+    // This array acts as a placeholder for the data selected later
+    // while keeping the correct order.
+    $tracker_data = $query
+      ->addTag('node_access')
+      ->fields('t', ['nid', 'changed'])
+      ->condition('t.published', 1)
+      ->orderBy('t.changed', 'DESC')
+      ->limit(25)
+      ->execute()
+      ->fetchAllAssoc('nid');
+
+    $cacheable_metadata = new CacheableMetadata();
+    $rows = [];
+    if (!empty($tracker_data)) {
+      // Load nodes into an array with the same order as $tracker_data.
+      /** @var \Drupal\node\NodeInterface[] $nodes */
+      $nodes = $this->nodeStorage->loadMultiple(array_keys($tracker_data));
+
+      // Enrich the node data.
+      $result = $this->commentStatistics->read($nodes, 'node', FALSE);
+      foreach ($result as $statistics) {
+        // The node ID may not be unique; there can be multiple comment fields.
+        // Make comment_count the total of all comments.
+        $nid = $statistics->entity_id;
+        if (empty($nodes[$nid]->comment_count)
+          || !is_numeric($tracker_data[$nid]->comment_count)) {
+          $tracker_data[$nid]->comment_count = $statistics->comment_count;
+        }
+        else {
+          $tracker_data[$nid]->comment_count += $statistics->comment_count;
+        }
+        // Make the last comment timestamp reflect the latest comment.
+        if (!isset($tracker_data[$nid]->last_comment_timestamp)) {
+          $tracker_data[$nid]->last_comment_timestamp = $statistics->last_comment_timestamp;
+        }
+        else {
+          $tracker_data[$nid]->last_comment_timestamp = max($tracker_data[$nid]->last_comment_timestamp, $statistics->last_comment_timestamp);
+        }
+      }
+
+      // Display the data.
+      foreach ($nodes as $node) {
+        // Set the last activity time from tracker data. This also takes into
+        // account comment activity, so getChangedTime() is not used.
+        $last_activity = $tracker_data[$node->id()]->changed;
+
+        $owner = $node->getOwner();
+        $row = [
+          'type' => node_get_type_label($node),
+          'title' => [
+            'data' => [
+              '#type' => 'link',
+              '#url' => $node->toUrl(),
+              '#title' => $node->getTitle(),
+            ],
+            'data-history-node-id' => $node->id(),
+            'data-history-node-timestamp' => $node->getChangedTime(),
+          ],
+          'author' => [
+            'data' => [
+              '#theme' => 'username',
+              '#account' => $owner,
+            ],
+          ],
+          'comments' => [
+            'class' => ['comments'],
+            'data' => $tracker_data[$node->id()]->comment_count ?? 0,
+            'data-history-node-last-comment-timestamp' => $tracker_data[$node->id()]->last_comment_timestamp ?? 0,
+          ],
+          'last updated' => [
+            'data' => t('@time ago', [
+              '@time' => $this->dateFormatter->formatTimeDiffSince($last_activity),
+            ]),
+          ],
+        ];
+
+        $rows[] = $row;
+
+        // Add node and node owner to cache tags.
+        $cacheable_metadata->addCacheTags($node->getCacheTags());
+        if ($owner) {
+          $cacheable_metadata->addCacheTags($owner->getCacheTags());
+        }
+      }
+    }
+
+    // Add the list cache tag for nodes.
+    $cacheable_metadata->addCacheTags($this->nodeStorage->getEntityType()->getListCacheTags());
+
+    $page['tracker'] = [
+      '#rows' => $rows,
+      '#header' => [
+        $this->t('Type'),
+        $this->t('Title'),
+        $this->t('Author'),
+        $this->t('Comments'),
+        $this->t('Last updated'),
+      ],
+      '#type' => 'table',
+      '#empty' => $this->t('No content available.'),
+    ];
+    $page['pager'] = [
+      '#type' => 'pager',
+      '#weight' => 10,
+    ];
+    $page['#sorted'] = TRUE;
+    $cacheable_metadata->addCacheContexts(['user.node_grants:view']);
+
+    // Display the reading history if that module is enabled.
+    if ($this->moduleHandler()->moduleExists('history')) {
+      // Reading history is tracked for authenticated users only.
+      if ($this->currentUser()->isAuthenticated()) {
+        $page['#attached']['library'][] = 'tracker/history';
+      }
+      $cacheable_metadata->addCacheContexts(['user.roles:authenticated']);
+    }
+    $cacheable_metadata->applyTo($page);
+    return $page;
+  }
+
+}
diff --git a/core/modules/tracker/src/Controller/TrackerPage.php b/core/modules/tracker/src/Controller/TrackerPage.php
index 03b077dbddec..6618e72cfb7d 100644
--- a/core/modules/tracker/src/Controller/TrackerPage.php
+++ b/core/modules/tracker/src/Controller/TrackerPage.php
@@ -2,19 +2,18 @@
 
 namespace Drupal\tracker\Controller;
 
-use Drupal\Core\Controller\ControllerBase;
+@trigger_error(__NAMESPACE__ . '\TrackerPage is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use \Drupal\tracker\Controller\TrackerController instead. See https://www.drupal.org/node/3030645', E_USER_DEPRECATED);
 
 /**
  * Controller for tracker.page route.
  */
-class TrackerPage extends ControllerBase {
+class TrackerPage extends TrackerController {
 
   /**
    * Content callback for the tracker.page route.
    */
   public function getContent() {
-    module_load_include('inc', 'tracker', 'tracker.pages');
-    return tracker_page();
+    return $this->buildContent();
   }
 
 }
diff --git a/core/modules/tracker/src/Controller/TrackerUserRecent.php b/core/modules/tracker/src/Controller/TrackerUserRecent.php
index ff3d4b02f0ac..256f48ab4acc 100644
--- a/core/modules/tracker/src/Controller/TrackerUserRecent.php
+++ b/core/modules/tracker/src/Controller/TrackerUserRecent.php
@@ -2,38 +2,20 @@
 
 namespace Drupal\tracker\Controller;
 
-use Drupal\Core\Access\AccessResult;
-use Drupal\Core\Controller\ControllerBase;
-use Drupal\Core\Session\AccountInterface;
+@trigger_error(__NAMESPACE__ . '\TrackerUserRecent is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use \Drupal\tracker\Controller\TrackerController instead. See https://www.drupal.org/node/3030645', E_USER_DEPRECATED);
+
 use Drupal\user\UserInterface;
 
 /**
  * Controller for tracker.users_recent_content route.
  */
-class TrackerUserRecent extends ControllerBase {
+class TrackerUserRecent extends TrackerController {
 
   /**
    * Content callback for the tracker.users_recent_content route.
    */
   public function getContent(UserInterface $user) {
-    module_load_include('inc', 'tracker', 'tracker.pages');
-    return tracker_page($user);
-  }
-
-  /**
-   * Checks access for the users recent content tracker page.
-   *
-   * @param \Drupal\user\UserInterface $user
-   *   The user being viewed.
-   * @param \Drupal\Core\Session\AccountInterface $account
-   *   The account viewing the page.
-   *
-   * @return \Drupal\Core\Access\AccessResult
-   *   The access result.
-   */
-  public function checkAccess(UserInterface $user, AccountInterface $account) {
-    return AccessResult::allowedIf($account->isAuthenticated() && $user->id() == $account->id())
-      ->cachePerUser();
+    return $this->buildContent($user);
   }
 
 }
diff --git a/core/modules/tracker/src/Controller/TrackerUserTab.php b/core/modules/tracker/src/Controller/TrackerUserTab.php
index c57aff0655b4..7ccfa5bce6df 100644
--- a/core/modules/tracker/src/Controller/TrackerUserTab.php
+++ b/core/modules/tracker/src/Controller/TrackerUserTab.php
@@ -2,27 +2,20 @@
 
 namespace Drupal\tracker\Controller;
 
-use Drupal\Core\Controller\ControllerBase;
+@trigger_error(__NAMESPACE__ . '\TrackerUserTab is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use \Drupal\tracker\Controller\TrackerController instead. See https://www.drupal.org/node/3030645', E_USER_DEPRECATED);
+
 use Drupal\user\UserInterface;
 
 /**
  * Controller for tracker.user_tab route.
  */
-class TrackerUserTab extends ControllerBase {
+class TrackerUserTab extends TrackerController {
 
   /**
    * Content callback for the tracker.user_tab route.
    */
   public function getContent(UserInterface $user) {
-    module_load_include('inc', 'tracker', 'tracker.pages');
-    return tracker_page($user);
-  }
-
-  /**
-   * Title callback for the tracker.user_tab route.
-   */
-  public function getTitle(UserInterface $user) {
-    return $user->getAccountName();
+    return $this->buildContent($user);
   }
 
 }
diff --git a/core/modules/tracker/tests/src/Kernel/TrackerLegacyTest.php b/core/modules/tracker/tests/src/Kernel/TrackerLegacyTest.php
new file mode 100644
index 000000000000..238b380e7c73
--- /dev/null
+++ b/core/modules/tracker/tests/src/Kernel/TrackerLegacyTest.php
@@ -0,0 +1,44 @@
+<?php
+
+namespace Drupal\Tests\tracker\Kernel;
+
+use Drupal\KernelTests\KernelTestBase;
+
+/**
+ * @group tracker
+ * @group legacy
+ */
+class TrackerLegacyTest extends KernelTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = [
+    'comment',
+    'tracker',
+    'history',
+    'node',
+    'node_test',
+    'user',
+  ];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+    $this->installEntitySchema('node');
+    $this->installSchema('node', 'node_access');
+    $this->installSchema('tracker', 'tracker_node');
+    $this->installSchema('tracker', 'tracker_user');
+  }
+
+  /**
+   * @expectedDeprecation tracker_page is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use \Drupal\tracker\Controller\TrackerController::buildContent() instead. See https://www.drupal.org/node/3030645
+   */
+  public function testDeprecatedTrackerPage() {
+    module_load_include('inc', 'tracker', 'tracker.pages');
+    $this->assertNotEmpty(tracker_page());
+  }
+
+}
diff --git a/core/modules/tracker/tracker.pages.inc b/core/modules/tracker/tracker.pages.inc
index 575c31bae069..61a97a49ebf3 100644
--- a/core/modules/tracker/tracker.pages.inc
+++ b/core/modules/tracker/tracker.pages.inc
@@ -5,8 +5,7 @@
  * User page callbacks for tracker.module.
  */
 
-use Drupal\Core\Cache\Cache;
-use Drupal\node\Entity\Node;
+use Drupal\tracker\Controller\TrackerController;
 
 /**
  * Page callback: Generates a page of tracked nodes for the site.
@@ -19,135 +18,13 @@
  *
  * @return array
  *   A renderable array.
+ *
+ * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use
+ *   \Drupal\tracker\Controller\TrackerController::buildContent() instead.
+ *
+ * @see https://www.drupal.org/node/3030645
  */
 function tracker_page($account = NULL) {
-  if ($account) {
-    $query = \Drupal::database()->select('tracker_user', 't')
-      ->extend('Drupal\Core\Database\Query\PagerSelectExtender')
-      ->addMetaData('base_table', 'tracker_user')
-      ->condition('t.uid', $account->id());
-  }
-  else {
-    $query = \Drupal::service('database.replica')->select('tracker_node', 't')
-      ->extend('Drupal\Core\Database\Query\PagerSelectExtender')
-      ->addMetaData('base_table', 'tracker_node');
-  }
-
-  // This array acts as a placeholder for the data selected later
-  // while keeping the correct order.
-  $tracker_data = $query
-    ->addTag('node_access')
-    ->fields('t', ['nid', 'changed'])
-    ->condition('t.published', 1)
-    ->orderBy('t.changed', 'DESC')
-    ->limit(25)
-    ->execute()
-    ->fetchAllAssoc('nid');
-
-  $cache_tags = [];
-  $rows = [];
-  if (!empty($tracker_data)) {
-    // Load nodes into an array with the same order as $tracker_data.
-    $nodes = Node::loadMultiple(array_keys($tracker_data));
-
-    // Enrich the node data.
-    $result = \Drupal::service('comment.statistics')->read($nodes, 'node', FALSE);
-    foreach ($result as $statistics) {
-      // The node ID may not be unique; there can be multiple comment fields.
-      // Make comment_count the total of all comments.
-      $nid = $statistics->entity_id;
-      if (empty($nodes[$nid]->comment_count)
-          || !is_numeric($nodes[$nid]->comment_count)) {
-        $nodes[$nid]->comment_count = $statistics->comment_count;
-      }
-      else {
-        $nodes[$nid]->comment_count += $statistics->comment_count;
-      }
-      // Make the last comment timestamp reflect the latest comment.
-      if (!isset($nodes[$nid]->last_comment_timestamp)) {
-        $nodes[$nid]->last_comment_timestamp = $statistics->last_comment_timestamp;
-      }
-      else {
-        $nodes[$nid]->last_comment_timestamp = max($nodes[$nid]->last_comment_timestamp, $statistics->last_comment_timestamp);
-      }
-    }
-
-    // Display the data.
-    foreach ($nodes as $node) {
-      // Set the last activity time from tracker data. This also takes into
-      // account comment activity, so getChangedTime() is not used.
-      $node->last_activity = $tracker_data[$node->id()]->changed;
-
-      // Determine the number of comments.
-      $comments = 0;
-      if ($node->comment_count) {
-        $comments = $node->comment_count;
-      }
-
-      $row = [
-        'type' => node_get_type_label($node),
-        'title' => [
-          'data' => [
-            '#type' => 'link',
-            '#url' => $node->toUrl(),
-            '#title' => $node->getTitle(),
-          ],
-          'data-history-node-id' => $node->id(),
-          'data-history-node-timestamp' => $node->getChangedTime(),
-        ],
-        'author' => [
-          'data' => [
-            '#theme' => 'username',
-            '#account' => $node->getOwner(),
-          ],
-        ],
-        'comments' => [
-          'class' => ['comments'],
-          'data' => $comments,
-          'data-history-node-last-comment-timestamp' => $node->last_comment_timestamp,
-        ],
-        'last updated' => [
-          'data' => t('@time ago', [
-            '@time' => \Drupal::service('date.formatter')->formatTimeDiffSince($node->last_activity),
-          ]),
-        ],
-      ];
-
-      $rows[] = $row;
-
-      // Add node and node owner to cache tags.
-      $cache_tags = Cache::mergeTags($cache_tags, $node->getCacheTags());
-      if ($node->getOwner()) {
-        $cache_tags = Cache::mergeTags($cache_tags, $node->getOwner()->getCacheTags());
-      }
-    }
-  }
-
-  // Add the list cache tag for nodes.
-  $cache_tags = Cache::mergeTags($cache_tags, \Drupal::entityTypeManager()->getDefinition('node')->getListCacheTags());
-
-  $page['tracker'] = [
-    '#rows' => $rows,
-    '#header' => [t('Type'), t('Title'), t('Author'), t('Comments'), t('Last updated')],
-    '#type' => 'table',
-    '#empty' => t('No content available.'),
-  ];
-  $page['pager'] = [
-    '#type' => 'pager',
-    '#weight' => 10,
-  ];
-  $page['#sorted'] = TRUE;
-  $page['#cache']['tags'] = $cache_tags;
-  $page['#cache']['contexts'][] = 'user.node_grants:view';
-
-  // Display the reading history if that module is enabled.
-  if (\Drupal::moduleHandler()->moduleExists('history')) {
-    // Reading history is tracked for authenticated users only.
-    if (\Drupal::currentUser()->isAuthenticated()) {
-      $page['#attached']['library'][] = 'tracker/history';
-    }
-    $page['#cache']['contexts'][] = 'user.roles:authenticated';
-  }
-
-  return $page;
+  @trigger_error(__FUNCTION__ . ' is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use \Drupal\tracker\Controller\TrackerController::buildContent() instead. See https://www.drupal.org/node/3030645', E_USER_DEPRECATED);
+  return \Drupal::classResolver(TrackerController::class)->buildContent($account);
 }
diff --git a/core/modules/tracker/tracker.routing.yml b/core/modules/tracker/tracker.routing.yml
index 7d94f9ba1016..113078ea0dde 100644
--- a/core/modules/tracker/tracker.routing.yml
+++ b/core/modules/tracker/tracker.routing.yml
@@ -1,7 +1,7 @@
 tracker.page:
   path: '/activity'
   defaults:
-    _controller: '\Drupal\tracker\Controller\TrackerPage::getContent'
+    _controller: '\Drupal\tracker\Controller\TrackerController::buildContent'
     _title: 'Recent content'
   requirements:
     _permission: 'access content'
@@ -9,18 +9,18 @@ tracker.page:
 tracker.users_recent_content:
   path: '/activity/{user}'
   defaults:
-    _controller: '\Drupal\tracker\Controller\TrackerUserRecent::getContent'
+    _controller: '\Drupal\tracker\Controller\TrackerController::buildContent'
     _title: 'My recent content'
   requirements:
     _permission: 'access content'
-    _custom_access: '\Drupal\tracker\Controller\TrackerUserRecent::checkAccess'
+    _custom_access: '\Drupal\tracker\Controller\TrackerController::checkAccess'
     user: \d+
 
 tracker.user_tab:
   path: '/user/{user}/activity'
   defaults:
-    _controller: '\Drupal\tracker\Controller\TrackerUserTab::getContent'
-    _title_callback: '\Drupal\tracker\Controller\TrackerUserTab::getTitle'
+    _controller: '\Drupal\tracker\Controller\TrackerController::buildContent'
+    _title_callback: '\Drupal\tracker\Controller\TrackerController::getTitle'
   requirements:
     _permission: 'access content'
     _entity_access: 'user.view'
-- 
GitLab