6 Debbugs::CGI -- General routines for the cgi scripts
10 use Debbugs::CGI qw(:url :html);
12 html_escape(bug_url($ref,mbox=>'yes',mboxstatus=>'yes'));
16 This module is a replacement for parts of common.pl; subroutines in
17 common.pl will be gradually phased out and replaced with equivalent
18 (or better) functionality here.
28 use vars qw($VERSION $DEBUG %EXPORT_TAGS @EXPORT_OK @EXPORT);
29 use base qw(Exporter);
32 use Debbugs::Common qw(getparsedaddrs);
33 use Params::Validate qw(validate_with :types);
34 use Debbugs::Config qw(:config);
35 use Debbugs::Status qw(splitpackages);
38 use Storable qw(dclone);
44 ($VERSION) = q$Revision: 1.3 $ =~ /^Revision:\s+([^\s+])/;
45 $DEBUG = 0 unless defined $DEBUG;
48 %EXPORT_TAGS = (url => [qw(bug_url bug_links bug_linklist maybelink),
49 qw(set_url_params pkg_url version_url),
50 qw(submitterurl mainturl)
52 html => [qw(html_escape htmlize_bugs htmlize_packagelinks),
53 qw(maybelink htmlize_addresslinks htmlize_maintlinks),
55 util => [qw(cgi_parameters quitcgi),
58 #status => [qw(getbugstatus)],
61 Exporter::export_ok_tags(qw(url html util));
62 $EXPORT_TAGS{all} = [@EXPORT_OK];
72 Sets the url params which will be used to generate urls.
81 my $url = Debbugs::URI->new($_[0]||'');
82 %URL_PARAMS = %{$url->query_form_hash};
89 bug_url($ref,mbox=>'yes',mboxstat=>'yes');
91 Constructs urls which point to a specific
93 XXX use Params::Validate
102 %params = (%URL_PARAMS,@_);
107 my $url = Debbugs::URI->new('bugreport.cgi?');
108 $url->query_form(bug=>$ref,%params);
109 return $url->as_string;
116 %params = (%URL_PARAMS,@_);
121 my $url = Debbugs::URI->new('pkgreport.cgi?');
122 $url->query_form(%params);
123 return $url->as_string;
128 version_url($package,$found,$fixed)
130 Creates a link to the version cgi script
135 my ($package,$found,$fixed,$width,$height) = @_;
136 my $url = Debbugs::URI->new('version.cgi?');
137 $url->query_form(package => $package,
140 (defined $width)?(width => $width):(),
141 (defined $height)?(height => $height):(),
142 (defined $width or defined $height)?(collapse => 1):(),
144 return $url->as_string;
151 Escapes html entities by calling HTML::Entities::encode_entities;
158 return HTML::Entities::encode_entities($string,q(<>&"'));
161 =head2 cgi_parameters
165 Returns all of the cgi_parameters from a CGI script using CGI::Simple
170 my %options = validate_with(params => \@_,
171 spec => {query => {type => OBJECT,
174 single => {type => ARRAYREF,
177 default => {type => HASHREF,
182 my $q = $options{query};
184 @single{@{$options{single}}} = (1) x @{$options{single}};
186 for my $paramname ($q->param) {
187 if ($single{$paramname}) {
188 $param{$paramname} = $q->param($paramname);
191 $param{$paramname} = [$q->param($paramname)];
194 for my $default (keys %{$options{default}}) {
195 if (not exists $param{$default}) {
196 # We'll clone the reference here to avoid surprises later.
197 $param{$default} = ref($options{default}{$default})?
198 dclone($options{default}{$default}):$options{default}{$default};
207 print "Content-Type: text/html\n\n";
208 print "<HTML><HEAD><TITLE>Error</TITLE></HEAD><BODY>\n";
209 print "An error occurred. Dammit.\n";
210 print "Error was: $msg.\n";
211 print "</BODY></HTML>\n";
220 htmlize_bugs({bug=>1,status=>\%status,extravars=>\%extra},{bug=>...}});
222 Turns a list of bugs into an html snippit of the bugs.
225 # htmlize_bugs(bugs=>[@bugs]);
230 for my $bug (@bugs) {
231 my $html = sprintf "<li><a href=\"%s\">#%d: %s</a>\n<br>",
232 bug_url($bug->{bug}), $bug->{bug}, html_escape($bug->{status}{subject});
233 $html .= htmlize_bugstatus($bug->{status}) . "\n";
239 sub htmlize_bugstatus {
240 my %status = %{$_[0]};
245 if ($status{severity} eq $config{default_severity}) {
247 } elsif (isstrongseverity($status{severity})) {
248 $showseverity = "Severity: <em class=\"severity\">$status{severity}</em>;\n";
250 $showseverity = "Severity: <em>$status{severity}</em>;\n";
253 $result .= htmlize_packagelinks($status{"package"}, 1);
255 my $showversions = '';
256 if (@{$status{found_versions}}) {
257 my @found = @{$status{found_versions}};
259 s{/}{ } foreach @found;
260 $showversions .= join ', ', map html_escape($_), @found;
262 if (@{$status{fixed_versions}}) {
263 $showversions .= '; ' if length $showversions;
264 $showversions .= '<strong>fixed</strong>: ';
265 my @fixed = @{$status{fixed_versions}};
266 $showversions .= join ', ', map {s#/##; html_escape($_)} @fixed;
268 $result .= " ($showversions)" if length $showversions;
271 $result .= $showseverity;
272 $result .= htmlize_addresslinks("Reported by: ", \&submitterurl,
273 $status{originator});
274 $result .= ";\nOwned by: " . html_escape($status{owner})
275 if length $status{owner};
276 $result .= ";\nTags: <strong>"
277 . html_escape(join(", ", sort(split(/\s+/, $status{tags}))))
279 if (length($status{tags}));
281 $result .= ";\nMerged with ".
284 split(/ /,$status{mergedwith}))
285 if length $status{mergedwith};
286 $result .= ";\nBlocked by ".
289 split(/ /,$status{blockedby}))
290 if length $status{blockedby};
291 $result .= ";\nBlocks ".
294 split(/ /,$status{blocks})
296 if length $status{blocks};
299 if (length($status{done})) {
300 $result .= "<br><strong>Done:</strong> " . html_escape($status{done});
301 $days = ceil($debbugs::gRemoveAge - -M buglog($status{id}));
303 $result .= ";\n<strong>Will be archived" . ( $days == 0 ? " today" : $days == 1 ? " in $days day" : " in $days days" ) . "</strong>";
305 $result .= ";\n<strong>Archived</strong>";
309 if (length($status{forwarded})) {
310 $result .= ";\n<strong>Forwarded</strong> to "
311 . maybelink($status{forwarded});
313 my $daysold = int((time - $status{date}) / 86400); # seconds to days
317 $font = "em" if ($daysold > 30);
318 $font = "strong" if ($daysold > 60);
319 $efont = "</$font>" if ($font);
320 $font = "<$font>" if ($font);
322 my $yearsold = int($daysold / 365);
323 $daysold -= $yearsold * 365;
325 $result .= ";\n $font";
327 push @age, "1 year" if ($yearsold == 1);
328 push @age, "$yearsold years" if ($yearsold > 1);
329 push @age, "1 day" if ($daysold == 1);
330 push @age, "$daysold days" if ($daysold > 1);
331 $result .= join(" and ", @age);
332 $result .= " old$efont";
341 =head2 htmlize_packagelinks
345 Given a scalar containing a list of packages separated by something
346 that L<Debbugs::CGI/splitpackages> can separate, returns a
347 formatted set of links to packages.
351 sub htmlize_packagelinks {
352 my ($pkgs,$strong) = @_;
353 return unless defined $pkgs and $pkgs ne '';
354 my @pkglist = splitpackages($pkgs);
357 my $openstrong = $strong ? '<strong>' : '';
358 my $closestrong = $strong ? '</strong>' : '';
360 return 'Package' . (@pkglist > 1 ? 's' : '') . ': ' .
363 '<a class="submitter" href="' . pkg_url(pkg=>$_||'') . '">' .
364 $openstrong . html_escape($_) . $closestrong . '</a>'
373 maybelink('http://foobarbaz,http://bleh',qr/[, ]+/);
374 maybelink('http://foobarbaz,http://bleh',qr/[, ]+/,', ');
377 In the first form, links the link if it looks like a link. In the
378 second form, first splits based on the regex, then reassembles the
379 link, linking things that look like links. In the third form, rejoins
380 the split links with commas and spaces.
385 my ($links,$regex,$join) = @_;
386 $join = ' ' if not defined $join;
389 if (defined $regex) {
390 @segments = split $regex, $links;
393 @segments = ($links);
395 for my $in (@segments) {
396 if ($in =~ /^[a-zA-Z0-9+.-]+:/) { # RFC 1738 scheme
397 push @return, qq{<a href="$in">} . html_escape($in) . '</a>';
399 push @return, html_escape($in);
402 return @return?join($join,@return):'';
406 =head2 htmlize_addresslinks
408 htmlize_addresslinks($prefixfunc,$urlfunc,$addresses,$class);
411 Generate a comma-separated list of HTML links to each address given in
412 $addresses, which should be a comma-separated list of RFC822
413 addresses. $urlfunc should be a reference to a function like mainturl
414 or submitterurl which returns the URL for each individual address.
419 sub htmlize_addresslinks {
420 my ($prefixfunc, $urlfunc, $addresses,$class) = @_;
421 $class = defined $class?qq(class="$class" ):'';
422 if (defined $addresses and $addresses ne '') {
423 my @addrs = getparsedaddrs($addresses);
424 my $prefix = (ref $prefixfunc) ?
425 $prefixfunc->(scalar @addrs):$prefixfunc;
428 { sprintf qq(<a ${class}).
430 $urlfunc->($_->address),
431 html_escape($_->format) ||
437 my $prefix = (ref $prefixfunc) ?
438 $prefixfunc->(1) : $prefixfunc;
439 return sprintf '%s<a '.$class.'href="%s">(unknown)</a>',
440 $prefix, $urlfunc->('');
445 my $addr = getparsedaddrs($_[0] || "");
446 $addr = defined $addr?$addr->address:'';
450 sub mainturl { pkg_url(maint => emailfromrfc822($_[0])); }
451 sub submitterurl { pkg_url(submitter => emailfromrfc822($_[0])); }
452 sub htmlize_maintlinks {
453 my ($prefixfunc, $maints) = @_;
454 return htmlize_addresslinks($prefixfunc, \&mainturl, $maints);
459 our $_maintainer_rev;
463 return $_pseudodesc if $_pseudodesc;
466 my $pseudo = new IO::File $config{pseudo_desc_file},'r'
467 or &quitcgi("Unable to open $config{pseudo_desc_file}: $!");
469 next unless m/^(\S+)\s+(\S.*\S)\s*$/;
470 $pseudodesc{lc $1} = $2;
473 $_pseudodesc = \%pseudodesc;
481 bug_links($starting_bug,$stoping_bugs,);
483 Creates a set of links to bugs, starting with bug number
484 $starting_bug, and finishing with $stoping_bug; if only one bug is
485 passed, makes a link to only a single bug.
487 The content of the link is the bug number.
489 XXX Use L<Params::Validate>; we want to be able to support query
495 my ($start,$stop,$query_arguments) = @_;
496 $stop = $stop || $start;
497 $query_arguments ||= '';
499 for my $bug ($start..$stop) {
500 push @output,'<a href="'.bug_url($bug,'').qq(">$bug</a>);
502 return join(', ',@output);
507 bug_linklist($separator,$class,@bugs)
509 Creates a set of links to C<@bugs> separated by C<$separator> with
510 link class C<$class>.
512 XXX Use L<Params::Validate>; we want to be able to support query
513 arguments here too; we should be able to combine bug_links and this
514 function into one. [Hell, bug_url should be one function with this one
521 my ($sep,$class,@bugs) = @_;
523 $class = qq(class="$class" );
525 return join($sep,map{qq(<a ${class}href=").
526 bug_url($_).qq(">#$_</a>)