diff --git a/core/modules/tracker/tests/src/Functional/TrackerNodeAccessTest.php b/core/modules/tracker/tests/src/Functional/TrackerNodeAccessTest.php index e06fd4cd14..a4865fc918 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; @@ -33,6 +34,9 @@ class TrackerNodeAccessTest extends BrowserTestBase { */ protected $defaultTheme = 'stark'; + /** + * {@inheritdoc} + */ protected function setUp(): void { parent::setUp(); node_access_rebuild(); @@ -101,6 +105,9 @@ public function testTrackerNodeAccess() { $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); $this->drupalGet('activity'); @@ -109,6 +116,21 @@ 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 c8d9784cb2..443a8c2525 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; @@ -20,7 +16,6 @@ */ class TrackerTest extends BrowserTestBase { - use CommentTestTrait; use AssertPageCacheContextsAndTagsTrait; /** @@ -36,6 +31,13 @@ class TrackerTest extends BrowserTestBase { 'node_test', ]; + /** + * Permissions to give to test users. + * + * @var array + */ + protected $permissions = ['create page content']; + /** * {@inheritdoc} */ @@ -55,15 +57,16 @@ class TrackerTest extends BrowserTestBase { */ protected $otherUser; + /** + * {@inheritdoc} + */ protected function setUp(): void { parent::setUp(); $this->drupalCreateContentType(['type' => 'page', 'name' => 'Basic page']); - $permissions = ['access comments', 'create page content', 'post comments', 'skip comment approval']; - $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', @@ -93,7 +96,15 @@ public function testTrackerAll() { $this->assertSession()->linkExists('My recent content', 0, 'User tab shows up on the global tracker page.'); // 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()); @@ -123,20 +134,6 @@ public function testTrackerAll() { $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())); } /** @@ -145,46 +142,35 @@ public function testTrackerAll() { public function testTrackerUser() { $this->drupalLogin($this->user); - $unpublished = $this->drupalCreateNode([ + $this->nodes = []; + $this->nodes['unpublished'] = $this->drupalCreateNode([ 'title' => $this->randomMachineName(8), 'uid' => $this->user->id(), 'status' => 0, ]); - $my_published = $this->drupalCreateNode([ + $this->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()); + $this->assertSession()->pageTextNotContains($this->nodes['unpublished']->label(), "Unpublished nodes do not show up in the user's tracker listing."); + $this->assertSession()->pageTextContains($this->nodes['my_published']->label(), "Published nodes show up in the user's tracker listing."); // 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()); + $expected_tags = Cache::mergeTags($this->nodes['my_published']->getCacheTags(), $this->nodes['my_published']->getOwner()->getCacheTags()); // Because the 'user.permissions' cache context is being optimized away. $role_tags = []; foreach ($this->user->getRoles() as $rid) { @@ -206,26 +192,23 @@ 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()); + $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($this->nodes['my_published']->label()); + $this->assertSession()->linkNotExists($this->nodes['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()); - // Test escaping of title on user's tracker tab. \Drupal::service('module_installer')->install(['user_hooks_test']); Cache::invalidateTags(['rendered']); @@ -250,116 +233,16 @@ public function testTrackerHistoryMetadata() { $edit = [ 'title' => $this->randomMachineName(8), ]; - $node = $this->drupalCreateNode($edit); + $this->node = $this->drupalCreateNode($edit); // Verify that the history metadata is present. $this->drupalGet('activity'); - $this->assertHistoryMetadata($node->id(), $node->getChangedTime(), $node->getChangedTime()); + $this->assertHistoryMetadata($this->node->id(), $this->node->getChangedTime()); $this->drupalGet('activity/' . $this->user->id()); - $this->assertHistoryMetadata($node->id(), $node->getChangedTime(), $node->getChangedTime()); + $this->assertHistoryMetadata($this->node->id(), $this->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->drupalGet('activity/' . $this->user->id()); - $this->assertHistoryMetadata($node->id(), $node->getChangedTime(), $node->get('comment')->last_comment_timestamp); - $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); + $this->assertHistoryMetadata($this->node->id(), $this->node->getChangedTime()); - // 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); } /** @@ -378,15 +261,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), @@ -395,7 +269,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); 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 --- 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 @@ */ class MigrateTrackerSettingsTest extends MigrateDrupalTestBase { + /** + * Modules to enable. + * + * @var array + */ protected static $modules = ['tracker']; /** diff --git a/core/modules/tracker/tracker.info.yml b/core/modules/tracker/tracker.info.yml index 4f1718f09f..d4abe184a2 100644 --- a/core/modules/tracker/tracker.info.yml +++ b/core/modules/tracker/tracker.info.yml @@ -3,6 +3,5 @@ type: module description: 'Enables tracking of recent content for users.' 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..bce155090d 100644 --- a/core/modules/tracker/tracker.module +++ b/core/modules/tracker/tracker.module @@ -91,29 +91,30 @@ function tracker_cron() { // Insert the user-level data for the commenters (except if a commenter // is the node's author). - // Get unique user IDs via entityQueryAggregate because it's the easiest // database agnostic way. We don't actually care about the comments here // so don't add an aggregate field. - $result = \Drupal::entityQueryAggregate('comment') - ->accessCheck(FALSE) - ->condition('entity_type', 'node') - ->condition('entity_id', $node->id()) - ->condition('uid', $node->getOwnerId(), '<>') - ->condition('status', CommentInterface::PUBLISHED) - ->groupBy('uid') - ->execute(); - if ($result) { - $query = $connection->insert('tracker_user'); - foreach ($result as $row) { - $query->fields([ - 'uid' => $row['uid'], - 'nid' => $nid, - 'published' => CommentInterface::PUBLISHED, - 'changed' => $changed, - ]); + if (\Drupal::moduleHandler()->moduleExists('comment')) { + $result = \Drupal::entityQueryAggregate('comment') + ->accessCheck(FALSE) + ->condition('entity_type', 'node') + ->condition('entity_id', $node->id()) + ->condition('uid', $node->getOwnerId(), '<>') + ->condition('status', CommentInterface::PUBLISHED) + ->groupBy('uid') + ->execute(); + if ($result) { + $query = db_insert('tracker_user'); + foreach ($result as $row) { + $query->fields([ + 'uid' => $row['uid'], + 'nid' => $nid, + 'published' => CommentInterface::PUBLISHED, + 'changed' => $changed, + ]); + } + $query->execute(); } - $query->execute(); } // Note that we have indexed at least one node. @@ -263,11 +264,13 @@ function _tracker_add($nid, $uid, $changed) { * @todo Check if we should introduce 'language context' here, because the * callers may need different timestamps depending on the users' language? */ -function _tracker_calculate_changed($node) { +function _tracker_calculate_changed(NodeInterface $node) { $changed = $node->getChangedTime(); - $latest_comment = \Drupal::service('comment.statistics')->read([$node], 'node', FALSE); - if ($latest_comment && $latest_comment->last_comment_timestamp > $changed) { - $changed = $latest_comment->last_comment_timestamp; + if (\Drupal::hasService('comment.statistics')) { + $latest_comment = \Drupal::service('comment.statistics')->read([$node], 'node', FALSE); + if ($latest_comment && $latest_comment->last_comment_timestamp > $changed) { + $changed = $latest_comment->last_comment_timestamp; + } } return $changed; } @@ -321,7 +324,6 @@ function _tracker_remove($nid, $uid = NULL, $changed = NULL) { if ($tracker_node && $changed >= $tracker_node->changed) { // If we're here, the item being removed is *possibly* the item that // established the node's changed timestamp. - // We just have to recalculate things from scratch. $changed = _tracker_calculate_changed($node); diff --git a/core/modules/tracker/tracker.views.inc b/core/modules/tracker/tracker.views.inc index be15d57478..4422f4605f 100644 --- a/core/modules/tracker/tracker.views.inc +++ b/core/modules/tracker/tracker.views.inc @@ -157,7 +157,7 @@ function tracker_views_data() { * Implements hook_views_data_alter(). */ function tracker_views_data_alter(&$data) { - // Provide additional uid_touch handlers which are handled by tracker + // Provide additional uid_touch handlers which are handled by tracker. $data['node_field_data']['uid_touch_tracker'] = [ 'group' => t('Tracker - User'), 'title' => t('User posted or commented'),