Look improvements. Can identify unread message and switch from a message to
[banana.git] / banana / spool.inc.php
CommitLineData
810ac1df 1<?php
2/********************************************************************************
3* include/spool.inc.php : spool subroutines
4* -----------------------
5*
6* This file is part of the banana distribution
7* Copyright: See COPYING files that comes with this distribution
8********************************************************************************/
9
7027794f 10require_once dirname(__FILE__) . '/banana.inc.php';
3ca86dfe 11
1a85c7a6 12define('BANANA_SPOOL_VERSION', '0.5.1');
01681efd 13
810ac1df 14/** Class spoolhead
15 * class used in thread overviews
16 */
d4c19591 17class BananaSpoolHead
e785d91c 18{
19 /** date (timestamp) */
7027794f 20 public $date;
e785d91c 21 /** subject */
7027794f 22 public $subject;
e785d91c 23 /** author */
7027794f 24 public $from;
dce7d862 25 public $color;
e785d91c 26 /** reference of parent */
7027794f 27 public $parent = null;
e785d91c 28 /** paren is direct */
7027794f 29 public $parent_direct;
e785d91c 30 /** array of children */
7027794f 31 public $children = Array();
e785d91c 32 /** true if post is read */
7027794f 33 public $isread;
e785d91c 34 /** number of posts deeper in this branch of tree */
7027794f 35 public $desc;
e785d91c 36 /** same as desc, but counts only unread posts */
7027794f 37 public $descunread;
38
39 /** storage data */
40 public $storage = array();
810ac1df 41
e785d91c 42 /** constructor
43 * @param $_date INTEGER timestamp of post
44 * @param $_subject STRING subject of post
45 * @param $_from STRING author of post
46 * @param $_desc INTEGER desc value (1 for a new post)
47 * @param $_read BOOLEAN true if read
48 * @param $_descunread INTEGER descunread value (0 for a new post)
49 */
7027794f 50 public function __construct(array &$message)
e785d91c 51 {
7027794f 52 $this->date = $message['date'];
b87c9103 53 $this->subject = $message['subject'];
7027794f 54 $this->from = $message['from'];
dce7d862 55 $this->color = sprintf('#%06x', abs(crc32($this->from) % 0xffffff));
7027794f 56 $this->desc = 1;
57 $this->isread = true;
58 $this->descunread = 0;
e785d91c 59 }
810ac1df 60}
61
3ca86dfe 62
d4c19591 63class BananaSpool
e785d91c 64{
7027794f 65 private $version;
e9360b11 66 private $mode;
7027794f 67
e785d91c 68 /** group name */
7027794f 69 public $group;
70 /** spool */
71 public $overview;
e785d91c 72 /** array msgid => msgnum */
7027794f 73 public $ids;
cced14b6 74 /** thread starts */
7027794f 75 public $roots;
dc5f77ad 76 /** protocole specific data */
77 public $storage = array();
7027794f 78
d265608a 79 private $unreadnb = 0;
810ac1df 80
e785d91c 81 /** constructor
e785d91c 82 * @param $_group STRING group name
83 * @param $_display INTEGER 1 => all posts, 2 => only threads with new posts
84 * @param $_since INTEGER time stamp (used for read/unread)
85 */
7027794f 86 protected function __construct($group)
e785d91c 87 {
7027794f 88 $this->version = BANANA_SPOOL_VERSION;
e9360b11 89 $this->mode = Banana::SPOOL_ALL;
7027794f 90 $this->group = $group;
91 }
fb6428c8 92
e02edbfe 93 public static function &getSpool($group, $since = 0, $clean = false)
7027794f 94 {
95 if (!is_null(Banana::$spool) && Banana::$spool->group == $group) {
b3d11736 96 $spool =& Banana::$spool;
82c17a91 97 } else {
e02edbfe 98 $spool =& BananaSpool::readFromFile($group);
64f89fae 99 }
7027794f 100 if (is_null($spool)) {
101 $spool = new BananaSpool($group);
82c17a91 102 }
7027794f 103 Banana::$spool =& $spool;
104 $spool->build();
b3d11736 105 if ($clean) {
106 $spool->markAllAsRead();
107 }
7027794f 108 $spool->updateUnread($since);
109 return $spool;
110 }
e785d91c 111
7027794f 112 private static function spoolFilename($group)
113 {
edff14de 114 $file = Banana::$spool_root . '/' . Banana::$protocole->name() . '/';
7027794f 115 if (!is_dir($file)) {
116 mkdir($file);
dd7d1c59 117 }
e9360b11 118 return $file . Banana::$protocole->filename();
dd7d1c59 119 }
120
e02edbfe 121 private static function &readFromFile($group)
dd7d1c59 122 {
e02edbfe 123 $spool = null;
7027794f 124 $file = BananaSpool::spoolFilename($group);
125 if (!file_exists($file)) {
e02edbfe 126 return $spool;
dd7d1c59 127 }
7027794f 128 $spool = unserialize(file_get_contents($file));
e9360b11 129 if ($spool->version != BANANA_SPOOL_VERSION || $spool->mode != Banana::SPOOL_ALL) {
e02edbfe 130 $spool = null;
131 return $spool;
7027794f 132 }
d265608a 133 $spool->markAllAsRead();
7027794f 134 return $spool;
135 }
136
137 private function compare($a, $b)
138 {
345c3a85 139 return ($this->overview[$b]->date >= $this->overview[$a]->date);
dd7d1c59 140 }
141
7027794f 142 private function saveToFile()
dd7d1c59 143 {
7027794f 144 $file = BananaSpool::spoolFilename($this->group);
dd7d1c59 145
146 $this->roots = Array();
345c3a85 147 foreach($this->overview as $id=>&$msg) {
dd7d1c59 148 if (is_null($msg->parent)) {
149 $this->roots[] = $id;
150 }
151 }
345c3a85 152 usort($this->roots, array($this, 'compare'));
2c606d23 153
e9360b11 154 if ($this->mode == Banana::SPOOL_ALL) {
155 file_put_contents($file, serialize($this));
64f89fae 156 }
dd7d1c59 157 }
158
7027794f 159 private function build()
160 {
161 $threshold = 0;
162
163 // Compute the range of indexes
164 list($msgnum, $first, $last) = Banana::$protocole->getIndexes();
165 if ($last < $first) {
6bd49b4b 166 $threshold = $first + $msgnum - $last;
7027794f 167 $threshold = (int)(log($threshold)/log(2));
168 $threshold = (2 ^ ($threshold + 1)) - 1;
169 }
e9360b11 170 if (Banana::$spool_max && Banana::$spool_max < $msgnum) {
171 $first = $last - Banana::$spool_max;
7027794f 172 if ($first < 0) {
173 $first += $threshold;
174 }
175 }
176 $clean = $this->clean($first, $last, $msgnum);
177 $update = $this->update($first, $last, $msgnum);
64f89fae 178
7027794f 179 if ($clean || $update) {
180 $this->saveToFile();
181 }
182 }
64f89fae 183
7027794f 184 private function clean(&$first, &$last, $msgnum)
9090c673 185 {
7027794f 186 $do_save = false;
187 if (is_array($this->overview)) {
188 $mids = array_keys($this->overview);
189 foreach ($mids as $id) {
190 if (($first <= $last && ($id < $first || $id > $last))
e9360b11 191 || ($first > $last && $id < $first && $id > $last)) {
7027794f 192 $this->delid($id, false);
193 $do_save = true;
194 }
195 }
196 if (!empty($this->overview)) {
197 $first = max(array_keys($this->overview))+1;
198 }
199 }
200 return $do_save;
9090c673
PHM
201 }
202
7027794f 203 private function update(&$first, &$last, $msgnum)
dd7d1c59 204 {
64f89fae 205 if ($first > $last || !$msgnum) {
7027794f 206 return false;
dd7d1c59 207 }
208
7027794f 209 $messages =& Banana::$protocole->getMessageHeaders($first, $last,
210 array('Date', 'Subject', 'From', 'Message-ID', 'References', 'In-Reply-To'));
dd7d1c59 211
7027794f 212 if (!is_array($this->ids)) {
213 $this->ids = array();
214 }
215 foreach ($messages as $id=>&$message) {
216 $this->ids[$message['message-id']] = $id;
217 }
218
9c118ac9 219 if (!is_array($this->overview)) {
220 $this->overview = array();
221 }
7027794f 222 foreach ($messages as $id=>&$message) {
223 if (!isset($this->overview[$id])) {
224 $this->overview[$id] = new BananaSpoolHead($message);
3316e34e 225 }
7027794f 226 $msg =& $this->overview[$id];
227 $msgrefs = BananaMessage::formatReferences($message);
228 $parents = preg_grep('/^\d+$/', $msgrefs);
229 $msg->parent = array_pop($parents);
230 $msg->parent_direct = preg_match('/^\d+$/', array_pop($msgrefs));
e785d91c 231
7027794f 232 if (!is_null($p = $msg->parent)) {
dd7d1c59 233 if (empty($this->overview[$p])) {
7027794f 234 $this->overview[$p] = new BananaSpoolHead($messages[$p]);
4ced5065 235 }
dd7d1c59 236 $this->overview[$p]->children[] = $id;
4ced5065 237
7027794f 238 while (!is_null($p)) {
dd7d1c59 239 $this->overview[$p]->desc += $msg->desc;
7027794f 240 if ($p != $this->overview[$p]->parent) {
241 $p = $this->overview[$p]->parent;
242 } else {
243 $p = null;
64f89fae 244 }
e785d91c 245 }
810ac1df 246 }
810ac1df 247 }
7027794f 248 Banana::$protocole->updateSpool($messages);
249 return true;
dd7d1c59 250 }
e785d91c 251
bffb37b4 252 public function updateUnread($since)
75ff2f64 253 {
7027794f 254 if (empty($since)) {
255 return;
256 }
257
258 $newpostsids = Banana::$protocole->getNewIndexes($since);
b3d11736 259
7027794f 260 if (empty($newpostsids)) {
261 return;
262 }
263
264 if (!is_array($this->ids)) {
265 $this->ids = array();
266 }
267 $newpostsids = array_intersect($newpostsids, array_keys($this->ids));
268 foreach ($newpostsids as $mid) {
269 $id = $this->ids[$mid];
270 if ($this->overview[$id]->isread) {
d265608a 271 $this->overview[$id]->isread = false;
272 $this->unreadnb++;
7027794f 273 while (isset($id)) {
d265608a 274 $this->overview[$id]->descunread++;
7027794f 275 $id = $this->overview[$id]->parent;
e785d91c 276 }
810ac1df 277 }
7027794f 278 }
279 }
dd7d1c59 280
7027794f 281 public function setMode($mode)
282 {
e9360b11 283 $this->mode = $mode;
7027794f 284 switch ($mode) {
285 case Banana::SPOOL_UNREAD:
64f89fae 286 $num = max(array_keys($this->overview));
287 if ($this->overview[$num]->isread) {
288 break;
289 }
7027794f 290 foreach ($this->roots as $k=>$i) {
291 if ($this->overview[$i]->descunread == 0) {
292 $this->killdesc($i);
293 unset($this->roots[$k]);
e785d91c 294 }
810ac1df 295 }
7027794f 296 break;
810ac1df 297 }
cced14b6 298 }
299
e9360b11 300 /** Mark the given id as read
301 * @param id MSGNUM of post
302 */
303 public function markAsRead($id)
304 {
305 if (!$this->overview[$id]->isread) {
306 $this->overview[$id]->isread = true;
d265608a 307 $this->unreadnb--;
e9360b11 308 while (isset($id)) {
309 $this->overview[$id]->descunread--;
310 $id = $this->overview[$id]->parent;
311 }
312 }
313 }
314
315 /** Mark all unread messages as read
316 */
317 public function markAllAsRead(array &$array = null)
318 {
d265608a 319 if (!$this->unreadnb) {
320 return;
321 }
c4f176d8 322 if (is_null($array) && is_array($this->roots)) {
e9360b11 323 $array =& $this->roots;
c4f176d8 324 } elseif (is_null($array)) {
c28d3016 325 return;
326 }
e9360b11 327 foreach ($array as $id) {
328 if (!$this->overview[$id]->isread) {
329 $this->markAsRead($id);
d265608a 330 if (!$this->unreadnb) {
331 return;
332 }
e9360b11 333 }
334 if ($this->overview[$id]->descunread) {
335 $this->markAllAsRead($this->overview[$id]->children);
336 }
337 }
338 }
339
e785d91c 340 /** kill post and childrens
341 * @param $_id MSGNUM of post
342 */
7027794f 343 private function killdesc($_id)
e785d91c 344 {
345 if (sizeof($this->overview[$_id]->children)) {
346 foreach ($this->overview[$_id]->children as $c) {
347 $this->killdesc($c);
348 }
349 }
350 unset($this->overview[$_id]);
dd7d1c59 351 if (($msgid = array_search($_id, $this->ids)) !== false) {
e785d91c 352 unset($this->ids[$msgid]);
353 }
810ac1df 354 }
e785d91c 355
356 /** delete a post from overview
357 * @param $_id MSGNUM of post
358 */
7027794f 359 public function delid($_id, $write = true)
e785d91c 360 {
361 if (isset($this->overview[$_id])) {
d265608a 362 $overview =& $this->overview[$_id];
363 if (!$overview->isread) {
364 $this->markAsRead($_id);
365 }
366 if ($overview->parent) {
367 $p = $overview->parent;
368 $parent =& $this->overview[$p];
369 $parent->children = array_diff($parent->children, array($_id));
370 if (sizeof($overview->children)) {
371 $parent->children = array_merge($parent->children, $overview->children);
372 foreach ($overview->children as $c) {
373 $this->overview[$c]->parent = $p;
e785d91c 374 $this->overview[$c]->parent_direct = false;
375 }
376 }
d265608a 377 while (isset($p)) {
e785d91c 378 $this->overview[$p]->desc--;
379 $p = $this->overview[$p]->parent;
380 }
d265608a 381 } elseif ($overview->children) {
382 foreach ($overview->children as $c) {
e785d91c 383 $this->overview[$c]->parent = null;
384 }
385 }
d265608a 386 unset($overview);
e785d91c 387 unset($this->overview[$_id]);
3ca86dfe 388 $msgid = array_search($_id, $this->ids);
cfb7fe5d 389 if ($msgid !== false) {
e785d91c 390 unset($this->ids[$msgid]);
391 }
cfb7fe5d 392 $msgid = array_search($_id, $this->roots);
393 if ($msgid !== false) {
394 unset($this->roots[$msgid]);
395 }
64f89fae 396
7027794f 397 if ($write) {
398 $this->saveToFile();
399 }
e785d91c 400 }
35ca8036 401 }
810ac1df 402
7027794f 403 private function formatDate($stamp)
404 {
405 $today = intval(time() / (24*3600));
406 $dday = intval($stamp / (24*3600));
407
408 if ($today == $dday) {
409 $format = "%H:%M";
410 } elseif ($today == 1 + $dday) {
411 $format = _b_('hier')." %H:%M";
412 } elseif ($today < 7 + $dday) {
413 $format = '%a %H:%M';
d924433f 414 } elseif ($today < 90 + $dday) {
7027794f 415 $format = '%a %e %b';
d924433f 416 } else {
417 $format = '%a %e %b %Y';
7027794f 418 }
22b95309 419 return strftime($format, $stamp);
7027794f 420 }
421
e785d91c 422 /** displays children tree of a post
423 * @param $_id INTEGER MSGNUM of post
424 * @param $_index INTEGER linear number of post in the tree
425 * @param $_first INTEGER linear number of first post displayed
426 * @param $_last INTEGER linear number of last post displayed
64f89fae 427 * @param $_ref STRING MSGNUM of current post
e785d91c 428 * @param $_pfx_node STRING prefix used for current node
429 * @param $_pfx_end STRING prefix used for children of current node
430 * @param $_head BOOLEAN true if first post in thread
3204d440 431 *
b87c9103 432 * If you want to analyse subject, you can define the function hook_formatDisplayHeader
e785d91c 433 */
96e1e874 434 private function _to_html($_id, $_index, $_first=0, $_last=0, $_ref="", $_pfx_node="", $_pfx_end="", $_head=true, $_pfx_id="")
75ff2f64 435 {
7027794f 436 static $spfx_f, $spfx_n, $spfx_Tnd, $spfx_Lnd, $spfx_snd, $spfx_T, $spfx_L, $spfx_s, $spfx_e, $spfx_I;
437 if (!isset($spfx_f)) {
64f89fae 438 $spfx_f = Banana::$page->makeImg(Array('img' => 'k1', 'alt' => 'o', 'height' => 21, 'width' => 9));
7027794f 439 $spfx_n = Banana::$page->makeImg(Array('img' => 'k2', 'alt' => '*', 'height' => 21, 'width' => 9));
440 $spfx_Tnd = Banana::$page->makeImg(Array('img' => 'T-direct', 'alt' => '+', 'height' => 21, 'width' => 12));
441 $spfx_Lnd = Banana::$page->makeImg(Array('img' => 'L-direct', 'alt' => '`', 'height' => 21, 'width' => 12));
442 $spfx_snd = Banana::$page->makeImg(Array('img' => 's-direct', 'alt' => '-', 'height' => 21, 'width' => 5));
443 $spfx_T = Banana::$page->makeImg(Array('img' => 'T', 'alt' => '+', 'height' => 21, 'width' => 12));
444 $spfx_L = Banana::$page->makeImg(Array('img' => 'L', 'alt' => '`', 'height' => 21, 'width' => 12));
445 $spfx_s = Banana::$page->makeImg(Array('img' => 's', 'alt' => '-', 'height' => 21, 'width' => 5));
446 $spfx_e = Banana::$page->makeImg(Array('img' => 'e', 'alt' => '&nbsp;', 'height' => 21, 'width' => 12));
447 $spfx_I = Banana::$page->makeImg(Array('img' => 'I', 'alt' => '|', 'height' => 21, 'width' => 12));
810ac1df 448 }
e785d91c 449
7027794f 450 $overview =& $this->overview[$_id];
451 if ($_index + $overview->desc < $_first || $_index > $_last) {
452 return '';
453 }
cced14b6 454
7027794f 455 $res = '';
456 if ($_index >= $_first) {
457 $hc = empty($overview->children);
458
96e1e874 459 $res .= '<tr id="'.$_pfx_id.$_id.'" class="' . ($_index%2 ? 'pair' : 'impair') . ($overview->isread ? '' : ' new') . "\">\n";
7027794f 460 $res .= '<td class="date">' . $this->formatDate($overview->date) . " </td>\n";
951030b7 461 $res .= '<td class="subj' . ($_index == $_ref ? ' cur' : '') . '"><div class="tree">'
462 . $_pfx_node .($hc ? ($_head ? $spfx_f : ($overview->parent_direct ? $spfx_s : $spfx_snd)) : $spfx_n)
463 . '</div>';
4f7c063d 464 $popup = $subject = $overview->subject;
b87c9103 465 if (function_exists('hook_formatDisplayHeader')) {
466 list($subject, $link) = hook_formatDisplayHeader('subject', $subject, true);
467 } else {
4f7c063d 468 $subject = banana_catchFormats(banana_entities(stripslashes($subject)));
b87c9103 469 $link = null;
470 }
7027794f 471 if (empty($subject)) {
79405147 472 $subject = _b_('(pas de sujet)');
473 }
7027794f 474 if ($_index != $_ref) {
475 $subject = Banana::$page->makeLink(Array('group' => $this->group, 'artid' => $_id,
4f7c063d 476 'text' => $subject, 'popup' => $popup));
cced14b6 477 }
7027794f 478 $res .= '&nbsp;' . $subject . $link;
479 $res .= "</td>\n<td class='from'>" . BananaMessage::formatFrom($overview->from) . "</td>\n</tr>";
dd7d1c59 480
7027794f 481 if ($hc) {
482 return $res;
483 }
64f89fae 484 }
cced14b6 485
dd7d1c59 486 $_index ++;
7027794f 487 $children = $overview->children;
e785d91c 488 while ($child = array_shift($children)) {
7027794f 489 $overview =& $this->overview[$child];
490 if ($_index > $_last) {
491 return $res;
492 }
493 if ($_index + $overview->desc >= $_first) {
e785d91c 494 if (sizeof($children)) {
65d96b1f 495 $res .= $this->_to_html($child, $_index, $_first, $_last, $_ref,
7027794f 496 $_pfx_end . ($overview->parent_direct ? $spfx_T : $spfx_Tnd),
96e1e874 497 $_pfx_end . $spfx_I, false,$_id.'_');
e785d91c 498 } else {
65d96b1f 499 $res .= $this->_to_html($child, $_index, $_first, $_last, $_ref,
7027794f 500 $_pfx_end . ($overview->parent_direct ? $spfx_L : $spfx_Lnd),
96e1e874 501 $_pfx_end . $spfx_e, false,$_id.'_');
e785d91c 502 }
503 }
7027794f 504 $_index += $overview->desc;
810ac1df 505 }
65d96b1f 506
507 return $res;
810ac1df 508 }
810ac1df 509
e785d91c 510 /** Displays overview
511 * @param $_first INTEGER MSGNUM of first post
512 * @param $_last INTEGER MSGNUM of last post
513 * @param $_ref STRING MSGNUM of current/selectionned post
514 */
7027794f 515 public function toHtml($first = 0, $overview = false)
75ff2f64 516 {
96e1e874 517 $res = Banana::$page->makeJs('jquery');
518 $res .= Banana::$page->makeJs('spool_toggle');
7027794f 519
520 if (!$overview) {
521 $_first = $first;
e9360b11 522 $_last = $first + Banana::$spool_tmax - 1;
7027794f 523 $_ref = null;
d8e2470c 524 } else {
7027794f 525 $_ref = $this->getNdx($first);
e9360b11 526 $_last = $_ref + Banana::$spool_tafter;
527 $_first = $_ref - Banana::$spool_tbefore;
7027794f 528 if ($_first < 0) {
529 $_last -= $_first;
530 }
65d96b1f 531 }
e785d91c 532 $index = 1;
7027794f 533 foreach ($this->roots as $id) {
534 $res .= $this->_to_html($id, $index, $_first, $_last, $_ref);
535 $index += $this->overview[$id]->desc ;
536 if ($index > $_last) {
537 break;
e785d91c 538 }
4f75645f 539 }
7027794f 540 return $res;
810ac1df 541 }
810ac1df 542
dce7d862 543
544 public function _buildTree($id, BananaSpoolHead &$head, $current) {
1a85c7a6 545 static $t_e, $u_h, $u_ht, $u_vt, $u_l, $u_f, $r_h, $r_ht, $r_vt, $r_l, $r_f;
546 if (!isset($spfx_f)) {
547 $t_e = Banana::$page->makeImg(Array('img' => 'e', 'alt' => '&nbsp;', 'height' => 18, 'width' => 14));
548 $u_h = Banana::$page->makeImg(Array('img' => 'h2', 'alt' => '-', 'height' => 18, 'width' => 14));
549 $u_ht = Banana::$page->makeImg(Array('img' => 'T2', 'alt' => '+', 'height' => 18, 'width' => 14));
550 $u_vt = Banana::$page->makeImg(Array('img' => 't2', 'alt' => '`', 'height' => 18, 'width' => 14));
551 $u_l = Banana::$page->makeImg(Array('img' => 'l2', 'alt' => '|', 'height' => 18, 'width' => 14));
552 $u_f = Banana::$page->makeImg(Array('img' => 'f2', 'alt' => 't', 'height' => 18, 'width' => 14));
553 $r_h = Banana::$page->makeImg(Array('img' => 'h2r', 'alt' => '-', 'height' => 18, 'width' => 14));
554 $r_ht = Banana::$page->makeImg(Array('img' => 'T2r', 'alt' => '+', 'height' => 18, 'width' => 14));
555 $r_vt = Banana::$page->makeImg(Array('img' => 't2r', 'alt' => '`', 'height' => 18, 'width' => 14));
556 $r_l = Banana::$page->makeImg(Array('img' => 'l2r', 'alt' => '|', 'height' => 18, 'width' => 14));
557 $r_f = Banana::$page->makeImg(Array('img' => 'f2r', 'alt' => 't', 'height' => 18, 'width' => 14));
558 }
559 $style = 'background-color:' . $head->color . '; text-decoration: none';
dce7d862 560 $prof = 1;
1a85c7a6 561 $text = '<span style="' . $style . '" title="' . banana_entities($head->from) . '">' .
562 '<input type="radio" name="banana_tree" '. ($id == $current ? 'checked="checked" ' : ' ' ) .
563 (Banana::$msgshow_javascript ? 'onchange="window.location=\'' .
564 Banana::$page->makeURL(array('group' => $this->group, 'artid' => $id)) . '\'"' : ' disabled="disabled"')
565 .' />' .
566 '</span>';
dce7d862 567 $array = array($text);
568 foreach ($head->children as $key=>&$child) {
1a85c7a6 569 $msg =& $this->overview[$child];
570 list($tpr, $tree) = $this->_buildTree($child, $msg, $current);
dce7d862 571 $last = $key == count($head->children) - 1;
572 foreach ($tree as $kt=>&$line) {
1a85c7a6 573 if ($kt == 0 && $key == 0 && !$last) {
574 $array[0] .= ($msg->isread ? $r_ht : $u_ht) . $line;
575 } else if($kt == 0 && $key == 0) {
576 $array[0] .= ($msg->isread ? $r_h : $u_h) . $line;
dce7d862 577 } else if ($kt == 0 && $last) {
1a85c7a6 578 $array[] = $t_e . ($msg->isread ? $r_vt : $u_vt) . $line;
dce7d862 579 } else if ($kt == 0) {
1a85c7a6 580 $array[] = $t_e . ($msg->isread ? $r_f : $u_f) . $line;
dce7d862 581 } else if ($last) {
1a85c7a6 582 $array[] = $t_e . $t_e . $line;
dce7d862 583 } else {
1a85c7a6 584 $array[] = $t_e . ($msg->isread ? $r_l : $u_l) . $line;
dce7d862 585 }
586 }
587 if ($tpr > $prof) {
588 $prof = $tpr + 1;
589 }
590 }
591 return array($prof, $array);
592 }
593
594 /** build the spool tree associated with the given message
595 */
596 public function buildTree($id) {
597 $pos = $id;
598 $overview =& $this->overview[$id];
599 while (!is_null($overview->parent)) {
600 $pos = $overview->parent;
601 $overview =& $this->overview[$pos];
602 }
603 list($prof, $tree) = $this->_buildTree($pos, $overview, $id);
604 return implode("\n", $tree);
605 }
606
e785d91c 607 /** computes linear post index
608 * @param $_id INTEGER MSGNUM of post
609 * @return INTEGER linear index of post
610 */
7027794f 611 public function getNdX($_id)
75ff2f64 612 {
cced14b6 613 $ndx = 1;
614 $id_cur = $_id;
4ced5065 615 while (true) {
cced14b6 616 $id_parent = $this->overview[$id_cur]->parent;
4ced5065 617 if (is_null($id_parent)) break;
cced14b6 618 $pos = array_search($id_cur, $this->overview[$id_parent]->children);
64f89fae 619
cced14b6 620 for ($i = 0; $i < $pos ; $i++) {
e785d91c 621 $ndx += $this->overview[$this->overview[$id_parent]->children[$i]]->desc;
622 }
951030b7 623 $ndx++; //noeud père
cced14b6 624
625 $id_cur = $id_parent;
810ac1df 626 }
cced14b6 627
628 foreach ($this->roots as $i) {
629 if ($i==$id_cur) {
e785d91c 630 break;
631 }
cced14b6 632 $ndx += $this->overview[$i]->desc;
e785d91c 633 }
634 return $ndx;
810ac1df 635 }
d8e2470c 636
637 /** Return root message of the given thread
638 * @param id INTEGER id of a message
639 */
7027794f 640 public function root($id)
641 {
d8e2470c 642 $id_cur = $id;
643 while (true) {
644 $id_parent = $this->overview[$id_cur]->parent;
645 if (is_null($id_parent)) break;
646 $id_cur = $id_parent;
647 }
648 return $id_cur;
649 }
650
bffb37b4 651 /** Return the last post id with the given subject
652 * @param subject
653 */
654 public function getPostId($subject)
655 {
656 $subject = trim($subject);
657 $id = max(array_keys($this->overview));
658 while (isset($this->overview[$id])) {
659 $test = $this->overview[$id]->subject;
660 if (function_exists('hook_formatDisplayHeader')) {
661 $val = hook_formatDisplayHeader('subject', $test, true);
662 if (is_array($val)) {
0a5b736c 663 $test = banana_html_entity_decode($val[0]);
bffb37b4 664 } else {
0a5b736c 665 $test = banana_html_entity_decode($val);
bffb37b4 666 }
667 }
668 $test = trim($test);
bffb37b4 669 if ($test == $subject) {
670 return $id;
671 }
672 $id--;
673 }
674 return -1;
675 }
676
d8e2470c 677 /** Returns previous thread root index
678 * @param id INTEGER message number
679 */
7027794f 680 public function prevThread($id)
d8e2470c 681 {
682 $root = $this->root($id);
683 $last = null;
684 foreach ($this->roots as $i) {
685 if ($i == $root) {
686 return $last;
687 }
688 $last = $i;
689 }
690 return $last;
691 }
692
693 /** Returns next thread root index
694 * @param id INTEGER message number
695 */
7027794f 696 public function nextThread($id)
d8e2470c 697 {
698 $root = $this->root($id);
699 $ok = false;
700 foreach ($this->roots as $i) {
701 if ($ok) {
702 return $i;
703 }
704 if ($i == $root) {
705 $ok = true;
706 }
707 }
708 return null;
709 }
710
711 /** Return prev post in the thread
712 * @param id INTEGER message number
713 */
7027794f 714 public function prevPost($id)
d8e2470c 715 {
716 $parent = $this->overview[$id]->parent;
717 if (is_null($parent)) {
718 return null;
719 }
720 $last = $parent;
721 foreach ($this->overview[$parent]->children as $child) {
722 if ($child == $id) {
723 return $last;
724 }
725 $last = $child;
726 }
727 return null;
728 }
729
730 /** Return next post in the thread
731 * @param id INTEGER message number
732 */
7027794f 733 public function nextPost($id)
d8e2470c 734 {
735 if (count($this->overview[$id]->children) != 0) {
736 return $this->overview[$id]->children[0];
737 }
64f89fae 738
d8e2470c 739 $cur = $id;
740 while (true) {
741 $parent = $this->overview[$cur]->parent;
742 if (is_null($parent)) {
743 return null;
744 }
745 $ok = false;
746 foreach ($this->overview[$parent]->children as $child) {
747 if ($ok) {
748 return $child;
749 }
750 if ($child == $cur) {
751 $ok = true;
752 }
753 }
754 $cur = $parent;
755 }
756 return null;
757 }
d634c13c 758
759 /** Look for an unread message in the thread rooted by the message
760 * @param id INTEGER message number
761 */
7027794f 762 private function _nextUnread($id)
d634c13c 763 {
764 if (!$this->overview[$id]->isread) {
765 return $id;
766 }
767 foreach ($this->overview[$id]->children as $child) {
bdad7c9d 768 $unread = $this->_nextUnread($child);
769 if (!is_null($unread)) {
770 return $unread;
64f89fae 771 }
d634c13c 772 }
773 return null;
774 }
775
776 /** Find next unread message
777 * @param id INTEGER message number
778 */
7027794f 779 public function nextUnread($id = null)
d634c13c 780 {
d265608a 781 if (!$this->unreadnb) {
782 return null;
783 }
784
b7d59a47 785 if (!is_null($id)) {
786 // Look in message children
787 foreach ($this->overview[$id]->children as $child) {
788 $next = $this->_nextUnread($child);
789 if (!is_null($next)) {
790 return $next;
791 }
d634c13c 792 }
793 }
794
795 // Look in current thread
796 $cur = $id;
b7d59a47 797 do {
798 $parent = is_null($cur) ? null : $this->overview[$cur]->parent;
799 $ok = is_null($cur) ? true : false;
1e016f3a 800 if (!is_null($parent)) {
d634c13c 801 $array = &$this->overview[$parent]->children;
802 } else {
803 $array = &$this->roots;
804 }
805 foreach ($array as $child) {
806 if ($ok) {
807 $next = $this->_nextUnread($child);
808 if (!is_null($next)) {
809 return $next;
810 }
811 }
812 if ($child == $cur) {
813 $ok = true;
814 }
815 }
816 $cur = $parent;
b7d59a47 817 } while(!is_null($cur));
d634c13c 818 return null;
64f89fae 819 }
810ac1df 820}
821
598a1c53 822// vim:set et sw=4 sts=4 ts=4 enc=utf-8:
810ac1df 823?>