use POSIX qw(ceil);
use Storable qw(dclone);
+use List::Util qw(max);
+
use Carp;
use Debbugs::Text qw(fill_in_template);
],
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];
}
else {
%params = @_;
}
+ carp "bug_url is deprecated, use bug_links instead";
+
return munge_url('bugreport.cgi?',%params,bug=>$ref);
}
else {
%params = @_;
}
+ carp "pkg_url is deprecated, use package_links instead";
return munge_url('pkgreport.cgi?',%params);
}
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
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
=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)
=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,
},
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 = '';
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);
html_escape($link_name).q(</a>);
}
}
- return @return;
+ if (wantarray) {
+ return @return;
+ }
+ else {
+ return join($param{separator},@return);
+ }
}
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";
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
--- /dev/null
+# 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__
+
+
+
+
+
+
# 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;
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}){
$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;
delete $param{$incexcmap->{key}};
}
+
my $maxdays = ($param{'maxdays'} || -1);
my $mindays = ($param{'mindays'} || 0);
my $version = $param{'version'} || undef;
"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}),
}
-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};
}
}
+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,
);
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;
# 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})];
else {
#yeah for magick!
@bugs = get_bugs((map {exists $param{$_}?($_,$param{$_}):()}
- keys %search_keys, 'archive'),
+ keys %package_search_keys, 'archive'),
usertags => \%ut,
);
}
$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";
# 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}) {
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> </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> </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> </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> </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> </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> </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];
- }
-}
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;
}
--- /dev/null
+<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>
--- /dev/null
+<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>
+
+
--- /dev/null
+<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;
+}
+
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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 <kbloom@gmail.com></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>