From 6f2cf73da85b74a1bd6a178e4dc5e2477e8c77bc Mon Sep 17 00:00:00 2001 From: Don Armstrong Date: Thu, 7 Aug 2008 12:37:39 -0700 Subject: [PATCH] * Completely reformat pkgreport.cgi output * Add tmeplates for new pkgreport.cgi output * Add form processing bits to automatically handle new pkgreport.cgi options * Fix cgi form on pkgreport.cgi * Update css for new changes --- Debbugs/CGI.pm | 362 ++++++- Debbugs/CGI/Pkgreport.pm | 791 ++++++++++++++ cgi/pkgreport.cgi | 993 ++++-------------- html/bugs.css | 30 + templates/en_US/cgi/pkgreport_javascript.tmpl | 128 +++ templates/en_US/cgi/pkgreport_options.tmpl | 83 ++ .../pkgreport_options_include_exclude.tmpl | 16 + ...pkgreport_options_include_exclude_key.tmpl | 14 + .../cgi/pkgreport_options_search_key.tmpl | 6 + templates/en_US/cgi/short_bug_status.tmpl | 185 ++++ 10 files changed, 1790 insertions(+), 818 deletions(-) create mode 100644 Debbugs/CGI/Pkgreport.pm create mode 100644 templates/en_US/cgi/pkgreport_javascript.tmpl create mode 100644 templates/en_US/cgi/pkgreport_options.tmpl create mode 100644 templates/en_US/cgi/pkgreport_options_include_exclude.tmpl create mode 100644 templates/en_US/cgi/pkgreport_options_include_exclude_key.tmpl create mode 100644 templates/en_US/cgi/pkgreport_options_search_key.tmpl create mode 100644 templates/en_US/cgi/short_bug_status.tmpl diff --git a/Debbugs/CGI.pm b/Debbugs/CGI.pm index e18891ab..1ba47996 100644 --- a/Debbugs/CGI.pm +++ b/Debbugs/CGI.pm @@ -45,6 +45,8 @@ use Mail::Address; use POSIX qw(ceil); use Storable qw(dclone); +use List::Util qw(max); + use Carp; use Debbugs::Text qw(fill_in_template); @@ -67,11 +69,13 @@ BEGIN{ ], util => [qw(cgi_parameters quitcgi), ], + forms => [qw(option_form form_options_and_normal_param)], misc => [qw(maint_decode)], + package_search => [qw(@package_search_key_order %package_search_keys)], #status => [qw(getbugstatus)], ); @EXPORT_OK = (); - Exporter::export_ok_tags(qw(url html util misc)); + Exporter::export_ok_tags(keys %EXPORT_TAGS); $EXPORT_TAGS{all} = [@EXPORT_OK]; } @@ -117,6 +121,8 @@ sub bug_url{ else { %params = @_; } + carp "bug_url is deprecated, use bug_links instead"; + return munge_url('bugreport.cgi?',%params,bug=>$ref); } @@ -129,6 +135,7 @@ sub pkg_url{ else { %params = @_; } + carp "pkg_url is deprecated, use package_links instead"; return munge_url('pkgreport.cgi?',%params); } @@ -293,7 +300,7 @@ sub quitcgi { Given a scalar containing a list of packages separated by something that L can separate, returns a -formatted set of links to packages. +formatted set of links to packages in html. =cut @@ -302,14 +309,12 @@ sub htmlize_packagelinks { return '' unless defined $pkgs and $pkgs ne ''; my @pkglist = splitpackages($pkgs); - carp "htmlize_packagelinks is deprecated"; + carp "htmlize_packagelinks is deprecated, use package_links instead"; return 'Package' . (@pkglist > 1 ? 's' : '') . ': ' . - join(', ', - package_links(package =>\@pkglist, - class => 'submitter' - ) - ); + package_links(package =>\@pkglist, + class => 'submitter' + ); } =head2 package_links @@ -324,7 +329,7 @@ Given a list of packages, return a list of html which links to the package =item submitter -- arrayref or scalar of submitter(s) -=item source -- arrayref or scalar of source(s) +=item src -- arrayref or scalar of source(s) =item maintainer -- arrayref or scalar of maintainer(s) @@ -337,23 +342,27 @@ returning htmlized links. =cut +our @package_search_key_order = (package => 'in package', + tag => 'tagged', + severity => 'with severity', + src => 'in source package', + maint => 'in packages maintained by', + submitter => 'submitted by', + owner => 'owned by', + status => 'with status', + correspondent => 'with mail from', + newest => 'newest bugs', + ); +our %package_search_keys = @package_search_key_order; + + sub package_links { my %param = validate_with(params => \@_, - spec => {package => {type => SCALAR|ARRAYREF, - optional => 1, - }, - source => {type => SCALAR|ARRAYREF, + spec => {(map { ($_,{type => SCALAR|ARRAYREF, optional => 1, - }, - maintainer => {type => SCALAR|ARRAYREF, - optional => 1, - }, - submitter => {type => SCALAR|ARRAYREF, - optional => 1, - }, - owner => {type => SCALAR|ARRAYREF, - optional => 1, - }, + }); + } keys %package_search_keys, + ), links_only => {type => BOOLEAN, default => 0, }, @@ -363,26 +372,44 @@ sub package_links { separator => {type => SCALAR, default => ', ', }, + options => {type => HASHREF, + default => {}, + }, }, + normalize_keys => + sub { + my ($key) = @_; + my %map = (source => 'src', + maintainer => 'maint', + pkg => 'package', + ); + return $map{$key} if exists $map{$key}; + return $key; + } ); + my %options = %{$param{options}}; + for ((keys %package_search_keys,qw(msg att))) { + delete $options{$_} if exists $options{$_}; + } 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}; + for my $type (qw(src package)) { + push @links, map {(munge_url('pkgreport.cgi?', + %options, + $type => $_, + ), + $_); + } make_list($param{$type}) if exists $param{$type}; + } + for my $type (qw(maint owner submitter correspondent)) { + push @links, map {my $addr = getparsedaddrs($_); + $addr = defined $addr?$addr->address:''; + (munge_url('pkgreport.cgi?', + %options, + $type => $addr, + ), + $_); + } make_list($param{$type}) if exists $param{$type}; + } my @return = (); my ($link,$link_name); my $class = ''; @@ -438,10 +465,25 @@ sub bug_links { class => {type => SCALAR, default => '', }, + separator => {type => SCALAR, + default => ', ', + }, + options => {type => HASHREF, + default => {}, + }, }, ); + my %options = %{$param{options}}; + + for (qw(bug)) { + delete $options{$_} if exists $options{$_}; + } my @links; - push @links, map {(bug_url($_),$_) + push @links, map {(munge_url('bugreport.cgi?', + %options, + bug => $_, + ), + $_); } make_list($param{bug}) if exists $param{bug}; my @return; my ($link,$link_name); @@ -460,7 +502,12 @@ sub bug_links { html_escape($link_name).q(); } } - return @return; + if (wantarray) { + return @return; + } + else { + return join($param{separator},@return); + } } @@ -547,8 +594,8 @@ sub emailfromrfc822{ return $addr; } -sub mainturl { pkg_url(maint => emailfromrfc822($_[0])); } -sub submitterurl { pkg_url(submitter => emailfromrfc822($_[0])); } +sub mainturl { package_links(maint => $_[0], links_only => 1); } +sub submitterurl { package_links(submitter => $_[0], links_only => 1); } sub htmlize_maintlinks { my ($prefixfunc, $maints) = @_; carp "htmlize_maintlinks is deprecated"; @@ -576,9 +623,234 @@ too.] sub bug_linklist{ my ($sep,$class,@bugs) = @_; - return join($sep,bug_links(bug=>\@bugs,class=>$class)); + carp "bug_linklist is deprecated; use bug_links instead"; + return scalar bug_links(bug=>\@bugs,class=>$class,separator=>$sep); +} + + + +=head1 Forms + +=cut + +=head2 form_options_and_normal_param + + my ($form_option,$param) = form_options_and_normal_param(\%param) + if $param{form_options}; + my $form_option = form_options_and_normal_param(\%param) + if $param{form_options}; + +Translates from special form_options to a set of parameters which can +be used to run the current page. + +The idea behind this is to allow complex forms to relatively easily +cause options that the existing cgi scripts understand to be set. + +Currently there are two commands which are understood: +combine, and concatenate. + +=head3 combine + +Combine works by entering key,value pairs into the parameters using +the key field option input field, and the value field option input +field. + +For example, you would have + + + +which would combine the _fo_searchkey and _fo_searchvalue input fields, so + + + + +would yield foo=>'bar' in %param. + +=head3 concatenate + +Concatenate concatenates values into a single entry in a parameter + +For example, you would have + + + +which would combine the _fo_searchkey and _fo_searchvalue input fields, so + + + + +would yield foo=>'bar:baz' in %param. + + +=cut + +my $form_option_leader = '_fo_'; +sub form_options_and_normal_param{ + my ($orig_param) = @_; + # all form_option parameters start with _fo_ + my ($param,$form_option) = ({},{}); + for my $key (keys %{$orig_param}) { + if ($key =~ /^\Q$form_option_leader\E/) { + $form_option->{$key} = $orig_param->{$key}; + } + else { + $param->{$key} = $orig_param->{$key}; + } + } + # at this point, we check for commands + COMMAND: for my $key (keys %{$form_option}) { + $key =~ s/^\Q$form_option_leader\E//; + if (my ($key_name,$value_name) = + $key =~ /combine_key(\Q$form_option_leader\E.+) + _value(\Q$form_option_leader\E.+)$/x + ) { + next unless defined $form_option->{$key_name}; + next unless defined $form_option->{$value_name}; + my @keys = make_list($form_option->{$key_name}); + my @values = make_list($form_option->{$value_name}); + for my $i (0 .. $#keys) { + last if $i > $#values; + next if not defined $keys[$i]; + next if not defined $values[$i]; + __add_to_param($param, + $keys[$i], + $values[$i], + ); + } + } + elsif (my ($field,$concatenate_key,$fields) = + $key =~ /concatenate_into_(.+?)((?:_with_[^_])?) + ((?:\Q$form_option_leader\E.+?)+) + $/x + ) { + if (length $concatenate_key) { + $concatenate_key =~ s/_with_//; + } + else { + $concatenate_key = ':'; + } + my @fields = $fields =~ m/(\Q$form_option_leader\E.+?)(?:(?=\Q$form_option_leader\E)|$)/g; + my %field_list; + my $max_num = 0; + for my $f (@fields) { + next COMMAND unless defined $form_option->{$f}; + $field_list{$f} = [make_list($form_option->{$f})]; + $max_num = max($max_num,$#{$field_list{$f}}); + } + for my $i (0 .. $max_num) { + next unless @fields == grep {$i <= $#{$field_list{$_}} and + defined $field_list{$_}[$i]} @fields; + __add_to_param($param, + $field, + join($concatenate_key, + map {$field_list{$_}[$i]} @fields + ) + ); + } + } + } + return wantarray?($form_option,$param):$form_option; } +=head2 option_form + + print option_form(template=>'pkgreport_options', + param => \%param, + form_options => $form_options, + ) + + + +=cut + +sub option_form{ + my %param = validate_with(params => \@_, + spec => {template => {type => SCALAR, + }, + variables => {type => HASHREF, + default => {}, + }, + language => {type => SCALAR, + optional => 1, + }, + param => {type => HASHREF, + default => {}, + }, + form_options => {type => HASHREF, + default => {}, + }, + }, + ); + + # First, we need to see if we need to add particular types of + # parameters + my $variables = dclone($param{variables}); + $variables->{param} = dclone($param{param}); + for my $key (keys %{$param{form_option}}) { + # strip out leader; shouldn't be anything here without one, + # but skip stupid things anyway + my $o_key = $key; + next unless $key =~ s/^\Q$form_option_leader\E//; + if ($key =~ /^add_(.+)$/) { + # this causes a specific parameter to be added + __add_to_param($variables->{param}, + $1, + '' + ); + } + elsif ($key =~ /^delete_(.+?)(?:_(\d+))?$/) { + next unless exists $variables->{param}{$1}; + if (ref $variables->{param}{$1} eq 'ARRAY' and + defined $2 and + defined $variables->{param}{$1}[$2] + ) { + splice @{$variables->{param}{$1}},$2,1; + } + else { + delete $variables->{param}{$1}; + } + } + # we'll add extra comands here once I figure out what they + # should be + } + # add in a few utility routines + $variables->{output_select_options} = sub { + my ($options,$value) = @_; + my @options = @{$options}; + my $output = ''; + while (my ($o_value,$name) = splice @options,0,2) { + my $selected = ''; + if (defined $value and $o_value eq $value) { + $selected = ' selected'; + } + $output .= qq(\n); + } + return $output; + }; + $variables->{make_list} = sub { make_list(@_); + }; + # now at this point, we're ready to create the template + return Debbugs::Text::fill_in_template(template=>$param{template}, + (exists $param{language}?(language=>$param{language}):()), + variables => $variables, + ); +} + +sub __add_to_param{ + my ($param,$key,@values) = @_; + + if (exists $param->{$key} and not + ref $param->{$key}) { + @{$param->{$key}} = [$param->{$key}, + @values + ]; + } + else { + push @{$param->{$key}}, @values; + } +} + + =head1 misc diff --git a/Debbugs/CGI/Pkgreport.pm b/Debbugs/CGI/Pkgreport.pm new file mode 100644 index 00000000..06946a63 --- /dev/null +++ b/Debbugs/CGI/Pkgreport.pm @@ -0,0 +1,791 @@ +# This module is part of debbugs, and is released +# under the terms of the GPL version 2, or any later version. See the +# file README and COPYING for more information. +# +# [Other people have contributed to this file; their copyrights should +# be listed here too.] +# Copyright 2008 by Don Armstrong . + + +package Debbugs::CGI::Pkgreport; + +=head1 NAME + +Debbugs::CGI::Pkgreport -- specific routines for the pkgreport cgi script + +=head1 SYNOPSIS + + +=head1 DESCRIPTION + + +=head1 BUGS + +None known. + +=cut + +use warnings; +use strict; +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::Config qw(:config :globals); +use Debbugs::CGI qw(:url :html :util); +use Debbugs::Common qw(:misc :util :date); +use Debbugs::Status qw(:status); +use Debbugs::Bugs qw(bug_filter); +use Debbugs::Packages qw(:mapping); + +use Debbugs::Text qw(:templates); + +use POSIX qw(strftime); + + +BEGIN{ + ($VERSION) = q$Revision: 494 $ =~ /^Revision:\s+([^\s+])/; + $DEBUG = 0 unless defined $DEBUG; + + @EXPORT = (); + %EXPORT_TAGS = (html => [qw(short_bug_status_html pkg_htmlizebugs), + qw(pkg_javascript), + qw(pkg_htmlselectyesno pkg_htmlselectsuite), + qw(buglinklist pkg_htmlselectarch) + ], + misc => [qw(generate_package_info make_order_list), + qw(myurl), + qw(get_bug_order_index determine_ordering), + ], + ); + @EXPORT_OK = (qw()); + Exporter::export_ok_tags(keys %EXPORT_TAGS); + $EXPORT_TAGS{all} = [@EXPORT_OK]; +} + +=head2 generate_package_info + + generate_package_info($srcorbin,$package) + +Generates the informational bits for a package and returns it + +=cut + +sub generate_package_info{ + my %param = validate_with(params => \@_, + spec => {binary => {type => BOOLEAN, + default => 1, + }, + package => {type => SCALAR|ARRAYREF, + }, + options => {type => HASHREF, + }, + bugs => {type => ARRAYREF, + }, + }, + ); + + my $output_scalar = ''; + my $output = globify_scalar(\$output_scalar); + + my $package = $param{package}; + + my %pkgsrc = %{getpkgsrc()}; + my $srcforpkg = $package; + if ($param{binary} and exists $pkgsrc{$package} + and defined $pkgsrc{$package}) { + $srcforpkg = $pkgsrc{$package}; + } + + my $showpkg = html_escape($package); + my $maintainers = getmaintainers(); + my $maint = $maintainers->{$srcforpkg}; + if (defined $maint) { + print {$output} '

