#!/cw/bin/perl ## ## changelog.cgi -- Team ChangeLog Web-Interface ## Copyright (c) 2001-2002 Ralf S. Engelschall ## require 5.006; use CGI; # standard in Perl 5.6 use POSIX; # standard in Perl 5.6 # unbuffered STDOUT $|++; # establish CGI query object my $Q = new CGI; # establish my configuration my $MY = {}; $MY->{PROGNAME} = ($0 =~ m|^.*?([^/]+?)(?:\.[^/.]+)?$|)[0]; $MY->{HEADER} = ''; open(FP, "<".$MY->{PROGNAME}.".head.html"); $MY->{HEADER} .= $_ while (); close(FP); $MY->{FOOTER} = ''; open(FP, "<".$MY->{PROGNAME}.".foot.html"); $MY->{FOOTER} .= $_ while (); close(FP); # general error handler $SIG{__DIE__} = sub { my ($err) = @_; $err =~ s|\s+at\s+.*||s; $err =~ s|\n|
\n|sg; my $page = "Content-Type: text/html\n" . "\n" . $MY->{HEADER} . "

Application Services ChangeLog: ERROR

\n" . "
$err
\n" . $MY->{FOOTER}; print STDOUT $page; exit(0); }; # database entry escaping sub entry_escape { my ($str) = @_; $str =~ s/\|/\\x07/sg; $str =~ s/\s+/ /sg; return $str; } # database entry unescaping sub entry_unescape { my ($str) = @_; $str =~ s/\\x07/\|/sg; return $str; } # wildcard patter matching sub wildmat { my ($str, $pat) = @_; $pat =~ s|\*|.*|sg; $pat =~ s/\|/\\|/sg; return ($str =~ m|^$pat$|s); } # remember some parameters via HTTP cookies for the user's convinience my $cookies = []; foreach $param (qw(a_team a_person v_team v_person v_display)) { if ($Q->param($param) ne '' and ($Q->param($param) ne $Q->cookie($param))) { push(@{$cookies}, $Q->cookie( -name => $param, -value => $Q->param($param), -expires => '+1d', -path => $Q->url(-absolute => 1), -secure => 0 )); } if ($Q->param($param) eq '' and $Q->cookie($param) ne '') { $Q->param(-name => $param, -value => [$Q->cookie($param)]); } } # fetch HTTP GET/POST parameters my $a_team = $Q->param('a_team') || 'is'; my $a_person = $Q->param('a_person') || ''; my $a_risk = $Q->param('a_risk') || 'low'; my $a_subject = $Q->param('a_subject') || ''; my $a_change = $Q->param('a_change') || ''; my $v_team = $Q->param('v_team') || '*'; my $v_person = $Q->param('v_person') || '*'; my $v_risk = $Q->param('v_risk') || '*'; my $v_subject = $Q->param('v_subject') || '*'; my $v_change = $Q->param('v_change') || '*'; my $v_start = $Q->param('v_start') || '0'; my $v_display = $Q->param('v_display') || '20'; # add a new ChangeLog entry if ($Q->param('form') eq 'Add') { # make sure the parameters really exists foreach $param (qw(a_team a_person a_risk a_subject a_change)) { if ($Q->param($param) eq '') { die "Parameter " . uc(substr($param,2,1)).substr($param,3). " is empty"; } } # generate time stamp my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(time()); my $time = sprintf("%04d-%02d-%02d/%02d:%02d", $year+1900, $mon+1, $mday, $hour, $min); # escape values my $x_team = &entry_escape($a_team); my $x_person = &entry_escape($a_person); my $x_subject = &entry_escape($a_subject); my $x_risk = &entry_escape($a_risk); my $x_change = &entry_escape($a_change); # read last entry from database my $entry_last = ""; open(FP, "<".$MY->{PROGNAME}.".txt") || die "unable to read logfile"; $entry_last = $_ while (); close(FP); # generate new entry my $entry = sprintf("%s|%s|%s|%s|%s|%s\n", $time, $x_team, $x_person, $x_risk, $x_subject, $x_change); # make sure we do not accidently add an entry multiple times # (this can happen if people press "RELOAD" in their browser # immediately after adding an entry by pressing "ADD" before). my $entry_now = $entry; $entry_now =~ s/^[^|]+|//s; $entry_last =~ s/^[^|]+|//s; if ($entry_now eq $entry_last) { die "Adding the last entry a second time is not allowed!"; } # write new entry to database open(FP, ">>".$MY->{PROGNAME}.".txt") || die "unable to write logfile"; printf FP $entry; close(FP); # reset values for subsequent processing $Q->param(-name => 'a_subject', -value => ''); $Q->param(-name => 'a_change', -value => ''); $a_subject = ''; $a_change = ''; }; # start generating output page my $page = ''; $page .= $Q->header (-cookie => $cookies, -expires => '+1s'); $page .= $MY->{HEADER}; $page .= "\n"; $page .= "\n" . "\n" . "\n" . "\n" . "\n" . "\n"; $page .= "" . "\n"; $page .= "\n" . "
\n" . "" . "" . "" . "
\n"; $page .= "\n"; $page .= "\n" . "" . "" . "" . "" . "" . "" . "\n"; $page .= "\n" . "" . "" . "" . "" . "" . "" . "\n"; # generate "ADD" form $page .= $Q->startform( -method => "POST", -action => $Q->url()) . "\n" . "" . "" . "" . "" . "" . "" . "\n" . $Q->endform; # generate "VIEW" form $page .= $Q->startform(-method => "GET", -action => $Q->url()) . "\n" . "" . "" . "" . "" . "" . "" . "\n" . $Q->endform; # read database my $changelog = ''; open(FP, "<".$MY->{PROGNAME}.".txt") || die "reading"; $changelog .= $_ while (); close(FP); # reverse order of entries my @changelog = reverse(split(/\n/, $changelog)); # calculate number of entries my $db_entries = $#changelog + 1; # restrict according to selected view my @changelog_view = (); foreach $line (@changelog) { next if ($line eq ''); my ($time, $team, $person, $risk, $subject, $change) = split(/(?= 1 and $v_display <= 999) ? $v_display : 20); my $prev = ($v_start - $numdisplay); $prev = 0 if ($prev < 0); my $next = ($v_start + $numdisplay); $next = ($#changelog - $numdisplay) if ($next > ($#changelog-$numdisplay)); $next = 0 if ($next < 0); if ($#changelog > 0) { $prev_percent = sprintf("%d", ($v_start / $#changelog) * 100); $next_percent = sprintf("%d", (($#changelog-$next) / $#changelog) * 100); } else { $prev_percent = 0; $next_percent = 0; } # determine navigation links my $nav_prev = "url(-relative => 1)."?v_start=$prev"."\">  ($prev_percent\%)"; $nav_prev = "" if ($v_start == 0); my $nav_next = "url(-relative => 1)."?v_start=$next"."\">  ($next_percent\%)"; $nav_next = "" if ($v_start == ($#changelog - $numdisplay) or $next == 0); # restrict to view window @changelog = @changelog[$v_start .. ($v_start + $numdisplay)]; $page .= "\n" . "" . "" . "" . "" . "" . "" . "\n"; my $first = ($v_start == 0); my $cnt = 0; my $col = { 'low' => ['#f0f0f0', '#ffffff'], 'high' => ['#f0f0f0', '#ffffff'], }; my $datenext = ''; foreach $line (@changelog) { next if ($line eq ''); my ($time, $team, $person, $risk, $subject, $change) = split(/\|/, $line); $team = &entry_unescape($team); $person = &entry_unescape($person); $subject = &entry_unescape($subject); $risk = &entry_unescape($risk); $change = &entry_unescape($change); my $color = $col->{$risk}->[$cnt]; my $date = $time; $date =~ s|/.*$||s; if ($datenext ne $date) { sub weekday { my ($date) = @_; my ($y, $m, $d) = ($date =~ m|^(\d{4})-(\d{2})-(\d{2})|s); my $t = mktime(0, 0, 0, $d, $m-1, $y-1900, 0, 0, 0); my $wday = (localtime($t))[6]; $wday = 7 if ($wday == 0); # Sun return $wday; } if (&weekday($datenext) < &weekday($date)) { $page .= "" . "" . "\n"; } else { $page .= "" . "" . "\n"; } $datenext = $date; } sub html_escape { my ($str) = @_; $str =~ s|&|&|sg; $str =~ s|<|<|sg; $str =~ s|>|>|sg; return $str; } $team = &html_escape($team); $person = &html_escape($person); $subject = &html_escape($subject); $risk = &html_escape($risk); $change = &html_escape($change); if ($first) { $first = 0; $color = "#e0e0ff"; } my $tcolor = "#000000"; if ($risk eq 'high') { $tcolor = "#333399"; $risk = "$risk"; } $page .= "" . "" . "" . "" . "" . "" . "" . "\n"; $cnt = (($cnt+1) % 2); } $page .= "\n" . "" . "" . "" . "" . "" . "" . "\n"; $page .= "
 TeamPersonRiskSubjectChange
" . "" . $Q->submit(-name => 'form', -value => 'Add') . "" . "" . $Q->popup_menu( -name => 'a_team', -values => ['is', 'hosting', 'development', 'database'], -default => $a_team ) . "" . $Q->popup_menu( -name => 'a_person', -values => ['', 'peter', 'cschug', 'rse', 'cbotta', 'mhendel', 'pbrudna', 'awaegner', 'kgruber', 'rohde', 'scholli', 'msander', 'cmuschio', 'tlottere', 'psmej', 'mschloh', 'sgans', 'cscheith', 'sgierth', 'asikeler', ], -default => $a_person ) . "" . $Q->popup_menu( -name => 'a_risk', -values => ['high', 'low'], -default => $a_risk ) . "" . $Q->textfield( -name => 'a_subject', -default => $a_subject, -size => 20, -maxlength => 80 ) . "" . "" . "
" . $Q->textfield( -name => 'a_change', -default => $a_change, -size => 50, -maxlength => 200 ) . "
" . "
" . $Q->textfield( -name => 'v_display', -default => $v_display, -size => 2, -maxlength => 3 ) . "" . $Q->submit(-name => 'form', -value => 'View') . "" . "" . $Q->popup_menu( -name => 'v_team', -values => ['*', 'is', 'hosting', 'development', 'database'], -default => $v_team ) . "" . $Q->popup_menu( -name => 'v_person', -values => ['*', 'peter', 'cschug', 'rse', 'cbotta', 'mhendel', 'pbrudna', 'awaegner', 'kgruber', 'rohde', 'scholli', 'msander', 'cmuschio', 'tlottere', 'psmej', 'mschloh', 'sgans', 'cscheith', 'sgierth', 'asikeler', ], -default => $v_person ) . "" . $Q->popup_menu( -name => 'v_risk', -values => ['*', 'high', 'low'], -default => $v_risk ) . "" . $Q->textfield( -name => 'v_subject', -default => $v_subject, -size => 20, -maxlength => 80 ) . "" . "" . "
" . $Q->textfield( -name => 'v_change', -default => $v_change, -size => 50, -maxlength => 200 ) . "
" . "
Time   $nav_prevTeamPersonRiskSubjectChange
$time  $team  $person  $risk  $subject  $change
Time   $nav_nextTeamPersonRiskSubjectChange
\n"; $page .= "
" . "" . "Copyright © 2001-2002 Cable & Wireless Deutschland GmbH
" . "Database: $db_entries entries, raw version" . "
" . "
\n"; $page .= $MY->{FOOTER}; print STDOUT $page; exit(0);