The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl

=head1 NAME 

PICA+Wiki - Wiki interface to a L<PICA::Store> of PICA+ records

=head1 DESCRIPTION

This is just a proof of concept and needs a major rewrite. To try
out, create a file picawiki.conf point with SQLite=sqlitefile.db 
to a file that is writeable to your webserver.

=cut


#use lib "../lib";

use CGI qw/:standard :form/;
use CGI::Carp qw(warningsToBrowser fatalsToBrowser);
use URI::Escape;
use PICA::Record;
use PICA::SQLiteStore;
use Data::Dumper;
use File::Basename;
use Cwd qw(abs_path);

# TODO: error handling if this fails (installation)
# TODO: add wsdl and soap files

my $store = eval { PICA::SQLiteStore->new( config => "picawiki.conf" ); };
my $error = $@;

my $PICAWIKI_VERSION = "0.1.0";

my $baseurl = url(-full => 0);
my $title = "PICA+Wiki"; 
my $ppn = param('ppn');
my $cmd = param('cmd');
my $record = param('record');
my $version = param('version');
my $submit = param('submit');
my $cancel = param('cancel');

my $offset = param('offset') || 0;
my $limit = param('limit') || 30;

my $user = $ENV{REMOTE_ADDR} or "0";
$store->access( userkey => $user ) if defined $user;

my $c_user = param('user');
$cmd = 'contributions' if defined $c_user and not $cmd;

$cmd = '' if ($error);

print header({type => 'text/html', charset => 'utf-8'});
print start_html(
    -encoding => 'utf-8',
    -style=> 'picawiki.css',
    title=>$title,
);

#print pre( { class => 'debug' }, "hallo" );
#print $baseurl;

print "<div id='page-base' class='noprint'></div>\n";
print "<div id='head-base' class='noprint'></div>\n";
print "<div id='content'>\n";
print "<a id='top' name='top'></a>\n";
print "<div id='bodyContent'>\n";

# cancel action
if ($cancel) {
    if ($cmd eq 'editrecord') {
        $cmd = 'viewrecord'; $version = 0;
    }
    # TODO: redirect to get HTTP GET (?)
}
$cmd = 'viewrecord' if ( ($ppn or $version) and not $cmd);
$cmd = '' if $cmd eq 'viewrecord' and not ($ppn or $version); 

if ($cmd eq 'editrecord' && $submit) {
    # TODO: nicht wenn version fehlt
    $record =~ s/\t/ /g;
    $record =~ s/ +/ /g;
    my $pprecord = eval { PICA::Record->new( $record ); };
    $pprecord = 0 if $pprecord && $pprecord->is_empty;
    if ($pprecord) {
        my %result = ();
        if ($ppn) {
            %result = $store->update( $ppn, $pprecord, $version );
        } else {
#print "CREATE: " . $pprecord->to_string();
            %result = $store->create( $pprecord );
        }
        if (%result) {
            if ($result{id}) {
                $ppn = $result{id};
                $cmd = 'viewrecord';
            } else {
                $error = "ERROR: " . $result{errormessage};
            }
            $version = $result{version};
        } else {
            $error = "Fehler beim Speichern des Datensatz";
        }
    } else {
        $error = $@ ? $@ : "Der Datensatz ist kein PICA+";
    }
}

if ($cmd eq 'newrecord') {
    $record = "";
    $version = "";
    $cmd = "editrecord";
} 

#if ($version && !$cmd) {
#    $cmd = 'viewrecord';
#}

if ($cmd eq 'viewrecord') {
    my %recorddata;
    if ($version) {
        %recorddata = $store->get( $ppn, $version );
    } elsif ($ppn) {
        %recorddata = $store->get( $ppn );
    }
    if ($recorddata{id}) {
        $ppn = $recorddata{id};
        $record = $recorddata{record}->to_string;
        $version = $recorddata{version};
        $timestamp = $recorddata{timestamp};
        $latest = $recorddata{latest};
    } else {
        $error = "Failed to get record $ppn version $version";
        $cmd = "";
    }
}

print div({class=>'error'},$error) if $error;