'; + print {$output} htmlize_maintlinks(sub { $_[0] == 1 ? "Maintainer for $showpkg is " + : "Maintainers for $showpkg are " + }, + $maint); + print {$output} ".

\n"; + } + else { + print {$output} "

No maintainer for $showpkg. Please do not report new bugs against this package.

\n"; + } + my @pkgs = getsrcpkgs($srcforpkg); + @pkgs = grep( !/^\Q$package\E$/, @pkgs ); + if ( @pkgs ) { + @pkgs = sort @pkgs; + if ($param{binary}) { + print {$output} "

You may want to refer to the following packages that are part of the same source:\n"; + } + else { + print {$output} "

You may want to refer to the following individual bug pages:\n"; + } + #push @pkgs, $src if ( $src && !grep(/^\Q$src\E$/, @pkgs) ); + print {$output} scalar package_links(package=>[@pkgs]); + print {$output} ".\n"; + } + my @references; + my $pseudodesc = getpseudodesc(); + if ($package and defined($pseudodesc) and exists($pseudodesc->{$package})) { + push @references, "to the ". + "list of other pseudo-packages"; + } + else { + if ($package and defined $gPackagePages) { + push @references, sprintf "to the %s package page", + html_escape("http://${gPackagePages}/$package"), html_escape("$package"); + } + if (defined $gSubscriptionDomain) { + my $ptslink = $param{binary} ? $srcforpkg : $package; + push @references, q(to the Package Tracking System); + } + # Only output this if the source listing is non-trivial. + if ($param{binary} and $srcforpkg) { + push @references, + "to the source package ". + package_links(src=>$srcforpkg, + options => $param{options}) . + "'s bug page"; + } + } + if (@references) { + $references[$#references] = "or $references[$#references]" if @references > 1; + print {$output} "

