]> git.ipfire.org Git - thirdparty/bugzilla.git/commitdiff
Bug 199635 - templatise duplicates.cgi. Patch by gerv, r=myk, bbaetz.
authorgerv%gerv.net <>
Thu, 11 Apr 2002 05:29:57 +0000 (05:29 +0000)
committergerv%gerv.net <>
Thu, 11 Apr 2002 05:29:57 +0000 (05:29 +0000)
duplicates.cgi
template/default/report/duplicates.html.tmpl [new file with mode: 0644]

index 78f29829fc927849bffa8863d75d07300bff41f9..a85eb6bb7aa1e9bd660cbb33accf488ec3bdae6d 100755 (executable)
@@ -25,7 +25,7 @@
 
 use diagnostics;
 use strict;
-use CGI "param";
+
 use AnyDBM_File;
 
 use lib qw(.);
@@ -33,260 +33,156 @@ use lib qw(.);
 require "globals.pl";
 require "CGI.pl";
 
+# Use global templatisation variables.
+use vars qw($template $vars);
+
 ConnectToDatabase(1);
 GetVersionTable();
 
 quietly_check_login();
 
-# Silly used-once warnings
-$::userid = $::userid;
-$::usergroupset = $::usergroupset;
+use vars qw (%FORM $userid $usergroupset @legal_product);
 
 my %dbmcount;
 my %count;
-my $dobefore = 0;
-my $before = "";
 my %before;
 
 # Get params from URL
