From: moodler Date: Sat, 29 Jan 2005 09:49:42 +0000 (+0000) Subject: MAJOR NEW FEATURE: Tracking of read/unread posts X-Git-Url: http://git.mjollnir.org/gw?a=commitdiff_plain;h=f37da850547e29c577eb532a62a5bdb53c489041;p=moodle.git MAJOR NEW FEATURE: Tracking of read/unread posts Many thanks to Mike Churchward for his work on this and persevering with sending updated versions to me. :-) Shane and I have polished it up, rewritten a few parts (display-related) and here it is, finally in CVS! I think there will still need to be some optimisation for the SQL, since it's still pretty intensive. Perhaps some sort of caching in the session that gets modified along with the database whenever something gets read. However if there are problems the whole thing can be switched off in the forum module config so this is not crucially urgent. --- diff --git a/course/lib.php b/course/lib.php index 9b1a9a0160..0b56e2e373 100644 --- a/course/lib.php +++ b/course/lib.php @@ -1036,6 +1036,16 @@ function print_section($course, $section, $mods, $modnamesused, $absolute=false, ' href="'.$CFG->wwwroot.'/mod/'.$mod->modname.'/view.php?id='.$mod->id.'">'. $instancename.''; } + if ($CFG->forum_trackreadposts && $mod->modname == 'forum') { + $groupmode = groupmode($course, $mod); + $groupid = ($groupmode == SEPARATEGROUPS && !isteacheredit($course->id)) ? + get_current_group($course->id) : false; + $unread = forum_tp_count_forum_posts($mod->instance, $groupid) - + forum_tp_count_forum_read_records($USER->id, $mod->instance, $groupid); + $cssclass = ($unread > 0) ? 'unread' : 'read'; + echo ' '.get_string('unreadpostsnumber', 'forum', $unread).'.'; + } + if ($isediting) { if ($groupbuttons) { if (! $mod->groupmodelink = $groupbuttonslink) { diff --git a/lang/en/forum.php b/lang/en/forum.php index 43381eeb38..115b635ff6 100644 --- a/lang/en/forum.php +++ b/lang/en/forum.php @@ -11,13 +11,18 @@ $string['allowsdiscussions'] = 'This forum allows each person to start one discu $string['anyfile'] = 'Any file'; $string['attachment'] = 'Attachment'; $string['bynameondate'] = 'by $a->name - $a->date'; +$string['configcleanreadtime'] = 'The hour of the day to clean old posts from the \'read\' table.'; $string['configdisplaymode'] = 'The default display mode for discussions if one isn\'t set.'; $string['configenablerssfeeds'] = 'This switch will enable the possibility of RSS feeds for all forums. You will still need to turn feeds on manually in the settings for each forum.'; $string['configlongpost'] = 'Any post over this length (not including HTML) is considered long.'; $string['configmanydiscussions'] = 'Maximum number of discussions shown in a forum per page'; $string['configmaxbytes'] = 'Default maximum size for all forum attachments on the site (subject to course limits and other local settings)'; +$string['configoldpostdays'] = 'Number of days old any post is considered read.'; $string['configreplytouser'] = 'When a forum post is mailed out, should it contain the user\'s email address so that recipients can reply personally rather than via the forum? Even if set to \'Yes\' users can choose in their profile to keep their email address secret.'; $string['configshortpost'] = 'Any post under this length (not including HTML) is considered short.'; +$string ['configtrackreadposts'] = 'Set to \'yes\' if you want to track read/unread for each user.'; +$string['configusermarksread'] = 'If \'yes\', the user must manually mark a post as read. '. + 'If \'no\', when the post is viewed it is marked as read.'; $string['couldnotadd'] = 'Could not add your post due to an unknown error'; $string['couldnotdeleteratings'] = 'Sorry, that cannot be deleted as people have already rated it'; $string['couldnotdeletereplies'] = 'Sorry, that cannot be deleted as people have already responded to it'; @@ -59,6 +64,8 @@ $string['introsocial'] = 'An open forum for chatting about anything you want to' $string['introteacher'] = 'A forum for teacher-only notes and discussion'; $string['lastpost'] = 'Last post'; $string['learningforums'] = 'Learning forums'; +$string['markread'] = 'Mark read'; +$string['markunread'] = 'Mark unread'; $string['maxattachmentsize'] = 'Maximum attachment size'; $string['maxtimehaspassed'] = 'Sorry, but the maximum time for editing this post ($a) has passed!'; $string['message'] = 'Message'; @@ -147,6 +154,9 @@ $string['subscribestart'] = 'Send me email copies of posts to this forum'; $string['subscribestop'] = 'I don\'t want email copies of posts to this forum'; $string['subscription'] = 'Subscription'; $string['subscriptions'] = 'Subscriptions'; +$string['unread'] = 'Unread'; +$string['unreadposts'] = 'Unread posts'; +$string['unreadpostsnumber'] = '$a posts unread'; $string['unsubscribe'] = 'Unsubscribe from this forum'; $string['unsubscribed'] = 'Unsubscribed'; $string['unsubscribeshort'] = 'Unsubscribe'; diff --git a/mod/forum/config.html b/mod/forum/config.html index 58c422d92f..f43c17a904 100644 --- a/mod/forum/config.html +++ b/mod/forum/config.html @@ -63,6 +63,81 @@ + + +

forum_trackreadposts: + + forum_trackreadposts, "", "", ""); + ?> + + + + + + +

forum_oldpostdays: + + + + + + + + +

forum_usermarksread: + + forum_usermarksread, "", "", ""); + ?> + + + + + + +

forum_cleanreadtime: + + forum_cleanreadtime, "", "", ""); + ?> + + + + + +

