2002-01-20 04:44:52 +03:00
|
|
|
#!/usr/bonsaitools/bin/perl -wT
|
2000-10-24 01:44:30 +04:00
|
|
|
# -*- 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.
|
|
|
|
|
2001-04-19 21:35:23 +04:00
|
|
|
|
2000-10-24 01:44:30 +04:00
|
|
|
use strict;
|
2002-04-11 02:30:00 +04:00
|
|
|
|
2001-04-06 22:19:47 +04:00
|
|
|
use AnyDBM_File;
|
2002-01-20 04:44:52 +03:00
|
|
|
|
|
|
|
use lib qw(.);
|
|
|
|
|
2000-10-24 01:44:30 +04:00
|
|
|
require "globals.pl";
|
|
|
|
require "CGI.pl";
|
|
|
|
|
2002-11-05 04:54:15 +03:00
|
|
|
use vars qw($buffer);
|
|
|
|
|
2003-01-14 23:00:11 +03:00
|
|
|
use Bugzilla;
|
2002-11-10 05:50:39 +03:00
|
|
|
use Bugzilla::Search;
|
|
|
|
use Bugzilla::CGI;
|
|
|
|
|
2002-11-05 04:54:15 +03:00
|
|
|
# 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.
|
2002-11-08 05:05:20 +03:00
|
|
|
if ($::FORM{'ctype'} && $::FORM{'ctype'} eq "xul") {
|
2002-11-05 04:54:15 +03:00
|
|
|
my $params = CanonicaliseParams($::buffer, ["format", "ctype"]);
|
|
|
|
print "Location: " . (-e "duplicates.jar" ? "duplicates.jar!/" : "") .
|
|
|
|
"duplicates.xul" . ($params ? "?$params" : "") . "\n\n";
|
|
|
|
exit;
|
|
|
|
}
|
|
|
|
|
2002-04-11 02:30:00 +04:00
|
|
|
# Use global templatisation variables.
|
|
|
|
use vars qw($template $vars);
|
|
|
|
|
2003-01-14 23:00:11 +03:00
|
|
|
ConnectToDatabase();
|
2000-10-24 01:44:30 +04:00
|
|
|
GetVersionTable();
|
|
|
|
|
2001-10-23 19:44:53 +04:00
|
|
|
quietly_check_login();
|
|
|
|
|
2003-01-14 23:00:11 +03:00
|
|
|
Bugzilla->instance->switch_to_shadow_db();
|
|
|
|
|
2002-09-22 21:15:13 +04:00
|
|
|
use vars qw (%FORM $userid @legal_product);
|
2001-10-23 19:44:53 +04:00
|
|
|
|
2001-04-06 22:19:47 +04:00
|
|
|
my %dbmcount;
|
2000-10-24 01:44:30 +04:00
|
|
|
my %count;
|
|
|
|
my %before;
|
|
|
|
|
2001-04-06 22:19:47 +04:00
|
|
|
# Get params from URL
|
2002-04-11 02:30:00 +04:00
|
|
|
sub formvalue {
|
|
|
|
my ($name, $default) = (@_);
|
|
|
|
return $FORM{$name} || $default || "";
|
2000-10-24 01:44:30 +04:00
|
|
|
}
|
2001-04-06 22:19:47 +04:00
|
|
|
|
2002-04-11 02:30:00 +04:00
|
|
|
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")));
|
2000-10-24 01:44:30 +04:00
|
|
|
|
2002-08-12 09:43:05 +04:00
|
|
|
my $product_id;
|
|
|
|
if ($product) {
|
|
|
|
$product_id = get_product_id($product);
|
|
|
|
if (!$product_id) {
|
2002-09-13 02:51:50 +04:00
|
|
|
$vars->{'product'} = $product;
|
|
|
|
ThrowUserError("invalid_product_name");
|
2002-08-12 09:43:05 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-04-11 02:30:00 +04:00
|
|
|
# Small backwards-compatibility hack, dated 2002-04-10.
|
|
|
|
$sortby = "count" if $sortby eq "dup_count";
|
2000-10-24 01:44:30 +04:00
|
|
|
|
2001-04-06 22:19:47 +04:00
|
|
|
# Open today's record of dupes
|
2002-04-11 02:30:00 +04:00
|
|
|
my $today = days_ago(0);
|
|
|
|
my $yesterday = days_ago(1);
|
2001-04-06 22:19:47 +04:00
|
|
|
|
2002-04-16 12:25:52 +04:00
|
|
|
# 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', "data/duplicates/dupes$today",
|
|
|
|
O_RDONLY, 0644)) {
|
|
|
|
if ($!{ENOENT}) {
|
|
|
|
if (!tie(%dbmcount, 'AnyDBM_File', "data/duplicates/dupes$yesterday",
|
|
|
|
O_RDONLY, 0644)) {
|
2002-08-16 02:57:21 +04:00
|
|
|
$vars->{'today'} = $today;
|
2002-04-16 12:25:52 +04:00
|
|
|
if ($!{ENOENT}) {
|
2002-08-16 02:57:21 +04:00
|
|
|
ThrowUserError("no_dupe_stats");
|
2002-04-16 12:25:52 +04:00
|
|
|
} else {
|
2002-08-16 02:57:21 +04:00
|
|
|
$vars->{'error_msg'} = $!;
|
|
|
|
ThrowUserError("no_dupe_stats_error_yesterday");
|
2002-04-16 12:25:52 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2002-08-16 02:57:21 +04:00
|
|
|
$vars->{'error_msg'} = $!;
|
|
|
|
ThrowUserError("no_dupe_stats_error_today");
|
2002-04-16 12:25:52 +04:00
|
|
|
}
|
2001-04-06 22:19:47 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
# Copy hash (so we don't mess up the on-disk file when we remove entries)
|
|
|
|
%count = %dbmcount;
|
2002-04-11 02:30:00 +04:00
|
|
|
|
|
|
|
# Remove all those dupes under the threshold parameter.
|
|
|
|
# We do this, before the sorting, for performance reasons.
|
2001-04-06 22:19:47 +04:00
|
|
|
my $threshold = Param("mostfreqthreshold");
|
2000-10-24 01:44:30 +04:00
|
|
|
|
2002-04-11 02:30:00 +04:00
|
|
|
while (my ($key, $value) = each %count) {
|
|
|
|
delete $count{$key} if ($value < $threshold);
|
2002-06-29 00:02:09 +04:00
|
|
|
|
|
|
|
# If there's a buglist, restrict the bugs to that list.
|
|
|
|
delete $count{$key} if $sortvisible && (lsearch(\@buglist, $key) == -1);
|
2000-10-24 01:44:30 +04:00
|
|
|
}
|
|
|
|
|
2001-04-06 22:19:47 +04:00
|
|
|
# Try and open the database from "changedsince" days ago
|
2002-04-11 02:30:00 +04:00
|
|
|
my $dobefore = 0;
|
2001-04-06 22:19:47 +04:00
|
|
|
my %delta;
|
2002-04-11 02:30:00 +04:00
|
|
|
my $whenever = days_ago($changedsince);
|
|
|
|
|
2002-04-16 12:25:52 +04:00
|
|
|
if (!tie(%before, 'AnyDBM_File', "data/duplicates/dupes$whenever",
|
|
|
|
O_RDONLY, 0644)) {
|
|
|
|
# Ignore file not found errors
|
|
|
|
if (!$!{ENOENT}) {
|
2002-08-16 02:57:21 +04:00
|
|
|
$vars->{'error_msg'} = $!;
|
|
|
|
$vars->{'changedsince'} = $changedsince;
|
|
|
|
$vars->{'whenever'} = $whenever;
|
|
|
|
ThrowUserError("no_dupe_stats_error_whenever");
|
2002-04-16 12:25:52 +04:00
|
|
|
}
|
|
|
|
} else {
|
2002-04-11 02:30:00 +04:00
|
|
|
# Calculate the deltas
|
2002-10-11 10:30:09 +04:00
|
|
|
($delta{$_} = $count{$_} - ($before{$_} || 0)) foreach (keys(%count));
|
2002-04-11 02:30:00 +04:00
|
|
|
|
|
|
|
$dobefore = 1;
|
|
|
|
}
|
|
|
|
|
2002-11-10 05:50:39 +03:00
|
|
|
detaint_natural($maxrows)
|
|
|
|
|| ThrowUserError("invalid_maxrows", { maxrows => $maxrows});
|
|
|
|
|
2002-04-11 02:30:00 +04:00
|
|
|
my @bugs;
|
|
|
|
my @bug_ids;
|
|
|
|
|
2002-08-10 13:53:32 +04:00
|
|
|
if (scalar(%count)) {
|
2002-11-10 05:50:39 +03:00
|
|
|
# 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 ($::FORM{'product'}) {
|
|
|
|
$params->param('product', $::FORM{'product'});
|
|
|
|
}
|
|
|
|
|
|
|
|
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());
|
2002-08-10 13:53:32 +04:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
2002-04-11 02:30:00 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
$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;
|
2002-04-27 03:18:57 +04:00
|
|
|
$vars->{'format'} = $::FORM{'format'};
|
2002-04-11 02:30:00 +04:00
|
|
|
$vars->{'product'} = $product;
|
|
|
|
$vars->{'products'} = \@::legal_product;
|
|
|
|
|
2002-04-27 03:18:57 +04:00
|
|
|
|
2002-09-18 03:28:24 +04:00
|
|
|
my $format =
|
|
|
|
GetFormat("reports/duplicates", $::FORM{'format'}, $::FORM{'ctype'});
|
2002-04-27 03:18:57 +04:00
|
|
|
|
2002-09-18 03:28:24 +04:00
|
|
|
print "Content-Type: $format->{'ctype'}\n\n";
|
2002-04-11 02:30:00 +04:00
|
|
|
|
|
|
|
# Generate and return the UI (HTML page) from the appropriate template.
|
2002-09-18 03:28:24 +04:00
|
|
|
$template->process($format->{'template'}, $vars)
|
2002-04-24 11:24:50 +04:00
|
|
|
|| ThrowTemplateError($template->error());
|
2002-04-11 02:30:00 +04:00
|
|
|
|
|
|
|
|
|
|
|
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;
|
2001-04-06 22:19:47 +04:00
|
|
|
}
|