Rewritten banana spool structure for faster build and access
authorx2003bruneau <x2003bruneau@9869982d-c50d-0410-be91-f2a2ec7c7c7b>
Sun, 28 Oct 2007 15:08:54 +0000 (15:08 +0000)
committerFlorent Bruneau <florent.bruneau@polytechnique.org>
Fri, 4 Jan 2008 23:35:52 +0000 (00:35 +0100)
Signed-off-by: Florent Bruneau <florent.bruneau@polytechnique.org>
git-svn-id: svn+ssh://murphy/home/svn/banana/trunk@284 9869982d-c50d-0410-be91-f2a2ec7c7c7b

banana/message.inc.php
banana/page.inc.php
banana/spool.inc.php
banana/templates/banana-thread.inc.tpl

index 51999cb..efe8f7a 100644 (file)
@@ -228,22 +228,14 @@ final class BananaMessage extends BananaMimePart
         return BananaMessage::formatReferences($this->headers);
     }
 
-    static public function formatReferences(array &$refs)
+    static public function &formatReferences(array &$refs)
     {
-        if (isset($refs['references'])) {
-            $text = preg_split('/\s/', str_replace('><', '> <', $refs['references']));
-            $references = array();
-            foreach ($text as $id=>&$value) {
-                if (isset(Banana::$spool->ids[$value])) {
-                    $references[] = Banana::$spool->ids[$value];
-                }
-            }
-            return $references;
-        } elseif (isset($refs['in-reply-to']) && isset(Banana::$spool->ids[$refs['in-reply-to']])) {
-            return array(Banana::$spool->ids[$refs['in-reply-to']]);
-        } else {
-            return array();
+        $references = array();
+        $msgs = Banana::$spool->getReferences($refs);
+        foreach ($msgs as &$msg) {
+            $references[] = $msg->id;
         }
+        return $references;
     }
 
     public function hasXFace()
index b325330..6e126dd 100644 (file)
@@ -24,7 +24,7 @@ class BananaPage extends Smarty
 
     public function __construct()
     {
-        $this->Smarty();
+        parent::Smarty();
 
         $this->compile_check = Banana::$debug_smarty;
         $this->template_dir  = dirname(__FILE__) . '/templates/';
@@ -67,7 +67,7 @@ class BananaPage extends Smarty
      * @param action_code HTML code of the action
      * @param pages ARRAY pages where to show the action (null == every pages)
      * @return true if success
-     */ 
+     */
     public function registerAction($action_code, array $pages = null)
     {
         $this->actions[] = array('text' => $action_code, 'pages' => $pages);
@@ -133,7 +133,7 @@ class BananaPage extends Smarty
 
         return 'banana-base.tpl';
     }
-    
+
     /** Generate XHTML code
      */
     public function run()
@@ -215,7 +215,7 @@ class BananaPage extends Smarty
      * @param params ARRAY location datas
      * @param smarty OBJECT Smarty instance associated (null if none)
      * @return URL of the page associated with the given parameters
-     * 
+     *
      * Usual parameters are :
      * - group : the box name
      * - artid : the current message id (index of message-id)
@@ -231,11 +231,11 @@ class BananaPage extends Smarty
         if (function_exists('hook_makeLink')
                 && $res = hook_makeLink($params)) {
             return $res;
-        }   
+        }
         $proto = empty($_SERVER['HTTPS']) ? 'http://' : 'https://';
         $host  = Banana::$baseurl ? Banana::$baseurl : $_SERVER['SERVER_NAME'];
         $file  = $_SERVER['PHP_SELF'];
-    
+
         if (count($params) != 0) {
             $get = '?';
             foreach ($params as $key=>$value) {
@@ -246,7 +246,7 @@ class BananaPage extends Smarty
             }
         } else {
             $get = '';
-        }     
+        }
         return $proto . $host . $file . $get;
     }
 
@@ -334,7 +334,7 @@ class BananaPage extends Smarty
 
         return '<img src="' . $url . '"' . $height . $width . ' alt="' . _b_($alt) . '" />';
     }