forum_enablerssfeeds: diff --git a/mod/forum/db/mysql.php b/mod/forum/db/mysql.php index f22da52116..e538c753f1 100644 --- a/mod/forum/db/mysql.php +++ b/mod/forum/db/mysql.php @@ -159,6 +159,22 @@ function forum_upgrade($oldversion) { modify_database('','ALTER TABLE prefix_forum_subscriptions ADD INDEX forum (forum);'); } + if ($oldversion < 2005011500) { + modify_database('','CREATE TABLE prefix_forum_read ( + `id` int(10) unsigned NOT NULL auto_increment, + `userid` int(10) NOT NULL default \'0\', + `forumid` int(10) NOT NULL default \'0\', + `discussionid` int(10) NOT NULL default \'0\', + `postid` int(10) NOT NULL default \'0\', + `firstread` int(10) NOT NULL default \'0\', + `lastread` int(10) NOT NULL default \'0\', + PRIMARY KEY (`id`), + KEY `prefix_forum_user_forum_idx` (`userid`,`forumid`), + KEY `prefix_forum_user_discussion_idx` (`userid`,`discussionid`), + KEY `prefix_forum_user_post_idx` (`userid`,`postid`) + ) COMMENT=\'Tracks each users read posts\';'); + } + return true; } diff --git a/mod/forum/db/mysql.sql b/mod/forum/db/mysql.sql index e0811f695b..e6795becbd 100644 --- a/mod/forum/db/mysql.sql +++ b/mod/forum/db/mysql.sql @@ -116,6 +116,24 @@ CREATE TABLE prefix_forum_subscriptions ( ) COMMENT='Keeps track of who is subscribed to what forum'; # -------------------------------------------------------- +# +# Table structure for table `forum_read` +# + +CREATE TABLE prefix_forum_read ( + `id` int(10) unsigned NOT NULL auto_increment, + `userid` int(10) NOT NULL default '0', + `forumid` int(10) NOT NULL default '0', + `discussionid` int(10) NOT NULL default '0', + `postid` int(10) NOT NULL default '0', + `firstread` int(10) NOT NULL default '0', + `lastread` int(10) NOT NULL default '0', + PRIMARY KEY (`id`), + KEY `prefix_forum_user_forum_idx` (`userid`,`forumid`), + KEY `prefix_forum_user_discussion_idx` (`userid`,`discussionid`), + KEY `prefix_forum_user_post_idx` (`userid`,`postid`) +) COMMENT='Tracks each users read posts'; + # # Dumping data for table `log_display` # diff --git a/mod/forum/db/postgres7.php b/mod/forum/db/postgres7.php index 94674dfa1e..ef5d6f23c0 100644 --- a/mod/forum/db/postgres7.php +++ b/mod/forum/db/postgres7.php @@ -101,6 +101,22 @@ function forum_upgrade($oldversion) { modify_database('','CREATE INDEX prefix_forum_subscriptions_forum_idx ON prefix_forum_subscriptions (forum);'); } + if ($oldversion < 2005011500) { + modify_database('','CREATE TABLE prefix_forum_read ( + id SERIAL PRIMARY KEY, + userid integer default 0 NOT NULL, + forumid integer default 0 NOT NULL, + discussionid integer default 0 NOT NULL, + postid integer default 0 NOT NULL, + firstread integer default 0 NOT NULL, + lastread integer default 0 NOT NULL + );'); + + modify_database('','CREATE INDEX prefix_forum_user_forum_idx ON prefix_forum_read (userid, forumid);'); + modify_database('','CREATE INDEX prefix_forum_user_discussion_idx ON prefix_forum_read (userid, discussionid);'); + modify_database('','CREATE INDEX prefix_forum_user_post_idx ON prefix_forum_read (userid, postid);'); + } + return true; } diff --git a/mod/forum/db/postgres7.sql b/mod/forum/db/postgres7.sql index 50c067c7b8..902f15ef42 100644 --- a/mod/forum/db/postgres7.sql +++ b/mod/forum/db/postgres7.sql @@ -119,6 +119,28 @@ CREATE TABLE prefix_forum_subscriptions ( CREATE INDEX prefix_forum_subscriptions_userid_idx ON prefix_forum_subscriptions (userid); CREATE INDEX prefix_forum_subscriptions_forum_idx ON prefix_forum_subscriptions (forum); +# -------------------------------------------------------- + + +# +# Table structure for table `forum_read` +# + +CREATE TABLE prefix_forum_read ( + id SERIAL PRIMARY KEY, + userid integer NOT NULL default '0', + forumid integer NOT NULL default '0', + discussionid integer NOT NULL default '0', + postid integer NOT NULL default '0', + firstread integer NOT NULL default '0', + lastread integer NOT NULL default '0' +); + +CREATE INDEX prefix_forum_user_forum_idx ON prefix_forum_read (userid, forumid); +CREATE INDEX prefix_forum_user_discussion_idx ON prefix_forum_read (userid, discussionid); +CREATE INDEX prefix_forum_user_post_idx ON prefix_forum_read (userid, postid); + + # -------------------------------------------------------- # diff --git a/mod/forum/discuss.php b/mod/forum/discuss.php index 566f401aa8..825059bd16 100644 --- a/mod/forum/discuss.php +++ b/mod/forum/discuss.php @@ -10,6 +10,8 @@ optional_variable($parent); // If set, then display this post and all children. optional_variable($mode); // If set, changes the layout of the thread optional_variable($move); // If set, moves this discussion to another forum + optional_variable($mark); // Used for tracking read posts if user initiated. + optional_variable($postid); // Used for tracking read posts if user initiated. if (! $discussion = get_record("forum_discussions", "id", $d)) { error("Discussion ID was incorrect or no longer exists"); @@ -93,6 +95,14 @@ error("Discussion no longer exists", "$CFG->wwwroot/mod/forum/view.php?f=$forum->id"); } + if ($CFG->forum_trackreadposts && $CFG->forum_usermarksread) { + if ($mark == 'read') { + forum_tp_add_read_record($USER->id, $postid, $discussion->id, $forum->id); + } else if ($mark == 'unread') { + forum_tp_delete_read_records($USER->id, $postid); + } + } + if (empty($navtail)) { $navtail = "id\">$discussion->name -> $post->subject"; } @@ -105,15 +115,13 @@ print_header("$course->shortname: $discussion->name", "$course->fullname", "id\">$course->shortname -> $navmiddle -> $navtail", "", "", true, $searchform, navmenu($course, $cm)); - - echo '

