]>
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-06-04 #
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(
47 create_forum_xml_string
57 # ====================================================
59 # ====================================================
61 ### sub create_message ($$) ####################################################
63 # create the 'Message' subtree
65 # Params: $xml - XML::DOM::Document object
66 # $par - hash reference
67 # (msg, ip, name, email, home, image, category, subject, time)
69 # Return: XML::DOM::Element object
71 sub create_message
($$) {
74 my $message = $xml -> createElement
('Message');
75 $message -> setAttribute
('id' => $par -> {msg
});
76 $message -> setAttribute
('ip' => $par -> {ip
});
78 my $header = $xml -> createElement
('Header');
79 my $author = $xml -> createElement
('Author');
80 $header -> appendChild
($author);
83 ['name' => 'Name' => $author],
84 ['email' => 'Email' => $author],
85 ['home' => 'HomepageUrl' => $author],
86 ['image' => 'ImageUrl' => $author],
87 ['category' => 'Category' => $header],
88 ['subject' => 'Subject' => $header]
89 );# key => element name => superior
94 my $obj = $xml -> createElement
($_->[1]);
98 defined $par -> {$_->[0]}
103 # link to superior element
104 $_ -> [2] -> appendChild
($obj);
107 my $date = $xml -> createElement
('Date');
108 $date -> setAttribute
('longSec'=> $par -> {time});
110 $header -> appendChild
($date);
111 $message -> appendChild
($header);
118 ### sub create_new_thread ($) ##################################################
120 # create a XML::DOM::Document object of a thread containing one posting
122 # Params: hash reference
123 # (dtd, thread, msg, body, ip, name, email, home,
124 # image, category, subject, time)
126 # Return: XML::DOM::Document object
128 sub create_new_thread
($) {
133 my $xml = new XML
::DOM
::Document
;
137 my $decl = new XML
::DOM
::XMLDecl
;
138 $decl -> setVersion
('1.0');
139 $decl -> setEncoding
('UTF-8');
140 $xml -> setXMLDecl
($decl);
144 my $dtd = $xml -> createDocumentType
('Forum' => $par -> {dtd
});
145 $xml -> setDoctype
($dtd);
147 # create root element 'Forum'
148 # create element 'Thread'
149 # create 'Message' subtree
150 # create element 'ContentList'
151 # create 'MessageContent' subtree
153 my $forum = $xml -> createElement
('Forum');
154 my $thread = $xml -> createElement
('Thread');
155 $thread -> setAttribute
('id' => $par -> {thread
});
156 my $message = create_message
($xml,$par);
157 my $content = $xml -> createElement
('ContentList');
158 my $mcontent = $xml -> createElement
('MessageContent');
159 $mcontent -> setAttribute
('mid' => $par -> {msg
});
160 $mcontent -> appendChild
(
161 $xml -> createCDATASection
(${$par -> {body
}})
164 # link all the nodes to
165 # their superior elements
167 $thread -> appendChild
($message);
168 $forum -> appendChild
($thread);
169 $content -> appendChild
($mcontent);
170 $forum -> appendChild
($content);
171 $xml -> appendChild
($forum);
178 ### get_message_header () ######################################################
180 # Read message header, return as a hash
182 # Params: $node - XML message node
183 # Return: hash reference (name, category, subject, email, home, image, time)
185 sub get_message_header
($)
190 my $header = $node -> getElementsByTagName
('Header' , 0) -> item
(0);
191 my $author = $header -> getElementsByTagName
('Author' , 0) -> item
(0);
192 my $name = $author -> getElementsByTagName
('Name' , 0) -> item
(0);
193 my $email = $author -> getElementsByTagName
('Email' , 0) -> item
(0);
194 my $home = $author -> getElementsByTagName
('HomepageUrl', 0) -> item
(0);
195 my $image = $author -> getElementsByTagName
('ImageUrl' , 0) -> item
(0);
196 my $cat = $header -> getElementsByTagName
('Category' , 0) -> item
(0);
197 my $subject = $header -> getElementsByTagName
('Subject' , 0) -> item
(0);
198 my $date = $header -> getElementsByTagName
('Date' , 0) -> item
(0);
201 name
=> ($name -> hasChildNodes
)?
$name -> getFirstChild
-> getData
:undef,
202 category
=> ($cat -> hasChildNodes
)?
$cat -> getFirstChild
-> getData
:undef,
203 subject
=> ($subject -> hasChildNodes
)?
$subject -> getFirstChild
-> getData
:undef,
204 email
=> (defined ($email) and $email -> hasChildNodes
)?
$email -> getFirstChild
-> getData
:undef,
205 home
=> (defined ($home) and $home -> hasChildNodes
)?
$home -> getFirstChild
-> getData
:undef,
206 image
=> (defined ($image) and $image -> hasChildNodes
)?
$image -> getFirstChild
-> getData
:undef,
207 time => $date -> getAttribute
('longSec')
213 ### get_body_node () ########################################################
215 # Search a specific message body in a XML tree
217 # Params: $xml XML::DOM::Document Object (Document Node)
220 # Return: MessageContent XML node (or -none-)
222 sub get_body_node
($$)
224 my ($xml, $mid) = @_;
226 for ($xml -> getElementsByTagName
('ContentList', 1) -> item
(0) -> getElementsByTagName
('MessageContent', 0)) {
227 return $_ if ($_ -> getAttribute
('mid') eq $mid);
233 ### get_message_body () ########################################################
237 # Params: $xml XML::DOM::Document Object (Document Node)
240 # Return: Scalar reference
242 sub get_message_body
($$)
244 my $cnode = get_body_node
($_[0], $_[1]);
247 $body = ($cnode -> hasChildNodes
)?
$cnode -> getFirstChild
-> getData
:'' if $cnode;
252 ### get_message_node () ########################################################
254 # Search a specific message in a XML tree
256 # Params: $xml XML::DOM::Document Object (Document Node)
260 # Return: Message XML node, Thread XML node (or -none-)
262 sub get_message_node
($$$)
264 my ($xml, $tid, $mid) = @_;
267 for ($xml->getElementsByTagName ('Thread')) {
268 if ($_->getAttribute ('id') eq $tid) {
271 for ($tnode -> getElementsByTagName
('Message')) {
272 if ($_ -> getAttribute
('id') eq $mid) {
286 ### sub parse_xml_file ($) #####################################################
288 # load the specified XML-File and create the DOM tree
289 # this sub is only to avoid errors and to centralize the parse process
291 # Params: $file filename
293 # Return: XML::DOM::Document Object (Document Node) or false
295 sub parse_xml_file
($) {
299 local $SIG{__DIE__
}; # CGI::Carp works unreliable ;-(
300 new XML
::DOM
::Parser
(KeepCDATA
=> 1)->parsefile ($file);
308 ###########################
309 # sub parse_single_thread
311 # einzelne Threaddatei
313 ###########################
315 sub parse_single_thread
($$;$) {
316 my ($tnode, $deleted, $sorted) = @_;
317 my ($header, @msg, %mno);
319 for ($tnode -> getElementsByTagName
('Message')) {
320 $header = get_message_header
($_);
322 push @msg,{mid
=> ($_ -> getAttribute
('id') =~ /(\d+)/)[0],
323 ip
=> $_ -> getAttribute
('ip'),
324 kids
=> [$_ -> getElementsByTagName
('Message', 0)],
325 answers
=> $_ -> getElementsByTagName
('Message') -> getLength
,
326 deleted
=> $_ -> getAttribute
('invisible'),
327 archive
=> $_ -> getAttribute
('archive'),
328 name
=> plain
($header -> {name
}),
329 cat
=> plain
($header -> {category
} or ''),
330 subject
=> plain
($header -> {subject
}),
331 time => plain
($header -> {time})};
334 # Eintraege ergaenzen und korrigieren
336 $msg[0] -> {level
} = 0;
338 $level = $_ -> {level
} + 1;
339 @
{$_ -> {kids
}} = map {$msg[$mno{$_}] -> {level
} = $level; $mno{$_}} @
{$_ -> {kids
}};}
342 # Sortieren und bei Bedarf
343 # geloeschte Messages entfernen
345 my $smsg = sort_thread
(\
@msg, $sorted);
346 delete_messages
($smsg) unless ($deleted);
351 ###########################
352 # sub create_message_xml
356 ###########################
358 sub create_message_xml
($$$) {
359 my ($xml, $msges, $num) = @_;
361 my $msg = $msges -> [$num];
363 my $message = $xml -> createElement
('Message');
364 $message -> setAttribute
('id', 'm'.$msg -> {mid
});
365 $message -> setAttribute
('invisible', '1') if ($msg -> {deleted
});
366 $message -> setAttribute
('archive', '1') if ($msg -> {archive
});
369 my $header = $xml -> createElement
('Header');
371 # alles inside of 'Header'
372 my $author = $xml -> createElement
('Author');
374 my $name = $xml -> createElement
('Name');
375 $name -> addText
(toUTF8
($msg -> {name
}));
377 my $email = $xml -> createElement
('Email');
379 my $category = $xml -> createElement
('Category');
380 $category -> addText
(toUTF8
($msg -> {cat
}));
382 my $subject = $xml -> createElement
('Subject');
383 $subject -> addText
(toUTF8
($msg -> {subject
}));
385 my $date = $xml -> createElement
('Date');
386 $date -> setAttribute
('longSec', $msg -> {time});
388 $author -> appendChild
($name);
389 $author -> appendChild
($email);
390 $header -> appendChild
($author);
391 $header -> appendChild
($category);
392 $header -> appendChild
($subject);
393 $header -> appendChild
($date);
394 $message -> appendChild
($header);
396 if ($msg -> {kids
}) {
397 for (@
{$msg -> {kids
}}) {
398 $message -> appendChild
(&create_message_xml
($xml, $msges, $_));
405 # ====================================================
406 # XML-Parsen von Hand
407 # ====================================================
409 ###########################
414 ###########################
416 sub sort_thread
($$) {
417 my ($msg, $sorted) = @_;
419 my ($z, %mhash) = (0);
421 if ($sorted) { # aelteste zuerst
423 @
$msg[@
{$_ -> {kids
}}] = sort {$a -> {mid
} <=> $b -> {mid
}} @
$msg[@
{$_ -> {kids
}}] if (@
{$_ -> {kids
}} > 1);
424 $mhash{$_ -> {mid
}} = [@
$msg[@
{$_ -> {kids
}}]];}}
426 else { # juengste zuerst
428 @
$msg[@
{$_ -> {kids
}}] = sort {$b -> {mid
} <=> $a -> {mid
}} @
$msg[@
{$_ -> {kids
}}] if (@
{$_ -> {kids
}} > 1);
429 $mhash{$_ -> {mid
}} = [@
$msg[@
{$_ -> {kids
}}]];}}
431 # Kinder wieder richtig einsortieren
432 my @smsg = ($msg -> [0]);
435 splice @smsg,$z,0,@
{$mhash{$_ -> {mid
}}} if ($_ -> {answers
});
436 delete $_ -> {kids
};}
441 ### delete_messages () #########################################################
443 # Filter out deleted messages
445 # Params: $smsg Reference of array of references of hashs
448 sub delete_messages
($) {
450 my ($z, $oldlevel, @path) = (0,0,0);
452 while ($z <= $#{$smsg}) {
454 if ($smsg -> [$z] -> {level
} > $oldlevel) {
456 $oldlevel = $smsg -> [$z] -> {level
};
458 elsif ($smsg -> [$z] -> {level
} < $oldlevel) {
459 splice @path, $smsg -> [$z] -> {level
};
461 $oldlevel = $smsg -> [$z] -> {'level'};
467 if ($smsg -> [$z] -> {deleted
}) {
468 my $n = $smsg -> [$z] -> {answers
} + 1;
469 $smsg -> [$_] -> {answers
} -= $n for (@path);
470 splice @
$smsg, $z, $n;
480 ### get_all_threads () #########################################################
482 # Read and Parse the main file (without any XML-module, they are too slow)
484 # Params: $file /path/to/filename of the main file
485 # $deleted hold deleted (invisible) messages in result (1) oder not (0)
486 # $sorted direction of message sort: descending (0) (default) or ascending (1)
487 # Return: scalar context: hash reference
488 # list context: list (\%threads, $last_thread, $last_message, $dtd, \@unids)
490 sub get_all_threads
($$;$)
492 my ($file, $deleted, $sorted) = @_;
493 my ($last_thread, $last_message, $dtd, @unids, %threads);
496 open FILE
,"< $file" or return;
497 my $xml = join '', <FILE
>;
498 close(FILE
) or return;
502 ($dtd) = $xml =~ /<!DOCTYPE\s+\S+\s+SYSTEM\s+"([^"]+)">/;
503 ($last_thread) = map {/(\d+)/} $xml =~ /<Forum.+?lastThread="([^"]+)"[^>]*>/;
504 ($last_message) = map {/(\d+)/} $xml =~ /<Forum.+?lastMessage="([^"]+)"[^>]*>/;
507 my $reg_msg = qr
~(?
:</Message
>
508 |<Message\s
+id
="m(\d+)"\s
+unid
="([^"]*)"(?:\s+invisible="([^"]*)")?
(?
:\s
+archive
="([^"]*)")?[^>]*>\s*
509 <Header>[^<]*(?:<(?!Name>)[^<]*)*
510 <Name>([^<]+)</Name>[^<]*(?:<(?!Category>)[^<]*)*
511 <Category>([^<]*)</Category>\s*
512 <Subject>([^<]+)</Subject>\s*
513 <Date\s+longSec="(\d
+)"[^>]*>\s*
514 </Header>\s*(?:(<)/Message>|(?=(<)Message\s*)))~sx;
516 while ($xml =~ /<Thread id="t
(\d
+)">([^<]*(?:<(?!\/Thread>)[^<]*)*)<\/Thread>/g)
518 my ($tid, $thread) = ($1, $2);
519 my ($level, $cmno, @msg, @stack) = (0);
521 while ($thread =~ m;$reg_msg;g)
525 push @stack,$cmno if (defined $cmno);
543 push @{$msg[$cmno] -> {kids}} => $#msg;
544 push @{$msg[$cmno] -> {unids}} => $2;
551 $msg[$_] -> {answers}++ for (@stack);
555 $msg[-1] -> {name} =~ s/&/&/g;
556 $msg[-1] -> {cat} =~ s/&/&/g;
557 $msg[-1] -> {subject} =~ s/&/&/g;
579 push @{$msg[$cmno] -> {kids}} => $#msg;
580 push @{$msg[$cmno] -> {unids}} => $2;
581 $msg[$cmno] -> {answers}++;
588 $msg[$_] -> {answers}++ for (@stack);
590 $msg[-1] -> {name} =~ s/&/&/g;
591 $msg[-1] -> {cat} =~ s/&/&/g;
592 $msg[-1] -> {subject} =~ s/&/&/g;
596 $cmno = pop @stack; $level--;
600 my $smsg = sort_thread (\@msg, $sorted); # sort messages
601 delete_messages ($smsg) unless ($deleted); # remove invisible messages
603 $threads{$tid} = $smsg if (@$smsg);
607 ? (\%threads, $last_thread, $last_message, $dtd, \@unids)
611 ###########################
612 # sub create_forum_xml_string
614 # Forumshauptdatei erzeugen
615 ###########################
617 sub create_forum_xml_string ($$) {
618 my ($threads, $param) = @_;
619 my ($level, $thread, $msg);
621 my $xml = '<?xml version="1.0" encoding="UTF
-8"?>'."\n"
622 .'<!DOCTYPE Forum SYSTEM "'.$param -> {dtd}.'">'."\n"
623 .'<Forum lastMessage="'.$param -> {lastMessage}.'" lastThread="'.$param -> {lastThread}.'">';
625 foreach $thread (sort {$b <=> $a} keys %$threads) {
626 $xml .= '<Thread id="t
'.$thread.'">';
629 foreach $msg (@{$threads -> {$thread}}) {
630 $xml .= '</Message>' x ($level - $msg -> {level} + 1) if ($msg -> {level} <= $level);
631 $level = $msg -> {level};
632 $xml .= '<Message id="m
'.$msg -> {mid}.'"'
633 .' unid="'.$msg -> {unid}.'"'
634 .(($msg -> {deleted})?' invisible="1"':'')
635 .(($msg -> {archive})?' archive="1"':'')
640 .plain($msg -> {name})
645 .((length $msg -> {cat})?plain($msg -> {cat}):'')
648 .plain($msg -> {subject})
655 $xml .= '</Message>' x ($level + 1);
656 $xml .= '</Thread>';}
663 ### save_file () ###############################################################
667 # Params: $filename Filename
668 # $content File content as scalar reference
669 # Return: Status (1 - ok, 0 - error)
673 my ($filename, $content) = @_;
676 open FILE, ">$filename.temp
" or return;
678 unless (print FILE $$content)
684 close FILE or return;
686 rename "$filename.temp
", $filename or return;
691 # ====================================================
693 # ====================================================
695 ###########################
697 # 02. Januar 2001, 12:02 Uhr
700 # 02. 01. 2001, 12:02 Uhr
703 # Dienstag, 02. Januar 2001, 12:02:01 Uhr
705 # formatierte Zeitangabe
706 ###########################
709 my @month = (qw(Januar Februar), "M\303\244rz", qw(April Mai Juni Juli August September Oktober November Dezember));
712 my (undef, $min, $hour, $day, $mon, $year) = localtime ($_[0]);
714 sprintf ('%02d. %s %04d, %02d:%02d Uhr', $day, $month[$mon], $year+1900, $hour, $min);
717 sub short_hr_time
($) {
718 my (undef, $min, $hour, $day, $mon, $year) = localtime ($_[0]);
720 sprintf ('%02d. %02d. %04d, %02d:%02d Uhr', $day, $mon+1, $year+1900, $hour, $min);
723 sub long_hr_time
($) {
724 my @month = (qw(Januar Februar), "M\303\244rz", qw(April Mai Juni Juli August September Oktober November Dezember));
727 my @wday = qw(Sonntag Montag Dienstag Mittwoch Donnerstag Freitag Samstag);
728 my ($sek, $min, $hour, $day, $mon, $year, $wday) = localtime ($_[0]);
730 sprintf ('%s, %02d. %s %04d, %02d:%02d:%02d Uhr', $wday[$wday], $day, $month[$mon], $year+1900, $hour, $min, $sek);
733 sub very_short_hr_time
($) {
734 my (undef, $min, $hour, $day, $mon, $year) = localtime ($_[0]);
736 sprintf ('%02d. %02d. %04d', $day, $mon+1, $year+1900);
740 my @month = (qw(Januar Februar), "M\303\244rz", qw(April Mai Juni Juli August September Oktober November Dezember));
743 return $month[$_[0]-1];
746 # ====================================================
747 # Modulinitialisierung
748 # ====================================================
750 # making require happy
patrick-canterino.de