]> git.ipfire.org Git - thirdparty/bugzilla.git/commitdiff
Fix for bug 115369: Templatization of long_list.cgi
authorjustdave%syndicomm.com <>
Wed, 13 Feb 2002 23:34:33 +0000 (23:34 +0000)
committerjustdave%syndicomm.com <>
Wed, 13 Feb 2002 23:34:33 +0000 (23:34 +0000)
Patch by Gervase Markham <gerv@mozilla.org>
r= bbaetz, dkl

globals.pl
long_list.cgi
template/default/show/comments.tmpl [new file with mode: 0644]
template/default/show/multiple.tmpl [new file with mode: 0644]

index e6bd2d7046500777a7f6798350727983c898e684..b096658bdf313287578df5c21070ceec2abd8e0b 100644 (file)
@@ -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/\&#013;/\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
@@ -1568,6 +1585,182 @@ sub trim {
 # 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.
+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(
index 552457b0625f7e0e1bd1bb060fa3107bda1b584e..479fbcd4a7c54cfc9aad6f6e29bb2d4448fb2aef 100755 (executable)
 # 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' => \&quoteUrls,
+    '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>&nbsp;";
-        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 (file)
index 0000000..77c621a
--- /dev/null
@@ -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 (file)
index 0000000..de5e6c2
--- /dev/null
@@ -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>&nbsp;
+        [% bug.component %]
+      </td>
+    
+      <td colspan="2">
+      [% IF Param('usetargetmilestone') %]
+          <b>Target Milestone:</b>&nbsp;
+          [% bug.target_milestone %]
+      [% END %]
+      </td>
+    </tr>
+    
+    <tr>
+      <td colspan="4">
+        <b>URL:</b>&nbsp;
+        <A HREF="[% bug.bug_file_loc %]">[% bug.bug_file_loc FILTER html %]</a> 
+    </tr>
+    
+    <tr>
+      <td colspan="4">
+        <b>Summary:</b>&nbsp;[% bug.short_desc %]
+      </td>
+    </tr>
+     
+    [% IF use_keywords %]
+      <tr>
+        <td colspan="4">
+          <b>Keywords: </b>&nbsp;[% bug.keywords %]
+        </td>
+      </tr>
+    [% END %]
+
+    [% IF Param("usestatuswhiteboard") %]
+      <tr>
+        <td colspan="4">
+          <b>Status Whiteboard:</b>&nbsp;
+          [% 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>&nbsp;
+    [% bug.${attr.name} %]
+  </td>
+[% END %]