]> git.donarmstrong.com Git - debbugs.git/commitdiff
* Completely reformat pkgreport.cgi output
authorDon Armstrong <don@donarmstrong.com>
Thu, 7 Aug 2008 19:37:39 +0000 (12:37 -0700)
committerDon Armstrong <don@donarmstrong.com>
Thu, 7 Aug 2008 19:37:39 +0000 (12:37 -0700)
 * 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
Debbugs/CGI/Pkgreport.pm [new file with mode: 0644]
cgi/pkgreport.cgi
html/bugs.css
templates/en_US/cgi/pkgreport_javascript.tmpl [new file with mode: 0644]
templates/en_US/cgi/pkgreport_options.tmpl [new file with mode: 0644]
templates/en_US/cgi/pkgreport_options_include_exclude.tmpl [new file with mode: 0644]
templates/en_US/cgi/pkgreport_options_include_exclude_key.tmpl [new file with mode: 0644]
templates/en_US/cgi/pkgreport_options_search_key.tmpl [new file with mode: 0644]
templates/en_US/cgi/short_bug_status.tmpl [new file with mode: 0644]

index e18891ab32c1a1882c4795548f03eb0050884534..1ba47996744eeb2ec4358db4da55910e30d9ba8e 100644 (file)
@@ -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<Debbugs::CGI/splitpackages> 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(</a>);
          }
      }
-     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
+
+ <input type="hidden" name="_fo_combine_key_fo_searchkey_value_fo_searchvalue" value="1">
+
+which would combine the _fo_searchkey and _fo_searchvalue input fields, so
+
+ <input type="text" name="_fo_searchkey" value="foo">
+ <input type="text" name="_fo_searchvalue" value="bar">
+
+would yield foo=>'bar' in %param.
+
+=head3 concatenate
+
+Concatenate concatenates values into a single entry in a parameter
+
+For example, you would have
+
+ <input type="hidden" name="_fo_concatentate_into_foo_with_:_fo_blah_fo_bleargh" value="1">
+
+which would combine the _fo_searchkey and _fo_searchvalue input fields, so
+
+ <input type="text" name="_fo_blah" value="bar">
+ <input type="text" name="_fo_bleargh" value="baz">
+
+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(<option value="$o_value"$selected>$name</option>\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 (file)
index 0000000..06946a6
--- /dev/null
@@ -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 <don@donarmstrong.com>.
+
+
+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} '<p>';
+         print {$output} htmlize_maintlinks(sub { $_[0] == 1 ? "Maintainer for $showpkg is "
+                                                       : "Maintainers for $showpkg are "
+                                                  },
+                                            $maint);
+         print {$output} ".</p>\n";
+     }
+     else {
+         print {$output} "<p>No maintainer for $showpkg. Please do not report new bugs against this package.</p>\n";
+     }
+     my @pkgs = getsrcpkgs($srcforpkg);
+     @pkgs = grep( !/^\Q$package\E$/, @pkgs );
+     if ( @pkgs ) {
+         @pkgs = sort @pkgs;
+         if ($param{binary}) {
+              print {$output} "<p>You may want to refer to the following packages that are part of the same source:\n";
+         }
+         else {
+              print {$output} "<p>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 <a href=\"http://${debbugs::gWebDomain}/pseudo-packages${debbugs::gHTMLSuffix}\">".
+              "list of other pseudo-packages</a>";
+     }
+     else {
+         if ($package and defined $gPackagePages) {
+              push @references, sprintf "to the <a href=\"%s\">%s package page</a>",
+                   html_escape("http://${gPackagePages}/$package"), html_escape("$package");
+         }
+         if (defined $gSubscriptionDomain) {
+              my $ptslink = $param{binary} ? $srcforpkg : $package;
+              push @references, q(to the <a href="http://).html_escape("$gSubscriptionDomain/$ptslink").q(">Package Tracking System</a>);
+         }
+         # 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} "<p>You might like to refer ", join(", ", @references), ".</p>\n";
+     }
+     if (defined $param{maint} || defined $param{maintenc}) {
+         print {$output} "<p>If you find a bug not listed here, please\n";
+         printf {$output} "<a href=\"%s\">report it</a>.</p>\n",
+              html_escape("http://${debbugs::gWebDomain}/Reporting${debbugs::gHTMLSuffix}");
+     }
+     if (not $maint and not @{$param{bugs}}) {
+         print {$output} "<p>There is no record of the " . html_escape($package) .
+              ($param{binary} ? " package" : " source package") .
+                   ", and no bugs have been filed against it.</p>";
+     }
+     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: <em class=\"severity\">$status{severity}</em>;\n";
+     }
+     else {
+         $showseverity = "Severity: <em>$status{severity}</em>;\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 .= '<strong>fixed</strong>: ';
+         my @fixed = @{$status{fixed_versions}};
+         $showversions .= join ', ', map {s{/}{ }; html_escape($_)} @fixed;
+     }
+     $result .= ' (<a href="'.
+         version_url(package => $status{package},
+                     found   => $status{found_versions},
+                     fixed   => $status{fixed_versions},
+                    ).qq{">$showversions</a>)} 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: <strong>"
+         . html_escape(join(", ", sort(split(/\s+/, $status{tags}))))
+              . "</strong>"
+                   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 .= "<br><strong>Done:</strong> " . 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 .= ";\n<strong>Can be archived" . ( $days == 0 ? " today" : $days == 1 ? " in $days day" : " in $days days" ) . "</strong>";
+         }
+         elsif (defined $status{location} and $status{location} eq 'archived') {
+              $result .= ";\n<strong>Archived.</strong>";
+         }
+     }
+
+     unless (length($status{done})) {
+         if (length($status{forwarded})) {
+              $result .= ";\n<strong>Forwarded</strong> 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 = "</$font>" 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 = "</$font>" 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 = "<h2 class=\"outstanding\">Summary</h2>\n";
+
+     my @dummy = ($gRemoveAge); #, @gSeverityList, @gSeverityDisplay);  #, $gHTMLExpireNote);
+
+     if (@bugs == 0) {
+         return "<HR><H2>No reports found!</H2></HR>\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 = "<li>"; #<a href=\"%s\">#%d: %s</a>\n<br>",
+              #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 .= "<UL class=\"bugs\">\n" . join("", map( { $_->[ 2 ] } @status ) ) . "</UL>\n";
+     }
+     else {
+         $header .= "<div class=\"msgreceived\">\n<ul>\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 .= "<li><a href=\"#$order\">$title</a> ($count $bugs)</li>\n";
+              if ($common{show_list_header}) {
+                   my $count = $count{"_$order"};
+                   my $bugs = $count == 1 ? "bug" : "bugs";
+                   $result .= "<H2 CLASS=\"outstanding\"><a name=\"$order\"></a>$title ($count $bugs)</H2>\n";
+              }
+              else {
+                   $result .= "<H2 CLASS=\"outstanding\">$title</H2>\n";
+              }
+              $result .= "<div class=\"msgreceived\">\n<UL class=\"bugs\">\n";
+              $result .= "\n\n\n\n";
+              $result .= $section{$order};
+              $result .= "\n\n\n\n";
+              $result .= "</UL>\n</div>\n";
+         } 
+         $header .= "</ul></div>\n";
+
+         $footer .= "<div class=\"msgreceived\">\n<ul>\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 .= "<li>$count $param{title}[$i]->[$key]</li>\n";
+              }
+              if ( $local_result ) {
+                   $footer .= "<li>$param{names}[$i]<ul>\n$local_result</ul></li>\n";
+              }
+         }
+         $footer .= "</ul>\n</div>\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('<select name="%s"><option value=no%s>%s</option><option value=yes%s>%s</option></select>', $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 '<select name=dist id="%s">', $id;
+     for my $s (@suites) {
+         $result .= sprintf '<option value="%s"%s>%s%s</option>',
+              $s, ($defaultsuite eq $s ? " selected" : ""),
+                   $s, (defined $suiteaka{$s} ? " (" . $suiteaka{$s} . ")" : "");
+     }
+     $result .= '</select>';
+     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 '<select name=arch id="%s">', $id;
+     $result .= '<option value="any">any architecture</option>';
+     for my $a (@arches) {
+         $result .= sprintf '<option value="%s">%s</option>', $a, $a;
+     }
+     $result .= '</select>';
+     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__
+
+
+
+
+
+
index 3c6afe36178b5d7137150d0fa99f21e24794d1df..3750949728c8dbbe38672d0d25d89445743383e1 100755 (executable)
 # Copyright 2007 by Don Armstrong <don@donarmstrong.com>.
 
 
