Current time: 04-24-2014, 04:44 PM Hello There, Guest! (LoginRegister)


Subforum read/unread inconsistencies [R]
12-09-2008, 09:38 PM
Post: #11
RE: Subforum read/unread inconsistencies [R]
This user has been denied support. This user has been denied support.
If there is interest I can produce a new patch based on my plugin code. The patch I posted above was suboptimal. The idea in general is still the same though.

Google SEO | Gravatar | Hooks | HTMLPurifier | Overview | Patches | PluginLibrary @ GitHub/frostschutz
Find all posts by this user
12-09-2008, 09:41 PM
Post: #12
RE: Subforum read/unread inconsistencies [R]
frostschutz if you can do it... please Big Grin
Find all posts by this user
12-18-2008, 10:08 PM
Post: #13
RE: Subforum read/unread inconsistencies [R]
Yeah, that would be great. The Forum on/off icons really irritate me when they are wrong

[Image: thelol.png]
Find all posts by this user
12-18-2008, 10:40 PM
Post: #14
RE: Subforum read/unread inconsistencies [R]
It will be great to, if guests when go on forum, has got thread marks as read not unread /that us now/
Find all posts by this user
12-19-2008, 12:04 AM (This post was last modified: 12-19-2008 12:19 AM by frostschutz.)
Post: #15
RE: Subforum read/unread inconsistencies [R]
This user has been denied support. This user has been denied support.
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,+21,@@
     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,+37,@@
             
$forums $subforums $sub_forums '';
             
$lastpost_data '';
             
$counters '';
+            
$forum_info 0;
+            
$lightbulb 0;
 
             
// Get the permissions for this forum
             
$permissions $forumpermissions[$forum['fid']];
@@ -
112,+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 == || $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,+135,@@
                 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,+331,@@
     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'] == || $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'] != && $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 == || $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""&amp;"$forum['name']); // Fix & but allow unicode
            
$forum['description'] = preg_replace("#&(?!\#[0-9]+;)#si""&amp;"$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 == && $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"10)."\";");
                    }

                    
// Fetch the template and append it to the list
                    
eval("\$forum_list .= \"".$templates->get("forumbit_depth3"10)."\";");
                    
$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'] == || $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_subject025)."...";
                    }
                    
$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'] != && $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'] == || $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
Find all posts by this user
12-28-2008, 08:29 PM
Post: #16
RE: Subforum read/unread inconsistencies [R]
frostschutz so how do it, that it will be do in 1.4.4 pack?
Find all posts by this user
01-22-2009, 06:55 PM
Post: #17
RE: Subforum read/unread inconsistencies [R]
frostschutz do you think you could make a video or screenshots detailing the bugs in this report? This would give me a better idea of what exactly is going wrong and what we can do about it.
Visit this user's website Find all posts by this user
01-29-2009, 07:00 PM
Post: #18
RE: Subforum read/unread inconsistencies [R]
Just a reminder for frostschutz.
Visit this user's website Find all posts by this user
01-29-2009, 11:17 PM (This post was last modified: 01-30-2009 12:09 AM by frostschutz.)
Post: #19
RE: Subforum read/unread inconsistencies [R]
This user has been denied support. This user has been denied support.
Okay. Screenshots it is.

#1:
I download latest MyBB and install it in localhost/latest. Here the specs of my server in MyBB requirements screenshot:
   

#2:
The new forum is created, still empty. No patches, no plugins, no nothing, just a fresh clean MyBB install, version of today.
   

#3:
Go to Admin CP, Forums, select 'Add Child Forum' to My Forum.
   

#4:
Call the new forum 'My Subforum'.
   

#5:
This is the new overview with the subforum. As no one posted anything yet, lightbulb status for all is 'off'.
   

#6:
In 'My Subforum', create a new thread. Call it 'Thread in My Subforum'.
   

#7:
Return to 'My Subforum' on the friendly redirect page. It shows your own posting as already read.
   

#8:
Return to the main index. It shows 'on' (unread new posts) indicator for 'My Forum', however there is only one thread in total, and this thread is in the subforum, which is 'off' (no new posts), and this is the thread I wrote myself, and there is nothing new at all to be read here.
   

This is the bug (the simplest case of it): the 'My Forum' lightbulb should be 'off' here.
What the current lightbulb logic does:
It takes a forum, traverses recursively through all its subforums, in order to find the lastpost (the posting that will also be shown in the right column of the forum index). For each (sub)forum it then compares the forum timestamp (time of when the user last read that forum) with the lastpost timestamp (time of when the last posting was made in the forum or one of its subforums). If the forum timestamp is older than the lastpost timestamp, then the lightbulb is on (unread forum, new posts).

However this is the wrong thing to do: The lastpost may already be read, then the forum should not be on. However at the same time there actually may be other unread threads in the parent forum, so it should still be on. So the problem can not be solved by considering lastpost of a subforum only.
What my patch does is, instead of considering only the lastpost, it considers the postings of each forum, and the unread status of each subforum individually. A forum lightbulb is 'on' (unread, new posts), if one of its subforums is 'on' with a timestamp newer than the forum timestamp, or if the lastpost local to that forum is newer than the forum timestamp.

Google SEO | Gravatar | Hooks | HTMLPurifier | Overview | Patches | PluginLibrary @ GitHub/frostschutz
Find all posts by this user
01-30-2009, 01:41 AM
Post: #20
RE: Subforum read/unread inconsistencies [R]
Okay yes, that is what I have been talking about this entire time. While we were designing the system in alpha stages we knew we'd run into this issue.

The reason why we can't do it is because it is simply too processor intensive. We can't recurse up and down every single child and parent three in the forum array. Just think if we did that on a forum like http://ncaabbs.com/ - It would overload the server.
Visit this user's website Find all posts by this user


Forum Jump:


User(s) browsing this thread: 1 Guest(s)

Contact Us | MyBB | Return to Top | Return to Content | Lite (Archive) Mode | RSS Syndication