-    
+
     /** Build a link to one of the banana built-in javascript
      * @param src STRING javascript name
      * @return Javascript tag
@@ -356,7 +356,7 @@ class BananaPage extends Smarty
 
         return '<script type="text/javascript" src="' . $url . '"/></script>';
     }
-    
+
     /** Build a link with an image as text
      * @param params ARRAY image and location data
      * @param smarty OBJECT Smarty instance associated (null if none)
@@ -371,7 +371,7 @@ class BananaPage extends Smarty
     {
         if (!isset($params['popup'])) {
             $params['popup'] = @$params['alt'];
-        }    
+        }
         $img = $this->makeImg($params, $smarty);
         if (isset($params['text'])) {
             $img .= ' ' . $params['text'];
index e65d05a..745ecd7 100644 (file)
@@ -9,13 +9,16 @@
 
 require_once dirname(__FILE__) . '/banana.inc.php';
 
-define('BANANA_SPOOL_VERSION', '0.5.9');
+define('BANANA_SPOOL_VERSION', '0.5.12');
 
 /** Class spoolhead
  *  class used in thread overviews
  */
 class BananaSpoolHead
 {
+    public $id;
+    public $msgid;
+
     /** date (timestamp) */
     public $date;
     /** subject */
@@ -26,8 +29,6 @@ class BananaSpoolHead
     public $color;
     /** reference of parent */
     public $parent = null;
-    /** paren is direct */
-    public $parent_direct;
     /** array of children */
     public $children = Array();
     /** true if post is read */
@@ -48,8 +49,10 @@ class BananaSpoolHead
      * @param $_read BOOLEAN true if read
      * @param $_descunread INTEGER descunread value (0 for a new post)
      */
-    public function __construct(array &$message)
+    public function __construct($id, array &$message)
     {
+        $this->id         = $id;
+        $this->msgid      = $message['message-id'];
         $this->date       = $message['date'];
         $this->subject    = $message['subject'];
         $this->from       = $message['from'];
@@ -83,13 +86,13 @@ class BananaSpool
     /**  group name */
     public $group;
     /**  spool */
-    public $overview;
+    public $overview = array();
     /**  array msgid => msgnum */
-    public $ids;
+    public $ids      = array();
     /** thread starts */
-    public $roots;
+    public $roots    = array();
     /** thread trees (one tree per root node) */
-    public $trees = array();
+    public $trees    = array();
 
     /** protocole specific data */
     public $storage = array();
@@ -124,6 +127,7 @@ class BananaSpool
             $spool->markAllAsRead();
         }
         $spool->updateUnread($since);
+        //var_dump($spool);
         return $spool;
     }
 
@@ -152,9 +156,9 @@ class BananaSpool
         return $spool;
     }
 
-    private function compare($a, $b)
+    private function compare(&$a, &$b)
     {
-        return ($this->overview[$b]->date >= $this->overview[$a]->date);
+        return ($b->date - $a->date);
     }
 
     private function saveToFile()
@@ -162,9 +166,9 @@ class BananaSpool
         $file = BananaSpool::spoolFilename($this->group);
 
         $this->roots = Array();