-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 '<p>';
-        print htmlize_maintlinks(sub { $_[0] == 1 ? "Maintainer for $showpkg is "
-                                        : "Maintainers for $showpkg are "
-                                   },
-                             $maint);
-        print ".</p>\n";
-    } else {
-        print "<p>No maintainer for $showpkg. Please do not report new bugs against this package.</p>\n";
-    }
-    my @pkgs = getsrcpkgs($srcforpkg);
-    @pkgs = grep( !/^\Q$package\E$/, @pkgs );
-    if ( @pkgs ) {
-        @pkgs = sort @pkgs;
-        if ($srcorbin eq 'binary') {
-             print "<p>You may want to refer to the following packages that are part of the same source:\n";
-        } else {
-             print "<p>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( "<A href=\"" . html_escape(munge_url($this,package=>$_,src=>[],newest=>[])) . "\">$_</A>", @pkgs ) );
-        print ".\n";
-    }
-    my @references;
-    my $pseudodesc = getpseudodesc();
-    if ($package and defined($pseudodesc) and exists($pseudodesc->{$package})) {
-        push @references, "to the <a href=\"http://${debbugs::gWebDomain}/pseudo-packages${debbugs::gHTMLSuffix}\">".
-             "list of other pseudo-packages</a>";
-    } else {
-        if ($package and defined $gPackagePages) {
-             push @references, sprintf "to the <a href=\"%s\">%s package page</a>",
-                  html_escape("http://${debbugs::gPackagePages}/$package"), html_escape("$package");
-        }
-        if (defined $gSubscriptionDomain) {
-             my $ptslink = $package ? $srcforpkg : $src;
-             push @references, q(to the <a href="http://).html_escape("$gSubscriptionDomain/$ptslink").q(">Package Tracking System</a>);
-        }
-        # Only output this if the source listing is non-trivial.
-        if ($srcorbin eq 'binary' and $srcforpkg) {
-             push @references, sprintf "to the source package <a href=\"%s\">%s</a>'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 "<p>You might like to refer ", join(", ", @references), ".</p>\n";
-    }
-    if (defined $param{maint} || defined $param{maintenc}) {
-        print "<p>If you find a bug not listed here, please\n";
-        printf "<a href=\"%s\">report it</a>.</p>\n",
-             html_escape("http://${debbugs::gWebDomain}/Reporting${debbugs::gHTMLSuffix}");
-    }
-    if (not $maint and not @bugs) {
-        print "<p>There is no record of the " . html_escape($package) .
-             ($srcorbin eq 'binary' ? " package" : " source package") .
-                   ", and no bugs have been filed against it.</p>";
-        $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(<a href=").
-         html_escape(pkg_url((
-                      map {
-                           $_ eq 'archive'?():($_,$param{$_})
-                      } keys %param),
-                           archive => $key
-                          )).qq(">$value reports </a>);
-}
-print '<p>See the '.join (' or ',@archive_links)."</p>\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(<a href=").
+#        html_escape(pkg_url((
+#                     map {
+#                          $_ eq 'archive'?():($_,$param{$_})
+#                     } keys %param),
+#                          archive => $key
+#                         )).qq(">$value reports </a>);
+}
+print '<p>See the '.join (' or ',@archive_links)."</p>\n";
+
+print $result;
 
 print pkg_javascript() . "\n";
-print "<h2 class=\"outstanding\"><a class=\"options\" href=\"javascript:toggle(1)\">Options</a></h2>\n";
-print "<div id=\"a_1\">\n";
-printf "<form action=\"%s\" method=POST>\n", myurl();
-
-print "<table class=\"forms\">\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 "<tr><td>Show bugs applicable to</td>\n";
-print "    <td><input id=\"b_1_1\" name=vt value=none type=radio onchange=\"enable(1);\" $checked_any>anything</td></tr>\n";
-print "<tr><td></td>";
-print "    <td><input id=\"b_1_2\" name=vt value=bysuite type=radio onchange=\"enable(1);\" $checked_sui>" . pkg_htmlselectsuite(1,2,1) . " for " . pkg_htmlselectarch(1,2,2) . "</td></tr>\n";
-
-if (defined $pkg) {
-    my $v = html_escape($version) || "";
-    my $pkgsane = html_escape($pkg->[0]);
-    print "<tr><td></td>";
-    print "    <td><input id=\"b_1_3\" name=vt value=bypkg type=radio onchange=\"enable(1);\" $checked_ver>$pkgsane version <input id=\"b_1_3_1\" name=version value=\"$v\"></td></tr>\n";
-} elsif (defined $src) {
-    my $v = html_escape($version) || "";
-    my $srcsane = html_escape($src->[0]);
-    print "<tr><td></td>";
-    print "    <td><input name=vt value=bysrc type=radio onchange=\"enable(1);\" $checked_ver>$srcsane version <input id=\"b_1_3_1\" name=version value=\"$v\"></td></tr>\n";
-}
-print "<tr><td>&nbsp;</td></tr>\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 <<EOF;
-<tr><td>Only include bugs tagged with </td><td><input name=include value="$includetags"> or that have <input name=includesubj value="$includesubj"> in their subject</td></tr>
-<tr><td>Exclude bugs tagged with </td><td><input name=exclude value="$excludetags"> or that have <input name=excludesubj value="$excludesubj"> in their subject</td></tr>
-<tr><td>Only show bugs older than</td><td><input name=mindays value="$vismindays" size=5> days, and younger than <input name=maxdays value="$vismaxdays" size=5> days</td></tr>
-
-<tr><td>&nbsp;</td></tr>
-
-<tr><td>Merged bugs should be</td><td>
-<select name=repeatmerged>
-<option value=yes$sel_rmy>displayed separately</option>
-<option value=no$sel_rmn>combined</option>
-</select>
-<tr><td>Categorise bugs by</td><td>
-<select name=ordering>
-<option value=raw$sel_ordraw>bug number only</option>
-<option value=old$sel_ordold>status and severity</option>
-<option value=normal$sel_ordnor>status, severity and classification</option>
-<option value=age$sel_ordage>status, severity, classification, and age</option>
-EOF
-
-{
-my $any = 0;
-my $o = $param{"ordering"} || "";
-for my $n (keys %cats) {
-    next if ($n eq "normal" || $n eq "oldview");
-    next if defined $hidden{$n};
-    unless ($any) {
-        $any = 1;
-       print "<option disabled>------</option>\n";
-    }
-    my @names = map { ref($_) eq "HASH" ? $_->{"nam"} : $_ } @{$cats{$n}};
-    my $name;
-    if (@names == 1) { $name = $names[0]; }
-    else { $name = " and " . pop(@names); $name = join(", ", @names) . $name; }
-
-    printf "<option value=\"%s\"%s>%s</option>\n",
-        $n, ($o eq $n ? " selected" : ""), $name;
-}
-}
 
-print "</select></td></tr>\n";
+print qq(<h2 class="outstanding"><!--<a class="options" href="javascript:toggle(1)">-->Options<!--</a>--></h2>\n);
 
-printf "<tr><td>Order bugs by</td><td>%s</td></tr>\n",
-    pkg_htmlselectyesno("pend-rev", "outstanding bugs first", "done bugs first", $pend_rev);
-printf "<tr><td></td><td>%s</td></tr>\n",
-    pkg_htmlselectyesno("sev-rev", "highest severity first", "lowest severity first", $sev_rev);
-printf "<tr><td></td><td>%s</td></tr>\n",
-    pkg_htmlselectyesno("bug-rev", "oldest bugs first", "newest bugs first", $bug_rev);
-
-print <<EOF;
-<tr><td>&nbsp;</td></tr>
-<tr><td colspan=2><input value="Reload page" type="submit"> with new settings</td></tr>
-EOF
+print option_form(template => 'cgi/pkgreport_options',
+                 param    => \%param,
+                 form_options => $form_options,
+                 variables => $form_option_variables,
+                );
 
-print "</table></form></div>\n";
+# print "<h2 class=\"outstanding\"><a class=\"options\" href=\"javascript:toggle(1)\">Options</a></h2>\n";
+# print "<div id=\"a_1\">\n";
+# printf "<form action=\"%s\" method=POST>\n", myurl();
+# 
+# print "<table class=\"forms\">\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 "<tr><td>Show bugs applicable to</td>\n";
+# print "    <td><input id=\"b_1_1\" name=vt value=none type=radio onchange=\"enable(1);\" $checked_any>anything</td></tr>\n";
+# print "<tr><td></td>";
+# print "    <td><input id=\"b_1_2\" name=vt value=bysuite type=radio onchange=\"enable(1);\" $checked_sui>" . pkg_htmlselectsuite(1,2,1) . " for " . pkg_htmlselectarch(1,2,2) . "</td></tr>\n";
+# 
+# if (defined $pkg) {
+#     my $v = html_escape($version) || "";
+#     my $pkgsane = html_escape($pkg->[0]);
+#     print "<tr><td></td>";
+#     print "    <td><input id=\"b_1_3\" name=vt value=bypkg type=radio onchange=\"enable(1);\" $checked_ver>$pkgsane version <input id=\"b_1_3_1\" name=version value=\"$v\"></td></tr>\n";
+# } elsif (defined $src) {
+#     my $v = html_escape($version) || "";
+#     my $srcsane = html_escape($src->[0]);
+#     print "<tr><td></td>";
+#     print "    <td><input name=vt value=bysrc type=radio onchange=\"enable(1);\" $checked_ver>$srcsane version <input id=\"b_1_3_1\" name=version value=\"$v\"></td></tr>\n";
+# }
+# print "<tr><td>&nbsp;</td></tr>\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 <<EOF;
+# <tr><td>Only include bugs tagged with </td><td><input name=include value="$includetags"> or that have <input name=includesubj value="$includesubj"> in their subject</td></tr>
+# <tr><td>Exclude bugs tagged with </td><td><input name=exclude value="$excludetags"> or that have <input name=excludesubj value="$excludesubj"> in their subject</td></tr>
+# <tr><td>Only show bugs older than</td><td><input name=mindays value="$vismindays" size=5> days, and younger than <input name=maxdays value="$vismaxdays" size=5> days</td></tr>
+# 
+# <tr><td>&nbsp;</td></tr>
+# 
+# <tr><td>Merged bugs should be</td><td>
+# <select name=repeatmerged>
+# <option value=yes$sel_rmy>displayed separately</option>
+# <option value=no$sel_rmn>combined</option>
+# </select>
+# <tr><td>Categorise bugs by</td><td>
+# <select name=ordering>
+# <option value=raw$sel_ordraw>bug number only</option>
+# <option value=old$sel_ordold>status and severity</option>
+# <option value=normal$sel_ordnor>status, severity and classification</option>
+# <option value=age$sel_ordage>status, severity, classification, and age</option>
+# EOF
+# 
+# {
+# my $any = 0;
+# my $o = $param{"ordering"} || "";
+# for my $n (keys %cats) {
+#     next if ($n eq "normal" || $n eq "oldview");
+#     next if defined $hidden{$n};
+#     unless ($any) {
+#         $any = 1;
+#      print "<option disabled>------</option>\n";
+#     }
+#     my @names = map { ref($_) eq "HASH" ? $_->{"nam"} : $_ } @{$cats{$n}};
+#     my $name;
+#     if (@names == 1) { $name = $names[0]; }
+#     else { $name = " and " . pop(@names); $name = join(", ", @names) . $name; }
+# 
+#     printf "<option value=\"%s\"%s>%s</option>\n",
+#         $n, ($o eq $n ? " selected" : ""), $name;
+# }
+# }
+# 
+# print "</select></td></tr>\n";
+# 
+# printf "<tr><td>Order bugs by</td><td>%s</td></tr>\n",
+#     pkg_htmlselectyesno("pend-rev", "outstanding bugs first", "done bugs first", $pend_rev);
+# printf "<tr><td></td><td>%s</td></tr>\n",
+#     pkg_htmlselectyesno("sev-rev", "highest severity first", "lowest severity first", $sev_rev);
+# printf "<tr><td></td><td>%s</td></tr>\n",
+#     pkg_htmlselectyesno("bug-rev", "oldest bugs first", "newest bugs first", $bug_rev);
+# 
+# print <<EOF;
+# <tr><td>&nbsp;</td></tr>
+# <tr><td colspan=2><input value="Reload page" type="submit"> with new settings</td></tr>
+# EOF
+# 
+# print "</table></form></div>\n";
 
 print "<hr>\n";
 print "<p>$tail_html";
 
 print "</body></html>\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: <em class=\"severity\">$status{severity}</em>;\n";
-    } else {
-        $showseverity = "Severity: <em>$status{severity}</em>;\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 .= '<strong>fixed</strong>: ';
-        my @fixed = @{$status{fixed_versions}};
-        $showversions .= join ', ', map {s{/}{ }; html_escape($_)} @fixed;
-    }
-    $result .= ' (<a href="'.
-        version_url(package => $status{package},
-                    found   => $status{found_versions},
-                    fixed   => $status{fixed_versions},
-                   ).qq{">$showversions</a>)} 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: <strong>" 
-                 . html_escape(join(", ", sort(split(/\s+/, $status{tags}))))
-                 . "</strong>"
-                       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 .= "<br><strong>Done:</strong> " . 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 .= ";\n<strong>Can be archived" . ( $days == 0 ? " today" : $days == 1 ? " in $days day" : " in $days days" ) . "</strong>";
-        }
-       elsif (defined $status{location} and $status{location} eq 'archived') {
-            $result .= ";\n<strong>Archived.</strong>";
-       }
-    }
-
-    unless (length($status{done})) {
-        if (length($status{forwarded})) {
-            $result .= ";\n<strong>Forwarded</strong> 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 = "</$font>" 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 = "</$font>" 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 = "<h2 class=\"outstanding\">Summary</h2>\n";
-
-    my @dummy = ($gRemoveAge); #, @gSeverityList, @gSeverityDisplay);  #, $gHTMLExpireNote);
-
-    if (@bugs == 0) {
-        return "<HR><H2>No reports found!</H2></HR>\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 "<li><a href=\"%s\">#%d: %s</a>\n<br>",
-            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 .= "<UL class=\"bugs\">\n" . join("", map( { $_->[ 2 ] } @status ) ) . "</UL>\n";
-    } else {
-        $header .= "<div class=\"msgreceived\">\n<ul>\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 .= "<li><a href=\"#$order\">$title</a> ($count $bugs)</li>\n";
-            if ($common{show_list_header}) {
-                my $count = $count{"_$order"};
-                my $bugs = $count == 1 ? "bug" : "bugs";
-                $result .= "<H2 CLASS=\"outstanding\"><a name=\"$order\"></a>$title ($count $bugs)</H2>\n";
-            } else {
-                $result .= "<H2 CLASS=\"outstanding\">$title</H2>\n";
-            }
-            $result .= "<div class=\"msgreceived\">\n<UL class=\"bugs\">\n";
-           $result .= "\n\n\n\n";
-            $result .= $section{$order};
-           $result .= "\n\n\n\n";
-            $result .= "</UL>\n</div>\n";
-        } 
-        $header .= "</ul></div>\n";
-
-        $footer .= "<div class=\"msgreceived\">\n<ul>\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 .= "<li>$count $title[$i]->[$key]</li>\n";
-            }
-            if ( $local_result ) {
-                $footer .= "<li>$names[$i]<ul>\n$local_result</ul></li>\n";
-            }
-        }
-        $footer .= "</ul>\n</div>\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 ? '<strong>' : '';
-    my $closestrong = $strong ? '</strong>' : '';
-
-    return 'Package' . (@pkglist > 1 ? 's' : '') . ': ' .
-           join(', ',
-                map {
-                    '<a class="submitter" href="' . munge_url($this,src=>[],package=>$_,newest=>[]) . '">' .
-                    $openstrong . html_escape($_) . $closestrong . '</a>'
-                } @pkglist
-           );
-}
-
-sub pkg_javascript {
-    return <<EOF ;
-<script type="text/javascript">
-<!--
-function pagemain() {
-       toggle(1);
-//     toggle(2);
-       enable(1);
-}
-
-function setCookie(name, value, expires, path, domain, secure) {
-  var curCookie = name + "=" + escape(value) +
-      ((expires) ? "; expires=" + expires.toGMTString() : "") +
-      ((path) ? "; path=" + path : "") +
-      ((domain) ? "; domain=" + domain : "") +
-      ((secure) ? "; secure" : "");
-  document.cookie = curCookie;
-}
-
-function save_cat_cookies() {
-  var cat = document.categories.categorisation.value;
-  var exp = new Date();
-  exp.setTime(exp.getTime() + 10 * 365 * 24 * 60 * 60 * 1000);
-  var oldexp = new Date();
-  oldexp.setTime(oldexp.getTime() - 1 * 365 * 24 * 60 * 60 * 1000);
-  var lev;
-  var done = 0;
-
-  var u = document.getElementById("users");
-  if (u != null) { u = u.value; }
-  if (u == "") { u = null; }
-  if (u != null) {
-      setCookie("cat" + cat + "_users", u, exp, "/");
-  } else {
-      setCookie("cat" + cat + "_users", "", oldexp, "/");
-  }
-
-  var bits = new Array("nam", "pri", "ttl", "ord");
-  for (var i = 0; i < 4; i++) {
-      for (var j = 0; j < bits.length; j++) {
-          var e = document.getElementById(bits[j] + i);
-         if (e) e = e.value;
-         if (e == null) { e = ""; }
-         if (j == 0 && e == "") { done = 1; }
-         if (done || e == "") {
-              setCookie("cat" + cat + "_" + bits[j] + i, "", oldexp, "/");
-         } else {
-              setCookie("cat" + cat + "_" + bits[j] + i, e, exp, "/");
-         }
-      }
-  }
-}
-
-function toggle(i) {
-        var a = document.getElementById("a_" + i);
-        if (a) {
-             if (a.style.display == "none") {
-                     a.style.display = "";
-             } else {
-                     a.style.display = "none";
-             }
-        }
-}
-
-function enable(x) {
-    for (var i = 1; ; i++) {
-        var a = document.getElementById("b_" + x + "_" + i);
-        if (a == null) break;
-        var ischecked = a.checked;
-        for (var j = 1; ; j++) {
-            var b = document.getElementById("b_" + x + "_"+ i + "_" + j);
-            if (b == null) break;
-            if (ischecked) {
-                b.disabled = false;
-            } else {
-                b.disabled = true;
-            }
-        }
-    }
-}
--->
-</script>
-EOF
-}
-
-sub pkg_htmlselectyesno {
-    my ($name, $n, $y, $default) = @_;
-    return sprintf('<select name="%s"><option value=no%s>%s</option><option value=yes%s>%s</option></select>', $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 '<select name=dist id="%s">', $id;
-    for my $s (@suites) {
-        $result .= sprintf '<option value="%s"%s>%s%s</option>',
-                $s, ($defaultsuite eq $s ? " selected" : ""),
-                $s, (defined $suiteaka{$s} ? " (" . $suiteaka{$s} . ")" : "");
-    }
-    $result .= '</select>';
-    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 '<select name=arch id="%s">', $id;
-    $result .= '<option value="any">any architecture</option>';
-    for my $a (@arches) {
-        $result .= sprintf '<option value="%s">%s</option>', $a, $a;
-    }
-    $result .= '</select>';
-    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];
-    }
-}
index abe4f580a383af1f7c0ac5ab1946a4b170586dbc..268317bb774f5f9031fe2116ab8faa8f3ecba759 100644 (file)
@@ -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 (file)
index 0000000..f801df9
--- /dev/null
@@ -0,0 +1,128 @@
+<script type="text/javascript">
+<!--
+toggle_extra_status_visible();
+function pagemain() \{
+       toggle(1);
+//     toggle(2);
+       enable(1);
+\}
+
+var visible_extra_status = 0;
+
+function toggle_extra_status_visible() \{
+  all_divs = document.getElementsByTagName("div");
+  for (var i = 0; i < all_divs.length; i++) \{
+      if (all_divs[i].className == "shortbugstatusextra") \{
+         if (all_divs[i].style.position == 'absolute' ) \{
+           all_divs[i].style.position = "static";
+           all_divs[i].style.display = "block";
+           all_divs[i].style.zIndex = 0;
+           all_divs[i].style.border = 0;
+           var subspans = all_divs[i].getElementsByTagName("span");
+           for (var j = 0; j < subspans.length; j++) \{
+               subspans[j].style.display = "inline";
+           \}
+         \}
+        else \{
+           all_divs[i].style.position = "absolute";
+           all_divs[i].style.display = "none"
+           all_divs[i].style.zIndex = 1;
+           all_divs[i].style.border = "#000 1px solid";
+           var subspans = all_divs[i].getElementsByTagName("span");
+           for (var j = 0; j < subspans.length; j++) \{
+               subspans[j].style.display = "block";
+           \}
+         \}
+      \}
+  \}
+\}
+
+function extra_status_visible(id) \{
+  if (visible_extra_status) \{
+     var t = document.getElementById("extra_status_"+visible_extra_status);
+     t.style.display = "none";
+     if (visible_extra_status == id) \{
+       visible_extra_status = 0;
+        return;
+     \}
+     visible_extra_status = 0;
+  \}
+  var e = document.getElementById("extra_status_"+id);
+  if (e) \{
+     e.style.display = "block";
+     visible_extra_status = id;
+  \}
+\}
+
+function setCookie(name, value, expires, path, domain, secure) \{
+  var curCookie = name + "=" + escape(value) +
+      ((expires) ? "; expires=" + expires.toGMTString() : "") +
+      ((path) ? "; path=" + path : "") +
+      ((domain) ? "; domain=" + domain : "") +
+      ((secure) ? "; secure" : "");
+  document.cookie = curCookie;
+\}
+
+function save_cat_cookies() \{
+  var cat = document.categories.categorisation.value;
+  var exp = new Date();
+  exp.setTime(exp.getTime() + 10 * 365 * 24 * 60 * 60 * 1000);
+  var oldexp = new Date();
+  oldexp.setTime(oldexp.getTime() - 1 * 365 * 24 * 60 * 60 * 1000);
+  var lev;
+  var done = 0;
+
+  var u = document.getElementById("users");
+  if (u != null) \{ u = u.value; \}
+  if (u == "") \{ u = null; \}
+  if (u != null) \{
+      setCookie("cat" + cat + "_users", u, exp, "/");
+  \} else \{
+      setCookie("cat" + cat + "_users", "", oldexp, "/");
+  \}
+
+  var bits = new Array("nam", "pri", "ttl", "ord");
+  for (var i = 0; i < 4; i++) \{
+      for (var j = 0; j < bits.length; j++) \{
+          var e = document.getElementById(bits[j] + i);
+         if (e) e = e.value;
+         if (e == null) \{ e = ""; \}
+         if (j == 0 && e == "") \{ done = 1; \}
+         if (done || e == "") \{
+              setCookie("cat" + cat + "_" + bits[j] + i, "", oldexp, "/");
+         \} else \{
+              setCookie("cat" + cat + "_" + bits[j] + i, e, exp, "/");
+         \}
+      \}
+  \}
+\}
+
+function toggle(i) \{
+        var a = document.getElementById("a_" + i);
+        if (a) \{
+             if (a.style.display == "none") \{
+                     a.style.display = "";
+             \} else \{
+                     a.style.display = "none";
+             \}
+        \}
+\}
+
+function enable(x) \{
+    for (var i = 1; ; i++) \{
+        var a = document.getElementById("b_" + x + "_" + i);
+        if (a == null) break;
+        var ischecked = a.checked;
+        for (var j = 1; ; j++) \{
+            var b = document.getElementById("b_" + x + "_"+ i + "_" + j);
+            if (b == null) break;
+            if (ischecked) \{
+                b.disabled = false;
+            \} else \{
+                b.disabled = true;
+            \}
+        \}
+    \}
+\}
+-->
+</script>
diff --git a/templates/en_US/cgi/pkgreport_options.tmpl b/templates/en_US/cgi/pkgreport_options.tmpl
new file mode 100644 (file)
index 0000000..7fa264a
--- /dev/null
@@ -0,0 +1,83 @@
+<form method="GET">
+<input type="hidden" name="_fo_combine_key_fo_searchkey_value_fo_searchvalue" value="1">
+<input type="hidden" name="form_options" value="1">
+<table class="forms">
+<tr><td><h2>Select bugs</h2>
+</td>
+<td>
+{ 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 .= '<br>';
+          $value_index++;
+        }
+   }
+ }
+ $search = '';
+ $search_value = '';
+ $output;
+}
+{include('cgi/pkgreport_options_search_key')}
+</td>
+<td>
+<p>The same search fields are ORed, different fields are ANDed.</p>
+<p>Valid severities are {$config{show_severities}}</p>
+<p>Valid tags are {join(', ',@{$config{tags}})}</p>
+</td>
+</tr>
+<tr><td><h2>Include Bugs</h2></td>
+<td>{our $incexc = 'include';
+include('cgi/pkgreport_options_include_exclude');
+}</td>
+<td></td>
+</tr>
+<tr><td><h2>Exclude Bugs</h2></td>
+<td>
+{our $incexc = 'exclude';
+include('cgi/pkgreport_options_include_exclude');
+}
+</td>
+<td></td>
+</tr>
+<tr><td><h2>Categorize using</h2></td>
+<td></td>
+</tr>
+<tr><td><h2>Order by</h2></td>
+<td><select name="ordering">{ my $output = '';
+  my @orderings = qw(normal oldview raw age);
+  for my $order (@orderings) {
+    $output .= '<option value="'.$order.'"'.(($order eq $param{ordering})?' selected':'').
+     ">$order</option>\n";
+  }
+$output;
+}</td>
+<td></td>
+</tr>
+<tr><td><h2>Misc options</h2></td>
+<td>
+<input type="checkbox" name="repeatmerged" {exists $param{repeatmerged} and $param{repeatmerged}?' checked':''}> Repeat Merged<br>
+<input type="checkbox" name="bug-rev" {exists $param{"bug-rev"} and $param{"bug-rev"}?' checked':''}> Reverse Bugs<br>
+<input type="checkbox" name="pend-rev" {exists $param{"pend-rev"} and $param{"pend-rev"}?' checked':''}> Reverse Pending<br>
+<input type="checkbox" name="sev-rev" {exists $param{"sev-rev"} and $param{"sev-rev"}?' checked':''}> Reverse Severity<br>
+<select name="archive">
+{output_select_options([0 => 'Unarchived',
+                        1 => 'Archived', 
+                        both => 'Archived and Unarchived',
+                       ],$param{archive}||0)
+}</select><br>
+<a href="javascript:toggle_extra_status_visible()">Toggle all extra information</a>
+</td>
+</tr>
+<tr><td><h2>Submit</h2></td><td colspan=2>
+<input type="submit" name="submit" value="Submit">
+</td></tr>
+</table>
+
+
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 (file)
index 0000000..c0f8acd
--- /dev/null
@@ -0,0 +1,16 @@
+<input type="hidden" name="_fo_concatenate_into_{$incexc}_fo_{$incexc}key_fo_{$incexc}value" value="1">
+{ 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 (file)
index 0000000..da67c30
--- /dev/null
@@ -0,0 +1,14 @@
+<nobr><select name="_fo_{$incexc}key">
+{output_select_options([subject => 'with subject containing',
+                               tags => 'tagged',
+                       severity => 'with severity',
+                        pending  => 'with pending state',
+                        originator => 'with submitter containing',
+                       forwarded  => 'with forwarded containing',
+                       owner      => 'with owner containing',
+                       package    => 'with package',
+                       ],$key1||'')}
+</select>
+<input type="text" name="_fo_{$incexc}value" value ="{$key2||''}">
+<!-- {$value_index} -->
+</nobr>
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 (file)
index 0000000..1c2ecd9
--- /dev/null
@@ -0,0 +1,6 @@
+<nobr><select name="_fo_searchkey">
+{output_select_options(\@search_key_order,$search||'')}
+</select>
+<input type="text" name="_fo_searchvalue" value ="{$search_value||''}">
+<!-- {$value_index} -->
+</nobr>
diff --git a/templates/en_US/cgi/short_bug_status.tmpl b/templates/en_US/cgi/short_bug_status.tmpl
new file mode 100644 (file)
index 0000000..fb77940
--- /dev/null
@@ -0,0 +1,185 @@
+<div class="shortbugstatus">
+  <a href="{html_escape(bug_links(bug=>$status{bug_num},links_only=>1))}"{length($status{done})?' style="text-decoration:line-through"':''}>#{html_escape($status{bug_num})}</a>
+  [<font face="fixed"><a href="javascript:extra_status_visible({html_escape($status{bug_num})})">{
+  my $output = qq(<span title="$status{severity}">);
+  my $temp = $status{severity};
+  $temp = substr $temp,0,1;
+  if (isstrongseverity($status{severity})){
+     $temp = q(<em class="severity">).uc($temp).q(</em);
+  }
+  $output .= $temp.qq(</span>);
+  $output;
+  }|{
+  my $output = '';
+  for my $tag (@{$status{tags_array}}) {
+     next unless exists $config{tags_single_letter}{$tag};
+     $output .= q(<span title=").$tag.q(">).$config{tags_single_letter}{$tag}.q(</span>);
+  }
+  $output;
+  }|{
+  my $output = '';
+  if (@{$status{mergedwith_array}}) {
+     $output .= qq(<span title="merged">=</span>);
+  }
+  if (@{$status{fixed_versions}}) {
+     $output .= qq(<span title="fixed versions">☺</span>);
+  }
+  if (@{$status{blockedby_array}}) {
+     $output .= qq(<span title="blocked by">┫</span>);
+  }
+  if (@{$status{blocks_array}}) {
+     $output .= qq(<span title="blocks">┣</span>);
+  }
+  if (length($status{forwarded})) {
+     $output .= qq(<span title="forwarded">↝</span>);
+  }
+  if ($status{archived}) {
+     $output .= qq(<span title="archived">♲</span>);
+  }
+  $output;
+  }</a></font>]
+  [{package_links(package=>$status{package},options=>\%options,class=>"submitter")}]
+  <a href="{html_escape(bug_links(bug=>$status{bug_num},links_only=>1))}">{html_escape($status{subject})}</a>
+  <div id="extra_status_{html_escape($status{bug_num})}" class="shortbugstatusextra">
+  <!--<div style="float:right">[<a href="javascript:extra_status_visible(0)">X</a>]</div>-->
+  <span>Reported by: {package_links(submitter=>$status{originator})};</span>
+  <span>Date: {$status{date_text}};</span>
+{ my $output = ''; 
+  if (defined $status{owner} and length $status{owner}) {
+     $output = q(<span>Owned by: ).package_links(owner=>$status{owner}).q(;</span>);
+  }
+  $output;
+}
+<span>Severity: {my $output = $status{severity};
+              if (isstrongseverity($status{severity})) {
+                   $output = q(<em class="severity">).$status{severity}.q(</em>);
+              }
+              $output;
+             };</span>
+<span>{@{$status{tags_array}}?q(Tags: ).html_escape(join(q(, ),@{$status{tags_array}})).';':''}</span>
+{my $output = '';
+ if (@{$status{mergedwith_array}}) {
+    $output .= q(<span>Merged with ).join(qq(,\n),bug_links(bug=>$status{mergedwith_array})).qq(;</span>\n);
+ }
+ $output;
+}
+{my $output = '';
+ if (@{$status{found_versions}} or @{$status{fixed_versions}}) {
+    $output .= '<a href="'.
+         version_url(package => $status{package},
+                     found   => $status{found_versions},
+                     fixed   => $status{fixed_versions},
+                    ).'") ';
+ }
+ if (@{$status{found_versions}}) {
+    $output .= q(<span>Found in );
+    $output .= (@{$status{found_versions}} == 1) ? 'version ' : 'versions ';
+    $output .= join(qq(, ),map {html_escape($_);} @{$status{found_versions}}).qq(;</span>\n);
+ }
+ if (@{$status{fixed_versions}}) {
+    $output .= q(<span>Fixed in );
+    $output .= (@{$status{fixed_versions}} == 1) ? 'version ' : 'versions ';
+    $output .= join(qq(, ),map {html_escape($_);} @{$status{fixed_versions}}).qq(;</span>\n);
+ }
+ if (@{$status{found_versions}} or @{$status{fixed_versions}}) {
+    $output .= qq(</a>);
+ }
+ $output;
+}
+{ my $output = '';
+  if (length($status{done})) {
+     $output .= q(<span><strong>Done:</strong> ).html_escape($status{done}).q(;</span> )
+  }
+  $output;
+}
+{ my $output = '';
+  if (@{$status{blockedby_array}}) {
+     $output .= q(<span>Fix blocked by ).
+        join(q(, ),
+         map {bug_links(bug=>$_->{bug_num}).q(: ).html_escape($_->{subject})}
+         @{$status{blockedby_array}}).q(;</span> )
+  }
+  if (@{$status{blocks_array}}) {
+     $output .= q(<span>Blocking fix for ).
+        join(q(, ),
+         map {bug_links(bug=>$_->{bug_num}).q(: ).html_escape($_->{subject})}
+         @{$status{blocks_array}}).q(;</span> )
+  }
+  $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 = "</$font>" if ($font);
+   $font = "<$font>" if ($font);
+
+   $output .= "<span>${font}Filed $eng ago$efont;</span>\n";
+ }
+ if ($days_last > 7) {
+   my $font = "";
+   my $efont = "";
+   $font = "em" if ($days_last > 30);
+   $font = "strong" if ($days_last > 60);
+   $efont = "</$font>" if ($font);
+   $font = "<$font>" if ($font);
+
+   $output .= "<span>${font}Modified $eng_last ago$efont;</span>\n";
+ }
+ $output;
+ }{ my $output = '';
+  if (exists $status{archived} and $status{archived}) {
+     $output .= q(<span>Bug is archived. No further changes may be made.<span> )
+  }
+  $output}
+<!--  <p>Package: {package_links(package=>$status{package},options=>\%options,class=>"submitter")}
+  {my $output = ''; my $showversions = '';
+   if (@{$status{found_versions}}) {
+     my @found = @{$status{found_versions}};
+     $showversions .= join ', ', map {html_escape($_)} @found;
+   }
+   if (@{$status{fixed_versions}}) {
+     $showversions .= '; ' if length $showversions;
+     $showversions .= '<strong>fixed</strong>: ';
+     my @fixed = @{$status{fixed_versions}};
+     $showversions .= join ', ', map {html_escape($_)} @fixed;
+   }
+   $output .= ' (<a href="'.
+         version_url(package => $status{package},
+                     found   => $status{found_versions},
+                     fixed   => $status{fixed_versions},
+                    ).qq{">$showversions</a>)} if length $showversions;
+   $output
+  };
+    Severity: {my $output = $status{severity};
+               if (isstrongseverity($status{severity})) {
+                  $output = q(<em class="severity">).$status{severity}.q(</em>);
+               }
+               $output;
+              };
+    Reported by: {package_links(submitter=>$status{originator},options=>\%options,class=>"submitter")}<a class="submitter" href="pkgreport.cgi?submitter=kbloom%40gmail.com">Ken Bloom &lt;kbloom@gmail.com&gt;</a>;
+    {@{$status{tags_array}}?q(Tags: ).html_escape(join(q(, ),@{$status{tags_array}})).q(; ):''}
+    {my $output = '';
+     if (@{$status{mergedwith_array}}) {
+        $output .= q(Merged with ).join(qq(,\n),bug_links(bug=>$status{mergedwith_array})).q(; )
+     }
+     $output;
+    }
+    { my $output = '';
+     if (@{$status{blockedby_array}}) {
+        $output .= q(Blocked by ).
+          bug_links(bug=>$status{blockedby_array}).q(; );
+     }
+     if (@{$status{blocks_array}}) {
+        $output .= q(Blocking ).
+         bug_links(bug=>$status{blocks_array}).q(; );
+     }
+     $output;
+    }
+    <strong>2 years and 238 days old</strong>;
+    <strong>Modified 1 year and 34 days ago</strong>.</p>--></div>
+</div>