OSSP CVS Repository

ossp - ossp-pkg/sdb/sdb.cgi 1.2
Not logged in
[Honeypot]  [Browse]  [Directory]  [Home]  [Login
[Reports]  [Search]  [Ticket]  [Timeline
  [Raw

ossp-pkg/sdb/sdb.cgi 1.2
#!/usr/opkg/bin/perl
##
##  OSSP sdb -- Skill Database
##  Copyright (c) 2003 The OSSP Project <http://www.ossp.org/>
##  Copyright (c) 2003 Cable & Wireless Deutschland <http://www.cw.com/de/>
##  Copyright (c) 2003 Ralf S. Engelschall <rse@engelschall.com>
##
##  This file is part of OSSP sdb, a small skill database Web UI
##  which can be found at http://www.ossp.org/pkg/tool/sdb/
##
##  This program is free software; you can redistribute it and/or
##  modify it under the terms of the GNU General Public License
##  as published by the Free Software Foundation; either version
##  2.0 of the License, or (at your option) any later version.
##
##  This program is distributed in the hope that it will be useful,
##  but WITHOUT ANY WARRANTY; without even the implied warranty of
##  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
##  General Public License for more details.
##
##  You should have received a copy of the GNU General Public License
##  along with this program; if not, write to the Free Software
##  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
##  USA, or contact The OSSP Project <ossp@ossp.org>.
##
##  sdb.cgi: skill database CGI (language: Perl)
##

#   requirements
require 5.006;
use POSIX;
use IO;
use CGI qw(:standard -nosticky -no_undef_params -oldstyle_urls -no_debug);
use DBI;
use DBD::SQLite;
use String::Divert;
use Data::Dumper;

#   program configuration
my $my = {
    PROG_NAME => 'sdb',
    PROG_VERS => '0.0.1',
    PROG_DATE => '09-May-2003',
    PROG_DESC => 'Skill Database',
    TEMPLATE  => "<html>\n<head>\n\@HEAD\@\n</head>\n<body>\n\@BODY\@\n</body></html>"
};

#   switch to unbuffered output
$|++;

##  _________________________________________________________________________ 
##
##  Helper Functions
##  _________________________________________________________________________ 
##

#   (un)escape URL chars
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;
}

#   (un)escape SQL chars
sub sql_escape {
    my ($text) = @_;
    $text =~ s|(.)|POSIX::isprint($1) ? $1 : "."|sge;
    $text =~ s|([''])|$1$1|sg;
    $text = "'" . $text . "'";
    return $text;
}
sub sql_unescape {
    my ($text) = @_;
    if ($text =~ m|^'(.*)'$|) {
        $text = $1;
        $text =~ s/''/'/sg;
    }
    return $text;
}

##  _________________________________________________________________________ 
##
##  Initialize Environment
##  _________________________________________________________________________ 
##

#   remove and remember GET query strings
my $qs = '';
if ($ENV{QUERY_STRING} ne '') {
    $qs = $ENV{QUERY_STRING};
    delete $ENV{QUERY_STRING};
}

#   open CGI environment
my $cgi = new CGI;
$cgi->autoEscape(undef);

#   re-insert GET query strings into CGI environment
if ($qs ne '') {
    foreach my $kv (split(/\&/, $qs)) {
        if ($kv =~ m|^([^=]+)(?:=([^=]+))?$|s) {
            my ($key, $val) = ($1, $2);
            $val ||= 1;
            $key = &url_unescape($key);
            $val = &url_unescape($val);
            $cgi->param($key, $val);
        }
    }
}

#   remember self-referencing URL
$my->{URL} = $cgi->url(-full => 1);

#   generate an HTTP response
sub http_response {
    my ($head, $body) = @_;

    #   fill in template page
    my $response = $my->{TEMPLATE};
    if ($response =~ m|\n([^\n]*)\@HEAD\@|s) {
        my $prefix = " " x length($1);
        $head =~ s|\n(.)|\n$prefix$1|sg;
    }
    if ($response =~ m|\@HEAD\@[ \t]*\n|s) {
        $head =~ s|\n$||s;
    }
    $response =~ s|\@HEAD\@|$head|sg;
    if ($response =~ m|\n([^\n]*)\@BODY\@|s) {
        my $prefix = " " x length($1);
        $body =~ s|\n(.)|\n$prefix$1|sg;
    }
    if ($response =~ m|\@BODY\@[ \t]*\n|s) {
        $body =~ s|\n$||s;
    }
    $response =~ s|\@BODY\@|$body|sg;

    #   prepend with HTTP header
    $response = $cgi->header(
        -type             => 'text/html',
        -expires          => 'now',
        '-Content-length' => length($response)
    ) . $response;

    return $response;
}

#   start output generation
my $head = '';
my $body = '';

#   optionally read HTML page template
if (-f "sdb.html") {
    my $io = new IO::File "<sdb.html" || die;
    $my->{TEMPLATE} = ''; 
    $my->{TEMPLATE} .= $_ while (<$io>);
    $io->close();
}

#   optionally activate CSS
if (-f "sdb.css") {
    $head .= "<link rel=\"stylesheet\" href=\"sdb.css\" type=\"text/css\"/>";
}

#   activate a general error handler
$SIG{__DIE__} = sub {
    my ($err) = @_;

    $err =~ s|at\s+\S+\s+line\s+(\d+)|(sdb.cgi:$1)|s;
    $err =~ s|\n|<br>\n|sg;
    $err =~ s|\n+$||s;
    $head .= "<title>".$my->{PROG_NAME}.": ERROR</title>";
    $body .= "<h2>ERROR</h1>" .
             "The following error occured:<p>" .
             "<pre class=error>$err</pre>\n";
    #   debugging
    $body .= "<pre>";
    my @names = $cgi->param;
    foreach my $name (@names) {
        my $value = $cgi->param($name);
        $body .= "$name=\"$value\"\n";
    }
    $body .= "</pre>";
    print STDOUT &http_response($head, $body);
    exit(0);
};

#   open DB environment
my $db;
($db = DBI->connect("dbi:SQLite:dbname=sdb.db", "", ""))
    || die "unable to connect to SQLite database \"sdb.db\"";
$db->{AutoCommit} = 1;
$db->{RaiseError} = 1;

##  _________________________________________________________________________ 
##
##  Stage 0: Determine Parameters
##  _________________________________________________________________________ 
##

#   list of UI display pages
my @ui_pages = qw(main person);

#   initialize UI display parameters
my $ui = {};
foreach my $page (@ui_pages) {
    my $found = 0;
    foreach my $sub (
        "ui_${page}_init",
        "ui_init"
    ) {
        if (defined(&$sub)) {
            &$sub($my, $cgi, $db, $ui, $page);
            $found = 1;
            last;
        }
    }
    die "no initialization handler found for page \"$page\""
        if (not $found);
}

#   determine UI display parameters
my @names = $cgi->param;
foreach my $name (@names) {
    if ($name =~ m|^([^.]+)(?:\.([^.]+)(?:\.([^.]+))?)?$|s) {
        my ($page, $area, $elem) = ($1, $2, $3);
        if (defined($page)) {
            $ui->{$page} = { -is => 'disable', -with => 'default' }
                if (not defined($ui->{$page}));
        }
        if (defined($area)) {
            $ui->{$page}->{$area} = { -is => 'disable', -with => 'default' }
                if (not defined($ui->{$page}->{$area}));
        }
        if (defined($elem)) {
            if ($name =~ m|\+$|s) {
                my @values = $cgi->param($name);
                $ui->{$page}->{$area}->{$elem} = [ @values ];
            }
            else {
                my $value = $cgi->param($name);
                $ui->{$page}->{$area}->{$elem} = $value;
            }
        }
        elsif (defined($area)) {
            my $value = $cgi->param($name);
            $value = "visible" if ($value eq "1");
            $ui->{$page}->{$area}->{-is} = $value;
        }
        elsif (defined($page)) {
            my $value = $cgi->param($name);
            $value = "visible" if ($value eq "1");
            $ui->{$page}->{-is} = $value;
        }
        $cgi->param($name, undef);
    }
}

##  _________________________________________________________________________ 
##
##  Stage 1: Process Last Action
##  _________________________________________________________________________ 
##

#   determine default UI display
my $page = undef;
foreach my $p (keys(%{$ui})) {
    if ($ui->{$p}->{-is} eq 'visible') {
        $page = $p;
        last;
    }
}
if (not defined($page)) {
    $ui->{main}->{-is} = 'visible';
    $ui->{main}->{all}->{-is} = 'visible';
}

#   determine which button was pressed and
#   perform an associated action (if defined)
foreach my $page (keys(%{$ui})) {
    foreach my $area (keys(%{$ui->{$page}})) {
        foreach my $elem (keys(%{$ui->{$page}->{$area}})) {
            if ($elem =~ m|^[A-Z][A-Z0-9_-]*$|) {
                if ($ui->{$page}->{$area}->{$elem}) {
                    my $found = 0;
                    foreach my $sub (
                        "ui_${page}_${area}_${elem}_action",
                        "ui_${page}_${area}_action",
                        "ui_${page}_action",
                        "ui_action"
                    ) {
                        if (defined(&$sub)) {
                            &$sub($my, $cgi, $db, $ui, $page, $area, $elem);
                            $found = 1;
                            last;
                        }
                    }
                    die "no action handler found for element \"$page.$area.$elem\""
                        if (not $found);
                }
                delete $ui->{$page}->{$area}->{$elem};
            }
        }
    }
}

##  _________________________________________________________________________ 
##
##  Stage 2: Output New Display
##  _________________________________________________________________________ 
##

#   start output generation
my $html = new String::Divert;
$html->overload(1);

#   generate outmost class
$html .= "<!-- SDB BEGIN -->\n";
$html .= "<div class=\"sdb\">\n";
$html .= "  "; $html *= q{outmost};
$html .= "</div>\n";
$html .= "<!-- SDB END -->\n";
$html >> q{outmost};

#   generate outmost self-referencing form
$html .= $cgi->startform(
    -method => "POST",
    -action => $my->{URL},
);
$html .= "  "; $html *= q{form};
$html .= $cgi->endform() . "\n";
$html >> q{form};

#   generate top-level header & footer
$html .= "<h1>Skill Database</h1>\n";
$html .= ""; $html *= q{page};
$html .= "<p/>\n" .
         "<span class=\"footer\">\n" .
         "  $my->{PROG_NAME} $my->{PROG_VERS} ($my->{PROG_DATE})\n" .
         "</span>\n";
$html >> q{page};

#   generate page contents
foreach my $page (@ui_pages) {
    next if (not defined($ui->{$page}));
    if ($ui->{$page}->{-is} =~ m/^(visible|hidden)$/) {
        my $found = 0;
        foreach my $sub (
            "ui_${page}_render",
            "ui_render"
        ) {
            if (defined(&$sub)) {
                $html .= &$sub($my, $cgi, $db, $ui, $page);
                $found = 1;
                last;
            }
        }
        die "no rendering handler found for page \"$page\""
            if (not $found);
    }
}

#   insert HTML into output HTTP response body
$html->undivert(0);
$body .= $html->string();

#   optional debugging
$body .= "<p/>\n";
$body .= "<pre>\n";
my @names = $cgi->param;
foreach my $name (sort(@names)) {
    my $value = $cgi->param($name);
    $body .= "  $name=\"$value\"\n";
}
$body .= "</pre>\n";

#   generate output
print STDOUT &http_response($head, $body);

exit(0);

##  _________________________________________________________________________ 
##
##  Page: main
##  _________________________________________________________________________ 
##

sub ui_main_init {
    my ($my, $cgi, $db, $ui, $page) = @_;

    $ui->{main}        = { -is => 'disable', -with => 'default' };
    $ui->{main}->{all} = { -is => 'disable', -with => 'default' };
}

sub ui_main_action {
    my ($my, $cgi, $db, $ui, $page, $area, $elem) = @_;

    die "invalid action \"$page.$area.$elem\"";
}

sub ui_main_render {
    my ($my, $cgi, $db, $ui, $page) = @_;

    #   start generating HTML
    my $html = new String::Divert;
    $html->overload(1);

    #   generate outer class
    $html .= "<span class=\"main\">\n";
    $html .= "  "; $html *= q{body};
    $html .= "</span>\n";
    $html >> q{body};

    #   generate header
    $html .= "<h2>Main Menu</h2>\n";

    #   generate main menu
    $html .=
        "<ul>\n" .
        "  <li><a href=\"$my->{URL}?person\">Browse Persons</a> &rarr;\n" .
        "  <li><a href=\"$my->{URL}?team\">Browse Teams</a> &rarr;\n" .
        "  <li><a href=\"$my->{URL}?skill\">Browse Skills</a> &rarr;\n" .
        "  <li><a href=\"$my->{URL}?query\">Perform Skill Query</a> &rarr;\n" .
        "</ul>\n";

    #   return unfolded HTML
    $html->undivert(0);
    return $html->string();
}

##  _________________________________________________________________________ 
##
##  Page: person
##  _________________________________________________________________________ 
##

sub ui_person_init {
    my ($my, $cgi, $db, $ui, $page) = @_;

    #   initialize person page
    $ui->{person}           = { -is => 'disable', -with => 'default' };
    $ui->{person}->{select} = { -is => 'disable', -with => 'default' };
    $ui->{person}->{detail} = { -is => 'disable', -with => 'default' };
    $ui->{person}->{skill}  = { -is => 'disable', -with => 'default' };
}

sub ui_person_action {
    my ($my, $cgi, $db, $ui, $page, $area, $elem) = @_;

    #   recreate action string
    my $action = "$page.$area.$elem";

    #   actions on select box
    if ($ui->{person}->{select}->{ADD}) {
        #   just open detail area for addition
        $ui->{person}->{detail}->{id}    = undef;
        $ui->{person}->{detail}->{-is}   = 'visible';
        $ui->{person}->{detail}->{-with} = 'add';
    }
    elsif ($ui->{person}->{select}->{VIEW}) {
        #   just open detail area for viewing
        $ui->{person}->{detail}->{-is}   = 'visible';
        $ui->{person}->{detail}->{-with} = 'view';
    }
    elsif ($ui->{person}->{select}->{EDIT}) {
        #   just open detail area for editing
        $ui->{person}->{detail}->{-is}   = 'visible';
        $ui->{person}->{detail}->{-with} = 'edit';
    }
    elsif ($ui->{person}->{select}->{DELETE}) {
        #   delete person from database
        my $pe_id = $ui->{person}->{select}->{id};
        if ($pe_id eq '') {
            die "no person selected";
        }
        my @rv = $db->selectrow_array("SELECT pe_id FROM sdb_person WHERE pe_id = '$pe_id';");
        if (@rv == 0) {
            die "invalid person \"$pe_id\" selected";
        }
        $db->do(sprintf(
            "DELETE FROM sdb_person WHERE pe_id = %s;",
            &sql_escape($pe_id)
        ));
        $ui->{person}->{select}->{id} = '';
        $ui->{person}->{detail}->{-is} = 'remove';
        $ui->{person}->{skills}->{-is} = 'remove';
    }

    #   actions on detail box
    if ($ui->{person}->{detail}->{'SKILL-VIEW'}) {
        #   just open skill area for viewing
        $ui->{person}->{skill}->{-is}   = 'visible';
        $ui->{person}->{skill}->{-with} = 'view';
    }
    elsif ($ui->{person}->{detail}->{'SKILL-EDIT'}) {
        #   just open skill area for editing
        $ui->{person}->{skill}->{-is}   = 'visible';
        $ui->{person}->{skill}->{-with} = 'edit';
    }
    elsif (   $ui->{person}->{detail}->{CLOSE}
           or $ui->{person}->{detail}->{CANCEL}) {
        #   just close detail area
        $ui->{person}->{detail}->{-is} = 'remove';
    }
    elsif ($ui->{person}->{detail}->{SAVE}) {
        #   save person details to database
        my $pe = {};
        $pe->{id} = $ui->{person}->{select}->{id};
        foreach my $a (qw(name email phone)) {
            $pe->{$a} = $ui->{person}->{detail}->{$a};
        }
        my $te_ids = $ui->{person}->{detail}->{"membership+"};
        if ($pe->{id} eq '') {
            #   add new entry
            my @rv = $db->selectrow_array(sprintf(
                "SELECT pe_id FROM sdb_person WHERE pe_name = %s;",
                &sql_escape($pe->{name})
            ));
            if (@rv > 0) {
               die "person \"$pe->{name}\" already existing -- delete first, please";
            }
            $db->do(sprintf(
                "INSERT INTO sdb_person (pe_name, pe_email, pe_phone) VALUES (%s, %s, %s);",
                &sql_escape($pe->{name}),
                &sql_escape($pe->{email}),
                &sql_escape($pe->{phone})
            ));
            foreach my $te_id (@{$te_ids}) {
                $db->do(sprintf(
                    "INSERT INTO sdb_member (ms_pe_id, ms_te_id) VALUES (%s, %s);",
                    &sql_escape($pe->{id}), &sql_escape($te_id)
                ));
            }
        }
        else {
            #   modify existing entry
            my @rv = $db->selectrow_array(sprintf(
                "SELECT pe_id FROM sdb_person WHERE pe_id = %s;",
                &sql_escape($pe->{id})
            ));
            if (@rv == 0) {
               die "person with id \"$pe->{id}\" not exists";
            }
            $db->do(sprintf(
                "UPDATE sdb_person" .
                "  SET pe_name = %s, pe_email = %s, pe_phone = %s" .
                "  WHERE pe_id = %s;",
                &sql_escape($pe->{name}),
                &sql_escape($pe->{email}),
                &sql_escape($pe->{phone}),
                &sql_escape($pe->{id})
            ));
            $db->do(sprintf(
                "DELETE FROM sdb_member WHERE ms_pe_id = %s;",
                &sql_escape($pe->{id})
            ));
            foreach my $te_id (@{$te_ids}) {
                $db->do(sprintf(
                    "INSERT INTO sdb_member (ms_pe_id, ms_te_id) " .
                    "  VALUES (%s, %s);",
                    &sql_escape($pe->{id}),
                    &sql_escape($te_id)
                ));
            }
        }
    }
}

sub ui_person_render {
    my ($my, $cgi, $db, $ui, $page) = @_;

    #   start output generation
    my $html = new String::Divert;
    $html->overload(1); 

    #   filter output according to visibility
    $html->storage(($ui->{person}->{-is} eq 'visible' ? 'all' : 'fold'));

    #   generate outer page CSS class
    $html .= "<span class=\"person\">\n";
    $html .= "  " . $cgi->hidden(-name  => "person", -default => 1)."\n";
    $html .= "  "; $html *= q{person};
    $html .= "</span>\n";
    $html >> q{person};

    #   generate header
    $html .= "<h2>Persons</h2>\n";
    $html .= "<a href=\"$my->{URL}\">&larr; Back to Main Menu</a>";

    #   generate page canvas
    #   +-------+-------+
    #   | area1 | area2 |
    #   +-------+-------+
    #   |     area3     |
    #   +---------------+
    $html .= "<p>\n";
    $html .= "<table width=100%>\n";
    $html .= "  <tr>\n";
    $html .= "    <td valign=top class=\"box\" width=\"50%\">\n";
    $html .= "      "; $html *= q{area1};
    $html .= "    </td>\n";
    $html .= "    <td>\n";
    $html .= "      &nbsp;&nbsp;\n";
    $html .= "    </td>\n";
    $html .= "    <td valign=top class=\"box\" width=\"50%\">\n";
    $html .= "      "; $html *= q{area2};
    $html .= "    </td>\n";
    $html .= "  </tr>\n";
    $html .= "  <tr>\n";
    $html .= "    <td colspan=3>\n";
    $html .= "      &nbsp;\n";
    $html .= "    </td>\n";
    $html .= "  </tr>\n";
    $html .= "  <tr>\n";
    $html .= "    <td colspan=3 valign=top class=\"box\" width=\"100%\">\n";
    $html .= "      "; $html *= q{area3};
    $html .= "    </td>\n";
    $html .= "  </tr>\n";
    $html .= "</table>\n";

    ##  _____________________________________________________________________
    ##
    ##  generate area: Person Selection
    ##  _____________________________________________________________________
    ##

    #   force selection box to be always visible if whole area is visible
    $ui->{person}->{select}->{-is} = 'visible'
        if ($ui->{person}->{-is} eq 'visible');

    #   generate CSS class
    $html >> q{area1};
    $html .= "<span class=\"select\">\n";
    $html .= "  "; $html *= q{select};
    $html .= "</span>\n";
    $html >> q{select};

    #   generate inner header
    $html .= "<h3>Select Person and Action</h3>\n";

    #   generate inner canvas
    $html .= "<table width=100%>\n";
    $html .= "  <tr>\n";
    $html .= "    <td width=100%>\n";
    $html .= "      "; $html *= q{select-list};
    $html .= "    </td>\n";
    $html .= "    <td valign=top height=100%>\n";
    $html .= "      "; $html *= q{select-buttons};
    $html .= "    </td>\n";
    $html .= "  </tr>\n";
    $html .= "</table>\n";

    #   generate the selection list widget
    $html >> q{select-list};
    my $rv = $db->selectall_arrayref(
        "SELECT pe_id,pe_name FROM sdb_person ORDER BY pe_name;"
    );
    my $pe_values = [];
    my $pe_labels = {};
    foreach my $r (@{$rv}) {
        push(@{$pe_values}, $r->[0]);
        $pe_labels->{$r->[0]} = $r->[1];
    }
    my $pe_default = $ui->{person}->{select}->{id} || $pe_values->[0];
    if ($ui->{person}->{select}->{-is} eq 'visible') {
        if (@{$pe_values} > 0) {
            $html .= $cgi->scrolling_list(
                -override  => 1,
                -name      => 'person.select.id',
                -values    => $pe_values,
                -labels    => $pe_labels,
                -default   => $pe_default,
                -size      => 20,
                -class     => 'id',
            ) . "\n";
        }
    }
    else {
        $html .= $cgi->hidden(
            -name    => 'person.select.id',
            -default => $pe_default
        );
    }
    $html << q{select-list};

    #   generate the selection list attached buttons
    $html >> "select-buttons";
    $html .= $cgi->submit(
        -name  => 'person.select.ADD', 
        -value => 'Add Person &rarr;',
        -class => 'ADD'
    ) . "<br>";
    if (@{$pe_values} > 0) {
        $html .= $cgi->submit(
            -name  => 'person.select.VIEW',
            -value => 'View Person &rarr;',
            -class => 'VIEW'
        ) . "<br/>";
        $html .= $cgi->submit(
            -name  => 'person.select.EDIT',
            -value => "Edit Person &rarr;",
            -class => "EDIT"
        ) . "<br/>";
        $html .= $cgi->submit(
            -name  => 'person.select.DELETE',
            -value => '&larr; Delete Person',
            -class => 'DELETE'
        ) . "<br/>";
    }
    $html << q{select-buttons};

    #   undivert from select area
    $html << q{area1};

    ##  _____________________________________________________________________
    ##
    ##  generate area: Person Detail
    ##  _____________________________________________________________________
    ##

    #   generate CSS class
    $html >> q{area2};
    $html .= "<span class=\"detail\">\n";
    $html .= "  "; $html *= q{detail};
    $html .= "</span>\n";
    $html >> q{detail};

    #   generate inner header
    my $action = $ui->{person}->{detail}->{-with};
    $html .= "<h3>".uc(substr($action,0,1)).substr($action,1)." Person</h3>\n";
    $html .= "<span class=\"$action\">\n";
    $html .= "  "; $html *= q{detail-sub};
    $html .= "</span>\n";
    $html >> q{detail-sub};

    #   generate inner canvas
    $html .= "<table>\n";
    $html .= "  "; $html *= q{detail-standard};
    $html .= "  "; $html *= q{detail-membership};
    $html .= "  "; $html *= q{detail-buttons};
    $html .= "</table>\n";

    #   fetch person details
    my $pe = undef;
    if ($action eq 'view' or $action eq 'edit') {
        my $id = $ui->{person}->{select}->{id};
        if ($id eq '') {
            die "no person selected";
        }
        $pe = $db->selectrow_hashref(sprintf(
            "SELECT pe_id AS id, pe_name AS name, pe_email AS email, pe_phone AS phone" .
            "  FROM sdb_person WHERE pe_id = %s;",
            &sql_escape($id)
        ));
        if (not defined($pe)) {
            die "person with id \"$id\" not found";
        }
    }
    else {
        $pe = { id => '', name => '', email => '', phone => '' };
    }

    #   display person details (standard)
    $html >> q{detail-standard};
    my $label = {
        'name'  => 'Person Name',
        'email' => 'Email Address',
        'phone' => 'Phone Number'
    };
    my $i = 0;
    foreach my $a (qw(name email phone)) {
        $html .= "<tr class=\"row-$i\">\n";
        $html .= "  <td class=\"label\">\n";
        $html .= "    ".$label->{$a}.":";
        $html .= "  </td>\n";
        $html .= "  <td class=\"content\">\n";
        if ($action eq 'view') {
            $html .= $pe->{$a};
        }
        else {
            if ($ui->{person}->{detail}->{-is} eq 'visible') {
                $html .= $cgi->textfield(
                    -override  => 1,
                    -name      => "person.detail.$a",
                    -default   => $pe->{$a},
                    -size      => 40,
                    -maxlength => 80
                );
            }
            else {
                $html .= $cgi->hidden(
                    -name      => "person.detail.$a",
                    -default   => $pe->{$a}
                );
            }
        }
        $html .= "  </td>\n";
        $html .= "</tr>\n";
        $i = ($i + 1) % 2;
    }
    $html << q{detail-standard};

    #   display person details (membership)
    $html >> q{detail-membership};
    $html .= "<tr class=\"$row-$i\">\n";
    $html .= "  <td valign=top class=\"label\">\n";
    $html .= "    Team Membership:\n";
    $html .= "  </td>\n";
    $html .= "  <td class=\"content\">\n";
    $html .= "    "; $html *= q{detail-membership-sub};
    $html .= "  </td>\n";
    $html .= "</tr>\n";
    $html >> q{detail-membership-sub};
    if ($action eq 'view') {
        my $te_all = $db->selectcol_arrayref(sprintf(
            "SELECT te_name FROM sdb_team,sdb_member" .
            "  WHERE te_id = ms_te_id AND ms_pe_id = %s" .
            "  ORDER BY te_name;",
            &sql_escape($pe->{id})
        ));
        if (@{$te_all} == 0) {
            $html .= "-none-";
        }
        else {
            for (my $i = 0; $i < @{$te_all}; $i++) {
                my $te_name = $te_all->[$i];
                $html .= ", " if ($i != 0);
                $html .= $te_name;
            }
        }
    }
    else {
        my $te_values = [];
        my $te_labels = {};
        my $te_all = $db->selectall_arrayref(
            "SELECT te_id, te_name FROM sdb_team ORDER BY te_name;"
        );
        foreach my $r (@{$te_all}) {
            push(@{$te_values}, $r->[0]);
            $te_labels->{$r->[0]} = $r->[1];
        }
        my $te_defaults = [];
        if ($action eq 'edit') {
            $te_defaults = $db->selectcol_arrayref(sprintf(
                "SELECT ms_te_id FROM sdb_member WHERE ms_pe_id = %s;",
                &sql_escape($pe->{id})
            ));
        };
        if (@{$te_values} == 0) {
            $html .= "-none-";
        }
        else {
            $html .= $cgi->scrolling_list(
                -override => 1,
                -name     => 'person.detail.membership+',
                -values   => $te_values,
                -labels   => $te_labels,
                -default  => $te_defaults,
                -multiple => 'true',
                -size     => 10,
                -class    => 'membership',
            );
        }
    }
    $html << q{detail-membership};

    #   generate attached buttons
    $html >> q{detail-buttons};
    $html .= "<tr>\n";
    $html .= "  <td>\n";
    $html .= "    &nbsp;";
    $html .= "  </td>\n";
    $html .= "  <td>\n";
    $html .= "    "; $html *= q{detail-buttons-sub1};
    $html .= "  </td>\n";
    $html .= "</tr>\n";
    $html >> q{detail-buttons-sub1};
    $html .= "<table width=100%>\n";
    $html .= "  <tr>\n";
    $html .= "    "; $html *= q{detail-buttons-sub2};
    $html .= "  </tr>\n";
    $html .= "</table>\n";
    $html >> q{detail-buttons-sub2};
    if ($action eq 'view') {
        $html .= "<td>" . $cgi->submit(
            -name  => 'person.detail.CLOSE',
            -value => '&larr; Close',
            -class => 'CLOSE'
        ) . "</td>";
        $html .= "<td>" . $cgi->submit(
            -name  => 'person.detail.SKILL-VIEW',
            -value => 'View Skills &darr;',
            -class => 'SKILL-VIEW'
        ) . "</td>";
    }
    else {
        $html .= "<td>" . $cgi->submit(
            -name  => 'person.detail.CANCEL',
            -value => '&larr; Cancel',
            -class => 'CANCEL'
        ) . "</td>";
        $html .= "<td>" . $cgi->submit(
            -name  => 'person.detail.SAVE',
            -value => '&uarr; Save',
            -class => 'SAVE'
        ) . "</td>";
        $html .= "<td>" . $cgi->submit(
            -name  => 'person.detail.SKILL-EDIT',
            -value => 'Edit Skills &darr;',
            -class => 'SKILL-EDIT'
        ) . "</td>";
    }
    $html << q{detail-buttons};
    
    #   undivert from detail area
    $html << q{area2};

    ##  _____________________________________________________________________
    ##
    ##  generate area: Person Rating
    ##  _____________________________________________________________________
    ##

    #   return unfolded HTML
    $html->undivert(0);
    return $html->string();
}


CVSTrac 2.0.1