RE: Subforum read/unread inconsistencies [R]
OK, here is a new attempt at a patch.
This code is backported into MyBB 1.4.4 (derived from Subforum Bulb 0.4).
(Second try. Naturally I found a bug that no one else found in my plugin while producing the patch).
The current MyBB logic fetches the very latest post of a forum and its subforums (lastpost), and marks a forum as unread if that lastpost is newer than the forums read timestamp. The problem is that a newer, already read subforum thread can cause parent forums to be marked as unread, even though there are no other new threads, and the lastpost of the subforum was already read (most common example: you yourself posted in a subforum and your own post marks the parent forum as unread).
This patch implements a different approach:
It gives each lightbulb a timestamp (lastbulb). For locked subforums, this timestamp is -1. For read subforums, this timestamp is 0. For unread subforums, it's the timestamp of the post that caused the subforum to be marked unread. A parent forum inherits the strongest bulb (highest timestamp) from its subforums. The parent forum is marked unread when this timestamp is newer than the forums read date.
This way a parent forum will only be marked unread when
a) it contains new posts
b) it contains an unread subforum with posts newer than the forums read date
Patch:
PHP Code:
--- functions_forumlist.php.orig 2008-12-18 23:41:59.591705306 +0100 +++ functions_forumlist.php 2008-12-19 00:59:58.727461168 +0100 @@ -21,6 +21,7 @@ global $fcache, $moderatorcache, $forumpermissions, $theme, $mybb, $templates, $bgcolor, $collapsed, $lang, $showdepth, $plugins, $parser, $forum_viewers; $forum_listing = ''; + $subforum_bulb = 0; // for inheriting the strongest bulb // If no forums exist with this parent, do nothing if(!is_array($fcache[$pid])) @@ -36,6 +37,8 @@ $forums = $subforums = $sub_forums = ''; $lastpost_data = ''; $counters = ''; + $forum_info = 0; + $lightbulb = 0; // Get the permissions for this forum $permissions = $forumpermissions[$forum['fid']]; @@ -112,6 +115,18 @@ $parent_counters['unapprovedposts'] += $forum['unapprovedposts']; $parent_counters['unapprovedthreads'] += $forum['unapprovedthreads']; $parent_counters['viewers'] += $forum['viewers']; + + // Get the subforum bulb. + $lightbulb = $forum_info['subforum_bulb']; + } + + // Get the lightbulb status indicator for this forum based on local lastpost and subforum bulb. + $lightbulb = get_forum_lightbulb($forum, $lightbulb, $hideinfo); + + // Use this as subforum_bulb if it's more potent than our last one. + if($subforum_bulb == 0 || $subforum_bulb['lastbulb'] < $lightbulb['lastbulb']) + { + $subforum_bulb = $lightbulb; } // Done with our math, lets talk about displaying - only display forums which are under a certain depth @@ -120,9 +135,6 @@ continue; } - // Get the lightbulb status indicator for this forum based on the lastpost - $lightbulb = get_forum_lightbulb($forum, $lastpost_data, $hideinfo); - // Fetch the number of unapproved threads and posts for this forum $unapproved = get_forum_unapproved($forum); @@ -319,7 +331,8 @@ return array( "forum_list" => $forum_list, "counters" => $parent_counters, - "lastpost" => $parent_lastpost + "lastpost" => $parent_lastpost, + "subforum_bulb" => $subforum_bulb ); } @@ -330,13 +343,14 @@ * @param array Array of information about the lastpost date * @return array Array of the folder image to be shown and the alt text */ -function get_forum_lightbulb($forum, $lastpost, $locked=0) +function get_forum_lightbulb($forum, $subforum_bulb, $locked=0) { global $mybb, $lang, $db, $unread_forums; // This forum is closed, so override the folder icon with the "offlock" icon. if($forum['open'] == 0 || $locked) { + $lastbulb = -1; $folder = "offlock"; $altonoff = $lang->forum_locked; } @@ -358,23 +373,38 @@ } // If the lastpost is greater than the last visit and is greater than the forum read date, we have a new post - if($lastpost['lastpost'] > $forum_read && $lastpost['lastpost'] != 0) + if($forum['lastpost'] && $forum['lastpost'] > $forum_read) { $unread_forums++; $folder = "on"; $altonoff = $lang->new_posts; + $lastbulb = $forum['lastpost']; } // Otherwise, no new posts else { $folder = "off"; $altonoff = $lang->no_new_posts; + $lastbulb = 0; + } + } + + if($subforum_bulb['lastbulb'] && $subforum_bulb['lastbulb'] > $forum_read) + { + // New unread subforum. + $folder = "on"; + $altonoff = $lang->new_posts; + + if($subforum_bulb['lastbulb'] > $lastbulb) + { + $lastbulb = $subforum_bulb['lastbulb']; } } return array( "folder" => $folder, - "altonoff" => $altonoff + "altonoff" => $altonoff, + "lastbulb" => $lastbulb ); }
Full Code (functions_forumlist::build_forumbits() and functions_forumlist::get_forum_lightbulb()).
PHP Code:
/** * Build a list of forum bits. * * @param int The parent forum to fetch the child forums for (0 assumes all) * @param int The depth to return forums with. * @return array Array of information regarding the child forums of this parent forum */ function build_forumbits($pid=0, $depth=1) { global $fcache, $moderatorcache, $forumpermissions, $theme, $mybb, $templates, $bgcolor, $collapsed, $lang, $showdepth, $plugins, $parser, $forum_viewers;
$forum_listing = ''; $subforum_bulb = 0; // for inheriting the strongest bulb
// If no forums exist with this parent, do nothing if(!is_array($fcache[$pid])) { return; }
// Foreach of the forums in this parent foreach($fcache[$pid] as $parent) { foreach($parent as $forum) { $forums = $subforums = $sub_forums = ''; $lastpost_data = ''; $counters = ''; $forum_info = 0; $lightbulb = 0;
// Get the permissions for this forum $permissions = $forumpermissions[$forum['fid']];
// If this user doesnt have permission to view this forum and we're hiding private forums, skip this forum if($permissions['canview'] != 1 && $mybb->settings['hideprivateforums'] == 1) { continue; }
$plugins->run_hooks_by_ref("build_forumbits_forum", $forum);
// Build the link to this forum $forum_url = get_forum_link($forum['fid']);
// This forum has a password, and the user isn't authenticated with it - hide post information $hideinfo = false; if($forum['password'] != '' && $mybb->cookies['forumpass'][$forum['fid']] != md5($mybb->user['uid'].$forum['password'])) { $hideinfo = true; }
$lastpost_data = array( "lastpost" => $forum['lastpost'], "lastpostsubject" => $forum['lastpostsubject'], "lastposter" => $forum['lastposter'], "lastposttid" => $forum['lastposttid'], "lastposteruid" => $forum['lastposteruid'] );
// Fetch subforums of this forum if(isset($fcache[$forum['fid']])) { $forum_info = build_forumbits($forum['fid'], $depth+1);
// Increment forum counters with counters from child forums $forum['threads'] += $forum_info['counters']['threads']; $forum['posts'] += $forum_info['counters']['posts']; $forum['unapprovedthreads'] += $forum_info['counters']['unapprovedthreads']; $forum['unapprovedposts'] += $forum_info['counters']['unapprovedposts']; $forum['viewers'] += $forum_info['counters']['viewing'];
// If the child forums' lastpost is greater than the one for this forum, set it as the child forums greatest. if($forum_info['lastpost']['lastpost'] > $lastpost_data['lastpost']) { $lastpost_data = $forum_info['lastpost']; }
$sub_forums = $forum_info['forum_list']; }
// If we are hiding information (lastpost) because we aren't authenticated against the password for this forum, remove them if($hideinfo == true) { unset($lastpost_data); }
// If the current forums lastpost is greater than other child forums of the current parent, overwrite it if($lastpost_data['lastpost'] > $parent_lastpost['lastpost']) { $parent_lastpost = $lastpost_data; }
if(is_array($forum_viewers) && $forum_viewers[$forum['fid']] > 0) { $forum['viewers'] = $forum_viewers[$forum['fid']]; }
// Increment the counters for the parent forum (returned later) if($hideinfo != true) { $parent_counters['threads'] += $forum['threads']; $parent_counters['posts'] += $forum['posts']; $parent_counters['unapprovedposts'] += $forum['unapprovedposts']; $parent_counters['unapprovedthreads'] += $forum['unapprovedthreads']; $parent_counters['viewers'] += $forum['viewers'];
// Get the subforum bulb. $lightbulb = $forum_info['subforum_bulb']; }
// Get the lightbulb status indicator for this forum based on local lastpost and subforum bulb. $lightbulb = get_forum_lightbulb($forum, $lightbulb, $hideinfo);
// Use this as subforum_bulb if it's more potent than our last one. if($subforum_bulb == 0 || $subforum_bulb['lastbulb'] < $lightbulb['lastbulb']) { $subforum_bulb = $lightbulb; }
// Done with our math, lets talk about displaying - only display forums which are under a certain depth if($depth > $showdepth) { continue; }
// Fetch the number of unapproved threads and posts for this forum $unapproved = get_forum_unapproved($forum);
if($hideinfo == true) { unset($unapproved); }
// Sanitize name and description of forum. $forum['name'] = preg_replace("#&(?!\#[0-9]+;)#si", "&", $forum['name']); // Fix & but allow unicode $forum['description'] = preg_replace("#&(?!\#[0-9]+;)#si", "&", $forum['description']); // Fix & but allow unicode $forum['name'] = preg_replace("#&([^\#])(?![a-z1-4]{1,10};)#i", "&$1", $forum['name']); $forum['description'] = preg_replace("#&([^\#])(?![a-z1-4]{1,10};)#i", "&$1", $forum['description']);
// If this is a forum and we've got subforums of it, load the subforums list template if($depth == 2 && $sub_forums) { eval("\$subforums = \"".$templates->get("forumbit_subforums")."\";"); }
// A depth of three indicates a comma separated list of forums within a forum else if($depth == 3) { if($donecount < $mybb->settings['subforumsindex']) { $statusicon = '';
// Showing mini status icons for this forum if($mybb->settings['subforumsstatusicons'] == 1) { $lightbulb['folder'] = "mini".$lightbulb['folder']; eval("\$statusicon = \"".$templates->get("forumbit_depth3_statusicon", 1, 0)."\";"); }
// Fetch the template and append it to the list eval("\$forum_list .= \"".$templates->get("forumbit_depth3", 1, 0)."\";"); $comma = ', '; }
// Have we reached our max visible subforums? put a nice message and break out of the loop ++$donecount; if($donecount == $mybb->settings['subforumsindex']) { if(subforums_count($fcache[$pid]) > $donecount) { $forum_list .= $comma.$lang->sprintf($lang->more_subforums, (subforums_count($fcache[$pid]) - $donecount)); } } continue; }
// Forum is a category, set template type if($forum['type'] == 'c') { $forumcat = '_cat'; } // Forum is a standard forum, set template type else { $forumcat = '_forum'; }
if($forum['linkto'] == '') { // No posts have been made in this forum - show never text if(($lastpost_data['lastpost'] == 0 || $lastpost_data['lastposter'] == '') && $hideinfo != true) { $lastpost = "<div style=\"text-align: center;\">{$lang->lastpost_never}</div>"; } elseif($hideinfo != true) { // Format lastpost date and time $lastpost_date = my_date($mybb->settings['dateformat'], $lastpost_data['lastpost']); $lastpost_time = my_date($mybb->settings['timeformat'], $lastpost_data['lastpost']);
// Set up the last poster, last post thread id, last post subject and format appropriately $lastpost_profilelink = build_profile_link($lastpost_data['lastposter'], $lastpost_data['lastposteruid']); $lastpost_link = get_thread_link($lastpost_data['lastposttid'], 0, "lastpost"); $lastpost_subject = $full_lastpost_subject = $parser->parse_badwords($lastpost_data['lastpostsubject']); if(my_strlen($lastpost_subject) > 25) { $lastpost_subject = my_substr($lastpost_subject, 0, 25)."..."; } $lastpost_subject = htmlspecialchars_uni($lastpost_subject); $full_lastpost_subject = htmlspecialchars_uni($full_lastpost_subject);
// Call lastpost template if($depth != 1) { eval("\$lastpost = \"".$templates->get("forumbit_depth{$depth}_forum_lastpost")."\";"); } }
$forum_viewers_text = ''; $forum_viewers_text_plain = ''; if($mybb->settings['showforumviewing'] != 0 && $forum['viewers'] > 0) { if($forum['viewers'] == 1) { $forum_viewers_text = $lang->viewing_one; } else { $forum_viewers_text = $lang->sprintf($lang->viewing_multiple, $forum['viewers']); } $forum_viewers_text_plain = $forum_viewers_text; $forum_viewers_text = "<span class=\"smalltext\">{$forum_viewers_text}</span>"; } } // If this forum is a link or is password protected and the user isn't authenticated, set lastpost and counters to "-" if($forum['linkto'] != '' || $hideinfo == true) { $lastpost = "<div style=\"text-align: center;\">-</div>"; $posts = "-"; $threads = "-"; } // Otherwise, format thread and post counts else { $posts = my_number_format($forum['posts']); $threads = my_number_format($forum['threads']); }
// Moderator column is not off if($mybb->settings['modlist'] != 0) { $done_moderators = array(); $moderators = ''; // Fetch list of moderators from this forum and its parents $parentlistexploded = explode(',', $forum['parentlist']); foreach($parentlistexploded as $mfid) { // This forum has moderators if(is_array($moderatorcache[$mfid])) { // Fetch each moderator from the cache and format it, appending it to the list foreach($moderatorcache[$mfid] as $moderator) { if(in_array($moderator['uid'], $done_moderators)) { continue; }
$moderators .= "{$comma}<a href=\"".get_profile_link($moderator['uid'])."\">".htmlspecialchars_uni($moderator['username'])."</a>"; $comma = ', ';
$done_moderators[] = $moderator['uid']; } } } $comma = '';
// If we have a moderators list, load the template if($moderators) { eval("\$modlist = \"".$templates->get("forumbit_moderators")."\";"); } else { $modlist = ''; } }
// Descriptions aren't being shown - blank them if($mybb->settings['showdescriptions'] == 0) { $forum['description'] = ''; }
// Check if this category is either expanded or collapsed and hide it as necessary. $expdisplay = ''; $collapsed_name = "cat_{$forum['fid']}_c"; if(isset($collapsed[$collapsed_name]) && $collapsed[$collapsed_name] == "display: show;") { $expcolimage = "collapse_collapsed.gif"; $expdisplay = "display: none;"; $expaltext = "[+]"; } else { $expcolimage = "collapse.gif"; $expaltext = "[-]"; }
// Swap over the alternate backgrounds $bgcolor = alt_trow();
// Add the forum to the list eval("\$forum_list .= \"".$templates->get("forumbit_depth$depth$forumcat")."\";"); } }
// Return an array of information to the parent forum including child forums list, counters and lastpost information return array( "forum_list" => $forum_list, "counters" => $parent_counters, "lastpost" => $parent_lastpost, "subforum_bulb" => $subforum_bulb ); }
/** * Fetch the status indicator for a forum based on its last post and the read date * * @param array Array of information about the forum * @param array Array of information about the lastpost date * @return array Array of the folder image to be shown and the alt text */ function get_forum_lightbulb($forum, $subforum_bulb, $locked=0) { global $mybb, $lang, $db, $unread_forums;
// This forum is closed, so override the folder icon with the "offlock" icon. if($forum['open'] == 0 || $locked) { $lastbulb = -1; $folder = "offlock"; $altonoff = $lang->forum_locked; }
else { // Fetch the last read date for this forum if($forum['lastread']) { $forum_read = $forum['lastread']; } else { $forum_read = my_get_array_cookie("forumread", $forum['fid']); }
if(!$forum_read) { $forum_read = $mybb->user['lastvisit']; }
// If the lastpost is greater than the last visit and is greater than the forum read date, we have a new post if($forum['lastpost'] && $forum['lastpost'] > $forum_read) { $unread_forums++; $folder = "on"; $altonoff = $lang->new_posts; $lastbulb = $forum['lastpost']; } // Otherwise, no new posts else { $folder = "off"; $altonoff = $lang->no_new_posts; $lastbulb = 0; } }
if($subforum_bulb['lastbulb'] && $subforum_bulb['lastbulb'] > $forum_read) { // New unread subforum. $folder = "on"; $altonoff = $lang->new_posts;
if($subforum_bulb['lastbulb'] > $lastbulb) { $lastbulb = $subforum_bulb['lastbulb']; } }
return array( "folder" => $folder, "altonoff" => $altonoff, "lastbulb" => $lastbulb ); }
Google SEO | Gravatar | Hooks | HTMLPurifier | Overview | Patches | PluginLibrary @ GitHub/frostschutz
|