--- 366511-126.patch 2022-11-18 12:14:11.000000000 +0530 +++ 366511-128.patch 2022-11-18 12:52:26.000000000 +0530 @@ -136,11 +136,424 @@ $page['pager'] = [ '#type' => 'pager', '#weight' => 10, +diff --git a/core/modules/tracker/tests/src/Functional/TrackerNodeAccessTest.php b/core/modules/tracker/tests/src/Functional/TrackerNodeAccessTest.php +index 58dbd26690..ffc4c3ee91 100644 +--- a/core/modules/tracker/tests/src/Functional/TrackerNodeAccessTest.php ++++ b/core/modules/tracker/tests/src/Functional/TrackerNodeAccessTest.php +@@ -4,6 +4,7 @@ + + use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface; + use Drupal\comment\Tests\CommentTestTrait; ++use Drupal\field\Entity\FieldStorageConfig; + use Drupal\node\Entity\NodeType; + use Drupal\Tests\BrowserTestBase; + +@@ -104,6 +105,8 @@ public function testTrackerNodeAccess() { + $this->drupalGet('user/' . $access_user->id() . '/activity'); + $this->assertSession()->pageTextContains($private_node->getTitle()); + $this->assertSession()->pageTextContains($public_node->getTitle()); ++ // Comments column displayed. ++ $this->assertSession()->pageTextContains('Comments'); + + // User without access should not see private node. + $this->drupalLogin($no_access_user); +@@ -113,6 +116,20 @@ public function testTrackerNodeAccess() { + $this->drupalGet('user/' . $access_user->id() . '/activity'); + $this->assertSession()->pageTextNotContains($private_node->getTitle()); + $this->assertSession()->pageTextContains($public_node->getTitle()); ++ // Ensure module pages works when comment module uninstalled. ++ $field = FieldStorageConfig::loadByName('node', 'comment'); ++ $field->delete(); ++ field_purge_batch(10); ++ $this->container->get('module_installer')->uninstall(['comment']); ++ $this->drupalLogin($access_user); ++ $this->drupalGet('activity'); ++ $this->assertSession()->pageTextContains($private_node->getTitle()); ++ // Comments column hidden. ++ $this->assertSession()->pageTextNotContains('Comments'); ++ $this->drupalGet('user/' . $access_user->id() . '/activity'); ++ $this->assertSession()->pageTextContains($private_node->getTitle()); ++ // Comments column hidden. ++ $this->assertSession()->pageTextNotContains('Comments'); + } + + } +diff --git a/core/modules/tracker/tests/src/Functional/TrackerTest.php b/core/modules/tracker/tests/src/Functional/TrackerTest.php +index 1cb6cd8a79..e499ec19ec 100644 +--- a/core/modules/tracker/tests/src/Functional/TrackerTest.php ++++ b/core/modules/tracker/tests/src/Functional/TrackerTest.php +@@ -2,14 +2,10 @@ + + namespace Drupal\Tests\tracker\Functional; + +-use Drupal\comment\CommentInterface; +-use Drupal\comment\Tests\CommentTestTrait; + use Drupal\Core\Cache\Cache; + use Drupal\Core\Database\Database; + use Drupal\Core\EventSubscriber\MainContentViewSubscriber; + use Drupal\Core\Session\AccountInterface; +-use Drupal\field\Entity\FieldStorageConfig; +-use Drupal\node\Entity\Node; + use Drupal\Tests\BrowserTestBase; + use Drupal\Tests\system\Functional\Cache\AssertPageCacheContextsAndTagsTrait; + +@@ -21,7 +17,6 @@ + */ + class TrackerTest extends BrowserTestBase { + +- use CommentTestTrait; + use AssertPageCacheContextsAndTagsTrait; + + /** +@@ -37,6 +32,13 @@ class TrackerTest extends BrowserTestBase { + 'node_test', + ]; + ++ /** ++ * Permissions to give to test users. ++ * ++ * @var array ++ */ ++ protected $permissions = ['create page content']; ++ + /** + * {@inheritdoc} + */ +@@ -68,6 +70,8 @@ protected function setUp(): void { + $this->user = $this->drupalCreateUser($permissions); + $this->otherUser = $this->drupalCreateUser($permissions); + $this->addDefaultCommentField('node', 'page'); ++ $this->user = $this->drupalCreateUser($this->permissions); ++ $this->otherUser = $this->drupalCreateUser($this->permissions); + user_role_grant_permissions(AccountInterface::ANONYMOUS_ROLE, [ + 'access content', + 'access user profiles', +@@ -98,6 +102,15 @@ public function testTrackerAll() { + + // Assert cache contexts, specifically the pager and node access contexts. + $this->assertCacheContexts(['languages:language_interface', 'route', 'theme', 'url.query_args:' . MainContentViewSubscriber::WRAPPER_FORMAT, 'url.query_args.pagers:0', 'user.node_grants:view', 'user']); ++ $this->assertCacheContexts([ ++ 'languages:language_interface', ++ 'route', 'theme', ++ 'url.query_args:' . MainContentViewSubscriber::WRAPPER_FORMAT, ++ 'url.query_args.pagers:0', ++ 'user.node_grants:view', ++ 'user', ++ ] ++ ); + // Assert cache tags for the action/tabs blocks, visible node, and node list + // cache tag. + $expected_tags = Cache::mergeTags($published->getCacheTags(), $published->getOwner()->getCacheTags()); +@@ -126,21 +139,6 @@ public function testTrackerAll() { + $published->delete(); + $this->drupalGet('activity'); + $this->assertSession()->pageTextNotContains($published->label()); +- +- // Test proper display of time on activity page when comments are disabled. +- // Disable comments. +- FieldStorageConfig::loadByName('node', 'comment')->delete(); +- $node = $this->drupalCreateNode([ +- // This title is required to trigger the custom changed time set in the +- // node_test module. This is needed in order to ensure a sufficiently +- // large 'time ago' interval that isn't numbered in seconds. +- 'title' => 'testing_node_presave', +- 'status' => 1, +- ]); +- +- $this->drupalGet('activity'); +- $this->assertSession()->pageTextContains($node->label()); +- $this->assertSession()->pageTextContains(\Drupal::service('date.formatter')->formatTimeDiffSince($node->getChangedTime())); + } + + /** +@@ -148,48 +146,31 @@ public function testTrackerAll() { + */ + public function testTrackerUser() { + $this->drupalLogin($this->user); +- +- $unpublished = $this->drupalCreateNode([ ++ $nodes = []; ++ $nodes['unpublished'] = $this->drupalCreateNode([ + 'title' => $this->randomMachineName(8), + 'uid' => $this->user->id(), + 'status' => 0, + ]); +- $my_published = $this->drupalCreateNode([ ++ $nodes['my_published'] = $this->drupalCreateNode([ + 'title' => $this->randomMachineName(8), + 'uid' => $this->user->id(), + 'status' => 1, +- ]); +- $other_published_no_comment = $this->drupalCreateNode([ +- 'title' => $this->randomMachineName(8), +- 'uid' => $this->otherUser->id(), +- 'status' => 1, +- ]); +- $other_published_my_comment = $this->drupalCreateNode([ +- 'title' => $this->randomMachineName(8), +- 'uid' => $this->otherUser->id(), +- 'status' => 1, +- ]); +- $comment = [ +- 'subject[0][value]' => $this->randomMachineName(), +- 'comment_body[0][value]' => $this->randomMachineName(20), +- ]; +- $this->drupalGet('comment/reply/node/' . $other_published_my_comment->id() . '/comment'); +- $this->submitForm($comment, 'Save'); +- ++ ]); + $this->drupalGet('user/' . $this->user->id() . '/activity'); +- $this->assertSession()->pageTextNotContains($unpublished->label()); +- $this->assertSession()->pageTextContains($my_published->label()); +- $this->assertSession()->pageTextNotContains($other_published_no_comment->label()); +- $this->assertSession()->pageTextContains($other_published_my_comment->label()); +- + // Assert cache contexts. +- $this->assertCacheContexts(['languages:language_interface', 'route', 'theme', 'url.query_args:' . MainContentViewSubscriber::WRAPPER_FORMAT, 'url.query_args.pagers:0', 'user', 'user.node_grants:view']); ++ $this->assertCacheContexts([ ++ 'languages:language_interface', ++ 'route', 'theme', ++ 'url.query_args:' . MainContentViewSubscriber::WRAPPER_FORMAT, ++ 'url.query_args.pagers:0', ++ 'user', ++ 'user.node_grants:view', ++ ] ++ ); + // Assert cache tags for the visible nodes (including owners) and node list + // cache tag. +- $expected_tags = Cache::mergeTags($my_published->getCacheTags(), $my_published->getOwner()->getCacheTags()); +- $expected_tags = Cache::mergeTags($expected_tags, $other_published_my_comment->getCacheTags()); +- $expected_tags = Cache::mergeTags($expected_tags, $other_published_my_comment->getOwner()->getCacheTags()); +- // Because the 'user.permissions' cache context is being optimized away. ++ $expected_tags = Cache::mergeTags($nodes['my_published']->getCacheTags(), $nodes['my_published']->getOwner()->getCacheTags());// Because the 'user.permissions' cache context is being optimized away. + $role_tags = []; + foreach ($this->user->getRoles() as $rid) { + $role_tags[] = "config:user.role.$rid"; +@@ -210,26 +191,20 @@ public function testTrackerUser() { + $expected_tags = Cache::mergeTags($expected_tags, $additional_tags); + + $this->assertCacheTags($expected_tags); +- $this->assertCacheContexts(['languages:language_interface', 'route', 'theme', 'url.query_args:' . MainContentViewSubscriber::WRAPPER_FORMAT, 'url.query_args.pagers:0', 'user', 'user.node_grants:view']); +- +- $this->assertSession()->linkExists($my_published->label()); +- $this->assertSession()->linkNotExists($unpublished->label()); +- // Verify that title and tab title have been set correctly. +- $this->assertSession()->pageTextContains('Activity'); +- $this->assertSession()->titleEquals($this->user->getAccountName() . ' | Drupal'); +- +- // Verify that unpublished comments are removed from the tracker. +- $admin_user = $this->drupalCreateUser([ +- 'post comments', +- 'administer comments', +- 'access user profiles', +- ]); +- $this->drupalLogin($admin_user); +- $this->drupalGet('comment/1/edit'); +- $this->submitForm(['status' => CommentInterface::NOT_PUBLISHED], 'Save'); +- $this->drupalGet('user/' . $this->user->id() . '/activity'); +- $this->assertSession()->pageTextNotContains($other_published_my_comment->label()); +- ++ $this->assertCacheContexts([ ++ 'languages:language_interface', ++ 'route', ++ 'theme', ++ 'url.query_args:' . MainContentViewSubscriber::WRAPPER_FORMAT, ++ 'url.query_args.pagers:0', ++ 'user', ++ 'user.node_grants:view', ++ ] ++ ); ++ ++ $this->assertSession()->linkExists($nodes['my_published']->label()); ++ $this->assertSession()->linkNotExists($nodes['unpublished']->label()); ++ + // Test escaping of title on user's tracker tab. + \Drupal::service('module_installer')->install(['user_hooks_test']); + Cache::invalidateTags(['rendered']); +@@ -258,112 +233,11 @@ public function testTrackerHistoryMetadata() { + + // Verify that the history metadata is present. + $this->drupalGet('activity'); +- $this->assertHistoryMetadata($node->id(), $node->getChangedTime(), $node->getChangedTime()); +- $this->drupalGet('activity/' . $this->user->id()); +- $this->assertHistoryMetadata($node->id(), $node->getChangedTime(), $node->getChangedTime()); +- $this->drupalGet('user/' . $this->user->id() . '/activity'); +- $this->assertHistoryMetadata($node->id(), $node->getChangedTime(), $node->getChangedTime()); +- +- // Add a comment to the page, make sure it is created after the node by +- // sleeping for one second, to ensure the last comment timestamp is +- // different from before. +- $comment = [ +- 'subject[0][value]' => $this->randomMachineName(), +- 'comment_body[0][value]' => $this->randomMachineName(20), +- ]; +- sleep(1); +- $this->drupalGet('comment/reply/node/' . $node->id() . '/comment'); +- $this->submitForm($comment, 'Save'); +- // Reload the node so that comment.module's hook_node_load() +- // implementation can set $node->last_comment_timestamp for the freshly +- // posted comment. +- $node = Node::load($node->id()); +- +- // Verify that the history metadata is updated. +- $this->drupalGet('activity'); +- $this->assertHistoryMetadata($node->id(), $node->getChangedTime(), $node->get('comment')->last_comment_timestamp); ++ $this->assertHistoryMetadata($node->id(), $node->getChangedTime()); + $this->drupalGet('activity/' . $this->user->id()); +- $this->assertHistoryMetadata($node->id(), $node->getChangedTime(), $node->get('comment')->last_comment_timestamp); ++ $this->assertHistoryMetadata($node->id(), $node->getChangedTime()); + $this->drupalGet('user/' . $this->user->id() . '/activity'); +- $this->assertHistoryMetadata($node->id(), $node->getChangedTime(), $node->get('comment')->last_comment_timestamp); +- +- // Log out, now verify that the metadata is still there, but the library is +- // not. +- $this->drupalLogout(); +- $this->drupalGet('activity'); +- $this->assertHistoryMetadata($node->id(), $node->getChangedTime(), $node->get('comment')->last_comment_timestamp, FALSE); +- $this->drupalGet('user/' . $this->user->id() . '/activity'); +- $this->assertHistoryMetadata($node->id(), $node->getChangedTime(), $node->get('comment')->last_comment_timestamp, FALSE); +- } +- +- /** +- * Tests for ordering on a users tracker listing when comments are posted. +- */ +- public function testTrackerOrderingNewComments() { +- $this->drupalLogin($this->user); +- +- $node_one = $this->drupalCreateNode([ +- 'title' => $this->randomMachineName(8), +- ]); +- +- $node_two = $this->drupalCreateNode([ +- 'title' => $this->randomMachineName(8), +- ]); +- +- // Now get otherUser to track these pieces of content. +- $this->drupalLogin($this->otherUser); +- +- // Add a comment to the first page. +- $comment = [ +- 'subject[0][value]' => $this->randomMachineName(), +- 'comment_body[0][value]' => $this->randomMachineName(20), +- ]; +- $this->drupalGet('comment/reply/node/' . $node_one->id() . '/comment'); +- $this->submitForm($comment, 'Save'); +- +- // If the comment is posted in the same second as the last one then Drupal +- // can't tell the difference, so we wait one second here. +- sleep(1); +- +- // Add a comment to the second page. +- $comment = [ +- 'subject[0][value]' => $this->randomMachineName(), +- 'comment_body[0][value]' => $this->randomMachineName(20), +- ]; +- $this->drupalGet('comment/reply/node/' . $node_two->id() . '/comment'); +- $this->submitForm($comment, 'Save'); +- +- // We should at this point have in our tracker for otherUser: +- // 1. node_two +- // 2. node_one +- // Because that's the reverse order of the posted comments. +- +- // Now we're going to post a comment to node_one which should jump it to the +- // top of the list. +- +- $this->drupalLogin($this->user); +- // If the comment is posted in the same second as the last one then Drupal +- // can't tell the difference, so we wait one second here. +- sleep(1); +- +- // Add a comment to the second page. +- $comment = [ +- 'subject[0][value]' => $this->randomMachineName(), +- 'comment_body[0][value]' => $this->randomMachineName(20), +- ]; +- $this->drupalGet('comment/reply/node/' . $node_one->id() . '/comment'); +- $this->submitForm($comment, 'Save'); +- +- // Switch back to the otherUser and assert that the order has swapped. +- $this->drupalLogin($this->otherUser); +- $this->drupalGet('user/' . $this->otherUser->id() . '/activity'); +- // This is a cheeky way of asserting that the nodes are in the right order +- // on the tracker page. +- // It's almost certainly too brittle. +- $pattern = '/' . preg_quote($node_one->getTitle()) . '.+' . preg_quote($node_two->getTitle()) . '/s'; +- // Verify that the most recent comment on node appears at the top of +- // tracker. +- $this->assertSession()->responseMatches($pattern); ++ $this->assertHistoryMetadata($node->id(), $node->getChangedTime()); + } + + /** +@@ -382,15 +256,6 @@ public function testTrackerCronIndexing() { + $nodes[$i] = $this->drupalCreateNode($edits[$i]); + } + +- // Add a comment to the last node as other user. +- $this->drupalLogin($this->otherUser); +- $comment = [ +- 'subject[0][value]' => $this->randomMachineName(), +- 'comment_body[0][value]' => $this->randomMachineName(20), +- ]; +- $this->drupalGet('comment/reply/node/' . $nodes[3]->id() . '/comment'); +- $this->submitForm($comment, 'Save'); +- + // Create an unpublished node. + $unpublished = $this->drupalCreateNode([ + 'title' => $this->randomMachineName(8), +@@ -399,7 +264,21 @@ public function testTrackerCronIndexing() { + + $this->drupalGet('activity'); + $this->assertSession()->responseNotContains($unpublished->label()); ++ // Run the tests. ++ $this->doTestTrackerCronIndexing($nodes); ++ } + ++ /** ++ * Runs assertions for testTrackerCronIndexing(). ++ * ++ * This has been moved into a separate method to ease tests of the sub-method ++ * \Drupal\Tests\tracker\Functional\TrackerCommentTest::testTrackerCronIndexing() ++ * and reduce code duplication. ++ * ++ * @param \Drupal\node\Entity\Node[] $nodes ++ * The created nodes to test. ++ */ ++ protected function doTestTrackerCronIndexing(array $nodes) { + // Start indexing backwards from node 4. + \Drupal::state()->set('tracker.index_nid', 4); + +@@ -427,6 +306,8 @@ public function testTrackerCronIndexing() { + // Assert that all node titles are displayed. + foreach ($nodes as $i => $node) { + $this->assertSession()->pageTextContains($node->label()); ++ // Run the tests. ++ $this->doTestTrackerCronIndexing($nodes); + } + } + +@@ -484,11 +365,13 @@ public function testTrackerAdminUnpublish() { + * + * @internal + */ +- public function assertHistoryMetadata(int $node_id, int $node_timestamp, int $node_last_comment_timestamp, bool $library_is_present = TRUE): void { ++ public function assertHistoryMetadata(int $node_id, int $node_timestamp, int $node_last_comment_timestamp = NULL, bool $library_is_present = TRUE): void { + $settings = $this->getDrupalSettings(); + $this->assertSame($library_is_present, isset($settings['ajaxPageState']) && in_array('tracker/history', explode(',', $settings['ajaxPageState']['libraries'])), 'drupal.tracker-history library is present.'); + $this->assertSession()->elementsCount('xpath', '//table/tbody/tr/td[@data-history-node-id="' . $node_id . '" and @data-history-node-timestamp="' . $node_timestamp . '"]', 1); +- $this->assertSession()->elementsCount('xpath', '//table/tbody/tr/td[@data-history-node-last-comment-timestamp="' . $node_last_comment_timestamp . '"]', 1); ++ if (isset($node_last_comment_timestamp)) { ++ $this->assertSession()->elementsCount('xpath', '//table/tbody/tr/td[@data-history-node-last-comment-timestamp="' . $node_last_comment_timestamp . '"]', 1); ++ } + } + + } diff --git a/core/modules/tracker/tests/src/Kernel/Migrate/d7/MigrateTrackerSettingsTest.php b/core/modules/tracker/tests/src/Kernel/Migrate/d7/MigrateTrackerSettingsTest.php -index c740054447..d48a43b3ad 100644 +index 48ad0da838..52b8ed7e42 100644 --- a/core/modules/tracker/tests/src/Kernel/Migrate/d7/MigrateTrackerSettingsTest.php +++ b/core/modules/tracker/tests/src/Kernel/Migrate/d7/MigrateTrackerSettingsTest.php -@@ -9,6 +9,11 @@ +@@ -10,6 +10,11 @@ */ class MigrateTrackerSettingsTest extends MigrateDrupalTestBase { @@ -152,6 +565,17 @@ protected static $modules = ['tracker']; /** +diff --git a/core/modules/tracker/tracker.info.yml b/core/modules/tracker/tracker.info.yml +index e51613d009..0d1b7fe9cc 100644 +--- a/core/modules/tracker/tracker.info.yml ++++ b/core/modules/tracker/tracker.info.yml +@@ -5,6 +5,5 @@ lifecycle: deprecated + lifecycle_link: 'https://www.drupal.org/about/core/policies/core-change-policies/deprecated-and-obsolete-modules-and-themes#s-activity-tracker' + dependencies: + - drupal:node +- - drupal:comment + package: Core + version: VERSION diff --git a/core/modules/tracker/tracker.module b/core/modules/tracker/tracker.module index 8dbc5ac2f2..8b85bc8d1f 100644 --- a/core/modules/tracker/tracker.module