From: Don Armstrong Date: Sat, 24 May 2008 12:46:52 +0000 (+0200) Subject: * Move code from bugreport.cgi to Debbugs::CGI::Bugreport X-Git-Tag: release/2.6.0~488^2~54 X-Git-Url: https://git.donarmstrong.com/?p=debbugs.git;a=commitdiff_plain;h=343464a69a83b5b2f1e956386482baaaccbc835c * Move code from bugreport.cgi to Debbugs::CGI::Bugreport * Switch bugreport.cgi to using templates * Remove useless calls from pkgreport * Add new package_links and report_links commands to unify the package and bug linking * Delete extra copy of bugs.css * Fix the naming of the html_tail file * Use UNTAINT in Debbugs::Text * Remove useless prototypes in Debbugs::MIME * Tweak Debbugs::Config to allow empty $ENV{HOME} --- diff --git a/Debbugs/CGI.pm b/Debbugs/CGI.pm index 86f82e7..e18891a 100644 --- a/Debbugs/CGI.pm +++ b/Debbugs/CGI.pm @@ -37,7 +37,7 @@ use vars qw($VERSION $DEBUG %EXPORT_TAGS @EXPORT_OK @EXPORT); use base qw(Exporter); use Debbugs::URI; use HTML::Entities; -use Debbugs::Common qw(getparsedaddrs); +use Debbugs::Common qw(getparsedaddrs make_list); use Params::Validate qw(validate_with :types); use Debbugs::Config qw(:config); use Debbugs::Status qw(splitpackages isstrongseverity); @@ -45,6 +45,8 @@ use Mail::Address; use POSIX qw(ceil); use Storable qw(dclone); +use Carp; + use Debbugs::Text qw(fill_in_template); our %URL_PARAMS = (); @@ -57,7 +59,8 @@ BEGIN{ @EXPORT = (); %EXPORT_TAGS = (url => [qw(bug_url bug_links bug_linklist maybelink), qw(set_url_params pkg_url version_url), - qw(submitterurl mainturl munge_url) + qw(submitterurl mainturl munge_url), + qw(package_links bug_links), ], html => [qw(html_escape htmlize_bugs htmlize_packagelinks), qw(maybelink htmlize_addresslinks htmlize_maintlinks), @@ -284,158 +287,184 @@ sub quitcgi { =head HTML -=head2 htmlize_bugs +=head2 htmlize_packagelinks - htmlize_bugs({bug=>1,status=>\%status,extravars=>\%extra},{bug=>...}}); + htmlize_packagelinks -Turns a list of bugs into an html snippit of the bugs. +Given a scalar containing a list of packages separated by something +that L can separate, returns a +formatted set of links to packages. =cut -# htmlize_bugs(bugs=>[@bugs]); -sub htmlize_bugs{ - my @bugs = @_; - my @html; - - for my $bug (@bugs) { - my $html = sprintf "
  • #%d: %s\n
    ", - bug_url($bug->{bug}), $bug->{bug}, html_escape($bug->{status}{subject}); - $html .= htmlize_bugstatus($bug->{status}) . "\n"; - } - return @html; + +sub htmlize_packagelinks { + my ($pkgs) = @_; + return '' unless defined $pkgs and $pkgs ne ''; + my @pkglist = splitpackages($pkgs); + + carp "htmlize_packagelinks is deprecated"; + + return 'Package' . (@pkglist > 1 ? 's' : '') . ': ' . + join(', ', + package_links(package =>\@pkglist, + class => 'submitter' + ) + ); } +=head2 package_links -sub htmlize_bugstatus { - my %status = %{$_[0]}; + join(', ', package_links(packages => \@packages)) - my $result = ""; +Given a list of packages, return a list of html which links to the package - my $showseverity; - if ($status{severity} eq $config{default_severity}) { - $showseverity = ''; - } elsif (isstrongseverity($status{severity})) { - $showseverity = "Severity: $status{severity};\n"; - } else { - $showseverity = "Severity: $status{severity};\n"; - } +=over - $result .= htmlize_packagelinks($status{"package"}, 1); +=item package -- arrayref or scalar of package(s) - my $showversions = ''; - if (@{$status{found_versions}}) { - my @found = @{$status{found_versions}}; - local $_; - s{/}{ } foreach @found; - $showversions .= join ', ', map html_escape($_), @found; - } - if (@{$status{fixed_versions}}) { - $showversions .= '; ' if length $showversions; - $showversions .= 'fixed: '; - my @fixed = @{$status{fixed_versions}}; - $showversions .= join ', ', map {s#/##; html_escape($_)} @fixed; +=item submitter -- arrayref or scalar of submitter(s) + +=item source -- arrayref or scalar of source(s) + +=item maintainer -- arrayref or scalar of maintainer(s) + +=item links_only -- return only links, not htmlized links, defaults to +returning htmlized links. + +=item class -- class of the a href, defaults to '' + +=back + +=cut + +sub package_links { + my %param = validate_with(params => \@_, + spec => {package => {type => SCALAR|ARRAYREF, + optional => 1, + }, + source => {type => SCALAR|ARRAYREF, + optional => 1, + }, + maintainer => {type => SCALAR|ARRAYREF, + optional => 1, + }, + submitter => {type => SCALAR|ARRAYREF, + optional => 1, + }, + owner => {type => SCALAR|ARRAYREF, + optional => 1, + }, + links_only => {type => BOOLEAN, + default => 0, + }, + class => {type => SCALAR, + default => '', + }, + separator => {type => SCALAR, + default => ', ', + }, + }, + ); + my @links = (); + push @links, map {(pkg_url(source => $_),$_) + } make_list($param{source}) if exists $param{source}; + push @links, map {my $addr = getparsedaddrs($_); + $addr = defined $addr?$addr->address:''; + (pkg_url(maint => $addr),$_) + } make_list($param{maintainer}) if exists $param{maintainer}; + push @links, map {my $addr = getparsedaddrs($_); + $addr = defined $addr?$addr->address:''; + (pkg_url(owner => $addr),$_) + } make_list($param{owner}) if exists $param{owner}; + push @links, map {my $addr = getparsedaddrs($_); + $addr = defined $addr?$addr->address:''; + (pkg_url(submitter => $addr),$_) + } make_list($param{submitter}) if exists $param{submitter}; + push @links, map {(pkg_url(pkg => $_), + html_escape($_)) + } make_list($param{package}) if exists $param{package}; + my @return = (); + my ($link,$link_name); + my $class = ''; + if (length $param{class}) { + $class = q( class=").html_escape($param{class}).q("); } - $result .= " ($showversions)" if length $showversions; - $result .= ";\n"; - - $result .= $showseverity; - $result .= htmlize_addresslinks("Reported by: ", \&submitterurl, - $status{originator}); - $result .= ";\nOwned by: " . html_escape($status{owner}) - if length $status{owner}; - $result .= ";\nTags: " - . html_escape(join(", ", sort(split(/\s+/, $status{tags})))) - . "" - if (length($status{tags})); - - $result .= ";\nMerged with ". - bug_linklist(', ', - 'submitter', - split(/ /,$status{mergedwith})) - if length $status{mergedwith}; - $result .= ";\nBlocked by ". - bug_linklist(", ", - 'submitter', - split(/ /,$status{blockedby})) - if length $status{blockedby}; - $result .= ";\nBlocks ". - bug_linklist(", ", - 'submitter', - split(/ /,$status{blocks}) - ) - if length $status{blocks}; - - my $days = 0; - if (length($status{done})) { - $result .= "
    Done: " . html_escape($status{done}); - $days = ceil($debbugs::gRemoveAge - -M buglog($status{id})); - if ($days >= 0) { - $result .= ";\nWill be archived" . ( $days == 0 ? " today" : $days == 1 ? " in $days day" : " in $days days" ) . ""; - } else { - $result .= ";\nArchived"; + while (($link,$link_name) = splice(@links,0,2)) { + if ($param{links_only}) { + push @return,$link } + else { + push @return, + qq(). + html_escape($link_name).q(); + } + } + if (wantarray) { + return @return; } else { - if (length($status{forwarded})) { - $result .= ";\nForwarded to " - . maybelink($status{forwarded}); - } - my $daysold = int((time - $status{date}) / 86400); # seconds to days - if ($daysold >= 7) { - my $font = ""; - my $efont = ""; - $font = "em" if ($daysold > 30); - $font = "strong" if ($daysold > 60); - $efont = "" if ($font); - $font = "<$font>" if ($font); - - my $yearsold = int($daysold / 365); - $daysold -= $yearsold * 365; - - $result .= ";\n $font"; - my @age; - push @age, "1 year" if ($yearsold == 1); - push @age, "$yearsold years" if ($yearsold > 1); - push @age, "1 day" if ($daysold == 1); - push @age, "$daysold days" if ($daysold > 1); - $result .= join(" and ", @age); - $result .= " old$efont"; - } - } + return join($param{separator},@return); + } +} - $result .= "."; +=head2 bug_links - return $result; -} + join(', ', bug_links(bug => \@packages)) -=head2 htmlize_packagelinks +Given a list of bugs, return a list of html which links to the bugs - htmlize_packagelinks +=over -Given a scalar containing a list of packages separated by something -that L can separate, returns a -formatted set of links to packages. +=item bug -- arrayref or scalar of bug(s) -=cut +=item links_only -- return only links, not htmlized links, defaults to +returning htmlized links. -sub htmlize_packagelinks { - my ($pkgs,$strong) = @_; - return unless defined $pkgs and $pkgs ne ''; - my @pkglist = splitpackages($pkgs); +=item class -- class of the a href, defaults to '' - $strong = 0; - my $openstrong = $strong ? '' : ''; - my $closestrong = $strong ? '' : ''; +=back - return 'Package' . (@pkglist > 1 ? 's' : '') . ': ' . - join(', ', - map { - '' . - $openstrong . html_escape($_) . $closestrong . '' - } @pkglist - ); +=cut + +sub bug_links { + my %param = validate_with(params => \@_, + spec => {bug => {type => SCALAR|ARRAYREF, + optional => 1, + }, + links_only => {type => BOOLEAN, + default => 0, + }, + class => {type => SCALAR, + default => '', + }, + }, + ); + my @links; + push @links, map {(bug_url($_),$_) + } make_list($param{bug}) if exists $param{bug}; + my @return; + my ($link,$link_name); + my $class = ''; + if (length $param{class}) { + $class = q( class=").html_escape($param{class}).q("); + } + while (($link,$link_name) = splice(@links,0,2)) { + if ($param{links_only}) { + push @return,$link + } + else { + push @return, + qq(). + html_escape($link_name).q(); + } + } + return @return; } + =head2 maybelink maybelink($in); @@ -487,6 +516,8 @@ or submitterurl which returns the URL for each individual address. sub htmlize_addresslinks { my ($prefixfunc, $urlfunc, $addresses,$class) = @_; + carp "htmlize_addresslinks is deprecated"; + $class = defined $class?qq(class="$class" ):''; if (defined $addresses and $addresses ne '') { my @addrs = getparsedaddrs($addresses); @@ -520,6 +551,7 @@ sub mainturl { pkg_url(maint => emailfromrfc822($_[0])); } sub submitterurl { pkg_url(submitter => emailfromrfc822($_[0])); } sub htmlize_maintlinks { my ($prefixfunc, $maints) = @_; + carp "htmlize_maintlinks is deprecated"; return htmlize_addresslinks($prefixfunc, \&mainturl, $maints); } @@ -527,33 +559,6 @@ sub htmlize_maintlinks { our $_maintainer; our $_maintainer_rev; -=head2 bug_links - - bug_links($one_bug); - bug_links($starting_bug,$stoping_bugs,); - -Creates a set of links to bugs, starting with bug number -$starting_bug, and finishing with $stoping_bug; if only one bug is -passed, makes a link to only a single bug. - -The content of the link is the bug number. - -XXX Use L; we want to be able to support query -arguments here too. - -=cut - -sub bug_links{ - my ($start,$stop,$query_arguments) = @_; - $stop = $stop || $start; - $query_arguments ||= ''; - my @output; - for my $bug ($start..$stop) { - push @output,'$bug); - } - return join(', ',@output); -} - =head2 bug_linklist bug_linklist($separator,$class,@bugs) @@ -571,12 +576,7 @@ too.] sub bug_linklist{ my ($sep,$class,@bugs) = @_; - if (length $class) { - $class = qq(class="$class" ); - } - return join($sep,map{qq(#$_) - } @bugs); + return join($sep,bug_links(bug=>\@bugs,class=>$class)); } diff --git a/Debbugs/CGI/Bugreport.pm b/Debbugs/CGI/Bugreport.pm index 28bc63f..85dc3b9 100644 --- a/Debbugs/CGI/Bugreport.pm +++ b/Debbugs/CGI/Bugreport.pm @@ -31,6 +31,10 @@ use vars qw($VERSION $DEBUG %EXPORT_TAGS @EXPORT_OK @EXPORT); use base qw(Exporter); use IO::Scalar; +use Params::Validate qw(validate_with :types); +use Debbugs::MIME qw(convert_to_utf8 decode_rfc1522 create_mime_message); +use Debbugs::CGI qw(:url :html :util); +use POSIX qw(strftime); BEGIN{ ($VERSION) = q$Revision: 494 $ =~ /^Revision:\s+([^\s+])/; @@ -74,40 +78,43 @@ BEGIN{ =cut sub display_entity { - my %param = valid_with(params => \@_, - spec => {entity => {type => OBJECT, - }, - bug_num => {type => SCALAR, - regex => qr/^\d+$/, - }, - outer => {type => BOOLEAN, - default => 1, - }, - msg_num => {type => SCALAR, - }, - attachments => {type => ARRAYREF, - default => [], - }, - output => {type => SCALARREF|HANDLE, - default => \*STDOUT, - }, - terse => {type => BOOLEAN, - default => 0, - }, - msg => {type => SCALAR, - optional => 1, - }, - attachment => {type => SCALAR, - optional => 1, - }, - } - ); + my %param = validate_with(params => \@_, + spec => {entity => {type => OBJECT, + }, + bug_num => {type => SCALAR, + regex => qr/^\d+$/, + }, + outer => {type => BOOLEAN, + default => 1, + }, + msg_num => {type => SCALAR, + }, + attachments => {type => ARRAYREF, + default => [], + }, + output => {type => SCALARREF|HANDLE, + default => \*STDOUT, + }, + terse => {type => BOOLEAN, + default => 0, + }, + msg => {type => SCALAR, + optional => 1, + }, + att => {type => SCALAR, + optional => 1, + }, + trim_headers => {type => BOOLEAN, + default => 1, + }, + } + ); my $entity = $param{entity}; my $ref = $param{bug_num}; - my $top = $param{outer} + my $top = $param{outer}; my $xmessage = $param{msg_num}; - if (defined ref($options) and + if (defined ref($param{output}) and ref($param{output}) eq 'SCALAR' and not UNIVERSAL::isa($param{output},'GLOB')) { $param{output} = IO::Scalar->new($param{output}); @@ -125,7 +132,7 @@ sub display_entity { if ($top and not $param{terse}) { my $header = $entity->head; print {$param{output}} "
    \n";
    -	 if ($trim_headers) {
    +	 if ($param{trim_headers}) {
     	      my @headers;
     	      foreach (qw(From To Cc Subject Date)) {
     		   my $head_field = $head->get($_);
    @@ -150,7 +157,7 @@ sub display_entity {
     		  "($type, $disposition)]
    \n"; if (exists $param{msg} and exists $param{att} and - $att == $#$attachments) { + $param{att} == $#$attachments) { my $head = $entity->head; chomp(my $type = $entity->effective_type); my $body = $entity->stringify_body; @@ -171,7 +178,7 @@ sub display_entity { } } - return if not $top and $disposition eq 'attachment' and not defined($att); + return if not $top and $disposition eq 'attachment' and not defined($param{att}); return unless ($type =~ m[^text/?] and $type !~ m[^text/(?:html|enriched)(?:;|$)]) or $type =~ m[^application/pgp(?:;|$)] or @@ -223,9 +230,11 @@ sub display_entity { # flowed e-mails cause they don't really matter. } # Add links to URLs - # We don't html escape here because we escape above - $body =~ s{((ftp|http|https)://[\S~-]+?/?)((\>\;)?[)]?[']?[:.\,]?(\s|$))} - {$1$3}go; + # We don't html escape here because we escape above; + # wierd terminators are because of that + $body =~ s{((?:ftp|http|https|svn|ftps|rsync)://[\S~-]+?/?) # Url + ((?:\>\;)?[)]?(?:'|\&\#39\;)?[:.\,]?(?:\s|$)) # terminators + }{$1$2}gox; # Add links to bug closures $body =~ s[(closes:\s*(?:bug)?\#?\s?\d+(?:,?\s*(?:bug)?\#?\s?\d+)*)] [my $temp = $1; @@ -244,7 +253,7 @@ sub display_entity { handle_email_message($record->{text}, ref => $bug_number, - msg_number => $msg_number, + msg_num => $msg_number, ); Returns a decoded e-mail message and displays entities/attachments as @@ -254,7 +263,7 @@ appropriate. =cut sub handle_email_message{ - my ($email,%options) = @_; + my ($email,%param) = @_; my $output = ''; my $parser = new MIME::Parser; @@ -265,12 +274,12 @@ sub handle_email_message{ my $entity = $parser->parse_data( $email); my @attachments = (); display_entity(entity => $entity, - bug_num => $options{ref}, + bug_num => $param{ref}, outer => 1, - msg_number => $options{msg_number}, - ouput => $output, + msg_num => $param{msg_num}, + output => \$output, attachments => \@attachments, - terse => $params{terse}, + terse => $param{terse}, exists $param{msg}?(msg=>$param{msg}):(), exists $param{att}?(attachment=>$param{att}):(), ); @@ -301,21 +310,21 @@ sub handle_record{ # (even though these links already exist at the top) $output =~ s,((?:ftp|http|https)://[\S~-]+?/?)([\)\'\:\.\,]?(?:\s|\.<|$)),$1$2,go; # Add links to the cloned bugs - $output =~ s{(Bug )(\d+)( cloned as bugs? )(\d+)(?:\-(\d+)|)}{$1.bug_links($2).$3.bug_links($4,$5)}eo; + $output =~ s{(Bug )(\d+)( cloned as bugs? )(\d+)(?:\-(\d+)|)}{$1.bug_links(bug=>$2).$3.bug_links(bug=>[$4..$5])}eo; # Add links to merged bugs - $output =~ s{(?<=Merged )([\d\s]+)(?=\.)}{join(' ',map {bug_links($_)} (split /\s+/, $1))}eo; + $output =~ s{(?<=Merged )([\d\s]+)(?=\.)}{join(' ',map {bug_links(bug=>$_)} (split /\s+/, $1))}eo; # Add links to blocked bugs $output =~ s{(?<=Blocking bugs)(?:( of )(\d+))?( (?:added|set to|removed):\s+)([\d\s\,]+)} - {(defined $2?$1.bug_links($2):'').$3. - join(' ',map {bug_links($_)} (split /\,?\s+/, $4))}eo; + {(defined $2?$1.bug_links(bug=>$2):'').$3. + join(' ',map {bug_links(bug=>$_)} (split /\,?\s+/, $4))}eo; # Add links to reassigned packages $output =~ s{(Bug reassigned from package \`)([^']+?)((?:'|\&\#39;) to \`)([^']+?)((?:'|\&\#39;))} {$1.q($2).$3.q($4).$5}eo; if (defined $time) { $output .= ' ('.strftime('%a, %d %b %Y %T GMT',gmtime($time)).') '; } - $output .= 'Full text and rfc822 format available.'; + $output .= 'Full text and rfc822 format available.'; $output = qq(

    \n\n) . $output . "
    \n"; } @@ -328,11 +337,11 @@ sub handle_record{ $$seen_msg_ids{$msg_id} = 1; } $output .= qq(

    \n); - $output .= 'View this message in rfc822 format

    '; + $output .= 'View this message in rfc822 format

    '; $output .= handle_email_message($record->{text}, - ref => $bug_number, - msg_number => $msg_number, - ); + ref => $bug_number, + msg_num => $msg_number, + ); } elsif (/autocheck/) { # Do nothing @@ -349,12 +358,12 @@ sub handle_record{ my ($received,$hostname) = $record->{text} =~ m/Received: \(at (\S+)\) by (\S+)\;/; $output .= qq|

    Message #$msg_number received at |. html_escape("$received\@$hostname") . - q| (full text'. - q|, mbox)'.":

    \n"; + q| (full text'. + q|, mbox)'.":

    \n"; $output .= handle_email_message($record->{text}, - ref => $bug_number, - msg_number => $msg_number, - ); + ref => $bug_number, + msg_num => $msg_number, + ); } else { die "Unknown record type $_"; diff --git a/Debbugs/Common.pm b/Debbugs/Common.pm index bb42bf8..8d83729 100644 --- a/Debbugs/Common.pm +++ b/Debbugs/Common.pm @@ -430,7 +430,7 @@ instead. (Or possibly a die handler, if the cleanups are important) =cut sub quit { - print $DEBUG_FH "quitting >$_[0]<\n" if $DEBUG; + print {$DEBUG_FH} "quitting >$_[0]<\n" if $DEBUG; my ($u); while ($u= $cleanups[$#cleanups]) { &$u; } die "*** $_[0]\n"; diff --git a/Debbugs/Config.pm b/Debbugs/Config.pm index 895b799..5c3e643 100644 --- a/Debbugs/Config.pm +++ b/Debbugs/Config.pm @@ -67,7 +67,7 @@ BEGIN { qw(@gDefaultArchitectures), qw($gTemplateDir), qw($gDefaultPackage), - qw($gSpamMaxThreads $gSpamSpamsPerThread $gSpamKeepRunning $gSpamScan $gSpamCrossassassinDb) + qw($gSpamMaxThreads $gSpamSpamsPerThread $gSpamKeepRunning $gSpamScan $gSpamCrossassassinDb), ], text => [qw($gBadEmailPrefix $gHTMLTail $gHTMLExpireNote), ], @@ -76,6 +76,7 @@ BEGIN { @EXPORT_OK = (); Exporter::export_ok_tags(qw(globals text config)); $EXPORT_TAGS{all} = [@EXPORT_OK]; + $ENV{HOME} = '' if not defined $ENV{HOME}; } use File::Basename qw(dirname); @@ -729,7 +730,6 @@ Site rules directory for spamassassin, defaults to set_default(\%config,'spam_rules_dir','/usr/share/spamassassin'); - =back diff --git a/Debbugs/MIME.pm b/Debbugs/MIME.pm index 183adc7..2a695e5 100644 --- a/Debbugs/MIME.pm +++ b/Debbugs/MIME.pm @@ -215,8 +215,7 @@ BEGIN { ])); } -sub decode_rfc1522 ($) -{ +sub decode_rfc1522 { my ($string) = @_; # this is craptacular, but leading space is hacked off by unmime. @@ -240,7 +239,7 @@ MIME::Words::encode_mimeword on distinct words as appropriate. # We cannot use MIME::Words::encode_mimewords because that function # does not handle spaces properly at all. -sub encode_rfc1522 ($) { +sub encode_rfc1522 { my ($rawstr) = @_; # handle being passed undef properly diff --git a/Debbugs/Text.pm b/Debbugs/Text.pm index 0f3a529..c499814 100644 --- a/Debbugs/Text.pm +++ b/Debbugs/Text.pm @@ -16,8 +16,8 @@ Debbugs::Text -- General routines for text templates =head1 SYNOPSIS -use Debbugs::Text qw(:templates); -print fill_in_template(template => 'cgi/foo'); + use Debbugs::Text qw(:templates); + print fill_in_template(template => 'cgi/foo'); =head1 DESCRIPTION @@ -164,6 +164,7 @@ sub fill_in_template{ qw(padsv padav padhv padany), qw(rv2gv refgen srefgen ref), qw(caller require entereval), + qw(gmtime sprintf prtf), ); $safe->share('*STDERR'); $safe->share('%config'); @@ -191,7 +192,7 @@ sub fill_in_template{ my $tt; if ($tt_type eq 'FILE' and defined $tt_templates{$tt_source} and - (stat $tt_source)[9] > $tt_templates{$tt_source}{mtime} + (stat $tt_source)[9] <= $tt_templates{$tt_source}{mtime} ) { $tt = $tt_templates{$tt_source}{template}; } @@ -202,6 +203,7 @@ sub fill_in_template{ } $tt = Text::Template->new(TYPE => $tt_type, SOURCE => $tt_source, + UNTAINT => 1, ); if ($tt_type eq 'FILE') { $tt_templates{$tt_source}{template} = $tt; @@ -210,10 +212,7 @@ sub fill_in_template{ if (not defined $tt) { die "Unable to create Text::Template for $tt_type:$tt_source"; } - my $ret = $tt->fill_in(#(defined $param{nosafe} and $param{nosafe})?():(HASH=>$param{variables}), - #(defined $param{nosafe} and $param{nosafe})?():(SAFE=>$safe), - SAFE => $safe, - #(defined $param{nosafe} and $param{nosafe})?(PACKAGE => 'main'):(), + my $ret = $tt->fill_in(SAFE => $safe, defined $param{output}?(OUTPUT=>$param{output}):(), ); if (not defined $ret) { diff --git a/cgi/bugreport.cgi b/cgi/bugreport.cgi index dcb6482..28c7824 100755 --- a/cgi/bugreport.cgi +++ b/cgi/bugreport.cgi @@ -3,7 +3,7 @@ use warnings; use strict; -use POSIX qw(strftime tzset); +use POSIX qw(strftime); use MIME::Parser; use MIME::Decoder; use IO::Scalar; @@ -13,7 +13,6 @@ use Debbugs::Config qw(:globals :text); # for read_log_records use Debbugs::Log qw(read_log_records); -use Debbugs::MIME qw(convert_to_utf8 decode_rfc1522 create_mime_message); use Debbugs::CGI qw(:url :html :util); use Debbugs::CGI::Bugreport qw(:all); use Debbugs::Common qw(buglog getmaintainers); @@ -21,6 +20,9 @@ use Debbugs::Packages qw(getpkgsrc); use Debbugs::Status qw(splitpackages get_bug_status isstrongseverity); use Scalar::Util qw(looks_like_number); + +use Debbugs::Text qw(:templates); + use CGI::Simple; my $q = new CGI::Simple; @@ -44,14 +46,12 @@ my %param = cgi_parameters(query => $q, ); # This is craptacular. -my $tail_html; - my $ref = $param{bug} or quitcgi("No bug number"); $ref =~ /(\d+)/ or quitcgi("Invalid bug number"); $ref = $1; my $short = "#$ref"; -my $msg = $param{'msg'}; -my $att = $param{'att'}; +my ($msg) = $param{msg} =~ /^\d+$/ if exists $param{msg}; +my ($att) = $param{att} =~ /^\d+$/ if exists $param{att}; my $boring = $param{'boring'} eq 'yes'; my $terse = $param{'terse'} eq 'yes'; my $reverse = $param{'reverse'} eq 'yes'; @@ -71,16 +71,18 @@ my $archive = $param{'archive'} eq 'yes'; my $repeatmerged = $param{'repeatmerged'} eq 'yes'; my $buglog = buglog($ref); +my @stat = stat $buglog; +my $mtime = ''; +if (@stat) { + $mtime = strftime '%a, %d %b %Y %T GMT', gmtime($stat[9]); +} -if (defined $ENV{REQUEST_METHOD} and $ENV{REQUEST_METHOD} eq 'HEAD' and not defined($att) and not $mbox) { - print "Content-Type: text/html; charset=utf-8\n"; - my @stat = stat $buglog; - if (@stat) { - my $mtime = strftime '%a, %d %b %Y %T GMT', gmtime($stat[9]); - print "Last-Modified: $mtime\n"; - } - print "\n"; - exit 0; +if ($q->request_method() eq 'HEAD' and not defined($att) and not $mbox) { + print $q->header(-type => "text/html", + -charset => 'utf-8', + (length $mtime)?(-last_modified => $mtime):(), + ); + exit 0; } @@ -88,13 +90,15 @@ my $buglogfh; if ($buglog =~ m/\.gz$/) { my $oldpath = $ENV{'PATH'}; $ENV{'PATH'} = '/bin:/usr/bin'; - $buglogfh = new IO::File "zcat $buglog |" or &quitcgi("open log for $ref: $!"); + $buglogfh = IO::File->new("zcat $buglog |") or quitcgi("open log for $ref: $!"); $ENV{'PATH'} = $oldpath; } else { - $buglogfh = new IO::File "<$buglog" or &quitcgi("open log for $ref: $!"); + $buglogfh = IO::File->new($buglog,'r') or quitcgi("open log for $ref: $!"); } +my %status = %{get_bug_status(bug=>$ref)}; + my @records; eval{ @records = read_log_records($buglogfh); @@ -211,218 +215,104 @@ my $tpack; my $tmain; my $dtime = strftime "%a, %e %b %Y %T UTC", gmtime; -$tail_html = $gHTMLTail; -$tail_html =~ s/SUBSTITUTE_DTIME/$dtime/; -my %status = %{get_bug_status(bug=>$ref)}; unless (%status) { - print "Content-Type: text/html; charset=utf-8\n\n"; + print $q->header(-type => "text/html", + -charset => 'utf-8', + (length $mtime)?(-last_modified => $mtime):(), + ); print fill_in_template(template=>'cgi/no_such_bug', variables => {modify_time => $dtime, bug_num => $ref, }, - ) + ); exit 0; } -$|=1; +#$|=1; -$tpack = lc $status{'package'}; -my @tpacks = splitpackages($tpack); +my %package; +my @packages = splitpackages($status{package}); -if ($status{severity} eq 'normal') { - $showseverity = ''; -} elsif (isstrongseverity($status{severity})) { - $showseverity = "Severity: $status{severity};\n"; -} else { - $showseverity = "Severity: $status{severity};\n"; +foreach my $pkg (@packages) { + $package{$pkg} = {maintainer => exists($maintainer{$pkg}) ? $maintainer{$pkg} : '(unknown)', + source => exists($pkgsrc{$pkg}) ? $pkgsrc{$pkg} : '(unknown)', + package => $pkg, + }; } +# fixup various bits of the status +$status{tags_array} = [sort(split(/\s+/, $status{tags}))]; +$status{date_text} = strftime('%a, %e %b %Y %T UTC', gmtime($status{date})); +$status{mergedwith_array} = [split(/ /,$status{mergedwith})]; + + +my $version_graph = ''; if (@{$status{found_versions}} or @{$status{fixed_versions}}) { - $indexentry.= q(}; -} - - -$indexentry .= "
    \n"; -$indexentry .= htmlize_packagelinks($status{package}, 0) . ";\n"; - -foreach my $pkg (@tpacks) { - my $tmaint = defined($maintainer{$pkg}) ? $maintainer{$pkg} : '(unknown)'; - my $tsrc = defined($pkgsrc{$pkg}) ? $pkgsrc{$pkg} : '(unknown)'; - - $indexentry .= - htmlize_maintlinks(sub { $_[0] == 1 ? "Maintainer for $pkg is\n" - : "Maintainers for $pkg are\n" }, - $tmaint); - $indexentry .= ";\nSource for $pkg is\n". - '$tsrc" if ($tsrc ne "(unknown)"); - $indexentry .= ".\n"; -} - -$indexentry .= "
    "; -$indexentry .= htmlize_addresslinks("Reported by: ", \&submitterurl, - $status{originator}) . ";\n"; -$indexentry .= sprintf "Date: %s.\n", - (strftime "%a, %e %b %Y %T UTC", localtime($status{date})); - -$indexentry .= "
    Owned by: " . html_escape($status{owner}) . ".\n" - if length $status{owner}; - -$indexentry .= "
    \n"; - -my @descstates; - -$indexentry .= "

    $showseverity"; -$indexentry .= sprintf "Tags: %s;\n", - html_escape(join(", ", sort(split(/\s+/, $status{tags})))) - if length($status{tags}); -$indexentry .= "
    " if (length($showseverity) or length($status{tags})); - -my @merged= split(/ /,$status{mergedwith}); -if (@merged) { - my $descmerged = 'Merged with '; - my $mseparator = ''; - for my $m (@merged) { - $descmerged .= $mseparator."#$m"; - $mseparator= ",\n"; - } - push @descstates, $descmerged; -} - -if (@{$status{found_versions}}) { - my $foundtext = 'Found in '; - $foundtext .= (@{$status{found_versions}} == 1) ? 'version ' : 'versions '; - $foundtext .= join ', ', map html_escape($_), @{$status{found_versions}}; - push @descstates, $foundtext; -} -if (@{$status{fixed_versions}}) { - my $fixedtext = 'Fixed in '; - $fixedtext .= (@{$status{fixed_versions}} == 1) ? 'version ' : 'versions '; - $fixedtext .= join ', ', map html_escape($_), @{$status{fixed_versions}}; - if (length($status{done})) { - $fixedtext .= ' by ' . html_escape(decode_rfc1522($status{done})); - } - push @descstates, $fixedtext; -} - -if (@{$status{found_versions}} or @{$status{fixed_versions}}) { - push @descstates, 'Version Graph}; -} - -if (length($status{done})) { - push @descstates, "Done: ".html_escape(decode_rfc1522($status{done})); + html_escape(version_url(package => $status{package}, + found => $status{found_versions}, + fixed => $status{fixed_versions}, + width => 2, + height => 2, + ) + ). + qq{">}; } -if (length($status{forwarded})) { - my $forward_link = html_escape($status{forwarded}); - $forward_link =~ s,((ftp|http|https)://[\S~-]+?/?)((\>\;)?[)]?[']?[:.\,]?(\s|$)),$1$3,go; - push @descstates, "Forwarded to $forward_link"; -} my @blockedby= split(/ /, $status{blockedby}); +$status{blockedby_array} = []; if (@blockedby && $status{"pending"} ne 'fixed' && ! length($status{done})) { for my $b (@blockedby) { my %s = %{get_bug_status($b)}; next if $s{"pending"} eq 'fixed' || length $s{done}; - push @descstates, "Fix blocked by #$b: ".html_escape($s{subject}); - } + push @{$status{blockedby_array}},{bug_num => $b, subject => $s{subject}, status => \%s}; + } } my @blocks= split(/ /, $status{blocks}); +$status{blocks_array} = []; if (@blocks && $status{"pending"} ne 'fixed' && ! length($status{done})) { for my $b (@blocks) { my %s = %{get_bug_status($b)}; next if $s{"pending"} eq 'fixed' || length $s{done}; - push @descstates, "Blocking fix for #$b: ".html_escape($s{subject}); + push @{$status{blocks_array}}, {bug_num => $b, subject => $s{subject}, status => \%s}; } } if ($buglog !~ m#^\Q$gSpoolDir/db#) { - push @descstates, "Bug is archived. No further changes may be made"; + $status{archived} = 1; } -$indexentry .= join(";\n
    ", @descstates) . ".\n" if @descstates; -$indexentry .= "

    \n"; - my $descriptivehead = $indexentry; -print "Content-Type: text/html; charset=utf-8\n"; - -my @stat = stat $buglog; -if (@stat) { - my $mtime = strftime '%a, %d %b %Y %T GMT', gmtime($stat[9]); - print "Last-Modified: $mtime\n"; -} - -print "\n"; - -my $title = html_escape($status{subject}); - -my $dummy2 = $gWebHostBugDir; - -print "\n"; -print < -$short - $title - $gProject $gBug report logs - - - - - -END -print "

    " . "$gProject $gBug report logs - $short" . - "
    " . $title . "

    \n"; -print "$descriptivehead\n"; - -if (looks_like_number($msg)) { - printf qq(

    Full log

    ),html_escape(bug_url($ref)); -} -else { - print qq(

    Reply ), - qq(or subscribe ), - qq(to this bug.

    \n); - print qq(

    Toggle useless messages

    ); - printf qq(

    View this report as an mbox folder, ). - qq(status mbox, maintainer mbox

    \n), - html_escape(bug_url($ref, mbox=>'yes')), - html_escape(bug_url($ref, mbox=>'yes',mboxstatus=>'yes')), - html_escape(bug_url($ref, mbox=>'yes',mboxmaint=>'yes')); -} -print "$log"; -print "
    "; -print "

    Send a report that this bug log contains spam.

    \n
    \n"; -print $tail_html; - -print "\n"; - -exit 0; +print $q->header(-type => "text/html", + -charset => 'utf-8', + (length $mtime)?(-last_modified => $mtime):(), + ); + +print fill_in_template(template => 'cgi/bugreport', + variables => {status => \%status, + package => \%package, + log => $log, + bug_num => $ref, + version_graph => $version_graph, + isstrongseverity => \&Debbugs::Status::isstrongseverity, + html_escape => \&Debbugs::CGI::html_escape, + looks_like_number => \&Scalar::Util::looks_like_number, + }, + hole_var => {'&package_links' => \&Debbugs::CGI::package_links, + '&bug_links' => \&Debbugs::CGI::bug_links, + '&version_url' => \&Debbugs::CGI::version_url, + '&bug_url' => \&Debbugs::CGI::bug_url, + '&strftime' => \&POSIX::strftime, + } + ); diff --git a/cgi/bugs.css b/cgi/bugs.css deleted file mode 100644 index ed42581..0000000 --- a/cgi/bugs.css +++ /dev/null @@ -1,184 +0,0 @@ -html { - color: #000; - background: #fefefe; - font-family: serif; - margin: 1em; - border: 0; - padding: 0; - line-height: 120%; -} - -body { - margin: 0; - border: 0; - padding: 0; -} - -h1, h2, h3 { - text-align: left; - font-family: sans-serif; -} - -h1 { - font-size: 150%; - line-height: 150%; -} - -h2 { - font-size: 130%; -} - -h3 { - font-size: 100%; -} - -a:link { - color: #1b56ce; - font-weight: bold; -} - -a:visited { - color:#1b56ce; -} - -a:link:active, a:link:visited { - color:#ff0000; -} - -a:link:hover, a:visited:hover { - color: #d81e1e; -} - -a.submitter:link { - color: #242424; - font-family: sans-serif; - font-size: 95%; - text-decoration: underline; - font-weight: normal; -} - -a.submitter:visited, a.submitter:active { - color: #6e6e6e; - font-family: sans-serif; - font-size: 95%; -} - -a.submitter:hover, a.submitter:visited:hover { - color: #d01414; - font-family: sans-serif; - font-size: 95%; -} - -pre.message { - font-family: monospace; - padding-top: 0; - margin-top: 0; - border-top: 0; -} - -.sparse li { - padding-top: 1ex; - margin-top: 1ex; - border-top: 1ex; -} - -a.bugtitle { - font-weight: bold; - font-size: 110%; -} - - -pre.headers { - font-family: sans-serif; - font-size: 95%; - color: #3c3c3c; - background-color: #f0f0f0; - padding: 2px; - border: #a7a7a7 1px solid; - line-height: 120% -} - -pre.mime { - font-family: monospace; - font-size: 95%; - color: #797979; -} - -/* This must be separate from the other CSS to make the showing of - unimportant messages work in bugreport.cgi. */ -.infmessage { display: none; } - -.infmessage { - font-family: sans-serif; - font-size: 90%; - color: #686868; -} - -.msgreceived { - font-family: sans-serif; - font-size: 90%; - color: #686868; -} - -pre.tags { - color: #a3a3a3; - font-size: 90%; -} - -hr.thin { - color: #a1a1a1; -} - -em.severity { - color: #c31212; -} - -code, address { - font-family: sans-serif; -} - -p { - font-family: sans-serif; - font-size: 95%; -} - -h2.outstanding { - font-family: sans-serif; - background-color: #f0f0f0; - color: #3c3c3c; - border: #a7a7a7 1px solid; - padding: 10px; -} - -a.options:link, a.options:visited { - font-family: sans-serif; - background-color: #f0f0f0; - color: #3c3c3c; - text-decoration: none; - border-bottom: #3c3c3c 1px dotted; -} - -li { - list-style-type: square; -} - -.bugs li { - margin-top: 5px; -} - -input { - border: #000 1px solid; - margin: 3px; -} - -table.forms { - font-size: 95%; - font-family: sans-serif; - margin-left: 10px; -} - -select { - margin: 3px; - border: #000 1px solid; -} - diff --git a/cgi/pkgreport.cgi b/cgi/pkgreport.cgi index a841b20..d0db7cb 100755 --- a/cgi/pkgreport.cgi +++ b/cgi/pkgreport.cgi @@ -661,9 +661,8 @@ sub pkg_htmlindexentrystatus { $result .= ";\n"; $result .= $showseverity; - $result .= pkg_htmladdresslinks("Reported by: ", \&submitterurl, - $status{originator}); - $result .= ";\nOwned by: " . html_escape($status{owner}) + $result .= "Reported by: ".package_links(submitter=>$status{originator}); + $result .= ";\nOwned by: " . package_links(owner => $status{owner}) if length $status{owner}; $result .= ";\nTags: " . html_escape(join(", ", sort(split(/\s+/, $status{tags})))) @@ -904,10 +903,6 @@ sub pkg_htmlpackagelinks { ); } -sub pkg_htmladdresslinks { - htmlize_addresslinks(@_,'submitter'); -} - sub pkg_javascript { return < diff --git a/html/bugs.css b/html/bugs.css index ed42581..abe4f58 100644 --- a/html/bugs.css +++ b/html/bugs.css @@ -2,14 +2,14 @@ html { color: #000; background: #fefefe; font-family: serif; - margin: 1em; + margin: 0; border: 0; padding: 0; line-height: 120%; } body { - margin: 0; + margin: 10px; border: 0; padding: 0; } @@ -120,6 +120,42 @@ pre.mime { color: #686868; } +.buginfo p +{ + font-family: sans-serif; + font-size: 110%; + margin-bottom: 0px +} + +.buginfo p + p +{ + margin: 0; + margin-top: 0px; + border: 0; +} + + +.pkginfo p +{ font-family: sans-serif; + font-size: 110%; + margin-bottom: 0px +} + +.pkginfo p + p +{ + margin: 0; + margin-top: 0px; + padding: 0; + border: 0; +} + + +.versiongraph +{ + float: right + +} + pre.tags { color: #a3a3a3; font-size: 90%; diff --git a/t/07_bugreport.t b/t/07_bugreport.t index dedd445..18ccbc5 100644 --- a/t/07_bugreport.t +++ b/t/07_bugreport.t @@ -74,7 +74,7 @@ my $mech = Test::WWW::Mechanize->new(); $mech->get_ok('http://localhost:'.$port.'/?bug=1', 'Page received ok'); -ok($mech->content() =~ qr/\\#1\s+\-\s+Submitting a bug/i, +ok($mech->content() =~ qr/\\#1.+Submitting a bug/i, 'Title of bug is submitting a bug'); # Other tests for bugs in the page should be added here eventually diff --git a/templates/en_US/cgi/bugreport.tmpl b/templates/en_US/cgi/bugreport.tmpl new file mode 100644 index 0000000..4a7c638 --- /dev/null +++ b/templates/en_US/cgi/bugreport.tmpl @@ -0,0 +1,48 @@ +{include(q(html/pre_title))}#{$bug_num} - {html_escape($status{subject})} - {html_escape($config{project})} {html_escape($config{bug})} report logs{include(q(html/post_title.tmpl))} + + + +

    {html_escape($config{project})} {html_escape($config{bug})} report logs - +#{$bug_num}
    +{html_escape($status{subject})}

    +
    {$version_graph}
    +{include(q(cgi/bugreport_pkginfo))} +{include(q(cgi/bugreport_buginfo))} +{ my $output = ''; + if (looks_like_number($msg)) { + $output .= sprintf qq(

    Full log

    ),html_escape(bug_url($ref)); + } + else { + $output .= qq(

    Reply ). + qq(or subscribe ). + qq(to this bug.

    \n); + $output .= qq(

    Toggle useless messages

    ); + $output .= sprintf qq(

    View this report as an mbox folder, ). + qq(status mbox, maintainer mbox

    \n), + html_escape(bug_url($bug_num, mbox=>'yes')), + html_escape(bug_url($bug_num, mbox=>'yes',mboxstatus=>'yes')), + html_escape(bug_url($bug_num, mbox=>'yes',mboxmaint=>'yes')); + } + $output; +} +{$log} +
    +

    Send a report that this bug log contains spam.

    +
    +{include(q(html/html_tail))} + + diff --git a/templates/en_US/cgi/bugreport_buginfo.tmpl b/templates/en_US/cgi/bugreport_buginfo.tmpl new file mode 100644 index 0000000..6bd16e0 --- /dev/null +++ b/templates/en_US/cgi/bugreport_buginfo.tmpl @@ -0,0 +1,62 @@ +
    +

    Reported by: {package_links(submitter=>$status{originator})}

    +

    Date: {$status{date_text}}

    +{ my $output = ''; + if (defined $status{owner} and length $status{owner}) { + $output = q(

    Owned by: ).package_links(owner=>$status{owner}).q(

    ); + } + $output; +} +

    Severity: {my $output = $status{severity}; + if (isstrongseverity($status{severity})) { + $output = q().$status{severity}.q(); + } + $output; + }

    +

    {@{$status{tags_array}}?q(Tags: ).html_escape(join(q(, ),@{$status{tags_array}})):''}

    +{my $output = ''; + if (@{$status{mergedwith_array}}) { + $output .= q(

    Merged with ).join(qq(,\n),bug_links(bug=>$status{mergedwith_array})).qq(

    \n); + } + $output; +} +{my $output = ''; + if (@{$status{found_versions}}) { + $output .= q(

    Found in ); + $output .= (@{$status{found_versions}} == 1) ? 'version ' : 'versions '; + $output .= join(qq(, ),map {html_escape($_);} @{$status{found_versions}}).qq(

    \n); + } + if (@{$status{fixed_versions}}) { + $output .= q(

    Fixed in ); + $output .= (@{$status{fixed_versions}} == 1) ? 'version ' : 'versions '; + $output .= join(qq(, ),map {html_escape($_);} @{$status{fixed_versions}}).qq(

    \n); + } + $output; +} +{ my $output = ''; + if (length($status{done})) { + $output .= q(

    Done: ).html_escape($status{done}).q(

    ) + } + $output; +} +{ my $output = ''; + if (@{$status{blockedby_array}}) { + $output .= q(

    Fix blocked by ). + join(q(, ), + map {bug_links(bug=>$_->{bug_num}).q(: ).html_escape($_->{subject})} + @{$status{blockedby_array}}).q(

    ) + } + if (@{$status{blocks_array}}) { + $output .= q(

    Blocking fix for ). + join(q(, ), + map {bug_links(bug=>$_->{bug_num}).q(: ).html_escape($_->{subject})} + @{$status{blocks_array}}).q(

    ) + } + $output; +} +{ my $output = ''; + if (exists $status{archived} and $status{archived}) { + $output .= q(

    Bug is archived. No further changes may be made.

    ) + } + $output +}

    diff --git a/templates/en_US/cgi/bugreport_pkginfo.tmpl b/templates/en_US/cgi/bugreport_pkginfo.tmpl new file mode 100644 index 0000000..22806f7 --- /dev/null +++ b/templates/en_US/cgi/bugreport_pkginfo.tmpl @@ -0,0 +1,16 @@ +
    +

    {if (keys %package > 1) { q(Packages)} else {q(Package)}}: + {join(q(, ),package_links(package => [map {$_->{package}} values %package], + class => q(submitter), + ) + )}; +{my $output =''; + for my $package (values %package) { + $output .= q(Maintainer for ).package_links(package=>$package->{package}).qq( is ). + package_links(maintainer => $package->{maintainer}).qq(; ); + $output .= q(Source for ).package_links(package=>$package->{package}).qq( is ). + package_links(source => $package->{source}).qq(. ); + } + $output; +}

    +
    diff --git a/templates/en_US/html/html_tail b/templates/en_US/html/html_tail deleted file mode 100644 index aa640d2..0000000 --- a/templates/en_US/html/html_tail +++ /dev/null @@ -1,11 +0,0 @@ -
    {$config{maintainer}} <$config{maintainer_email}>. -Last modified: - -{$last_modified||strftime('%c',gmtime)} - -

    -{$config{project}} {$config{bug}} tracking system
    -Copyright (C) 1999 Darren O. Benham, -1997,2003 nCipher Corporation Ltd, -1994-97 Ian Jackson. -

    diff --git a/templates/en_US/html/html_tail.tmpl b/templates/en_US/html/html_tail.tmpl new file mode 100644 index 0000000..d7eb979 --- /dev/null +++ b/templates/en_US/html/html_tail.tmpl @@ -0,0 +1,11 @@ +
    {$config{maintainer}} <{$config{maintainer_email}}>. +Last modified: + +{$last_modified||strftime('%c',gmtime)} + +

    +{$config{project}} {$config{bug}} tracking system
    +Copyright (C) 1999 Darren O. Benham, +1997,2003 nCipher Corporation Ltd, +1994-97 Ian Jackson. +

    diff --git a/templates/en_US/html/post_title.tmpl b/templates/en_US/html/post_title.tmpl new file mode 100644 index 0000000..169d161 --- /dev/null +++ b/templates/en_US/html/post_title.tmpl @@ -0,0 +1,3 @@ + + + diff --git a/templates/en_US/html/pre_title.tmpl b/templates/en_US/html/pre_title.tmpl new file mode 100644 index 0000000..2f7ab77 --- /dev/null +++ b/templates/en_US/html/pre_title.tmpl @@ -0,0 +1,3 @@ + + + \ No newline at end of file