Should fix "unread state" issues.
[banana.git] / banana / spool.inc.php
index 610d192..ed898e2 100644 (file)
@@ -9,7 +9,7 @@
 
 require_once dirname(__FILE__) . '/banana.inc.php';
 
-define('BANANA_SPOOL_VERSION', '0.3');
+define('BANANA_SPOOL_VERSION', '0.4');
 
 /** Class spoolhead
  *  class used in thread overviews
@@ -49,7 +49,7 @@ class BananaSpoolHead
     public function __construct(array &$message)
     {
         $this->date       = $message['date'];
-        $this->subject    = stripslashes($message['subject']);
+        $this->subject    = $message['subject'];
         $this->from       = $message['from'];
         $this->desc       = 1;
         $this->isread     = true;
@@ -61,6 +61,7 @@ class BananaSpoolHead
 class BananaSpool
 {
     private $version;
+    private $mode;
 
     /**  group name */
     public $group;
@@ -71,6 +72,7 @@ class BananaSpool
     /** thread starts */
     public $roots;
 
+    private $unreadnb = 0;
 
     /** constructor
      * @param $_group STRING group name
@@ -80,13 +82,14 @@ class BananaSpool
     protected function __construct($group)
     {
         $this->version    = BANANA_SPOOL_VERSION;
+        $this->mode       = Banana::SPOOL_ALL;
         $this->group      = $group;
     }
 
-    public static function getSpool($group, $since = 0)
+    public static function getSpool($group, $since = 0, $clean = false)
     {
         if (!is_null(Banana::$spool) && Banana::$spool->group == $group) {
-            $spool = Banana::$spool;
+            $spool =& Banana::$spool;
         } else {
             $spool = BananaSpool::readFromFile($group);
         }        
@@ -95,26 +98,20 @@ class BananaSpool
         }
         Banana::$spool =& $spool;
         $spool->build();
+        if ($clean) {
+            $spool->markAllAsRead();
+        }
         $spool->updateUnread($since);
         return $spool;
     }
 
     private static function spoolFilename($group)
     {
-        $file = dirname(dirname(__FILE__));
-        $file .= '/spool/' . Banana::$protocole->name() . '/';
+        $file = Banana::$spool_root . '/' . Banana::$protocole->name() . '/';
         if (!is_dir($file)) {
             mkdir($file);
         }
-        $url  = parse_url(Banana::$host);
-        if (isset($url['host'])) {
-            $file .= $url['host'] . '_';
-        }
-        if (isset($url['port'])) {
-            $file .= $url['port'] . '_';
-        }
-        $file .= $group;
-        return $file;
+        return $file . Banana::$protocole->filename();
     }
 
     private static function readFromFile($group)
@@ -124,9 +121,10 @@ class BananaSpool
             return null;
         }
         $spool =  unserialize(file_get_contents($file));
-        if ($spool->version != BANANA_SPOOL_VERSION) {
+        if ($spool->version != BANANA_SPOOL_VERSION || $spool->mode != Banana::SPOOL_ALL) {
             return null;
         }
+        $spool->markAllAsRead();
         return $spool;
     }
 
@@ -147,7 +145,9 @@ class BananaSpool
             }
         }
 
-        file_put_contents($file, serialize($this));
+        if ($this->mode == Banana::SPOOL_ALL) {
+            file_put_contents($file, serialize($this));
+        }    
     }
 
     private function build()
@@ -161,8 +161,8 @@ class BananaSpool
             $threshold = (int)(log($threshold)/log(2));
             $threshold = (2 ^ ($threshold + 1)) - 1;
         }
-        if (Banana::$maxspool && Banana::$maxspool < $msgnum) {
-            $first = $last - Banana::$maxspool;
+        if (Banana::$spool_max && Banana::$spool_max < $msgnum) {
+            $first = $last - Banana::$spool_max;
             if ($first < 0) {
                 $first += $threshold;
             }
@@ -182,8 +182,7 @@ class BananaSpool
             $mids = array_keys($this->overview);
             foreach ($mids as $id) {
                 if (($first <= $last && ($id < $first || $id > $last))
-                        || ($first > $last && $id < $first && $id > $last))
-                {
+                        || ($first > $last && $id < $first && $id > $last)) {
                     $this->delid($id, false);
                     $do_save = true;
                 }
@@ -197,7 +196,7 @@ class BananaSpool
 
     private function update(&$first, &$last, $msgnum)
     {
-        if ($first >= $last || !$msgnum) {       
+        if ($first > $last || !$msgnum) {       
             return false;
         }
 
@@ -211,6 +210,9 @@ class BananaSpool
             $this->ids[$message['message-id']] = $id;
         }
 
+        if (!is_array($this->overview)) {
+            $this->overview = array();
+        }
         foreach ($messages as $id=>&$message) {
             if (!isset($this->overview[$id])) {
                 $this->overview[$id] = new BananaSpoolHead($message);
@@ -248,7 +250,7 @@ class BananaSpool
         }
 
         $newpostsids = Banana::$protocole->getNewIndexes($since);
-        
+
         if (empty($newpostsids)) {
             return;
         }
@@ -260,10 +262,10 @@ class BananaSpool
         foreach ($newpostsids as $mid) {
             $id = $this->ids[$mid];
             if ($this->overview[$id]->isread) {
-                $this->overview[$id]->isread     = false;
-                $this->overview[$id]->descunread = 1;
+                $this->overview[$id]->isread = false;
+                $this->unreadnb++;
                 while (isset($id)) {
-                    $this->overview[$id]->descunread ++;
+                    $this->overview[$id]->descunread++;
                     $id = $this->overview[$id]->parent;
                 }
             }
@@ -272,6 +274,7 @@ class BananaSpool
 
     public function setMode($mode)
     {
+        $this->mode = $mode;
         switch ($mode) {
           case Banana::SPOOL_UNREAD:
             foreach ($this->roots as $k=>$i) {
@@ -284,6 +287,46 @@ class BananaSpool
         }
     }
 
+    /** Mark the given id as read
+     * @param id MSGNUM of post
+     */
+    public function markAsRead($id)
+    {
+        if (!$this->overview[$id]->isread) {
+            $this->overview[$id]->isread = true;
+            $this->unreadnb--;
+            while (isset($id)) {
+                $this->overview[$id]->descunread--;
+                $id = $this->overview[$id]->parent;
+            }
+        }
+    }
+
+    /** Mark all unread messages as read
+     */
+    public function markAllAsRead(array &$array = null)
+    {
+        if (!$this->unreadnb) {
+            return;
+        }
+        if (is_null($array) && is_array($this->roots)) {
+            $array =& $this->roots;
+        } elseif (is_null($array)) {
+            return;
+        }
+        foreach ($array as $id) {
+            if (!$this->overview[$id]->isread) {
+                $this->markAsRead($id);
+                if (!$this->unreadnb) {
+                    return;
+                }
+            }
+            if ($this->overview[$id]->descunread) {
+                $this->markAllAsRead($this->overview[$id]->children);
+            }
+        }
+    }
+
     /** kill post and childrens
      * @param $_id MSGNUM of post
      */
