*** /dev/null Sat Nov 23 05:16:27 2024
--- - Sat Nov 23 05:16:52 2024
***************
*** 0 ****
--- 1,841 ----
+ #!/e/is/bin/perl
+ ##
+ ## xmldb.cgi -- XML Database Engine
+ ## Copyright (c) 2002 Ralf S. Engelschall <rse@engelschall.com>
+ ## Copyright (c) 2002 Cable & Wireless Deutschland <http://www.cw.com/de/>
+ ##
+ ## Permission to use, copy, modify, and distribute this software for
+ ## any purpose with or without fee is hereby granted, provided that
+ ## the above copyright notice and this permission notice appear in all
+ ## copies.
+ ##
+ ## THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ ## WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ ## MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ ## IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
+ ## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ ## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ ## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ ## USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ ## ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ ## OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ ## OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ ## SUCH DAMAGE.
+ ##
+
+ # requirements
+ require 5.006;
+ use IO; # standard in Perl 5.6
+ use POSIX; # standard in Perl 5.6
+ use CGI; # standard in Perl 5.6
+ use Fcntl ':flock'; # standard in Perl 5.6
+
+ use Data::Dumper;
+
+ # program configuration
+ my $MY = {
+ PROG_NAME => 'OSSP xmldb',
+ PROG_VERS => '0.1.0',
+ PROG_DESC => 'XML Database CGI Engine',
+ PROG_HOME => 'http://www.ossp.org/pkg/tool/xmldb/',
+ TEMPLATE => "<html>\n<head>\n\@HEAD\@\n</head>\n<body>\n\@BODY\@\n</body></html>"
+ };
+
+ # switch to unbuffered output
+ $|++;
+
+ # adjust CGI environment to make sure that self-referencing
+ # URLs go again through the direct URL of the MCT file!!
+ $ENV{SCRIPT_NAME} = $ENV{PATH_INFO};
+ delete $ENV{REQUEST_URI};
+
+ # remove and remember GET query strings
+ my $qs = '';
+ if ($ENV{QUERY_STRING} ne '') {
+ $qs = $ENV{QUERY_STRING};
+ delete $ENV{QUERY_STRING};
+ }
+
+ # establish CGI object
+ my $cgi = new CGI;
+
+ # re-insert GET query strings into CGI environment
+ if ($qs ne '') {
+ if ($qs =~ m|^([^=]+)=([^=]+)$|s) {
+ $cgi->param("action", "view");
+ $cgi->param("field", &url_unescape($1));
+ $cgi->param("key", &url_unescape($2));
+ }
+ }
+
+ # generate an HTTP response
+ sub http_response {
+ my ($head, $body) = @_;
+
+ # fill in template page
+ my $response = $MY->{TEMPLATE};
+ $response =~ s|\@HEAD\@|$head|sg;
+ $response =~ s|\@BODY\@|$body|sg;
+
+ # prepend with HTTP header
+ $response = $cgi->header(
+ -type => 'text/html',
+ -expires => 'now',
+ '-Content-length' => length($response)
+ ) . $response;
+
+ return $response;
+ }
+
+ # activate a general error handler
+ $SIG{__DIE__} = sub {
+ my ($err) = @_;
+
+ $err =~ s|at\s+\S+\s+line\s+(\d+)|(line $1)|s;
+ $err =~ s|\n|<br>\n|sg;
+ print STDOUT &http_response(
+ "<title>XMLDB ERROR</title>",
+ "<h1>XMLDB ERROR</h1>\n" .
+ "<pre>$err</pre>\n"
+ );
+ exit(0);
+ };
+
+ # XML escape/unescape
+ sub xml_escape {
+ my ($str) = @_;
+ $str =~ s|&|&|sg;
+ $str =~ s|<|<|sg;
+ $str =~ s|>|>|sg;
+ $str =~ s|"|"|sg;
+ return $str;
+ }
+ sub xml_unescape {
+ my ($str) = @_;
+ $str =~ s|&|&|sg;
+ $str =~ s|<|<|sg;
+ $str =~ s|>|>|sg;
+ $str =~ s|"|"|sg;
+ return $str;
+ }
+
+ # import database
+ sub db_import {
+ my ($file) = @_;
+
+ # read XML file
+ my $fh = new IO::File "<$file"
+ || die "unable to open database \"$file\" for reading";
+ my $xml = '';
+ $xml .= $_ while (<$fh>);
+ $fh->close;
+
+ # parse XML
+ my $db = {
+ 'TEXT' => { 'PROLOG' => '', 'EPILOG' => '' },
+ 'ORDER' => { 'USER' => undef, 'ATTRIB' => undef, 'RECORD' => undef },
+ 'INDEX' => { 'USER' => {}, 'ATTRIB' => {}, 'RECORD' => {} },
+ 'CONFIG' => {},
+ 'USER' => [],
+ 'ATTRIB' => [],
+ 'RECORD' => [],
+ };
+ if ($xml =~ m|^(.*?)<xmldb>(.*?)</xmldb>(.*)$|s) {
+ $db->{TEXT}->{PROLOG} = $1;
+ $xml = $2;
+ $db->{TEXT}->{EPILOG} = $3;
+ }
+ else {
+ die "invalid XML markup in database file \"$file\"";
+ }
+ $xml =~ s|<config>(.*?)</config>|&parse_config($1), ''|sge;
+ sub parse_config {
+ my ($xml) = @_;
+ $xml =~ s|<([a-z][a-z0-9_-]*)((?:\s+[a-zA-Z][a-zA-Z0-9-]*="[^"]*")+)/>|&parse_config_entry($1, $2), ''|sge;
+ sub parse_config_entry {
+ my ($tag, $xml) = @_;
+ $db->{CONFIG}->{$tag} = {};
+ while ($xml =~ s|^\s*([a-zA-Z][a-zA-Z0-9-]*)="([^"]*)"\s*||) {
+ $db->{CONFIG}->{$tag}->{$1} = &xml_unescape($2);
+ }
+ }
+ }
+ $xml =~ s|<user((?:\s+[a-zA-Z][a-zA-Z0-9-]*="[^"]*")+)/>|&parse_user($1), ''|sge;
+ $xml =~ s|<attrib((?:\s+[a-zA-Z][a-zA-Z0-9-]*="[^"]*")+)/>|&parse_attrib($1), ''|sge;
+ $xml =~ s|<record((?:\s+[a-zA-Z][a-zA-Z0-9-]*="[^"]*")+)/>|&parse_record($1), ''|sge;
+ sub parse_user {
+ my ($xml) = @_;
+ my $user = {};
+ my $order = [];
+ while ($xml =~ s|^\s*([a-zA-Z][a-zA-Z0-9-]*)="([^"]*)"\s*||) {
+ $user->{$1} = &xml_unescape($2);
+ push(@{$order}, $1);
+ }
+ if ($user->{"id"} ne '') {
+ push(@{$db->{USER}}, $user);
+ $db->{INDEX}->{USER}->{$user->{"id"}} = $user;
+ if (not defined($db->{ORDER}->{USER})) {
+ $db->{ORDER}->{USER} = $order;
+ }
+ }
+ }
+ sub parse_attrib {
+ my ($xml) = @_;
+ my $attrib = {};
+ my $order = [];
+ while ($xml =~ s|^\s*([a-zA-Z][a-zA-Z0-9-]*)="([^"]*)"\s*||) {
+ $attrib->{$1} = &xml_unescape($2);
+ push(@{$order}, $1);
+ }
+ if ($attrib->{"id"} ne '') {
+ push(@{$db->{ATTRIB}}, $attrib);
+ $db->{INDEX}->{ATTRIB}->{$attrib->{"id"}} = $attrib;
+ if ($attrib->{"index"} eq 'yes') {
+ $db->{INDEX}->{RECORD}->{$attrib->{"id"}} = {};
+ }
+ if (not defined($db->{ORDER}->{ATTRIB})) {
+ $db->{ORDER}->{ATTRIB} = $order;
+ }
+ }
+ }
+ sub parse_record {
+ my ($xml) = @_;
+ my $record = {};
+ my $order = [];
+ while ($xml =~ s|^\s*([a-zA-Z][a-zA-Z0-9-]*)="([^"]*)"\s*||) {
+ if (defined($db->{INDEX}->{ATTRIB}->{$1})) {
+ $record->{$1} = &xml_unescape($2);
+ if ($db->{INDEX}->{ATTRIB}->{$1}->{"index"} eq 'yes') {
+ $db->{INDEX}->{RECORD}->{$1}->{$2} = $record;
+ }
+ push(@{$order}, $1);
+ }
+ }
+ push(@{$db->{RECORD}}, $record);
+ if (not defined($db->{ORDER}->{RECORD})) {
+ $db->{ORDER}->{RECORD} = $order;
+ }
+ }
+
+ return $db;
+ }
+
+ # export database
+ sub db_export {
+ my ($db, $file) = @_;
+
+ # read XML file
+ my $fh = new IO::File "<$file"
+ || die "unable to open database \"$file\" for reading";
+ my $old = '';
+ $old .= $_ while (<$fh>);
+ $fh->close;
+
+ my $out = '';
+ $out .= $db->{TEXT}->{PROLOG};
+ $out .= "<xmldb>\n";
+ $out .= " <config>\n";
+ $out .= " <title major=\"".&xml_escape($db->{CONFIG}->{title}->{major} || "")."\" ".
+ "minor=\"".&xml_escape($db->{CONFIG}->{title}->{minor} || "")."\"/>\n";
+ $out .= " <contact name=\"".&xml_escape($db->{CONFIG}->{contact}->{name} || "")."\" ".
+ "email=\"".&xml_escape($db->{CONFIG}->{contact}->{email} || "")."\"/>\n";
+ $out .= " <copyright year=\"".&xml_escape($db->{CONFIG}->{copyright}->{year} || "")."\" ".
+ "holder=\"".&xml_escape($db->{CONFIG}->{copyright}->{holder} || "")."\"/>\n";
+ $out .= " </config>\n";
+ $out .= " <access>\n";
+ foreach $user (@{$db->{USER}}) {
+ $out .= " <user";
+ foreach $a (@{$db->{ORDER}->{USER}}) {
+ $out .= " $a=\"".$user->{$a}."\"";
+ }
+ $out .= "/>\n";
+ }
+ $out .= " </access>\n";
+ $out .= " <schema>\n";
+ foreach $attrib (@{$db->{ATTRIB}}) {
+ $out .= " <attrib";
+ foreach $a (@{$db->{ORDER}->{ATTRIB}}) {
+ $out .= " $a=\"".$attrib->{$a}."\"";
+ }
+ $out .= "/>\n";
+ }
+ $out .= " </schema>\n";
+ $out .= " <records>\n";
+ foreach $record (@{$db->{RECORD}}) {
+ $out .= " <record";
+ foreach $r (@{$db->{ORDER}->{RECORD}}) {
+ $out .= " $r=\"".$record->{$r}."\"";
+ }
+ $out .= "/>\n";
+ }
+ $out .= " </records>\n";
+ $out .= "</xmldb>";
+ $out .= $db->{TEXT}->{EPILOG};
+
+ my $fh = new IO::File ">$file"
+ or die "unable to open database \"$file\" for writing";
+ flock($fh, LOCK_EX) or die "(exclusive) lock failed: $!";
+ $fh->print($out);
+ $fh->flush();
+ $fh->sync();
+ flock($fh, LOCK_UN) or die "unlock failed: $!";
+ $fh->close();
+
+ # read XML file
+ $fh = new IO::File "<$file"
+ || die "unable to open database \"$file\" for reading";
+ my $new = '';
+ $new .= $_ while (<$fh>);
+ $fh->close;
+
+ if ($new ne $out) {
+ my $fh = new IO::File ">$file"
+ or die "unable to open database \"$file\" for writing";
+ flock($fh, LOCK_EX) or die "(exclusive) lock failed: $!";
+ $fh->print($old);
+ $fh->flush();
+ $fh->sync();
+ flock($fh, LOCK_UN) or die "unlock failed: $!";
+ $fh->close();
+ die "re-read newly written new database and found difference -- restored old database!";
+ }
+
+ return $out;
+ }
+
+ # convert Sugar-style markup text to HTML text
+ sub sug2html {
+ my ($text) = @_;
+
+ # escape HTML special characters
+ $text =~ s|&|&|sg;
+ $text =~ s|<|<|sg;
+ $text =~ s|>|>|sg;
+
+ # expand Sugar markup
+ $text =~ s/\|\|(.+?)\|\|/<tt>$1<\/tt>/sg;
+ $text =~ s|//(.+?)//|<i>$1</i>|sg;
+ $text =~ s|\*\*(.+?)\*\*|<b>$1</b>|sg;
+ $text =~ s|->(.+?)::(.+?)<-|<a href="$2" target="mct-extern">$1</a>|sg;
+ $text =~ s|->(.+?)<-|<a href="$1" target="mct-extern">$1</a>|sg;
+
+ return $text;
+ }
+
+ # fetch CGI parameters
+ my $action = $cgi->param('action') || 'list';
+ my $xmldb = $cgi->path_translated();
+
+ my $head = '';
+ my $body = '';
+
+ # optionally read HTML page template
+ my $template = $xmldb;
+ $template =~ s|\.xmldb$|.html|s;
+ my $fh = new IO::File "<$template" || die;
+ $MY->{TEMPLATE} = '';
+ $MY->{TEMPLATE} .= $_ while (<$fh>);
+ $fh->close();
+
+ # import CSS
+ sub addcss {
+ my ($file) = @_;
+ $head .= "<style type=\"text/css\"><!--\n";
+ my $fh = new IO::File "<$file" || die;
+ $head .= $_ while (<$fh>);
+ $fh->close();
+ $head .= "--></style>\n";
+ }
+ my $css = $xmldb;
+ $css =~ s|\.xmldb$|.css|s;
+ if (-f "$css") {
+ &addcss("$css");
+ }
+ if (-f "xmldb.css") {
+ &addcss("xmldb.css");
+ }
+
+ # read database
+ my $db = &db_import($xmldb);
+
+ #################################
+
+ #$body .= "<pre>";
+ #$body .= Data::Dumper->Dump([$db]);
+ #$body .= "</pre>";
+
+ #foreach $e (sort(keys(%ENV))) {
+ # $body .= "$e=$ENV{$e}<br>\n";
+ #}
+
+ sub url_escape {
+ my ($text) = @_;
+ $text =~ s|([ \t&+?:/=\n\r])|sprintf("%%%02x", ord($1))|sge;
+ return $text;
+ }
+
+ sub url_unescape {
+ my ($text) = @_;
+ $text =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack('C', hex($1))/eg;
+ return $text;
+ }
+
+ my $sortby = $cgi->param("view.sortby") || "";
+ if ($sortby eq '') {
+ for ($i = 0; $i < ($#{$db->{ORDER}->{RECORD}}+1); $i++) {
+ if (($cgi->param("sortby-$i") || '') ne '') {
+ $sortby = $i;
+ $action = "list";
+ last;
+ }
+ }
+ }
+ if ($sortby eq '' or $sortby < 0) {
+ $sortby = 0;
+ }
+
+ if ($cgi->param('new') ne '') {
+ $action = "new";
+ }
+ if ($cgi->param('update') ne '') {
+ $action = "list";
+ }
+ if ($cgi->param('list') ne '') {
+ $action = "list";
+ }
+ if ($cgi->param('view') ne '') {
+ $action = "view";
+ }
+ if ($cgi->param('edit') ne '') {
+ $action = "edit";
+ }
+ if ($cgi->param('edit-save') ne '') {
+ $action = "edit-save";
+ }
+
+ my $hint = {};
+ DISPATCH:
+ my $title = "";
+ if ($action eq 'list') {
+ $body .= "<table border=0 cellspacing=0 cellpadding=2>\n";
+ $body .= $cgi->hidden(-name => 'view.sortby', -default => [ $sortby ]);
+
+ # determine default list of columns
+ my $view_field_name = [];
+ foreach my $a (@{$db->{ATTRIB}}) {
+ if ($a->{"list"} eq 'yes') {
+ push(@{$view_field_name}, $a->{"id"});
+ }
+ }
+
+ # determine number of fields to view
+ my $view_fields_list = [];
+ for ($i = 0; $i < $#{$db->{ORDER}->{RECORD}}+1; $i++) {
+ push(@{$view_fields_list}, $i);
+ }
+ my $view_fields = $cgi->param("view.fields") || "";
+ if ($view_fields eq '') {
+ # $view_fields = sprintf("%d", ($#{$db->{ORDER}->{RECORD}}+1)/2);
+ # $view_fields = 6 if ($view_fields > 6);
+ $view_fields = sprintf("%d", $#{$view_field_name}+1);
+ }
+ elsif ($view_fields < 1) {
+ $view_fields = 1;
+ }
+ elsif ($view_fields > $#{$db->{ORDER}->{RECORD}}+1) {
+ $view_fields = $#{$db->{ORDER}->{RECORD}}+1;
+ }
+
+ if ($view_fields > $#{$view_field_name}+1) {
+ foreach my $a (@{$db->{ATTRIB}}) {
+ if ($a->{"list"} eq 'no') {
+ if ($view_fields > $#{$view_field_name}+1) {
+ push(@{$view_field_name}, $a->{"id"});
+ }
+ }
+ }
+ }
+
+ $body .= " <tr>\n";
+ $body .= " <td>\n";
+ $body .= "<span class=\"xmldb-button-update\">" .
+ $cgi->submit(-name => 'update', -value => "Update") .
+ "</span>\n";
+ $body .= " </td>\n";
+ for ($i = 0; $i < $view_fields; $i++) {
+ $body .= " <td>\n";
+ $body .= "<span class=\"xmldb-button-sort".($i == $sortby ? "-active" :"")."\">";
+ $body .= $cgi->submit(-name => "sortby-$i", -value => "Sort") ;
+ $body .= "</span>";
+ $body .= " </td>\n";
+ }
+ $body .= " </tr>\n";
+
+ $body .= " <tr>\n";
+ $body .= " <td>\n";
+ # FIXME
+ # $body .= "<span class=\"xmldb-button\">" .
+ # $cgi->submit(-name => 'new', -value => "New") .
+ # "</span>\n";
+ $body .= " </td>\n";
+ for ($i = 0; $i < $view_fields; $i++) {
+ my $pattern = $cgi->param("view.field.$i.pattern") || "*";
+ $body .= " <td>\n";
+ $body .= "<span class=\"xmldb-field-pattern".($i == $sortby ? "-active" :"")."\">";
+ $body .= $cgi->textfield(-name => "view.field.$i.pattern",
+ -default => $pattern,
+ -size => 10,
+ -maxlength => 20);
+ $body .= "</span>";
+ $body .= " </td>\n";
+ }
+ $body .= " </tr>\n";
+
+ $body .= " <tr>\n";
+ $body .= " <td>\n";
+ $body .= "<span class=\"xmldb-field-list\">";
+ $body .= $cgi->popup_menu(-name => 'view.fields',
+ -values => $view_fields_list,
+ -default => $view_fields);
+ $body .= "</span>";
+
+ $body .= " </td>\n";
+ for ($i = 0; $i < $view_fields; $i++) {
+ #my $name = $cgi->param("view.field.$i.name") || $db->{ORDER}->{RECORD}->[$i];
+ my $name = $cgi->param("view.field.$i.name") || $view_field_name->[$i];
+ $body .= " <td>\n";
+ $body .= "<span class=\"xmldb-field-select".($i == $sortby ? "-active" :"")."\">";
+ $body .= $cgi->popup_menu(-name => "view.field.$i.name",
+ -values => $db->{ORDER}->{RECORD},
+ -default => $name);
+ $body .= "</span>";
+ $body .= " </td>\n";
+ }
+ $body .= " </tr>\n";
+
+ my @result = ();
+ foreach my $r (@{$db->{RECORD}}) {
+ my $match = 1;
+ for ($i = 0; $i < $view_fields; $i++) {
+ #my $name = $cgi->param("view.field.$i.name") || $db->{ORDER}->{RECORD}->[$i];
+ my $name = $cgi->param("view.field.$i.name") || $view_field_name->[$i];
+ my $pattern = $cgi->param("view.field.$i.pattern") || "*";
+ sub pattern_match {
+ my ($s, $p) = @_;
+ $p =~ s|\*|.*|sg;
+ $p =~ s|\?|.|sg;
+ return ($s =~ m|^.*$p.*$|is);
+ }
+ if (not &pattern_match($r->{$name}, $pattern)) {
+ $match = 0;
+ last;
+ }
+ }
+ if ($match) {
+ my $entry = [];
+ for ($i = 0; $i < $view_fields; $i++) {
+ #my $name = $cgi->param("view.field.$i.name") || $db->{ORDER}->{RECORD}->[$i];
+ my $name = $cgi->param("view.field.$i.name") || $view_field_name->[$i];
+ push(@{$entry}, $r->{$name});
+ }
+ push(@result, $entry);
+ }
+ }
+
+ my $view_rows = $cgi->param("view.rows") || 20;
+ my $view_rows_list = [];
+ for ($i = 1; $i < 100; $i++) {
+ push(@{$view_rows_list}, $i);
+ }
+
+ if ($sortby > $#result) {
+ $sortby = $#result;
+ }
+ my @sorted = sort { return ($a->[$sortby] cmp $b->[$sortby]); } @result;
+
+ my $c = 0;
+ my $row = 0;
+ my $scroll = (($#sorted+1) > 10);
+ foreach my $r (@sorted) {
+ $body .= " <tr class=xmldb-list-row$c>\n";
+ $body .= " <td>\n";
+ # if ($scroll and $row == 0) {
+ # $body .= "<span class=\"xmldb-button\">" .
+ # $cgi->submit(-name => 'scroll-up', -value => "U") .
+ # "</span>\n";
+ # }
+ # elsif ($scroll and $row == 1) {
+ # $body .= $cgi->popup_menu(-name => 'view.rows',
+ # -values => $view_rows_list,
+ # -default => $view_rows);
+ # }
+ # elsif ($scroll and $row == 2) {
+ # $body .= "<span class=\"xmldb-button\">" .
+ # $cgi->submit(-name => 'scroll-down', -value => "D") .
+ # "</span>\n";
+ # }
+ # else {
+ $body .= " ";
+ # }
+ $body .= " </td>\n";
+ for ($i = 0; $i < $view_fields; $i++) {
+ #my $name = $cgi->param("view.field.$i.name") || $db->{ORDER}->{RECORD}->[$i];
+ my $name = $cgi->param("view.field.$i.name") || $view_field_name->[$i];
+ my $a = $r->[$i];
+ $body .= " <td class=xmldb-list-col$c".($i == $sortby ? "-active" :"").">\n";
+ $body .= " <span class=\"xmldb-list-row-text".($i == $sortby ? "-active" :"")."\">";
+ if ($db->{INDEX}->{ATTRIB}->{$name}->{"index"} eq 'yes') {
+ $body .= "<a href=\"". $cgi->url(-full => 1)."?" .
+ &url_escape($name)."=".&url_escape($a) .
+ "\">$a</a>";
+ }
+ else {
+ $body .= $a;
+ }
+ $body .= " </span>\n";
+ $body .= " </td>\n";
+ }
+ $body .= " </tr>\n";
+ $c = (($c + 1) % 2);
+ $row++;
+ }
+
+ $body .= "</table>\n";
+ $title = "List Database";
+ }
+ elsif ($action eq 'view') {
+ my $field = $cgi->param("field") || die;
+ my $key = $cgi->param("key") || die;
+ my $r = $db->{INDEX}->{RECORD}->{$field}->{$key} || die;
+ $body .= "<input type=\"hidden\" name=\"field\" value=\"$field\" />\n";
+ $body .= "<input type=\"hidden\" name=\"key\" value=\"$key\" />\n";
+ $body .= "<span class=\"xmldb-button-back\">" .
+ $cgi->submit(-name => 'list', -value => "Back") .
+ "</span>\n";
+ $body .= "<span class=\"xmldb-button-refresh\">" .
+ $cgi->submit(-name => 'view', -value => "Refresh") .
+ "</span>\n";
+ $body .= "<span class=\"xmldb-button-edit\">" .
+ $cgi->submit(-name => 'edit', -value => "Edit") .
+ "</span>\n";
+ $body .= "<p>";
+ $body .= "<table width=100% border=0 cellspacing=0 cellpadding=2>\n";
+ my $c = 0;
+ foreach $k (@{$db->{ORDER}->{RECORD}}) {
+ $body .= " <tr class=xmldb-list-row$c>\n";
+ $body .= " <td width=50%>";
+ $body .= " <span class=\"xmldb-list-row-text".($i == $sortby ? "-active" :"")."\">";
+ $body .= $db->{INDEX}->{ATTRIB}->{$k}->{name} . ": ";
+ $body .= " </span>\n";
+ $body .= " </td>\n";
+ $body .= " <td width=50%>\n";
+ $body .= " <span class=\"xmldb-list-row-text".($i == $sortby ? "-active" :"")."\">";
+ my $x = $r->{$k};
+ if ($x =~ m|^[^@]+@([^@.]+\.)*[^@.]+$|) {
+ $x = "<a href=\"mailto:$x\">$x</a>";
+ }
+ elsif ($x =~ m/^(http|ftp):\/\/.+/) {
+ $x = "<a href=\"$x\">$x</a>";
+ }
+ $body .= " $x\n";
+ $body .= " </span>\n";
+ $body .= " </td>\n";
+ $body .= " </tr>\n";
+ $c = ($c + 1) % 2;
+ }
+ $body .= "</table>\n";
+ $title = "View Database Record";
+ }
+ elsif ($action eq 'edit') {
+ my $field = $cgi->param("field") || die;
+ my $key = $cgi->param("key") || die;
+ my $r = $db->{INDEX}->{RECORD}->{$field}->{$key} || die;
+ $body .= "<input type=\"hidden\" name=\"field\" value=\"$field\" />\n";
+ $body .= "<input type=\"hidden\" name=\"key\" value=\"$key\" />\n";
+ $body .= "<span class=\"xmldb-button-cancel\">" .
+ $cgi->submit(-name => 'view', -value => "Cancel") .
+ "</span>\n";
+ $body .= "<span class=\"xmldb-button-refresh\">" .
+ $cgi->submit(-name => 'edit', -value => "Refresh") .
+ "</span>\n";
+ $body .= "<span class=\"xmldb-button-save\">" .
+ $cgi->submit(-name => 'edit-save', -value => "Save") .
+ "</span>\n";
+ $body .= "<p>";
+ $body .= "<table width=100% border=0 cellspacing=0 cellpadding=0>\n";
+ my $c = 0;
+ foreach $k (@{$db->{ORDER}->{RECORD}}) {
+ $body .= " <tr class=xmldb-list-row$c>\n";
+ $body .= " <td width=50%>";
+ $body .= " <span class=\"xmldb-list-row-text".($i == $sortby ? "-active" :"")."\">";
+ $body .= $db->{INDEX}->{ATTRIB}->{$k}->{name} . ": ";
+ $body .= " </span>\n";
+ $body .= " </td>";
+ $body .= " <td width=50%>";
+ $body .= " <span class=\"xmldb-list-row-text".($i == $sortby ? "-active" :"")."\">";
+ my $match = $db->{INDEX}->{ATTRIB}->{$k}->{match};
+ if ($match =~ m|^mc:(.*)$|) {
+ my @mc = split(/\|/, $1);
+ my $l = 0;
+ $body .= "<table cellspacing=0 cellpadding=0 border=0>\n";
+ foreach $mc (@mc) {
+ $body .= "<tr>" if ($l % 3 == 0);
+ $body .= "<td>";
+ $body .= "<input ".($r->{$k} eq $mc ? "checked " : "").
+ "type=\"radio\" name=\"$k\" value=\"$mc\">";
+ $body .= " " . $mc . " ";
+ $body .= "</td>";
+ $l++;
+ $body .= "</tr>" if ($l % 3 == 0);
+ }
+ $body .= "</tr>" if ($l % 3 != 0);
+ $body .= "</table>\n";
+ }
+ else {
+ $body .= $cgi->textfield(-name => "$k",
+ -default => $r->{$k},
+ -size => 40,
+ -maxlength => 80);
+ }
+ $body .= " </span>\n";
+ $body .= " </td>\n";
+ $body .= " </tr>\n";
+ if (defined($hint->{$k}) && $hint->{$k} ne '') {
+ $body .= " <tr>\n";
+ $body .= " <td>\n";
+ $body .= " </td>\n";
+ $body .= " <td>\n";
+ $body .= " <span class=\"xmldb-text-edit-hint\">\n";
+ $body .= " ".$hint->{$k}."\n";
+ $body .= " </span>\n";
+ $body .= " </td>\n";
+ $body .= " </tr>\n";
+ }
+ $c = ($c + 1) % 2;
+ }
+ $body .= "</table>\n";
+ $title = "Edit Database Record";
+ }
+ elsif ($action eq 'edit-save') {
+ my $field = $cgi->param("field") || die;
+ my $key = $cgi->param("key") || die;
+ my $r = $db->{INDEX}->{RECORD}->{$field}->{$key} || die;
+ my $ok_global = 1;
+ foreach $k (@{$db->{ORDER}->{RECORD}}) {
+ my $val = $cgi->param("$k");
+ my $match = $db->{INDEX}->{ATTRIB}->{$k}->{match};
+ my $ok_local = 0;
+ if ($match =~ m|^mc:(.*)$|s) {
+ my @mc = split(/\|/, $1);
+ foreach $mc (@mc) {
+ if ($mc eq $val) {
+ $ok_local = 1;
+ last;
+ }
+ }
+ if (not $ok_local) {
+ $hint->{$k} = "has to be one of: ".join(", ", @mc).".";
+ }
+ }
+ elsif ($match =~ m|^re:(.*)$|s) {
+ my $re = $1;
+ if ($val =~ m|$re|s) {
+ $ok_local = 1;
+ }
+ else {
+ $hint->{$k} = "has to match on regular expression \"$re\".";
+ }
+ }
+ else {
+ die "invalid match attribute \"$match\"";
+ }
+ if (not $ok_local) {
+ $ok_global = 0;
+ }
+ else {
+ $r->{$k} = $val;
+ }
+ }
+ if (not $ok_global) {
+ $action = "edit";
+ }
+ else {
+ $action = "view";
+ &db_export($db, $xmldb);
+ }
+ goto DISPATCH;
+ }
+ elsif ($action eq 'new') {
+ $body .= "NEW!";
+ $title = "New Database Record";
+ }
+ elsif ($action eq 'login') {
+ my $login_username = '';
+ my $login_password = '';
+ my $body = '';
+ $body .= "<table><tr>\n";
+ $body .= "<td>Username:</td>\n";
+ $body .= "<td>".$cgi->textfield(-name => "login_username", -default => $login_username, -size => 20, -maxlength => 40)."</td>";
+ $body .= "<td>Password:</td>\n";
+ $body .= "<td>".$cgi->password_field(-name => "login_password", -default => $login_password, -size => 20, -maxlength => 40)."</td>";
+ $body .= "<td>".$cgi->submit(-name => 'login', -value => "Login")."</td>";
+ $body .= "</tr></table>\n";
+ }
+
+ my $login_dialog = '';
+ sub login_valid {
+ my ($username, $password) = @_;
+ return 0;
+ }
+ # FIXME
+ # if (&login_valid($login_username, $login_password)) {
+ # $login_dialog .= "Logged in as ". $login_username;
+ # }
+ # else {
+ # $login_dialog .= $cgi->submit(-name => 'login', -value => "Login");
+ # }
+
+ $head .= "<title>XMLDB: $title</title>\n";
+ $body = $cgi->startform(-method => "POST", -action => $cgi->url(-full => 1)) .
+ "<table class=xmldb-box-outmost>\n" .
+ " <tr>\n" .
+ " <td class=xmldb-title-cell valign=top>\n" .
+ " <span class=xmldb-title-text-major>".$db->{CONFIG}->{title}->{major}."</span><br>\n" .
+ " <span class=xmldb-title-text-minor>".$db->{CONFIG}->{title}->{minor}."</span>\n" .
+ " </td>\n" .
+ " <td align=right valign=top>\n" .
+ # $login_dialog .
+ # " <br>\n" .
+ " " .
+ " <span class=xmldb-title-dialog>$title</span>\n" .
+ " </td>\n" .
+ " </tr>\n" .
+ " <tr>\n" .
+ " <td colspan=2>\n" .
+ $body .
+ " </td>\n" .
+ " </tr>\n" .
+ # " <tr>\n" .
+ # " <td class=xmldb-foot-left-cell>\n" .
+ # " <span class=xmldb-foot-left-text>".
+ # "Copyright © ".$db->{CONFIG}->{copyright}->{year} ." ".
+ # $db->{CONFIG}->{copyright}->{holder} . "<br>\n" .
+ # "Contact: ".$db->{CONFIG}->{contact}->{name} . " " .
+ # "<<a href=\"mailto:".$db->{CONFIG}->{contact}->{email}."\">".$db->{CONFIG}->{contact}->{email}."</a>>".
+ # " </span>\n" .
+ # " </td>\n" .
+ # " <td class=xmldb-foot-right-cell>\n" .
+ # " <span class=xmldb-foot-right-text>".
+ # "<a href=\"".$MY->{PROG_HOME}."\">".
+ # $MY->{PROG_NAME} . "</a> " . $MY->{PROG_VERS} . "<br>" .
+ # $MY->{PROG_DESC} .
+ # " </span>\n" .
+ # " </td>\n" .
+ # " </tr>\n" .
+ "</table>\n" .
+ $cgi->endform;
+
+ print STDOUT &http_response($head, $body);
+
+ exit(0);
+
|