summaryrefslogtreecommitdiff
path: root/modules/tracker/tracker.module
diff options
context:
space:
mode:
Diffstat (limited to 'modules/tracker/tracker.module')
-rw-r--r--modules/tracker/tracker.module287
1 files changed, 285 insertions, 2 deletions
diff --git a/modules/tracker/tracker.module b/modules/tracker/tracker.module
index 89ea0e826..c8a32ec57 100644
--- a/modules/tracker/tracker.module
+++ b/modules/tracker/tracker.module
@@ -36,10 +36,12 @@ function tracker_menu() {
);
$items['tracker/%user_uid_optional'] = array(
'title' => 'My recent posts',
+ 'page callback' => 'tracker_page',
'access callback' => '_tracker_myrecent_access',
'access arguments' => array(1),
'page arguments' => array(1),
'type' => MENU_LOCAL_TASK,
+ 'file' => 'tracker.pages.inc',
);
$items['user/%user/track'] = array(
@@ -55,11 +57,91 @@ function tracker_menu() {
'title' => 'Track posts',
'type' => MENU_DEFAULT_LOCAL_TASK,
);
+
return $items;
}
/**
- * Access callback for tracker/%user_uid_optional
+ * Implement hook_cron().
+ */
+function tracker_cron() {
+ $max_nid = variable_get('tracker_index_nid', 0);
+ $batch_size = variable_get('tracker_batch_size', 1000);
+ if ($max_nid > 0) {
+ $last_nid = FALSE;
+ $result = db_query_range('SELECT nid, uid, status FROM {node} WHERE nid <= :max_nid ORDER BY nid DESC', array(':max_nid' => $max_nid), 0, $batch_size);
+
+ $count = 0;
+
+ foreach ($result as $row) {
+ // Calculate the changed timestamp for this node.
+ $changed = _tracker_calculate_changed($row->nid);
+
+ // Remove existing data for this node.
+ db_delete('tracker_node')
+ ->condition('nid', $row->nid)
+ ->execute();
+ db_delete('tracker_user')
+ ->condition('nid', $row->nid)
+ ->execute();
+
+ // Insert the node-level data.
+ db_insert('tracker_node')
+ ->fields(array(
+ 'nid' => $row->nid,
+ 'published' => $row->status,
+ 'changed' => $changed,
+ ))
+ ->execute();
+
+ // Insert the user-level data for the node's author.
+ db_insert('tracker_user')
+ ->fields(array(
+ 'nid' => $row->nid,
+ 'published' => $row->status,
+ 'changed' => $changed,
+ 'uid' => $row->uid,
+ ))
+ ->execute();
+
+ $query = db_select('comment', 'c');
+ // Force PostgreSQL to do an implicit cast by adding 0.
+ $query->addExpression('0 + :changed', 'changed', array(':changed' => $changed));
+ $query->addField('c', 'status', 'published');
+ $query
+ ->distinct()
+ ->fields('c', array('uid', 'nid'))
+ ->condition('c.nid', $row->nid)
+ ->condition('c.uid', $row->uid, '<>')
+ ->condition('c.status', COMMENT_PUBLISHED);
+
+ // Insert the user-level data for the commenters (except if a commenter
+ // is the node's author).
+ db_insert('tracker_user')
+ ->from($query)
+ ->execute();
+
+ // Note that we have indexed at least one node.
+ $last_nid = $row->nid;
+
+ $count++;
+ }
+
+ if ($last_nid !== FALSE) {
+ // Prepare a starting point for the next run.
+ variable_set('tracker_index_nid', $last_nid - 1);
+
+ watchdog('tracker', t('Indexed %count nodes for tracking.', array('%count' => $count)));
+ }
+ else {
+ // If all nodes have been indexed, set to zero to skip future cron runs.
+ variable_set('tracker_index_nid', 0);
+ }
+ }
+}
+
+/**
+ * Access callback for tracker/%user_uid_optional.
*/
function _tracker_myrecent_access($account) {
// This path is only allowed for authenticated users looking at their own posts.
@@ -67,9 +149,210 @@ function _tracker_myrecent_access($account) {
}
/**
- * Access callback for user/%user/track
+ * Access callback for user/%user/track.
*/
function _tracker_user_access($account) {
return user_view_access($account) && user_access('access content');
}
+/**
+ * Implement hook_nodeapi_insert().
+ */
+function tracker_node_insert($node, $arg = 0) {
+ _tracker_add($node->nid, $node->uid, $node->changed);
+}
+
+/**
+ * Implement hook_nodeapi_update().
+ */
+function tracker_node_update($node, $arg = 0) {
+ _tracker_add($node->nid, $node->uid, $node->changed);
+}
+
+/**
+ * Implement hook_nodeapi_delete().
+ */
+function tracker_node_delete($node, $arg = 0) {
+ _tracker_remove($node->nid, $node->uid, $node->changed);
+}
+
+/**
+ * Implement hook_comment_update().
+ *
+ * Comment module doesn't call hook_comment_unpublish() when saving individual
+ * comments so we need to check for those here.
+ */
+function tracker_comment_update($comment) {
+ $comment = (array) $comment;
+ // comment_save() calls hook_comment_publish() for all published comments
+ // so we to handle all other values here.
+ if ($comment['status'] != COMMENT_PUBLISHED) {
+ _tracker_remove($comment['nid'], $comment['uid'], $comment['timestamp']);
+ }
+}
+
+/**
+ * Implement hook_comment_publish().
+ *
+ * This actually handles the insert and update of published nodes since
+ * comment_save() calls hook_comment_publish() for all published comments.
+ */
+function tracker_comment_publish($comment) {
+ _tracker_add($comment->nid, $comment->uid, $comment->timestamp);
+}
+
+/**
+ * Implement hook_comment_unpublish().
+ */
+function tracker_comment_unpublish($comment) {
+ _tracker_remove($comment->nid, $comment->uid, $comment->timestamp);
+}
+
+/**
+ * Implement hook_comment_delete().
+ */
+function tracker_comment_delete($comment) {
+ _tracker_remove($comment->nid, $comment->uid, $comment->timestamp);
+}
+
+/**
+ * Update indexing tables when a node is added, updated or commented on.
+ *
+ * @param $nid
+ * A node ID.
+ * @param $uid
+ * The node or comment author.
+ * @param $changed
+ * The node updated timestamp or comment timestamp.
+ */
+function _tracker_add($nid, $uid, $changed) {
+ $node = db_query('SELECT nid, status, uid, changed FROM {node} WHERE nid = :nid', array(':nid' => $nid))->fetchObject();
+
+ // Adding a comment can only increase the changed timestamp, so our
+ // calculation here is simple.
+ $changed = max($node->changed, $changed);
+
+ // Update the node-level data.
+ db_merge('tracker_node')
+ ->key(array('nid' => $nid))
+ ->fields(array(
+ 'changed' => $changed,
+ 'published' => $node->status,
+ ))
+ ->execute();
+
+ // Create or update the user-level data.
+ db_merge('tracker_user')
+ ->key(array(
+ 'nid' => $nid,
+ 'uid' => $uid,
+ ))
+ ->fields(array(
+ 'changed' => $changed,
+ 'published' => $node->status,
+ ))
+ ->execute();
+}
+
+/**
+ * Determine the max timestamp between $node->changed and the last comment.
+ *
+ * @param $nid
+ * A node ID.
+ *
+ * @return
+ * The $node->changed timestamp, or most recent comment timestamp, whichever
+ * is the greatest.
+ */
+function _tracker_calculate_changed($nid) {
+ $changed = db_query('SELECT changed FROM {node} WHERE nid = :nid', array(':nid' => $nid))->fetchField();
+ $latest_comment = db_query_range('SELECT cid, timestamp FROM {comment} WHERE nid = :nid AND status = :status ORDER BY timestamp DESC', array(
+ ':nid' => $nid,
+ ':status' => COMMENT_PUBLISHED,
+ ), 0, 1)->fetchObject();
+ if ($latest_comment && $latest_comment->timestamp > $changed) {
+ $changed = $latest_comment->timestamp;
+ }
+ return $changed;
+}
+
+/**
+ * Clean up indexed data when nodes or comments are removed.
+ *
+ * @param $nid
+ * The node ID.
+ * @param $uid
+ * The author of the node or comment.
+ * @param $changed
+ * The last changed timestamp of the node.
+ */
+function _tracker_remove($nid, $uid = NULL, $changed = NULL) {
+ $node = db_query('SELECT nid, status, uid, changed FROM {node} WHERE nid = :nid', array(':nid' => $nid))->fetchObject();
+
+ // The user only keeps his or her subscription if both of the following are true:
+ // (1) The node exists.
+ // (2) The user is either the node author or has commented on the node.
+ $keep_subscription = FALSE;
+
+ if ($node) {
+ // Self-authorship is one reason to keep the user's subscription.
+ $keep_subscription = ($node->uid == $uid);
+
+ // Comments are a second reason to keep the user's subscription.
+ if (!$keep_subscription) {
+ // Check if the user has commented at least once on the given nid
+ $keep_subscription = db_query_range('SELECT COUNT(*) FROM {comment} WHERE nid = :nid AND uid = :uid AND status = 0', array(
+ ':nid' => $nid,
+ ':uid' => $uid,
+ ), 0, 1)->fetchField();
+ }
+
+ // If we haven't found a reason to keep the user's subscription, delete it.
+ if (!$keep_subscription) {
+ db_delete('tracker_user')
+ ->condition('nid', $nid)
+ ->condition('uid', $uid)
+ ->execute();
+ }
+
+ // Now we need to update the (possibly) changed timestamps for other users
+ // and the node itself.
+
+ // We only need to do this if the removed item has a timestamp that equals
+ // or exceeds the listed changed timestamp for the node
+ $tracker_node = db_query('SELECT nid, changed FROM {tracker_node} WHERE nid = :nid', array(':nid' => $nid))->fetchObject();
+ 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($nid);
+
+ // And then we push the out the new changed timestamp to our denormalized
+ // tables.
+ db_update('tracker_node')
+ ->fields(array(
+ 'changed' => $changed,
+ 'published' => $node->status,
+ ))
+ ->condition('nid', $nid)
+ ->execute();
+ db_update('tracker_node')
+ ->fields(array(
+ 'changed' => $changed,
+ 'published' => $node->status,
+ ))
+ ->condition('nid', $nid)
+ ->execute();
+ }
+ }
+ else {
+ // If the node doesn't exist, remove everything.
+ db_delete('tracker_node')
+ ->condition('nid', $nid)
+ ->execute();
+ db_delete('tracker_user')
+ ->condition('nid', $nid)
+ ->execute();
+ }
+}