]> git.p6c8.net - selfforum.git/blob - selfforum-cgi/shared/Posting/_lib.pm
forum.dtd fixed, lockfiles removed from repository
[selfforum.git] / selfforum-cgi / shared / Posting / _lib.pm
1 # Posting/_lib.pm
2
3 # ====================================================
4 # Autor: n.d.p. / 2001-01-07
5 # lm : n.d.p. / 2001-02-25
6 # ====================================================
7 # Funktion:
8 # * Schnittstellen fuer den Zugriff auf Messages
9 # * Zeitdarstellung
10 # ====================================================
11
12 use strict;
13
14 package Posting::_lib;
15
16 use vars qw(@EXPORT_OK);
17 use base qw(Exporter);
18
19 use Encode::Plain; $Encode::Plain::utf8 = 1;
20
21 use XML::DOM;
22
23 # ====================================================
24 # Funktionsexport
25 # ====================================================
26
27 @EXPORT_OK = qw(get_message_header get_message_body get_message_node parse_single_thread
28 hr_time short_hr_time long_hr_time
29 get_all_threads
30 create_forum_xml_string
31 save_file);
32
33 # ====================================================
34 # Zugriff uebers DOM
35 # ====================================================
36
37 ###########################
38 # sub get_message_header
39 #
40 # Messageheader auslesen
41 ###########################
42
43 sub get_message_header ($) {
44 my $node = shift;
45 my %conf;
46
47 my $header = $node -> getElementsByTagName ('Header', 0) -> item (0);
48 my $author = $header -> getElementsByTagName ('Author', 0) -> item (0);
49 my $name = $author -> getElementsByTagName ('Name', 0) -> item (0);
50 my $email = $author -> getElementsByTagName ('Email', 0) -> item (0);
51 my $home = $author -> getElementsByTagName ('HomepageUrl', 0) -> item (0);
52 my $image = $author -> getElementsByTagName ('ImageUrl', 0) -> item (0);
53 my $cat = $header -> getElementsByTagName ('Category', 0) -> item (0);
54 my $subject = $header -> getElementsByTagName ('Subject', 0) -> item (0);
55 my $date = $header -> getElementsByTagName ('Date', 0) -> item (0);
56
57 %conf = (name => ($name -> hasChildNodes)?$name -> getFirstChild -> getData:undef,
58 category => ($cat -> hasChildNodes)?$cat -> getFirstChild -> getData:undef,
59 subject => ($subject -> hasChildNodes)?$subject -> getFirstChild -> getData:undef,
60 email => (defined ($email) and $email -> hasChildNodes)?$email -> getFirstChild -> getData:undef,
61 home => (defined ($home) and $home -> hasChildNodes)?$home -> getFirstChild -> getData:undef,
62 image => (defined ($image) and $image -> hasChildNodes)?$image -> getFirstChild -> getData:undef,
63 time => $date -> getAttribute ('longSec'));
64 \%conf;
65 }
66
67 ###########################
68 # sub get_message_header
69 #
70 # Messagebody auslesen
71 ###########################
72
73 sub get_message_body ($$)
74 {
75 my ($xml, $mid) = @_;
76 my $body;
77
78 foreach ($xml -> getElementsByTagName ('ContentList', 1) -> item (0) -> getElementsByTagName ('MessageContent', 0))
79 {
80 if ($_ -> getAttribute ('mid') eq $mid)
81 {
82 $body = ($_ -> hasChildNodes)?$_ -> getFirstChild -> getData:'';
83 last;
84 }
85 }
86
87 \$body;
88 }
89
90 ###########################
91 # sub get_message_header
92 #
93 # Messagenode bestimmen
94 ###########################
95
96 sub get_message_node ($$$) {
97 my ($xml,$tid,$mid) = @_;
98 my ($mnode,$tnode);
99
100 for ( $xml -> getElementsByTagName ('Thread')) {
101 if ($_ -> getAttribute ('id') eq $tid) {
102 $tnode = $_;
103 for ($tnode -> getElementsByTagName ('Message')) {
104 if ($_ -> getAttribute ('id') eq $mid) {
105 $mnode = $_;
106 last;}}
107 last;}}
108
109 wantarray?($mnode, $tnode):$mnode;
110 }
111
112 ###########################
113 # sub parse_single_thread
114 #
115 # einzelne Threaddatei
116 # parsen
117 ###########################
118
119 sub parse_single_thread ($$;$) {
120 my ($tnode, $deleted, $sorted) = @_;
121 my ($header, @msg, %mno);
122
123 for ($tnode -> getElementsByTagName ('Message')) {
124 $header = get_message_header ($_);
125
126 push @msg,{mid => ($_ -> getAttribute ('id') =~ /(\d+)/)[0],
127 ip => $_ -> getAttribute ('ip'),
128 kids => [$_ -> getElementsByTagName ('Message', 0)],
129 answers => $_ -> getElementsByTagName ('Message') -> getLength,
130 deleted => ($_ -> getAttribute ('flag') eq 'deleted')?1:0,
131 name => plain($header -> {name}),
132 cat => plain($header -> {category} or ''),
133 subject => plain($header -> {subject}),
134 time => plain($header -> {time})};
135 $mno{$_} = $#msg;}
136
137 # Eintraege ergaenzen und korrigieren
138 my $level;
139 $msg[0] -> {level} = 0;
140 for (@msg) {
141 $level = $_ -> {level} + 1;
142 @{$_ -> {kids}} = map {$msg[$mno{$_}] -> {level} = $level; $mno{$_}} @{$_ -> {kids}};}
143
144 # ============
145 # Sortieren und bei Bedarf
146 # geloeschte Messages entfernen
147
148 my $smsg = sort_thread (\@msg, $sorted);
149 delete_messages ($smsg) unless ($deleted);
150
151 $smsg;
152 }
153
154 ###########################
155 # sub create_message_xml
156 #
157 # Message-XML-String
158 # erzeugen
159 ###########################
160
161 sub create_message_xml ($$$) {
162 my ($xml, $msges, $num) = @_;
163
164 my $msg = $msges -> [$num];
165
166 my $message = $xml -> createElement ('Message');
167 $message -> setAttribute ('id', 'm'.$msg -> {mid});
168 $message -> setAttribute ('flag', 'deleted') if ($msg -> {deleted});
169
170 # Header erzeugen
171 my $header = $xml -> createElement ('Header');
172
173 # alles inside of 'Header'
174 my $author = $xml -> createElement ('Author');
175
176 my $name = $xml -> createElement ('Name');
177 $name -> addText (toUTF8($msg -> {name}));
178
179 my $email = $xml -> createElement ('Email');
180
181 my $category = $xml -> createElement ('Category');
182 $category -> addText (toUTF8($msg -> {cat}));
183
184 my $subject = $xml -> createElement ('Subject');
185 $subject -> addText (toUTF8($msg -> {subject}));
186
187 my $date = $xml -> createElement ('Date');
188 $date -> setAttribute ('longSec', $msg -> {time});
189
190 $author -> appendChild ($name);
191 $author -> appendChild ($email);
192 $header -> appendChild ($author);
193 $header -> appendChild ($category);
194 $header -> appendChild ($subject);
195 $header -> appendChild ($date);
196 $message -> appendChild ($header);
197
198 if ($msg -> {kids}) {
199 for (@{$msg -> {kids}}) {
200 $message -> appendChild (&create_message_xml ($xml, $msges, $_));
201 }
202 }
203
204 $message;
205 }
206
207 # ====================================================
208 # XML-Parsen von Hand
209 # ====================================================
210
211 ###########################
212 # sub sort_thread
213 #
214 # Messages eines
215 # Threads sortieren
216 ###########################
217
218 sub sort_thread ($$) {
219 my ($msg, $sorted) = @_;
220
221 my ($z, %mhash) = (0);
222
223 if ($sorted) { # aelteste zuerst
224 for (@$msg) {
225 @$msg[@{$_ -> {kids}}] = sort {$a -> {mid} <=> $b -> {mid}} @$msg[@{$_ -> {kids}}] if (@{$_ -> {kids}} > 1);
226 $mhash{$_ -> {mid}} = [@$msg[@{$_ -> {kids}}]];}}
227
228 else { # juengste zuerst
229 for (@$msg) {
230 @$msg[@{$_ -> {kids}}] = sort {$b -> {mid} <=> $a -> {mid}} @$msg[@{$_ -> {kids}}] if (@{$_ -> {kids}} > 1);
231 $mhash{$_ -> {mid}} = [@$msg[@{$_ -> {kids}}]];}}
232
233 # Kinder wieder richtig einsortieren
234 my @smsg = ($msg -> [0]);
235 for (@smsg) {
236 ++$z;
237 splice @smsg,$z,0,@{$mhash{$_ -> {mid}}} if ($_ -> {answers});
238 delete $_ -> {kids};}
239
240 \@smsg;
241 }
242
243 ###########################
244 # sub delete_messages
245 #
246 # geoeschte Nachrichten
247 # herausfiltern
248 ###########################
249
250 sub delete_messages ($) {
251 my $smsg = shift;
252
253 my ($z, $oldlevel, @path) = (0,0,0);
254
255 for (@$smsg) {
256 if ($_ -> {deleted}) {
257 my $n = $_ -> {answers}+1;
258 for (@path) {$smsg -> [$_] -> {answers} -= $n;}
259 splice @$smsg,$z,$n;}
260
261 else {
262 if ($_ -> {level} > $oldlevel) {
263 push @path,$z;
264 $oldlevel = $_ -> {level};}
265
266 elsif ($_ -> {level} < $oldlevel) {
267 splice @path,$_ -> {level}-$oldlevel;
268 $oldlevel = $_ -> {level};}
269
270 else { $path[-1] = $z; }
271
272 $z++;}}
273
274 return;
275 }
276
277 ###########################
278 # sub get_all_threads
279 #
280 # Hauptdatei laden und
281 # parsen
282 ###########################
283
284 sub get_all_threads ($$;$) {
285 my ($file, $deleted, $sorted) = @_;
286 my ($last_thread, $last_message, @unids, %threads);
287 local *FILE;
288
289 open FILE, $file or return undef;
290 my $xml = join '', <FILE>;
291 close(FILE) or return undef;
292
293 if (wantarray) {
294 ($last_thread) = map {/(\d+)/} $xml =~ /<Forum.+?lastThread="([^"]+)"[^>]*>/;
295 ($last_message) = map {/(\d+)/} $xml =~ /<Forum.+?lastMessage="([^"]+)"[^>]*>/;}
296
297 my $reg_msg = qr~(?:</Message>
298 |<Message\s+id="m(\d+)"\s+unid="([^"]*)"(?:\s+flag="([^"]*)")?[^>]*>\s*
299 <Header>[^<]*(?:<(?!Name>)[^<]*)*
300 <Name>([^<]+)</Name>[^<]*(?:<(?!Category>)[^<]*)*
301 <Category>([^<]*)</Category>\s*
302 <Subject>([^<]+)</Subject>\s*
303 <Date\s+longSec="(\d+)"[^>]*>\s*
304 </Header>\s*(?:(<)/Message>|(?=(<)Message\s*)))~sx;
305
306 while ($xml =~ /<Thread id="t(\d+)">([^<]*(?:<(?!\/Thread>)[^<]*)*)<\/Thread>/g) {
307
308 my ($tid, $thread) = ($1, $2);
309 my ($level, $cmno, @msg, @stack) = (0);
310
311 while ($thread =~ m;$reg_msg;g) {
312
313 if (defined($9)) {
314 push @stack,$cmno if (defined $cmno);
315 push @msg, {};
316
317 if (defined $cmno) {
318 push @{$msg[$cmno] -> {kids}} => $#msg;
319 push @{$msg[$cmno] -> {unids}} => $2;}
320 else {
321 push @unids => $2;}
322
323 for (@stack) {$msg[$_] -> {answers}++}
324
325 $cmno=$#msg;
326
327 ($msg[-1] -> {mid},
328 $msg[-1] -> {unid},
329 $msg[-1] -> {name},
330 $msg[-1] -> {cat},
331 $msg[-1] -> {subject},
332 $msg[-1] -> {time}) = ($1, $2, $4, $5, $6, $7);
333
334 $msg[-1] -> {deleted} = ($3 eq 'deleted')?1:undef;
335
336 $msg[-1] -> {name} =~ s/&amp;/&/g;
337 $msg[-1] -> {cat} =~ s/&amp;/&/g;
338 $msg[-1] -> {subject} =~ s/&amp;/&/g;
339
340 $msg[-1] -> {unids} = [];
341 $msg[-1] -> {kids} = [];
342 $msg[-1] -> {answers} = 0;
343 $msg[-1] -> {level} = $level++;}
344
345 elsif (defined ($8)) {
346 push @msg, {};
347
348 if (defined $cmno) {
349 push @{$msg[$cmno] -> {kids}} => $#msg;
350 push @{$msg[$cmno] -> {unids}} => $2;
351 $msg[$cmno] -> {answers}++;}
352 else {
353 push @unids => $2;}
354
355 for (@stack) {$msg[$_] -> {answers}++}
356
357 ($msg[-1] -> {mid},
358 $msg[-1] -> {unid},
359 $msg[-1] -> {name},
360 $msg[-1] -> {cat},
361 $msg[-1] -> {subject},
362 $msg[-1] -> {time}) = ($1, $2, $4, $5, $6, $7);
363
364 $msg[-1] -> {deleted} = ($3 eq 'deleted')?1:undef;
365
366 $msg[-1] -> {name} =~ s/&amp;/&/g;
367 $msg[-1] -> {cat} =~ s/&amp;/&/g;
368 $msg[-1] -> {subject} =~ s/&amp;/&/g;
369
370 $msg[-1] -> {level} = $level;
371 $msg[-1] -> {unids} = [];
372 $msg[-1] -> {kids} = [];
373 $msg[-1] -> {answers} = 0;}
374
375 else {
376 $cmno = pop @stack; $level--;}}
377
378 # ============
379 # Sortieren und bei Bedarf
380 # geloeschte Messages entfernen
381
382 my $smsg = sort_thread (\@msg, $sorted);
383 delete_messages ($smsg) unless ($deleted);
384
385 $threads{$tid} = $smsg if (@$smsg);
386 }
387
388 wantarray?(\%threads, $last_thread, $last_message, \@unids): \%threads;
389 }
390
391 ###########################
392 # sub create_forum_xml_string
393 #
394 # Forumshauptdatei erzeugen
395 ###########################
396
397 sub create_forum_xml_string ($$) {
398 my ($threads, $param) = @_;
399 my ($level, $thread, $msg);
400
401 my $xml = '<?xml version="1.0" encoding="UTF-8"?>'."\n"
402 .'<!DOCTYPE Forum SYSTEM "'.$param -> {dtd}.'">'."\n"
403 .'<Forum lastMessage="'.$param -> {lastMessage}.'" lastThread="'.$param -> {lastThread}.'">';
404
405 foreach $thread (sort {$b <=> $a} keys %$threads) {
406 $xml .= '<Thread id="t'.$thread.'">';
407 $level = -1;
408
409 foreach $msg (@{$threads -> {$thread}}) {
410 $xml .= '</Message>' x ($level - $msg -> {level} + 1) if ($msg -> {level} <= $level);
411 $level = $msg -> {level};
412 $xml .= '<Message id="m'.$msg -> {mid}.'"'
413 .' unid="'.$msg -> {unid}.'"'
414 .(($msg -> {deleted})?' flag="deleted"':'')
415 .'>'
416 .'<Header>'
417 .'<Author>'
418 .'<Name>'
419 .plain($msg -> {name})
420 .'</Name>'
421 .'<Email></Email>'
422 .'</Author>'
423 .'<Category>'
424 .((length $msg -> {cat})?plain($msg -> {cat}):'')
425 .'</Category>'
426 .'<Subject>'
427 .plain($msg -> {subject})
428 .'</Subject>'
429 .'<Date longSec="'
430 .$msg -> {time}
431 .'"/>'
432 .'</Header>';}
433
434 $xml .= '</Message>' x ($level + 1);
435 $xml .= '</Thread>';}
436
437 $xml.='</Forum>';
438
439 \$xml;
440 }
441
442 ###########################
443 # sub save_file
444 #
445 # Datei speichern
446 ###########################
447
448 sub save_file ($$) {
449 my ($filename,$content) = @_;
450 local *FILE;
451
452 open FILE,">$filename.temp" or return;
453
454 unless (print FILE $$content) {
455 close FILE;
456 return;};
457
458 close FILE or return;
459
460 rename "$filename.temp", $filename or return;
461
462 1;
463 }
464
465 # ====================================================
466 # Zeitdarstellung
467 # ====================================================
468
469 ###########################
470 # sub hr_time
471 # 02. Januar 2001, 12:02 Uhr
472 #
473 # sub short_hr_time
474 # 02. 01. 2001, 12:02 Uhr
475 #
476 # sub long_hr_time
477 # Dienstag, 02. Januar 2001, 12:02:01 Uhr
478 #
479 # formatierte Zeitangabe
480 ###########################
481
482 sub hr_time ($) {
483 my @month = qw(Januar Februar M\303\244rz April Mail Juni Juli August September Oktober November Dezember);
484 # ^^^^^^^^ - UTF8 #
485
486 my (undef, $min, $hour, $day, $mon, $year) = localtime ($_[0]);
487
488 sprintf ('%02d. %s %04d, %02d:%02d Uhr', $day, $month[$mon], $year+1900, $hour, $min);
489 }
490
491 sub short_hr_time ($) {
492 my (undef, $min, $hour, $day, $mon, $year) = localtime ($_[0]);
493
494 sprintf ('%02d. %02d. %04d, %02d:%02d Uhr', $day, $mon+1, $year+1900, $hour, $min);
495 }
496
497 sub long_hr_time ($) {
498 my @month = qw(Januar Februar M\303\244rz April Mail Juni Juli August September Oktober November Dezember);
499 # ^^^^^^^^ - UTF8 #
500
501 my @wday = qw(Sonntag Montag Dienstag Mittwoch Donnerstag Freitag Samstag);
502 my ($sek, $min, $hour, $day, $mon, $year, $wday) = localtime ($_[0]);
503
504 sprintf ('%s, %02d. %s %04d, %02d:%02d:%02d Uhr', $wday[$wday], $day, $month[$mon], $year+1900, $hour, $min, $sek);
505 }
506
507 # ====================================================
508 # Modulinitialisierung
509 # ====================================================
510
511 # making require happy
512 1;
513
514 # ====================================================
515 # end of Posting::_lib
516 # ====================================================

patrick-canterino.de