-
-my $changedsince = 7;     # default one week
-my $maxrows = 100;        # arbitrary limit on max number of rows
-my $sortby = "dup_count"; # default to sorting by dup count
-
-if (defined(param("sortby")))
-{
-  $sortby = param("sortby");
-}
-
-# Check for changedsince param, and see if it's a positive integer
-if (defined(param("changedsince")) && param("changedsince") =~ /^\d{1,4}$/) 
-{
-  $changedsince = param("changedsince");
+sub formvalue {
+    my ($name, $default) = (@_);
+    return $FORM{$name} || $default || "";
 }
 
-# check for max rows param, and see if it's a positive integer
-if (defined(param("maxrows")) && param("maxrows") =~ /^\d{1,4}$/)
-{
-  $maxrows = param("maxrows");
-}
+my $sortby = formvalue("sortby");
+my $changedsince = formvalue("changedsince", 7);
+my $maxrows = formvalue("maxrows", 100);
+my $openonly = formvalue("openonly");
+my $reverse = formvalue("reverse");
+my $product = formvalue("product");
+my $sortvisible = formvalue("sortvisible");
+my @buglist = (split(/[:,]/, formvalue("bug_id")));
 
-# Start the page
-print "Content-type: text/html\n";
-print "\n";
-PutHeader("Most Frequently Reported Bugs");
+# Small backwards-compatibility hack, dated 2002-04-10.
+$sortby = "count" if $sortby eq "dup_count";
 
 # Open today's record of dupes
-my $today = &days_ago(0);
+my $today = days_ago(0);
+my $yesterday = days_ago(1);
 
-if (<data/duplicates/dupes$today*>)
-{
-  dbmopen(%dbmcount, "data/duplicates/dupes$today", 0644) || 
-                            &die_politely("Can't open today's dupes file: $!");
+if (<data/duplicates/dupes$today*>) {
+    dbmopen(%dbmcount, "data/duplicates/dupes$today", 0644) 
+      || DisplayError("Can't open today ($today)'s dupes file: $!")
+      && exit;
+}
+elsif (<data/duplicates/dupes$yesterday*>) {
+    dbmopen(%dbmcount, "data/duplicates/dupes$yesterday", 0644) 
+      || DisplayError("Can't open yesterday ($yesterday)'s dupes file: $!")
+      && exit;
 }
-else
-{
-  # Try yesterday's, then (in case today's hasn't been created yet)
-  $today = &days_ago(1);
-  if (<data/duplicates/dupes$today*>)
-  {
-    dbmopen(%dbmcount, "data/duplicates/dupes$today", 0644) || 
-                        &die_politely("Can't open yesterday's dupes file: $!");
-  }
-  else
-  {
-    &die_politely("There are no duplicate statistics for today ($today) or yesterday.");
-  }
+else {
+    DisplayError("There are no duplicate statistics for today ($today) or
+                yesterday.");
+    exit;
 }
 
 # Copy hash (so we don't mess up the on-disk file when we remove entries)
 %count = %dbmcount;
-my $key;
-my $value;
+
+# Remove all those dupes under the threshold parameter. 
+# We do this, before the sorting, for performance reasons.
 my $threshold = Param("mostfreqthreshold");
 
-# Remove all those dupes under the threshold (for performance reasons)
-while (($key, $value) = each %count)
-{
-  if ($value < $threshold)
-  {
-    delete $count{$key};
-  } 
+while (my ($key, $value) = each %count) {
+    delete $count{$key} if ($value < $threshold);
 }
 
 # Try and open the database from "changedsince" days ago
-$before = &days_ago($changedsince);    
-
-if (<data/duplicates/dupes$before*>) 
-{
-  dbmopen(%before, "data/duplicates/dupes$before", 0644) && ($dobefore = 1);
-}
-
-print Param("mostfreqhtml");
-
+my $dobefore = 0;
 my %delta;
-
-if ($dobefore) 
-{
-  # Calculate the deltas if we are doing a "before"
-  foreach (keys(%count))
-  {
-    $delta{$_} = $count{$_} - $before{$_};
-  }
-}
-
-# Sort, if required
-my @sortedcount;
-
-if    ($sortby eq "delta")
-{
-  @sortedcount = sort by_delta keys(%count);
-}
-elsif ($sortby eq "bug_no")
-{
-  @sortedcount = reverse sort by_bug_no keys(%count);
-}
-elsif ($sortby eq "dup_count")
-{
-  @sortedcount = sort by_dup_count keys(%count);
-}
-
-my $i = 0;
-
-# Produce a string of bug numbers for a Bugzilla buglist.
-my $commabugs = "";
-foreach (@sortedcount) 
-{
-  last if ($i == $maxrows);
-
-  $commabugs .= ($_ . ",");
-  $i++;  
-}
-
-# Avoid having a comma at the end - Bad Things happen.
-chop $commabugs; 
-
-print qq|
-
-<form method="POST" action="buglist.cgi">
-<input type="hidden" name="bug_id" value="$commabugs">
-<input type="hidden" name="order" value="Reuse same sort as last time">
-Give this to me as a <input type="submit" value="Bug List">. (Note: the order may not be the same.)
-</form>
-
-<table BORDER>
-<tr BGCOLOR="#CCCCCC">
-
-<td><center><b>
-<a href="duplicates.cgi?sortby=bug_no&maxrows=$maxrows&changedsince=$changedsince">Bug #</a>
-</b></center></td>
-<td><center><b>
-<a href="duplicates.cgi?sortby=dup_count&maxrows=$maxrows&changedsince=$changedsince">Dupe<br>Count</a>
-</b></center></td>\n|;
-
-if ($dobefore) 
-{
-  print "<td><center><b>
-  <a href=\"duplicates.cgi?sortby=delta&maxrows=$maxrows&changedsince=$changedsince\">Change in
-  last<br>$changedsince day(s)</a></b></center></td>";
-}
-
-print "
-<td><center><b>Component</b></center></td>
-<td><center><b>Severity</b></center></td>
-<td><center><b>Op Sys</b></center></td>
-<td><center><b>Target<br>Milestone</b></center></td>
-<td><center><b>Summary</b></center></td>
-</tr>\n\n";
-
-$i = 0;
-
-foreach (@sortedcount)
-{
-  my $id = $_;
-  SendSQL(SelectVisible("SELECT component, bug_severity, op_sys, target_milestone, short_desc, bug_status, resolution" .
-                 " FROM bugs WHERE bugs.bug_id = $id", $::userid, $::usergroupset));
-  next unless MoreSQLData();
-  my ($component, $severity, $op_sys, $milestone, $summary, $bug_status, $resolution) = FetchSQLData();
-  $summary = html_quote($summary);
-
-  # Show all bugs except those CLOSED _OR_ VERIFIED but not INVALID or WONTFIX.
-  # We want to see VERIFIED INVALID and WONTFIX because common "bugs" which aren't
-  # bugs end up in this state.
-  unless ( ($bug_status eq "CLOSED") || ( ($bug_status eq "VERIFIED") &&
-          ! ( ($resolution eq "INVALID") || ($resolution eq "WONTFIX") ) ) ) {
-    print "<tr>";
-    print '<td><center>';
-    if  ( ($bug_status eq "RESOLVED") || ($bug_status eq "VERIFIED") ) {
-      print "<strike>";
-    }
-    print "<A HREF=\"show_bug.cgi?id=" . $id . "\">";
-    print $id . "</A>";
-    if  ( ($bug_status eq "RESOLVED") || ($bug_status eq "VERIFIED") ) {
-      print "</strike>";
-    }
-    print "</center></td>";
-    print "<td><center>$count{$id}</center></td>";
-    if ($dobefore) 
-    {
-      print "<td><center>$delta{$id}</center></td>";
-    }
-    print "<td>$component</td>\n    ";
-    print "<td><center>$severity</center></td>";
-    print "<td><center>$op_sys</center></td>";
-    print "<td><center>$milestone</center></td>";
-    print "<td>$summary</td>";
-    print "</tr>\n";
-
-    $i++;
-  }
-
-  if ($i == $maxrows)
-  {
-    last;
-  }
-}
-
-print "</table><br><br>";
-PutFooter();
-
-
-sub by_bug_no 
-{
-  return ($a <=> $b);
-}
-
-sub by_dup_count 
-{
-  return -($count{$a} <=> $count{$b});
-}
-
-sub by_delta 
-{
-  return -($delta{$a} <=> $delta{$b});
-}
-
-sub days_ago 
-{
-  my ($dom, $mon, $year) = (localtime(time - ($_[0]*24*60*60)))[3, 4, 5];
-  return sprintf "%04d-%02d-%02d", 1900 + $year, ++$mon, $dom;
-}
-
-sub die_politely {
-  my $msg = shift;
-
-  print <<FIN;
-<p>
-<table border=1 cellpadding=10>
-<tr>
-<td align=center>
-<font color=blue>$msg</font>
-</td>
-</tr>
-</table>
-<p>
-FIN
-    
-  PutFooter();
-  exit;
+my $whenever = days_ago($changedsince);    
+
+if (<data/duplicates/dupes$whenever*>) {
+    dbmopen(%before, "data/duplicates/dupes$whenever", 0644) 
+      || DisplayError("Can't open $changedsince days ago ($whenever)'s " .
+                      "dupes file: $!");
+
+    # Calculate the deltas
+    ($delta{$_} = $count{$_} - $before{$_}) foreach (keys(%count));
+
+    $dobefore = 1;
+}
+
+# Don't add CLOSED, and don't add VERIFIED unless they are INVALID or 
+# WONTFIX. We want to see VERIFIED INVALID and WONTFIX because common 
+# "bugs" which aren't bugs end up in this state.
+my $generic_query = "
+  SELECT component, bug_severity, op_sys, target_milestone,
+         short_desc, bug_status, resolution
+  FROM bugs 
+  WHERE (bug_status != 'CLOSED') 
+  AND   ((bug_status = 'VERIFIED' AND resolution IN ('INVALID', 'WONTFIX')) 
+         OR (bug_status != 'VERIFIED'))
+  AND ";
+
+# Limit to a single product if requested             
+$generic_query .= (" product = " . SqlQuote($product) . " AND ") if $product;
+my @bugs;
+my @bug_ids; 
+my $loop = 0;
+
+foreach my $id (keys(%count)) {
+    # Maximum row count is dealt with in the template.
+    # If there's a buglist, restrict the bugs to that list.
+    next if $sortvisible && $buglist[0] && (lsearch(\@buglist, $id) == -1);
+
+    SendSQL(SelectVisible("$generic_query bugs.bug_id = $id", 
+                           $userid, 
+                           $usergroupset));
+                           
+    next unless MoreSQLData();
+    my ($component, $bug_severity, $op_sys, $target_milestone, 
+        $short_desc, $bug_status, $resolution) = FetchSQLData();
+
+    # Limit to open bugs only if requested
+    next if $openonly && ($resolution ne "");
+
+    push (@bugs, { id => $id,
+                   count => $count{$id},
+                   delta => $delta{$id}, 
+                   component => $component,
+                   bug_severity => $bug_severity,
+                   op_sys => $op_sys,
+                   target_milestone => $target_milestone,
+                   short_desc => $short_desc,
+                   bug_status => $bug_status, 
+                   resolution => $resolution });
+    push (@bug_ids, $id); 
+    $loop++;                
+}
+
+$vars->{'bugs'} = \@bugs;
+$vars->{'bug_ids'} = \@bug_ids;
+
+$vars->{'dobefore'} = $dobefore;
+$vars->{'sortby'} = $sortby;
+$vars->{'sortvisible'} = $sortvisible;
+$vars->{'changedsince'} = $changedsince;
+$vars->{'maxrows'} = $maxrows;
+$vars->{'openonly'} = $openonly;
+$vars->{'reverse'} = $reverse;
+$vars->{'product'} = $product;
+$vars->{'products'} = \@::legal_product;
+
+print "Content-type: text/html\n\n";
+
+# Generate and return the UI (HTML page) from the appropriate template.
+$template->process("report/duplicates.html.tmpl", $vars)
+  || DisplayError("Template process failed: " . $template->error())
+  && exit;
+
+
+sub days_ago {
+    my ($dom, $mon, $year) = (localtime(time - ($_[0]*24*60*60)))[3, 4, 5];
+    return sprintf "%04d-%02d-%02d", 1900 + $year, ++$mon, $dom;
 }
diff --git a/template/default/report/duplicates.html.tmpl b/template/default/report/duplicates.html.tmpl
new file mode 100644 (file)
index 0000000..1f60666
--- /dev/null
@@ -0,0 +1,275 @@
+[%# 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>
+  #%]
+
+[%# INTERFACE:
+  # bugs: list of hashes. May be empty. Each hash has nine members:
+  #   id: integer. The bug number
+  #   count: integer. The number of dupes
+  #   delta: integer. The change in count in the last $changedsince days
+  #   component: string. The bug's component
+  #   bug_severity: string. The bug's severity.
+  #   op_sys: string. The bug's reported OS.
+  #   target_milestone: string. The bug's TM.
+  #   short_desc: string. The bug's summary.
+  #   bug_status: string. The bug's status.
+  #   
+  # bug_ids: list of integers. May be empty. The IDs of the bugs in $bugs.
+  # products: list of strings. The products this user can see.
+  #  
+  # sortby: string. the column on which we are sorting the buglist.
+  # reverse: boolean. True if we are reversing the current sort.
+  # maxrows: integer. Max number of rows to display.
+  # changedsince: integer. The number of days ago for the changedsince column.
+  # openonly: boolean. True if we are only showing open bugs.    
+  # product: string. Restrict to this product only.
+  #%]
+[% IF product %]
+  [% title = "Most Frequently Reported Bugs for $product" %]
+[% ELSE %]   
+  [% title = "Most Frequently Reported Bugs" %]
+[% END%]
+
+[% INCLUDE global/header %]
+
+<p>
+  <a href="#explanation">What is this data?</a>
+  <br>
+  <a href="#params">Change parameters</a>
+</p>
+
+[%# *** Column Headers *** %]
+
+<table border>
+  <tr BGCOLOR="#CCCCCC">
+    [% FOREACH column = [ { name => "id", description => "Bug #" },
+                          { name => "count", description => "Dupe<br>Count" },
+                          { name => "delta", 
+                      description => "Change in last<br>$changedsince day(s)" },
+                          { name => "component", description => "Component" },
+                          { name => "bug_severity", description => "Severity" },
+                          { name => "op_sys", description => "Op Sys" }, 
+                          { name => "target_milestone", 
+                            description => "Target<br>Milestone" },
+                          { name => "short_desc", description => "Summary" } ] 
+     %]
+                          
+      [%# Small hack to keep delta column out if we don't need it %]
+      [% NEXT IF column.name == "delta" AND NOT dobefore %]                     
+
+      <td>
+        <center>
+          <b>
+            [% bug_ids_string = bug_ids.join(',') %]
+            <a href="duplicates.cgi?sortby=[% column.name %]
+              [% "&reverse=1" IF NOT reverse AND sortby == column.name %]
+              [% "&maxrows=$maxrows" IF maxrows %]
+              [% "&changedsince=$changedsince" IF changedsince %]
+              [% "&openonly=1" IF openonly %]
+              [% "&product=$product" IF product %]
+              [% "&bug_id=$bug_ids_string&sortvisible=1" IF sortvisible %]">
+              [% column.description %]</a>
+          </b>
+        </center>
+      </td>
+    [% END %]
+  </tr>
+  
+[% IF NOT sortby %]
+  [% sortby = "count"; reverse = "1" %]
+[% END %]
+
+[% IF sortby == "id" OR sortby == "count" OR sortby == "delta" %]
+  [%# Numeric sort %]
+  [% sortedbugs = bugs.nsort(sortby) %]
+[% ELSE %]
+  [% sortedbugs = bugs.sort(sortby) %]
+[% END %]
+
+[% IF reverse %]
+  [% bugs = sortedbugs.reverse %]
+[% ELSE %]
+  [% bugs = sortedbugs %]
+[% END %]
+
+[%# *** Buglist *** %]
+
+[%# We need to keep track of the bug IDs we are actually displaying, because
+  # if the user decides to sort the visible list, we need to know what that
+  # list actually is. %]
+[% vis_bug_ids = [] %]
+
+[% FOREACH bug = bugs %]
+  [% LAST IF loop.index() >= maxrows %]
+  [% vis_bug_ids.push(bug.id) %]      
+
+  <tr>
+    <td>
+      <center>
+        [% "<strike>" IF bug.resolution != "" %]
+        <A HREF="show_bug.cgi?id=[% bug.id %]">[% bug.id %]</a>
+        [% "</strike>" IF bug.resolution != "" %]
+      </center>
+    </td>
+
+    <td>
+      <center>
+        [% bug.count %]
+      </center>
+    </td>
+
+    [% IF dobefore %]
+      <td><center>[% bug.delta %]</center></td>
+    [% END %]
+
+    <td>[% bug.component %]</td>
+    <td><center>[% bug.bug_severity %]</center></td>
+    <td><center>[% bug.op_sys %]</center></td>
+    <td><center>[% bug.target_milestone %]</center></td>
+    <td>[% bug.short_desc FILTER html %]</td>
+  </tr>
+[% END %]
+
+</table>
+
+<br>
+<br>
+
+[%# *** Parameters *** %]
+
+[% bug_ids_string = vis_bug_ids.join(',') %]
+
+<h3><a name="params">Change Parameters</a></h3>
+
+<form method="get" action="duplicates.cgi">
+  <input type="hidden" name="sortby" value="[% sortby %]" />
+  <input type="hidden" name="reverse" value="[% reverse %]" />
+  <input type="hidden" name="bug_id" value="[% bug_ids_string %]">
+  <table>
+    <tr>
+      <td>When sorting or restricting,
+          work with:</td>
+      <td>
+        <input type="radio" name="sortvisible" id="entirelist" value="0" 
+          [% "checked" IF NOT sortvisible %] />         
+        <label for="entirelist">
+          entire list
+        </label>
+        <br />        
+        <input type="radio" name="sortvisible" id="visiblelist" value="1" 
+          [% "checked" IF sortvisible %] /> 
+        <label for="visiblelist">
+          currently visible list
+        </label>
+      </td>
+      <td rowspan="4" valign="top">Restrict to products:</td>
+      <td rowspan="4" valign="top">
+        <select name="product" size="5" multiple>
+          [% FOREACH p = products %]
+            <option name="[% p %]"
+            [% " selected" IF product == p %]>[% p %]</option>
+          [% END %]
+        </select>
+      </td>
+     </tr>
+
+    <tr>
+      <td>Max rows:</td>
+      <td>
+        <input size="4" name="maxrows" value="[% maxrows %]" />
+      </td>
+    </tr>
+
+    <tr>
+      <td>Change column is change in the last:</td>
+      <td>
+        <input size="4" name="changedsince" value="[% changedsince %]" /> days
+      </td>
+    </tr>
+
+    <tr>
+      <td>
+        <label for="openonly">
+          Open bugs only:
+        </label>
+      </td>
+      <td>
+        <input type="checkbox" name="openonly" id="openonly" value="1" 
+          [% "checked" IF openonly %] />      
+      </td>
+    </tr>
+
+  </table>
+
+  <input type="submit" value="Change" /> 
+</form>
+
+<form method="post" action="buglist.cgi">
+  <input type="hidden" name="bug_id" value="[% bug_ids_string %]">
+  <input type="hidden" name="order" value="Reuse same sort as last time">
+  Or just give this to me as a <input type="submit" value="bug list">. 
+  (Note: the order may not be the same.)
+</form>
+
+<hr />
+
+<b>
+  <a name="explanation">What are "Most Frequently Reported Bugs"?</a>
+</b>
+
+<blockquote>
+  The Most Frequent Bugs page lists the known open bugs which 
+  are reported most frequently in recent builds of Mozilla. It is 
+  automatically generated from the Bugzilla database every 24 hours, by 
+  counting the number of direct and indirect duplicates of bugs.
+  This information is provided in order to assist in minimizing
+  the amount of duplicate bugs entered into Bugzilla which in turn cuts down
+  on development time.
+</blockquote>
+
+<b>How do I use this list?</b>
+
+<ul>
+  <li>Review the most frequent bugs list.</li>
+  <li>If problem is listed:</li>
+
+  <ul>
+    <li>Click on Bug # link to confirm that you have found the same bug and 
+      comment if you have additional information. Or move on with your testing
+      of the product.
+    </li>
+  </ul>
+
+  <li>If problem not listed:</li>
+
+  <ul>
+    <li>Go to the <a href="query.cgi">Bugzilla Search</a>
+      page to try and locate a similar bug that has already been written.</li>
+    <li>If you find your bug in Bugzilla, feel free to comment with any new or
+      additional data you may have.</li>
+    <li>If you cannot find your problem already documented in Bugzilla, go to
+      the
+      <a href="http://www.mozilla.org/quality/help/bug-form.html">Bugzilla
+        Helper</a>
+      and post a new bug.</li>
+  </ul>
+</ul>
+
+[% INCLUDE global/footer %]