s/issue/bug/g + updated ncdc-share-report

This commit is contained in:
Yorhel 2012-03-30 13:50:56 +02:00
parent 05c36a1aef
commit d6865e7b42
5 changed files with 121 additions and 119 deletions

View file

@ -22,7 +22,7 @@
# TODO: Atom feed?
package TUWF::Issue;
package TUWF::Bug;
use TUWF ':html', 'html_escape';
@ -100,21 +100,21 @@ sub dbSave {
# TODO: pagination / filtering
sub htmlListing {
my($s, $l, $lnk) = @_;
table class => 'issue_listing';
table class => 'bug_listing';
thead; Tr;
td class => 'issue_col_id', 'Id';
td class => 'issue_col_type', 'Type';
td class => 'issue_col_status', 'Status';
td class => 'issue_col_date', 'Updated';
td class => 'issue_col_summary','Summary';
td class => 'bug_col_id', 'Id';
td class => 'bug_col_type', 'Type';
td class => 'bug_col_status', 'Status';
td class => 'bug_col_date', 'Updated';
td class => 'bug_col_summary','Summary';
end; end;
for(@$l) {
Tr $_->{closed} ? (class => 'issue_closed') : ();
td class => 'issue_col_id', $_->{issue};
td class => 'issue_col_type', $_->{type};
td class => 'issue_col_status', $_->{status};
td class => 'issue_col_date', $_->{date};
td class => 'issue_col_summary';
Tr $_->{closed} ? (class => 'bug_closed') : ();
td class => 'bug_col_id', $_->{issue};
td class => 'bug_col_type', $_->{type};
td class => 'bug_col_status', $_->{status};
td class => 'bug_col_date', $_->{date};
td class => 'bug_col_summary';
a href => $lnk->($_->{issue}), $_->{summary};
end;
end;
@ -138,16 +138,16 @@ sub _escape_url {
sub htmlItem {
my($s, $d) = @_;
my $last = $d->[$#$d];
dl class => 'issue_status';
dl class => 'bug_status';
dt 'Id'; dd $last->{issue};
dt 'Messages'; dd $#$d+1;
dt 'Type'; dd $last->{type};
dt 'Status'; dd $last->{status};
end;
div class => 'issue_item';
div class => 'bug_item';
my $num = -1;
for my $m (@$d) {
div class => 'issue_message';
div class => 'bug_message';
h1 !++$num ? 'Description' : "Reply $num";
dl;
dt !$num ? 'Created' : 'Added'; dd $m->{date};
@ -172,49 +172,49 @@ sub htmlItem {
sub htmlForm {
my($s, $l, $url) = @_;
# TODO: anti-spam JS
form class => 'issue_frm', action => $url, method => 'post';
form class => 'bug_frm', action => $url, method => 'post';
fieldset;
input type => 'hidden', name => 'issue_id', value => $l ? $l->{issue} : 0;
legend $l ? 'Reply' : 'Report a new issue';
input type => 'hidden', name => 'bug_id', value => $l ? $l->{issue} : 0;
legend $l ? 'Reply' : 'Report a new bug';
ul;
li class => 'issue_frm_summary';
label for => 'issue_summary', 'Summary';
input type => 'text', name => 'issue_summary', id => 'issue_summary', size => 45, value => $l?$l->{summary}:'';
li class => 'bug_frm_summary';
label for => 'bug_summary', 'Summary';
input type => 'text', name => 'bug_summary', id => 'bug_summary', size => 45, value => $l?$l->{summary}:'';
end;
li class => 'issue_frm_mail';
label for => 'issue_email', 'Email';
input type => 'text', name => 'issue_email', id => 'issue_email', size => 20;
li class => 'bug_frm_mail';
label for => 'bug_email', 'Email';
input type => 'text', name => 'bug_email', id => 'bug_email', size => 20;
lit ' ';
txt 'Optional, only used for notifications.';
end;
if($l) {
li class => 'issue_frm_admin';
label for => 'issue_type', 'Admin';
Select name => 'issue_type';
li class => 'bug_frm_admin';
label for => 'bug_type', 'Admin';
Select name => 'bug_type';
option value => $_, $_ eq $l->{type} ? (selected => 'selected') : (), $_ for @{$s->{types}};
end;
Select name => 'issue_status';
Select name => 'bug_status';
option value => $_, $_ eq $l->{status} ? (selected => 'selected') : (), $_ for @{$s->{statusses}};
end;
Select name => 'issue_closed';
Select name => 'bug_closed';
option value => 0, !$l->{closed} ? (selected => 'selected') : (), 'Open';
option value => 1, $l->{closed} ? (selected => 'selected') : (), 'Closed';
end;
input type => 'password', name => 'issue_code', id => 'issue_code', size => 10, value => 'code';
input type => 'password', name => 'bug_code', id => 'bug_code', size => 10, value => 'code';
end;
} else {
li class => 'issue_frm_type';
label for => 'issue_type', 'Type';
Select name => 'issue_type';
li class => 'bug_frm_type';
label for => 'bug_type', 'Type';
Select name => 'bug_type';
option value => $_, $_ eq $s->{default_type} ? (selected => 'selected') : (), $_ for @{$s->{types}};
end;
end;
}
li class => 'issue_frm_message';
textarea name => 'issue_message';end; br;
li class => 'bug_frm_message';
textarea name => 'bug_message';end; br;
lit 'Please use a <a href="http://p.blicky.net/">pastebin</a> if you want to include large chunks of code or program output.';
end;
li class => 'issue_frm_submit';
li class => 'bug_frm_submit';
input type => 'submit', value => 'Submit';
end;
end 'ul';
@ -226,60 +226,60 @@ sub htmlForm {
sub handleForm {
my($s, $url) = @_;
my $f = $TUWF::OBJ->formValidate(
{ post => 'issue_id', min => 0 },
{ post => 'issue_summary', maxlength => 200, minlength => 2 },
{ post => 'issue_email', required => 0, regex => qr/^[^@<>]+@[^@.<>]+(?:\.[^@.<>]+)+$/ },
{ post => 'issue_code', required => 0, default => '' },
{ post => 'issue_message', maxlength => 256*1024, minlength => 1 },
{ post => 'bug_id', min => 0 },
{ post => 'bug_summary', maxlength => 200, minlength => 2 },
{ post => 'bug_email', required => 0, regex => qr/^[^@<>]+@[^@.<>]+(?:\.[^@.<>]+)+$/ },
{ post => 'bug_code', required => 0, default => '' },
{ post => 'bug_message', maxlength => 256*1024, minlength => 1 },
);
return($f, undef) if $f->{_err};
my $l;
# Reply
if($f->{issue_id} > 0) {
$l = $s->dbListing(id => $f->{issue_id})->[0];
push @{$f->{_err}}, ['issue_id', 'db_check', ''] and return($f, undef) if !$l;
if($f->{bug_id} > 0) {
$l = $s->dbListing(id => $f->{bug_id})->[0];
push @{$f->{_err}}, ['bug_id', 'db_check', ''] and return($f, undef) if !$l;
# Check admin things
if(grep $_ eq $f->{issue_code}, @{$s->{admins}}) {
if(grep $_ eq $f->{bug_code}, @{$s->{admins}}) {
my $fa = $TUWF::OBJ->formValidate(
{ post => 'issue_type', enum => $s->{types} },
{ post => 'issue_status', enum => $s->{statusses} },
{ post => 'issue_closed', enum => [0,1] },
{ post => 'bug_type', enum => $s->{types} },
{ post => 'bug_status', enum => $s->{statusses} },
{ post => 'bug_closed', enum => [0,1] },
);
$f = { %$f, %$fa };
return($f, $l) if $f->{_err};
} else {
$f->{issue_type} = $l->{type};
$f->{issue_status} = $l->{status};
$f->{issue_closed} = $l->{closed};
$f->{bug_type} = $l->{type};
$f->{bug_status} = $l->{status};
$f->{bug_closed} = $l->{closed};
}
# New issue
# New bug
} else {
$f->{issue_status} = $s->{default_status};
$f->{issue_closed} = 0;
my $fa = $TUWF::OBJ->formValidate({ post => 'issue_type', enum => $s->{types} });
$f->{bug_status} = $s->{default_status};
$f->{bug_closed} = 0;
my $fa = $TUWF::OBJ->formValidate({ post => 'bug_type', enum => $s->{types} });
$f = { %$f, %$fa };
return($f, $l) if $f->{_err};
}
# No errors? Save!
my $id = $s->dbSave(map $f->{"issue_$_"}, qw|id closed summary email type status message|);
my $id = $s->dbSave(map $f->{"bug_$_"}, qw|id closed summary email type status message|);
# For replies, send out notification emails
if($l) {
my $mails = $s->dbEmails($id);
my $u = $url->($id);
for(grep $_ ne $f->{issue_email}, @$mails) {
for(grep $_ ne $f->{bug_email}, @$mails) {
$TUWF::OBJ->mail(
"Hello!\n\n".
"A new reply has been posted to an issue you have previously shown\n".
"A new reply has been posted to an bug you have previously shown\n".
"an interest in. You can view the reply at the following URL:\n".
" $u\n\n".
"If you do not wish to receive any more notifications for this (and\n".
"perhaps other) issues, please reply to this email stating your intent.",
Subject => "Reply to $f->{issue_summary}",
"perhaps other) bugs, please reply to this email stating your intent.",
Subject => "Reply to $f->{bug_summary}",
To => "$_",
);
}

View file

@ -59,5 +59,5 @@ it. L<source|http://p.blicky.net/agolr>
December 2011. Playing around with the Go programming language, I wrote another
transfer log parser and statistics generator for ncdc.
L<source|http://p.blicky.net/6yx2d> (L<older
version|http://p.blicky.net/ab4lm>).
L<0.3|http://p.blicky.net/h25z8>
(L<0.2|http://p.blicky.net/6yx2d>, L<0.1|http://p.blicky.net/ab4lm>).

View file

@ -157,5 +157,5 @@ myself, though. I just run ncdc directly on my router. :-)
Nothing like that is included in the release yet, but there is a simple Perl
script available: L<ncdc-transfer-stats|http://p.blicky.net/agolr>, and a short
Go program: L<ncdc-share-report|http://p.blicky.net/6yx2d>.
Go program: L<ncdc-share-report|http://p.blicky.net/h25z8>.

View file

@ -12,8 +12,9 @@ BEGIN { ($ROOT = abs_path $0) =~ s{index\.cgi$}{}; }
my @changes = (
[ '2012-03-24', '/ncdu/issue', 'Moved issue tracker from sourceforge to this site' ],
[ '2012-03-17', '/ncdc/issue', 'Wrote a small issue tracker for ncdc' ],
[ '2012-03-30', '/dump', 'Updated ncdc-share-report for Go 1' ],
[ '2012-03-24', '/ncdu/bug', 'Moved ncdu bug tracker from sourceforge to this site' ],
[ '2012-03-17', '/ncdc/bug', 'Wrote a small bug tracker for ncdc' ],
[ '2012-03-14', '/ncdc', 'ncdc 1.9 released.' ],
[ '2012-02-15', '/doc/commvis', 'Added an article on my new communication system.' ],
[ '2012-02-13', '/ncdc', 'ncdc 1.8 released.' ],
@ -76,16 +77,16 @@ TUWF::register(
qr{dump/grenamr} => sub { podpage(shift, 'dump-grenamr', 'dump', 'grenamr', 'GTK+ Mass File Renamer') },
qr{dump/nccolour} => sub { podpage(shift, 'dump-nccolour', 'dump', 'nccolour', 'Colours in NCurses') },
qr{feed\.atom} => \&atom,
qr{(ncdc|ncdu)/issue} => \&issue_list,
qr{(ncdc|ncdu)/issue/post} => \&issue_post,
qr{(ncdc|ncdu)/issue/new} => \&issue_new,
qr{(ncdc|ncdu)/issue/([1-9][0-9]*)} => \&issue_item,
qr{(ncdc|ncdu)/bug} => \&bug_list,
qr{(ncdc|ncdu)/bug/post} => \&bug_post,
qr{(ncdc|ncdu)/bug/new} => \&bug_new,
qr{(ncdc|ncdu)/bug/([1-9][0-9]*)} => \&bug_item,
);
TUWF::set(
logfile => '/www/err.log',
error_404_handler => \&notfound,
mail_from => 'Yorhel\'s issue tracker <projects@yorhel.nl>',
mail_from => 'Yorhel\'s Bug Tracker <projects@yorhel.nl>',
# this is a fairly static site, allow some aggressive caching
pre_request_handler => sub { $_[0]->resHeader('Cache-Control', 's-max-age=86400, max-age=3600'); 1; },
);
@ -189,6 +190,7 @@ sub notfound {
[ 'dump/grenamr', '/dump/grenamr' ],
);
return $s->resRedirect("/$u", 'perm') if $u =~ s/\/$//;
return $s->resRedirect("/$1/bug$2", 'perm') if $u =~ /^(ncd[uc])\/issue(.*)$/;
$s->resStatus(404);
$s->htmlHeader(title => '404', page => '404');
txt 'Sorry, there is no page at this URI. Try one of the links from the menu to find the page you are looking for.';
@ -198,69 +200,69 @@ sub notfound {
# Issue handling
# Bug tracker
sub _issue_init {
require "$ROOT/Issue.pm";
sub _bug_init {
require "$ROOT/Bug.pm";
my($s, $p) = @_;
$s->resHeader('Cache-Control', 'no-cache');
$s->_load_module('TUWF::DB');
$s->{_TUWF}{db_login} = [ undef, undef, undef ];
$s->dbInit;
return TUWF::Issue->new(prefix => $p.'_', admins => [ $ENV{ISSUE_CODE} ]);
return TUWF::Bug->new(prefix => $p.'_', admins => [ $ENV{ISSUE_CODE} ]);
}
sub issue_list {
sub bug_list {
my($s, $p) = @_;
my $is = _issue_init(@_);
$s->htmlHeader(title => "\u$p Issue tracker", page => $p, sec => 'issue');
br; a href => "/$p/issue/new", 'Report new issue'; br; br;
$is->htmlListing((scalar $is->dbListing()), sub { "/$p/issue/".shift });
br; a href => "/$p/issue/new", 'Report new issue'; br; br;
my $is = _bug_init(@_);
$s->htmlHeader(title => "\u$p Bug tracker", page => $p, sec => 'bug');
br; a href => "/$p/bug/new", 'Report new bug'; br; br;
$is->htmlListing((scalar $is->dbListing()), sub { "/$p/bug/".shift });
br; a href => "/$p/bug/new", 'Report new bug'; br; br;
$s->htmlFooter;
}
sub issue_new {
sub bug_new {
my($s, $p) = @_;
my $is = _issue_init(@_);
$s->htmlHeader(title => "\u$p: Report new issue", page => $p, sec => 'issue');
br; a href => "/$p/issue", 'Back to the issue index'; br; br;
$is->htmlForm(undef, "/$p/issue/post");
my $is = _bug_init(@_);
$s->htmlHeader(title => "\u$p: Report new bug", page => $p, sec => 'bug');
br; a href => "/$p/bug", 'Back to the bug index'; br; br;
$is->htmlForm(undef, "/$p/bug/post");
$s->htmlFooter;
}
sub issue_post {
sub bug_post {
my($s, $p) = @_;
return $s->resNotFound if $s->reqMethod() ne 'POST';
my $is = _issue_init($s, $p);
my($f, $l) = $is->handleForm(sub { "http://dev.yorhel.nl/$p/issue/".shift });
my $is = _bug_init($s, $p);
my($f, $l) = $is->handleForm(sub { "http://dev.yorhel.nl/$p/bug/".shift });
if($f->{_err}) {
$s->htmlHeader(title => 'Error creating message', page => $p, sec => 'issue');
$s->htmlHeader(title => 'Error creating message', page => $p, sec => 'bug');
p 'There was an error in the form. Please use the \'back\' button of your
browser to go back to the form (hopefully) without losing your message.
There was an error in the following fields: '.join(', ', map {(my$f=$_->[0])=~s/issue_// ;"\u$f"} @{$f->{_err}}).'.';
There was an error in the following fields: '.join(', ', map {(my$f=$_->[0])=~s/bug_// ;"\u$f"} @{$f->{_err}}).'.';
return $s->htmlFooter;
}
$s->resRedirect("/$p/issue/$l->{issue}", 'post');
$s->resRedirect("/$p/bug/$l->{bug}", 'post');
}
sub issue_item {
sub bug_item {
my($s, $p, $i) = @_;
my $is = _issue_init($s, $p);
my $is = _bug_init($s, $p);
my $item = $is->dbItem($i);
return $s->resNotFound if !@$item;
my $last = $item->[$#$item];
$s->htmlHeader(title => "\u$p: $last->{summary}", page => $p, sec => 'issue');
br; a href => "/$p/issue", 'Back to the issue index'; br; br;
$s->htmlHeader(title => "\u$p: $last->{summary}", page => $p, sec => 'bug');
br; a href => "/$p/bug", 'Back to the bug index'; br; br;
$is->htmlItem($item);
$is->htmlForm($last, "/$p/issue/post") if !$last->{closed};
br; a href => "/$p/issue", 'Back to the issue index'; br; br;
$is->htmlForm($last, "/$p/bug/post") if !$last->{closed};
br; a href => "/$p/bug", 'Back to the bug index'; br; br;
$s->htmlFooter;
}
@ -404,7 +406,7 @@ sub htmlMenu {
$m->('/ncdu/man', 'Manual', $o{sec} eq 'man');
$m->('/ncdu/changes', 'Changelog', $o{sec} eq 'changes');
$m->('/ncdu/scr', 'Screenshots', $o{sec} eq 'scr');
$m->('/ncdu/issue', 'Issue tracker', $o{sec} eq 'issue');
$m->('/ncdu/bug', 'Bug tracker', $o{sec} eq 'bug');
});
$m->('/ncdc', 'Ncdc', $o{page} eq 'ncdc', sub {
$m->('/ncdc', 'Info', !$o{sec});
@ -412,7 +414,7 @@ sub htmlMenu {
$m->('/ncdc/man', 'Manual', $o{sec} eq 'man');
$m->('/ncdc/changes', 'Changelog', $o{sec} eq 'changes');
$m->('/ncdc/scr', 'Screenshots', $o{sec} eq 'scr');
$m->('/ncdc/issue', 'Issue tracker', $o{sec} eq 'issue');
$m->('/ncdc/bug', 'Bug tracker', $o{sec} eq 'bug');
});
$m->('/tuwf', 'Tuwf', $o{page} eq 'tuwf', sub {
$m->('/tuwf', 'Info', !$o{sec});

View file

@ -65,23 +65,23 @@ textarea:focus, input:focus { background: #fff }
table { border-collapse: collapse }
table td { padding: 0 2px }
table thead td { font-weight: bold }
.issue_listing tbody tr:nth-child(odd) { background-color: #f4f4f4 }
.issue_listing { width: 95% }
.issue_col_id, .issue_col_type, .issue_col_status, .issue_col_date { white-space: nowrap }
.issue_closed td { text-decoration: line-through }
.issue_status { display: block; height: 20px }
.issue_status dt { float: left; font-weight: bold }
.issue_status dd { float: left; }
.issue_item h1 { margin-top: 30px }
.issue_item dt { clear: left; float: left; font-weight: bold; width: 60px }
.issue_item dd { float: left; padding-right: 20px }
.issue_item p { clear: left; padding-top: 5px }
.issue_frm fieldset { border: 0; margin-top: 40px }
.issue_frm legend { font-size: 19px; color: #000; }
.issue_frm li { list-style-type: none; margin-left: 10px; clear: left; padding-top: 5px }
.issue_frm label { display: block; width: 80px; float: left }
.issue_frm input, .issue_frm select { float: left }
.issue_frm textarea { width: 100%; height: 200px }
.issue_frm_submit input { float: right; width: 200px }
.bug_listing tbody tr:nth-child(odd) { background-color: #f4f4f4 }
.bug_listing { width: 95% }
.bug_col_id, .bug_col_type, .bug_col_status, .bug_col_date { white-space: nowrap }
.bug_closed td { text-decoration: line-through }
.bug_status { display: block; height: 20px }
.bug_status dt { float: left; font-weight: bold }
.bug_status dd { float: left; }
.bug_item h1 { margin-top: 30px }
.bug_item dt { clear: left; float: left; font-weight: bold; width: 60px }
.bug_item dd { float: left; padding-right: 20px }
.bug_item p { clear: left; padding-top: 5px }
.bug_frm fieldset { border: 0; margin-top: 40px }
.bug_frm legend { font-size: 19px; color: #000; }
.bug_frm li { list-style-type: none; margin-left: 10px; clear: left; padding-top: 5px }
.bug_frm label { display: block; width: 80px; float: left }
.bug_frm input, .bug_frm select { float: left }
.bug_frm textarea { width: 100%; height: 200px }
.bug_frm_submit input { float: right; width: 200px }
.issue_status dt::after, .issue_item dt::after, .issue_frm label::after { content: ":" }
.bug_status dt::after, .bug_item dt::after, .bug_frm label::after { content: ":" }