sub version_line {
    my ($line, $item) = @_;

    my %del = $item->{deleted} ? (class=>'deleted') : ();
    
    if ($line =~ /{TIMESTAMP_LINK}/) {
        my $timestamp = a( { href=>"$baseurl?version=" . $item->{version}, %del }, $item->{timestamp} );
        $line =~ s/{TIMESTAMP_LINK}/$timestamp/g;
    }
    if ($line =~ /{PPN_LINK}/) {
        my $link = a( { href=>"$baseurl?ppn=" . $item->{ppn}, , %del }, "Datensatz " . $item->{ppn} );
        $line =~ s/{PPN_LINK}/$link/g;
    }
    if ($line =~ /{IS_NEW}/) {
        my $new = $item->{is_new} ? ' <b>neu</b> ' : '';
        $line =~ s/{IS_NEW}/$is_new/g;
    }
    if ($line =~ /{IS_DELETED}/) {
        my $text = $item->{deleted} ? ' <b>gelöscht</b> ' : '';
        $line =~ s/{IS_DELETED}/$text/g;
    }
    if ($line =~ /{BY_USER}/) {
        my $by_user = " von " . a({ href=>"$baseurl?user=".$item->{user} }, $item->{user});
        $by_user = '' if not $item->{user};
        $line =~ s/{BY_USER}/$by_user/g;
    }
    $line = "<li>$line</li>";
    return $line;
}


if ($cmd eq 'editrecord') {
    my %rec;
    if ($ppn and not $version) {
        my %rec = $store->get( $ppn ); # TODO: vorher machen, und wenn nicht vorhanden: fehler
        $record = $rec{record}->to_string;
        $version = $rec{version};
    }
    print h2( $ppn ? "Datensatz $ppn bearbeiten" : "Datensatz anlegen" );
    print "Version $version" if $version;
    print start_form( { action=>$baseurl, method=>'post' } );
    print input( {type=>'hidden', name=>'cmd', value=>'editrecord'} );
    print input( {type=>'hidden', name=>'ppn', value=>$ppn} );
    print input( {type=>'hidden', name=>'version', value=>$version} );
    print textarea( { name=>'record', style=>'width:100%', rows=>25, cols=>80, value=>$record } );
    print br,
        input( { type=>'submit', name=>'submit', value=>($ppn?'Speichern':'Anlegen') } ),
        input( { type=>'submit', name=>'cancel', value=>'Abbrechen' } );
    print end_form;

} elsif($cmd eq 'viewrecord') {
    print h2("Datensatz $ppn");
    if ($version) {
        my $prevnext = $store->prevnext($ppn, $version, 1);
        my @pn = sort keys %$prevnext;
        my $n = shift @pn;
        if ($n && $n < $version) {
            print a({href=>"$baseurl?version=".$n}, " \x{2190} " );
            $n = shift @pn;
        }
        print "Version id $version ";
        print " ($timestamp)" if $timestamp;
        if ($n && $n > $version) {
            print a({href=>"$baseurl?version=".$n}, " \x{2192} ");
        }
        # TODO: add user
        if ($latest && $version < $latest) {
            print " Von diesem Datensatz existiert eine " 
                  . a({href=>"$baseurl?ppn=$ppn"}, "aktuelle Version") . "!";
        }
    }

    print pre($record);
    print div(a({href=>"$baseurl?cmd=editrecord&ppn=$ppn"}, "Bearbeiten"));
    #print div(a({href=>"$baseurl?cmd=deleterecord&ppn=$ppn"}, "Löschen"));

} elsif ($cmd eq 'history' and $ppn) {
    print h2("Versionen von Datensatz $ppn");
    $history = $store->history($ppn, $offset, $limit);
    print "<ul>";
    foreach my $item (@$history) {
        print version_line('{TIMESTAMP_LINK} {IS_NEW}{IS_DELETED} {BY_USER}', $item);
    }
    print "</ul>";
    # print Dumper($history);
} elsif ($cmd eq 'contributions') {
    print h2("Bearbeitungen von $c_user");
    $revisions = $store->contributions($c_user, $offset, $limit);
    print div("Es liegen keine Bearbeitungen dieses Accounts vor.") unless @$revisions;
    print "<ul>";
    foreach my $item (@$revisions) {
        print "<li>";
        print a( { href=>"$baseurl?version=" . $item->{version} }, $item->{timestamp} );
        print " ";
        print a( { href=>"$baseurl?ppn=" . $item->{ppn} }, "Datensatz " . $item->{ppn} );
        print " <b>neu</b>" if ($item->{is_new});
        print " <b>gelöscht</b>" if ($item->{deleted});
        print "</li>";
    }
    print "</ul>";    
} elsif ($cmd eq 'recentchanges') {
    print h2("Letzte Änderungen");
    $rc = $store->recentchanges($offset, $limit);
    if (!@$rc) {
        print div("Es liegen keine Änderungen vor.");
    }
    print "<ul>"; # TODO: Gelöschte Datensätze markieren!
    foreach my $item (@$rc) {
        print version_line('{TIMESTAMP_LINK} {PPN_LINK} {IS_NEW}{IS_DELETED} {BY_USER}', $item);
    }
    print "</ul>";
    # print Dumper($rc);    
} elsif ($cmd eq 'stats') {
    print h2("Statistik");
    my %stats = getStats();
    print '<div>';
    print '<dl>';
    print map { dt($_) . dd($stats{$_}); } (keys %stats);
    print '</dl>';
    print '</div>';
} elsif ($cmd eq 'deleted') {
    print h2("Gelöschte Datensätze");
    my $del = $store->deletions($offset, $limit);
    if (@$del) {
        print "<ul>";
        foreach my $item (@$del) {
            print version_line('{TIMESTAMP_LINK} {PPN_LINK} {IS_NEW}{IS_DELETED} {BY_USER}', $item);
        }
        print "</ul>";
    } else {
        print "Es liegen keine gelöschten Datensätze vor.";
    }
} else { # startseite
    print h2("$title");
    print "<div>";
    print "Herzlich Willkommen zur ersten Demo des PICAWiki. Hier können Datensätze im ";
    print a({href=>"http://www.gbv.de/wikis/cls/PicaPlus"},"PICA+ Format") . " angelegt und bearbeitet werden.";
    print "</div>";
    #if (!$store) {
    #print div("Bitte lesen Sie sich die Installationsanweisung durch!");
    #}
}

