* 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
* @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;
+ $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($this->from) % 0xffffff));
+ $this->desc = 1;
+ $this->isread = true;
+ $this->descunread = 0;
+ if (preg_match("/^([^ ]+@[^ ]+) \((.*)\)$/", $this->from, $regs)) {
+ $this->name = $regs[2];
+ }
+ if (preg_match("/^\"?([^<>\"]+)\"? +<(.+@.+)>$/", $this->from, $regs)) {
+ $this->name = preg_replace("/^'(.*)'$/", '\1', $regs[1]);
+ $this->name = stripslashes($this->name);
+ }
+ if ($this->name) {
+ $this->name = preg_replace("/\\\(\(|\))/","\\1", $this->name);
+ } else if (preg_match("/([^< ]+)@([^> ]+)/", $this->from, $regs)) {
+ $this->name = $regs[1];
+ } else {
+ $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)
+ {
+ $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;
+ }
+
+ public static function getPath($file = null)
{
- global $banana;
- $this->group = $_group;
- $groupinfo = $banana->nntp->group($_group);
- if (!$groupinfo) {
- $this->valid = false;
- return 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;
+ }
- $this->_readFromFile();
+ private static function spoolFilename()
+ {
+ return BananaSpool::getPath('spool');
+ }
- $do_save = false;
- $first = $banana->maxspool ? max($groupinfo[2] - $banana->maxspool, $groupinfo[1]) : $groupinfo[1];
- $last = $groupinfo[2];
+ 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);
+ }
- if ($this->version == BANANA_SPOOL_VERSION && is_array($this->overview)) {
+ 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 ($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;
}
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));
-
- if (isset($this->overview[$id])) {
- $msg->desc = $this->overview[$id]->desc;
- $msg->children = $this->overview[$id]->children;
+ $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->overview[$id] = $msg;
+ }
+ $this->unreadnb += count($newpostsids);
+ }
- 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
*/
-
- 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;
- }
- } elseif (sizeof($this->overview[$_id]->children)) {
- foreach ($this->overview[$_id]->children as $c) {
- $this->overview[$c]->parent = null;
- }
- }
- unset($this->overview[$_id]);
- $msgid = array_search($_id, $this->ids);
- if ($msgid) {
- unset($this->ids[$msgid]);
- }
-
- 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
- *
- * 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)
+ public function delid($_id, $write = true)
{
- $spfx_f = makeImg('k1.gif', 'o', 21, 9);
- $spfx_n = makeImg('k2.gif', '*', 21, 9);
- $spfx_Tnd = makeImg('T-direct.gif', '+', 21, 12);
- $spfx_Lnd = makeImg('L-direct.gif', '`', 21, 12);
- $spfx_snd = makeImg('s-direct.gif', '-', 21, 5);
- $spfx_T = makeImg('T.gif', '+', 21, 12);
- $spfx_L = makeImg('L.gif', '`', 21, 12);
- $spfx_s = makeImg('s.gif', '-', 21, 5);
- $spfx_e = makeImg('e.gif', ' ', 21, 12);
- $spfx_I = makeImg('I.gif', '|', 21, 12);
-
- if ($_index + $this->overview[$_id]->desc < $_first || $_index > $_last) {
+ if (!isset($this->overview[$_id])) {
return;
}
- $res = '';
+ $overview =& $this->overview[$_id];
+ // Be sure it is not counted as unread
+ if (!$overview->isread) {
+ $this->markAsRead($_id);
+ }
- if ($_index>=$_first) {
- $hc = empty($this->overview[$_id]->children);
+ $parent =& $overview->parent;
- $res .= '<tr class="'.($_index%2?'pair':'impair').($this->overview[$_id]->isread?'':' new')."\">\n";
- $res .= "<td class='date'>".fancyDate($this->overview[$_id]->date)." </td>\n";
- $res .= "<td class='subj'>"
- ."<div class='tree'>$_pfx_node".($hc?($_head?$spfx_f:($this->overview[$_id]->parent_direct?$spfx_s:$spfx_snd)):$spfx_n)
- ."</div>";
- $subject = $this->overview[$_id]->subject;
- if (strlen($subject) == 0) {
- $subject = _b_('(pas de sujet)');
- }
- $link = null;
- if (function_exists('hook_getSubject')) {
- $link = hook_getSubject($subject);
+ // 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;
+ }
}
- $subject = formatPlainText(htmlentities($subject));
- if ($_index == $_ref) {
- $res .= '<span class="cur">' . $subject . $link . '</span>';
- } else {
- $res .= makeHREF(Array('group' => $this->group,
- 'artid' => $_id),
- $subject,
- $subject)
- . $link;
- }
- $res .= "</td>\n<td class='from'>".formatFrom($this->overview[$_id]->from)."</td>\n</tr>";
-
- if ($hc) { return $res; }
- }
-
- $_index ++;
-
- $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);
- } 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);
+ if (sizeof($overview->children)) {
+ foreach ($overview->children as &$child) {
+ $parent->children[] =& $child;
+ $child->time = $time;
+ $child->parent =& $parent;
}
}
- $_index += $this->overview[$child]->desc;
+ while (!is_null($parent)) {
+ $parent->desc--;
+ $parent->time = $time;
+ $parent =& $parent->parent;
+ }
}
- return $res;
- }
+ // 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;
- /** Displays overview
- * @param $_first INTEGER MSGNUM of first post
- * @param $_last INTEGER MSGNUM of last post
- * @param $_ref STRING MSGNUM of current/selectionned post
- */
+ if ($write) {
+ $this->saveToFile();
+ }
+ }
- function to_html($_first=0, $_last=0, $_ref = null)
- {
- $res = '<table class="bicol banana_thread" cellpadding="0" cellspacing="0">';
-
- $new = '<div class="banana_action">'
- . makeImgLink(Array('group' => $this->group,
- 'action' => 'new'),
- 'post.gif',
- _b_('Nouveau message'));
- $new .= '</div>';
-
- if (is_null($_ref)) {
- $res .= '<tr><th>' . _b_('Date') . '</th>';
- $res .= '<th>' . $new . _b_('Sujet') . '</th>';
- $res .= '<th>' . _b_('Auteur') . '</th></tr>';
+ 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 {
- $res .= '<tr><th colspan="3">' . _b_('Aperçu de ')
- . makeHREF(Array('group' => $this->group),
- $this->group)
- . '</th></tr>';
+ $format = '%a %e %b %Y';
}
+ return strftime($format, $stamp);
+ }
- $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; }
- }
+ 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 {
- $res .= '<tr><td colspan="3">'._b_('Aucun message dans ce forum').'</td></tr>';
+ $subject = banana_catchFormats(banana_entities(stripslashes($subject)));
+ $link = null;
}
-
- global $banana;
- if (is_object($banana->groups)) {
- $res .= '<tr><td colspan="3" class="subs">'
- . $banana->groups->to_html()
- . '</td></tr>';
+ if (empty($subject)) {
+ $subject = _b_('(pas de sujet)');
+ }
+ if (strlen($subject) > 100) {
+ $subject = substr($subject, 0, 99) . '…';
}
- return $res .= '</table>';
+ if ($head->id !== Banana::$artid) {
+ $subject = Banana::$page->makeLink(Array('group' => $this->group, 'artid' => $head->id,
+ 'text' => $subject, 'popup' => $popup));
+ }
+ return $subject . $link;
}
- /** computes linear post index
- * @param $_id INTEGER MSGNUM of post
- * @return INTEGER linear index of post
- */
-
- function getndx($_id)
+ public function formatFrom(BananaSpoolHead $head)
{
- $ndx = 1;
- $id_cur = $_id;
- 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;
- }
- $ndx++; //noeud père
+ return BananaMessage::formatFrom($head->from);
+ }
- $id_cur = $id_parent;
+ public function start()
+ {
+ if (Banana::$first) {
+ return Banana::$first;
+ } else {
+ $first = array_search(Banana::$artid, $this->roots);
+ return max(0, $first - Banana::$spool_tbefore);
}
+ }
- foreach ($this->roots as $i) {
- if ($i==$id_cur) {
- break;
- }
- $ndx += $this->overview[$i]->desc;
- }
- return $ndx;
+ public function context()
+ {
+ return Banana::$first ? Banana::$spool_tmax : Banana::$spool_tcontext;
}
/** Return root message of the given thread
* @param id INTEGER id of a message
*/
- 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;
+ public function &root($id)
+ {
+ $parent =& $this->overview[$id];
+ while (!is_null($parent->parent)) {
+ $parent =& $parent->parent;
}
- return $id_cur;
+ return $parent;
+ }
+
+ /** 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 {
+ $test = banana_html_entity_decode($val);
+ }
+ }
+ $test = trim($test);
+ if ($test == $subject) {
+ return $id;
+ }
+ $id--;
+ }
+ return -1;
}
/** Returns previous thread root index
* @param id INTEGER message number
*/
- function prevThread($id)
+ public function prevThread($id)
{
- $root = $this->root($id);
+ $root =& $this->root($id);
$last = null;
- foreach ($this->roots as $i) {
- if ($i == $root) {
+ foreach ($this->roots as &$i) {
+ if ($i === $root) {
return $last;
}
- $last = $i;
+ $last = $i->id;
}
return $last;
}
/** Returns next thread root index
* @param id INTEGER message number
*/
- function nextThread($id)
+ public function nextThread($id)
{
- $root = $this->root($id);
+ $root =& $this->root($id);
$ok = false;
- foreach ($this->roots as $i) {
+ foreach ($this->roots as &$i) {
if ($ok) {
- return $i;
+ return $i->id;
}
- if ($i == $root) {
+ if ($i === $root) {
$ok = true;
}
}
/** Return prev post in the thread
* @param id INTEGER message number
*/
- function prevPost($id)
+ public function prevPost($id)
{
- $parent = $this->overview[$id]->parent;
+ $parent =& $this->overview[$id]->parent;
if (is_null($parent)) {
return null;
}
- $last = $parent;
- foreach ($this->overview[$parent]->children as $child) {
- if ($child == $id) {
+ $last = $parent->id;
+ foreach ($parent->children as &$child) {
+ if ($child->id == $id) {
return $last;
}
- $last = $child;
+ $last = $child->id;
}
return null;
}
/** Return next post in the thread
* @param id INTEGER message number
*/
- function nextPost($id)
+ public function nextPost($id)
{
- if (count($this->overview[$id]->children) != 0) {
- return $this->overview[$id]->children[0];
+ $cur =& $this->overview[$id];
+ if (count($cur->children) != 0) {
+ return $cur->children[0]->id;
}
-
- $cur = $id;
+
+ $parent =& $cur;
while (true) {
- $parent = $this->overview[$cur]->parent;
+ $parent =& $cur->parent;
if (is_null($parent)) {
return null;
}
$ok = false;
- foreach ($this->overview[$parent]->children as $child) {
+ foreach ($parent->children as &$child) {
if ($ok) {
- return $child;
+ return $child->id;
}
- if ($child == $cur) {
+ if ($child === $cur) {
$ok = true;
}
}
- $cur = $parent;
+ $cur =& $parent;
}
return null;
}
/** Look for an unread message in the thread rooted by the message
* @param id INTEGER message number
*/
- function _nextUnread($id)
+ private function _nextUnread(BananaSpoolHead $cur)
{
- if (!$this->overview[$id]->isread) {
- return $id;
+ if (!$cur->isread) {
+ return $cur->id;
}
- foreach ($this->overview[$id]->children as $child) {
- return $this->_nextUnread($child);
+ foreach ($cur->children as &$child) {
+ $unread = $this->_nextUnread($child);
+ if (!is_null($unread)) {
+ return $unread;
+ }
}
return null;
}
/** Find next unread message
* @param id INTEGER message number
*/
- function nextUnread($id)
+ public function nextUnread($id = null)
{
- // Look in message children
- foreach ($this->overview[$id]->children as $child) {
- $next = $this->_nextUnread($child);
- if (is_null($next)) {
- return $next;
+ 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;
+ }
}
}
// Look in current thread
- $cur = $id;
- while (true) {
+ if (is_null($id)) {
+ $cur = null;
+ } else {
+ $cur =& $this->overview[$id];
+ }
+ do {
if (is_null($cur)) {
- break;
+ $parent =& $cur;
+ $ok = true;
+ } else {
+ $parent =& $cur->parent;
+ $ok = false;
}
- $parent = $this->overview[$cur]->parent;
- $ok = false;
- if (is_null($parent)) {
- $array = &$this->overview[$parent]->children;
+ if (!is_null($parent)) {
+ $array =& $parent->children;
} else {
- $array = &$this->roots;
+ $array =& $this->roots;
}
- foreach ($array as $child) {
+ foreach ($array as &$child) {
if ($ok) {
$next = $this->_nextUnread($child);
if (!is_null($next)) {
return $next;
}
}
- if ($child == $cur) {
+ if ($child === $cur) {
$ok = true;
}
}
- $cur = $parent;
- }
+ $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:
?>