-        foreach($this->overview as $id=>&$msg) {
+        foreach($this->overview as &$msg) {
             if (is_null($msg->parent)) {
-                $this->roots[] = $id;
+                $this->roots[] =& $msg;
             }
         }
         usort($this->roots, array($this, 'compare'));
@@ -202,7 +206,7 @@ class BananaSpool
     private function clean(&$first, &$last, $msgnum)
     {
         $do_save = false;
-        if (is_array($this->overview)) {
+        if (!empty($this->overview)) {
             $mids = array_keys($this->overview);
             foreach ($mids as $id) {
                 if (($first <= $last && ($id < $first || $id > $last))
@@ -227,44 +231,38 @@ class BananaSpool
         $messages =& Banana::$protocole->getMessageHeaders($first, $last,
             array('Date', 'Subject', 'From', 'Message-ID', 'References', 'In-Reply-To'));
 
-        if (!is_array($this->ids)) {
-            $this->ids = array();
-        }
+        // Build all the new Spool Heads
         foreach ($messages as $id=>&$message) {
-            $this->ids[$message['message-id']] = $id;
+            if (!isset($this->overview[$id])) {
+                $this->overview[$id] = new BananaSpoolHead($id, $message);
+                $head =& $this->overview[$id];
+                $this->ids[$head->msgid] =& $head;
+            }
         }
 
-        if (!is_array($this->overview)) {
-            $this->overview = array();
-        }
+        // Build tree
         $updateTrees = array();
+        $null = null;
         foreach ($messages as $id=>&$message) {
-            if (!isset($this->overview[$id])) {
-                $this->overview[$id] = new BananaSpoolHead($message);
-            }
-            $msg    =& $this->overview[$id];
-            $msgrefs = BananaMessage::formatReferences($message);
-            $parents = preg_grep('/^\d+$/', $msgrefs);
-            $msg->parent = array_pop($parents);
-            $msg->parent_direct = preg_match('/^\d+$/', array_pop($msgrefs));
-
-            if (!is_null($p = $msg->parent)) {
-                if (empty($this->overview[$p])) {
-                    $this->overview[$p] = new BananaSpoolHead($messages[$p]);
-                }
-                $this->overview[$p]->children[] = $id;
-                while (!is_null($p)) {
-                    $this->overview[$p]->desc += $msg->desc;
-                    $prev = $p;
-                    if ($p != $this->overview[$p]->parent) {
-                        $p = $this->overview[$p]->parent;
+            $msg         =& $this->overview[$id];
+            $parents     =& $this->getReferences($message);
+            while (!empty($parents) && ($msg->parent === $msg || is_null($msg->parent))) {
+                $msg->parent =& array_pop($parents);
+            }
+
+            if (!is_null($msg->parent)) {
+                $parent =& $msg->parent;
+                $parent->children[] =& $msg;
+                while (!is_null($parent)) {
+                    $parent->desc += $msg->desc;
+                    $prev =& $p;
+                    if ($parent !== $parent->parent) {
+                        $parent =& $parent->parent;
                     } else {
-                        $p = null;
-                    }
-                    if (is_null($p)) {
-                        $updateTrees[$prev] = true;
+                        $parent =& $null;
                     }
                 }
+                $updateTrees[$prev->id] = true;
             }
         }
         foreach ($updateTrees as $root=>$t) {
@@ -286,21 +284,18 @@ class BananaSpool
             return;
         }
 
-        if (!is_array($this->ids)) {
-            $this->ids = array();
-        }
         $newpostsids = array_intersect($newpostsids, array_keys($this->ids));
         foreach ($newpostsids as $mid) {
-            $id = $this->ids[$mid];
-            if ($this->overview[$id]->isread) {
-                $this->overview[$id]->isread = false;
-                $this->unreadnb++;
-                while (isset($id)) {
-                    $this->overview[$id]->descunread++;
-                    $id = $this->overview[$id]->parent;
+            $overview =& $this->ids[$mid];
+            if ($overview->isread) {
+                $overview->isread = false;
+                while (!is_null($overview)) {
+                    $overview->descunread++;
+                    $overview =& $overview->parent;
                 }
             }
         }
+        $this->unreadnb += count($newpostsids);
     }
 
     public function setMode($mode)
@@ -312,16 +307,33 @@ class BananaSpool
             if ($this->overview[$num]->isread) {
                 break;
             }
-            foreach ($this->roots as $k=>$i) {
-                if ($this->overview[$i]->descunread == 0) {
-                    $this->killdesc($i);
-                    unset($this->roots[$k]);
+            foreach ($this->roots as &$root) {
+                if ($root->descunread == 0) {
+                    $this->killdesc($root->id);
                 }
             }
             break;
         }
     }
 
+    /** Fetch list of references
+     */
+    public function &getReferences(array &$refs)
+    {
+        $references = array();
+        if (isset($refs['references'])) {
+            $text = preg_split('/\s/', str_replace('><', '> <', $refs['references']));
+            foreach ($text as $id=>&$value) {
+                if (isset($this->ids[$value])) {
+                    $references[] =& $this->ids[$value];
+                }
+            }
+        } elseif (isset($refs['in-reply-to']) && isset($this->ids[$refs['in-reply-to']])) {
+            $references[] =& $this->ids[$refs['in-reply-to']];
+        }
+        return $references;
+    }
+
     /** Mark the given id as read
      * @param id MSGNUM of post
      */
@@ -344,20 +356,20 @@ class BananaSpool
         if (!$this->unreadnb) {
             return;
         }
-        if (is_null($array) && is_array($this->roots)) {
+        if (is_null($array) && !empty($this->roots)) {
             $array =& $this->roots;
         } elseif (is_null($array)) {
             return;
         }
-        foreach ($array as $id) {
-            if (!$this->overview[$id]->isread) {
-                $this->markAsRead($id);
+        foreach ($array as &$msg) {
+            if (!$msg->isread) {
+                $this->markAsRead($msg->id);
                 if (!$this->unreadnb) {
                     return;
                 }
             }
-            if ($this->overview[$id]->descunread) {
-                $this->markAllAsRead($this->overview[$id]->children);
+            if ($msg->descunread) {
+                $this->markAllAsRead($msg->children);
             }
         }
     }
@@ -367,15 +379,23 @@ class BananaSpool
      */
     private function killdesc($_id)
     {
-        if (sizeof($this->overview[$_id]->children)) {
-            foreach ($this->overview[$_id]->children as $c) {
-                $this->killdesc($c);
+        $overview =& $this->overview[$_id];
+        $children =& $overview->children;
+        if (sizeof($children)) {
+            foreach ($children as &$c) {
+                $this->killdesc($c->id);
             }
         }
         unset($this->overview[$_id]);
-        if (($msgid = array_search($_id, $this->ids)) !== false) {
-            unset($this->ids[$msgid]);
+        foreach ($this->roots as $k=>&$root) {
+            if ($root === $overview) {
+                unset($this->roots[$k]);
+                break;
+            }
         }
+        unset($this->ids[$overview->msgid]);
+        unset($this->trees[$_id]);
+        $overview = null;
     }
 
     /** delete a post from overview
@@ -383,51 +403,58 @@ class BananaSpool
      */
     public function delid($_id, $write = true)
     {
-        if (isset($this->overview[$_id])) {
-            $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;
-                    }
-                }
-                while (isset($p)) {
-                    $this->overview[$p]->desc--;
-                    $p = $this->overview[$p]->parent;
-                }
-            } elseif ($overview->children) {
-                foreach ($overview->children as $c) {
-                    $this->overview[$c]->parent = null;
+        if (!isset($this->overview[$_id])) {
+            return;
+        }
+
+        $overview =& $this->overview[$_id];
+        // Be sure it is not counted as unread
+        if (!$overview->isread) {
+            $this->markAsRead($_id);
+        }
+
+        $parent =& $overview->parent;
+
+        // Remove from the message tree
+        if (!is_null($parent)) {
+            foreach ($parent->children as $key=>&$child) {
+                if ($child === $overview) {
+                    unset($parent->children[$key]);
+                    break;
                 }
             }
-            unset($overview);
-            unset($this->overview[$_id]);
-            unset($this->trees[$_id]);
-            $msgid = array_search($_id, $this->ids);
-            if ($msgid !== false) {
-                unset($this->ids[$msgid]);
+            if (sizeof($overview->children)) {
+                $parent->children = array_merge($parent->children, $overview->children);
+                foreach ($overview->children as &$child) {
+                    $child->parent =& $parent;
+                }
             }
-            $msgid = array_search($_id, $this->roots);
-            if ($msgid !== false) {
-                unset($this->roots[$msgid]);
+            while (!is_null($parent)) {
+                $parent->desc--;
+                $parent =& $parent->parent;
             }
+        }
 
-            if ($write) {
-                $this->saveToFile();
+        // Remove all refenrences and assign null to the object
+        unset($this->ids[$overview->msgid]);
+        unset($this->overview[$_id]);
+        unset($this->trees[$_id]);
+        foreach ($this->roots as $k=>&$root) {
+            if ($root === $overview) {
+                unset($this->roots[$k]);
+                break;
             }
         }
+        $overview = null;
+
+        if ($write) {
+            $this->saveToFile();
+        }
     }
 
-    public function formatDate($stamp)
+    public function formatDate(BananaSpoolHead &$head)
     {
+        $stamp  = $head->date;
         $today  = intval(time() / (24*3600));
         $dday   = intval($stamp / (24*3600));
 
@@ -445,9 +472,9 @@ class BananaSpool
         return strftime($format, $stamp);
     }
 
-    public function formatSubject($id, $subject)
+    public function formatSubject(BananaSpoolHead &$head)
     {
-        $subject = banana_html_entity_decode($subject);
+        $subject = $popup = $head->subject;
         $popup = $subject;
         if (function_exists('hook_formatDisplayHeader')) {
             list($subject, $link) = hook_formatDisplayHeader('subject', $subject, true);
@@ -458,17 +485,16 @@ class BananaSpool
         if (empty($subject)) {
             $subject = _b_('(pas de sujet)');
         }
-        if ($id != Banana::$artid) {
-            $subject = Banana::$page->makeLink(Array('group' => $this->group, 'artid' => $id,
+        if ($head->id != Banana::$artid) {
+            $subject = Banana::$page->makeLink(Array('group' => $this->group, 'artid' => $head->id,
                                                      'text'  => $subject, 'popup' => $popup));
         }
         return $subject . $link;
     }
 
-    public function formatFrom($from)
+    public function formatFrom(BananaSpoolHead &$head)
     {
-        $from = banana_html_entity_decode($from);
-        return BananaMessage::formatFrom($from);
+        return BananaMessage::formatFrom($head->from);
     }
 
     public function start()
@@ -487,7 +513,7 @@ class BananaSpool
     }
 
 
-    private function &_buildTree($id, BananaSpoolHead &$head) {
+    private function &_buildTree(BananaSpoolHead &$head) {
         static $t_e, $u_h, $u_ht, $u_vt, $u_l, $u_f, $r_h, $r_ht, $r_vt, $r_l, $r_f;
         if (!isset($spfx_f)) {
             $t_e   = Banana::$page->makeImg(Array('img' => 'e',  'alt' => '&nbsp;', 'height' => 18,  'width' => 14));
@@ -503,16 +529,15 @@ class BananaSpool
             $r_f   = Banana::$page->makeImg(Array('img' => 'f2r', 'alt' => 't', 'height' => 18, 'width' => 14));
         }
         $style = 'background-color:' . $head->color . '; text-decoration: none';
-        $text = '<span style="' . $style . '" title="' . banana_entities($head->name . ', ' . $this->formatDate($head->date))
+        $text = '<span style="' . $style . '" title="' . banana_entities($head->name . ', ' . $this->formatDate($head))
               . '"><input type="radio" name="banana_tree" '
               . (Banana::$msgshow_javascript ? 'onchange="window.location=\'' .
                     banana_entities(Banana::$page->makeURL(array('group' => $this->group, 'artid' => $id))) . '\'"'
                     : ' disabled="disabled"')
               . ' /></span>';
         $array = array($text);
-        foreach ($head->children as $key=>&$child) {
-            $msg =& $this->overview[$child];
-            $tree =& $this->_buildTree($child, $msg);
+        foreach ($head->children as $key=>&$msg) {
+            $tree =& $this->_buildTree($msg);
             $last = $key == count($head->children) - 1;
             foreach ($tree as $kt=>&$line) {
                 if ($kt === 0 && $key === 0 && !$last) {
@@ -541,7 +566,7 @@ class BananaSpool
         if (!$force && isset($this->trees[$id])) {
             return $this->trees[$id];
         } else {
-            $tree =& $this->_buildTree($id, $this->overview[$id]);
+            $tree =& $this->_buildTree($this->overview[$id]);
             $tree = '<div class="tree"><div style="height:18px">'
                   . implode("</div>\n<div style=\"height:18px\">", $tree)
                   . '</div></div>';
index 3729385..91a7098 100644 (file)
   </tr>
   {if $spool->roots|@count}
   {section name=threads loop=$spool->roots step=1 start=$spool->start() max=$spool->context()}
-  {assign var=id value=$spool->roots[$smarty.section.threads.index]}
-  {assign var=overview value=$spool->overview[$id]}
+  {assign var=overview value=$spool->roots[$smarty.section.threads.index]}
+  {assign var=id value=$overview->id}
   {cycle assign=class values="impair,pair"}
   <tr class="{$class} {if $overview->descunread}new{/if}">
-    <td class="date">{$spool->formatDate($overview->date)}</td>
-    <td class="subj">{$spool->formatSubject($id, $overview->subject)|smarty:nodefaults}</td>
-    <td class="from">{$spool->formatFrom($overview->from)|smarty:nodefaults}</td>
+    <td class="date">{$spool->formatDate($overview)}</td>
+    <td class="subj">{$spool->formatSubject($overview)|smarty:nodefaults}</td>
+    <td class="from">{$spool->formatFrom($overview)|smarty:nodefaults}</td>
   </tr>
   {if !$artid && $spool->nextPost($id)}
   <tr class="{$class}">