| 1 |
<?php
|
| 2 |
// $Id: tracker2.module,v 1.14 2009/01/12 22:09:35 toddnienkerk Exp $
|
| 3 |
/**
|
| 4 |
* @file
|
| 5 |
* Enables tracking of recent posts for users.
|
| 6 |
*/
|
| 7 |
|
| 8 |
/**
|
| 9 |
* Implementation of hook_help().
|
| 10 |
*/
|
| 11 |
function tracker2_help($path, $arg) {
|
| 12 |
switch ($path) {
|
| 13 |
case 'admin/help#tracker2':
|
| 14 |
return '<p>'. t('The Tracker2 module is a much more efficient tracker that maintains seperate database tables of updated items.') .'</p>';
|
| 15 |
}
|
| 16 |
}
|
| 17 |
|
| 18 |
/**
|
| 19 |
* Implementation of hook_menu().
|
| 20 |
*/
|
| 21 |
function tracker2_menu() {
|
| 22 |
$base = 'tracker2';
|
| 23 |
$user_base = 'track2';
|
| 24 |
if (!module_exists('tracker')) {
|
| 25 |
$base = 'tracker';
|
| 26 |
$user_base = 'track';
|
| 27 |
}
|
| 28 |
|
| 29 |
$items[$base] = array(
|
| 30 |
'title' => 'Recent posts',
|
| 31 |
'page callback' => 'tracker2_page',
|
| 32 |
'access arguments' => array('access content'),
|
| 33 |
'weight' => 1,
|
| 34 |
'file' => 'tracker2.pages.inc',
|
| 35 |
);
|
| 36 |
|
| 37 |
$items[$base .'/all'] = array(
|
| 38 |
'title' => 'All recent posts',
|
| 39 |
'type' => MENU_DEFAULT_LOCAL_TASK,
|
| 40 |
);
|
| 41 |
$items[$base .'/%user_uid_optional'] = array(
|
| 42 |
'title' => 'My recent posts',
|
| 43 |
'access callback' => '_tracker2_myrecent_access',
|
| 44 |
'access arguments' => array(1),
|
| 45 |
'page arguments' => array(1),
|
| 46 |
'type' => MENU_LOCAL_TASK,
|
| 47 |
);
|
| 48 |
|
| 49 |
$items['admin/settings/tracker2'] = array(
|
| 50 |
'title' => 'Tracker 2',
|
| 51 |
'description' => 'High-performance reimplementation of the Tracker module.',
|
| 52 |
'page callback' => 'drupal_get_form',
|
| 53 |
'access arguments' => array('administer tracker'),
|
| 54 |
'page arguments' => array('tracker2_admin'),
|
| 55 |
'file' => 'tracker2.admin.inc',
|
| 56 |
);
|
| 57 |
|
| 58 |
$items['user/%user/'. $user_base] = array(
|
| 59 |
'title' => 'Track',
|
| 60 |
'page callback' => 'tracker2_track_user',
|
| 61 |
'access arguments' => array('access content'),
|
| 62 |
'type' => MENU_LOCAL_TASK,
|
| 63 |
'file' => 'tracker2.pages.inc',
|
| 64 |
);
|
| 65 |
$items['user/%user/'. $user_base .'/posts'] = array(
|
| 66 |
'title' => 'Track posts',
|
| 67 |
'type' => MENU_DEFAULT_LOCAL_TASK,
|
| 68 |
);
|
| 69 |
|
| 70 |
return $items;
|
| 71 |
}
|
| 72 |
|
| 73 |
/**
|
| 74 |
* Access callback for tracker/%user.
|
| 75 |
*/
|
| 76 |
function _tracker2_myrecent_access($account) {
|
| 77 |
// This path is only allowed for authenticated users looking at their own posts.
|
| 78 |
return $account->uid && ($GLOBALS['user']->uid == $account->uid) && user_access('access content');
|
| 79 |
}
|
| 80 |
|
| 81 |
/**
|
| 82 |
* Access callback for user account tracker tab.
|
| 83 |
*/
|
| 84 |
function _tracker2_user_access($account) {
|
| 85 |
return user_view_access($account) && user_access('access content');
|
| 86 |
}
|
| 87 |
|
| 88 |
|
| 89 |
/**
|
| 90 |
* Implementation of hook_perm().
|
| 91 |
*/
|
| 92 |
function tracker2_perm() {
|
| 93 |
return array('administer tracker');
|
| 94 |
}
|
| 95 |
|
| 96 |
/**
|
| 97 |
* Implementation of hook_cron().
|
| 98 |
*/
|
| 99 |
function tracker2_cron() {
|
| 100 |
$max_nid = variable_get('tracker2_index_nid', 0);
|
| 101 |
$batch_size = variable_get('tracker2_batch_size', 1000);
|
| 102 |
if ($max_nid) {
|
| 103 |
$last_nid = FALSE;
|
| 104 |
$res = db_query_range("SELECT nid, uid, status FROM {node} WHERE nid <= %d ORDER BY nid DESC", $max_nid, 0, $batch_size);
|
| 105 |
$count = 0;
|
| 106 |
|
| 107 |
while ($row = db_fetch_object($res)) {
|
| 108 |
// Calculate the changed timestamp for this node.
|
| 109 |
$changed = _tracker2_calculate_changed($row->nid);
|
| 110 |
|
| 111 |
// Remove existing data for this node.
|
| 112 |
db_query("DELETE FROM {tracker2_node} WHERE nid = %d", $row->nid);
|
| 113 |
db_query("DELETE FROM {tracker2_user} WHERE nid = %d", $row->nid);
|
| 114 |
|
| 115 |
// Insert the node-level data.
|
| 116 |
db_query("INSERT INTO {tracker2_node} (nid, published, changed) VALUES (%d, %d, %d)", $row->nid, $row->status, $changed);
|
| 117 |
|
| 118 |
// Insert the user-level data for the node's author.
|
| 119 |
db_query("INSERT INTO {tracker2_user} (nid, published, uid, changed) VALUES (%d, %d, %d, %d)", $row->nid, $row->status, $row->uid, $changed);
|
| 120 |
|
| 121 |
// Insert the user-level data for the commenters (except if a commenter is the node's author).
|
| 122 |
db_query("INSERT INTO {tracker2_user} (nid, published, uid, changed) SELECT DISTINCT %d AS nid, %d AS published, uid, %d AS changed FROM {comments} WHERE nid = %d AND uid <> %d AND status = %d", $row->nid, $row->status, $changed, $row->nid, $row->uid, COMMENT_PUBLISHED);
|
| 123 |
|
| 124 |
// Note that we have indexed at least one node.
|
| 125 |
$last_nid = $row->nid;
|
| 126 |
|
| 127 |
$count++;
|
| 128 |
}
|
| 129 |
|
| 130 |
if ($last_nid !== FALSE) {
|
| 131 |
// Prepare a starting point for the next run.
|
| 132 |
variable_set('tracker2_index_nid', $last_nid - 1);
|
| 133 |
|
| 134 |
watchdog('tracker2', 'Indexed %count nodes for tracking.', array('%count' => $count));
|
| 135 |
}
|
| 136 |
else {
|
| 137 |
// If all nodes have been indexed, set to zero to skip future cron runs.
|
| 138 |
variable_set('tracker2_index_nid', 0);
|
| 139 |
}
|
| 140 |
}
|
| 141 |
}
|
| 142 |
|
| 143 |
/**
|
| 144 |
* Create or update the tracker2 records.
|
| 145 |
*/
|
| 146 |
function _tracker2_add($nid, $uid, $changed) {
|
| 147 |
$node = db_fetch_object(db_query("SELECT nid, status, uid, changed FROM {node} WHERE nid = %d", $nid));
|
| 148 |
|
| 149 |
$insert = new stdClass();
|
| 150 |
$insert->nid = $nid;
|
| 151 |
// Adding a comment can only increase the changed timestamp, so our calculation here is easy.
|
| 152 |
$insert->changed = max($node->changed, $changed);
|
| 153 |
$insert->published = $node->status;
|
| 154 |
|
| 155 |
// Create or update the node-level data.
|
| 156 |
$exists = db_result(db_query("SELECT COUNT(*) FROM {tracker2_node} WHERE nid = %d", $nid));
|
| 157 |
if ($exists) {
|
| 158 |
drupal_write_record('tracker2_node', $insert, 'nid');
|
| 159 |
}
|
| 160 |
else {
|
| 161 |
drupal_write_record('tracker2_node', $insert);
|
| 162 |
}
|
| 163 |
|
| 164 |
// Create or update the user-level data.
|
| 165 |
$exists = db_result(db_query("SELECT COUNT(*) FROM {tracker2_user} WHERE nid = %d AND uid = %d", $nid, $uid));
|
| 166 |
$insert->uid = $uid;
|
| 167 |
if ($exists) {
|
| 168 |
drupal_write_record('tracker2_user', $insert, 'nid');
|
| 169 |
}
|
| 170 |
else {
|
| 171 |
drupal_write_record('tracker2_user', $insert);
|
| 172 |
}
|
| 173 |
}
|
| 174 |
|
| 175 |
/**
|
| 176 |
* Calculate the last time the node was changed or commented upon.
|
| 177 |
*/
|
| 178 |
function _tracker2_calculate_changed($nid) {
|
| 179 |
$changed = db_result(db_query("SELECT changed FROM {node} WHERE nid = %d", $nid));
|
| 180 |
$comment_changed = db_result(db_query_range("SELECT timestamp FROM {comments} WHERE nid = %d AND status = %d ORDER BY timestamp DESC", $nid, COMMENT_PUBLISHED, 0, 1));
|
| 181 |
return max($comment_changed, $changed);
|
| 182 |
}
|
| 183 |
|
| 184 |
/**
|
| 185 |
* Delete from the tracker2 records.
|
| 186 |
*/
|
| 187 |
function _tracker2_remove($nid, $uid = NULL, $changed = NULL) {
|
| 188 |
$node = db_fetch_object(db_query("SELECT nid, status, uid, changed FROM {node} WHERE nid = %d", $nid));
|
| 189 |
|
| 190 |
if ($node) {
|
| 191 |
$keep_subscription = FALSE;
|
| 192 |
|
| 193 |
// The user only keeps his or her subscription if both of the following are true:
|
| 194 |
// (1) The node exists.
|
| 195 |
// (2) The user is either the node author or has commented on the node.
|
| 196 |
|
| 197 |
// Self-authorship is one reason to keep the user's subscription.
|
| 198 |
$keep_subscription = ($node->uid == $uid);
|
| 199 |
|
| 200 |
// Comments are a second reason to keep the user's subscription.
|
| 201 |
if (!$keep_subscription) {
|
| 202 |
// Check if the user has a published comment at least once on the given nid.
|
| 203 |
$keep_subscription = db_result(db_query_range("SELECT COUNT(*) FROM {comments} WHERE nid = %d AND uid = %d AND status = 0", $nid, $uid, 0, 1));
|
| 204 |
}
|
| 205 |
|
| 206 |
// If we haven't found a reason to keep the user's subscription, delete it.
|
| 207 |
if (!$keep_subscription) {
|
| 208 |
db_query("DELETE FROM {tracker2_user} WHERE nid = %d AND uid = %d", $nid, $uid);
|
| 209 |
}
|
| 210 |
|
| 211 |
// Now we need to update the (possibly) changed timestamps for other users and the node itself.
|
| 212 |
|
| 213 |
// We only need to do this if the removed item has a timestamp that equals
|
| 214 |
// or exceeds the listed changed timestamp for the node.
|
| 215 |
$tracker_node = db_fetch_object(db_query("SELECT nid, changed FROM {tracker2_node} WHERE nid = %d", $nid));
|
| 216 |
if ($tracker_node && $changed >= $tracker_node->changed) {
|
| 217 |
// If we're here, the item being removed is *possibly* the item that established the node's changed timestamp.
|
| 218 |
|
| 219 |
$insert = new stdClass();
|
| 220 |
$insert->nid = $nid;
|
| 221 |
$insert->published = $node->status;
|
| 222 |
|
| 223 |
// We just have to recalculate things from scratch.
|
| 224 |
$insert->changed = _tracker2_calculate_changed($nid);
|
| 225 |
|
| 226 |
// And then we push the out the new changed timestamp to our denormalized tables.
|
| 227 |
drupal_write_record('tracker2_node', $insert, 'nid');
|
| 228 |
drupal_write_record('tracker2_user', $insert, 'nid');
|
| 229 |
}
|
| 230 |
}
|
| 231 |
else {
|
| 232 |
// If the node doesn't exist, remove everything.
|
| 233 |
db_query("DELETE FROM {tracker2_node} WHERE nid = %d", $nid);
|
| 234 |
db_query("DELETE FROM {tracker2_user} WHERE nid = %d", $nid);
|
| 235 |
}
|
| 236 |
}
|
| 237 |
|
| 238 |
/**
|
| 239 |
* Implementation of hook_nodeapi().
|
| 240 |
*/
|
| 241 |
function tracker2_nodeapi(&$node, $op) {
|
| 242 |
if ($op == 'insert' || $op == 'update') {
|
| 243 |
_tracker2_add($node->nid, $node->uid, $node->changed);
|
| 244 |
}
|
| 245 |
else if ($op == 'delete') {
|
| 246 |
_tracker2_remove($node->nid, $node->uid, $node->changed);
|
| 247 |
}
|
| 248 |
}
|
| 249 |
|
| 250 |
/**
|
| 251 |
* Implementation of hook_comment().
|
| 252 |
*/
|
| 253 |
function tracker2_comment($a1, $op) {
|
| 254 |
$comment = (array) $a1;
|
| 255 |
switch ($op) {
|
| 256 |
case 'insert':
|
| 257 |
case 'update':
|
| 258 |
case 'publish':
|
| 259 |
if ($comment['status'] == COMMENT_PUBLISHED) {
|
| 260 |
_tracker2_add($comment['nid'], $comment['uid'], $comment['timestamp']);
|
| 261 |
}
|
| 262 |
else {
|
| 263 |
_tracker2_remove($comment['nid'], $comment['uid'], $comment['timestamp']);
|
| 264 |
}
|
| 265 |
break;
|
| 266 |
case 'delete':
|
| 267 |
case 'unpublish':
|
| 268 |
_tracker2_remove($comment['nid'], $comment['uid'], $comment['timestamp']);
|
| 269 |
break;
|
| 270 |
}
|
| 271 |
}
|
| 272 |
|
| 273 |
/**
|
| 274 |
* Implementation of hook_theme().
|
| 275 |
*/
|
| 276 |
function tracker2_theme() {
|
| 277 |
return array(
|
| 278 |
'tracker2_page' => array(
|
| 279 |
'arguments' => array('nodes' => array()),
|
| 280 |
'file' => 'tracker2.pages.inc',
|
| 281 |
),
|
| 282 |
);
|
| 283 |
}
|