You might like to refer ", join(", ", @references), ".

\n"; + } + if (defined $param{maint} || defined $param{maintenc}) { + print {$output} "

If you find a bug not listed here, please\n"; + printf {$output} "report it.

\n", + html_escape("http://${debbugs::gWebDomain}/Reporting${debbugs::gHTMLSuffix}"); + } + if (not $maint and not @{$param{bugs}}) { + print {$output} "

There is no record of the " . html_escape($package) . + ($param{binary} ? " package" : " source package") . + ", and no bugs have been filed against it.

"; + } + return $output_scalar; +} + + +=head2 short_bug_status_html + + print short_bug_status_html(status => read_bug(bug => 5), + options => \%param, + ); + +=over + +=item status -- status hashref as returned by read_bug + +=item options -- hashref of options to pass to package_links (defaults +to an empty hashref) + +=item bug_options -- hashref of options to pass to bug_links (default +to an empty hashref) + +=item snippet -- optional snippet of information about the bug to +display below + + +=back + + + +=cut + +sub short_bug_status_html { + my %param = validate_with(params => \@_, + spec => {status => {type => HASHREF, + }, + options => {type => HASHREF, + default => {}, + }, + bug_options => {type => HASHREF, + default => {}, + }, + snippet => {type => SCALAR, + default => '', + }, + }, + ); + + my %status = %{$param{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 @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 @{$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 @{$status{blocks_array}}, {bug_num => $b, subject => $s{subject}, status => \%s}; + } + } + + + return fill_in_template(template => 'cgi/short_bug_status', + variables => {status => \%status, + 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, + '&secs_to_english' => \&Debbugs::Common::secs_to_english, + '&strftime' => \&POSIX::strftime, + }, + ); + + my $result = ""; + + my $showseverity; + if ($status{severity} eq 'normal') { + $showseverity = ''; + } + elsif (isstrongseverity($status{severity})) { + $showseverity = "Severity: $status{severity};\n"; + } + else { + $showseverity = "Severity: $status{severity};\n"; + } + + $result .= package_links(package => $status{package}, + options => $param{options}, + ); + + my $showversions = ''; + if (@{$status{found_versions}}) { + my @found = @{$status{found_versions}}; + $showversions .= join ', ', map {s{/}{ }; 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; + } + $result .= ' ($showversions)} if length $showversions; + $result .= ";\n"; + + $result .= $showseverity; + $result .= "Reported by: ".package_links(submitter=>$status{originator}, + class => "submitter", + ); + $result .= ";\nOwned by: " . package_links(owner => $status{owner}, + class => "submitter", + ) + if length $status{owner}; + $result .= ";\nTags: " + . html_escape(join(", ", sort(split(/\s+/, $status{tags})))) + . "" + if (length($status{tags})); + + $result .= (length($status{mergedwith})?";\nMerged with ":"") . + bug_links(bug => [split(/ /,$status{mergedwith})], + class => "submitter", + ); + $result .= (length($status{blockedby})?";\nBlocked by ":"") . + bug_links(bug => [split(/ /,$status{blockedby})], + class => "submitter", + ); + $result .= (length($status{blocks})?";\nBlocks ":"") . + bug_links(bug => [split(/ /,$status{blocks})], + class => "submitter", + ); + + if (length($status{done})) { + $result .= "
Done: " . html_escape($status{done}); + my $days = bug_archiveable(bug => $status{id}, + status => \%status, + days_until => 1, + ); + if ($days >= 0 and defined $status{location} and $status{location} ne 'archive') { + $result .= ";\nCan be archived" . ( $days == 0 ? " today" : $days == 1 ? " in $days day" : " in $days days" ) . ""; + } + elsif (defined $status{location} and $status{location} eq 'archived') { + $result .= ";\nArchived."; + } + } + + unless (length($status{done})) { + if (length($status{forwarded})) { + $result .= ";\nForwarded to " + . join(', ', + map {maybelink($_)} + split /\,\s+/,$status{forwarded} + ); + } + # Check the age of the logfile + my ($days_last,$eng_last) = secs_to_english(time - $status{log_modified}); + my ($days,$eng) = secs_to_english(time - $status{date}); + + if ($days >= 7) { + my $font = ""; + my $efont = ""; + $font = "em" if ($days > 30); + $font = "strong" if ($days > 60); + $efont = "" if ($font); + $font = "<$font>" if ($font); + + $result .= ";\n ${font}$eng old$efont"; + } + if ($days_last > 7) { + my $font = ""; + my $efont = ""; + $font = "em" if ($days_last > 30); + $font = "strong" if ($days_last > 60); + $efont = "" if ($font); + $font = "<$font>" if ($font); + + $result .= ";\n ${font}Modified $eng_last ago$efont"; + } + } + + $result .= "."; + + return $result; +} + + +sub pkg_htmlizebugs { + my %param = validate_with(params => \@_, + spec => {bugs => {type => ARRAYREF, + }, + names => {type => ARRAYREF, + }, + title => {type => ARRAYREF, + }, + prior => {type => ARRAYREF, + }, + order => {type => ARRAYREF, + }, + ordering => {type => SCALAR, + }, + bugusertags => {type => HASHREF, + default => {}, + }, + bug_rev => {type => BOOLEAN, + default => 0, + }, + bug_order => {type => SCALAR, + }, + repeatmerged => {type => BOOLEAN, + default => 1, + }, + include => {type => ARRAYREF, + default => [], + }, + exclude => {type => ARRAYREF, + default => [], + }, + this => {type => SCALAR, + default => '', + }, + options => {type => HASHREF, + default => {}, + }, + } + ); + my @bugs = @{$param{bugs}}; + + my @status = (); + my %count; + my $header = ''; + my $footer = "

Summary

\n"; + + my @dummy = ($gRemoveAge); #, @gSeverityList, @gSeverityDisplay); #, $gHTMLExpireNote); + + if (@bugs == 0) { + return "

No reports found!

