X-Git-Url: http://git.polytechnique.org/?a=blobdiff_plain;f=banana%2Fspool.inc.php;h=ace19f7277dde077404d0aba500faba7a184384c;hb=ade32c5593c555591eb5cf859ea64b8ef7fdaaf8;hp=e681a407065054bf4b3bf806fe62091645e279be;hpb=d55883182213760e1938fca4970fb693b6512d62;p=banana.git diff --git a/banana/spool.inc.php b/banana/spool.inc.php index e681a40..ace19f7 100644 --- a/banana/spool.inc.php +++ b/banana/spool.inc.php @@ -7,44 +7,42 @@ * Copyright: See COPYING files that comes with this distribution ********************************************************************************/ -if(!function_exists('file_put_contents')) { - function file_put_contents($filename, $data) - { - $fp = fopen($filename, 'w'); - if(!$fp) { - trigger_error('file_put_contents cannot write in file '.$filename, E_USER_ERROR); - return; - } - fputs($fp, $data); - fclose($fp); - } -} +require_once dirname(__FILE__) . '/banana.inc.php'; +require_once dirname(__FILE__) . '/tree.inc.php'; -function spoolCompare($a,$b) { return ($b->date>=$a->date); } +define('BANANA_SPOOL_VERSION', '0.5.14'); /** Class spoolhead * class used in thread overviews */ class BananaSpoolHead { + public $id; + public $msgid; + /** date (timestamp) */ - var $date; + public $date; /** subject */ - var $subject; + public $subject; /** author */ - var $from; + public $from; + public $name; + public $color; /** reference of parent */ - var $parent; - /** paren is direct */ - var $parent_direct; + public $parent = null; /** array of children */ - var $children = Array(); + public $children = Array(); /** true if post is read */ - var $isread; + public $isread; /** number of posts deeper in this branch of tree */ - var $desc; + public $desc; /** same as desc, but counts only unread posts */ - var $descunread; + public $descunread; + /** last time the number of children has been updated */ + public $time = 0; + + /** storage data */ + public $storage = array(); /** constructor * @param $_date INTEGER timestamp of post @@ -54,65 +52,164 @@ class BananaSpoolHead * @param $_read BOOLEAN true if read * @param $_descunread INTEGER descunread value (0 for a new post) */ - - function BananaSpoolHead($_date, $_subject, $_from, $_desc=1, $_read=true, $_descunread=0) + public function __construct($id, array &$message) { - $this->date = $_date; - $this->subject = $_subject; - $this->from = $_from; - $this->desc = $_desc; - $this->isread = $_read; - $this->descunread = $_descunread; + list($name, $from) = BananaMessage::extractMail($message['from']); + $this->id = $id; + $this->msgid = @$message['message-id']; + $this->date = $message['date']; + $this->subject = @$message['subject']; + $this->from = $message['from']; + $this->color = sprintf('#%06x', abs(crc32($from) % 0xffffff)); + $this->desc = 1; + $this->isread = true; + $this->descunread = 0; + $this->name = $name; + if ($name === $from) { + $this->name = 'Anonymous'; + } } } -/** Class spool - * builds and updates spool - */ - -define("BANANA_SPOOL_VERSION", '0.2'); class BananaSpool { - var $version; - /** spool */ - var $overview; + private $version; + private $mode; + /** group name */ - var $group; + public $group; + /** spool */ + public $overview = array(); /** array msgid => msgnum */ - var $ids; + public $ids = array(); /** thread starts */ - var $roots; - /** test validity */ - var $valid = true; + public $roots = array(); + + /** protocole specific data */ + public $storage = array(); + + private $unreadnb = 0; /** constructor * @param $_group STRING group name * @param $_display INTEGER 1 => all posts, 2 => only threads with new posts * @param $_since INTEGER time stamp (used for read/unread) */ - function BananaSpool($_group, $_display=0, $_since="") + protected function __construct($group) { - global $banana; - $this->group = $_group; - $groupinfo = $banana->nntp->group($_group); - if (!$groupinfo) { - $this->valid = false; - return null; + $this->version = BANANA_SPOOL_VERSION; + $this->mode = Banana::SPOOL_ALL; + $this->group = $group; + } + + public static function &getSpool($group, $since = 0, $clean = false) + { + if (!is_null(Banana::$spool) && Banana::$spool->group == $group) { + $spool =& Banana::$spool; + } else { + $spool =& BananaSpool::readFromFile($group); } + if (is_null($spool)) { + $spool = new BananaSpool($group); + } + Banana::$spool =& $spool; + $spool->build(); + if ($clean) { + $spool->markAllAsRead(); + } + $spool->updateUnread($since); + return $spool; + } - $this->_readFromFile(); + public static function getPath($file = null) + { + $path = Banana::$spool_root . '/' . Banana::$protocole->name() . '/' . Banana::$protocole->filename(); + if (!is_dir($path)) { + if (file_exists($path)) { + @unlink($path); + } + mkdir($path, 0777, true); + } + return $path . '/' . $file; + } - $do_save = false; - $first = $banana->maxspool ? max($groupinfo[2] - $banana->maxspool, $groupinfo[1]) : $groupinfo[1]; - $last = $groupinfo[2]; + private static function spoolFilename() + { + return BananaSpool::getPath('spool'); + } + + private static function &readFromFile($group) + { + $spool = null; + $file = BananaSpool::spoolFilename(); + if (!file_exists($file)) { + return $spool; + } + $spool = unserialize(file_get_contents($file)); + if ($spool->version != BANANA_SPOOL_VERSION || $spool->mode != Banana::SPOOL_ALL) { + $spool = null; + return $spool; + } + $spool->markAllAsRead(); + return $spool; + } + + private function compare($a, $b) + { + return ($b->date - $a->date); + } + + private function saveToFile() + { + $file = BananaSpool::spoolFilename(); + + $this->roots = Array(); + foreach($this->overview as &$msg) { + if (is_null($msg->parent)) { + $this->roots[] =& $msg; + } + } + usort($this->roots, array($this, 'compare')); + + if ($this->mode == Banana::SPOOL_ALL) { + file_put_contents($file, serialize($this)); + } + } + + private function build() + { + $threshold = 0; + + // Compute the range of indexes + list($msgnum, $first, $last) = Banana::$protocole->getIndexes(); + if ($last < $first) { + $threshold = $first + $msgnum - $last; + $threshold = (int)(log($threshold)/log(2)); + $threshold = (2 ^ ($threshold + 1)) - 1; + } + if (Banana::$spool_max && Banana::$spool_max < $msgnum) { + $first = $last - Banana::$spool_max; + if ($first < 0) { + $first += $threshold; + } + } + $clean = $this->clean($first, $last, $msgnum); + $update = $this->update($first, $last, $msgnum); - if ($this->version == BANANA_SPOOL_VERSION && is_array($this->overview)) { + if ($clean || $update) { + $this->saveToFile(); + } + } + + private function clean(&$first, &$last, $msgnum) + { + $do_save = false; + if (!empty($this->overview)) { $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; } @@ -120,323 +217,518 @@ class BananaSpool if (!empty($this->overview)) { $first = max(array_keys($this->overview))+1; } - } else { - unset($this->overview, $this->ids); - $this->version = BANANA_SPOOL_VERSION; - } - - if ($first<=$last && $groupinfo[0]) { - $do_save = true; - $this->_updateSpool("$first-$last"); } - - if ($do_save) { $this->_saveToFile(); } - - $this->_updateUnread($_since, $_display); + return $do_save; } - function _readFromFile() + private function update(&$first, &$last, $msgnum) { - $file = $this->_spoolfile(); - if (file_exists($file)) { - $temp = unserialize(file_get_contents($file)); - foreach (get_object_vars($temp) as $key=>$val) { - $this->$key = $val; + if ($first > $last || !$msgnum) { + return false; + } + + $messages =& Banana::$protocole->getMessageHeaders($first, $last, + array('Date', 'Subject', 'From', 'Message-ID', 'References', 'In-Reply-To')); + + // Build all the new Spool Heads + $time = time(); + foreach ($messages as $id=>&$message) { + if (!isset($this->overview[$id])) { + $this->overview[$id] = new BananaSpoolHead($id, $message); + $head =& $this->overview[$id]; + $this->ids[$head->msgid] =& $head; + $head->time = $time; } } - } - function _saveToFile() - { - $file = $this->_spoolfile(); - uasort($this->overview, "spoolcompare"); + // Build tree + $null = null; + foreach ($messages as $id=>&$message) { + $msg =& $this->overview[$id]; + $parents =& $this->getReferences($message); + while (!empty($parents) && ($msg->parent === $msg || is_null($msg->parent))) { + @$msg->parent =& array_pop($parents); + } - $this->roots = Array(); - foreach($this->overview as $id=>$msg) { - if (is_null($msg->parent)) { - $this->roots[] = $id; + if (!is_null($msg->parent)) { + $parent =& $msg->parent; + $parent->children[] =& $msg; + while (!is_null($parent)) { + $parent->desc += $msg->desc; + $parent->time = $time; + $prev =& $parent; + if ($parent !== $parent->parent) { + $parent =& $parent->parent; + } else { + $parent =& $null; + } + } } } - - file_put_contents($file, serialize($this)); + Banana::$protocole->updateSpool($messages); + return true; } - function _spoolfile() + public function updateUnread($since) { - global $banana; - $url = parse_url($banana->host); - $file = $url['host'].'_'.$url['port'].'_'.$this->group; - return dirname(dirname(__FILE__)).'/spool/'.$file; - } + if (empty($since)) { + return; + } - function _updateSpool($arg) - { - global $banana; - $dates = array_map('strtotime', $banana->nntp->xhdr('Date', $arg)); - $subjects = array_map('headerdecode', $banana->nntp->xhdr('Subject', $arg)); - $froms = array_map('headerdecode', $banana->nntp->xhdr('From', $arg)); - $msgids = $banana->nntp->xhdr('Message-ID', $arg); - $refs = $banana->nntp->xhdr('References', $arg); + $newpostsids = Banana::$protocole->getNewIndexes($since); - if (is_array($this->ids)) { - $this->ids = array_merge($this->ids, array_flip($msgids)); - } else { - $this->ids = array_flip($msgids); + if (empty($newpostsids)) { + return; } - foreach ($msgids as $id=>$msgid) { - $msg = new BananaSpoolHead($dates[$id], $subjects[$id], $froms[$id]); - $refs[$id] = str_replace('><', '> <', $refs[$id]); - $msgrefs = preg_split("/[ \t]/", strtr($refs[$id], $this->ids)); - $parents = preg_grep('/^\d+$/', $msgrefs); - $msg->parent = array_pop($parents); - $msg->parent_direct = preg_match('/^\d+$/', array_pop($msgrefs)); + $newpostsids = array_intersect($newpostsids, array_keys($this->ids)); + foreach ($newpostsids as $mid) { + $overview =& $this->ids[$mid]; + if ($overview->isread) { + $overview->isread = false; + while (!is_null($overview)) { + $overview->descunread++; + $overview =& $overview->parent; + } + } + } + $this->unreadnb += count($newpostsids); - if (isset($this->overview[$id])) { - $msg->desc = $this->overview[$id]->desc; - $msg->children = $this->overview[$id]->children; + if (function_exists('hook_listReadMessages')) { + $msgs = hook_listReadMessages($this->group); + if (!is_array($msgs)) { + return; } - $this->overview[$id] = $msg; + foreach ($msgs as $msg) { + if (!is_numeric($msg)) { + if (!isset($this->ids[$msg])) { + continue; + } + $msg = $this->ids[$msg]->id; + } + $this->markAsRead($msg); + } + } + } - if ($p = $msg->parent) { - if (empty($this->overview[$p])) { - $this->overview[$p] = new BananaSpoolHead($dates[$p], $subjects[$p], $froms[$p], 1); + public function setMode($mode) + { + $this->mode = $mode; + switch ($mode) { + case Banana::SPOOL_UNREAD: + $num = max(array_keys($this->overview)); + if ($this->overview[$num]->isread) { + break; + } + foreach ($this->roots as &$root) { + if ($root->descunread == 0) { + $this->killdesc($root->id); } - $this->overview[$p]->children[] = $id; + } + break; + } + } - while ($p) { - $this->overview[$p]->desc += $msg->desc; - $p = $this->overview[$p]->parent; + /** 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; } - function _updateUnread($since, $mode) + /** Get the tree associated to a given id + */ + public function &getTree($id) { - global $banana; - if (empty($since)) { return; } + return BananaTree::build($id)->show(); + } - if (is_array($newpostsids = $banana->nntp->newnews($since, $this->group))) { - if (!is_array($this->ids)) { $this->ids = array(); } - $newpostsids = array_intersect($newpostsids, array_keys($this->ids)); - foreach ($newpostsids as $mid) { - $this->overview[$this->ids[$mid]]->isread = false; - $this->overview[$this->ids[$mid]]->descunread = 1; - $parentmid = $this->ids[$mid]; - while (isset($parentmid)) { - $this->overview[$parentmid]->descunread ++; - $parentmid = $this->overview[$parentmid]->parent; - } + /** Mark the given id as read + * @param id MSGNUM of post + */ + public function markAsRead($id) + { + $overview =& $this->overview[$id]; + if (!$overview->isread) { + $overview->isread = true; + $this->unreadnb--; + while (!is_null($overview)) { + $overview->descunread--; + $overview =& $overview->parent; } + } + } - if (count($newpostsids)) { - switch ($mode) { - case 1: - foreach ($this->roots as $k=>$i) { - if ($this->overview[$i]->descunread==0) { - $this->killdesc($i); - unset($this->roots[$k]); - } - } - break; + /** Mark all unread messages as read + */ + public function markAllAsRead(array &$array = null) + { + if (!$this->unreadnb) { + return; + } + if (is_null($array) && !empty($this->roots)) { + $array =& $this->roots; + } elseif (is_null($array)) { + return; + } + foreach ($array as &$msg) { + if (!$msg->isread) { + $this->markAsRead($msg->id); + if (!$this->unreadnb) { + return; } } + if ($msg->descunread) { + $this->markAllAsRead($msg->children); + } } } /** kill post and childrens * @param $_id MSGNUM of post */ - - function killdesc($_id) + 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]); + $overview = null; } /** delete a post from overview * @param $_id MSGNUM of post */ + public function delid($_id, $write = true) + { + if (!isset($this->overview[$_id])) { + return; + } - 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; - $this->overview[$c]->parent_direct = false; - } - } - $p = $this->overview[$_id]->parent; - while ($p) { - $this->overview[$p]->desc--; - $p = $this->overview[$p]->parent; + $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)) { + $time = time(); + foreach ($parent->children as $key=>&$child) { + if ($child === $overview) { + unset($parent->children[$key]); + break; } - } elseif (sizeof($this->overview[$_id]->children)) { - foreach ($this->overview[$_id]->children as $c) { - $this->overview[$c]->parent = null; + } + if (sizeof($overview->children)) { + foreach ($overview->children as &$child) { + $parent->children[] =& $child; + $child->time = $time; + $child->parent =& $parent; } } - unset($this->overview[$_id]); - $msgid = array_search($_id, $this->ids); - if ($msgid) { - unset($this->ids[$msgid]); + while (!is_null($parent)) { + $parent->desc--; + $parent->time = $time; + $parent =& $parent->parent; } - - if ($write) { $this->_saveToFile(); } } - } - /** displays children tree of a post - * @param $_id INTEGER MSGNUM of post - * @param $_index INTEGER linear number of post in the tree - * @param $_first INTEGER linear number of first post displayed - * @param $_last INTEGER linear number of last post displayed - * @param $_ref STRING MSGNUM of current post - * @param $_pfx_node STRING prefix used for current node - * @param $_pfx_end STRING prefix used for children of current node - * @param $_head BOOLEAN true if first post in thread - */ + // Remove all refenrences and assign null to the object + unset($this->ids[$overview->msgid]); + unset($this->overview[$_id]); + BananaTree::kill($_id); + foreach ($this->roots as $k=>&$root) { + if ($root === $overview) { + unset($this->roots[$k]); + break; + } + } + $overview = null; - function _to_html($_id, $_index, $_first=0, $_last=0, $_ref="", $_pfx_node="", $_pfx_end="", $_head=true) - { - $spfx_f = 'o'; - $spfx_n = '*'; - $spfx_Tnd = '+'; - $spfx_Lnd = '`'; - $spfx_snd = '-'; - $spfx_T = '+'; - $spfx_L = '`'; - $spfx_s = '-'; - $spfx_e = ' '; - $spfx_I = '|'; + if ($write) { + $this->saveToFile(); + } + } - if ($_index + $this->overview[$_id]->desc < $_first || $_index > $_last) { - return; + public function formatDate(BananaSpoolHead $head) + { + $stamp = $head->date; + $today = intval(time() / (24*3600)); + $dday = intval($stamp / (24*3600)); + + if ($today == $dday) { + $format = "%H:%M"; + } elseif ($today == 1 + $dday) { + $format = _b_('hier')." %H:%M"; + } elseif ($today < 7 + $dday) { + $format = '%a %H:%M'; + } elseif ($today < 90 + $dday) { + $format = '%a %e %b'; + } else { + $format = '%a %e %b %Y'; } + return strftime($format, $stamp); + } - $res = ''; + public function formatSubject(BananaSpoolHead $head) + { + $subject = $popup = $head->subject; + $popup = $subject; + if (function_exists('hook_formatDisplayHeader')) { + list($subject, $link) = hook_formatDisplayHeader('subject', $subject, true); + } else { + $subject = banana_catchFormats(banana_entities(stripslashes($subject))); + $link = null; + } + if (empty($subject)) { + $subject = _b_('(pas de sujet)'); + } + if (mb_strlen($subject) > 100) { + $subject = mb_substr($subject, 0, 99) . '…'; + } + if ($head->id !== Banana::$artid) { + $subject = Banana::$page->makeLink(Array('group' => $this->group, 'artid' => $head->id, + 'text' => $subject, 'popup' => $popup)); + } + return $subject . $link; + } - if ($_index>=$_first) { - $hc = empty($this->overview[$_id]->children); + public function formatFrom(BananaSpoolHead $head) + { + return BananaMessage::formatFrom($head->from); + } - $res .= '\n"; - $res .= "".fancyDate($this->overview[$_id]->date)." \n"; - $res .= "" - ."
$_pfx_node".($hc?($_head?$spfx_f:($this->overview[$_id]->parent_direct?$spfx_s:$spfx_snd)):$spfx_n) - ."
"; - $subject = $this->overview[$_id]->subject; - if (strlen($subject) == 0) { - $subject = _b_('(pas de sujet)'); - } - if ($_index == $_ref) { - $res .= ''.htmlentities($subject).''; - } else { - $res .= makeHREF(Array('group' => $this->group, - 'artid' => $_id), - htmlentities($subject)); - } - $res .= "\n".formatFrom($this->overview[$_id]->from)."\n"; + public function start() + { + if (Banana::$first) { + return Banana::$first; + } else { + $first = array_search(Banana::$artid, $this->roots); + return max(0, $first - Banana::$spool_tbefore); + } + } - if ($hc) { return $res; } - } + public function context() + { + return Banana::$first ? Banana::$spool_tmax : Banana::$spool_tcontext; + } - $_index ++; + /** Return root message of the given thread + * @param id INTEGER id of a message + */ + public function &root($id) + { + $parent =& $this->overview[$id]; + while (!is_null($parent->parent)) { + $parent =& $parent->parent; + } + return $parent; + } - $children = $this->overview[$_id]->children; - while ($child = array_shift($children)) { - if ($_index > $_last) { return $res; } - if ($_index+$this->overview[$child]->desc >= $_first) { - if (sizeof($children)) { - $res .= $this->_to_html($child, $_index, $_first, $_last, $_ref, - $_pfx_end.($this->overview[$child]->parent_direct?$spfx_T:$spfx_Tnd), - $_pfx_end.$spfx_I, false); + /** Return the last post id with the given subject + * @param subject + */ + public function getPostId($subject) + { + $subject = trim($subject); + $id = max(array_keys($this->overview)); + while (isset($this->overview[$id])) { + $test = $this->overview[$id]->subject; + if (function_exists('hook_formatDisplayHeader')) { + $val = hook_formatDisplayHeader('subject', $test, true); + if (is_array($val)) { + $test = banana_html_entity_decode($val[0]); } else { - $res .= $this->_to_html($child, $_index, $_first, $_last, $_ref, - $_pfx_end.($this->overview[$child]->parent_direct?$spfx_L:$spfx_Lnd), - $_pfx_end.$spfx_e, false); + $test = banana_html_entity_decode($val); } } - $_index += $this->overview[$child]->desc; + $test = trim($test); + if ($test == $subject) { + return $id; + } + $id--; } - - return $res; + return -1; } - /** Displays overview - * @param $_first INTEGER MSGNUM of first post - * @param $_last INTEGER MSGNUM of last post - * @param $_ref STRING MSGNUM of current/selectionned post + /** Returns previous thread root index + * @param id INTEGER message number */ - - function to_html($_first=0, $_last=0, $_ref = null) + public function prevThread($id) { - $res = ''; - - if (is_null($_ref)) { - $res .= ''; - $res .= ''; - $res .= ''; + $root =& $this->root($id); + $last = null; + foreach ($this->roots as &$i) { + if ($i === $root) { + return $last; + } + $last = $i->id; } + return $last; + } - $index = 1; - if (sizeof($this->overview)) { - foreach ($this->roots as $id) { - $res .= $this->_to_html($id, $index, $_first, $_last, $_ref); - $index += $this->overview[$id]->desc ; - if ($index > $_last) { break; } + /** Returns next thread root index + * @param id INTEGER message number + */ + public function nextThread($id) + { + $root =& $this->root($id); + $ok = false; + foreach ($this->roots as &$i) { + if ($ok) { + return $i->id; + } + if ($i === $root) { + $ok = true; } - } else { - $res .= ''; } - - return $res .= '
'._b_('Date').''._b_('Sujet').''._b_('Auteur').'
'._b_('Aucun message dans ce forum').'
'; + return null; } - /** computes linear post index - * @param $_id INTEGER MSGNUM of post - * @return INTEGER linear index of post + /** Return prev post in the thread + * @param id INTEGER message number */ + public function prevPost($id) + { + $parent =& $this->overview[$id]->parent; + if (is_null($parent)) { + return null; + } + $last = $parent->id; + foreach ($parent->children as &$child) { + if ($child->id == $id) { + return $last; + } + $last = $child->id; + } + return null; + } - function getndx($_id) + /** Return next post in the thread + * @param id INTEGER message number + */ + public function nextPost($id) { - $ndx = 1; - $id_cur = $_id; + $cur =& $this->overview[$id]; + if (count($cur->children) != 0) { + return $cur->children[0]->id; + } + + $parent =& $cur; while (true) { - $id_parent = $this->overview[$id_cur]->parent; - if (is_null($id_parent)) break; - $pos = array_search($id_cur, $this->overview[$id_parent]->children); - - for ($i = 0; $i < $pos ; $i++) { - $ndx += $this->overview[$this->overview[$id_parent]->children[$i]]->desc; + $parent =& $cur->parent; + if (is_null($parent)) { + return null; + } + $ok = false; + foreach ($parent->children as &$child) { + if ($ok) { + return $child->id; + } + if ($child === $cur) { + $ok = true; + } } - $ndx++; //noeud père + $cur =& $parent; + } + return null; + } - $id_cur = $id_parent; + /** Look for an unread message in the thread rooted by the message + * @param id INTEGER message number + */ + private function _nextUnread(BananaSpoolHead $cur) + { + if (!$cur->isread) { + return $cur->id; } + foreach ($cur->children as &$child) { + $unread = $this->_nextUnread($child); + if (!is_null($unread)) { + return $unread; + } + } + return null; + } - foreach ($this->roots as $i) { - if ($i==$id_cur) { - break; + /** Find next unread message + * @param id INTEGER message number + */ + 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) { + $next = $this->_nextUnread($child); + if (!is_null($next)) { + return $next; + } } - $ndx += $this->overview[$i]->desc; } - return $ndx; + + // Look in current thread + if (is_null($id)) { + $cur = null; + } else { + $cur =& $this->overview[$id]; + } + do { + if (is_null($cur)) { + $parent =& $cur; + $ok = true; + } else { + $parent =& $cur->parent; + $ok = false; + } + if (!is_null($parent)) { + $array =& $parent->children; + } else { + $array =& $this->roots; + } + foreach ($array as &$child) { + if ($ok) { + $next = $this->_nextUnread($child); + if (!is_null($next)) { + return $next; + } + } + if ($child === $cur) { + $ok = true; + } + } + $cur =& $parent; + } while(!is_null($cur)); + return null; } } - -// vim:set et sw=4 sts=4 ts=4 +// vim:set et sw=4 sts=4 ts=4 enc=utf-8: ?>