bugfix : articles should be dropped in spool->overview AND spool->ids...
[banana.git] / include / spool.inc.php
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
10 /** Class spoolhead
11 * class used in thread overviews
12 */
13 class spoolhead {
14 /** date (timestamp) */
15 var $date;
16 /** subject */
17 var $subject;
18 /** author */
19 var $from;
20 /** reference of parent */
21 var $parent;
22 /** array of children */
23 var $children;
24 /** true if post is read */
25 var $isread;
26 /** number of posts deeper in this branch of tree */
27 var $desc;
28 /** same as desc, but counts only unread posts */
29 var $descunread;
30
31 /** constructor
32 * @param $_date INTEGER timestamp of post
33 * @param $_subject STRING subject of post
34 * @param $_from STRING author of post
35 * @param $_desc INTEGER desc value (1 for a new post)
36 * @param $_read BOOLEAN true if read
37 * @param $_descunread INTEGER descunread value (0 for a new post)
38 */
39
40 function spoolhead($_date,$_subject,$_from,$_desc=1,$_read=true,$_descunread=0) {
41 $this->date=$_date;
42 $this->subject=$_subject;
43 $this->from=$_from;
44 $this->children=array();
45 $this->desc=$_desc;
46 $this->isread=$_read;
47 $this->descunread=$_descunread;
48 }
49 }
50
51 /** Class spool
52 * builds and updates spool
53 */
54
55 class spool {
56 # var $date;
57 /** spool */
58 var $overview;
59 /** group name */
60 var $group;
61 /** array msgid => msgnum */
62 var $ids;
63
64 /** constructor
65 * @param $_nntp RESOURCE NNTP socket filehandle
66 * @param $_group STRING group name
67 * @param $_display INTEGER 1 => all posts, 2 => only threads with new posts
68 * @param $_since INTEGER time stamp (used for read/unread)
69 */
70
71 function spool(&$_nntp,$_group,$_display=0,$_since=""){
72 global $news;
73 $prefix_path=(preg_match("/\/scripts\/?$/",getcwd())?"..":".");
74 $groupinfo = $_nntp->group($_group);
75 if (!$groupinfo) {
76 $this = false;
77 return false;
78 }
79 $spoolfile=realpath("$prefix_path/spool/spool-$_group.dat");
80 if (file_exists($spoolfile)) {
81 $f = fopen($spoolfile,"r");
82 $this = unserialize(fread($f,filesize($spoolfile)));
83 fclose($f);
84 $keys = array_values($this->ids);
85 rsort($keys);
86 $first = max($groupinfo[2]-$news['maxspool'],$groupinfo[1]);
87 $last = $groupinfo[2];
88 // remove expired messages
89 $msgids=array_flip($this->ids);
90 for ($id=min(array_keys($this->overview)); $id<$first; $id++) {
91 $this->delid($id);
92 }
93 $this->ids=array_flip($msgids);
94 $first=max(array_keys($this->overview))+1;
95 } else {
96 $first = max($groupinfo[2]-$news['maxspool'],$groupinfo[1]);
97 $last = $groupinfo[2];
98 $this->group = $_group;
99 }
100
101 if (($first<=$groupinfo[2]) && ($groupinfo[0]!=0)) {
102 $dates = array_map("strtotime",
103 $_nntp->xhdr("Date","$first-$last"));
104 $msgids=$_nntp->xhdr("Message-ID","$first-$last");
105 $subjects = array_map("headerdecode",$_nntp->xhdr("Subject",
106 "$first-$last"));
107 $froms = array_map("headerdecode",$_nntp->xhdr("From",
108 "$first-$last"));
109 $refs = $_nntp->xhdr("References","$first-$last");
110 # $this->date=$nntp->date;
111 if (isset($this->ids)) {
112 $this->ids=array_merge($this->ids,array_flip($msgids));
113 } else {
114 $this->ids=array_flip($msgids);
115 }
116
117 foreach ($msgids as $id=>$msgid) {
118 if (isset($this->overview[$id])) {
119 $msg = $this->overview[$id];
120 $msg->desc++;
121 } else {
122 $msg = new spoolhead($dates[$id],$subjects[$id],$froms[$id],1);
123 }
124 $refs[$id]=str_replace("><","> <",$refs[$id]);
125 $msgrefs=preg_split("/( |\t)/",strtr($refs[$id],$this->ids));
126 $parents=preg_grep("/^\d+$/",$msgrefs);
127 $msg->parent=array_pop($parents);
128 $p = $msg->parent;
129 while ($p) {
130 if (isset($this->overview[$p])) {
131 $this->overview[$p]->desc++;
132 if (isset($this->overview[$p]->parent)) {
133 $p = $this->overview[$p]->parent;
134 } else {
135 $p = false;
136 }
137 } else {
138 $this->overview[$p] = new spoolhead($dates[$p],$subjects[$p],$froms[$p],1);
139 $p = false;
140 }
141 }
142 if ($msg->parent!="")
143 $this->overview[$msg->parent]->children[]=$id;
144 $this->overview[$id] = $msg;
145 }
146 uasort($this->overview,"spoolcompare");
147 $f = fopen("$prefix_path/spool/spool-$_group.dat","w");
148 fputs($f,serialize($this));
149 fclose($f);
150 }
151
152 if ($_since != "") {
153 $newpostsids = $_nntp->newnews($_since,$_group);
154 if (sizeof($newpostsids)) {
155 $newpostsids = array_intersect($newpostsids,
156 array_keys($this->ids));
157 if ($newpostsids && !is_null($newpostsids)) {
158 foreach ($newpostsids as $mid) {
159 $this->overview[$this->ids[$mid]]->isread=false;
160 $this->overview[$this->ids[$mid]]->descunread=1;
161 $parentmid = $this->overview[$this->ids[$mid]]->parent;
162 while (!is_null($parentmid)) {
163 $this->overview[$parentmid]->descunread++;
164 $parentmid = $this->overview[$parentmid]->parent;
165 }
166 }
167 }
168 }
169 if (sizeof($newpostsids)>0) {
170 $flipids = array_flip($this->ids);
171 switch ($_display) {
172 case 1:
173 foreach ($this->overview as $i=>$p) {
174 if (isset($this->overview[$i]) &&
175 !isset($this->overview[$i]->parent) &&
176 ($this->overview[$i]->descunread==0)) {
177 $this->killdesc($i);
178 }
179 }
180 break;
181 case 2:
182 foreach ($this->overview as $i=>$p) {
183 if ($p->isread) {
184 unset($this->overview[$i]);
185 unset($flipids[$i]);
186 }
187 }
188 $this->ids=array_flip($flipids);
189 break;
190 }
191 }
192 }
193 return true;
194 }
195
196 /** kill post and childrens
197 * @param $_id MSGNUM of post
198 */
199
200 function killdesc($_id) {
201 if (sizeof($this->overview[$_id]->children)) {
202 foreach ($this->overview[$_id]->children as $c) {
203 $this->killdesc($c);
204 }
205 }
206 unset($this->overview[$_id]);
207 $msgid=array_search($this->ids,$_id);
208 if ($msgids) {
209 unset($this->ids[$msgid]);
210 }
211 # $flipid=array_flip($this->ids);
212 # unset($flipid[$id]);
213 # $this->ids=array_flip($flipid);
214 }
215
216 /** delete a post from overview
217 * @param $_id MSGNUM of post
218 */
219
220 function delid($_id) {
221 if (isset($this->overview[$_id])) {
222 if (sizeof($this->overview[$_id]->parent)) {
223 $this->overview[$this->overview[$_id]->parent]->children =
224 array_diff($this->overview[$this->overview[$_id]->parent]->children,
225 array($_id));
226 if (sizeof($this->overview[$_id]->children)) {
227 $this->overview[$this->overview[$_id]->parent]->children =
228 array_merge($this->overview[$this->overview[$_id]->parent]->children,
229 $this->overview[$_id]->children);
230 foreach ($this->overview[$_id]->children as $c) {
231 $this->overview[$c]->parent = $this->overview[$_id]->parent;
232 }
233 }
234 $p = $this->overview[$_id]->parent;
235 while ($p) {
236 $this->overview[$p]->desc--;
237 $p = $this->overview[$p]->parent;
238 }
239 } elseif (sizeof($this->overview[$_id]->children)) {
240 foreach ($this->overview[$_id]->children as $c) {
241 $this->overview[$c]->parent = null;
242 }
243 }
244 unset($this->overview[$_id]);
245 $msgid=array_search($this->ids,$_id);
246 if ($msgids) {
247 unset($this->ids[$msgid]);
248 }
249 # $ids = array_flip($this->ids);
250 # unset($ids[$_id]);
251 # $this->ids = array_flip($ids);
252 $prefix_path=(preg_match("/\/scripts\/?$/",getcwd())?"..":".");
253 $f = fopen("$prefix_path/spool/spool-{$this->group}.dat","w");
254 fputs($f,serialize($this));
255 fclose($f);
256 }
257 }
258
259 /** displays children tree of a post
260 * @param $_id INTEGER MSGNUM of post
261 * @param $_index INTEGER linear number of post in the tree
262 * @param $_first INTEGER linear number of first post displayed
263 * @param $_last INTEGER linear number of last post displayed
264 * @param $_ref STRING MSGNUM of current post
265 * @param $_pfx_node STRING prefix used for current node
266 * @param $_pfx_end STRING prefix used for children of current node
267 * @param $_head BOOLEAN true if first post in thread
268 */
269
270 function disp_desc($_id,$_index="",$_first=0,$_last=0,$_ref="",
271 $_pfx_node="", $_pfx_end="",$_head=true) {
272 global $css;
273 $debug = false;
274 $spfx_f = '<img src="img/k1.gif" height="21" width="9" alt="o" />';
275 $spfx_n = '<img src="img/k2.gif" height="21" width="9" alt="*" />';
276 $spfx_T = '<img src="img/T.gif" height="21" width="12" alt="+" />';
277 $spfx_L = '<img src="img/L.gif" height="21" width="12" alt="`" />';
278 $spfx_s = '<img src="img/s.gif" height="21" width="5" alt="-" />';
279 $spfx_e = '<img src="img/e.gif" height="21" width="12" alt="&nbsp;" />';
280 $spfx_I = '<img src="img/I.gif" height="21" width="12"alt="|" />';
281
282 if ($_index == "") $_index = $this->getndx($_id);
283
284 if (!sizeof($this->overview[$_id]->children) && ($_index<=$_last)
285 && ($_index>=$_first)) {
286 echo '<tr class="'.($_index%2?$css["pair"]:$css["impair"])."\">\n";
287 echo "<td class=\"{$css['date']}\">"
288 .formatSpoolHeader("date",$this->overview[$_id]->date,$_id,
289 $this->group,($_index==$_ref),$this->overview[$_id]->isread)
290 ." </td>\n";
291 echo "<td class=\"{$css['subject']}\"><div class=\"{$css['tree']}\">"
292 .$_pfx_node.($_head?$spfx_f:$spfx_s)."</div>"
293 .formatSpoolHeader("subject",$this->overview[$_id]->subject,$_id,
294 $this->group,($_index==$_ref),$this->overview[$_id]->isread)
295 .($debug?" $_id $_index ".
296 $this->overview[$_id]->desc." ".$this->overview[$_id]->descunread." ":"")." </td>\n";
297 echo "<td class=\"{$css['author']}\">"
298 .formatSpoolHeader("from",$this->overview[$_id]->from,$_id,
299 $this->group,($_index==$_ref),$this->overview[$_id]->isread)
300 ." </td>\n</tr>";
301 return true;
302 }
303 $children = $this->overview[$_id]->children;
304 if (($_index<=$_last) && ($_index>=$_first)) {
305 echo '<tr class="'.($_index%2?$css["pair"]:$css["impair"])."\">\n";
306 echo "<td class=\"{$css['date']}\">"
307 .formatSpoolHeader("date",$this->overview[$_id]->date,$_id,
308 $this->group,($_index==$_ref),$this->overview[$_id]->isread)
309 ." </td>\n";
310 echo "<td class=\"{$css['subject']}\"><div class=\"{$css['tree']}\">"
311 .$_pfx_node.$spfx_n."</div>"
312 .formatSpoolHeader("subject",$this->overview[$_id]->subject,$_id,
313 $this->group,($_index==$_ref),$this->overview[$_id]->isread)
314 .($debug?" $_id $_index ".
315 $this->overview[$_id]->desc." ".$this->overview[$_id]->descunread." ":"")." </td>\n";
316 echo "<td class=\"{$css['author']}\">"
317 .formatSpoolHeader("from",$this->overview[$_id]->from,$_id,
318 $this->group,($_index==$_ref),$this->overview[$_id]->isread)
319 ." </td>\n</tr>";
320 }
321 $index=$_index+1;
322 while ($child = array_shift($children)) {
323 if (($index+$this->overview[$child]->desc-1>=$_first)
324 ||($index<$_last)){
325 if (sizeof($children)) {
326 $this->disp_desc($child,$index,$_first,$_last,$_ref,
327 $_pfx_end.$spfx_T,$_pfx_end.$spfx_I,false);
328 } else {
329 $this->disp_desc($child,$index,$_first,$_last,$_ref,
330 $_pfx_end.$spfx_L,$_pfx_end.$spfx_e,false);
331 }
332 }
333 $index += $this->overview[$child]->desc;
334 }
335 }
336
337 /** Displays overview
338 * @param $_first INTEGER MSGNUM of first post
339 * @param $_last INTEGER MSGNUM of last post
340 * @param $_ref STRING MSGNUM of current/selectionned post
341 */
342
343 function disp($_first=0,$_last=0,$_ref="") {
344 global $css;
345 $index=1;
346 if (sizeof($this->overview)) {
347 foreach ($this->overview as $id=>$msg) {
348 if (!sizeof($msg->parent)) {
349 $this->disp_desc($id,$index,$_first,$_last,$_ref);
350 $index += $msg->desc;
351 }
352 }
353 } else {
354 echo "<tr class=\"{$css['pair']}\">\n";
355 echo "\t<td colspan=\"3\">\n";
356 echo "\t\tNo post in this newsgroup\n";
357 echo "\t</td>\n";
358 echo "</tr>\n";
359 }
360 }
361
362 /** computes linear post index
363 * @param $_id INTEGER MSGNUM of post
364 * @return INTEGER linear index of post
365 */
366
367 function getndx($_id) {
368 $ndx = 1;
369 // on remonte l'arbre
370 $id_parent = $this->overview[$_id]->parent;
371 $id_curr = $_id;
372 while (!is_null($id_parent)) {
373 for ($i=0; $i<array_search($id_curr,
374 $this->overview[$id_parent]->children) ; $i++) {
375 $ndx += $this->overview[$this->overview[$id_parent]->children[$i]]->desc;
376 }
377 $ndx++; //noeud père
378 $id_curr = $id_parent;
379 $id_parent = $this->overview[$id_curr]->parent;
380 }
381 // on compte les threads précédents
382 foreach ($this->overview as $i=>$p) {
383 if ($i==$id_curr) {
384 break;
385 }
386 if (is_null($p->parent)) {
387 $ndx += $this->overview[$i]->desc;
388 }
389 }
390 return $ndx;
391 }
392 }
393
394 ?>