# File: shared/Posting/_lib.pm #
# #
# Authors: André Malo <nd@o3media.de>, 2001-03-03 #
-# Frank Schoenmann <fs@tower.de>, 2001-03-02 #
+# Frank Schoenmann <fs@tower.de>, 2001-03-13 #
# #
# Description: Message access interface, time format routines #
# #
use strict;
-use vars qw(@EXPORT_OK);
-use base qw(Exporter);
-
use Encode::Plain; $Encode::Plain::utf8 = 1;
use XML::DOM;
# Export
# ====================================================
-@EXPORT_OK = qw(get_message_header get_message_body get_message_node get_body_node parse_single_thread
- hr_time short_hr_time long_hr_time
- get_all_threads create_forum_xml_string
- save_file);
+use constant SORT_ASCENT => 0; # (young postings first)
+use constant SORT_DESCENT => 1;
+use constant KEEP_DELETED => 1;
+use constant KILL_DELETED => 0;
+
+use base qw(Exporter);
+@Posting::_lib::EXPORT_OK = qw(
+ get_message_header
+ get_message_body
+ get_message_node
+ get_body_node
+ parse_single_thread
+ parse_xml_file
+ create_new_thread
+ create_message
+
+ hr_time
+ short_hr_time
+ long_hr_time
+
+ get_all_threads
+ create_forum_xml_string
+
+ save_file
+
+ SORT_ASCENT
+ SORT_DESCENT
+ KEEP_DELETED
+ KILL_DELETED
+);
# ====================================================
# Access via XML::DOM
# ====================================================
+### sub create_message ($$) ####################################################
+#
+# create the 'Message' subtree
+#
+# Params: $xml - XML::DOM::Document object
+# $par - hash reference
+# (msg, ip, name, email, home, image, category, subject, time)
+#
+# Return: XML::DOM::Element object
+#
+sub create_message ($$) {
+ my ($xml,$par) = @_;
+
+ my $message = $xml -> createElement ('Message');
+ $message -> setAttribute ('id' => $par -> {msg});
+ $message -> setAttribute ('ip' => $par -> {ip});
+
+ my $header = $xml -> createElement ('Header');
+ my $author = $xml -> createElement ('Author');
+ $header -> appendChild ($author);
+
+ my @may = (
+ ['name' => 'Name' => $author],
+ ['email' => 'Email' => $author],
+ ['home' => 'HomepageUrl' => $author],
+ ['image' => 'ImageUrl' => $author],
+ ['category' => 'Category' => $header],
+ ['subject' => 'Subject' => $header]
+ );# key => element name => superior
+
+ for (@may) {
+
+ # create element
+ my $obj = $xml -> createElement ($_->[1]);
+
+ # insert content
+ $obj -> addText (
+ defined $par -> {$_->[0]}
+ ? $par -> {$_->[0]}
+ : ''
+ );
+
+ # link to superior element
+ $_ -> [2] -> appendChild ($obj);
+ }
+
+ my $date = $xml -> createElement ('Date');
+ $date -> setAttribute ('longSec'=> $par -> {time});
+
+ $header -> appendChild ($date);
+ $message -> appendChild ($header);
+
+ # return
+ #
+ $message;
+}
+
+### sub create_new_thread ($) ##################################################
+#
+# create a XML::DOM::Document object of a thread containing one posting
+#
+# Params: hash reference
+# (dtd, thread, msg, body, ip, name, email, home,
+# image, category, subject, time)
+#
+# Return: XML::DOM::Document object
+#
+sub create_new_thread ($) {
+ my $par = shift;
+
+ # new document
+ #
+ my $xml = new XML::DOM::Document;
+
+ # xml declaration
+ #
+ my $decl = new XML::DOM::XMLDecl;
+ $decl -> setVersion ('1.0');
+ $decl -> setEncoding ('UTF-8');
+ $xml -> setXMLDecl ($decl);
+
+ # set doctype
+ #
+ my $dtd = $xml -> createDocumentType ('Forum' => $par -> {dtd});
+ $xml -> setDoctype ($dtd);
+
+ # create root element 'Forum'
+ # create element 'Thread'
+ # create 'Message' subtree
+ # create element 'ContentList'
+ # create 'MessageContent' subtree
+ #
+ my $forum = $xml -> createElement ('Forum');
+ my $thread = $xml -> createElement ('Thread');
+ $thread -> setAttribute ('id' => $par -> {thread});
+ my $message = create_message ($xml,$par);
+ my $content = $xml -> createElement ('ContentList');
+ my $mcontent = $xml -> createElement ('MessageContent');
+ $mcontent -> setAttribute ('mid' => $par -> {msg});
+ $mcontent -> appendChild (
+ $xml -> createCDATASection (${$par -> {body}})
+ );
+
+ # link all the nodes to
+ # their superior elements
+ #
+ $thread -> appendChild ($message);
+ $forum -> appendChild ($thread);
+ $content -> appendChild ($mcontent);
+ $forum -> appendChild ($content);
+ $xml -> appendChild ($forum);
+
+ # return
+ #
+ $xml;
+}
+
### get_message_header () ######################################################
#
# Read message header, return as a hash
#
-# Params: $node XML message node
-# Return: Hash reference (name, category, subject, email, home, image, time)
+# Params: $node - XML message node
+# Return: hash reference (name, category, subject, email, home, image, time)
#
sub get_message_header ($)
{
my $node = shift;
my %conf;
- my $header = $node -> getElementsByTagName ('Header', 0) -> item (0);
- my $author = $header -> getElementsByTagName ('Author', 0) -> item (0);
- my $name = $author -> getElementsByTagName ('Name', 0) -> item (0);
- my $email = $author -> getElementsByTagName ('Email', 0) -> item (0);
+ my $header = $node -> getElementsByTagName ('Header' , 0) -> item (0);
+ my $author = $header -> getElementsByTagName ('Author' , 0) -> item (0);
+ my $name = $author -> getElementsByTagName ('Name' , 0) -> item (0);
+ my $email = $author -> getElementsByTagName ('Email' , 0) -> item (0);
my $home = $author -> getElementsByTagName ('HomepageUrl', 0) -> item (0);
- my $image = $author -> getElementsByTagName ('ImageUrl', 0) -> item (0);
- my $cat = $header -> getElementsByTagName ('Category', 0) -> item (0);
- my $subject = $header -> getElementsByTagName ('Subject', 0) -> item (0);
- my $date = $header -> getElementsByTagName ('Date', 0) -> item (0);
+ my $image = $author -> getElementsByTagName ('ImageUrl' , 0) -> item (0);
+ my $cat = $header -> getElementsByTagName ('Category' , 0) -> item (0);
+ my $subject = $header -> getElementsByTagName ('Subject' , 0) -> item (0);
+ my $date = $header -> getElementsByTagName ('Date' , 0) -> item (0);
%conf = (
name => ($name -> hasChildNodes)?$name -> getFirstChild -> getData:undef,
#
# Params: $xml XML::DOM::Document Object (Document Node)
# $mid Message ID
+#
# Return: MessageContent XML node (or -none-)
#
sub get_body_node ($$)
{
my ($xml, $mid) = @_;
- for ($xml->getElementsByTagName ('ContentList', 1)->item (0)->getElementsByTagName ('MessageContent', 0))
- {
+ for ($xml -> getElementsByTagName ('ContentList', 1) -> item (0) -> getElementsByTagName ('MessageContent', 0)) {
return $_ if ($_ -> getAttribute ('mid') eq $mid);
}
#
# Params: $xml XML::DOM::Document Object (Document Node)
# $mid Message ID
+#
# Return: Scalar reference
#
sub get_message_body ($$)
# Params: $xml XML::DOM::Document Object (Document Node)
# $tid Thread ID
# $mid Message ID
+#
# Return: Message XML node, Thread XML node (or -none-)
#
sub get_message_node ($$$)
my ($xml, $tid, $mid) = @_;
my ($mnode, $tnode);
- for ($xml->getElementsByTagName ('Thread'))
- {
- if ($_->getAttribute ('id') eq $tid)
- {
+ for ($xml->getElementsByTagName ('Thread')) {
+ if ($_->getAttribute ('id') eq $tid) {
$tnode = $_;
- for ($tnode -> getElementsByTagName ('Message'))
- {
- if ($_ -> getAttribute ('id') eq $mid)
- {
+
+ for ($tnode -> getElementsByTagName ('Message')) {
+ if ($_ -> getAttribute ('id') eq $mid) {
$mnode = $_;
last;
}
}
}
- wantarray ? ($mnode, $tnode) : $mnode;
+ wantarray
+ ? ($mnode, $tnode)
+ : $mnode;
+}
+
+### sub parse_xml_file ($) #####################################################
+#
+# load the specified XML-File and create the DOM tree
+# this sub is only to avoid errors and to centralize the parse process
+#
+# Params: $file filename
+#
+# Return: XML::DOM::Document Object (Document Node) or false
+#
+sub parse_xml_file ($) {
+ my $file = shift;
+
+ my $xml = eval {
+ local $SIG{__DIE__}; # CGI::Carp works unreliable ;-(
+ new XML::DOM::Parser (KeepCDATA => 1) -> parsefile ($file);
+ };
+
+ return if ($@);
+
+ $xml;
}
###########################
\@smsg;
}
-### delete_messages () ##########################################################
+### delete_messages () #########################################################
#
# Filter out deleted messages
#
# Params: $smsg Reference of array of references of hashs
# Return: -none-
#
-sub delete_messages ($)
-{
+sub delete_messages ($) {
my $smsg = shift;
-
my ($z, $oldlevel, @path) = (0,0,0);
- for (@$smsg)
- {
- if ($_ -> {'deleted'})
- {
- my $n = $_ -> {'answers'} + 1;
- $smsg -> [$_] -> {'answers'} -= $n for (@path);
- splice @$smsg,$z,$n;
+ while ($z <= $#{$smsg}) {
+
+ if ($smsg -> [$z] -> {level} > $oldlevel) {
+ push @path => $z;
+ $oldlevel = $smsg -> [$z] -> {level};
+ }
+ elsif ($smsg -> [$z] -> {level} < $oldlevel) {
+ splice @path, $smsg -> [$z] -> {level};
+ push @path => $z;
+ $oldlevel = $smsg -> [$z] -> {'level'};
+ }
+ else {
+ $path[-1] = $z;
}
- else
- {
- if ($_ -> {'level'} > $oldlevel)
- {
- push @path,$z;
- $oldlevel = $_ -> {'level'};
- }
- elsif ($_ -> {'level'} < $oldlevel)
- {
- splice @path,$_ -> {'level'} - $oldlevel;
- $oldlevel = $_ -> {'level'};
- }
- else
- {
- $path[-1] = $z;
- }
+ if ($smsg -> [$z] -> {deleted}) {
+ my $n = $smsg -> [$z] -> {answers} + 1;
+ $smsg -> [$_] -> {answers} -= $n for (@path);
+ splice @$smsg, $z, $n;
+ }
+ else {
$z++;
}
}
return;
}
-### get_all_threads () ##########################################################
+### get_all_threads () #########################################################
#
# Read and Parse the main file (without any XML-module, they are too slow)
#
# $deleted hold deleted (invisible) messages in result (1) oder not (0)
# $sorted direction of message sort: descending (0) (default) or ascending (1)
# Return: scalar context: hash reference
-# list context: list (\%threads, $last_thread, $last_message, \@unids)
+# list context: list (\%threads, $last_thread, $last_message, $dtd, \@unids)
#
sub get_all_threads ($$;$)
{
my ($file, $deleted, $sorted) = @_;
- my ($last_thread, $last_message, @unids, %threads);
+ my ($last_thread, $last_message, $dtd, @unids, %threads);
local (*FILE, $/);
- open FILE, $file or return undef;
+ open FILE,"< $file" or return;
my $xml = join '', <FILE>;
- close(FILE) or return undef;
+ close(FILE) or return;
if (wantarray)
{
+ ($dtd) = $xml =~ /<!DOCTYPE\s+\S+\s+SYSTEM\s+"([^"]+)">/;
($last_thread) = map {/(\d+)/} $xml =~ /<Forum.+?lastThread="([^"]+)"[^>]*>/;
($last_message) = map {/(\d+)/} $xml =~ /<Forum.+?lastMessage="([^"]+)"[^>]*>/;
}
if (defined($10))
{
push @stack,$cmno if (defined $cmno);
- push @msg, {mid => $1,
- unid => $2,
- deleted => $3,
- archive => $4,
- name => $5,
- cat => $6,
- subject => $7,
- time => $8,
- level => $level++,
- unids => [],
- kids => [],
- answers => 0};
+ push @msg, {
+ mid => $1,
+ unid => $2,
+ deleted => $3 || 0,
+ archive => $4 || 0,
+ name => $5,
+ cat => $6,
+ subject => $7,
+ time => $8,
+ level => $level++,
+ unids => [],
+ kids => [],
+ answers => 0
+ };
if (defined $cmno)
{
}
elsif (defined ($9))
{
- push @msg, {mid => $1,
- unid => $2,
- deleted => $3,
- archive => $4,
- name => $5,
- cat => $6,
- subject => $7,
- time => $8,
- level => $level,
- unids => [],
- kids => [],
- answers => 0};
+ push @msg, {
+ mid => $1,
+ unid => $2,
+ deleted => $3 || 0,
+ archive => $4 || 0,
+ name => $5,
+ cat => $6,
+ subject => $7,
+ time => $8,
+ level => $level,
+ unids => [],
+ kids => [],
+ answers => 0
+ };
if (defined $cmno)
{
$threads{$tid} = $smsg if (@$smsg);
}
- wantarray ?
- (\%threads, $last_thread, $last_message, \@unids)
- : \%threads;
+ wantarray
+ ? (\%threads, $last_thread, $last_message, $dtd, \@unids)
+ : \%threads;
}
###########################