Commit c09158c3 authored by Dries's avatar Dries

- Patch #10308 by Bart Jansens/ccourtne: fixed shadow copies.

- Patch #10308 by ccourtne: performance improvements: comment statistics are now cached in a new SQL table which significantly improves performance of the forum block and the forum pages.  These pages are about 3 times faster now!
parent 93f193d7
......@@ -80,7 +80,8 @@
"2004-08-11" => "update_101",
"2004-08-12" => "update_102",
"2004-08-17" => "update_103",
"2004-08-19" => "update_104"
"2004-08-19" => "update_104",
"2004-09-14" => "update_105"
);
function update_32() {
......@@ -1750,6 +1751,51 @@ function update_104() {
return $ret;
}
function update_105() {
$ret = array();
$shadowupdates = db_query("SELECT nid,tid FROM {forum} WHERE shadow=0");
while ($shadowrecord = db_fetch_object($shadowupdates)) {
db_query("DELETE FROM term_node WHERE nid = %d AND tid <> %d", $shadowrecord->nid, $shadowrecord-tid);
}
$ret[] = update_sql("ALTER TABLE {forum} DROP shadow");
$ret[] = update_sql('ALTER TABLE node ADD INDEX node_status_type (status, type, nid)');
$ret[] = update_sql("CREATE TABLE node_comment_statistics (
nid int(10) unsigned NOT NULL auto_increment,
cid int(10) unsigned NOT NULL default '0',
last_comment_timestamp int(11) NOT NULL default '0',
last_comment_name varchar(60) default NULL,
last_comment_uid int(10) NOT NULL default '0',
comment_count int(10) unsigned NOT NULL default '0',
PRIMARY KEY (nid),
KEY node_comment_timestamp (last_comment_timestamp)
) TYPE=MyISAM");
$ret[] = update_sql("INSERT INTO {node_comment_statistics} (nid, cid, last_comment_timestamp, last_comment_name, last_comment_uid, comment_count) SELECT n.nid, 0, n.created, NULL, n.uid, 0 FROM {node} n");
$ret[] = update_sql("CREATE TABLE {forum_conv_temp} (
nid int(10) unsigned NOT NULL default '0',
cid int(10) unsigned NOT NULL default '0',
comment_count int(10) unsigned NOT NULL default '0',
PRIMARY KEY (nid)
)");
$ret[] = update_sql('INSERT INTO {forum_conv_temp} SELECT f.nid, MAX(c.cid), COUNT(c.nid) FROM {forum} f INNER JOIN {comments} c ON f.nid = c.nid WHERE c.status = 0 GROUP BY f.nid');
/* This would be faster but only works with MySQL 4.0.4 or higher
$ret[] = update_sql('UPDATE {node_comment_statistics} n, {forum_conv_temp} t, {comments} c SET n.comment_count = t.comment_count, n.last_comment_timestamp = c.timestamp, n.last_comment_name = c.name, n.last_comment_uid = c.uid, n.cid = t.cid WHERE t.cid = c.cid AND n.nid = t.nid');
*/
$commentupdates = db_query("SELECT t.nid, t.cid, t.comment_count, c.timestamp, c.name, c.uid FROM {forum_conv_temp} t INNER JOIN {comments} c ON t.cid = c.cid");
while ($commentrecord = db_fetch_object($commentupdates)) {
db_query("UPDATE {node_comment_statistics} SET comment_count = %d, last_comment_timestamp = %d, last_comment_name = '%s', last_comment_uid = %d, cid = %d WHERE nid = %d", $commentrecord->comment_count, $commentrecord->timestamp, $commentrecord->name, $commentrecord->uid, $commentrecord->cid, $commentrecord->nid);
}
$ret[] = update_sql("DROP TABLE {forum_conv_temp}");
return $ret;
}
function update_sql($sql) {
$edit = $_POST["edit"];
......
......@@ -226,6 +226,7 @@ function comment_link($type, $node = 0, $main = 0) {
/**
* Implementation of hook_nodeapi().
*
*/
function comment_nodeapi(&$node, $op, $arg = 0) {
switch ($op) {
......@@ -241,14 +242,20 @@ function comment_nodeapi(&$node, $op, $arg = 0) {
return form_group(t('User comments'), $output);
}
break;
case 'load':
return db_fetch_array(db_query("SELECT last_comment_timestamp, last_comment_name, last_comment_name, comment_count, cid as last_comment_cid FROM {node_comment_statistics} WHERE nid = %d", $node->nid));
case 'validate':
if (!user_access('administer nodes')) {
// Force default for normal users:
$node->comment = variable_get("comment_$node->type", 2);
}
break;
case 'insert':
db_query('INSERT INTO {node_comment_statistics} (nid, cid, last_comment_timestamp, last_comment_name, last_comment_uid, comment_count) VALUES (%d,0,%d,NULL,%d,0)', $node->nid, $node->created, $node->uid);
break;
case 'delete':
db_query("DELETE FROM {comments} WHERE nid = '$node->nid'");
db_query('DELETE FROM {comments} WHERE nid = %d', $node->nid);
db_query('DELETE FROM {node_comment_statistics} WHERE nid = %d', $node->nid);
break;
}
}
......@@ -558,6 +565,8 @@ function comment_post($edit) {
// user.
db_query("UPDATE {comments} SET subject = '%s', comment = '%s' WHERE cid = %d AND uid = '$user->uid'", $edit['subject'], $edit['comment'], $edit["cid"]);
_comment_update_node_statistics($edit['nid']);
// Allow modules to respond to the updating of a comment.
module_invoke_all('comment', 'update', $edit);
......@@ -664,8 +673,16 @@ function comment_post($edit) {
$edit["cid"] = db_next_id("{comments}_cid");
$edit['timestamp'] = time();
if ($edit['uid'] = $user->uid) {
$edit['name'] = $user->name;
}
db_query("INSERT INTO {comments} (cid, nid, pid, uid, subject, comment, hostname, timestamp, status, score, users, thread, name, mail, homepage) VALUES (%d, %d, %d, %d, '%s', '%s', '%s', %d, %d, %d, '%s', '%s', '%s', '%s', '%s')", $edit["cid"], $edit["nid"], $edit["pid"], $user->uid, $edit['subject'], $edit['comment'], $_SERVER['REMOTE_ADDR'], time(), $status, $score, $users, $thread, $edit["name"], $edit['mail'], $edit["homepage"]);
db_query("INSERT INTO {comments} (cid, nid, pid, uid, subject, comment, hostname, timestamp, status, score, users, thread, name, mail, homepage) VALUES (%d, %d, %d, %d, '%s', '%s', '%s', %d, %d, %d, '%s', '%s', '%s', '%s', '%s')", $edit["cid"], $edit["nid"], $edit["pid"], $edit['uid'], $edit['subject'], $edit['comment'], $_SERVER['REMOTE_ADDR'], $edit['timestamp'], $status, $score, $users, $thread, $edit["name"], $edit['mail'], $edit["homepage"]);
_comment_update_node_statistics($edit['nid']);
// Tell the other modules a new comment has been submitted.
module_invoke_all('comment', 'insert', $edit);
......@@ -985,9 +1002,12 @@ function comment_delete($cid) {
// Delete comment and its replies.
_comment_delete_thread($comment);
_comment_update_node_statistics($comment->nid);
// Clear the cache so an anonymous user
// can see his comment being added.
cache_clear_all();
}
// Print a confirmation.
......@@ -1326,7 +1346,7 @@ function comment_num_all($nid) {
static $cache;
if (!isset($cache[$nid])) {
$cache[$nid] = db_result(db_query('SELECT COUNT(cid) FROM {comments} WHERE nid = %d AND status = 0', $nid));
$cache[$nid] = db_result(db_query('SELECT comment_count FROM {node_comment_statistics} WHERE nid = %d', $nid));
}
return $cache[$nid];
}
......@@ -1631,9 +1651,12 @@ function _comment_delete_thread($comment) {
db_query('DELETE FROM {comments} WHERE cid = %d', $comment->cid);
watchdog('special', t('Comment: deleted %subject.', array('%subject' => "<em>$comment->subject</em>")));
module_invoke_all('comment', 'delete', $comment);
// Delete the comment's replies:
$result = db_query('SELECT cid, subject FROM {comments} WHERE pid = %d', $comment->cid);
$result = db_query('SELECT c.*, u.name AS registered_name, u.uid FROM {comments} c INNER JOIN {users} u ON u.uid = c.uid WHERE pid = %d', $comment->cid);
while ($comment = db_fetch_object($result)) {
$comment->name = $comment->registered_name ? $comment->registered_name : $comment->name;
_comment_delete_thread($comment);
}
}
......@@ -1666,4 +1689,22 @@ function _comment_per_page() {
return drupal_map_assoc(array(10, 30, 50, 70, 90));
}
/**
* Updates the comment statistics for a given node. This should be called any
* time a comment is added, deleted, or updated.
*
* The following fields are contained in the node_comment_statistics table.
* - cid: cid of the last comment to be created for the node.
* - last_comment_timestamp: the timestamp of the last comment for this node or the node create stamp if no comments exist for the node.
* - last_comment_name: the name of the anonymous poster for the last comment
* - last_comment_uid: the uid of the poster for the last comment for this node or the node authors uid if no comments exists for the node.
* - comment_count: the total number of comments on this node.
*/
function _comment_update_node_statistics($nid) {
$count = db_result(db_query('SELECT COUNT(c.cid) FROM {comments} c WHERE c.nid = %d AND c.status = 0', $nid));
$node = node_load(array('nid' => $nid));
$last_reply = db_fetch_object(db_query_range('SELECT c.cid, c.name, c.timestamp, c.uid FROM {comments} c WHERE c.nid = %d AND c.status = 0 ORDER BY c.cid DESC', $nid, 0, 1));
db_query("UPDATE {node_comment_statistics} n SET n.comment_count = %d, last_comment_timestamp = '%s', n.last_comment_name = '%s', n.last_comment_uid = %d WHERE n.nid = %d", $count, $last_reply ? $last_reply->timestamp : $node->created, $last_reply->name, $last_reply ? $last_reply->uid : $node->uid, $comment->nid);
}
?>
......@@ -226,6 +226,7 @@ function comment_link($type, $node = 0, $main = 0) {
/**
* Implementation of hook_nodeapi().
*
*/
function comment_nodeapi(&$node, $op, $arg = 0) {
switch ($op) {
......@@ -241,14 +242,20 @@ function comment_nodeapi(&$node, $op, $arg = 0) {
return form_group(t('User comments'), $output);
}
break;
case 'load':
return db_fetch_array(db_query("SELECT last_comment_timestamp, last_comment_name, last_comment_name, comment_count, cid as last_comment_cid FROM {node_comment_statistics} WHERE nid = %d", $node->nid));
case 'validate':
if (!user_access('administer nodes')) {
// Force default for normal users:
$node->comment = variable_get("comment_$node->type", 2);
}
break;
case 'insert':
db_query('INSERT INTO {node_comment_statistics} (nid, cid, last_comment_timestamp, last_comment_name, last_comment_uid, comment_count) VALUES (%d,0,%d,NULL,%d,0)', $node->nid, $node->created, $node->uid);
break;
case 'delete':
db_query("DELETE FROM {comments} WHERE nid = '$node->nid'");
db_query('DELETE FROM {comments} WHERE nid = %d', $node->nid);
db_query('DELETE FROM {node_comment_statistics} WHERE nid = %d', $node->nid);
break;
}
}
......@@ -558,6 +565,8 @@ function comment_post($edit) {
// user.
db_query("UPDATE {comments} SET subject = '%s', comment = '%s' WHERE cid = %d AND uid = '$user->uid'", $edit['subject'], $edit['comment'], $edit["cid"]);
_comment_update_node_statistics($edit['nid']);
// Allow modules to respond to the updating of a comment.
module_invoke_all('comment', 'update', $edit);
......@@ -664,8 +673,16 @@ function comment_post($edit) {
$edit["cid"] = db_next_id("{comments}_cid");
$edit['timestamp'] = time();
if ($edit['uid'] = $user->uid) {
$edit['name'] = $user->name;
}
db_query("INSERT INTO {comments} (cid, nid, pid, uid, subject, comment, hostname, timestamp, status, score, users, thread, name, mail, homepage) VALUES (%d, %d, %d, %d, '%s', '%s', '%s', %d, %d, %d, '%s', '%s', '%s', '%s', '%s')", $edit["cid"], $edit["nid"], $edit["pid"], $user->uid, $edit['subject'], $edit['comment'], $_SERVER['REMOTE_ADDR'], time(), $status, $score, $users, $thread, $edit["name"], $edit['mail'], $edit["homepage"]);
db_query("INSERT INTO {comments} (cid, nid, pid, uid, subject, comment, hostname, timestamp, status, score, users, thread, name, mail, homepage) VALUES (%d, %d, %d, %d, '%s', '%s', '%s', %d, %d, %d, '%s', '%s', '%s', '%s', '%s')", $edit["cid"], $edit["nid"], $edit["pid"], $edit['uid'], $edit['subject'], $edit['comment'], $_SERVER['REMOTE_ADDR'], $edit['timestamp'], $status, $score, $users, $thread, $edit["name"], $edit['mail'], $edit["homepage"]);
_comment_update_node_statistics($edit['nid']);
// Tell the other modules a new comment has been submitted.
module_invoke_all('comment', 'insert', $edit);
......@@ -985,9 +1002,12 @@ function comment_delete($cid) {
// Delete comment and its replies.
_comment_delete_thread($comment);
_comment_update_node_statistics($comment->nid);
// Clear the cache so an anonymous user
// can see his comment being added.
cache_clear_all();
}
// Print a confirmation.
......@@ -1326,7 +1346,7 @@ function comment_num_all($nid) {
static $cache;
if (!isset($cache[$nid])) {
$cache[$nid] = db_result(db_query('SELECT COUNT(cid) FROM {comments} WHERE nid = %d AND status = 0', $nid));
$cache[$nid] = db_result(db_query('SELECT comment_count FROM {node_comment_statistics} WHERE nid = %d', $nid));
}
return $cache[$nid];
}
......@@ -1631,9 +1651,12 @@ function _comment_delete_thread($comment) {
db_query('DELETE FROM {comments} WHERE cid = %d', $comment->cid);
watchdog('special', t('Comment: deleted %subject.', array('%subject' => "<em>$comment->subject</em>")));
module_invoke_all('comment', 'delete', $comment);
// Delete the comment's replies:
$result = db_query('SELECT cid, subject FROM {comments} WHERE pid = %d', $comment->cid);
$result = db_query('SELECT c.*, u.name AS registered_name, u.uid FROM {comments} c INNER JOIN {users} u ON u.uid = c.uid WHERE pid = %d', $comment->cid);
while ($comment = db_fetch_object($result)) {
$comment->name = $comment->registered_name ? $comment->registered_name : $comment->name;
_comment_delete_thread($comment);
}
}
......@@ -1666,4 +1689,22 @@ function _comment_per_page() {
return drupal_map_assoc(array(10, 30, 50, 70, 90));
}
/**
* Updates the comment statistics for a given node. This should be called any
* time a comment is added, deleted, or updated.
*
* The following fields are contained in the node_comment_statistics table.
* - cid: cid of the last comment to be created for the node.
* - last_comment_timestamp: the timestamp of the last comment for this node or the node create stamp if no comments exist for the node.
* - last_comment_name: the name of the anonymous poster for the last comment
* - last_comment_uid: the uid of the poster for the last comment for this node or the node authors uid if no comments exists for the node.
* - comment_count: the total number of comments on this node.
*/
function _comment_update_node_statistics($nid) {
$count = db_result(db_query('SELECT COUNT(c.cid) FROM {comments} c WHERE c.nid = %d AND c.status = 0', $nid));
$node = node_load(array('nid' => $nid));
$last_reply = db_fetch_object(db_query_range('SELECT c.cid, c.name, c.timestamp, c.uid FROM {comments} c WHERE c.nid = %d AND c.status = 0 ORDER BY c.cid DESC', $nid, 0, 1));
db_query("UPDATE {node_comment_statistics} n SET n.comment_count = %d, last_comment_timestamp = '%s', n.last_comment_name = '%s', n.last_comment_uid = %d WHERE n.nid = %d", $count, $last_reply ? $last_reply->timestamp : $node->created, $last_reply->name, $last_reply ? $last_reply->uid : $node->uid, $comment->nid);
}
?>
......@@ -122,9 +122,9 @@ function forum_block($op = 'list', $delta = 0) {
}
else {
if (user_access('access content')) {
$content = node_title_list(db_query_range("SELECT DISTINCT(n.nid), n.title, GREATEST(n.created, MAX(c.timestamp)) AS sort FROM {node} n ". node_access_join_sql() ." LEFT JOIN {comments} c ON n.nid = c.nid WHERE n.type = 'forum' AND n.status = 1 AND ". node_access_where_sql() ." GROUP BY n.nid, n.title, n.created ORDER BY sort DESC", 0, variable_get('forum_block_num', '5')), t('Active forum topics:'));
$content = node_title_list(db_query_range("SELECT n.nid, n.title, l.last_comment_timestamp FROM {node} n INNER JOIN {node_comment_statistics} l ON n.nid = l.nid ". node_access_join_sql() ." WHERE n.status = 1 AND n.type='forum' AND ". node_access_where_sql() ." ORDER BY l.last_comment_timestamp DESC", 0, variable_get('forum_block_num', '5')), t('Active forum topics:'));
$content .= node_title_list(db_query_range("SELECT DISTINCT(n.nid), n.title FROM {node} n ". node_access_join_sql() ." WHERE n.type = 'forum' AND n.status = 1 AND ". node_access_where_sql() ." ORDER BY n.nid DESC", 0, variable_get('forum_block_num', '5')), t('New forum topics:'));
$content .= node_title_list(db_query_range("SELECT n.nid, n.title FROM {node} n ". node_access_join_sql() ." WHERE n.type = 'forum' AND n.status = 1 AND ". node_access_where_sql() ." ORDER BY n.nid DESC", 0, variable_get('forum_block_num', '5')), t('New forum topics:'));
if ($content) {
$content .= '<div class="more-link">'. l(t('more'), 'forum', array('title' => t('Read the latest forum topics.'))) .'</div>';
......@@ -153,7 +153,7 @@ function forum_link($type, $node = 0, $main = 0) {
if (!$main && $type == 'node' && $node->type == 'forum') {
// get previous and next topic
$result = db_query('SELECT DISTINCT(n.nid), n.title, n.sticky, GREATEST(n.created, MAX(c.timestamp)) AS date_sort FROM {node} n '. node_access_join_sql() .' INNER JOIN {forum} f ON n.nid = f.nid LEFT JOIN {comments} c ON n.nid = c.nid WHERE n.nid = f.nid AND f.tid = %d AND n.status = 1 AND '. node_access_where_sql() .' GROUP BY n.nid, n.title, n.created ORDER BY n.sticky DESC, '. _forum_get_topic_order_sql(variable_get('forum_order', 1)), $node->tid);
$result = db_query("SELECT n.nid, n.title, n.sticky, l.comment_count, l.last_comment_timestamp FROM {node} n INNER JOIN {node_comment_statistics} l ON n.nid = l.nid " . node_access_join_sql() . " INNER JOIN {term_node} r ON n.nid = r.nid AND r.tid = %d WHERE n.status = 1 AND n.type='forum' AND " . node_access_where_sql() . ' ORDER BY n.sticky DESC, '. _forum_get_topic_order_sql(variable_get('forum_order', 1)), $node->tid);
while ($topic = db_fetch_object($result)) {
if ($stop == 1) {
......@@ -238,7 +238,6 @@ function forum_view(&$node, $teaser = FALSE, $page = FALSE) {
function forum_validate(&$node) {
// Make sure all fields are set properly:
$node->icon = $node->icon ? $node->icon : '';
$node->shadow = $node->shadow ? $node->shadow : 0;
if ($node->taxonomy) {
// Extract the node's proper topic ID.
......@@ -255,9 +254,23 @@ function forum_validate(&$node) {
}
}
}
if ($node->tid && $node->shadow) {
$terms = array_keys(taxonomy_node_get_terms($node->nid));
if (!in_array($node->tid, $terms)) {
$terms[] = $node->tid;
}
$node->taxonomy = $terms;
}
}
}
/**
* Implementation of hook_update().
*/
function forum_update($node) {
db_query('UPDATE {forum} SET tid = %d WHERE nid = %d', $node->tid, $node->nid);
}
/**
* Implementation of hook_form().
*/
......@@ -266,12 +279,16 @@ function forum_form(&$node) {
// new topic
$node->taxonomy[] = arg(3);
}
else {
$node->taxonomy = array($node->tid);
}
$output = implode('', taxonomy_node_form('forum', $node));
if ($node->nid) {
// if editing, give option to leave shadows
$output .= form_checkbox(t('Leave shadow copy'), 'shadow', 1, $node->shadow, t('If you move this topic, you can leave a link in the old forum to the new forum.'));
$shadow = (count(taxonomy_node_get_terms($node->nid)) > 1);
$output .= form_checkbox(t('Leave shadow copy'), 'shadow', 1, $shadow, t('If you move this topic, you can leave a link in the old forum to the new forum.'));
}
$output .= form_textarea(t('Body'), 'body', $node->body, 60, 10, '');
......@@ -283,14 +300,8 @@ function forum_form(&$node) {
* Implementation of hook_insert().
*/
function forum_insert($node) {
db_query('INSERT INTO {forum} (nid, shadow, tid) VALUES (%d, %d, %d)', $node->nid, $node->shadow, $node->tid);
}
/**
* Implementation of hook_update().
*/
function forum_update($node) {
db_query('UPDATE {forum} SET shadow = %d, tid = %d WHERE nid = %d', $node->shadow, $node->tid, $node->nid);
global $user;
db_query('INSERT INTO {forum} (nid, tid) VALUES (%d, %d)', $node->nid, $node->tid);
}
/**
......@@ -300,27 +311,11 @@ function forum_delete(&$node) {
db_query('DELETE FROM {forum} WHERE nid = %d', $node->nid);
}
function _forum_num_comments($nid) {
$value = db_fetch_object(db_query('SELECT COUNT(cid) AS count FROM {comments} WHERE nid = %d AND status = 0', $nid));
return ($value) ? $value->count : 0;
}
function _forum_last_comment($nid) {
$value = db_fetch_object(db_query_range('SELECT timestamp FROM {comments} WHERE nid = %d AND status = 0 ORDER BY timestamp DESC', $nid, 0, 1));
return ($value) ? format_date($value->timestamp, 'small') : '&nbsp;';
}
function _forum_last_reply($nid) {
$value = db_fetch_object(db_query_range('SELECT c.timestamp, c.name AS anonymous_name, u.name, u.uid FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.nid = %d AND c.status = 0 ORDER BY c.timestamp DESC', $nid, 0, 1));
if ($value) {
$value->name = $value->uid ? $value->name : $value->anonymous_name;
}
else {
$value = new StdClass();
}
return $value;
}
/**
* Formats a topic for display
*
* @TODO Give a better description. Not sure where this funciton is used yet.
*/
function _forum_format($topic) {
if ($topic && $topic->timestamp) {
return t('%time ago<br />by %author', array('%time' => format_interval(time() - $topic->timestamp), '%author' => format_name($topic)));
......@@ -330,77 +325,85 @@ function _forum_format($topic) {
}
}
/**
* Returns a list of all forums for a given taxonomy id
*
* Forum objects containt the following fields
* -num_topics Number of topics in the forum
* -num_posts Total number of posts in all topics
* -last_post Most recent post for the forum
*
* @param $tid
* Taxonomy ID of the vocabulary that holds the forum list.
* @return
* Array of object containing the forum information.
*/
function forum_get_forums($tid = 0) {
if (!$tid) {
$tid = 0;
}
$cache = cache_get("forum:$tid");
$forums = array();
$_forums = taxonomy_get_tree(variable_get('forum_nav_vocabulary', ''), $tid);
if (empty($cache)) {
$forums = array();
$_forums = taxonomy_get_tree(variable_get('forum_nav_vocabulary', ''), $tid);
foreach ($_forums as $forum) {
if (in_array($forum->tid, variable_get('forum_containers', array()))) {
$forum->container = 1;
}
$forum->num_topics = _forum_num_topics($forum->tid);
$forum->num_posts = _forum_num_replies($forum->tid) + $forum->num_topics;
$forum->last_post = _forum_last_post($forum->tid);
$forums[$forum->tid] = $forum;
}
if (count($_forums)) {
cache_set("forum:$tid", serialize($forums), 1);
}
else {
$forums = unserialize($cache->data);
$counts = array();
$_counts = db_query("SELECT r.tid, COUNT(n.nid) AS topic_count, SUM(l.comment_count) AS comment_count FROM {node} n INNER JOIN {node_comment_statistics} l ON n.nid = l.nid INNER JOIN {term_node} r ON n.nid = r.nid " . node_access_join_sql() . " WHERE n.status = 1 AND n.type = 'forum' AND " . node_access_where_sql() . " GROUP BY r.tid", $forum->tid);
while ($count = db_fetch_object($_counts)) {
$counts[$count->tid] = $count;
}
}
return $forums;
}
foreach ($_forums as $forum) {
if (in_array($forum->tid, variable_get('forum_containers', array()))) {
$forum->container = 1;
}
function _forum_num_topics($term) {
return db_result(db_query('SELECT COUNT(DISTINCT(n.nid)) FROM {forum} f INNER JOIN {node} n ON n.nid = f.nid '. node_access_join_sql() .' WHERE f.tid = %d AND n.status = 1 AND '. node_access_where_sql() .' AND f.shadow = 0', $term));
}
if ($counts[$forum->tid]) {
$forum->num_topics = $counts[$forum->tid]->topic_count;
$forum->num_posts = $counts[$forum->tid]->topic_count + $counts[$forum->tid]->comment_count;
}
else {
$forum->num_topics = 0;
$forum->num_posts = 0;
}
// This query does not use full ANSI syntax since MySQL 3.x does not support
// table1 INNER JOIN table2 INNER JOIN table3 ON table2_criteria ON table3_criteria
// used to join node_comment_statistics to users
$topic = db_fetch_object(db_query_range('SELECT n.nid, l.last_comment_timestamp, IF(l.last_comment_uid, cu.name, l.last_comment_name) as last_comment_name, l.last_comment_uid FROM {node} n ' . node_access_join_sql() . ", {node_comment_statistics} l /*! USE INDEX (node_comment_timestamp) */, {users} cu, {term_node} r WHERE n.nid = r.nid AND r.tid = %d AND n.status = 1 AND n.type = 'forum' AND l.last_comment_uid = cu.uid AND n.nid = l.nid AND " . node_access_where_sql() . ' ORDER BY l.last_comment_timestamp DESC', $forum->tid, 0, 1));
$last_post->timestamp = $topic->last_comment_timestamp;
$last_post->name = $topic->last_comment_name;
$last_post->uid = $topic->last_comment_uid;
$forum->last_post = $last_post;
function _forum_num_replies($term) {
return db_result(db_query('SELECT COUNT(DISTINCT(n.nid)) AS count FROM {comments} c INNER JOIN {node} n ON n.nid = c.nid '. node_access_join_sql() .' INNER JOIN {forum} f ON n.nid = f.nid WHERE f.tid = %d AND n.nid = f.nid AND n.nid = c.nid AND n.status = 1 AND '. node_access_where_sql() ." AND c.status = 0 AND n.type = 'forum'", $term));
$forums[$forum->tid] = $forum;
}
return $forums;
}
function _forum_topics_read($term, $uid) {
// Calculate the number of topics the user has read. Assume all entries older
// than NODE_NEW_LIMIT are read, and include the recent posts that user has
// read.
$ancient = db_result(db_query('SELECT COUNT(DISTINCT(n.nid)) FROM {forum} f INNER JOIN {node} n ON f.nid = n.nid '. node_access_join_sql() .' WHERE f.tid = %d AND n.status = 1 AND '. node_access_where_sql() .' AND n.created <= %d AND f.shadow = 0', $term, NODE_NEW_LIMIT));
$recent = db_result(db_query('SELECT COUNT(DISTINCT(n.nid)) FROM {forum} f INNER JOIN {node} n ON f.nid = n.nid '. node_access_join_sql() .' INNER JOIN {history} h ON n.nid = h.nid WHERE n.status = 1 AND '. node_access_where_sql() .' AND f.tid = %d AND h.uid = %d AND n.created > %d AND f.shadow = 0', $term, $uid, NODE_NEW_LIMIT));
$ancient = db_result(db_query('SELECT COUNT(n.nid) FROM {node} n INNER JOIN {term_node} r ON n.nid = r.nid AND r.tid = %d '. node_access_join_sql() .' WHERE n.created <= %d AND n.status = 1 AND '. node_access_where_sql(), $term, NODE_NEW_LIMIT));
$recent = db_result(db_query('SELECT COUNT(n.nid) FROM {node} n '. node_access_join_sql() .' INNER JOIN {history} h ON n.nid = h.nid AND h.uid = %d INNER JOIN {term_node} r ON n.nid = r.nid AND r.tid = %d WHERE n.status = 1 AND n.created > %d AND '. node_access_where_sql(), $uid, $term, NODE_NEW_LIMIT));
return $ancient + $recent;
}
function _forum_last_post($term) {
$topic = db_fetch_object(db_query_range("SELECT DISTINCT(n.nid), n.created AS timestamp, u.name AS name, u.uid AS uid FROM {forum} f INNER JOIN {node} n ON n.nid = f.nid ". node_access_join_sql() ." INNER JOIN {users} u ON n.uid = u.uid WHERE f.tid = %d AND n.nid = f.nid AND n.type = 'forum' AND n.status = 1 AND ". node_access_where_sql() ." ORDER BY timestamp DESC", $term, 0, 1));
$reply = db_fetch_object(db_query_range("SELECT DISTINCT(n.nid), c.timestamp, c.name AS anonymous_name, u.name AS name, u.uid AS uid FROM {forum} f INNER JOIN {node} n ON n.nid = f.nid ". node_access_join_sql() ." INNER JOIN {comments} c ON n.nid = c.nid INNER JOIN {users} u ON c.uid = u.uid WHERE f.tid = %d AND n.nid = f.nid AND n.type = 'forum' AND n.status = 1 AND ". node_access_where_sql() ." AND c.status = 0 ORDER BY c.timestamp DESC", $term, 0, 1));
if ($reply) {
$reply->name = $reply->uid ? $reply->name : $reply->anonymous_name;
}
else {
$reply = new StdClass();
}
$value = ($topic->timestamp > $reply->timestamp) ? $topic : $reply;
return $value;
}
function forum_get_topics($tid, $sortby, $forum_per_page) {
global $user, $forum_topic_list_header;
$forum_topic_list_header = array(
array('data' => '&nbsp;'),
array('data' => t('Topic'), 'field' => 'n.title'),
array('data' => t('Replies'), 'field' => 'num_comments'),
array('data' => t('Replies'), 'field' => 'l.comment_count'),
array('data' => t('Created'), 'field' => 'n.created'),
array('data' => t('Last reply'), 'field' => 'date_sort'),
array('data' => t('Last reply'), 'field' => 'l.last_comment_timestamp'),
);
$order = _forum_get_topic_order($sortby);
......@@ -413,25 +416,22 @@ function forum_get_topics($tid, $sortby, $forum_per_page) {
$term = taxonomy_get_term($tid);
$check_tid = $tid ? "'". check_query($tid) ."'" : 'NULL';
// show topics with the correct tid, or in the forum but with shadow = 1
// @TODO: this is not ANSI SQL! ("user error: 'n.created' isn't in GROUP BY")
// @TODO: timestamp is a sql reserved word. are there more?
$sql = "SELECT DISTINCT(n.nid), n.title, n.sticky, u.name AS name, u.uid AS uid, n.created AS timestamp, GREATEST(n.created, MAX(c.timestamp)) AS date_sort, COUNT(c.nid) AS num_comments, n.comment AS comment_mode, f.tid FROM {node} n ". node_access_join_sql() ." INNER JOIN {term_node} r ON n.nid = r.nid INNER JOIN {users} u ON n.uid = u.uid LEFT JOIN {comments} c ON n.nid = c.nid INNER JOIN {forum} f ON n.nid = f.nid WHERE ((r.tid = $check_tid AND f.shadow = 1) OR f.tid = $check_tid) AND n.status = 1 AND ". node_access_where_sql() ." AND n.type = 'forum' GROUP BY n.nid, n.title, u.name, u.uid, n.created, n.comment, f.tid";
$sql = "SELECT n.nid, f.tid, n.title, n.sticky, u.name, u.uid, n.created AS timestamp, n.comment AS comment_mode, l.last_comment_timestamp, IF(l.last_comment_uid, cu.name, l.last_comment_name) as last_comment_name, l.last_comment_uid, l.comment_count AS num_comments FROM {node} n ". node_access_join_sql() .", {node_comment_statistics} l, {users} cu, {term_node} r, {users} u, {forum} f WHERE n.status = 1 AND l.last_comment_uid = cu.uid AND n.nid = l.nid AND n.nid = r.nid AND r.tid = $check_tid AND n.uid = u.uid AND n.nid = f.nid AND ". node_access_where_sql();
$sql .= tablesort_sql($forum_topic_list_header, 'n.sticky DESC,');
$sql_count = "SELECT COUNT(DISTINCT(n.nid)) FROM {node} n ". node_access_join_sql() ." INNER JOIN {forum} f ON n.nid = f.nid INNER JOIN {term_node} r ON n.nid = r.nid WHERE ( (r.tid = $check_tid AND f.shadow = 1) OR f.tid = $check_tid) AND n.status = 1 AND ". node_access_where_sql() ." AND n.type = 'forum'";
$sql_count = "SELECT COUNT(n.nid) FROM {node} n ". node_access_join_sql() ." INNER JOIN {term_node} r ON n.nid = r.nid AND r.tid = $check_tid WHERE n.status = 1 AND n.type = 'forum' AND ". node_access_where_sql();
$result = pager_query($sql, $forum_per_page, 0, $sql_count);
while ($topic = db_fetch_object($result)) {
if ($user->uid) {
// folder is new if topic is new or there are new comments since last visit
if ($topic->shadow > 0) {
if ($topic->tid != $tid) {
$topic->new = 0;
}
else {
$history = _forum_user_last_visit($topic->nid);
$topic->new_replies = db_result(db_query('SELECT COUNT(DISTINCT(c.nid)) FROM {node} n '. node_access_join_sql() .' INNER JOIN {comments} c ON n.nid = c.nid WHERE n.nid = %d AND n.status = 1 AND '. node_access_where_sql() .' AND c.status = 0 AND c.timestamp > %d', $topic->nid, $history));
$topic->new_replies = comment_num_new($topic->nid, $history);
$topic->new = $topic->new_replies || ($topic->timestamp > $history);
}
}
......@@ -441,21 +441,25 @@ function forum_get_topics($tid, $sortby, $forum_per_page) {
$topic->new = 0;
}
$topic->last_reply = _forum_last_reply($topic->nid);
if ($topic->num_comments > 0) {
$last_reply->timestamp = $topic->last_comment_timestamp;
$last_reply->name = $topic->last_comment_name;
$last_reply->uid = $topic->last_comment_uid;
$topic->last_reply = $last_reply;
}
$topics[] = $topic;
}
return $topics;
}
/**
* Finds the first unread node for a given forum.
*/
function _forum_new($tid) {
global $user;
$result = db_query("SELECT DISTINCT(n.nid) FROM {node} n, {history} h, {forum} f ". node_access_join_sql() ." WHERE n.type = 'forum' AND n.status = 1 AND ". node_access_where_sql() ." AND h.nid = n.nid AND f.nid = h.nid AND f.tid = %d AND h.uid = %d", $tid, $user->uid);
while ($r = db_fetch_object($result)) {
$read[] = $r->nid;
}
$nid = db_result(db_query_range("SELECT DISTINCT(n.nid) FROM {node} n ". node_access_join_sql() ." INNER JOIN {forum} f ON n.nid = f.nid WHERE n.type = 'forum' AND f.nid = n.nid AND n.status = 1 AND ". node_access_where_sql() ." AND f.tid = %d AND n.created > %d ". ($read ? 'AND NOT (n.nid IN ('. implode(',', $read) .')) ' : '') .'ORDER BY created', $tid, NODE_NEW_LIMIT, 0, 1));
$nid = db_result(db_query_range("SELECT n.nid FROM {node} n LEFT JOIN {history} h ON n.nid = h.hid AND h.uid = %d INNER JOIN {term_node} r ON n.nid = r.nid AND r.tid = %d " . node_access_join_sql() . " WHERE n.status = 1 AND n.type = 'forum' AND h.nid IS NULL AND n.created > %d AND " . node_access_where_sql() . " ORDER BY created", $user->uid, $tid, NODE_NEW_LIMIT, 0, 1));
return $nid ? $nid : 0;
}
......@@ -704,16 +708,16 @@ function _forum_user_last_visit($nid) {
function _forum_get_topic_order($sortby) {
switch ($sortby) {
case 1:
return array('field' => 'date_sort', 'sort' => 'desc');
return array('field' => 'l.last_comment_timestamp', 'sort' => 'desc');
break;
case 2:
return array('field' => 'date_sort', 'sort' => 'asc');
return array('field' => 'l.last_comment_timestamp', 'sort' => 'asc');
break;
case 3:
return array('field' => 'num_comments', 'sort' => 'desc');
return array('field' => 'l.comment_count', 'sort' => 'desc');
break;
case 4:
return array('field' => 'num_comments', 'sort' => 'asc');
return array('field' => 'l.comment_count', 'sort' => 'asc');
break;
}
}
......
......@@ -122,9 +122,9 @@ function forum_block($op = 'list', $delta = 0) {
}
else {