Dump of bugs.webkit.org's Bugzilla instance.
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@30048 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/BugsSite/duplicates.cgi b/BugsSite/duplicates.cgi
new file mode 100755
index 0000000..744ad81
--- /dev/null
+++ b/BugsSite/duplicates.cgi
@@ -0,0 +1,286 @@
+#!/usr/bin/perl -wT
+# -*- Mode: perl; indent-tabs-mode: nil -*-
+#
+# 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>
+#
+# Generates mostfreq list from data collected by collectstats.pl.
+
+
+use strict;
+
+use AnyDBM_File;
+
+use lib qw(.);
+
+require "globals.pl";
+require "CGI.pl";
+
+use Bugzilla;
+use Bugzilla::Search;
+use Bugzilla::Config qw(:DEFAULT $datadir);
+use Bugzilla::Constants;
+
+my $cgi = Bugzilla->cgi;
+
+# Go directly to the XUL version of the duplicates report (duplicates.xul)
+# if the user specified ctype=xul. Adds params if they exist, and directs
+# the user to a signed copy of the script in duplicates.jar if it exists.
+if (defined $cgi->param('ctype') && $cgi->param('ctype') eq "xul") {
+ my $params = CanonicaliseParams($cgi->query_string(), ["format", "ctype"]);
+ my $url = (-e "duplicates.jar" ? "duplicates.jar!/" : "") .
+ "duplicates.xul" . ($params ? "?$params" : "") . "\n\n";
+
+ print $cgi->redirect($url);
+ exit;
+}
+
+# Use global templatisation variables.
+use vars qw($template $vars);
+
+GetVersionTable();
+
+# collectstats.pl uses duplicates.cgi to generate the RDF duplicates stats.
+# However, this conflicts with requirelogin if it's enabled; so we make
+# logging-in optional if we are running from the command line.
+if ($::ENV{'GATEWAY_INTERFACE'} eq "cmdline") {
+ Bugzilla->login(LOGIN_OPTIONAL);
+}
+else {
+ Bugzilla->login();
+}
+
+Bugzilla->switch_to_shadow_db();
+
+use vars qw ($userid @legal_product);
+
+my %dbmcount;
+my %count;
+my %before;
+
+# Get params from URL
+sub formvalue {
+ my ($name, $default) = (@_);
+ return $cgi->param($name) || $default || "";
+}
+
+my $sortby = formvalue("sortby");
+my $changedsince = formvalue("changedsince", 7);
+my $maxrows = formvalue("maxrows", 100);
+my $openonly = formvalue("openonly");
+my $reverse = formvalue("reverse") ? 1 : 0;
+my @query_products = $cgi->param('product');
+my $sortvisible = formvalue("sortvisible");
+my @buglist = (split(/[:,]/, formvalue("bug_id")));
+
+my $product_id;
+foreach my $p (@query_products) {
+ $product_id = get_product_id($p);
+ if (!$product_id) {
+ ThrowUserError("invalid_product_name",
+ { product => $p });
+ }
+}
+
+# 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 $yesterday = days_ago(1);
+
+# We don't know the exact file name, because the extention depends on the
+# underlying dbm library, which could be anything. We can't glob, because
+# perl < 5.6 considers if (<*>) { ... } to be tainted
+# Instead, just check the return value for today's data and yesterday's,
+# and ignore file not found errors
+
+use Errno;
+use Fcntl;
+
+if (!tie(%dbmcount, 'AnyDBM_File', "$datadir/duplicates/dupes$today",
+ O_RDONLY, 0644)) {
+ if ($!{ENOENT}) {
+ if (!tie(%dbmcount, 'AnyDBM_File', "$datadir/duplicates/dupes$yesterday",
+ O_RDONLY, 0644)) {
+ my $vars = { today => $today };
+ if ($!{ENOENT}) {
+ ThrowUserError("no_dupe_stats", $vars);
+ } else {
+ $vars->{'error_msg'} = $!;
+ ThrowUserError("no_dupe_stats_error_yesterday", $vars);
+ }
+ }
+ } else {
+ ThrowUserError("no_dupe_stats_error_today",
+ { error_msg => $! });
+ }
+}
+
+# Copy hash (so we don't mess up the on-disk file when we remove entries)
+%count = %dbmcount;
+
+# Remove all those dupes under the threshold parameter.
+# We do this, before the sorting, for performance reasons.
+my $threshold = Param("mostfreqthreshold");
+
+while (my ($key, $value) = each %count) {
+ delete $count{$key} if ($value < $threshold);
+
+ # If there's a buglist, restrict the bugs to that list.
+ delete $count{$key} if $sortvisible && (lsearch(\@buglist, $key) == -1);
+}
+
+my $origmaxrows = $maxrows;
+detaint_natural($maxrows)
+ || ThrowUserError("invalid_maxrows", { maxrows => $origmaxrows});
+
+my $origchangedsince = $changedsince;
+detaint_natural($changedsince)
+ || ThrowUserError("invalid_changedsince",
+ { changedsince => $origchangedsince });
+
+# Try and open the database from "changedsince" days ago
+my $dobefore = 0;
+my %delta;
+my $whenever = days_ago($changedsince);
+
+if (!tie(%before, 'AnyDBM_File', "$datadir/duplicates/dupes$whenever",
+ O_RDONLY, 0644)) {
+ # Ignore file not found errors
+ if (!$!{ENOENT}) {
+ ThrowUserError("no_dupe_stats_error_whenever",
+ { error_msg => $!,
+ changedsince => $changedsince,
+ whenever => $whenever,
+ });
+ }
+} else {
+ # Calculate the deltas
+ ($delta{$_} = $count{$_} - ($before{$_} || 0)) foreach (keys(%count));
+
+ $dobefore = 1;
+}
+
+my @bugs;
+my @bug_ids;
+
+if (scalar(%count)) {
+ # use Bugzilla::Search so that we get the security checking
+ my $params = new Bugzilla::CGI({ 'bug_id' => [keys %count] });
+
+ if ($openonly) {
+ $params->param('resolution', '---');
+ } else {
+ # We want to show bugs which:
+ # a) Aren't CLOSED; and
+ # b) i) Aren't VERIFIED; OR
+ # ii) Were resolved INVALID/WONTFIX
+
+ # The rationale behind this is that people will eventually stop
+ # reporting fixed bugs when they get newer versions of the software,
+ # but if the bug is determined to be erroneous, people will still
+ # keep reporting it, so we do need to show it here.
+
+ # a)
+ $params->param('field0-0-0', 'bug_status');
+ $params->param('type0-0-0', 'notequals');
+ $params->param('value0-0-0', 'CLOSED');
+
+ # b) i)
+ $params->param('field0-1-0', 'bug_status');
+ $params->param('type0-1-0', 'notequals');
+ $params->param('value0-1-0', 'VERIFIED');
+
+ # b) ii)
+ $params->param('field0-1-1', 'resolution');
+ $params->param('type0-1-1', 'anyexact');
+ $params->param('value0-1-1', 'INVALID,WONTFIX');
+ }
+
+ # Restrict to product if requested
+ if ($cgi->param('product')) {
+ $params->param('product', join(',', @query_products));
+ }
+
+ my $query = new Bugzilla::Search('fields' => [qw(bugs.bug_id
+ map_components.name
+ bugs.bug_severity
+ bugs.op_sys
+ bugs.target_milestone
+ bugs.short_desc
+ bugs.bug_status
+ bugs.resolution
+ )
+ ],
+ 'params' => $params,
+ );
+
+ SendSQL($query->getSQL());
+
+ while (MoreSQLData()) {
+ # Note: maximum row count is dealt with in the template.
+
+ my ($id, $component, $bug_severity, $op_sys, $target_milestone,
+ $short_desc, $bug_status, $resolution) = FetchSQLData();
+
+ 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);
+ }
+}
+
+$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->{'format'} = $cgi->param('format');
+$vars->{'query_products'} = \@query_products;
+my @selectable_products = GetSelectableProducts();
+$vars->{'products'} = \@selectable_products;
+
+
+my $format = GetFormat("reports/duplicates", scalar($cgi->param('format')),
+ scalar($cgi->param('ctype')));
+
+print $cgi->header($format->{'ctype'});
+
+# Generate and return the UI (HTML page) from the appropriate template.
+$template->process($format->{'template'}, $vars)
+ || ThrowTemplateError($template->error());
+
+
+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;
+}