X-Git-Url: http://git.polytechnique.org/?a=blobdiff_plain;f=banana%2Fspool.inc.php;h=505c2c1a115b0fdcaf7d7db064befbd3926ad26f;hb=d7718cc55c6aec2963ff919ddf13cfa190f34307;hp=c938a24b1c25e69f5edcde228759d5077cdc9b7f;hpb=dc12019c868a8e302a29e2730f7da8afa67ab990;p=banana.git diff --git a/banana/spool.inc.php b/banana/spool.inc.php index c938a24..505c2c1 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,96 @@ 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 = $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, $clean = false) + { + 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); } - - if ($first<=$last && $groupinfo[0]) { - $do_save = true; - $this->_updateSpool("$first-$last"); + Banana::$spool =& $spool; + $spool->build(); + if ($clean) { + $spool->markAllAsRead(); } + $spool->updateUnread($since); + return $spool; + } - if ($do_save) { $this->_saveToFile(); } - - $this->_updateUnread($_since, $_display); + private static function spoolFilename($group) + { + $file = dirname(dirname(__FILE__)); + $file .= '/spool/' . Banana::$protocole->name() . '/'; + if (!is_dir($file)) { + mkdir($file); + } + 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 +144,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 +332,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,10 +362,29 @@ 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 * @param $_id INTEGER MSGNUM of post * @param $_index INTEGER linear number of post in the tree @@ -310,78 +395,78 @@ class BananaSpool * @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 */ - - 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.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) { - 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 .= '
' . _b_('Date') . ' | '; - $res .= '' . $new . _b_('Sujet') . ' | '; - $res .= '' . _b_('Auteur') . ' |
---|---|---|
' . _b_('Aperçu de ') - . makeHREF(Array('group' => $this->group), - $this->group) - . ' | ||
'._b_('Aucun message dans ce forum').' | ||
' - . $banana->groups->to_html() - . ' |