diff --git a/core/modules/comment/comment.services.yml b/core/modules/comment/comment.services.yml index 3595854ceb..25fc9084af 100644 --- a/core/modules/comment/comment.services.yml +++ b/core/modules/comment/comment.services.yml @@ -7,7 +7,7 @@ services: comment.manager: class: Drupal\comment\CommentManager - arguments: ['@entity_type.manager', '@config.factory', '@string_translation', '@module_handler', '@current_user', '@entity_field.manager', '@entity_display.repository'] + arguments: ['@entity_type.manager', '@config.factory', '@string_translation', '@module_handler', '@current_user', '@entity_field.manager', '@entity_display.repository', '@datetime.time'] comment.statistics: class: Drupal\comment\CommentStatistics diff --git a/core/modules/comment/src/CommentManager.php b/core/modules/comment/src/CommentManager.php index f7458f4d57..e16f328a91 100644 --- a/core/modules/comment/src/CommentManager.php +++ b/core/modules/comment/src/CommentManager.php @@ -3,6 +3,7 @@ namespace Drupal\comment; use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface; +use Drupal\Component\Datetime\TimeInterface; use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\DependencyInjection\DeprecatedServicePropertyTrait; use Drupal\Core\Entity\EntityDisplayRepositoryInterface; @@ -81,6 +82,20 @@ class CommentManager implements CommentManagerInterface { */ protected $currentUser; + /** + * The configuration factory. + * + * @var \Drupal\Core\Config\ConfigFactoryInterface + */ + protected $configFactory; + + /** + * The time service. + * + * @var \Drupal\Component\Datetime\TimeInterface + */ + protected $time; + /** * Construct the CommentManager object. * @@ -98,8 +113,10 @@ class CommentManager implements CommentManagerInterface { * The entity field manager service. * @param \Drupal\Core\Entity\EntityDisplayRepositoryInterface $entity_display_repository * The entity display repository service. + * @param \Drupal\Component\Datetime\TimeInterface $time + * The time service. */ - public function __construct(EntityTypeManagerInterface $entity_type_manager, ConfigFactoryInterface $config_factory, TranslationInterface $string_translation, ModuleHandlerInterface $module_handler, AccountInterface $current_user, EntityFieldManagerInterface $entity_field_manager = NULL, EntityDisplayRepositoryInterface $entity_display_repository = NULL) { + public function __construct(EntityTypeManagerInterface $entity_type_manager, ConfigFactoryInterface $config_factory, TranslationInterface $string_translation, ModuleHandlerInterface $module_handler, AccountInterface $current_user, EntityFieldManagerInterface $entity_field_manager = NULL, EntityDisplayRepositoryInterface $entity_display_repository = NULL, TimeInterface $time = NULL) { $this->entityTypeManager = $entity_type_manager; $this->userConfig = $config_factory->get('user.settings'); $this->stringTranslation = $string_translation; @@ -116,6 +133,13 @@ public function __construct(EntityTypeManagerInterface $entity_type_manager, Con $entity_display_repository = \Drupal::service('entity_display.repository'); } $this->entityDisplayRepository = $entity_display_repository; + + $this->configFactory = $config_factory; + if (!$time) { + @trigger_error('Invoking the CommentManager constructor without the time service parameter is deprecated in drupal:8.8.0 and will no longer be supported in drupal:9.0.0. The time service parameter is now required in the CommentManager constructor. See https://www.drupal.org/node/2785211', E_USER_DEPRECATED); + $time = \Drupal::service('datetime.time'); + } + $this->time = $time; } /** @@ -232,7 +256,10 @@ public function getCountNewComments(EntityInterface $entity, $field_name = NULL, } } } - $timestamp = ($timestamp > HISTORY_READ_LIMIT ? $timestamp : HISTORY_READ_LIMIT); + + $historyConfig = $this->configFactory->get('history.settings'); + $history_read_limit = $this->time->getRequestTime() - $historyConfig->get('history_mark_read'); + $timestamp = ($timestamp > $history_read_limit ? $timestamp : $history_read_limit); // Use the timestamp to retrieve the number of new comments. $query = $this->entityTypeManager->getStorage('comment')->getQuery() diff --git a/core/modules/comment/src/Plugin/views/field/NodeNewComments.php b/core/modules/comment/src/Plugin/views/field/NodeNewComments.php index b1a3de16f4..8f1814b8b1 100644 --- a/core/modules/comment/src/Plugin/views/field/NodeNewComments.php +++ b/core/modules/comment/src/Plugin/views/field/NodeNewComments.php @@ -2,6 +2,8 @@ namespace Drupal\comment\Plugin\views\field; +use Drupal\Component\Datetime\TimeInterface; +use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Database\Connection; use Drupal\comment\CommentInterface; use Drupal\Core\Entity\EntityFieldManagerInterface; @@ -37,6 +39,20 @@ public function usesGroupBy() { */ protected $database; + /** + * The configuration factory. + * + * @var \Drupal\Core\Config\ConfigFactoryInterface + */ + protected $configFactory; + + /** + * The time service. + * + * @var \Drupal\Component\Datetime\TimeInterface + */ + protected $time; + /** * The entity type manager. * @@ -66,8 +82,12 @@ public function usesGroupBy() { * The entity type manager service. * @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager * The entity field manager service. + * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory + * The config factory. + * @param \Drupal\Component\Datetime\TimeInterface $time + * The time service. */ - public function __construct(array $configuration, $plugin_id, $plugin_definition, Connection $database, EntityTypeManagerInterface $entity_type_manager = NULL, EntityFieldManagerInterface $entity_field_manager = NULL) { + public function __construct(array $configuration, $plugin_id, $plugin_definition, Connection $database, EntityTypeManagerInterface $entity_type_manager = NULL, EntityFieldManagerInterface $entity_field_manager = NULL, ConfigFactoryInterface $config_factory = NULL, TimeInterface $time = NULL) { parent::__construct($configuration, $plugin_id, $plugin_definition); $this->database = $database; if (!$entity_type_manager) { @@ -78,8 +98,18 @@ public function __construct(array $configuration, $plugin_id, $plugin_definition @trigger_error("Not passing the entity type manager to the NodeNewComments constructor is deprecated in drupal:8.8.0 and will be required in drupal 9.0.0. @see https://www.drupal.org/node/3047897"); $entity_field_manager = \Drupal::service('entity_field.manager'); } + if (!$config_factory) { + @trigger_error('Invoking the NodeNewComments constructor without the config factory parameter is deprecated in drupal:8.8.0 and will no longer be supported in drupal:9.0.0. The config factory parameter is now required in the NodeNewComments constructor. See https://www.drupal.org/project/drupal/issues/2006632', E_USER_DEPRECATED); + $config_factory = \Drupal::service('config.factory'); + } + if (!$time) { + @trigger_error('Invoking the NodeNewComments constructor without the time service parameter is deprecated in drupal:8.8.0 and will no longer be supported in drupal:9.0.0. The time service parameter is now required in the NodeNewComments constructor. See https://www.drupal.org/node/2785211', E_USER_DEPRECATED); + $time = \Drupal::service('datetime.time'); + } $this->entityTypeManager = $entity_type_manager; $this->entityFieldManager = $entity_field_manager; + $this->configFactory = $config_factory; + $this->time = $time; } /** @@ -92,7 +122,9 @@ public static function create(ContainerInterface $container, array $configuratio $plugin_definition, $container->get('database'), $container->get('entity_type.manager'), - $container->get('entity_field.manager') + $container->get('entity_field.manager'), + $container->get('config.factory'), + $container->get('datetime.time') ); } @@ -163,14 +195,15 @@ public function preRender(&$values) { } if ($nids) { + $history_read_limit = $this->time->getRequestTime() - $this->configFactory->get('history.settings')->get('history_mark_read'); $result = $this->database->query("SELECT n.nid, COUNT(c.cid) as num_comments FROM {node} n INNER JOIN {comment_field_data} c ON n.nid = c.entity_id AND c.entity_type = 'node' AND c.default_langcode = 1 LEFT JOIN {history} h ON h.nid = n.nid AND h.uid = :h_uid WHERE n.nid IN ( :nids[] ) AND c.changed > GREATEST(COALESCE(h.timestamp, :timestamp1), :timestamp2) AND c.status = :status GROUP BY n.nid", [ ':status' => CommentInterface::PUBLISHED, ':h_uid' => $user->id(), ':nids[]' => $nids, - ':timestamp1' => HISTORY_READ_LIMIT, - ':timestamp2' => HISTORY_READ_LIMIT, + ':timestamp1' => $history_read_limit, + ':timestamp2' => $history_read_limit, ]); foreach ($result as $node) { foreach ($ids[$node->nid] as $id) { diff --git a/core/modules/comment/tests/src/Unit/CommentManagerTest.php b/core/modules/comment/tests/src/Unit/CommentManagerTest.php index cfc65d3af4..067deb057d 100644 --- a/core/modules/comment/tests/src/Unit/CommentManagerTest.php +++ b/core/modules/comment/tests/src/Unit/CommentManagerTest.php @@ -56,6 +56,7 @@ public function testGetFields() { $this->createMock('Drupal\Core\Extension\ModuleHandlerInterface'), $this->createMock(AccountInterface::class), $entity_field_manager, + $this->createMock('Drupal\Component\Datetime\TimeInterface'), $this->prophesize(EntityDisplayRepositoryInterface::class)->reveal() ); $comment_fields = $comment_manager->getFields('node'); diff --git a/core/modules/forum/src/ForumManager.php b/core/modules/forum/src/ForumManager.php index 29e03e1cb7..86796c1186 100644 --- a/core/modules/forum/src/ForumManager.php +++ b/core/modules/forum/src/ForumManager.php @@ -326,19 +326,22 @@ protected function getTopicOrder($sortby) { * * @return int * The timestamp when the user last viewed this node, if the user has - * previously viewed the node; otherwise HISTORY_READ_LIMIT. + * previously viewed the node; otherwise - history read limit. */ protected function lastVisit($nid, AccountInterface $account) { + $history_read_limit = $this->configFactory + ->get('history.settings') + ->get('history_mark_read'); if (empty($this->history[$nid])) { $result = $this->connection->select('history', 'h') ->fields('h', ['nid', 'timestamp']) ->condition('uid', $account->id()) ->execute(); foreach ($result as $t) { - $this->history[$t->nid] = $t->timestamp > HISTORY_READ_LIMIT ? $t->timestamp : HISTORY_READ_LIMIT; + $this->history[$t->nid] = $t->timestamp > $history_read_limit ? $t->timestamp : $history_read_limit; } } - return isset($this->history[$nid]) ? $this->history[$nid] : HISTORY_READ_LIMIT; + return isset($this->history[$nid]) ? $this->history[$nid] : $history_read_limit; } /** @@ -496,6 +499,9 @@ public function checkNodeType(NodeInterface $node) { * {@inheritdoc} */ public function unreadTopics($term, $uid) { + $history_read_limit = $this->configFactory + ->get('history.settings') + ->get('history_mark_read'); $query = $this->connection->select('node_field_data', 'n'); $query->join('forum', 'f', 'n.vid = f.vid AND f.tid = :tid', [':tid' => $term]); $query->leftJoin('history', 'h', 'n.nid = h.nid AND h.uid = :uid', [':uid' => $uid]); @@ -505,7 +511,7 @@ public function unreadTopics($term, $uid) { // @todo This should be actually filtering on the desired node status // field language and just fall back to the default language. ->condition('n.default_langcode', 1) - ->condition('n.created', HISTORY_READ_LIMIT, '>') + ->condition('n.created', $history_read_limit, '>') ->isNull('h.nid') ->addTag('node_access') ->execute() diff --git a/core/modules/forum/src/ForumManagerInterface.php b/core/modules/forum/src/ForumManagerInterface.php index 9e8833ebcf..4f81ae0997 100644 --- a/core/modules/forum/src/ForumManagerInterface.php +++ b/core/modules/forum/src/ForumManagerInterface.php @@ -79,7 +79,7 @@ public function checkNodeType(NodeInterface $node); /** * Calculates the number of new posts in a forum that the user has not yet read. * - * Nodes are new if they are newer than HISTORY_READ_LIMIT. + * Nodes are new if they are newer than history read limit. * * @param int $term * The term ID of the forum. diff --git a/core/modules/history/history.module b/core/modules/history/history.module index fc64961f49..a2355c6561 100644 --- a/core/modules/history/history.module +++ b/core/modules/history/history.module @@ -15,14 +15,6 @@ use Drupal\Core\Routing\RouteMatchInterface; use Drupal\user\UserInterface; -/** - * Entities changed before this time are always shown as read. - * - * Entities changed within this time may be marked as new, updated, or read, - * depending on their state for the current user. Defaults to 30 days ago. - */ -define('HISTORY_READ_LIMIT', REQUEST_TIME - 30 * 24 * 60 * 60); - /** * Implements hook_help(). */ @@ -126,8 +118,9 @@ function history_write($nid, $account = NULL) { * Implements hook_cron(). */ function history_cron() { + $history_read_limit = \Drupal::time()->getRequestTime() - \Drupal::config('history.settings')->get('history_mark_read'); \Drupal::database()->delete('history') - ->condition('timestamp', HISTORY_READ_LIMIT, '<') + ->condition('timestamp', $history_read_limit, '<') ->execute(); } diff --git a/core/modules/history/src/Plugin/views/field/HistoryUserTimestamp.php b/core/modules/history/src/Plugin/views/field/HistoryUserTimestamp.php index 617cd23e12..c64add373b 100644 --- a/core/modules/history/src/Plugin/views/field/HistoryUserTimestamp.php +++ b/core/modules/history/src/Plugin/views/field/HistoryUserTimestamp.php @@ -2,11 +2,15 @@ namespace Drupal\history\Plugin\views\field; +use Drupal\Component\Datetime\TimeInterface; +use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\views\ResultRow; use Drupal\views\ViewExecutable; use Drupal\views\Plugin\views\display\DisplayPluginBase; use Drupal\node\Plugin\views\field\Node; +use Symfony\Component\DependencyInjection\ContainerInterface; /** * Field handler to display the marker for new content. @@ -18,7 +22,54 @@ * * @ViewsField("history_user_timestamp") */ -class HistoryUserTimestamp extends Node { +class HistoryUserTimestamp extends Node implements ContainerFactoryPluginInterface { + + /** + * Config factory. + * + * @var \Drupal\Core\Config\ConfigFactoryInterface + */ + protected $configFactory; + + /** + * Time service. + * + * @var \Drupal\Component\Datetime\TimeInterface + */ + protected $time; + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('config.factory'), + $container->get('datetime.time') + ); + } + + /** + * HistoryUserTimestamp constructor. + * + * @param array $configuration + * A configuration array containing information about the plugin instance. + * @param string $plugin_id + * The plugin ID for the plugin instance. + * @param mixed $plugin_definition + * The plugin implementation definition. + * @param \Drupal\Core\Config\ConfigFactoryInterface $configFactory + * Config factory. + * @param \Drupal\Component\Datetime\TimeInterface $time + * Time service. + */ + public function __construct(array $configuration, $plugin_id, $plugin_definition, ConfigFactoryInterface $configFactory, TimeInterface $time) { + parent::__construct($configuration, $plugin_id, $plugin_definition); + $this->configFactory = $configFactory; + $this->time = $time; + } /** * {@inheritdoc} @@ -92,13 +143,14 @@ public function render(ResultRow $values) { $last_comment = \Drupal::moduleHandler()->moduleExists('comment') && !empty($this->options['comments']) ? $this->getValue($values, 'last_comment') : 0; - if (!$last_read && $changed > HISTORY_READ_LIMIT) { + $history_read_limit = $this->time->getRequestTime() - $this->configFactory->get('history.settings')->get('history_mark_read'); + if (!$last_read && $changed > $history_read_limit) { $mark = MARK_NEW; } - elseif ($changed > $last_read && $changed > HISTORY_READ_LIMIT) { + elseif ($changed > $last_read && $changed > $history_read_limit) { $mark = MARK_UPDATED; } - elseif ($last_comment > $last_read && $last_comment > HISTORY_READ_LIMIT) { + elseif ($last_comment > $last_read && $last_comment > $history_read_limit) { $mark = MARK_UPDATED; } $build = [ diff --git a/core/modules/history/src/Plugin/views/filter/HistoryUserTimestamp.php b/core/modules/history/src/Plugin/views/filter/HistoryUserTimestamp.php index 02288da1c4..488f7f5856 100644 --- a/core/modules/history/src/Plugin/views/filter/HistoryUserTimestamp.php +++ b/core/modules/history/src/Plugin/views/filter/HistoryUserTimestamp.php @@ -2,9 +2,13 @@ namespace Drupal\history\Plugin\views\filter; +use Drupal\Component\Datetime\TimeInterface; use Drupal\Core\Cache\UncacheableDependencyTrait; +use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\views\Plugin\views\filter\FilterPluginBase; +use Symfony\Component\DependencyInjection\ContainerInterface; /** * Filter for new content. @@ -16,10 +20,58 @@ * * @ViewsFilter("history_user_timestamp") */ -class HistoryUserTimestamp extends FilterPluginBase { +class HistoryUserTimestamp extends FilterPluginBase implements ContainerFactoryPluginInterface { use UncacheableDependencyTrait; + /** + * Config factory. + * + * @var \Drupal\Core\Config\ConfigFactoryInterface + */ + protected $configFactory; + + /** + * Time service. + * + * @var \Drupal\Component\Datetime\TimeInterface + */ + protected $time; + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('config.factory'), + $container->get('datetime.time') + + ); + } + + /** + * HistoryUserTimestamp constructor. + * + * @param array $configuration + * A configuration array containing information about the plugin instance. + * @param string $plugin_id + * The plugin ID for the plugin instance. + * @param mixed $plugin_definition + * The plugin implementation definition. + * @param \Drupal\Core\Config\ConfigFactoryInterface $configFactory + * Config factory. + * @param \Drupal\Component\Datetime\TimeInterface $time + * Time service. + */ + public function __construct(array $configuration, $plugin_id, $plugin_definition, ConfigFactoryInterface $configFactory, TimeInterface $time) { + parent::__construct($configuration, $plugin_id, $plugin_definition); + $this->configFactory = $configFactory; + $this->time = $time; + } + /** * {@inheritdoc} */ @@ -80,8 +132,8 @@ public function query() { } // Hey, Drupal kills old history, so nodes that haven't been updated - // since HISTORY_READ_LIMIT are bzzzzzzzt outta here! - $limit = REQUEST_TIME - HISTORY_READ_LIMIT; + // since history_cron() are bzzzzzzzt outta here! + $limit = $this->time->getRequestTime() - $this->configFactory->get('history.settings')->get('history_mark_read'); $this->ensureMyTable(); $field = "$this->tableAlias.$this->realField"; diff --git a/core/modules/history/tests/src/Kernel/Views/HistoryTimestampTest.php b/core/modules/history/tests/src/Kernel/Views/HistoryTimestampTest.php index f66ce52476..5622eeda8f 100644 --- a/core/modules/history/tests/src/Kernel/Views/HistoryTimestampTest.php +++ b/core/modules/history/tests/src/Kernel/Views/HistoryTimestampTest.php @@ -40,6 +40,7 @@ protected function setUp($import_test_views = TRUE) { $this->installEntitySchema('node'); $this->installEntitySchema('user'); $this->installSchema('history', ['history']); + $this->installConfig('history'); // Use classy theme because its marker is wrapped in a span so it can be // easily targeted with xpath. \Drupal::service('theme_handler')->install(['classy']); diff --git a/core/modules/node/node.module b/core/modules/node/node.module index 4560a7c66e..a6b6b50ef7 100644 --- a/core/modules/node/node.module +++ b/core/modules/node/node.module @@ -247,6 +247,7 @@ function node_title_list(StatementInterface $result, $title = NULL) { function node_mark($nid, $timestamp) { $cache = &drupal_static(__FUNCTION__, []); + $history_read_limit = REQUEST_TIME - \Drupal::config('history.settings')->get('history_mark_read'); if (\Drupal::currentUser()->isAnonymous() || !\Drupal::moduleHandler()->moduleExists('history')) { return MARK_READ; @@ -254,10 +255,10 @@ function node_mark($nid, $timestamp) { if (!isset($cache[$nid])) { $cache[$nid] = history_read($nid); } - if ($cache[$nid] == 0 && $timestamp > HISTORY_READ_LIMIT) { + if ($cache[$nid] == 0 && $timestamp > $history_read_limit) { return MARK_NEW; } - elseif ($timestamp > $cache[$nid] && $timestamp > HISTORY_READ_LIMIT) { + elseif ($timestamp > $cache[$nid] && $timestamp > $history_read_limit) { return MARK_UPDATED; } return MARK_READ;