diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module index 24f48add80941eeaeb8841bfda5d00f99e1435ae..e7007238c7a49652ae4983a5fc8262158b7ca5c0 100644 --- a/core/modules/comment/comment.module +++ b/core/modules/comment/comment.module @@ -1477,12 +1477,12 @@ function comment_load($cid, $reset = FALSE) { function comment_num_new($nid, $timestamp = 0) { global $user; - if ($user->uid) { + if ($user->uid && module_exists('history')) { // Retrieve the timestamp at which the current user last viewed this node. if (!$timestamp) { - $timestamp = node_last_viewed($nid); + $timestamp = history_read($nid); } - $timestamp = ($timestamp > NODE_NEW_LIMIT ? $timestamp : NODE_NEW_LIMIT); + $timestamp = ($timestamp > HISTORY_READ_LIMIT ? $timestamp : HISTORY_READ_LIMIT); // Use the timestamp to retrieve the number of new comments. return db_query('SELECT COUNT(cid) FROM {comment} WHERE nid = :nid AND created > :timestamp AND status = :status', array( diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentLinksTest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentLinksTest.php index e1dcbecf40fb9a84df5e0fd97a8a571814689474..d049aa309b8f5d4188342421be83c32e13e504c6 100644 --- a/core/modules/comment/lib/Drupal/comment/Tests/CommentLinksTest.php +++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentLinksTest.php @@ -141,8 +141,8 @@ function setEnvironment(array $info) { comment_save($comment); $this->comment = $comment; - // comment_num_new() relies on node_last_viewed(), so ensure that no one - // has seen the node of this comment. + // comment_num_new() relies on history_read(), so ensure that no one has + // seen the node of this comment. db_delete('history')->condition('nid', $this->node->nid)->execute(); } else { diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentTestBase.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentTestBase.php index e8bab5d5c3ab72b742ea3ceb7569d8e06101f5b4..902dbddee4b2e37225d6cf16617254086ae089f7 100644 --- a/core/modules/comment/lib/Drupal/comment/Tests/CommentTestBase.php +++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentTestBase.php @@ -20,7 +20,7 @@ abstract class CommentTestBase extends WebTestBase { * * @var array */ - public static $modules = array('comment', 'node'); + public static $modules = array('comment', 'node', 'history'); /** * An administrative user with permission to configure comment settings. diff --git a/core/modules/forum/forum.info b/core/modules/forum/forum.info index 887ed93ddf7640f2a1e542bc7587956c8b3a5bb5..c348a2a81c3f0c19f3be1917378bbed556795130 100644 --- a/core/modules/forum/forum.info +++ b/core/modules/forum/forum.info @@ -1,6 +1,7 @@ name = Forum description = Provides discussion forums. dependencies[] = node +dependencies[] = history dependencies[] = taxonomy dependencies[] = comment package = Core diff --git a/core/modules/forum/forum.module b/core/modules/forum/forum.module index 459cbeb02ed8525b9c05f018bf5a4a92a54dc555..05960a08f61003589e958fa202fb9f006cdabbce 100644 --- a/core/modules/forum/forum.module +++ b/core/modules/forum/forum.module @@ -873,7 +873,7 @@ function forum_forum_load($tid = NULL) { /** * Calculates the number of new posts in a forum that the user has not yet read. * - * Nodes are new if they are newer than NODE_NEW_LIMIT. + * Nodes are new if they are newer than HISTORY_READ_LIMIT. * * @param $term * The term ID of the forum. @@ -890,7 +890,7 @@ function _forum_topics_unread($term, $uid) { $query->addExpression('COUNT(n.nid)', 'count'); return $query ->condition('status', 1) - ->condition('n.created', NODE_NEW_LIMIT, '>') + ->condition('n.created', HISTORY_READ_LIMIT, '>') ->isNull('h.nid') ->addTag('node_access') ->execute() @@ -1311,7 +1311,7 @@ function template_preprocess_forum_submitted(&$variables) { * * @return * The timestamp when the user last viewed this node, if the user has - * previously viewed the node; otherwise NODE_NEW_LIMIT. + * previously viewed the node; otherwise HISTORY_READ_LIMIT. */ function _forum_user_last_visit($nid) { global $user; @@ -1320,10 +1320,10 @@ function _forum_user_last_visit($nid) { if (empty($history)) { $result = db_query('SELECT nid, timestamp FROM {history} WHERE uid = :uid', array(':uid' => $user->uid)); foreach ($result as $t) { - $history[$t->nid] = $t->timestamp > NODE_NEW_LIMIT ? $t->timestamp : NODE_NEW_LIMIT; + $history[$t->nid] = $t->timestamp > HISTORY_READ_LIMIT ? $t->timestamp : HISTORY_READ_LIMIT; } } - return isset($history[$nid]) ? $history[$nid] : NODE_NEW_LIMIT; + return isset($history[$nid]) ? $history[$nid] : HISTORY_READ_LIMIT; } /** diff --git a/core/modules/history/history.api.php b/core/modules/history/history.api.php new file mode 100644 index 0000000000000000000000000000000000000000..0c7276e2fdfca9c31091913bdeeabfd4238548cf --- /dev/null +++ b/core/modules/history/history.api.php @@ -0,0 +1,6 @@ +<?php + +/** + * @file + * API documentation for History module. + */ diff --git a/core/modules/history/history.info b/core/modules/history/history.info new file mode 100644 index 0000000000000000000000000000000000000000..7658749082dec45fd530d5eac35617d4a1c9145d --- /dev/null +++ b/core/modules/history/history.info @@ -0,0 +1,6 @@ +name = History +description = Records which user has read which content. +package = Core +version = VERSION +core = 8.x +dependencies[] = node diff --git a/core/modules/history/history.install b/core/modules/history/history.install new file mode 100644 index 0000000000000000000000000000000000000000..dcfd871322cd161028b18e0a8bcbafaaeeec1757 --- /dev/null +++ b/core/modules/history/history.install @@ -0,0 +1,41 @@ +<?php + +/** + * @file + * Installation functions for History module. + */ + +/** + * Implements hook_schema(). + */ +function history_schema() { + $schema['history'] = array( + 'description' => 'A record of which {users} have read which {node}s.', + 'fields' => array( + 'uid' => array( + 'description' => 'The {users}.uid that read the {node} nid.', + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'nid' => array( + 'description' => 'The {node}.nid that was read.', + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'timestamp' => array( + 'description' => 'The Unix timestamp at which the read occurred.', + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + ), + 'primary key' => array('uid', 'nid'), + 'indexes' => array( + 'nid' => array('nid'), + ), + ); + + return $schema; +} diff --git a/core/modules/history/history.module b/core/modules/history/history.module new file mode 100644 index 0000000000000000000000000000000000000000..d2c8e7d843210ab08848414e0d1701b6cc72df2f --- /dev/null +++ b/core/modules/history/history.module @@ -0,0 +1,108 @@ +<?php + +/** + * @file + * Records which users has read which content. + * + * @todo + * - Generic helper for _forum_user_last_visit() + history_read(). + * - Generic helper for node_mark(). + */ + +use Drupal\node\Plugin\Core\Entity\Node; + +/** + * 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); + +/** + * Retrieves the timestamp for the current user's last view of a specified node. + * + * @param int $nid + * A node ID. + * + * @return int + * If a node has been previously viewed by the user, the timestamp in seconds + * of when the last view occurred; otherwise, zero. + */ +function history_read($nid) { + global $user; + $history = &drupal_static(__FUNCTION__, array()); + + if (!isset($history[$nid])) { + $history[$nid] = db_query("SELECT timestamp FROM {history} WHERE uid = :uid AND nid = :nid", array(':uid' => $user->uid, ':nid' => $nid))->fetchObject(); + } + + return (isset($history[$nid]->timestamp) ? $history[$nid]->timestamp : 0); +} + +/** + * Updates 'last viewed' timestamp of the specified entity for the current user. + * + * @param $nid + * The node ID that has been read. + * @param $account + * (optional) The user account to update the history for. Defaults to the + * current user. + */ +function history_write($nid, $account = NULL) { + global $user; + + if (!isset($account)) { + $account = $user; + } + + if ($account->uid) { + db_merge('history') + ->key(array( + 'uid' => $account->uid, + 'nid' => $nid, + )) + ->fields(array('timestamp' => REQUEST_TIME)) + ->execute(); + } +} + +/** + * Implements hook_cron(). + */ +function history_cron() { + db_delete('history') + ->condition('timestamp', HISTORY_READ_LIMIT, '<') + ->execute(); +} + +/** + * Implements hook_node_delete(). + */ +function history_node_delete(Node $node) { + db_delete('history') + ->condition('nid', $node->nid) + ->execute(); +} + +/** + * Implements hook_user_cancel(). + */ +function history_user_cancel($edit, $account, $method) { + switch ($method) { + case 'user_cancel_reassign': + db_delete('history') + ->condition('uid', $account->uid) + ->execute(); + break; + } +} + +/** + * Implements hook_user_delete(). + */ +function history_user_delete($account) { + db_delete('history') + ->condition('uid', $account->uid) + ->execute(); +} diff --git a/core/modules/history/history.views.inc b/core/modules/history/history.views.inc new file mode 100644 index 0000000000000000000000000000000000000000..8597cfcb95d6265fa587fd005428140fd3e3cdac --- /dev/null +++ b/core/modules/history/history.views.inc @@ -0,0 +1,47 @@ +<?php + +/** + * @file + * Provide views data and handlers for history.module. + * + * @ingroup views_module_handlers + */ + +/** + * Implements hook_views_data(). + */ +function history_views_data() { + // History table + + // We're actually defining a specific instance of the table, so let's + // alias it so that we can later add the real table for other purposes if we + // need it. + $data['history']['table']['group'] = t('Content'); + + // Explain how this table joins to others. + $data['history']['table']['join'] = array( + // Directly links to node table. + 'node' => array( + 'table' => 'history', + 'left_field' => 'nid', + 'field' => 'nid', + 'extra' => array( + array('field' => 'uid', 'value' => '***CURRENT_USER***', 'numeric' => TRUE), + ), + ), + ); + + $data['history']['timestamp'] = array( + 'title' => t('Has new content'), + 'field' => array( + 'id' => 'node_history_user_timestamp', + 'help' => t('Show a marker if the content is new or updated.'), + ), + 'filter' => array( + 'help' => t('Show only content that is new or updated.'), + 'id' => 'node_history_user_timestamp', + ), + ); + + return $data; +} diff --git a/core/modules/node/lib/Drupal/node/NodeStorageController.php b/core/modules/node/lib/Drupal/node/NodeStorageController.php index 3993d24a935900f4ee8f5c00643ed1fb5578e189..1a56fe02be8f4e89e06b28a8ac0e36bd89a9d607 100644 --- a/core/modules/node/lib/Drupal/node/NodeStorageController.php +++ b/core/modules/node/lib/Drupal/node/NodeStorageController.php @@ -159,9 +159,6 @@ protected function postDelete($nodes) { // Delete values from other tables also referencing this node. $ids = array_keys($nodes); - db_delete('history') - ->condition('nid', $ids, 'IN') - ->execute(); db_delete('node_access') ->condition('nid', $ids, 'IN') ->execute(); diff --git a/core/modules/node/node.install b/core/modules/node/node.install index f9c30a5cea84970eb08d10155d231828eab05e3e..c6fd7e6819901fed635f176b27a0e54d39c8e10e 100644 --- a/core/modules/node/node.install +++ b/core/modules/node/node.install @@ -406,36 +406,6 @@ function node_schema() { ), ); - $schema['history'] = array( - 'description' => 'A record of which {users} have read which {node}s.', - 'fields' => array( - 'uid' => array( - 'description' => 'The {users}.uid that read the {node} nid.', - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - ), - 'nid' => array( - 'description' => 'The {node}.nid that was read.', - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - ), - 'timestamp' => array( - 'description' => 'The Unix timestamp at which the read occurred.', - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - ), - ), - 'primary key' => array('uid', 'nid'), - 'indexes' => array( - 'nid' => array('nid'), - ), - ); - return $schema; } @@ -739,6 +709,15 @@ function node_update_8011() { update_variables_to_state(array('node_cron_last' =>'node.cron_last')); } +/** + * Enable History module. + */ +function node_update_8012() { + // Enable the history module without re-installing the schema. + update_module_enable(array('history')); +} + + /** * @} End of "addtogroup updates-7.x-to-8.x" * The next series of updates should start at 9000. diff --git a/core/modules/node/node.module b/core/modules/node/node.module index 97d0f7a49407cf27e47a5ba3ca2212457ff3e316..08d781080a989e03279abe4812a51e31d8ce5c4b 100644 --- a/core/modules/node/node.module +++ b/core/modules/node/node.module @@ -50,15 +50,6 @@ */ const NODE_STICKY = 1; -/** - * Denotes the time cutoff for nodes marked as read. - * - * Nodes changed before this time are always marked as read. Nodes changed after - * this time may be marked new, updated, or read, depending on their state for - * the current user. Defaults to 30 days ago. - */ -define('NODE_NEW_LIMIT', REQUEST_TIME - 30 * 24 * 60 * 60); - /** * Denotes that access is allowed for a node. * @@ -182,15 +173,6 @@ function node_theme() { ); } -/** - * Implements hook_cron(). - */ -function node_cron() { - db_delete('history') - ->condition('timestamp', NODE_NEW_LIMIT, '<') - ->execute(); -} - /** * Implements hook_entity_info(). */ @@ -301,46 +283,6 @@ function node_title_list($result, $title = NULL) { return $num_rows ? array('#theme' => 'item_list__node', '#items' => $items, '#title' => $title) : FALSE; } -/** - * Updates the 'last viewed' timestamp of the specified node for current user. - * - * @param Drupal\node\Node $node - * A node entity. - */ -function node_tag_new(Node $node) { - global $user; - if ($user->uid) { - db_merge('history') - ->key(array( - 'uid' => $user->uid, - 'nid' => $node->nid, - )) - ->fields(array('timestamp' => REQUEST_TIME)) - ->execute(); - } -} - -/** - * Retrieves the timestamp for the current user's last view of a specified node. - * - * @param int $nid - * A node ID. - * - * @return int - * If a node has been previously viewed by the user, the timestamp in seconds - * of when the last view occurred; otherwise, zero. - */ -function node_last_viewed($nid) { - global $user; - $history = &drupal_static(__FUNCTION__, array()); - - if (!isset($history[$nid])) { - $history[$nid] = db_query("SELECT timestamp FROM {history} WHERE uid = :uid AND nid = :nid", array(':uid' => $user->uid, ':nid' => $nid))->fetchObject(); - } - - return (isset($history[$nid]->timestamp) ? $history[$nid]->timestamp : 0); -} - /** * Decides on the type of marker to be displayed for a given node. * @@ -356,16 +298,16 @@ function node_mark($nid, $timestamp) { global $user; $cache = &drupal_static(__FUNCTION__, array()); - if (!$user->uid) { + if (!$user->uid || !module_exists('history')) { return MARK_READ; } if (!isset($cache[$nid])) { - $cache[$nid] = node_last_viewed($nid); + $cache[$nid] = history_read($nid); } - if ($cache[$nid] == 0 && $timestamp > NODE_NEW_LIMIT) { + if ($cache[$nid] == 0 && $timestamp > HISTORY_READ_LIMIT) { return MARK_NEW; } - elseif ($timestamp > $cache[$nid] && $timestamp > NODE_NEW_LIMIT) { + elseif ($timestamp > $cache[$nid] && $timestamp > HISTORY_READ_LIMIT) { return MARK_UPDATED; } return MARK_READ; @@ -1114,7 +1056,9 @@ function node_show(Node $node, $message = FALSE) { $nodes = array('nodes' => node_view_multiple(array($node->nid => $node), 'full')); // Update the history table, stating that this user viewed this node. - node_tag_new($node); + if (module_exists('history')) { + history_write($node->nid); + } return $nodes; } @@ -1503,10 +1447,6 @@ function node_user_cancel($edit, $account, $method) { ->fields(array('uid' => 0)) ->condition('uid', $account->uid) ->execute(); - // Clean history. - db_delete('history') - ->condition('uid', $account->uid) - ->execute(); break; } } @@ -1528,10 +1468,6 @@ function node_user_predelete($account) { foreach ($revisions as $revision) { node_revision_delete($revision); } - // Clean history. - db_delete('history') - ->condition('uid', $account->uid) - ->execute(); } /** diff --git a/core/modules/node/node.views.inc b/core/modules/node/node.views.inc index bf30614cebb565d05b2cda3fee12f5009a66f4f4..3a396bbb861f0b4eaff6ffe8d16627c0c4ff4d16 100644 --- a/core/modules/node/node.views.inc +++ b/core/modules/node/node.views.inc @@ -640,37 +640,6 @@ function node_views_data() { ), ); - // History table - - // We're actually defining a specific instance of the table, so let's - // alias it so that we can later add the real table for other purposes if we - // need it. - $data['history']['table']['group'] = t('Content'); - - // Explain how this table joins to others. - $data['history']['table']['join'] = array( - // Directly links to node table. - 'node' => array( - 'table' => 'history', - 'left_field' => 'nid', - 'field' => 'nid', - 'extra' => array( - array('field' => 'uid', 'value' => '***CURRENT_USER***', 'numeric' => TRUE), - ), - ), - ); - - $data['history']['timestamp'] = array( - 'title' => t('Has new content'), - 'field' => array( - 'id' => 'node_history_user_timestamp', - 'help' => t('Show a marker if the content is new or updated.'), - ), - 'filter' => array( - 'help' => t('Show only content that is new or updated.'), - 'id' => 'node_history_user_timestamp', - ), - ); return $data; } diff --git a/core/modules/system/lib/Drupal/system/Tests/Module/DependencyTest.php b/core/modules/system/lib/Drupal/system/Tests/Module/DependencyTest.php index ebcc11f637dba1e8189bee33388c9b332bfb3c79..cb56a9010f6e2d7f2239a77757f131d6856d7321 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Module/DependencyTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Module/DependencyTest.php @@ -134,11 +134,11 @@ function testModuleEnableOrder() { $this->assertModules(array('module_test'), TRUE); variable_set('dependency_test', 'dependency'); // module_test creates a dependency chain: - // - forum depends on taxonomy, comment, and poll (via module_test) + // - forum depends on taxonomy, comment, history, and poll (via module_test) // - taxonomy depends on options // - poll depends on php (via module_test) // The correct enable order is: - $expected_order = array('comment', 'options', 'taxonomy', 'php', 'poll', 'forum'); + $expected_order = array('comment', 'history', 'options', 'taxonomy', 'php', 'poll', 'forum'); // Enable the modules through the UI, verifying that the dependency chain // is correct. @@ -146,14 +146,15 @@ function testModuleEnableOrder() { $edit['modules[Core][forum][enable]'] = 'forum'; $this->drupalPost('admin/modules', $edit, t('Save configuration')); $this->assertModules(array('forum'), FALSE); - $this->assertText(t('You must enable the Taxonomy, Options, Comment, Poll, PHP Filter modules to install Forum.')); + $this->assertText(t('You must enable the History, Taxonomy, Options, Comment, Poll, PHP Filter modules to install Forum.')); + $edit['modules[Core][history][enable]'] = 'history'; $edit['modules[Core][options][enable]'] = 'options'; $edit['modules[Core][taxonomy][enable]'] = 'taxonomy'; $edit['modules[Core][comment][enable]'] = 'comment'; $edit['modules[Core][poll][enable]'] = 'poll'; $edit['modules[Core][php][enable]'] = 'php'; $this->drupalPost('admin/modules', $edit, t('Save configuration')); - $this->assertModules(array('forum', 'poll', 'php', 'comment', 'taxonomy', 'options'), TRUE); + $this->assertModules(array('forum', 'poll', 'php', 'comment', 'history', 'taxonomy', 'options'), TRUE); // Check the actual order which is saved by module_test_modules_enabled(). $this->assertIdentical(variable_get('test_module_enable_order', array()), $expected_order); diff --git a/core/modules/tracker/lib/Drupal/tracker/Tests/TrackerTest.php b/core/modules/tracker/lib/Drupal/tracker/Tests/TrackerTest.php index 0473d38ce59464fee96ec3e16685c3d58acbda77..189b92796c7cc2085b2d7d04c98836ccfc8b965c 100644 --- a/core/modules/tracker/lib/Drupal/tracker/Tests/TrackerTest.php +++ b/core/modules/tracker/lib/Drupal/tracker/Tests/TrackerTest.php @@ -19,7 +19,7 @@ class TrackerTest extends WebTestBase { * * @var array */ - public static $modules = array('comment', 'tracker'); + public static $modules = array('comment', 'tracker', 'history'); /** * The main user for testing. diff --git a/core/modules/user/user.api.php b/core/modules/user/user.api.php index 648cc0f82d17368eda95391452edb96ef039f5d6..8714d16ee09b34a45148af8162746fb42c883856 100644 --- a/core/modules/user/user.api.php +++ b/core/modules/user/user.api.php @@ -126,10 +126,6 @@ function hook_user_cancel($edit, $account, $method) { ->fields(array('uid' => 0)) ->condition('uid', $account->uid) ->execute(); - // Clean history. - db_delete('history') - ->condition('uid', $account->uid) - ->execute(); break; } } diff --git a/core/profiles/standard/standard.info b/core/profiles/standard/standard.info index 4c812d0c39b3e3d85f2cbe7affe0e620f59ea521..9747bd53857bf3a91f337243643e8bd213d4f3d8 100644 --- a/core/profiles/standard/standard.info +++ b/core/profiles/standard/standard.info @@ -3,6 +3,7 @@ description = Install with commonly used features pre-configured. version = VERSION core = 8.x dependencies[] = node +dependencies[] = history dependencies[] = block dependencies[] = color dependencies[] = config