diff options
-rw-r--r-- | globals.pl | 219 | ||||
-rwxr-xr-x | long_list.cgi | 161 | ||||
-rw-r--r-- | template/default/show/comments.tmpl | 50 | ||||
-rw-r--r-- | template/default/show/multiple.tmpl | 154 |
4 files changed, 492 insertions, 92 deletions
diff --git a/globals.pl b/globals.pl index e6bd2d704..b096658bd 100644 --- a/globals.pl +++ b/globals.pl @@ -986,7 +986,7 @@ sub detaint_natural { # expressions. sub quoteUrls { - my ($knownattachments, $text) = (@_); + my ($text) = (@_); return $text unless $text; my $base = Param('urlbase'); @@ -994,8 +994,6 @@ sub quoteUrls { my $protocol = join '|', qw(afs cid ftp gopher http https mid news nntp prospero telnet wais); - my %options = ( metachars => 1, @_ ); - my $count = 0; # Now, quote any "#" characters so they won't confuse stuff later @@ -1046,9 +1044,9 @@ sub quoteUrls { $item = GetBugLink($num, $item); $things[$count++] = $item; } - while ($text =~ s/\battachment(\s|%\#)*(\d+)/"##$count##"/ei) { + while ($text =~ s/\b(Created an )?attachment(\s|%\#)*(\(id=)?(\d+)\)?/"##$count##"/ei) { my $item = $&; - my $num = $2; + my $num = $4; $item = value_quote($item); # Not really necessary, since we know # there's no special chars in it. $item = qq{<A HREF="attachment.cgi?id=$num&action=view">$item</A>}; @@ -1062,14 +1060,6 @@ sub quoteUrls { $item =~ s@\d+@$bug_link@; $things[$count++] = $item; } - while ($text =~ s/Created an attachment \(id=(\d+)\)/"##$count##"/e) { - my $item = $&; - my $num = $1; - if ($knownattachments->{$num}) { - $item = qq{<A HREF="attachment.cgi?id=$num&action=view">$item</A>}; - } - $things[$count++] = $item; - } $text = value_quote($text); $text =~ s/\
/\n/g; @@ -1233,6 +1223,33 @@ sub GetLongDescriptionAsHTML { return $result; } + +sub GetComments { + my ($id) = (@_); + my @comments; + + SendSQL("SELECT profiles.realname, profiles.login_name, + date_format(longdescs.bug_when,'%Y-%m-%d %H:%i'), + longdescs.thetext + FROM longdescs, profiles + WHERE profiles.userid = longdescs.who + AND longdescs.bug_id = $id + ORDER BY longdescs.bug_when"); + + while (MoreSQLData()) { + my %comment; + ($comment{'name'}, $comment{'email'}, $comment{'time'}, $comment{'body'}) = FetchSQLData(); + + $comment{'email'} .= Param('emailsuffix'); + $comment{'name'} = $comment{'name'} || $comment{'email'}; + + push (@comments, \%comment); + } + + return \@comments; +} + + # Fills in a hashtable with info about the columns for the given table in the # database. The hashtable has the following entries: # -list- the list of column names @@ -1570,6 +1587,182 @@ use Template; # Create the global template object that processes templates and specify # configuration parameters that apply to all templates processed in this script. +our $template = Template->new( + { + # Colon-separated list of directories containing templates. + INCLUDE_PATH => "template/custom:template/default" , + + # Allow templates to be specified with relative paths. + RELATIVE => 1 , + + # Remove white-space before template directives (PRE_CHOMP) and at the + # beginning and end of templates and template blocks (TRIM) for better + # looking, more compact content. Use the plus sign at the beginning + # of directives to maintain white space (i.e. [%+ DIRECTIVE %]). + PRE_CHOMP => 1 , + TRIM => 1 , + + # Functions for processing text within templates in various ways. + FILTERS => + { + # Render text in strike-through style. + strike => sub { return "<strike>" . $_[0] . "</strike>" } , + } , + } +); + +# Use the Toolkit Template's Stash module to add utility pseudo-methods +# to template variables. +use Template::Stash; + +# Add "contains***" methods to list variables that search for one or more +# items in a list and return boolean values representing whether or not +# one/all/any item(s) were found. +$Template::Stash::LIST_OPS->{ contains } = + sub { + my ($list, $item) = @_; + return grep($_ eq $item, @$list); + }; + +$Template::Stash::LIST_OPS->{ containsany } = + sub { + my ($list, $items) = @_; + foreach my $item (@$items) { + return 1 if grep($_ eq $item, @$list); + } + return 0; + }; + +# Add a "substr" method to the Template Toolkit's "scalar" object +# that returns a substring of a string. +$Template::Stash::SCALAR_OPS->{ substr } = + sub { + my ($scalar, $offset, $length) = @_; + return substr($scalar, $offset, $length); + }; + +# Add a "truncate" method to the Template Toolkit's "scalar" object +# that truncates a string to a certain length. +$Template::Stash::SCALAR_OPS->{ truncate } = + sub { + my ($string, $length, $ellipsis) = @_; + $ellipsis ||= ""; + + return $string if !$length || length($string) <= $length; + + my $strlen = $length - length($ellipsis); + my $newstr = substr($string, 0, $strlen) . $ellipsis; + return $newstr; + }; + +# Define the global variables and functions that will be passed to the UI +# template. Additional values may be added to this hash before templates +# are processed. +our $vars = + { + # Function for retrieving global parameters. + 'Param' => \&Param , + + # Function for processing global parameters that contain references + # to other global parameters. + 'PerformSubsts' => \&PerformSubsts , + }; +my $suppress_used_only_once_warning = $vars; + +sub GetOutputFormats { + # Builds a list of possible output formats for a script by looking for + # format files in the appropriate template directories as specified by + # the template include path and the "sub-directory name" parameter. + + # This function is relevant for scripts with one basic function whose + # results can be represented in multiple formats, f.e. buglist.cgi, + # which has one function (query and display of a list of bugs) that can + # be represented in multiple formats (i.e. html, rdf, xml, etc.). + + # It is *not* relevant for scripts with several functions but only one + # basic output format, f.e. editattachstatuses.cgi, which not only lists + # statuses but also provides adding, editing, and deleting functions. + + # Format files have names that look like NAME_format.EXT.atml, where NAME + # is the name of the format and EXT is the filename extension identifying + # the type of content the format file generates. If the generated content + # gets saved to a file, the name of that file should have the extension + # appended to it. If the content gets sent to the user without being saved + # in a file (f.e. when returned as the response to an HTTP request), + # the content type (in MIME type format) should be looked up from the list + # of types, indexed by extension, in the "localconfig" file. + + my ($subdir) = @_; + + # A set of output format records, indexed by format name, each record + # containing template, extension, and contenttype fields. + my $formats = {}; + + # The list of directories in which we look for templates to process. + my $includepath = $template->context->{ LOAD_TEMPLATES }->[0]->include_path(); + + # Use the Perl module wrapper to the directory manipulation routines. + use IO::Dir; + + foreach my $path (@$includepath) { + my $dirname = $path . "/" . $subdir; + my $dir = new IO::Dir $dirname; + next if !defined $dir; + my $file; + while (defined($file = $dir->read())) { + if ($file =~ /^(.+)_format\.(.+)\.(atml|tmpl)$/ + && $::contenttypes->{$2}) + { + $formats->{$1} = { + 'template' => $file , + 'extension' => $2 , + 'contenttype' => $::contenttypes->{$2} + }; + } + } + } + return $formats; +} + +sub ValidateOutputFormat { + my ($subdir, $name) = @_; + + if ($name eq "default") { + return + { + 'template' => "default_format.html.tmpl" , + 'extension' => "html" , + 'contenttype' => "text/html" + }; + } + + # Get the list of output formats supported by this script. + my $formats = GetOutputFormats($subdir); + + # Validate the output format requested by the user. + if (!$formats->{$name}) { + my $escapedname = html_quote($name); + DisplayError("The <em>$escapedname</em> output format is not + supported by this script. Supported formats are <em>" + . join("</em>, <em>", map(html_quote($_), keys(%$formats))) . + "</em>."); + exit; + } + + # Return the record of information about this output format. + return $formats->{$name}; +} +############################################################################### + +############################################################################### +# Global Templatization Code + +# Use the template toolkit (http://www.template-toolkit.org/) to generate +# the user interface using templates in the "template/" subdirectory. +use Template; + +# Create the global template object that processes templates and specify +# configuration parameters that apply to all templates processed in this script. $::template = Template->new( { # Colon-separated list of directories containing templates. diff --git a/long_list.cgi b/long_list.cgi index 552457b06..479fbcd4a 100755 --- a/long_list.cgi +++ b/long_list.cgi @@ -19,10 +19,11 @@ # Rights Reserved. # # Contributor(s): Terry Weissman <terry@mozilla.org> - +# Gervase Markham <gerv@gerv.net> use diagnostics; use strict; +use lib "."; use lib qw(.); @@ -33,17 +34,43 @@ require "CGI.pl"; sub sillyness { my $zz; - $zz = $::legal_keywords; $zz = $::userid; $zz = $::usergroupset; $zz = %::FORM; } -print "Content-type: text/html\n"; -#Changing attachment to inline to resolve 46897 -#zach@zachlipton.com -print "Content-disposition: inline; filename=bugzilla_bug_list.html\n\n"; -PutHeader ("Full Text Bug Listing"); +# Use the template toolkit (http://www.template-toolkit.org/) to generate +# the user interface (HTML pages and mail messages) using templates in the +# "template/" subdirectory. +use Template; + +# Create the global template object that processes templates and specify +# configuration parameters that apply to all templates processed in this script. +my $template = Template->new( +{ + # Colon-separated list of directories containing templates. + INCLUDE_PATH => "template/custom:template/default", + # Allow templates to be specified with relative paths. + RELATIVE => 1, + PRE_CHOMP => 1, +}); + +# Define the global variables and functions that will be passed to the UI +# template. Individual functions add their own values to this hash before +# sending them to the templates they process. +my $vars = +{ + # Function for retrieving global parameters. + 'Param' => \&Param, + + # Function for processing global parameters that contain references + # to other global parameters. + 'PerformSubsts' => \&PerformSubsts, + + 'quoteUrls' => \"eUrls, + 'time2str' => \&time2str, + 'str2time' => \&str2time, +}; ConnectToDatabase(); quietly_check_login(); @@ -51,79 +78,55 @@ quietly_check_login(); GetVersionTable(); my $generic_query = " -select - bugs.bug_id, - bugs.product, - bugs.version, - bugs.rep_platform, - bugs.op_sys, - bugs.bug_status, - bugs.bug_severity, - bugs.priority, - bugs.resolution, - assign.login_name, - report.login_name, - bugs.component, - bugs.bug_file_loc, - bugs.short_desc, - bugs.target_milestone, - bugs.qa_contact, - bugs.status_whiteboard, - bugs.keywords -from bugs,profiles assign,profiles report -where assign.userid = bugs.assigned_to and report.userid = bugs.reporter and"; - -$::FORM{'buglist'} = "" unless exists $::FORM{'buglist'}; -foreach my $bug (split(/:/, $::FORM{'buglist'})) { - detaint_natural($bug) || next; - SendSQL(SelectVisible("$generic_query bugs.bug_id = $bug", + SELECT bugs.bug_id, bugs.product, bugs.version, bugs.rep_platform, + bugs.op_sys, bugs.bug_status, bugs.resolution, bugs.priority, + bugs.bug_severity, bugs.component, assign.login_name, report.login_name, + bugs.bug_file_loc, bugs.short_desc, bugs.target_milestone, + bugs.qa_contact, bugs.status_whiteboard, bugs.keywords + FROM bugs,profiles assign,profiles report + WHERE assign.userid = bugs.assigned_to AND report.userid = bugs.reporter"; + +my $buglist = $::FORM{'buglist'} || + $::FORM{'bug_id'} || + $::FORM{'id'} || ""; + +my @bugs; + +foreach my $bug_id (split(/[:,]/, $buglist)) { + detaint_natural($bug_id) || next; + SendSQL(SelectVisible("$generic_query AND bugs.bug_id = $bug_id", $::userid, $::usergroupset)); - my @row; - if (@row = FetchSQLData()) { - my ($id, $product, $version, $platform, $opsys, $status, $severity, - $priority, $resolution, $assigned, $reporter, $component, $url, - $shortdesc, $target_milestone, $qa_contact, - $status_whiteboard, $keywords) = (@row); - print "<IMG SRC=\"1x1.gif\" WIDTH=1 HEIGHT=80 ALIGN=LEFT>\n"; - print "<TABLE WIDTH=100%>\n"; - print "<TD COLSPAN=4><TR><DIV ALIGN=CENTER><B><FONT =\"+3\">" . - html_quote($shortdesc) . - "</B></FONT></DIV>\n"; - print "<TR><TD><B>Bug#:</B> <A HREF=\"show_bug.cgi?id=$id\">$id</A>\n"; - print "<TD><B>Product:</B> $product\n"; - print "<TD><B>Version:</B> $version\n"; - print "<TD><B>Platform:</B> $platform\n"; - print "<TR><TD><B>OS/Version:</B> $opsys\n"; - print "<TD><B>Status:</B> $status\n"; - print "<TD><B>Severity:</B> $severity\n"; - print "<TD><B>Priority:</B> $priority\n"; - print "<TR><TD><B>Resolution:</B> $resolution</TD>\n"; - print "<TD><B>Assigned To:</B> $assigned\n"; - print "<TD><B>Reported By:</B> $reporter\n"; - if (Param("useqacontact")) { - my $name = ""; - if ($qa_contact > 0) { - $name = DBID_to_name($qa_contact); - } - print "<TD><B>QA Contact:</B> $name\n"; - } - print "<TR><TD COLSPAN=2><B>Component:</B> $component\n"; - if (Param("usetargetmilestone")) { - print "<TD COLSPAN=2><B>Target Milestone:</B> $target_milestone\n"; - } - print "<TR><TD COLSPAN=6><B>URL:</B> "; - print "<A HREF=\"" . $url . "\">" . html_quote($url) . "</A>\n"; - print "<TR><TD COLSPAN=6><B>Summary:</B> " . html_quote($shortdesc) . "\n"; - if (@::legal_keywords) { - print "<TR><TD><B>Keywords: </B>$keywords</TD></TR>\n"; - } - if (Param("usestatuswhiteboard")) { - print "<TR><TD COLSPAN=6><B>Status Whiteboard:" . - html_quote($status_whiteboard) . "\n"; - } - print "<TR><TD><B>Description:</B>\n</TABLE>\n"; - print GetLongDescriptionAsHTML($bug); - print "<HR>\n"; + my %bug; + my @row = FetchSQLData(); + + foreach my $field ("bug_id", "product", "version", "rep_platform", + "op_sys", "bug_status", "resolution", "priority", + "bug_severity", "component", "assigned_to", "reporter", + "bug_file_loc", "short_desc", "target_milestone", + "qa_contact", "status_whiteboard", "keywords") + { + $bug{$field} = shift @row; + } + + if ($bug{'bug_id'}) { + $bug{'comments'} = GetComments($bug{'bug_id'}); + $bug{'qa_contact'} = $bug{'qa_contact'} > 0 ? + DBID_to_name($bug{'qa_contact'}) : ""; + + push (@bugs, \%bug); } } + +# Add the bug list of hashes to the variables +$vars->{'bugs'} = \@bugs; + +$vars->{'use_keywords'} = 1 if (@::legal_keywords); + +print "Content-type: text/html\n"; +print "Content-disposition: inline; filename=bugzilla_bug_list.html\n\n"; + +# Generate and return the UI (HTML page) from the appropriate template. +$template->process("show/multiple.tmpl", $vars) + || DisplayError("Template process failed: " . $template->error()) + && exit; diff --git a/template/default/show/comments.tmpl b/template/default/show/comments.tmpl new file mode 100644 index 000000000..77c621a74 --- /dev/null +++ b/template/default/show/comments.tmpl @@ -0,0 +1,50 @@ +[%# The contents of this file are subject to the Mozilla Public + # License Version 1.1 (the "License"); you may not use this file + # except in compliance with the License. You may obtain a copy of + # the License at http://www.mozilla.org/MPL/ + # + # Software distributed under the License is distributed on an "AS + # IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + # implied. See the License for the specific language governing + # rights and limitations under the License. + # + # The Original Code is the Bugzilla Bug Tracking System. + # + # The Initial Developer of the Original Code is Netscape Communications + # Corporation. Portions created by Netscape are + # Copyright (C) 1998 Netscape Communications Corporation. All + # Rights Reserved. + # + # Contributor(s): Gervase Markham <gerv@gerv.net> + #%] + +[% DEFAULT start_at = 0 %] +[% count = 0 %] +[% FOREACH comment = bug.comments %] + [% IF count >= start_at %] + [% PROCESS a_comment %] + [% END %] + + [% count = count + 1 %] +[% END %] + + +[%############################################################################%] +[%# Block for individual comments #%] +[%############################################################################%] + +[% BLOCK a_comment %] + [% IF count > 0 %] + <br> + <i>------- Additional Comment + <a name="c[% count %]" href="#c[% count %]">#[% count %]</a> From + <a href="mailto:[% comment.email %]">[% comment.name %]</a> + [%+ comment.time %] ------- + </i> + [% END %] + + <br> + <pre> + [% quoteUrls(comment.body) %] + </pre> +[% END %] diff --git a/template/default/show/multiple.tmpl b/template/default/show/multiple.tmpl new file mode 100644 index 000000000..de5e6c251 --- /dev/null +++ b/template/default/show/multiple.tmpl @@ -0,0 +1,154 @@ +[%# The contents of this file are subject to the Mozilla Public + # License Version 1.1 (the "License"); you may not use this file + # except in compliance with the License. You may obtain a copy of + # the License at http://www.mozilla.org/MPL/ + # + # Software distributed under the License is distributed on an "AS + # IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + # implied. See the License for the specific language governing + # rights and limitations under the License. + # + # The Original Code is the Bugzilla Bug Tracking System. + # + # The Initial Developer of the Original Code is Netscape Communications + # Corporation. Portions created by Netscape are + # Copyright (C) 1998 Netscape Communications Corporation. All + # Rights Reserved. + # + # Contributor(s): Terry Weissman <terry@mozilla.org> + # Gervase Markham <gerv@gerv.net> + #%] + +[% INCLUDE global/header + title = "Full Text Bug Listing" +%] + +[% IF bugs.first %] + [% FOREACH bug = bugs %] + [% PROCESS bug_display %] + [% END %] +[% ELSE %] + <p> + You'd have more luck if you gave me some bug numbers. + </p> +[% END %] + +[% INCLUDE global/footer %] + + +[%###########################################################################%] +[%# Block for an individual bug #%] +[%###########################################################################%] + +[% BLOCK bug_display %] + <img src="1x1.gif" width="1" height="80" align="left"> + <div align="center"> + <b> + <font ="+3">Bug [% bug.bug_id %] - [% bug.short_desc FILTER html %]</font> + </b> + </div> + + <table width="100%"> + <tr> + <td> + <b>Bug#:</b> + <a href="show_bug.cgi?id=[% bug.bug_id %]">[% bug.bug_id %]</a> + </td> + [% PROCESS cell attr = { description => "Product", + name => "product" } %] + [% PROCESS cell attr = { description => "Version", + name => "version" } %] + [% PROCESS cell attr = { description => "Platform", + name => "rep_platform" } %] + </tr> + + <tr> + [% PROCESS cell attr = { description => "OS/Version", + name => "op_sys" } %] + [% PROCESS cell attr = { description => "Status", + name => "bug_status" } %] + [% PROCESS cell attr = { description => "Severity", + name => "bug_severity" } %] + [% PROCESS cell attr = { description => "Priority", + name => "priority" } %] + </tr> + + <tr> + [% PROCESS cell attr = { description => "Resolution", + name => "resolution" } %] + [% PROCESS cell attr = { description => "Assigned To", + name => "assigned_to" } %] + [% PROCESS cell attr = { description => "Reported By", + name => "reporter" } %] + [% IF Param('useqacontact') %] + [% PROCESS cell attr = { description => "QA Contact", + name => "qa_contact" } %] + [% END %] + </tr> + + <tr> + <td colspan="2"> + <b>Component:</b> + [% bug.component %] + </td> + + <td colspan="2"> + [% IF Param('usetargetmilestone') %] + <b>Target Milestone:</b> + [% bug.target_milestone %] + [% END %] + </td> + </tr> + + <tr> + <td colspan="4"> + <b>URL:</b> + <A HREF="[% bug.bug_file_loc %]">[% bug.bug_file_loc FILTER html %]</a> + </tr> + + <tr> + <td colspan="4"> + <b>Summary:</b> [% bug.short_desc %] + </td> + </tr> + + [% IF use_keywords %] + <tr> + <td colspan="4"> + <b>Keywords: </b> [% bug.keywords %] + </td> + </tr> + [% END %] + + [% IF Param("usestatuswhiteboard") %] + <tr> + <td colspan="4"> + <b>Status Whiteboard:</b> + [% bug.status_whiteboard FILTER html %] + </td> + </tr> + [% END %] + + <tr> + <td colspan="4"> + <b>Description:</b> + </td> + </tr> + </table> + + [% PROCESS show/comments.tmpl %] + + <hr> +[% END %] + + +[%###########################################################################%] +[%# Block for standard table cells #%] +[%###########################################################################%] + +[% BLOCK cell %] + <td> + <b>[% attr.description%]:</b> + [% bug.${attr.name} %] + </td> +[% END %] |