\n"; + } + + if ( $param{bug_rev} ) { + @bugs = sort {$b<=>$a} @bugs; + } + else { + @bugs = sort {$a<=>$b} @bugs; + } + my %seenmerged; + + my %common = ( + 'show_list_header' => 1, + 'show_list_footer' => 1, + ); + + my %section = (); + # Make the include/exclude map + my %include; + my %exclude; + for my $include (make_list($param{include})) { + next unless defined $include; + my ($key,$value) = split /\s*:\s*/,$include,2; + unless (defined $value) { + $key = 'tags'; + $value = $include; + } + push @{$include{$key}}, split /\s*,\s*/, $value; + } + for my $exclude (make_list($param{exclude})) { + next unless defined $exclude; + my ($key,$value) = split /\s*:\s*/,$exclude,2; + unless (defined $value) { + $key = 'tags'; + $value = $exclude; + } + push @{$exclude{$key}}, split /\s*,\s*/, $value; + } + + foreach my $bug (@bugs) { + my %status = %{get_bug_status(bug=>$bug, + (exists $param{dist}?(dist => $param{dist}):()), + bugusertags => $param{bugusertags}, + (exists $param{version}?(version => $param{version}):()), + (exists $param{arch}?(arch => $param{arch}):(arch => $config{default_architectures})), + )}; + next unless %status; + next if bug_filter(bug => $bug, + status => \%status, + repeat_merged => $param{repeatmerged}, + seen_merged => \%seenmerged, + (keys %include ? (include => \%include):()), + (keys %exclude ? (exclude => \%exclude):()), + ); + + my $html = "
  • "; ##%d: %s\n
    ", + #bug_url($bug), $bug, html_escape($status{subject}); + $html .= short_bug_status_html(status => \%status, + options => $param{options}, + ) . "\n"; + push @status, [ $bug, \%status, $html ]; + } + if ($param{bug_order} eq 'age') { + # MWHAHAHAHA + @status = sort {$a->[1]{log_modified} <=> $b->[1]{log_modified}} @status; + } + elsif ($param{bug_order} eq 'agerev') { + @status = sort {$b->[1]{log_modified} <=> $a->[1]{log_modified}} @status; + } + for my $entry (@status) { + my $key = ""; + for my $i (0..$#{$param{prior}}) { + my $v = get_bug_order_index($param{prior}[$i], $entry->[1]); + $count{"g_${i}_${v}"}++; + $key .= "_$v"; + } + $section{$key} .= $entry->[2]; + $count{"_$key"}++; + } + + my $result = ""; + if ($param{ordering} eq "raw") { + $result .= "
      \n" . join("", map( { $_->[ 2 ] } @status ) ) . "
    \n"; + } + else { + $header .= "
    \n
      \n"; + my @keys_in_order = (""); + for my $o (@{$param{order}}) { + push @keys_in_order, "X"; + while ((my $k = shift @keys_in_order) ne "X") { + for my $k2 (@{$o}) { + $k2+=0; + push @keys_in_order, "${k}_${k2}"; + } + } + } + for my $order (@keys_in_order) { + next unless defined $section{$order}; + my @ttl = split /_/, $order; + shift @ttl; + my $title = $param{title}[0]->[$ttl[0]] . " bugs"; + if ($#ttl > 0) { + $title .= " -- "; + $title .= join("; ", grep {($_ || "") ne ""} + map { $param{title}[$_]->[$ttl[$_]] } 1..$#ttl); + } + $title = html_escape($title); + + my $count = $count{"_$order"}; + my $bugs = $count == 1 ? "bug" : "bugs"; + + $header .= "
    • $title ($count $bugs)
    • \n"; + if ($common{show_list_header}) { + my $count = $count{"_$order"}; + my $bugs = $count == 1 ? "bug" : "bugs"; + $result .= "

      $title ($count $bugs)

      \n"; + } + else { + $result .= "

      $title

      \n"; + } + $result .= "
      \n
        \n"; + $result .= "\n\n\n\n"; + $result .= $section{$order}; + $result .= "\n\n\n\n"; + $result .= "
      \n
      \n"; + } + $header .= "
    \n"; + + $footer .= "
    \n
      \n"; + for my $i (0..$#{$param{prior}}) { + my $local_result = ''; + foreach my $key ( @{$param{order}[$i]} ) { + my $count = $count{"g_${i}_$key"}; + next if !$count or !$param{title}[$i]->[$key]; + $local_result .= "
    • $count $param{title}[$i]->[$key]
    • \n"; + } + if ( $local_result ) { + $footer .= "
    • $param{names}[$i]
        \n$local_result
    • \n"; + } + } + $footer .= "
    \n
    \n"; + } + + $result = $header . $result if ( $common{show_list_header} ); + $result .= $footer if ( $common{show_list_footer} ); + return $result; +} + +sub pkg_javascript { + return fill_in_template(template=>'cgi/pkgreport_javascript', + ); +} + +sub pkg_htmlselectyesno { + my ($name, $n, $y, $default) = @_; + return sprintf('', $name, ($default ? "" : " selected"), $n, ($default ? " selected" : ""), $y); +} + +sub pkg_htmlselectsuite { + my $id = sprintf "b_%d_%d_%d", $_[0], $_[1], $_[2]; + my @suites = ("stable", "testing", "unstable", "experimental"); + my %suiteaka = ("stable", "etch", "testing", "lenny", "unstable", "sid"); + my $defaultsuite = "unstable"; + + my $result = sprintf ''; + return $result; +} + +sub pkg_htmlselectarch { + my $id = sprintf "b_%d_%d_%d", $_[0], $_[1], $_[2]; + my @arches = qw(alpha amd64 arm hppa i386 ia64 m68k mips mipsel powerpc s390 sparc); + + my $result = sprintf ''; + return $result; +} + +sub myurl { + my %param = @_; + return html_escape(pkg_url(map {exists $param{$_}?($_,$param{$_}):()} + qw(archive repeatmerged mindays maxdays), + qw(version dist arch package src tag maint submitter) + ) + ); +} + +sub make_order_list { + my $vfull = shift; + my @x = (); + + if ($vfull =~ m/^([^:]+):(.*)$/) { + my $v = $1; + for my $vv (split /,/, $2) { + push @x, "$v=$vv"; + } + } + else { + for my $v (split /,/, $vfull) { + next unless $v =~ m/.=./; + push @x, $v; + } + } + push @x, ""; # catch all + return @x; +} + +sub get_bug_order_index { + my $order = shift; + my $status = shift; + my $pos = -1; + + my %tags = (); + %tags = map { $_, 1 } split / /, $status->{"tags"} + if defined $status->{"tags"}; + + for my $el (@${order}) { + $pos++; + my $match = 1; + for my $item (split /[+]/, $el) { + my ($f, $v) = split /=/, $item, 2; + next unless (defined $f and defined $v); + my $isokay = 0; + $isokay = 1 if (defined $status->{$f} and $v eq $status->{$f}); + $isokay = 1 if ($f eq "tag" && defined $tags{$v}); + unless ($isokay) { + $match = 0; + last; + } + } + if ($match) { + return $pos; + last; + } + } + return $pos + 1; +} + +sub buglinklist { + my ($prefix, $infix, @els) = @_; + return '' if not @els; + return $prefix . bug_linklist($infix,'submitter',@els); +} + + +# sets: my @names; my @prior; my @title; my @order; + +sub determine_ordering { + my %param = validate_with(params => \@_, + spec => {cats => {type => HASHREF, + }, + param => {type => HASHREF, + }, + ordering => {type => SCALARREF, + }, + names => {type => ARRAYREF, + }, + pend_rev => {type => BOOLEAN, + default => 0, + }, + sev_rev => {type => BOOLEAN, + default => 0, + }, + prior => {type => ARRAYREF, + }, + title => {type => ARRAYREF, + }, + order => {type => ARRAYREF, + }, + }, + ); + $param{cats}{status}[0]{ord} = [ reverse @{$param{cats}{status}[0]{ord}} ] + if ($param{pend_rev}); + $param{cats}{severity}[0]{ord} = [ reverse @{$param{cats}{severity}[0]{ord}} ] + if ($param{sev_rev}); + + my $i; + if (defined $param{param}{"pri0"}) { + my @c = (); + $i = 0; + while (defined $param{param}{"pri$i"}) { + my $h = {}; + + my ($pri) = make_list($param{param}{"pri$i"}); + if ($pri =~ m/^([^:]*):(.*)$/) { + $h->{"nam"} = $1; # overridden later if necesary + $h->{"pri"} = [ map { "$1=$_" } (split /,/, $2) ]; + } + else { + $h->{"pri"} = [ split /,/, $pri ]; + } + + ($h->{"nam"}) = make_list($param{param}{"nam$i"}) + if (defined $param{param}{"nam$i"}); + $h->{"ord"} = [ map {split /\s*,\s*/} make_list($param{param}{"ord$i"}) ] + if (defined $param{param}{"ord$i"}); + $h->{"ttl"} = [ map {split /\s*,\s*/} make_list($param{param}{"ttl$i"}) ] + if (defined $param{param}{"ttl$i"}); + + push @c, $h; + $i++; + } + $param{cats}{"_"} = [@c]; + ${$param{ordering}} = "_"; + } + + ${$param{ordering}} = "normal" unless defined $param{cats}{${$param{ordering}}}; + + sub get_ordering { + my @res; + my $cats = shift; + my $o = shift; + for my $c (@{$cats->{$o}}) { + if (ref($c) eq "HASH") { + push @res, $c; + } + else { + push @res, get_ordering($cats, $c); + } + } + return @res; + } + my @cats = get_ordering($param{cats}, ${$param{ordering}}); + + sub toenglish { + my $expr = shift; + $expr =~ s/[+]/ and /g; + $expr =~ s/[a-z]+=//g; + return $expr; + } + + $i = 0; + for my $c (@cats) { + $i++; + push @{$param{prior}}, $c->{"pri"}; + push @{$param{names}}, ($c->{"nam"} || "Bug attribute #" . $i); + if (defined $c->{"ord"}) { + push @{$param{order}}, $c->{"ord"}; + } + else { + push @{$param{order}}, [ 0..$#{$param{prior}[-1]} ]; + } + my @t = @{ $c->{"ttl"} } if defined $c->{ttl}; + if (@t < $#{$param{prior}[-1]}) { + push @t, map { toenglish($param{prior}[-1][$_]) } @t..($#{$param{prior}[-1]}); + } + push @t, $c->{"def"} || ""; + push @{$param{title}}, [@t]; + } +} + + + + +1; + + +__END__ + + + + + + diff --git a/cgi/pkgreport.cgi b/cgi/pkgreport.cgi index 3c6afe36..37509497 100755 --- a/cgi/pkgreport.cgi +++ b/cgi/pkgreport.cgi @@ -10,21 +10,24 @@ # Copyright 2007 by Don Armstrong . -package debbugs; - use warnings; use strict; + use POSIX qw(strftime nice); use Debbugs::Config qw(:globals :text :config); + use Debbugs::User; -use Debbugs::CGI qw(version_url maint_decode); -use Debbugs::Common qw(getparsedaddrs :date make_list getmaintainers getpseudodesc); + +use Debbugs::Common qw(getparsedaddrs make_list getmaintainers getpseudodesc); + use Debbugs::Bugs qw(get_bugs bug_filter newest_bug); use Debbugs::Packages qw(getsrcpkgs getpkgsrc get_versions); -use Debbugs::Status qw(:status); + use Debbugs::CGI qw(:all); +use Debbugs::CGI::Pkgreport qw(:all); + use Debbugs::Text qw(:templates); use CGI::Simple; @@ -37,20 +40,47 @@ if ($q->request_method() eq 'HEAD') { exit 0; } +my $default_params = {ordering => 'normal', + archive => 0, + repeatmerged => 0, + include => [], + exclude => [], + }; + our %param = cgi_parameters(query => $q, single => [qw(ordering archive repeatmerged), qw(bug-rev pend-rev sev-rev), qw(maxdays mindays version), qw(data which dist newest), ], - default => {ordering => 'normal', - archive => 0, - repeatmerged => 1, - include => [], - exclude => [], - }, + default => $default_params, ); +my ($form_options,$param) = ({},undef); +($form_options,$param)= form_options_and_normal_param(\%param) + if $param{form_options}; + +%param = %{$param} if defined $param; + +if (exists $param{form_options} and defined $param{form_options}) { + delete $param{form_options}; + delete $param{submit} if exists $param{submit}; + for my $default (keys %{$default_params}) { + if (exists $param{$default} and + not ref($default_params->{$default}) and + $default_params->{$default} eq $param{$default} + ) { + delete $param{$default}; + } + } + for my $incexc (qw(include exclude)) { + next unless exists $param{$incexc}; + $param{$incexc} = [grep /\S\:\S/, make_list($param{$incexc})]; + } + print $q->redirect(munge_url('pkgreport.cgi?',%param)); + exit 0; +} + # map from yes|no to 1|0 for my $key (qw(repeatmerged bug-rev pend-rev sev-rev)) { if (exists $param{$key}){ @@ -86,6 +116,8 @@ unless (defined $ordering) { $ordering = "raw" if $raw_sort; $ordering = 'age' if $age_sort; } +$param{ordering} = $ordering; + our ($bug_order) = $ordering =~ /(age(?:rev)?)/; $bug_order = '' if not defined $bug_order; @@ -125,6 +157,7 @@ for my $incexcmap (@inc_exc_mapping) { delete $param{$incexcmap->{key}}; } + my $maxdays = ($param{'maxdays'} || -1); my $mindays = ($param{'mindays'} || 0); my $version = $param{'version'} || undef; @@ -165,11 +198,6 @@ our %cats = ( "normal" => [ qw(status severity classification) ], ); -my @select_key = (qw(submitter maint pkg package src usertag), - qw(status tag maintenc owner severity newest), - qw(correspondent), - ); - if (exists $param{which} and exists $param{data}) { $param{$param{which}} = [exists $param{$param{which}}?(make_list($param{$param{which}})):(), make_list($param{data}), @@ -184,12 +212,10 @@ if (defined $param{maintenc}) { } -if (not grep {exists $param{$_}} @select_key and exists $param{users}) { +if (not grep {exists $param{$_}} keys %package_search_keys and exists $param{users}) { $param{usertag} = [make_list($param{users})]; } -quitcgi("You have to choose something to select by") unless grep {exists $param{$_}} @select_key; - if (exists $param{pkg}) { $param{package} = $param{pkg}; delete $param{pkg}; @@ -215,9 +241,12 @@ if (defined $param{usertag}) { } } +quitcgi("You have to choose something to select by") unless grep {exists $param{$_}} keys %package_search_keys; + + my $Archived = $param{archive} ? " Archived" : ""; -our $this = munge_url('pkgreport.cgi?', +my $this = munge_url('pkgreport.cgi?', %param, ); @@ -225,8 +254,8 @@ my %indexentry; my %strings = (); my $dtime = strftime "%a, %e %b %Y %T UTC", gmtime; -my $tail_html = $debbugs::gHTMLTail; -$tail_html = $debbugs::gHTMLTail; +my $tail_html = $gHTMLTail; +$tail_html = $gHTMLTail; $tail_html =~ s/SUBSTITUTE_DTIME/$dtime/; our %seen_users; @@ -286,20 +315,13 @@ for my $package (# For binary packages, add the binary package # walk through the keys and make the right get_bugs query. -my @search_key_order = (package => 'in package', - tag => 'tagged', - severity => 'with severity', - src => 'in source package', - maint => 'in packages maintained by', - submitter => 'submitted by', - owner => 'owned by', - status => 'with status', - ); -my %search_keys = @search_key_order; +my $form_option_variables = {}; +$form_option_variables->{search_key_order} = [@package_search_key_order]; # Set the title sanely and clean up parameters my @title; -while (my ($key,$value) = splice @search_key_order, 0, 2) { +my @temp = @package_search_key_order; +while (my ($key,$value) = splice @temp, 0, 2) { next unless exists $param{$key}; my @entries = (); $param{$key} = [map {split /\s*,\s*/} make_list($param{$key})]; @@ -356,7 +378,7 @@ elsif (defined $param{newest}) { else { #yeah for magick! @bugs = get_bugs((map {exists $param{$_}?($_,$param{$_}):()} - keys %search_keys, 'archive'), + keys %package_search_keys, 'archive'), usertags => \%ut, ); } @@ -371,14 +393,35 @@ elsif (defined $param{dist}) { $title = html_escape($title); my @names; my @prior; my @order; -determine_ordering(); +determine_ordering(cats => \%cats, + param => \%param, + ordering => \$ordering, + names => \@names, + prior => \@prior, + title => \@title, + order => \@order, + ); # strip out duplicate bugs my %bugs; @bugs{@bugs} = @bugs; @bugs = keys %bugs; -my $result = pkg_htmlizebugs(\@bugs); +my $result = pkg_htmlizebugs(bugs => \@bugs, + names => \@names, + title => \@title, + order => \@order, + prior => \@prior, + ordering => $ordering, + bugusertags => \%bugusertags, + bug_rev => $bug_rev, + bug_order => $bug_order, + repeatmerged => $param{repeatmerged}, + include => $include, + exclude => $exclude, + this => $this, + options => \%param, + ); print "Content-Type: text/html; charset=utf-8\n\n"; @@ -405,82 +448,18 @@ if (defined $pseudodesc and defined $pkg and exists $pseudodesc->{$pkg}) { # output infomration about the packages for my $package (make_list($param{package}||[])) { - output_package_info('binary',$package); + print generate_package_info(binary => 1, + package => $package, + options => \%param, + bugs => \@bugs, + ); } for my $package (make_list($param{src}||[])) { - output_package_info('source',$package); -} - -sub output_package_info{ - my ($srcorbin,$package) = @_; - - my %pkgsrc = %{getpkgsrc()}; - my $srcforpkg = $package; - if ($srcorbin eq 'binary') { - $srcforpkg = $pkgsrc{$package}; - defined $srcforpkg or $srcforpkg = $package; - } - - my $showpkg = html_escape($package); - my $maintainers = getmaintainers(); - my $maint = $maintainers->{$srcforpkg}; - if (defined $maint) { - print '

    '; - print htmlize_maintlinks(sub { $_[0] == 1 ? "Maintainer for $showpkg is " - : "Maintainers for $showpkg are " - }, - $maint); - print ".

    \n"; - } else { - print "

    No maintainer for $showpkg. Please do not report new bugs against this package.

    \n"; - } - my @pkgs = getsrcpkgs($srcforpkg); - @pkgs = grep( !/^\Q$package\E$/, @pkgs ); - if ( @pkgs ) { - @pkgs = sort @pkgs; - if ($srcorbin eq 'binary') { - print "

    You may want to refer to the following packages that are part of the same source:\n"; - } else { - print "

    You may want to refer to the following individual bug pages:\n"; - } - #push @pkgs, $src if ( $src && !grep(/^\Q$src\E$/, @pkgs) ); - print join( ", ", map( "$_,src=>[],newest=>[])) . "\">$_", @pkgs ) ); - print ".\n"; - } - my @references; - my $pseudodesc = getpseudodesc(); - if ($package and defined($pseudodesc) and exists($pseudodesc->{$package})) { - push @references, "to the ". - "list of other pseudo-packages"; - } else { - if ($package and defined $gPackagePages) { - push @references, sprintf "to the %s package page", - html_escape("http://${debbugs::gPackagePages}/$package"), html_escape("$package"); - } - if (defined $gSubscriptionDomain) { - my $ptslink = $package ? $srcforpkg : $src; - push @references, q(to the Package Tracking System); - } - # Only output this if the source listing is non-trivial. - if ($srcorbin eq 'binary' and $srcforpkg) { - push @references, sprintf "to the source package %s's bug page", html_escape(munge_url($this,src=>$srcforpkg,package=>[],newest=>[])), html_escape($srcforpkg); - } - } - if (@references) { - $references[$#references] = "or $references[$#references]" if @references > 1; - print "

    You might like to refer ", join(", ", @references), ".

    \n"; - } - if (defined $param{maint} || defined $param{maintenc}) { - print "

    If you find a bug not listed here, please\n"; - printf "report it.

    \n", - html_escape("http://${debbugs::gWebDomain}/Reporting${debbugs::gHTMLSuffix}"); - } - if (not $maint and not @bugs) { - print "

    There is no record of the " . html_escape($package) . - ($srcorbin eq 'binary' ? " package" : " source package") . - ", and no bugs have been filed against it.

    "; - $showresult = 0; - } + print generate_package_info(binary => 0, + package => $package, + options => \%param, + bugs => \@bugs, + ); } if (exists $param{maint} or exists $param{maintenc}) { @@ -494,677 +473,145 @@ if (exists $param{submitter}) { print "different addresses.\n"; } -my $archive_links; -my @archive_links; -my %archive_values = (both => 'archived and unarchived', - 0 => 'not archived', - 1 => 'archived', - ); -while (my ($key,$value) = each %archive_values) { - next if $key eq lc($param{archive}); - push @archive_links, qq($value reports ); -} -print '

    See the '.join (' or ',@archive_links)."

    \n"; - -print $result if $showresult; +# my $archive_links; +# my @archive_links; +# my %archive_values = (both => 'archived and unarchived', +# 0 => 'not archived', +# 1 => 'archived', +# ); +# while (my ($key,$value) = each %archive_values) { +# next if $key eq lc($param{archive}); +# push @archive_links, qq($value reports ); +# } +# print '

    See the '.join (' or ',@archive_links)."

    \n"; + +print $result; print pkg_javascript() . "\n"; -print "

    Options

    \n"; -print "
    \n"; -printf "
    \n", myurl(); - -print "\n"; - -my ($checked_any, $checked_sui, $checked_ver) = ("", "", ""); -if (defined $dist) { - $checked_sui = "CHECKED"; -} elsif (defined $version) { - $checked_ver = "CHECKED"; -} else { - $checked_any = "CHECKED"; -} - -print "\n"; -print " \n"; -print ""; -print " \n"; - -if (defined $pkg) { - my $v = html_escape($version) || ""; - my $pkgsane = html_escape($pkg->[0]); - print ""; - print " \n"; -} elsif (defined $src) { - my $v = html_escape($version) || ""; - my $srcsane = html_escape($src->[0]); - print ""; - print " \n"; -} -print "\n"; - -my $includetags = html_escape(join(" ", grep { !m/^subj:/i } map {split /[\s,]+/} ref($include)?@{$include}:$include)); -my $excludetags = html_escape(join(" ", grep { !m/^subj:/i } map {split /[\s,]+/} ref($exclude)?@{$exclude}:$exclude)); -my $includesubj = html_escape(join(" ", map { s/^subj://i; $_ } grep { m/^subj:/i } map {split /[\s,]+/} ref($include)?@{$include}:$include)); -my $excludesubj = html_escape(join(" ", map { s/^subj://i; $_ } grep { m/^subj:/i } map {split /[\s,]+/} ref($exclude)?@{$exclude}:$exclude)); -my $vismindays = ($mindays == 0 ? "" : $mindays); -my $vismaxdays = ($maxdays == -1 ? "" : $maxdays); - -my $sel_rmy = ($param{repeatmerged} ? " selected" : ""); -my $sel_rmn = ($param{repeatmerged} ? "" : " selected"); -my $sel_ordraw = ($ordering eq "raw" ? " selected" : ""); -my $sel_ordold = ($ordering eq "oldview" ? " selected" : ""); -my $sel_ordnor = ($ordering eq "normal" ? " selected" : ""); -my $sel_ordage = ($ordering eq "age" ? " selected" : ""); - -my $chk_bugrev = ($bug_rev ? " checked" : ""); -my $chk_pendrev = ($pend_rev ? " checked" : ""); -my $chk_sevrev = ($sev_rev ? " checked" : ""); - -print < - - - - - -\n"; +print qq(

    Options

    \n); -printf "\n", - pkg_htmlselectyesno("pend-rev", "outstanding bugs first", "done bugs first", $pend_rev); -printf "\n", - pkg_htmlselectyesno("sev-rev", "highest severity first", "lowest severity first", $sev_rev); -printf "\n", - pkg_htmlselectyesno("bug-rev", "oldest bugs first", "newest bugs first", $bug_rev); - -print < - -EOF +print option_form(template => 'cgi/pkgreport_options', + param => \%param, + form_options => $form_options, + variables => $form_option_variables, + ); -print "
    Show bugs applicable toanything
    " . pkg_htmlselectsuite(1,2,1) . " for " . pkg_htmlselectarch(1,2,2) . "
    $pkgsane version
    $srcsane version
     
    Only include bugs tagged with or that have in their subject
    Exclude bugs tagged with or that have in their subject
    Only show bugs older than days, and younger than days
     
    Merged bugs should be - -
    Categorise bugs by -
    Order bugs by%s
    %s
    %s
     
    with new settings
    \n"; +# print "

    Options

    \n"; +# print "
    \n"; +# printf "
    \n", myurl(); +# +# print "\n"; +# +# my ($checked_any, $checked_sui, $checked_ver) = ("", "", ""); +# if (defined $dist) { +# $checked_sui = "CHECKED"; +# } elsif (defined $version) { +# $checked_ver = "CHECKED"; +# } else { +# $checked_any = "CHECKED"; +# } +# +# print "\n"; +# print " \n"; +# print ""; +# print " \n"; +# +# if (defined $pkg) { +# my $v = html_escape($version) || ""; +# my $pkgsane = html_escape($pkg->[0]); +# print ""; +# print " \n"; +# } elsif (defined $src) { +# my $v = html_escape($version) || ""; +# my $srcsane = html_escape($src->[0]); +# print ""; +# print " \n"; +# } +# print "\n"; +# +# my $includetags = html_escape(join(" ", grep { !m/^subj:/i } map {split /[\s,]+/} ref($include)?@{$include}:$include)); +# my $excludetags = html_escape(join(" ", grep { !m/^subj:/i } map {split /[\s,]+/} ref($exclude)?@{$exclude}:$exclude)); +# my $includesubj = html_escape(join(" ", map { s/^subj://i; $_ } grep { m/^subj:/i } map {split /[\s,]+/} ref($include)?@{$include}:$include)); +# my $excludesubj = html_escape(join(" ", map { s/^subj://i; $_ } grep { m/^subj:/i } map {split /[\s,]+/} ref($exclude)?@{$exclude}:$exclude)); +# my $vismindays = ($mindays == 0 ? "" : $mindays); +# my $vismaxdays = ($maxdays == -1 ? "" : $maxdays); +# +# my $sel_rmy = ($param{repeatmerged} ? " selected" : ""); +# my $sel_rmn = ($param{repeatmerged} ? "" : " selected"); +# my $sel_ordraw = ($ordering eq "raw" ? " selected" : ""); +# my $sel_ordold = ($ordering eq "oldview" ? " selected" : ""); +# my $sel_ordnor = ($ordering eq "normal" ? " selected" : ""); +# my $sel_ordage = ($ordering eq "age" ? " selected" : ""); +# +# my $chk_bugrev = ($bug_rev ? " checked" : ""); +# my $chk_pendrev = ($pend_rev ? " checked" : ""); +# my $chk_sevrev = ($sev_rev ? " checked" : ""); +# +# print < +# +# +# +# +# +# \n"; +# +# printf "\n", +# pkg_htmlselectyesno("pend-rev", "outstanding bugs first", "done bugs first", $pend_rev); +# printf "\n", +# pkg_htmlselectyesno("sev-rev", "highest severity first", "lowest severity first", $sev_rev); +# printf "\n", +# pkg_htmlselectyesno("bug-rev", "oldest bugs first", "newest bugs first", $bug_rev); +# +# print < +# +# EOF +# +# print "
    Show bugs applicable toanything
    " . pkg_htmlselectsuite(1,2,1) . " for " . pkg_htmlselectarch(1,2,2) . "
    $pkgsane version
    $srcsane version
     
    Only include bugs tagged with or that have in their subject
    Exclude bugs tagged with or that have in their subject
    Only show bugs older than days, and younger than days
     
    Merged bugs should be +# +#
    Categorise bugs by +#
    Order bugs by%s
    %s
    %s
     
    with new settings
    \n"; print "
    \n"; print "

    $tail_html"; print "\n"; -sub pkg_htmlindexentrystatus { - my $s = shift; - my %status = %{$s}; - - my $result = ""; - - my $showseverity; - if ($status{severity} eq 'normal') { - $showseverity = ''; - } elsif (isstrongseverity($status{severity})) { - $showseverity = "Severity: $status{severity};\n"; - } else { - $showseverity = "Severity: $status{severity};\n"; - } - - $result .= pkg_htmlpackagelinks($status{"package"}, 1); - - my $showversions = ''; - if (@{$status{found_versions}}) { - my @found = @{$status{found_versions}}; - $showversions .= join ', ', map {s{/}{ }; 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; - } - $result .= ' ($showversions)} if length $showversions; - $result .= ";\n"; - - $result .= $showseverity; - $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})))) - . "" - if (length($status{tags})); - - $result .= buglinklist(";\nMerged with ", ", ", - split(/ /,$status{mergedwith})); - $result .= buglinklist(";\nBlocked by ", ", ", - split(/ /,$status{blockedby})); - $result .= buglinklist(";\nBlocks ", ", ", - split(/ /,$status{blocks})); - - if (length($status{done})) { - $result .= "
    Done: " . html_escape($status{done}); - my $days = bug_archiveable(bug => $status{id}, - status => \%status, - days_until => 1, - ); - if ($days >= 0 and defined $status{location} and $status{location} ne 'archive') { - $result .= ";\nCan be archived" . ( $days == 0 ? " today" : $days == 1 ? " in $days day" : " in $days days" ) . ""; - } - elsif (defined $status{location} and $status{location} eq 'archived') { - $result .= ";\nArchived."; - } - } - - unless (length($status{done})) { - if (length($status{forwarded})) { - $result .= ";\nForwarded to " - . join(', ', - map {maybelink($_)} - split /\,\s+/,$status{forwarded} - ); - } - # Check the age of the logfile - my ($days_last,$eng_last) = secs_to_english(time - $status{log_modified}); - my ($days,$eng) = secs_to_english(time - $status{date}); - - if ($days >= 7) { - my $font = ""; - my $efont = ""; - $font = "em" if ($days > 30); - $font = "strong" if ($days > 60); - $efont = "" if ($font); - $font = "<$font>" if ($font); - - $result .= ";\n ${font}$eng old$efont"; - } - if ($days_last > 7) { - my $font = ""; - my $efont = ""; - $font = "em" if ($days_last > 30); - $font = "strong" if ($days_last > 60); - $efont = "" if ($font); - $font = "<$font>" if ($font); - - $result .= ";\n ${font}Modified $eng_last ago$efont"; - } - } - - $result .= "."; - - return $result; -} - - -sub pkg_htmlizebugs { - $b = $_[0]; - my @bugs = @$b; - - my @status = (); - my %count; - my $header = ''; - my $footer = "

    Summary

    \n"; - - my @dummy = ($gRemoveAge); #, @gSeverityList, @gSeverityDisplay); #, $gHTMLExpireNote); - - if (@bugs == 0) { - return "

    No reports found!

    \n"; - } - - if ( $bug_rev ) { - @bugs = sort {$b<=>$a} @bugs; - } else { - @bugs = sort {$a<=>$b} @bugs; - } - my %seenmerged; - - my %common = ( - 'show_list_header' => 1, - 'show_list_footer' => 1, - ); - - my %section = (); - # Make the include/exclude map - my %include; - my %exclude; - for my $include (make_list($param{include})) { - next unless defined $include; - my ($key,$value) = split /\s*:\s*/,$include,2; - unless (defined $value) { - $key = 'tags'; - $value = $include; - } - push @{$include{$key}}, split /\s*,\s*/, $value; - } - for my $exclude (make_list($param{exclude})) { - next unless defined $exclude; - my ($key,$value) = split /\s*:\s*/,$exclude,2; - unless (defined $value) { - $key = 'tags'; - $value = $exclude; - } - push @{$exclude{$key}}, split /\s*,\s*/, $value; - } - - foreach my $bug (@bugs) { - my %status = %{get_bug_status(bug=>$bug, - (exists $param{dist}?(dist => $param{dist}):()), - bugusertags => \%bugusertags, - (exists $param{version}?(version => $param{version}):()), - (exists $param{arch}?(arch => $param{arch}):(arch => $config{default_architectures})), - )}; - next unless %status; - next if bug_filter(bug => $bug, - status => \%status, - (exists $param{repeatmerged}?(repeat_merged => $param{repeatmerged}):()), - seen_merged => \%seenmerged, - (keys %include ? (include => \%include):()), - (keys %exclude ? (exclude => \%exclude):()), - ); - - my $html = sprintf "
  • #%d: %s\n
    ", - bug_url($bug), $bug, html_escape($status{subject}); - $html .= pkg_htmlindexentrystatus(\%status) . "\n"; - push @status, [ $bug, \%status, $html ]; - } - if ($bug_order eq 'age') { - # MWHAHAHAHA - @status = sort {$a->[1]{log_modified} <=> $b->[1]{log_modified}} @status; - } - elsif ($bug_order eq 'agerev') { - @status = sort {$b->[1]{log_modified} <=> $a->[1]{log_modified}} @status; - } - for my $entry (@status) { - my $key = ""; - for my $i (0..$#prior) { - my $v = get_bug_order_index($prior[$i], $entry->[1]); - $count{"g_${i}_${v}"}++; - $key .= "_$v"; - } - $section{$key} .= $entry->[2]; - $count{"_$key"}++; - } - - my $result = ""; - if ($ordering eq "raw") { - $result .= "
      \n" . join("", map( { $_->[ 2 ] } @status ) ) . "
    \n"; - } else { - $header .= "
    \n
      \n"; - my @keys_in_order = (""); - for my $o (@order) { - push @keys_in_order, "X"; - while ((my $k = shift @keys_in_order) ne "X") { - for my $k2 (@{$o}) { - $k2+=0; - push @keys_in_order, "${k}_${k2}"; - } - } - } - for my $order (@keys_in_order) { - next unless defined $section{$order}; - my @ttl = split /_/, $order; shift @ttl; - my $title = $title[0]->[$ttl[0]] . " bugs"; - if ($#ttl > 0) { - $title .= " -- "; - $title .= join("; ", grep {($_ || "") ne ""} - map { $title[$_]->[$ttl[$_]] } 1..$#ttl); - } - $title = html_escape($title); - - my $count = $count{"_$order"}; - my $bugs = $count == 1 ? "bug" : "bugs"; - - $header .= "
    • $title ($count $bugs)
    • \n"; - if ($common{show_list_header}) { - my $count = $count{"_$order"}; - my $bugs = $count == 1 ? "bug" : "bugs"; - $result .= "

      $title ($count $bugs)

      \n"; - } else { - $result .= "

      $title

      \n"; - } - $result .= "
      \n
        \n"; - $result .= "\n\n\n\n"; - $result .= $section{$order}; - $result .= "\n\n\n\n"; - $result .= "
      \n
      \n"; - } - $header .= "
    \n"; - - $footer .= "
    \n
      \n"; - for my $i (0..$#prior) { - my $local_result = ''; - foreach my $key ( @{$order[$i]} ) { - my $count = $count{"g_${i}_$key"}; - next if !$count or !$title[$i]->[$key]; - $local_result .= "
    • $count $title[$i]->[$key]
    • \n"; - } - if ( $local_result ) { - $footer .= "
    • $names[$i]
        \n$local_result
    • \n"; - } - } - $footer .= "
    \n
    \n"; - } - - $result = $header . $result if ( $common{show_list_header} ); - $result .= $footer if ( $common{show_list_footer} ); - return $result; -} - -sub pkg_htmlpackagelinks { - my $pkgs = shift; - return unless defined $pkgs and $pkgs ne ''; - my $strong = shift; - my @pkglist = splitpackages($pkgs); - - $strong = 0; - my $openstrong = $strong ? '' : ''; - my $closestrong = $strong ? '' : ''; - - return 'Package' . (@pkglist > 1 ? 's' : '') . ': ' . - join(', ', - map { - '' . - $openstrong . html_escape($_) . $closestrong . '' - } @pkglist - ); -} - -sub pkg_javascript { - return < - - -EOF -} - -sub pkg_htmlselectyesno { - my ($name, $n, $y, $default) = @_; - return sprintf('', $name, ($default ? "" : " selected"), $n, ($default ? " selected" : ""), $y); -} - -sub pkg_htmlselectsuite { - my $id = sprintf "b_%d_%d_%d", $_[0], $_[1], $_[2]; - my @suites = ("stable", "testing", "unstable", "experimental"); - my %suiteaka = ("stable", "etch", "testing", "lenny", "unstable", "sid"); - my $defaultsuite = "unstable"; - - my $result = sprintf ''; - return $result; -} - -sub pkg_htmlselectarch { - my $id = sprintf "b_%d_%d_%d", $_[0], $_[1], $_[2]; - my @arches = qw(alpha amd64 arm hppa i386 ia64 m68k mips mipsel powerpc s390 sparc); - - my $result = sprintf ''; - return $result; -} - -sub myurl { - return html_escape(pkg_url(map {exists $param{$_}?($_,$param{$_}):()} - qw(archive repeatmerged mindays maxdays), - qw(version dist arch package src tag maint submitter) - ) - ); -} - -sub make_order_list { - my $vfull = shift; - my @x = (); - - if ($vfull =~ m/^([^:]+):(.*)$/) { - my $v = $1; - for my $vv (split /,/, $2) { - push @x, "$v=$vv"; - } - } else { - for my $v (split /,/, $vfull) { - next unless $v =~ m/.=./; - push @x, $v; - } - } - push @x, ""; # catch all - return @x; -} - -sub get_bug_order_index { - my $order = shift; - my $status = shift; - my $pos = -1; - - my %tags = (); - %tags = map { $_, 1 } split / /, $status->{"tags"} - if defined $status->{"tags"}; - - for my $el (@${order}) { - $pos++; - my $match = 1; - for my $item (split /[+]/, $el) { - my ($f, $v) = split /=/, $item, 2; - next unless (defined $f and defined $v); - my $isokay = 0; - $isokay = 1 if (defined $status->{$f} and $v eq $status->{$f}); - $isokay = 1 if ($f eq "tag" && defined $tags{$v}); - unless ($isokay) { - $match = 0; - last; - } - } - if ($match) { - return $pos; - last; - } - } - return $pos + 1; -} - -sub buglinklist { - my ($prefix, $infix, @els) = @_; - return '' if not @els; - return $prefix . bug_linklist($infix,'submitter',@els); -} - - -# sets: my @names; my @prior; my @title; my @order; - -sub determine_ordering { - $cats{status}[0]{ord} = [ reverse @{$cats{status}[0]{ord}} ] - if ($pend_rev); - $cats{severity}[0]{ord} = [ reverse @{$cats{severity}[0]{ord}} ] - if ($sev_rev); - - my $i; - if (defined $param{"pri0"}) { - my @c = (); - $i = 0; - while (defined $param{"pri$i"}) { - my $h = {}; - - my ($pri) = make_list($param{"pri$i"}); - if ($pri =~ m/^([^:]*):(.*)$/) { - $h->{"nam"} = $1; # overridden later if necesary - $h->{"pri"} = [ map { "$1=$_" } (split /,/, $2) ]; - } else { - $h->{"pri"} = [ split /,/, $pri ]; - } - - ($h->{"nam"}) = make_list($param{"nam$i"}) - if (defined $param{"nam$i"}); - $h->{"ord"} = [ map {split /\s*,\s*/} make_list($param{"ord$i"}) ] - if (defined $param{"ord$i"}); - $h->{"ttl"} = [ map {split /\s*,\s*/} make_list($param{"ttl$i"}) ] - if (defined $param{"ttl$i"}); - - push @c, $h; - $i++; - } - $cats{"_"} = [@c]; - $ordering = "_"; - } - - $ordering = "normal" unless defined $cats{$ordering}; - - sub get_ordering { - my @res; - my $cats = shift; - my $o = shift; - for my $c (@{$cats->{$o}}) { - if (ref($c) eq "HASH") { - push @res, $c; - } else { - push @res, get_ordering($cats, $c); - } - } - return @res; - } - my @cats = get_ordering(\%cats, $ordering); - - sub toenglish { - my $expr = shift; - $expr =~ s/[+]/ and /g; - $expr =~ s/[a-z]+=//g; - return $expr; - } - - $i = 0; - for my $c (@cats) { - $i++; - push @prior, $c->{"pri"}; - push @names, ($c->{"nam"} || "Bug attribute #" . $i); - if (defined $c->{"ord"}) { - push @order, $c->{"ord"}; - } else { - push @order, [ 0..$#{$prior[-1]} ]; - } - my @t = @{ $c->{"ttl"} } if defined $c->{ttl}; - if (@t < $#{$prior[-1]}) { - push @t, map { toenglish($prior[-1][$_]) } @t..($#{$prior[-1]}); - } - push @t, $c->{"def"} || ""; - push @title, [@t]; - } -} diff --git a/html/bugs.css b/html/bugs.css index abe4f580..268317bb 100644 --- a/html/bugs.css +++ b/html/bugs.css @@ -198,6 +198,36 @@ li { list-style-type: square; } +.shortbugstatus +{ + font-family: sans-serif; + + } + +.shortbugstatusextra +{ font-family: sans-serif; + margin: 5px; + margin-top: 2px; + padding: 5px; + /* display: none; */ + /* z-index: 1; */ + /* position: absolute; */ + left: 120px; + background-color: #f0f0f0; +/* border: #000 1px solid; */ + position: static; + display: block; + border: 0; + } + +.shortbugstatusextra span +{ margin: 0; + margin-top: 0px; + padding: 0; + border: 0; + display: block; + } + .bugs li { margin-top: 5px; } diff --git a/templates/en_US/cgi/pkgreport_javascript.tmpl b/templates/en_US/cgi/pkgreport_javascript.tmpl new file mode 100644 index 00000000..f801df96 --- /dev/null +++ b/templates/en_US/cgi/pkgreport_javascript.tmpl @@ -0,0 +1,128 @@ + diff --git a/templates/en_US/cgi/pkgreport_options.tmpl b/templates/en_US/cgi/pkgreport_options.tmpl new file mode 100644 index 00000000..7fa264a2 --- /dev/null +++ b/templates/en_US/cgi/pkgreport_options.tmpl @@ -0,0 +1,83 @@ +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    Select bugs

    +
    +{ my $output = ''; +our $value_index = 0; +our $search = ''; +our $search_value = ''; +for my $key (@search_key_order){ + if (exists $param{$key}){ + for my $value (make_list($param{$key})){ + $search = $key; + $search_value = $value; + $output .= include('cgi/pkgreport_options_search_key'); + $output .= '
    '; + $value_index++; + } + } + } + $search = ''; + $search_value = ''; + $output; +} +{include('cgi/pkgreport_options_search_key')} +
    +

    The same search fields are ORed, different fields are ANDed.

    +

    Valid severities are {$config{show_severities}}

    +

    Valid tags are {join(', ',@{$config{tags}})}

    +

    Include Bugs

    {our $incexc = 'include'; +include('cgi/pkgreport_options_include_exclude'); +}

    Exclude Bugs

    +{our $incexc = 'exclude'; +include('cgi/pkgreport_options_include_exclude'); +} +

    Categorize using

    Order by

    Misc options

    + Repeat Merged
    + Reverse Bugs
    + Reverse Pending
    + Reverse Severity
    +
    +Toggle all extra information +

    Submit

    + +
    + + diff --git a/templates/en_US/cgi/pkgreport_options_include_exclude.tmpl b/templates/en_US/cgi/pkgreport_options_include_exclude.tmpl new file mode 100644 index 00000000..c0f8acda --- /dev/null +++ b/templates/en_US/cgi/pkgreport_options_include_exclude.tmpl @@ -0,0 +1,16 @@ + +{ my $output = ''; + our $value_index = 0; + our $key1 = ''; + our $key2 = ''; + for my $field (make_list($param{$incexc})) { + ($key1,$key2) = $field =~ m/^([^:]+)\:(.+)/; + next unless defined $key2; + $output .= include('cgi/pkgreport_options_include_exclude_key'); + } + $key1 = ''; + $key2 = ''; + $output .= include('cgi/pkgreport_options_include_exclude_key'); + $output; +} + diff --git a/templates/en_US/cgi/pkgreport_options_include_exclude_key.tmpl b/templates/en_US/cgi/pkgreport_options_include_exclude_key.tmpl new file mode 100644 index 00000000..da67c300 --- /dev/null +++ b/templates/en_US/cgi/pkgreport_options_include_exclude_key.tmpl @@ -0,0 +1,14 @@ + + + + diff --git a/templates/en_US/cgi/pkgreport_options_search_key.tmpl b/templates/en_US/cgi/pkgreport_options_search_key.tmpl new file mode 100644 index 00000000..1c2ecd9d --- /dev/null +++ b/templates/en_US/cgi/pkgreport_options_search_key.tmpl @@ -0,0 +1,6 @@ + + + + diff --git a/templates/en_US/cgi/short_bug_status.tmpl b/templates/en_US/cgi/short_bug_status.tmpl new file mode 100644 index 00000000..fb779401 --- /dev/null +++ b/templates/en_US/cgi/short_bug_status.tmpl @@ -0,0 +1,185 @@ +
    + #{html_escape($status{bug_num})} + [{ + my $output = qq(); + my $temp = $status{severity}; + $temp = substr $temp,0,1; + if (isstrongseverity($status{severity})){ + $temp = q().uc($temp).q(); + $output; + }|{ + my $output = ''; + for my $tag (@{$status{tags_array}}) { + next unless exists $config{tags_single_letter}{$tag}; + $output .= q().$config{tags_single_letter}{$tag}.q(); + } + $output; + }|{ + my $output = ''; + if (@{$status{mergedwith_array}}) { + $output .= qq(=); + } + if (@{$status{fixed_versions}}) { + $output .= qq(☺); + } + if (@{$status{blockedby_array}}) { + $output .= qq(┫); + } + if (@{$status{blocks_array}}) { + $output .= qq(┣); + } + if (length($status{forwarded})) { + $output .= qq(↝); + } + if ($status{archived}) { + $output .= qq(♲); + } + $output; + }] + [{package_links(package=>$status{package},options=>\%options,class=>"submitter")}] + {html_escape($status{subject})} +
    + + 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}} or @{$status{fixed_versions}}) { + $output .= '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); + } + if (@{$status{found_versions}} or @{$status{fixed_versions}}) { + $output .= qq(); + } + $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 = ''; + my ($days_last,$eng_last) = secs_to_english(time - $status{log_modified}); + my ($days,$eng) = secs_to_english(time - $status{date}); + + if ($days >= 7) { + my $font = ""; + my $efont = ""; + $font = "em" if ($days > 30); + $font = "strong" if ($days > 60); + $efont = "" if ($font); + $font = "<$font>" if ($font); + + $output .= "${font}Filed $eng ago$efont;\n"; + } + if ($days_last > 7) { + my $font = ""; + my $efont = ""; + $font = "em" if ($days_last > 30); + $font = "strong" if ($days_last > 60); + $efont = "" if ($font); + $font = "<$font>" if ($font); + + $output .= "${font}Modified $eng_last ago$efont;\n"; + } + $output; + }{ my $output = ''; + if (exists $status{archived} and $status{archived}) { + $output .= q(Bug is archived. No further changes may be made. ) + } + $output} +
    +
    -- 2.39.5