diff options
Diffstat (limited to 'modules/tracker/tracker.module')
-rw-r--r-- | modules/tracker/tracker.module | 287 |
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(); + } +} |