]>
git.p6c8.net - selfforum.git/blob - selfforum-cgi/shared/Posting/_lib.pm
3 ################################################################################
5 # File: shared/Posting/_lib.pm #
7 # Authors: André Malo <nd@o3media.de>, 2001-03-03 #
8 # Frank Schoenmann <fs@tower.de>, 2001-03-13 #
10 # Description: Message access interface, time format routines #
12 ################################################################################
16 use Encode
::Plain
; $Encode::Plain
::utf8
= 1;
20 # ====================================================
22 # ====================================================
24 use constant SORT_ASCENT
=> 0; # (young postings first)
25 use constant SORT_DESCENT
=> 1;
26 use constant KEEP_DELETED
=> 1;
27 use constant KILL_DELETED
=> 0;
29 use base
qw(Exporter);
30 @Posting::_lib
::EXPORT_OK
= qw(
31 get_message_header get_message_body get_message_node get_body_node parse_single_thread parse_xml_file
32 hr_time short_hr_time long_hr_time
33 get_all_threads create_forum_xml_string
35 SORT_ASCENT SORT_DESCENT KEEP_DELETED KILL_DELETED
38 # ====================================================
40 # ====================================================
42 ### get_message_header () ######################################################
44 # Read message header, return as a hash
46 # Params: $node XML message node
47 # Return: Hash reference (name, category, subject, email, home, image, time)
49 sub get_message_header
($)
54 my $header = $node -> getElementsByTagName
('Header', 0) -> item
(0);
55 my $author = $header -> getElementsByTagName
('Author', 0) -> item
(0);
56 my $name = $author -> getElementsByTagName
('Name', 0) -> item
(0);
57 my $email = $author -> getElementsByTagName
('Email', 0) -> item
(0);
58 my $home = $author -> getElementsByTagName
('HomepageUrl', 0) -> item
(0);
59 my $image = $author -> getElementsByTagName
('ImageUrl', 0) -> item
(0);
60 my $cat = $header -> getElementsByTagName
('Category', 0) -> item
(0);
61 my $subject = $header -> getElementsByTagName
('Subject', 0) -> item
(0);
62 my $date = $header -> getElementsByTagName
('Date', 0) -> item
(0);
65 name
=> ($name -> hasChildNodes
)?
$name -> getFirstChild
-> getData
:undef,
66 category
=> ($cat -> hasChildNodes
)?
$cat -> getFirstChild
-> getData
:undef,
67 subject
=> ($subject -> hasChildNodes
)?
$subject -> getFirstChild
-> getData
:undef,
68 email
=> (defined ($email) and $email -> hasChildNodes
)?
$email -> getFirstChild
-> getData
:undef,
69 home
=> (defined ($home) and $home -> hasChildNodes
)?
$home -> getFirstChild
-> getData
:undef,
70 image
=> (defined ($image) and $image -> hasChildNodes
)?
$image -> getFirstChild
-> getData
:undef,
71 time => $date -> getAttribute
('longSec')
77 ### get_body_node () ########################################################
79 # Search a specific message body in a XML tree
81 # Params: $xml XML::DOM::Document Object (Document Node)
83 # Return: MessageContent XML node (or -none-)
85 sub get_body_node
($$)
89 for ($xml->getElementsByTagName ('ContentList', 1)->item (0)->getElementsByTagName ('MessageContent', 0))
91 return $_ if ($_ -> getAttribute
('mid') eq $mid);
97 ### get_message_body () ########################################################
101 # Params: $xml XML::DOM::Document Object (Document Node)
103 # Return: Scalar reference
105 sub get_message_body
($$)
107 my $cnode = get_body_node
($_[0], $_[1]);
110 $body = ($cnode -> hasChildNodes
)?
$cnode -> getFirstChild
-> getData
:'' if $cnode;
115 ### get_message_node () ########################################################
117 # Search a specific message in a XML tree
119 # Params: $xml XML::DOM::Document Object (Document Node)
122 # Return: Message XML node, Thread XML node (or -none-)
124 sub get_message_node
($$$)
126 my ($xml, $tid, $mid) = @_;
129 for ($xml->getElementsByTagName ('Thread'))
131 if ($_->getAttribute ('id') eq $tid)
134 for ($tnode -> getElementsByTagName
('Message'))
136 if ($_ -> getAttribute
('id') eq $mid)
146 wantarray ?
($mnode, $tnode) : $mnode;
149 ### sub parse_xml_file ($) #####################################################
151 # load the specified XML-File and create the DOM tree
152 # this sub is only to avoid errors and to centralize the parse process
154 # Params: $file filename
155 # Return: XML::DOM::Document Object (Document Node) or false
157 sub parse_xml_file
($) {
161 new XML
::DOM
::Parser
(KeepCDATA
=> 1) -> parsefile
($file);
169 ###########################
170 # sub parse_single_thread
172 # einzelne Threaddatei
174 ###########################
176 sub parse_single_thread
($$;$) {
177 my ($tnode, $deleted, $sorted) = @_;
178 my ($header, @msg, %mno);
180 for ($tnode -> getElementsByTagName
('Message')) {
181 $header = get_message_header
($_);
183 push @msg,{mid
=> ($_ -> getAttribute
('id') =~ /(\d+)/)[0],
184 ip
=> $_ -> getAttribute
('ip'),
185 kids
=> [$_ -> getElementsByTagName
('Message', 0)],
186 answers
=> $_ -> getElementsByTagName
('Message') -> getLength
,
187 deleted
=> $_ -> getAttribute
('invisible'),
188 archive
=> $_ -> getAttribute
('archive'),
189 name
=> plain
($header -> {name
}),
190 cat
=> plain
($header -> {category
} or ''),
191 subject
=> plain
($header -> {subject
}),
192 time => plain
($header -> {time})};
195 # Eintraege ergaenzen und korrigieren
197 $msg[0] -> {level
} = 0;
199 $level = $_ -> {level
} + 1;
200 @
{$_ -> {kids
}} = map {$msg[$mno{$_}] -> {level
} = $level; $mno{$_}} @
{$_ -> {kids
}};}
203 # Sortieren und bei Bedarf
204 # geloeschte Messages entfernen
206 my $smsg = sort_thread
(\
@msg, $sorted);
207 delete_messages
($smsg) unless ($deleted);
212 ###########################
213 # sub create_message_xml
217 ###########################
219 sub create_message_xml
($$$) {
220 my ($xml, $msges, $num) = @_;
222 my $msg = $msges -> [$num];
224 my $message = $xml -> createElement
('Message');
225 $message -> setAttribute
('id', 'm'.$msg -> {mid
});
226 $message -> setAttribute
('invisible', '1') if ($msg -> {deleted
});
227 $message -> setAttribute
('archive', '1') if ($msg -> {archive
});
230 my $header = $xml -> createElement
('Header');
232 # alles inside of 'Header'
233 my $author = $xml -> createElement
('Author');
235 my $name = $xml -> createElement
('Name');
236 $name -> addText
(toUTF8
($msg -> {name
}));
238 my $email = $xml -> createElement
('Email');
240 my $category = $xml -> createElement
('Category');
241 $category -> addText
(toUTF8
($msg -> {cat
}));
243 my $subject = $xml -> createElement
('Subject');
244 $subject -> addText
(toUTF8
($msg -> {subject
}));
246 my $date = $xml -> createElement
('Date');
247 $date -> setAttribute
('longSec', $msg -> {time});
249 $author -> appendChild
($name);
250 $author -> appendChild
($email);
251 $header -> appendChild
($author);
252 $header -> appendChild
($category);
253 $header -> appendChild
($subject);
254 $header -> appendChild
($date);
255 $message -> appendChild
($header);
257 if ($msg -> {kids
}) {
258 for (@
{$msg -> {kids
}}) {
259 $message -> appendChild
(&create_message_xml
($xml, $msges, $_));
266 # ====================================================
267 # XML-Parsen von Hand
268 # ====================================================
270 ###########################
275 ###########################
277 sub sort_thread
($$) {
278 my ($msg, $sorted) = @_;
280 my ($z, %mhash) = (0);
282 if ($sorted) { # aelteste zuerst
284 @
$msg[@
{$_ -> {kids
}}] = sort {$a -> {mid
} <=> $b -> {mid
}} @
$msg[@
{$_ -> {kids
}}] if (@
{$_ -> {kids
}} > 1);
285 $mhash{$_ -> {mid
}} = [@
$msg[@
{$_ -> {kids
}}]];}}
287 else { # juengste zuerst
289 @
$msg[@
{$_ -> {kids
}}] = sort {$b -> {mid
} <=> $a -> {mid
}} @
$msg[@
{$_ -> {kids
}}] if (@
{$_ -> {kids
}} > 1);
290 $mhash{$_ -> {mid
}} = [@
$msg[@
{$_ -> {kids
}}]];}}
292 # Kinder wieder richtig einsortieren
293 my @smsg = ($msg -> [0]);
296 splice @smsg,$z,0,@
{$mhash{$_ -> {mid
}}} if ($_ -> {answers
});
297 delete $_ -> {kids
};}
302 ### delete_messages () #########################################################
304 # Filter out deleted messages
306 # Params: $smsg Reference of array of references of hashs
309 sub delete_messages
($)
313 my ($z, $oldlevel, @path) = (0,0,0);
317 if ($_ -> {'deleted'})
319 my $n = $_ -> {'answers'} + 1;
320 $smsg -> [$_] -> {'answers'} -= $n for (@path);
325 if ($_ -> {'level'} > $oldlevel)
328 $oldlevel = $_ -> {'level'};
330 elsif ($_ -> {'level'} < $oldlevel)
332 splice @path,$_ -> {'level'} - $oldlevel;
333 $oldlevel = $_ -> {'level'};
347 ### get_all_threads () #########################################################
349 # Read and Parse the main file (without any XML-module, they are too slow)
351 # Params: $file /path/to/filename of the main file
352 # $deleted hold deleted (invisible) messages in result (1) oder not (0)
353 # $sorted direction of message sort: descending (0) (default) or ascending (1)
354 # Return: scalar context: hash reference
355 # list context: list (\%threads, $last_thread, $last_message, $dtd, \@unids)
357 sub get_all_threads
($$;$)
359 my ($file, $deleted, $sorted) = @_;
360 my ($last_thread, $last_message, $dtd, @unids, %threads);
363 open FILE
, $file or return undef;
364 my $xml = join '', <FILE
>;
365 close(FILE
) or return undef;
369 ($dtd) = $xml =~ /<!DOCTYPE\s+\S+\s+SYSTEM\s+"([^"]+)">/;
370 ($last_thread) = map {/(\d+)/} $xml =~ /<Forum.+?lastThread="([^"]+)"[^>]*>/;
371 ($last_message) = map {/(\d+)/} $xml =~ /<Forum.+?lastMessage="([^"]+)"[^>]*>/;
374 my $reg_msg = qr
~(?
:</Message
>
375 |<Message\s
+id
="m(\d+)"\s
+unid
="([^"]*)"(?:\s+invisible="([^"]*)")?
(?
:\s
+archive
="([^"]*)")?[^>]*>\s*
376 <Header>[^<]*(?:<(?!Name>)[^<]*)*
377 <Name>([^<]+)</Name>[^<]*(?:<(?!Category>)[^<]*)*
378 <Category>([^<]*)</Category>\s*
379 <Subject>([^<]+)</Subject>\s*
380 <Date\s+longSec="(\d
+)"[^>]*>\s*
381 </Header>\s*(?:(<)/Message>|(?=(<)Message\s*)))~sx;
383 while ($xml =~ /<Thread id="t
(\d
+)">([^<]*(?:<(?!\/Thread>)[^<]*)*)<\/Thread>/g)
385 my ($tid, $thread) = ($1, $2);
386 my ($level, $cmno, @msg, @stack) = (0);
388 while ($thread =~ m;$reg_msg;g)
392 push @stack,$cmno if (defined $cmno);
393 push @msg, {mid => $1,
408 push @{$msg[$cmno] -> {kids}} => $#msg;
409 push @{$msg[$cmno] -> {unids}} => $2;
416 $msg[$_] -> {answers}++ for (@stack);
420 $msg[-1] -> {name} =~ s/&/&/g;
421 $msg[-1] -> {cat} =~ s/&/&/g;
422 $msg[-1] -> {subject} =~ s/&/&/g;
427 push @msg, {mid => $1,
442 push @{$msg[$cmno] -> {kids}} => $#msg;
443 push @{$msg[$cmno] -> {unids}} => $2;
444 $msg[$cmno] -> {answers}++;
451 $msg[$_] -> {answers}++ for (@stack);
453 $msg[-1] -> {name} =~ s/&/&/g;
454 $msg[-1] -> {cat} =~ s/&/&/g;
455 $msg[-1] -> {subject} =~ s/&/&/g;
459 $cmno = pop @stack; $level--;
463 my $smsg = sort_thread (\@msg, $sorted); # sort messages
464 delete_messages ($smsg) unless ($deleted); # remove invisible messages
466 $threads{$tid} = $smsg if (@$smsg);
470 ? (\%threads, $last_thread, $last_message, $dtd, \@unids)
474 ###########################
475 # sub create_forum_xml_string
477 # Forumshauptdatei erzeugen
478 ###########################
480 sub create_forum_xml_string ($$) {
481 my ($threads, $param) = @_;
482 my ($level, $thread, $msg);
484 my $xml = '<?xml version="1.0" encoding="UTF
-8"?>'."\n"
485 .'<!DOCTYPE Forum SYSTEM "'.$param -> {dtd}.'">'."\n"
486 .'<Forum lastMessage="'.$param -> {lastMessage}.'" lastThread="'.$param -> {lastThread}.'">';
488 foreach $thread (sort {$b <=> $a} keys %$threads) {
489 $xml .= '<Thread id="t
'.$thread.'">';
492 foreach $msg (@{$threads -> {$thread}}) {
493 $xml .= '</Message>' x ($level - $msg -> {level} + 1) if ($msg -> {level} <= $level);
494 $level = $msg -> {level};
495 $xml .= '<Message id="m
'.$msg -> {mid}.'"'
496 .' unid="'.$msg -> {unid}.'"'
497 .(($msg -> {deleted})?' invisible="1"':'')
498 .(($msg -> {archive})?' archive="1"':'')
503 .plain($msg -> {name})
508 .((length $msg -> {cat})?plain($msg -> {cat}):'')
511 .plain($msg -> {subject})
518 $xml .= '</Message>' x ($level + 1);
519 $xml .= '</Thread>';}
526 ### save_file () ###############################################################
530 # Params: $filename Filename
531 # $content File content as scalar reference
532 # Return: Status (1 - ok, 0 - error)
536 my ($filename, $content) = @_;
539 open FILE, ">$filename.temp
" or return;
541 unless (print FILE $$content)
547 close FILE or return;
549 rename "$filename.temp
", $filename or return;
554 # ====================================================
556 # ====================================================
558 ###########################
560 # 02. Januar 2001, 12:02 Uhr
563 # 02. 01. 2001, 12:02 Uhr
566 # Dienstag, 02. Januar 2001, 12:02:01 Uhr
568 # formatierte Zeitangabe
569 ###########################
572 my @month = (qw(Januar Februar), "M\303\244rz", qw(April Mail Juni Juli August September Oktober November Dezember));
575 my (undef, $min, $hour, $day, $mon, $year) = localtime ($_[0]);
577 sprintf ('%02d. %s %04d, %02d:%02d Uhr', $day, $month[$mon], $year+1900, $hour, $min);
580 sub short_hr_time
($) {
581 my (undef, $min, $hour, $day, $mon, $year) = localtime ($_[0]);
583 sprintf ('%02d. %02d. %04d, %02d:%02d Uhr', $day, $mon+1, $year+1900, $hour, $min);
586 sub long_hr_time
($) {
587 my @month = (qw(Januar Februar), "M\303\244rz", qw(April Mail Juni Juli August September Oktober November Dezember));
590 my @wday = qw(Sonntag Montag Dienstag Mittwoch Donnerstag Freitag Samstag);
591 my ($sek, $min, $hour, $day, $mon, $year, $wday) = localtime ($_[0]);
593 sprintf ('%s, %02d. %s %04d, %02d:%02d:%02d Uhr', $wday[$wday], $day, $month[$mon], $year+1900, $hour, $min, $sek);
596 # ====================================================
597 # Modulinitialisierung
598 # ====================================================
600 # making require happy
patrick-canterino.de