'; // forum-discuss wrapper start } else { print_header("$course->shortname: $discussion->name", "$course->fullname", "$navmiddle -> $navtail", "", "", true, $searchform, navmenu($course, $cm)); - - echo '
'; // forum-discuss wrapper start } + echo '
'; // forum-discuss wrapper start + /// Check to see if groups are being used in this forum /// If so, make sure the current person is allowed to see this discussion diff --git a/mod/forum/index.php b/mod/forum/index.php index 2fbd904cf0..c8dcc6b75f 100644 --- a/mod/forum/index.php +++ b/mod/forum/index.php @@ -35,6 +35,7 @@ $strdescription = get_string("description"); $strdiscussions = get_string("discussions", "forum"); $strsubscribed = get_string("subscribed", "forum"); + $strunreadposts = get_string("unreadposts", "forum"); $strrss = get_string("rss"); $searchform = forum_print_search_form($course, "", true, "plain"); @@ -43,14 +44,19 @@ // Start of the table for General Forums $generaltable->head = array ($strforum, $strdescription, $strdiscussions); - $generaltable->align = array ("left", "left", "center"); + $generaltable->align = array ("left", "left", "right"); + + if ($CFG->forum_trackreadposts) { + $generaltable->head[] = $strunreadposts; + $generaltable->align[] = "right"; + } if ($can_subscribe = (isstudent($course->id) or isteacher($course->id) or isadmin())) { $generaltable->head[] = $strsubscribed; $generaltable->align[] = "center"; } - if ($show_rss = (($can_subscribe || $course->id == SITEID) && + if ($show_rss = (($can_subscribe || $course->id == SITEID) && isset($CFG->enablerssfeeds) && isset($CFG->forum_enablerssfeeds) && $CFG->enablerssfeeds && $CFG->forum_enablerssfeeds)) { $generaltable->head[] = $strrss; @@ -126,6 +132,17 @@ $count = count_records("forum_discussions", "forum", "$forum->id"); } + if ($CFG->forum_trackreadposts) { + $groupid = ($groupmode==SEPARATEGROUPS && !isteacheredit($course->id)) ? $currentgroup : false; + $unread = forum_tp_count_forum_posts($forum->id, $groupid) - + forum_tp_count_forum_read_records($USER->id, $forum->id, $groupid); + if ($unread > 0) { + $unreadlink = ''.$unread.''; + } else { + $unreadlink = ''.$unread.''; + } + } + $forum->intro = forum_shorten_post($forum->intro); replace_smilies($forum->intro); $forum->intro = "$forum->intro";; @@ -174,20 +191,24 @@ $sublink = "id\">$subscribed"; } } - //Depending of rsslink + $row = array ($forumlink, $forum->intro, "$count"); + if ($CFG->forum_trackreadposts) { + $row[] = $unreadlink; + } + $row[] = $sublink; if (!empty($rsslink)) { - //Save data - $generaltable->data[] = array ($forumlink, "$forum->intro", "$count", $sublink, $rsslink); - } else { - $generaltable->data[] = array ($forumlink, "$forum->intro", "$count", $sublink); + $row[] = $rsslink; } + $generaltable->data[] = $row; } else { - //Depending of rsslink + $row = array ($forumlink, $forum->intro, "$count"); + if ($CFG->forum_trackreadposts) { + $row[] = $unreadlink; + } if (!empty($rsslink)) { - $generaltable->data[] = array ($forumlink, "$forum->intro", "$count", $rsslink); - } else { - $generaltable->data[] = array ($forumlink, "$forum->intro", "$count"); + $row[] = $rsslink; } + $generaltable->data[] = $row; } } } @@ -197,12 +218,17 @@ $learningtable->head = array ($strforum, $strdescription, $strdiscussions); $learningtable->align = array ("left", "left", "center"); + if ($CFG->forum_trackreadposts) { + $learningtable->head[] = $strunreadposts; + $learningtable->align[] = 'right'; + } + if ($can_subscribe = (isstudent($course->id) or isteacher($course->id) or isadmin())) { $learningtable->head[] = $strsubscribed; $learningtable->align[] = "center"; } - if ($show_rss = (($can_subscribe || $course->id == SITEID) && + if ($show_rss = (($can_subscribe || $course->id == SITEID) && isset($CFG->enablerssfeeds) && isset($CFG->forum_enablerssfeeds) && $CFG->enablerssfeeds && $CFG->forum_enablerssfeeds)) { $learningtable->head[] = $strrss; @@ -229,6 +255,17 @@ $count = count_records("forum_discussions", "forum", "$forum->id"); } + if ($CFG->forum_trackreadposts) { + $groupid = ($groupmode==SEPARATEGROUPS && !isteacheredit($course->id)) ? $currentgroup : false; + $unread = forum_tp_count_forum_posts($forum->id, $groupid) - + forum_tp_count_forum_read_records($USER->id, $forum->id, $groupid); + if ($unread > 0) { + $unreadlink = ''.$unread.''; + } else { + $unreadlink = ''.$unread.''; + } + } + $forum->intro = forum_shorten_post($forum->intro); replace_smilies($forum->intro); $forum->intro = "$forum->intro"; @@ -268,7 +305,7 @@ $rsslink = rss_get_link($course->id, $userid, "forum", $forum->id, $tooltiptext); } } - + if ($can_subscribe) { if (forum_is_forcesubscribed($forum->id)) { $sublink = get_string("yes"); @@ -289,20 +326,26 @@ $sublink = "id\">$subscribed"; } } - //Depending of rsslink + + $row = array ($printsection, $forumlink, $forum->intro, $count); + if ($CFG->forum_trackreadposts) { + $row[] = $unreadlink; + } + $row[] = $sublink; if (!empty($rsslink)) { - //Save data - $learningtable->data[] = array ($printsection,$forumlink, "$forum->intro", "$count", $sublink, $rsslink); - } else { - $learningtable->data[] = array ($printsection,$forumlink, "$forum->intro", "$count", $sublink); + $row[] = $rsslink; } + $learningtable->data[] = $row; + } else { - //Depending of rsslink + $row = array ($printsection, $forumlink, $forum->intro, $count); + if ($CFG->forum_trackreadposts) { + $row[] = $unreadlink; + } if (!empty($rsslink)) { - $learningtable->data[] = array ($printsection, $forumlink, "$forum->intro", "$count", $rsslink); - } else { - $learningtable->data[] = array ($printsection, $forumlink, "$forum->intro", "$count"); + $row[] = $rsslink; } + $learningtable->data[] = $row; } } } @@ -315,13 +358,9 @@ print_header("$course->shortname: $strforums", "$course->fullname", "id\">$course->shortname -> $strforums", "", "", true, $searchform, navmenu($course)); - - echo '
'; // forum-index wrapper start } else { print_header("$course->shortname: $strforums", "$course->fullname", "$strforums", "", "", true, $searchform, navmenu($course)); - - echo '
'; // forum-index wrapper start } if ($generalforums) { @@ -334,8 +373,6 @@ print_table($learningtable); } - echo '
'; // forum-index wrapper end - print_footer($course); ?> diff --git a/mod/forum/lib.php b/mod/forum/lib.php index fa32a006b9..8b6ac34a9f 100644 --- a/mod/forum/lib.php +++ b/mod/forum/lib.php @@ -46,6 +46,22 @@ if (!isset($CFG->forum_maxbytes)) { set_config("forum_maxbytes", 512000); // Default maximum size for all forums } +if (!isset($CFG->forum_trackreadposts)) { + set_config("forum_trackreadposts", true); // Default whether user needs to mark a post as read +} + +if (!isset($CFG->forum_oldpostdays)) { + set_config("forum_oldpostdays", 60); // Default number of days that a post is considered old +} + +if (!isset($CFG->forum_usermarksread)) { + set_config("forum_usermarksread", false); // Default whether user needs to mark a post as read +} + +if (!isset($CFG->forum_cleanreadtime)) { + set_config("forum_cleanreadtime", 2); // Default time (hour) to execute 'clean_read_records' cron +} + if (!isset($CFG->forum_replytouser)) { set_config("forum_replytouser", true); // Default maximum size for all forums } @@ -182,6 +198,8 @@ function forum_delete_instance($id) { $result = false; } + forum_tp_delete_read_records(-1, -1, -1, $forum->id); + if (! delete_records("forum", "id", "$forum->id")) { $result = false; } @@ -213,6 +231,7 @@ function forum_cron () { $timenow = time(); $endtime = $timenow - $CFG->maxeditingtime; $starttime = $endtime - 48 * 3600; /// Two days earlier + $starttime = $endtime - 365 * 24 * 3600; /// Two days earlier if ($posts = forum_get_unmailed_posts($starttime, $endtime)) { @@ -330,6 +349,14 @@ function forum_cron () { substr($post->subject,0,30), $cm->id, $userto->id); } else { $mailcount++; + + /// Mark post as read if forum_usermarksread is set off + if ($CFG->forum_trackreadposts && !$CFG->forum_usermarksread) { + if (!forum_tp_mark_post_read($userto->id, $post, $forum->id)) { + mtrace("Error: mod/forum/cron.php: Could not mark post $post->id read for user $userto->id". + " while sending email."); + } + } } } @@ -463,6 +490,10 @@ function forum_cron () { $postsarray = $discussionposts[$discussionid]; sort($postsarray); + /// Create an empty array to use for marking read posts. + /// (I'm sure there's already a structure I can use here, but I can't be sure.) + $markread = array(); + foreach ($postsarray as $postid) { if (! $post = get_record("forum_posts", "id", "$postid")) { mtrace("Error: Could not find post $postid"); @@ -490,6 +521,12 @@ function forum_cron () { // The full treatment $posttext .= forum_make_mail_text($course, $forum, $discussion, $post, $userfrom, $userto, true); $posthtml .= forum_make_mail_post($post, $userfrom, $userto, $course, false, $canreply, false, false); + + /// Create an array of postid's for this user to mark as read. + if ($CFG->forum_trackreadposts && !$CFG->forum_usermarksread) { + $markread[$post->id]->post = $post; + $markread[$post->id]->forumid = $forum->id; + } } } if ($canunsubscribe) { @@ -511,6 +548,16 @@ function forum_cron () { } else { mtrace("success."); $usermailcount++; + + /// Mark post as read if forum_usermarksread is set off + if ($CFG->forum_trackreadposts && !$CFG->forum_usermarksread) { + foreach ($markread as $postinfo) { + if (!forum_tp_mark_post_read($userto->id, $postinfo->post, $postinfo->forumid)) { + mtrace("Error: mod/forum/cron.php: Could not mark post $postid read for user $userto->id". + " while sending digest email."); + } + } + } } } @@ -526,6 +573,21 @@ function forum_cron () { $USER = $realuser; } + if (($lastreadclean = get_record("config", "name", "forum_lastreadclean"))) { + $cleantime = mktime($CFG->forum_cleanreadtime, 0); + $timenow = time(); + if (($timenow > $cleantime) && ($lastreadclean->value+(24*3600) < $timenow)) { + $lastreadclean->value = $timenow; + update_record("config", $lastreadclean); + forum_tp_clean_read_records(); + } + } else { + $lastreadclean->name = 'forum_lastreadclean'; + $lastreadclean->value = time(); + insert_record("config", $lastreadclean); + } + + return true; } @@ -634,6 +696,10 @@ function forum_user_complete($course, $user, $mod, $forum) { if ($posts = forum_get_user_posts($forum->id, $user->id)) { foreach ($posts as $post) { + + /// Add the forum id to the post object - used by read tracking. + $post->forum = $forum->id; + forum_print_post($post, $course->id, $ownpost=false, $reply=false, $link=false, $rate=false); } @@ -1099,12 +1165,15 @@ function forum_count_discussion_replies($forum="0") { if ($forum) { $forumselect = " AND d.forum = '$forum'"; + } else { + $forumselect = ''; } return get_records_sql("SELECT p.discussion, (count(*)) as replies, max(p.id) as lastpostid FROM {$CFG->prefix}forum_posts p, {$CFG->prefix}forum_discussions d WHERE p.parent > 0 AND p.discussion = d.id + {$forumselect} GROUP BY p.discussion"); } @@ -1175,7 +1244,7 @@ function forum_get_discussions($forum="0", $forumsort="d.timemodified DESC", AND p.discussion = d.id AND p.parent = 0 AND p.userid = u.id $groupselect $userselect - AND d.usermodified = um.id + AND (d.usermodified = um.id OR d.usermodified = 0) ORDER BY $forumsort"); } @@ -1413,12 +1482,14 @@ function forum_make_mail_post(&$post, $user, $touser, $course, function forum_print_post(&$post, $courseid, $ownpost=false, $reply=false, $link=false, - $ratings=NULL, $footer="", $highlight="") { + $ratings=NULL, $footer="", $highlight="", $post_read=-99) { global $USER, $CFG; static $stredit, $strdelete, $strreply, $strparent, $strprune, $strpruneheading, $threadedmode, $isteacher, $adminedit; + static $strmarkread, $strmarkunread; + if (empty($stredit)) { $stredit = get_string('edit', 'forum'); $strdelete = get_string('delete', 'forum'); @@ -1429,13 +1500,29 @@ function forum_print_post(&$post, $courseid, $ownpost=false, $reply=false, $link $threadedmode = (!empty($USER->mode) and ($USER->mode == FORUM_MODE_THREADED)); $isteacher = isteacher($courseid); $adminedit = (isadmin() and !empty($CFG->admineditalways)); + $strmarkread = get_string('markread', 'forum'); + $strmarkunread = get_string('markunread', 'forum'); + } + + if ($CFG->forum_trackreadposts) { + if ($post_read == -99) { // If we don't know yet... + $post_read = forum_tp_is_post_read($USER->id, $post); + } + if ($post_read) { + $read_style = ' read'; + } else { + $read_style = ' unread'; + echo ''; + } + } else { + $read_style = ''; } - echo "id\">"; + echo ''; if ($post->parent) { - echo ''; + echo '
'; } else { - echo '
'; + echo '
'; } echo ""; + if ($CFG->forum_trackreadposts) { + echo ""; + } else { + echo ""; + } + echo "\n
"; @@ -1451,16 +1538,20 @@ function forum_print_post(&$post, $courseid, $ownpost=false, $reply=false, $link if (!empty($CFG->filterall)) { /// Put the subject through the filters $post->subject = filter_text("$post->subject", $courseid); } - echo "

"; echo "$post->subject
"; echo ""; - + $fullname = fullname($post, $isteacher); $by->name = "wwwroot/user/view.php?id=$post->userid&course=$courseid\">$fullname"; $by->date = userdate($post->modified); print_string("bynameondate", "forum", $by); - echo "

"; if ($group = user_group($courseid, $post->userid)) { print_group_picture($group, $courseid, false, false, true); @@ -1498,6 +1589,23 @@ function forum_print_post(&$post, $courseid, $ownpost=false, $reply=false, $link $commands = array(); + if ($CFG->forum_trackreadposts) { + if ($CFG->forum_usermarksread) { + if ($post_read) { + $mcmd = '&mark=unread&postid='.$post->id; + $mtxt = $strmarkunread; + } else { + $mcmd = '&mark=read&postid='.$post->id; + $mtxt = $strmarkread; + } + if ($threadedmode) { + $commands[] = "wwwroot/mod/forum/discuss.php?d=$post->discussion&parent=$post->id$mcmd\">$mtxt"; + } else { + $commands[] = "wwwroot/mod/forum/discuss.php?d=$post->discussion$mcmd#$post->id\">$mtxt"; + } + } + } + if ($post->parent) { if ($threadedmode) { $commands[] = "wwwroot/mod/forum/discuss.php?d=$post->discussion&parent=$post->parent\">$strparent"; @@ -1574,6 +1682,12 @@ function forum_print_post(&$post, $courseid, $ownpost=false, $reply=false, $link echo ""; echo "
\n\n"; +/// *** Need to get the forum ID from somewhere. Its not automatically part of post. +/// *** Backtrack through code to find where 'post' gets set. + if ($CFG->forum_trackreadposts && !$CFG->forum_usermarksread && !empty($post->forum)) { + forum_tp_mark_post_read($USER->id, $post, $post->forum); + } + return $ratingsmenuused; } @@ -1611,8 +1725,20 @@ function forum_print_discussion_header(&$post, $forum, $datestring="") { if ($forum->open or $forum->type == "teacher") { // Show the column with replies echo ""; - echo "wwwroot/mod/forum/discuss.php?d=$post->discussion\">$post->replies"; + echo "wwwroot/mod/forum/discuss.php?d=$post->discussion\">"; + echo $post->replies.""; echo "\n"; + + if ($CFG->forum_trackreadposts) { + echo ''; + echo "wwwroot/mod/forum/discuss.php?d=$post->discussion#unread\">"; + if ($post->unread > 0) echo ''; + echo $post->unread; + if ($post->unread > 0) echo ''; + echo ''; + echo "\n"; + } + } echo ""; @@ -2014,7 +2140,7 @@ function forum_add_attachment($post, $inputname,&$message) { function forum_add_new_post($post,&$message) { - global $USER; + global $USER, $CFG; $post->created = $post->modified = time(); $post->mailed = "0"; @@ -2033,12 +2159,16 @@ function forum_add_new_post($post,&$message) { set_field("forum_discussions", "timemodified", $post->modified, "id", $post->discussion); set_field("forum_discussions", "usermodified", $post->userid, "id", $post->discussion); + if ($CFG->forum_trackreadposts) { + forum_tp_mark_post_read($post->userid, $post, $post->forum); + } + return $post->id; } function forum_update_post($post,&$message) { - global $USER; + global $USER, $CFG; $post->modified = time(); $post->userid = $USER->id; @@ -2057,6 +2187,10 @@ function forum_update_post($post,&$message) { set_field("forum_discussions", "timemodified", $post->modified, "id", $post->discussion); set_field("forum_discussions", "usermodified", $post->userid, "id", $post->discussion); + if ($CFG->forum_trackreadposts) { + forum_tp_mark_post_read($post->userid, $post, $post->forum); + } + return update_record("forum_posts", $post); } @@ -2064,7 +2198,7 @@ function forum_add_discussion($discussion,&$message) { // Given an object containing all the necessary data, // create a new discussion and return the id - GLOBAL $USER; + GLOBAL $USER, $CFG; $timenow = time(); @@ -2112,6 +2246,10 @@ function forum_add_discussion($discussion,&$message) { return 0; } + if ($CFG->forum_trackreadposts) { + forum_tp_mark_post_read($post->userid, $post, $post->forum); + } + return $discussion->id; } @@ -2134,6 +2272,8 @@ function forum_delete_discussion($discussion) { } } + forum_tp_delete_read_records(-1, -1, $discussion->id); + if (! delete_records("forum_discussions", "id", "$discussion->id")) { $result = false; } @@ -2145,6 +2285,9 @@ function forum_delete_discussion($discussion) { function forum_delete_post($post) { if (delete_records("forum_posts", "id", $post->id)) { delete_records("forum_ratings", "post", $post->id); // Just in case + + forum_tp_delete_read_records(-1, $post->id); + if ($post->attachment) { $discussion = get_record("forum_discussions", "id", $post->discussion); $post->course = $discussion->course; @@ -2463,6 +2606,9 @@ function forum_print_latest_discussions($forum_id=0, $forum_numdiscussions=5, if ($forum->open or $forum->type == "teacher") { echo "".get_string("replies", "forum").""; } + if ($CFG->forum_trackreadposts) { + echo ''.get_string('unread', 'forum').''; + } echo "".get_string("lastpost", "forum").""; echo ""; } @@ -2482,12 +2628,19 @@ function forum_print_latest_discussions($forum_id=0, $forum_numdiscussions=5, $olddiscussionlink = true; break; } + if (!empty($replies[$discussion->discussion])) { $discussion->replies = $replies[$discussion->discussion]->replies; $discussion->lastpostid = $replies[$discussion->discussion]->lastpostid; } else { $discussion->replies = 0; } + if ($CFG->forum_trackreadposts) { + /// Add in the unread posts. Add one to the replies to include the original post. + $discussion->unread = $discussion->replies+1 - + forum_tp_count_discussion_read_records($USER->id, $discussion->discussion); + } + if (!empty($USER->id)) { $ownpost = ($discussion->userid == $USER->id); } else { @@ -2520,6 +2673,10 @@ function forum_print_latest_discussions($forum_id=0, $forum_numdiscussions=5, } else { $link = false; } + + /// Need to add in the forum id for forum_print_post. + $discussion->forum = $forum_id; + forum_print_post($discussion, $forum->course, $ownpost, $reply=0, $link, $assessed=false); echo "
\n"; break; @@ -2553,7 +2710,7 @@ function forum_print_latest_discussions($forum_id=0, $forum_numdiscussions=5, function forum_print_discussion($course, $forum, $discussion, $post, $mode, $canreply=NULL) { - global $USER; + global $USER, $CFG; if (!empty($USER->id)) { $ownpost = ($USER->id == $post->userid); @@ -2580,7 +2737,17 @@ function forum_print_discussion($course, $forum, $discussion, $post, $mode, $can } } - if (forum_print_post($post, $course->id, $ownpost, $reply, $link=false, $ratings)) { +/// Add the forum id to the post object - used by read tracking. + $post->forum = $forum->id; + + if ($CFG->forum_trackreadposts) { + $user_read_array = forum_tp_get_discussion_read_records($USER->id, $post->discussion); + } else { + $user_read_array = array(); + } + + if (forum_print_post($post, $course->id, $ownpost, $reply, $link=false, $ratings, + '', '', (isset($user_read_array[$post->id]) || forum_tp_is_post_old($post)))) { $ratingsmenuused = true; } @@ -2588,19 +2755,22 @@ function forum_print_discussion($course, $forum, $discussion, $post, $mode, $can case FORUM_MODE_FLATOLDEST : case FORUM_MODE_FLATNEWEST : default: - if (forum_print_posts_flat($post->discussion, $course->id, $mode, $ratings, $reply)) { + if (forum_print_posts_flat($post->discussion, $course->id, $mode, $ratings, $reply, + $user_read_array, $post->forum)) { $ratingsmenuused = true; } break; case FORUM_MODE_THREADED : - if (forum_print_posts_threaded($post->id, $course->id, 0, $ratings, $reply)) { + if (forum_print_posts_threaded($post->id, $course->id, 0, $ratings, $reply, + $user_read_array, $post->forum)) { $ratingsmenuused = true; } break; case FORUM_MODE_NESTED : - if (forum_print_posts_nested($post->id, $course->id, $ratings, $reply)) { + if (forum_print_posts_nested($post->id, $course->id, $ratings, $reply, + $user_read_array, $post->forum)) { $ratingsmenuused = true; } break; @@ -2618,8 +2788,10 @@ function forum_print_discussion($course, $forum, $discussion, $post, $mode, $can } } -function forum_print_posts_flat($discussion, $course, $direction, $ratings, $reply) { - global $USER; +/// Add the forum id to the argument list, for use in 'forum_print_post'. +/// Add the user_read_array to the argument list. +function forum_print_posts_flat($discussion, $course, $direction, $ratings, $reply, &$user_read_array, $forumid=0) { + global $USER, $CFG; $link = false; $ratingsmenuused = false; @@ -2632,8 +2804,17 @@ function forum_print_posts_flat($discussion, $course, $direction, $ratings, $rep if ($posts = forum_get_discussion_posts($discussion, $sort)) { foreach ($posts as $post) { + + /// Add the forum id to the post object - used by read tracking. + if (!$CFG->forum_usermarksread) { + $post->forum = $forumid; + } else { + $post->forum = 0; + } + $ownpost = ($USER->id == $post->userid); - if (forum_print_post($post, $course, $ownpost, $reply, $link, $ratings)) { + if (forum_print_post($post, $course, $ownpost, $reply, $link, $ratings, + '', '', (isset($user_read_array[$post->id]) || forum_tp_is_post_old($post)))) { $ratingsmenuused = true; } } @@ -2642,8 +2823,10 @@ function forum_print_posts_flat($discussion, $course, $direction, $ratings, $rep return $ratingsmenuused; } -function forum_print_posts_threaded($parent, $course, $depth, $ratings, $reply) { - global $USER; +/// Add the forum id to the argument list, for use in 'forum_print_post'. +/// Add the user_read_array to the argument list. +function forum_print_posts_threaded($parent, $course, $depth, $ratings, $reply, &$user_read_array, $forumid=0) { + global $USER, $CFG; $link = false; $ratingsmenuused = false; @@ -2654,19 +2837,39 @@ function forum_print_posts_threaded($parent, $course, $depth, $ratings, $reply) echo '
'; if ($depth > 0) { $ownpost = ($USER->id == $post->userid); - if (forum_print_post($post, $course, $ownpost, $reply, $link, $ratings)) { + + if (!$CFG->forum_usermarksread) { + $post->forum = $forumid; + } else { + $post->forum = 0; + } + + if (forum_print_post($post, $course, $ownpost, $reply, $link, $ratings, + '', '', (isset($user_read_array[$post->id]) || forum_tp_is_post_old($post)))) { $ratingsmenuused = true; } echo "
"; } else { - $by->name = fullname($post, isteacher($course->id)); + $by->name = fullname($post, isteacher($course)); $by->date = userdate($post->modified); - echo "

id\">discussion&parent=$post->id\">$post->subject "; + + if ($CFG->forum_trackreadposts) { + if (isset($user_read_array[$post->id]) || forum_tp_is_post_old($post)) { + $style = ''; + } else { + $style = ''; + } + } else { + $style = ''; + } + echo $style."id\">". + "discussion&parent=$post->id\">$post->subject "; print_string("bynameondate", "forum", $by); - echo "

"; + echo ""; } - if (forum_print_posts_threaded($post->id, $course, $depth-1, $ratings, $reply)) { + if (forum_print_posts_threaded($post->id, $course, $depth-1, $ratings, $reply, + $user_read_array, $forumid)) { $ratingsmenuused = true; } echo "
\n"; @@ -2675,8 +2878,10 @@ function forum_print_posts_threaded($parent, $course, $depth, $ratings, $reply) return $ratingsmenuused; } -function forum_print_posts_nested($parent, $course, $ratings, $reply) { - global $USER; +/// Add the forum id to the argument list, for use in 'forum_print_post'. +/// Add the user_read_array to the argument list. +function forum_print_posts_nested($parent, $course, $ratings, $reply, &$user_read_array, $forumid=0) { + global $USER, $CFG; $link = false; $ratingsmenuused = false; @@ -2684,18 +2889,26 @@ function forum_print_posts_nested($parent, $course, $ratings, $reply) { if ($posts = forum_get_child_posts($parent)) { foreach ($posts as $post) { + echo '
'; if (empty($USER->id)) { $ownpost = false; } else { $ownpost = ($USER->id == $post->userid); } - echo '
'; - if (forum_print_post($post, $course, $ownpost, $reply, $link, $ratings)) { + /// Add the forum id to the post object - used by read tracking. + if (!$CFG->forum_usermarksread) { + $post->forum = $forumid; + } else { + $post->forum = 0; + } + + if (forum_print_post($post, $course, $ownpost, $reply, $link, $ratings, + '', '', (isset($user_read_array[$post->id]) || forum_tp_is_post_old($post)))) { $ratingsmenuused = true; } echo "
"; - if (forum_print_posts_nested($post->id, $course, $ratings, $reply)) { + if (forum_print_posts_nested($post->id, $course, $ratings, $reply, $user_read_array, $forumid)) { $ratingsmenuused = true; } echo "
\n"; @@ -2857,4 +3070,189 @@ function forum_add_user($userid, $courseid) { } } +function forum_tp_add_read_record($userid, $postid, $discussionid=-1, $forumid=-1) { + if (($readrecord = forum_tp_get_read_records($userid, $postid)) === false) { + /// New read record + unset($readrecord); + $readrecord->userid = $userid; + $readrecord->postid = $postid; + $readrecord->discussionid = $discussionid; + $readrecord->forumid = $forumid; + $readrecord->firstread = time(); + $readrecord->lastread = $readrecord->firstread; + return insert_record('forum_read', $readrecord, true, 'userid'); + } + else { + /// Update read record + $readrecord = reset($readrecord); + $readrecord->lastread = time(); + + /// This shouldn't happen, but just in case... + if (!$readrecord->firstread) { + $readrecord->firstread = $readrecord->lastread; + /// Update the 'firstread' field. + set_field('forum_read', 'firstread', $readrecord->firstread, 'userid', $userid, 'postid', $postid); + } + if ($discussionid > -1) { + /// Update the 'discussionid' field. + set_field('forum_read', 'discussionid', $discussionid, 'userid', $userid, 'postid', $postid); + } + if ($forumid > -1) { + /// Update the 'forumid' field. + set_field('forum_read', 'forumid', $forumid, 'userid', $userid, 'postid', $postid); + } + + $readrecord->forumid = $forumid; + /// Update the 'lastread' field. + return set_field('forum_read', 'lastread', $readrecord->lastread, 'userid', $userid, 'postid', $postid); + } +} + +function forum_tp_get_read_records($userid=-1, $postid=-1, $discussionid=-1, $forumid=-1) { + /// Returns all records in the 'forum_read' table matching the passed keys, indexed + /// by userid. + $select = ''; + if ($userid > -1) { + if ($select != '') $select .= ' AND '; + $select .= 'userid = \''.$userid.'\''; + } + if ($postid > -1) { + if ($select != '') $select .= ' AND '; + $select .= 'postid = \''.$postid.'\''; + } + if ($discussionid > -1) { + if ($select != '') $select .= ' AND '; + $select .= 'discussionid = \''.$discussionid.'\''; + } + if ($forumid > -1) { + if ($select != '') $select .= ' AND '; + $select .= 'forumid = \''.$forumid.'\''; + } + + return get_records_select('forum_read', $select); +} + +function forum_tp_get_discussion_read_records($userid, $discussionid) { + /// Returns all read records for the provided user and discussion, indexed by postid. + $select = 'userid = \''.$userid.'\' AND discussionid = \''.$discussionid.'\''; + $fields = 'postid, firstread, lastread'; + return get_records_select('forum_read', $select, '', $fields); +} + +function forum_tp_mark_post_read($userid, &$post, $forumid) { +/// If its an old post, do nothing. If the record exists, the maintenance will clear it up later. + if (!forum_tp_is_post_old($post)) { + return forum_tp_add_read_record($userid, $post->id, $post->discussion, $forumid); + } else { + return true; + } +} + +function forum_tp_is_post_read($userid, &$post) { + return (forum_tp_is_post_old($post) || + (get_record('forum_read', 'userid', $userid, 'postid', $post->id) !== false)); +} + +function forum_tp_is_post_old(&$post, $time=null) { + global $CFG; + + if (is_null($time)) $time = time(); + return ($post->modified < ($time - ($CFG->forum_oldpostdays * 24 * 3600))); +} + +function forum_tp_count_discussion_read_records($userid, $discussionid) { + /// Returns the count of records for the provided user and discussion. + global $CFG; + + $cutoffdate = isset($CFG->forum_oldpostdays) ? (time() - ($CFG->forum_oldpostdays*24*60*60)) : 0; + + $sql = 'SELECT COUNT(DISTINCT p.id) '. + 'FROM '.$CFG->prefix.'forum_discussions d '. + 'LEFT JOIN '.$CFG->prefix.'forum_read r ON d.id = r.discussionid AND r.userid = '.$userid.' '. + 'LEFT JOIN '.$CFG->prefix.'forum_posts p ON p.discussion = d.id '. + 'AND (p.modified < '.$cutoffdate.' OR p.id = r.postid) '. + 'WHERE d.id = '.$discussionid; + + return (count_records_sql($sql)); +} + +function forum_tp_count_forum_posts($forumid, $groupid=false) { + /// Returns the count of posts for the provided forum and [optionally] group. + global $CFG; + + $sql = 'SELECT COUNT(*) '. + 'FROM '.$CFG->prefix.'forum_posts fp,'.$CFG->prefix.'forum_discussions fd '. + 'WHERE fd.forum = '.$forumid.' AND fp.discussion = fd.id'; + if ($groupid !== false) { + $sql .= ' AND (fd.groupid = '.$groupid.' OR fd.groupid = -1)'; + } + $count = count_records_sql($sql); + + + return $count; +} + +function forum_tp_count_forum_read_records($userid, $forumid, $groupid=false) { + /// Returns the count of records for the provided user and forum and [optionally] group. + global $CFG; + + $cutoffdate = isset($CFG->forum_oldpostdays) ? (time() - ($CFG->forum_oldpostdays*24*60*60)) : 0; + + $groupsel = ''; + if ($groupid !== false) { + $groupsel = ' AND (d.groupid = '.$groupid.' OR d.groupid = -1)'; + } + + $sql = 'SELECT COUNT(DISTINCT p.id) '. + 'FROM '.$CFG->prefix.'forum_posts p,'.$CFG->prefix.'forum_read r,'.$CFG->prefix.'forum_discussions d '. + 'WHERE d.forum = '.$forumid.$groupsel.' AND p.discussion = d.id AND '. + '((p.id = r.postid AND r.userid = '.$userid.') OR p.modified < '.$cutoffdate.' ) '; + + return (count_records_sql($sql)); +} + +function forum_tp_delete_read_records($userid=-1, $postid=-1, $discussionid=-1, $forumid=-1) { +/// Deletes read records for the specified index. At least one parameter must be specified. + $select = ''; + if ($userid > -1) { + if ($select != '') $select .= ' AND '; + $select .= 'userid = \''.$userid.'\''; + } + if ($postid > -1) { + if ($select != '') $select .= ' AND '; + $select .= 'postid = \''.$postid.'\''; + } + if ($discussionid > -1) { + if ($select != '') $select .= ' AND '; + $select .= 'discussionid = \''.$discussionid.'\''; + } + if ($forumid > -1) { + if ($select != '') $select .= ' AND '; + $select .= 'forumid = \''.$forumid.'\''; + } + if ($select == '') { + return false; + } + else { + return delete_records_select('forum_read', $select); + } +} + +/// Clean old records from the forum_read table. +function forum_tp_clean_read_records() { + global $CFG; + +/// Look for records older than the cutoffdate that are still in the forum_read table. + $cutoffdate = isset($CFG->forum_oldpostdays) ? (time() - ($CFG->forum_oldpostdays*24*60*60)) : 0; + $sql = 'SELECT fr.id, fr.userid, fr.postid '. + 'FROM '.$CFG->prefix.'forum_posts fp, '.$CFG->prefix.'forum_read fr '. + 'WHERE fp.modified < '.$cutoffdate.' AND fp.id = fr.postid'; +echo $sql.'
'; + if (($oldreadposts = get_records_sql($sql))) { +echo 'Deleting records: ';print_object($oldreadposts); + foreach($oldreadposts as $oldreadpost) { + delete_records('forum_read', 'userid', $oldreadpost->userid, 'postid', $oldreadpost->postid); + } + } +} ?> diff --git a/mod/forum/post.php b/mod/forum/post.php index 90745a831e..d11f845fac 100644 --- a/mod/forum/post.php +++ b/mod/forum/post.php @@ -489,7 +489,7 @@ $cm = get_coursemodule_from_instance("forum", $forum->id, $course->id); - if (!empty($discussion) and empty($discussion->name)) { + if (empty($discussion->name)) { $discussion->name = $forum->name; } @@ -509,7 +509,12 @@ if (!empty($parent)) { forum_print_post($parent, $course->id, $ownpost=false, $reply=false, $link=false); if (empty($post->edit)) { - forum_print_posts_threaded($parent->id, $course, 0, false, false); + if ($CFG->forum_trackreadposts) { + $user_read_array = forum_tp_get_discussion_read_records($USER->id, $discussion->id); + } else { + $user_read_array = array(); + } + forum_print_posts_threaded($parent->id, $course, 0, false, false, $user_read_array, $discussion->forum); } echo "
"; echo "

".get_string("yourreply", "forum").":

"; diff --git a/mod/forum/search.php b/mod/forum/search.php index 9d2cb7c622..4fc8be90c8 100644 --- a/mod/forum/search.php +++ b/mod/forum/search.php @@ -40,9 +40,7 @@ if (!$search) { print_header_simple("$strsearch", "", "id\">$strforums -> $strsearch", "search.search", - "", "", " ", navmenu($course)); - - echo ''; // forum-search wrapper end - print_footer($course); exit; } @@ -88,9 +80,7 @@ print_header_simple("$strsearchresults", "", "id\">$strforums -> id\">$strsearch -> \"$search\"", "search.search", - "", "", $searchform, navmenu($course)); - - echo '
"; } - echo '
'; // forum-search wrapper end - print_footer($course); ?> diff --git a/mod/forum/version.php b/mod/forum/version.php index 3a4990439d..2c411dc13a 100644 --- a/mod/forum/version.php +++ b/mod/forum/version.php @@ -5,7 +5,7 @@ // This fragment is called by /admin/index.php //////////////////////////////////////////////////////////////////////////////// -$module->version = 2004111700; +$module->version = 2005011500; $module->requires = 2004091700; // Requires this Moodle version $module->cron = 60;