print "</div>";
print "</div><!-- content -->\n";
print "<div id='head' class='noprint'>\n";

print div({id=>'title'}, h1($title) ) . "\n";
print div({id=>'personal'}, span($user));

print "<div id='left-navigation'>";
if ($ppn) {
    print "<ul>";
    print "<li><a href='$baseurl?ppn=$ppn'><span>Datensatz</span></a></li>";
    print "<li><a href='$baseurl?cmd=history&ppn=$ppn'><span>Versionen</span></a></li>";
    print "</ul>";
}
print "</div>\n";
print "</div> <!-- head -->";
print "<div id='panel' class='noprint'>\n";

my @panel = (
  'Navigation', [
      $baseurl, 'Startseite',
      "$baseurl?cmd=recentchanges", 'Letzte Änderungen',
     # "$baseurl?cmd=listrecords", 'Alle Datensätze'
  ],
  'Werkzeuge', [
      "$baseurl?cmd=newrecord", 'Neuer Datensatz',
      "$baseurl?cmd=stats", 'Statistik',
      "$baseurl?cmd=deleted", 'Löschlog'
  ]
);
for(my $i=0; $i<@panel; $i+=2) {
    my @list = @{$panel[$i+1]};
    print '<div class="portal">' . h5($panel[$i]) . '<div><ul>';
    for(my $j=0; $j<@list; $j+=2) {
        print li(a({href=>$list[$j]},$list[$j+1]));
    }
    print '</ul></div></div>';
}
print "</div> <!-- panel -->\n";

print "<div class='break'/>\n";
print "<div id='foot'><!-- footer --></div>\n";
print end_html;


sub getStats {
    my %stat;

    #my @s = stat($dbfile);
    #$stat{dbfilename} = $dbfile;
    #$stat{dbfilesize} = $s[7];
    #$stat{dbfilemtime} = $s[9];
    #$stat{wikiversion} = $PICAWIKI_VERSION;

    return %stat;    
}

=head1 SEEALSO

HTML and CSS design is adopted from the MediaWiki Vector theme.

=cut

__END__

TODO: 
- allow to query raw PICA via same URL-parameters as PSI and via unAPI
- add SOAP server to emulate CBS Webcat