X-Git-Url: http://git.polytechnique.org/?a=blobdiff_plain;f=banana%2Fspool.inc.php;h=9de8df821ba939be6da903f7e7dcf2f2bcaf8197;hb=598a1c538cde55ef90fcacd7d09d36e025f64dee;hp=f387ebf934d152df7511fc21944e214523af69b6;hpb=4f75645f09e19699a0d4554b966378233b5b3c89;p=banana.git diff --git a/banana/spool.inc.php b/banana/spool.inc.php index f387ebf..9de8df8 100644 --- a/banana/spool.inc.php +++ b/banana/spool.inc.php @@ -7,20 +7,9 @@ * 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'; -function spoolCompare($a,$b) { return ($b->date>=$a->date); } +define('BANANA_SPOOL_VERSION', '0.3'); /** Class spoolhead * class used in thread overviews @@ -28,23 +17,26 @@ function spoolCompare($a,$b) { return ($b->date>=$a->date); } class BananaSpoolHead { /** date (timestamp) */ - var $date; + public $date; /** subject */ - var $subject; + public $subject; /** author */ - var $from; + public $from; /** reference of parent */ - var $parent; + public $parent = null; /** paren is direct */ - var $parent_direct; + public $parent_direct; /** 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; + + /** storage data */ + public $storage = array(); /** constructor * @param $_date INTEGER timestamp of post @@ -54,102 +46,93 @@ 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(array &$message) { - $this->date = $_date; - $this->subject = $_subject; - $this->from = $_from; - $this->desc = $_desc; - $this->isread = $_read; - $this->descunread = $_descunread; + $this->date = $message['date']; + $this->subject = stripslashes($message['subject']); + $this->from = $message['from']; + $this->desc = 1; + $this->isread = true; + $this->descunread = 0; } } -/** 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 msgid => msgnum */ - var $ids; + public $ids; /** thread starts */ - var $roots; - /** test validity */ - var $valid = true; + public $roots; + /** 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->_readFromFile(); - - $do_save = false; - $first = $banana->maxspool ? max($groupinfo[2] - $banana->maxspool, $groupinfo[1]) : $groupinfo[1]; - $last = $groupinfo[2]; + $this->version = BANANA_SPOOL_VERSION; + $this->mode = Banana::SPOOL_ALL; + $this->group = $group; + } - if ($this->version == BANANA_SPOOL_VERSION && is_array($this->overview)) { - $mids = array_keys($this->overview); - foreach ($mids as $id) { - if (($first <= $last && ($id < $first || $id > $last)) - || ($first > $last && $id < $first && $id > $last)) - { - $this->delid($id, false); - $do_save = true; - } - } - if (!empty($this->overview)) { - $first = max(array_keys($this->overview))+1; - } + public static function getSpool($group, $since = 0) + { + if (!is_null(Banana::$spool) && Banana::$spool->group == $group) { + $spool = Banana::$spool; } else { - unset($this->overview, $this->ids); - $this->version = BANANA_SPOOL_VERSION; + $spool = BananaSpool::readFromFile($group); + } + if (is_null($spool)) { + $spool = new BananaSpool($group); } + Banana::$spool =& $spool; + $spool->build(); + $spool->updateUnread($since); + return $spool; + } - if ($first<=$last && $groupinfo[0]) { - $do_save = true; - $this->_updateSpool("$first-$last"); + private static function spoolFilename($group) + { + $file = dirname(dirname(__FILE__)); + $file .= '/spool/' . Banana::$protocole->name() . '/'; + if (!is_dir($file)) { + mkdir($file); } - - if ($do_save) { $this->_saveToFile(); } - - $this->_updateUnread($_since, $_display); + return $file . Banana::$protocole->filename(); } - function _readFromFile() + private static function readFromFile($group) { - $file = $this->_spoolfile(); - if (file_exists($file)) { - $temp = unserialize(file_get_contents($file)); - foreach (get_object_vars($temp) as $key=>$val) { - $this->$key = $val; - } + $file = BananaSpool::spoolFilename($group); + if (!file_exists($file)) { + return null; + } + $spool = unserialize(file_get_contents($file)); + if ($spool->version != BANANA_SPOOL_VERSION || $spool->mode != Banana::SPOOL_ALL) { + return null; } + return $spool; } - function _saveToFile() + private function compare($a, $b) { - $file = $this->_spoolfile(); - uasort($this->overview, "spoolcompare"); + return ($b->date >= $a->date); + } + + private function saveToFile() + { + $file = BananaSpool::spoolFilename($this->group); + uasort($this->overview, array($this, 'compare')); $this->roots = Array(); foreach($this->overview as $id=>$msg) { @@ -158,98 +141,179 @@ class BananaSpool } } - file_put_contents($file, serialize($this)); + if ($this->mode == Banana::SPOOL_ALL) { + file_put_contents($file, serialize($this)); + } } - function _spoolfile() + private function build() { - global $banana; - $url = parse_url($banana->host); - $file = $url['host'].'_'.$url['port'].'_'.$this->group; - return dirname(dirname(__FILE__)).'/spool/'.$file; + $threshold = 0; + + // Compute the range of indexes + list($msgnum, $first, $last) = Banana::$protocole->getIndexes(); + if ($last < $first) { + $threshold = $firt + $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 ($clean || $update) { + $this->saveToFile(); + } + } + + private function clean(&$first, &$last, $msgnum) + { + $do_save = false; + if (is_array($this->overview)) { + $mids = array_keys($this->overview); + foreach ($mids as $id) { + if (($first <= $last && ($id < $first || $id > $last)) + || ($first > $last && $id < $first && $id > $last)) { + $this->delid($id, false); + $do_save = true; + } + } + if (!empty($this->overview)) { + $first = max(array_keys($this->overview))+1; + } + } + return $do_save; } - function _updateSpool($arg) + private function update(&$first, &$last, $msgnum) { - 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); - - if (is_array($this->ids)) { - $this->ids = array_merge($this->ids, array_flip($msgids)); - } else { - $this->ids = array_flip($msgids); + if ($first > $last || !$msgnum) { + return false; } - 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)); + $messages =& Banana::$protocole->getMessageHeaders($first, $last, + array('Date', 'Subject', 'From', 'Message-ID', 'References', 'In-Reply-To')); + + if (!is_array($this->ids)) { + $this->ids = array(); + } + foreach ($messages as $id=>&$message) { + $this->ids[$message['message-id']] = $id; + } - if (isset($this->overview[$id])) { - $msg->desc = $this->overview[$id]->desc; - $msg->children = $this->overview[$id]->children; + foreach ($messages as $id=>&$message) { + if (!isset($this->overview[$id])) { + $this->overview[$id] = new BananaSpoolHead($message); } - $this->overview[$id] = $msg; + $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 ($p = $msg->parent) { + if (!is_null($p = $msg->parent)) { if (empty($this->overview[$p])) { - $this->overview[$p] = new BananaSpoolHead($dates[$p], $subjects[$p], $froms[$p], 1); + $this->overview[$p] = new BananaSpoolHead($messages[$p]); } $this->overview[$p]->children[] = $id; - while ($p) { + while (!is_null($p)) { $this->overview[$p]->desc += $msg->desc; - $p = $this->overview[$p]->parent; + if ($p != $this->overview[$p]->parent) { + $p = $this->overview[$p]->parent; + } else { + $p = null; + } } } } + Banana::$protocole->updateSpool($messages); + return true; } - function _updateUnread($since, $mode) + private function updateUnread($since) { - global $banana; - if (empty($since)) { return; } - - 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; + if (empty($since)) { + return; + } + + $newpostsids = Banana::$protocole->getNewIndexes($since); + + if (empty($newpostsids)) { + 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; + while (isset($id)) { + $this->overview[$id]->descunread ++; + $id = $this->overview[$id]->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; + public function setMode($mode) + { + $this->mode = $mode; + switch ($mode) { + case Banana::SPOOL_UNREAD: + foreach ($this->roots as $k=>$i) { + if ($this->overview[$i]->descunread == 0) { + $this->killdesc($i); + unset($this->roots[$k]); } } + break; + } + } + + /** 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; + 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 (is_null($array)) { + $array =& $this->roots; + } + foreach ($array as $id) { + if (!$this->overview[$id]->isread) { + $this->markAsRead($id); + } + if ($this->overview[$id]->descunread) { + $this->markAllAsRead($this->overview[$id]->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) { @@ -265,8 +329,7 @@ class BananaSpool /** delete a post from overview * @param $_id MSGNUM of post */ - - function delid($_id, $write=true) + public function delid($_id, $write = true) { if (isset($this->overview[$_id])) { if (sizeof($this->overview[$_id]->parent)) { @@ -296,8 +359,27 @@ class BananaSpool unset($this->ids[$msgid]); } - if ($write) { $this->_saveToFile(); } + if ($write) { + $this->saveToFile(); + } + } + } + + private function formatDate($stamp) + { + $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'; + } else { + $format = '%a %e %b'; } + return utf8_encode(strftime($format, $stamp)); } /** displays children tree of a post @@ -309,68 +391,80 @@ class BananaSpool * @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 + * + * 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. */ - - function _to_html($_id, $_index, $_first=0, $_last=0, $_ref="", $_pfx_node="", $_pfx_end="", $_head=true) + private function _to_html($_id, $_index, $_first=0, $_last=0, $_ref="", $_pfx_node="", $_pfx_end="", $_head=true) { - $spfx_f = makeImg('k1', 'o', 21, 9); - $spfx_n = makeImg('k2', '*', 21, 9); - $spfx_Tnd = makeImg('T-direct', '+', 21, 12); - $spfx_Lnd = makeImg('L-direct', '`', 21, 12); - $spfx_snd = makeImg('s-direct', '-', 21, 5); - $spfx_T = makeImg('T', '+', 21, 12); - $spfx_L = makeImg('L', '`', 21, 12); - $spfx_s = makeImg('s', '-', 21, 5); - $spfx_e = makeImg('e', ' ', 21, 12); - $spfx_I = makeImg('I', '|', 21, 12); - - if ($_index + $this->overview[$_id]->desc < $_first || $_index > $_last) { - return; + static $spfx_f, $spfx_n, $spfx_Tnd, $spfx_Lnd, $spfx_snd, $spfx_T, $spfx_L, $spfx_s, $spfx_e, $spfx_I; + if (!isset($spfx_f)) { + $spfx_f = Banana::$page->makeImg(Array('img' => 'k1', 'alt' => 'o', 'height' => 21, 'width' => 9)); + $spfx_n = Banana::$page->makeImg(Array('img' => 'k2', 'alt' => '*', 'height' => 21, 'width' => 9)); + $spfx_Tnd = Banana::$page->makeImg(Array('img' => 'T-direct', 'alt' => '+', 'height' => 21, 'width' => 12)); + $spfx_Lnd = Banana::$page->makeImg(Array('img' => 'L-direct', 'alt' => '`', 'height' => 21, 'width' => 12)); + $spfx_snd = Banana::$page->makeImg(Array('img' => 's-direct', 'alt' => '-', 'height' => 21, 'width' => 5)); + $spfx_T = Banana::$page->makeImg(Array('img' => 'T', 'alt' => '+', 'height' => 21, 'width' => 12)); + $spfx_L = Banana::$page->makeImg(Array('img' => 'L', 'alt' => '`', 'height' => 21, 'width' => 12)); + $spfx_s = Banana::$page->makeImg(Array('img' => 's', 'alt' => '-', 'height' => 21, 'width' => 5)); + $spfx_e = Banana::$page->makeImg(Array('img' => 'e', 'alt' => ' ', 'height' => 21, 'width' => 12)); + $spfx_I = Banana::$page->makeImg(Array('img' => 'I', 'alt' => '|', 'height' => 21, 'width' => 12)); } - $res = ''; - - if ($_index>=$_first) { - $hc = empty($this->overview[$_id]->children); + $overview =& $this->overview[$_id]; + if ($_index + $overview->desc < $_first || $_index > $_last) { + return ''; + } - $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) { + $res = ''; + if ($_index >= $_first) { + $hc = empty($overview->children); + + $res .= '\n"; + $res .= '' . $this->formatDate($overview->date) . " \n"; + $res .= '' + . $_pfx_node .($hc ? ($_head ? $spfx_f : ($overview->parent_direct ? $spfx_s : $spfx_snd)) : $spfx_n); + $subject = $overview->subject; + if (empty($subject)) { $subject = _b_('(pas de sujet)'); } - if ($_index == $_ref) { - $res .= ''.htmlentities($subject).''; - } else { - $res .= makeHREF(Array('group' => $this->group, - 'artid' => $_id), - htmlentities($subject)); + $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)); } - $res .= "\n".formatFrom($this->overview[$_id]->from)."\n"; + $res .= ' ' . $subject . $link; + $res .= "\n" . BananaMessage::formatFrom($overview->from) . "\n"; - if ($hc) { return $res; } + if ($hc) { + return $res; + } } $_index ++; - - $children = $this->overview[$_id]->children; + $children = $overview->children; while ($child = array_shift($children)) { - if ($_index > $_last) { return $res; } - if ($_index+$this->overview[$child]->desc >= $_first) { + $overview =& $this->overview[$child]; + if ($_index > $_last) { + return $res; + } + if ($_index + $overview->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); + $_pfx_end . ($overview->parent_direct ? $spfx_T : $spfx_Tnd), + $_pfx_end . $spfx_I, false); } 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); + $_pfx_end . ($overview->parent_direct ? $spfx_L : $spfx_Lnd), + $_pfx_end . $spfx_e, false); } } - $_index += $this->overview[$child]->desc; + $_index += $overview->desc; } return $res; @@ -381,47 +475,38 @@ class BananaSpool * @param $_last INTEGER MSGNUM of last post * @param $_ref STRING MSGNUM of current/selectionned post */ - - function to_html($_first=0, $_last=0, $_ref = null) + public function toHtml($first = 0, $overview = false) { - $res = ''; - - if (is_null($_ref)) { - $res .= ''; - $res .= ''; - $res .= ''; - } + $res = ''; - $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; } - } + if (!$overview) { + $_first = $first; + $_last = $first + Banana::$spool_tmax - 1; + $_ref = null; } else { - $res .= ''; + $_ref = $this->getNdx($first); + $_last = $_ref + Banana::$spool_tafter; + $_first = $_ref - Banana::$spool_tbefore; + if ($_first < 0) { + $_last -= $_first; + } } - - global $banana; - if (is_object($banana->groups)) { - $res .= ''; + $index = 1; + foreach ($this->roots as $id) { + $res .= $this->_to_html($id, $index, $_first, $_last, $_ref); + $index += $this->overview[$id]->desc ; + if ($index > $_last) { + break; + } } - $res .= ''; - - return $res .= '
'._b_('Date').''._b_('Sujet').''._b_('Auteur').'
'._b_('Aucun message dans ce forum').'
' - . $banana->groups->to_html() - . '
' - . makeHREF(Array('subscribe' => 1), 'Gérer mes abonnements') - . '
'; + return $res; } /** computes linear post index * @param $_id INTEGER MSGNUM of post * @return INTEGER linear index of post */ - - function getndx($_id) + public function getNdX($_id) { $ndx = 1; $id_cur = $_id; @@ -433,7 +518,7 @@ class BananaSpool for ($i = 0; $i < $pos ; $i++) { $ndx += $this->overview[$this->overview[$id_parent]->children[$i]]->desc; } - $ndx++; //noeud père + $ndx++; //noeud père $id_cur = $id_parent; } @@ -446,7 +531,161 @@ class BananaSpool } return $ndx; } + + /** Return root message of the given thread + * @param id INTEGER id of a message + */ + public function root($id) + { + $id_cur = $id; + while (true) { + $id_parent = $this->overview[$id_cur]->parent; + if (is_null($id_parent)) break; + $id_cur = $id_parent; + } + return $id_cur; + } + + /** Returns previous thread root index + * @param id INTEGER message number + */ + public function prevThread($id) + { + $root = $this->root($id); + $last = null; + foreach ($this->roots as $i) { + if ($i == $root) { + return $last; + } + $last = $i; + } + return $last; + } + + /** 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; + } + if ($i == $root) { + $ok = true; + } + } + return null; + } + + /** 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; + foreach ($this->overview[$parent]->children as $child) { + if ($child == $id) { + return $last; + } + $last = $child; + } + return null; + } + + /** Return next post in the thread + * @param id INTEGER message number + */ + public function nextPost($id) + { + if (count($this->overview[$id]->children) != 0) { + return $this->overview[$id]->children[0]; + } + + $cur = $id; + while (true) { + $parent = $this->overview[$cur]->parent; + if (is_null($parent)) { + return null; + } + $ok = false; + foreach ($this->overview[$parent]->children as $child) { + if ($ok) { + return $child; + } + if ($child == $cur) { + $ok = true; + } + } + $cur = $parent; + } + return null; + } + + /** Look for an unread message in the thread rooted by the message + * @param id INTEGER message number + */ + private function _nextUnread($id) + { + if (!$this->overview[$id]->isread) { + return $id; + } + foreach ($this->overview[$id]->children as $child) { + $unread = $this->_nextUnread($child); + if (!is_null($unread)) { + return $unread; + } + } + return null; + } + + /** Find next unread message + * @param id INTEGER message number + */ + public function nextUnread($id = 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; + } + } + } + + // Look in current thread + $cur = $id; + do { + $parent = is_null($cur) ? null : $this->overview[$cur]->parent; + $ok = is_null($cur) ? true : false; + if (!is_null($parent)) { + $array = &$this->overview[$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: ?>