@@ -306,32 +349,40 @@ class BananaSpool
     public function delid($_id, $write = true)
     {
         if (isset($this->overview[$_id])) {
-            if (sizeof($this->overview[$_id]->parent)) {
-                $this->overview[$this->overview[$_id]->parent]->children = 
-                    array_diff($this->overview[$this->overview[$_id]->parent]->children, array($_id));
-                if (sizeof($this->overview[$_id]->children)) {
-                    $this->overview[$this->overview[$_id]->parent]->children = 
-                        array_merge($this->overview[$this->overview[$_id]->parent]->children, $this->overview[$_id]->children);
-                    foreach ($this->overview[$_id]->children as $c) {
-                        $this->overview[$c]->parent        = $this->overview[$_id]->parent;
+            $overview =& $this->overview[$_id];
+            if (!$overview->isread) {
+                $this->markAsRead($_id);
+            }
+            if ($overview->parent) {
+                $p      =  $overview->parent;
+                $parent =& $this->overview[$p];
+                $parent->children = array_diff($parent->children, array($_id));
+                if (sizeof($overview->children)) {
+                    $parent->children = array_merge($parent->children, $overview->children);
+                    foreach ($overview->children as $c) {
+                        $this->overview[$c]->parent        = $p;
                         $this->overview[$c]->parent_direct = false;
                     }
                 }
-                $p = $this->overview[$_id]->parent;
-                while ($p) {
+                while (isset($p)) {
                     $this->overview[$p]->desc--;
                     $p = $this->overview[$p]->parent;
                 }
-            } elseif (sizeof($this->overview[$_id]->children)) {
-                foreach ($this->overview[$_id]->children as $c) {
+            } elseif ($overview->children) {
+                foreach ($overview->children as $c) {
                     $this->overview[$c]->parent = null;
                 }
             }
+            unset($overview);
             unset($this->overview[$_id]);
             $msgid = array_search($_id, $this->ids);
-            if ($msgid) {
+            if ($msgid !== false) {
                 unset($this->ids[$msgid]);
             }
+            $msgid = array_search($_id, $this->roots);
+            if ($msgid !== false) {
+                unset($this->roots[$msgid]);
+            }
             
             if ($write) {
                 $this->saveToFile();
@@ -353,7 +404,7 @@ class BananaSpool
         } else {
             $format = '%a %e %b';
         }
-        return utf8_encode(strftime($format, $stamp));
+        return strftime($format, $stamp);
     }
 
     /** displays children tree of a post
@@ -366,9 +417,7 @@ class BananaSpool
      * @param $_pfx_end STRING prefix used for children of current node
      * @param $_head BOOLEAN true if first post in thread
      *
-     * If you want to analyse subject, you can define the function hook_getSubject(&$subject) which
-     * take the subject as a reference parameter, transform this subject to be displaid in the spool
-     * view and return a string. This string will be put after the subject.
+     * If you want to analyse subject, you can define the function hook_formatDisplayHeader
      */
     private function _to_html($_id, $_index, $_first=0, $_last=0, $_ref="", $_pfx_node="", $_pfx_end="", $_head=true)
     {
@@ -397,17 +446,19 @@ class BananaSpool
 
             $res .= '<tr class="' . ($_index%2 ? 'pair' : 'impair') . ($overview->isread ? '' : ' new') . "\">\n";
             $res .= '<td class="date">' . $this->formatDate($overview->date) . " </td>\n";
-            $res .= '<td class="subj' . ($_index == $_ref ? ' cur' : '') . '">'
-                . $_pfx_node .($hc ? ($_head ? $spfx_f : ($overview->parent_direct ? $spfx_s : $spfx_snd)) : $spfx_n);
+            $res .= '<td class="subj' . ($_index == $_ref ? ' cur' : '') . '"><div class="tree">'
+                . $_pfx_node .($hc ? ($_head ? $spfx_f : ($overview->parent_direct ? $spfx_s : $spfx_snd)) : $spfx_n)
+                . '</div>';
             $subject = $overview->subject;
+            if (function_exists('hook_formatDisplayHeader')) {
+                list($subject, $link) = hook_formatDisplayHeader('subject', $subject, true);
+            } else {
+                $subject = banana_catchFormats(banana_htmlentities(stripslashes($subject)));
+                $link = null;
+            }
             if (empty($subject)) {
                 $subject = _b_('(pas de sujet)');
             }
-            $link = null;
-            if (function_exists('hook_getSubject')) {
-                $link = hook_getSubject($subject);
-            }
-            $subject = banana_catchFormats($subject);
             if ($_index != $_ref) {
                 $subject = Banana::$page->makeLink(Array('group' => $this->group, 'artid' => $_id,
                                                     'text'  => $subject, 'popup' => $subject));
@@ -455,12 +506,12 @@ class BananaSpool
 
         if (!$overview) {
             $_first = $first;
-            $_last  = $first + Banana::$tmax - 1;
+            $_last  = $first + Banana::$spool_tmax - 1;
             $_ref   = null;
         } else {
             $_ref   = $this->getNdx($first);
-            $_last  = $_ref + Banana::$tafter;
-            $_first = $_ref - Banana::$tbefore;
+            $_last  = $_ref + Banana::$spool_tafter;
+            $_first = $_ref - Banana::$spool_tbefore;
             if ($_first < 0) {
                 $_last -= $_first;
             }
@@ -624,6 +675,10 @@ class BananaSpool
      */
     public function nextUnread($id = null)
     {
+        if (!$this->unreadnb) {
+            return null;
+        }
+
         if (!is_null($id)) {
             // Look in message children
             foreach ($this->overview[$id]->children as $child) {
@@ -661,5 +716,5 @@ class BananaSpool
     }    
 }
 
-// vim:set et sw=4 sts=4 ts=4
+// vim:set et sw=4 sts=4 ts=4 enc=utf-8:
 ?>