require_once dirname(__FILE__) . '/banana.inc.php';
-define('BANANA_SPOOL_VERSION', '0.3');
+define('BANANA_SPOOL_VERSION', '0.5.1');
/** Class spoolhead
* class used in thread overviews
public $subject;
/** author */
public $from;
+ public $color;
/** reference of parent */
public $parent = null;
/** paren is direct */
public function __construct(array &$message)
{
$this->date = $message['date'];
- $this->subject = stripslashes($message['subject']);
+ $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;
class BananaSpool
{
private $version;
+ private $mode;
/** group name */
public $group;
public $ids;
/** thread starts */
public $roots;
+ /** protocole specific data */
+ public $storage = array();
+ private $unreadnb = 0;
/** constructor
* @param $_group STRING group name
protected function __construct($group)
{
$this->version = BANANA_SPOOL_VERSION;
+ $this->mode = Banana::SPOOL_ALL;
$this->group = $group;
}
- public static function getSpool($group, $since = 0)
+ public static function &getSpool($group, $since = 0, $clean = false)
{
if (!is_null(Banana::$spool) && Banana::$spool->group == $group) {
- $spool = Banana::$spool;
+ $spool =& Banana::$spool;
} else {
- $spool = BananaSpool::readFromFile($group);
- }
+ $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;
}
private static function spoolFilename($group)
{
- $file = dirname(dirname(__FILE__));
- $file .= '/spool/' . Banana::$protocole->name() . '/';
+ $file = Banana::$spool_root . '/' . Banana::$protocole->name() . '/';
if (!is_dir($file)) {
mkdir($file);
}
- $url = parse_url(Banana::$host);
- if (isset($url['host'])) {
- $file .= $url['host'] . '_';
- }
- if (isset($url['port'])) {
- $file .= $url['port'] . '_';
- }
- $file .= $group;
- return $file;
+ return $file . Banana::$protocole->filename();
}
- private static function readFromFile($group)
+ private static function &readFromFile($group)
{
+ $spool = null;
$file = BananaSpool::spoolFilename($group);
if (!file_exists($file)) {
- return null;
+ return $spool;
}
$spool = unserialize(file_get_contents($file));
- if ($spool->version != BANANA_SPOOL_VERSION) {
- return null;
+ 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);
+ return ($this->overview[$b]->date >= $this->overview[$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) {
+ foreach($this->overview as $id=>&$msg) {
if (is_null($msg->parent)) {
$this->roots[] = $id;
}
}
+ usort($this->roots, array($this, 'compare'));
- file_put_contents($file, serialize($this));
+ if ($this->mode == Banana::SPOOL_ALL) {
+ file_put_contents($file, serialize($this));
+ }
}
private function build()
// Compute the range of indexes
list($msgnum, $first, $last) = Banana::$protocole->getIndexes();
if ($last < $first) {
- $threshold = $firt + $msgnum - $last;
+ $threshold = $first + $msgnum - $last;
$threshold = (int)(log($threshold)/log(2));
$threshold = (2 ^ ($threshold + 1)) - 1;
}
- if (Banana::$maxspool && Banana::$maxspool < $msgnum) {
- $first = $last - Banana::$maxspool;
+ if (Banana::$spool_max && Banana::$spool_max < $msgnum) {
+ $first = $last - Banana::$spool_max;
if ($first < 0) {
$first += $threshold;
}
}
$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;
$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;
}
private function update(&$first, &$last, $msgnum)
{
- if ($first >= $last || !$msgnum) {
+ if ($first > $last || !$msgnum) {
return false;
}
$this->ids[$message['message-id']] = $id;
}
+ if (!is_array($this->overview)) {
+ $this->overview = array();
+ }
foreach ($messages as $id=>&$message) {
if (!isset($this->overview[$id])) {
$this->overview[$id] = new BananaSpoolHead($message);
$p = $this->overview[$p]->parent;
} else {
$p = null;
- }
+ }
}
}
}
return true;
}
- private function updateUnread($since)
+ public function updateUnread($since)
{
if (empty($since)) {
return;
}
$newpostsids = Banana::$protocole->getNewIndexes($since);
-
+
if (empty($newpostsids)) {
return;
}
foreach ($newpostsids as $mid) {
$id = $this->ids[$mid];
if ($this->overview[$id]->isread) {
- $this->overview[$id]->isread = false;
- $this->overview[$id]->descunread = 1;
+ $this->overview[$id]->isread = false;
+ $this->unreadnb++;
while (isset($id)) {
- $this->overview[$id]->descunread ++;
+ $this->overview[$id]->descunread++;
$id = $this->overview[$id]->parent;
}
}
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 $k=>$i) {
if ($this->overview[$i]->descunread == 0) {
$this->killdesc($i);
}
}
+ /** Mark the given id as read
+ * @param id MSGNUM of post
+ */
+ public function markAsRead($id)
+ {
+ if (!$this->overview[$id]->isread) {
+ $this->overview[$id]->isread = true;
+ $this->unreadnb--;
+ while (isset($id)) {
+ $this->overview[$id]->descunread--;
+ $id = $this->overview[$id]->parent;
+ }
+ }
+ }
+
+ /** Mark all unread messages as read
+ */
+ public function markAllAsRead(array &$array = null)
+ {
+ if (!$this->unreadnb) {
+ return;
+ }
+ if (is_null($array) && is_array($this->roots)) {
+ $array =& $this->roots;
+ } elseif (is_null($array)) {
+ return;
+ }
+ foreach ($array as $id) {
+ if (!$this->overview[$id]->isread) {
+ $this->markAsRead($id);
+ if (!$this->unreadnb) {
+ return;
+ }
+ }
+ if ($this->overview[$id]->descunread) {
+ $this->markAllAsRead($this->overview[$id]->children);
+ }
+ }
+ }
+
/** kill post and childrens
* @param $_id MSGNUM of post
*/
public function delid($_id, $write = true)
{
if (isset($this->overview[$_id])) {
- if (sizeof($this->overview[$_id]->parent)) {
- $this->overview[$this->overview[$_id]->parent]->children =
- array_diff($this->overview[$this->overview[$_id]->parent]->children, array($_id));
- if (sizeof($this->overview[$_id]->children)) {
- $this->overview[$this->overview[$_id]->parent]->children =
- array_merge($this->overview[$this->overview[$_id]->parent]->children, $this->overview[$_id]->children);
- foreach ($this->overview[$_id]->children as $c) {
- $this->overview[$c]->parent = $this->overview[$_id]->parent;
+ $overview =& $this->overview[$_id];
+ if (!$overview->isread) {
+ $this->markAsRead($_id);
+ }
+ if ($overview->parent) {
+ $p = $overview->parent;
+ $parent =& $this->overview[$p];
+ $parent->children = array_diff($parent->children, array($_id));
+ if (sizeof($overview->children)) {
+ $parent->children = array_merge($parent->children, $overview->children);
+ foreach ($overview->children as $c) {
+ $this->overview[$c]->parent = $p;
$this->overview[$c]->parent_direct = false;
}
}
- $p = $this->overview[$_id]->parent;
- while ($p) {
+ while (isset($p)) {
$this->overview[$p]->desc--;
$p = $this->overview[$p]->parent;
}
- } elseif (sizeof($this->overview[$_id]->children)) {
- foreach ($this->overview[$_id]->children as $c) {
+ } elseif ($overview->children) {
+ foreach ($overview->children as $c) {
$this->overview[$c]->parent = null;
}
}
+ unset($overview);
unset($this->overview[$_id]);
$msgid = array_search($_id, $this->ids);
- if ($msgid) {
+ if ($msgid !== false) {
unset($this->ids[$msgid]);
}
-
+ $msgid = array_search($_id, $this->roots);
+ if ($msgid !== false) {
+ unset($this->roots[$msgid]);
+ }
+
if ($write) {
$this->saveToFile();
}
$format = _b_('hier')." %H:%M";
} elseif ($today < 7 + $dday) {
$format = '%a %H:%M';
- } else {
+ } elseif ($today < 90 + $dday) {
$format = '%a %e %b';
+ } else {
+ $format = '%a %e %b %Y';
}
- return utf8_encode(strftime($format, $stamp));
+ return strftime($format, $stamp);
}
/** displays children tree of a 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 $_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.
+ * If you want to analyse subject, you can define the function hook_formatDisplayHeader
*/
- private function _to_html($_id, $_index, $_first=0, $_last=0, $_ref="", $_pfx_node="", $_pfx_end="", $_head=true)
+ private function _to_html($_id, $_index, $_first=0, $_last=0, $_ref="", $_pfx_node="", $_pfx_end="", $_head=true, $_pfx_id="")
{
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_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));
if ($_index >= $_first) {
$hc = empty($overview->children);
- $res .= '<tr class="' . ($_index%2 ? 'pair' : 'impair') . ($overview->isread ? '' : ' new') . "\">\n";
+ $res .= '<tr id="'.$_pfx_id.$_id.'" class="' . ($_index%2 ? 'pair' : 'impair') . ($overview->isread ? '' : ' new') . "\">\n";
$res .= '<td class="date">' . $this->formatDate($overview->date) . " </td>\n";
- $res .= '<td class="subj' . ($_index == $_ref ? ' cur' : '') . '">'
- . $_pfx_node .($hc ? ($_head ? $spfx_f : ($overview->parent_direct ? $spfx_s : $spfx_snd)) : $spfx_n);
- $subject = $overview->subject;
+ $res .= '<td class="subj' . ($_index == $_ref ? ' cur' : '') . '"><div class="tree">'
+ . $_pfx_node .($hc ? ($_head ? $spfx_f : ($overview->parent_direct ? $spfx_s : $spfx_snd)) : $spfx_n)
+ . '</div>';
+ $popup = $subject = $overview->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)');
}
- $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));
+ 'text' => $subject, 'popup' => $popup));
}
$res .= ' ' . $subject . $link;
$res .= "</td>\n<td class='from'>" . BananaMessage::formatFrom($overview->from) . "</td>\n</tr>";
if ($hc) {
return $res;
}
- }
+ }
$_index ++;
$children = $overview->children;
if (sizeof($children)) {
$res .= $this->_to_html($child, $_index, $_first, $_last, $_ref,
$_pfx_end . ($overview->parent_direct ? $spfx_T : $spfx_Tnd),
- $_pfx_end . $spfx_I, false);
+ $_pfx_end . $spfx_I, false,$_id.'_');
} else {
$res .= $this->_to_html($child, $_index, $_first, $_last, $_ref,
$_pfx_end . ($overview->parent_direct ? $spfx_L : $spfx_Lnd),
- $_pfx_end . $spfx_e, false);
+ $_pfx_end . $spfx_e, false,$_id.'_');
}
}
$_index += $overview->desc;
*/
public function toHtml($first = 0, $overview = false)
{
- $res = '';
+ $res = Banana::$page->makeJs('jquery');
+ $res .= Banana::$page->makeJs('spool_toggle');
if (!$overview) {
$_first = $first;
- $_last = $first + Banana::$tmax - 1;
+ $_last = $first + Banana::$spool_tmax - 1;
$_ref = null;
} else {
$_ref = $this->getNdx($first);
- $_last = $_ref + Banana::$tafter;
- $_first = $_ref - Banana::$tbefore;
+ $_last = $_ref + Banana::$spool_tafter;
+ $_first = $_ref - Banana::$spool_tbefore;
if ($_first < 0) {
$_last -= $_first;
}
return $res;
}
+
+ public function _buildTree($id, BananaSpoolHead &$head, $current) {
+ 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' => ' ', 'height' => 18, 'width' => 14));
+ $u_h = Banana::$page->makeImg(Array('img' => 'h2', 'alt' => '-', 'height' => 18, 'width' => 14));
+ $u_ht = Banana::$page->makeImg(Array('img' => 'T2', 'alt' => '+', 'height' => 18, 'width' => 14));
+ $u_vt = Banana::$page->makeImg(Array('img' => 't2', 'alt' => '`', 'height' => 18, 'width' => 14));
+ $u_l = Banana::$page->makeImg(Array('img' => 'l2', 'alt' => '|', 'height' => 18, 'width' => 14));
+ $u_f = Banana::$page->makeImg(Array('img' => 'f2', 'alt' => 't', 'height' => 18, 'width' => 14));
+ $r_h = Banana::$page->makeImg(Array('img' => 'h2r', 'alt' => '-', 'height' => 18, 'width' => 14));
+ $r_ht = Banana::$page->makeImg(Array('img' => 'T2r', 'alt' => '+', 'height' => 18, 'width' => 14));
+ $r_vt = Banana::$page->makeImg(Array('img' => 't2r', 'alt' => '`', 'height' => 18, 'width' => 14));
+ $r_l = Banana::$page->makeImg(Array('img' => 'l2r', 'alt' => '|', 'height' => 18, 'width' => 14));
+ $r_f = Banana::$page->makeImg(Array('img' => 'f2r', 'alt' => 't', 'height' => 18, 'width' => 14));
+ }
+ $style = 'background-color:' . $head->color . '; text-decoration: none';
+ $prof = 1;
+ $text = '<span style="' . $style . '" title="' . banana_entities($head->from) . '">' .
+ '<input type="radio" name="banana_tree" '. ($id == $current ? 'checked="checked" ' : ' ' ) .
+ (Banana::$msgshow_javascript ? 'onchange="window.location=\'' .
+ 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];
+ list($tpr, $tree) = $this->_buildTree($child, $msg, $current);
+ $last = $key == count($head->children) - 1;
+ foreach ($tree as $kt=>&$line) {
+ if ($kt == 0 && $key == 0 && !$last) {
+ $array[0] .= ($msg->isread ? $r_ht : $u_ht) . $line;
+ } else if($kt == 0 && $key == 0) {
+ $array[0] .= ($msg->isread ? $r_h : $u_h) . $line;
+ } else if ($kt == 0 && $last) {
+ $array[] = $t_e . ($msg->isread ? $r_vt : $u_vt) . $line;
+ } else if ($kt == 0) {
+ $array[] = $t_e . ($msg->isread ? $r_f : $u_f) . $line;
+ } else if ($last) {
+ $array[] = $t_e . $t_e . $line;
+ } else {
+ $array[] = $t_e . ($msg->isread ? $r_l : $u_l) . $line;
+ }
+ }
+ if ($tpr > $prof) {
+ $prof = $tpr + 1;
+ }
+ }
+ return array($prof, $array);
+ }
+
+ /** build the spool tree associated with the given message
+ */
+ public function buildTree($id) {
+ $pos = $id;
+ $overview =& $this->overview[$id];
+ while (!is_null($overview->parent)) {
+ $pos = $overview->parent;
+ $overview =& $this->overview[$pos];
+ }
+ list($prof, $tree) = $this->_buildTree($pos, $overview, $id);
+ return implode("\n", $tree);
+ }
+
/** computes linear post index
* @param $_id INTEGER MSGNUM of post
* @return INTEGER linear index of post
$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;
}
return $id_cur;
}
+ /** 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
*/
if (count($this->overview[$id]->children) != 0) {
return $this->overview[$id]->children[0];
}
-
+
$cur = $id;
while (true) {
$parent = $this->overview[$cur]->parent;
$unread = $this->_nextUnread($child);
if (!is_null($unread)) {
return $unread;
- }
+ }
}
return null;
}
*/
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) {
$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:
?>