diff --git a/mozilla/webtools/bugzilla/Bugzilla/Search.pm b/mozilla/webtools/bugzilla/Bugzilla/Search.pm index d6e7a9b7f66..642965eb2c4 100644 --- a/mozilla/webtools/bugzilla/Bugzilla/Search.pm +++ b/mozilla/webtools/bugzilla/Bugzilla/Search.pm @@ -813,6 +813,10 @@ sub init { $suppseen{$str} = 1; } } + + # Make sure we create a legal SQL query. + @andlist = ("1 = 1") if !@andlist; + my $query = ("SELECT DISTINCT " . join(', ', @fields) . ", COUNT(DISTINCT ugmap.group_id) AS cntuseringroups, " . diff --git a/mozilla/webtools/bugzilla/checksetup.pl b/mozilla/webtools/bugzilla/checksetup.pl index b752f9b6564..27bcf26f9ca 100755 --- a/mozilla/webtools/bugzilla/checksetup.pl +++ b/mozilla/webtools/bugzilla/checksetup.pl @@ -563,6 +563,7 @@ $contenttypes = { "rdf" => "application/xml" , "xml" => "text/xml" , "js" => "application/x-javascript" , + "csv" => "text/plain" , }; '); @@ -932,6 +933,7 @@ END js => sub { return $_; }, html_linebreak => sub { return $_; }, url_quote => sub { return $_; }, + csv => sub { return $_; }, }, }) || die ("Could not create Template: " . Template->error() . "\n"); diff --git a/mozilla/webtools/bugzilla/globals.pl b/mozilla/webtools/bugzilla/globals.pl index 242c8a5f654..64031bc8534 100644 --- a/mozilla/webtools/bugzilla/globals.pl +++ b/mozilla/webtools/bugzilla/globals.pl @@ -1564,6 +1564,18 @@ $::template ||= Template->new( # filter should be used for a full URL that may have # characters that need encoding. url_quote => \&url_quote , + + # In CSV, quotes are doubled, and any value containing a quote or a + # comma is enclosed in quotes. + csv => sub + { + my ($var) = @_; + $var =~ s/"/""/; + if ($var =~ /",/) { + $var = "\"$var\""; + } + return $var; + } , } , } ) || die("Template creation failed: " . Template->error()); diff --git a/mozilla/webtools/bugzilla/query.cgi b/mozilla/webtools/bugzilla/query.cgi index cd679e8e860..4bda141c35e 100755 --- a/mozilla/webtools/bugzilla/query.cgi +++ b/mozilla/webtools/bugzilla/query.cgi @@ -130,7 +130,9 @@ sub PrefillForm { "long_desc", "long_desc_type", "bug_file_loc", "bug_file_loc_type", "status_whiteboard", "status_whiteboard_type", "bug_id", - "bugidtype", "keywords", "keywords_type") { + "bugidtype", "keywords", "keywords_type", + "x_axis_field", "y_axis_field") + { # This is a bit of a hack. The default, empty list has # three entries to accommodate the needs of the email fields - # we use each position to denote the relevant field. Array diff --git a/mozilla/webtools/bugzilla/report.cgi b/mozilla/webtools/bugzilla/report.cgi new file mode 100755 index 00000000000..52589777171 --- /dev/null +++ b/mozilla/webtools/bugzilla/report.cgi @@ -0,0 +1,130 @@ +#!/usr/bonsaitools/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 +# + +use diagnostics; +use strict; +use lib "."; + +require "CGI.pl"; + +use vars qw($template $vars); + +use Bugzilla::Search; + +ConnectToDatabase(); + +GetVersionTable(); + +quietly_check_login(); + +# If other report types are added, some of this code can be moved into a sub, +# and the correct sub chosen based on $::FORM{'type'}. + +$::FORM{'y_axis_field'} || ThrowCodeError("no_y_axis_defined"); + +my $col_field = $::FORM{'x_axis_field'}; +my $row_field = $::FORM{'y_axis_field'}; + +my %columns; +$columns{'bug_severity'} = "bugs.bug_severity"; +$columns{'priority'} = "bugs.priority"; +$columns{'rep_platform'} = "bugs.rep_platform"; +$columns{'assigned_to'} = "map_assigned_to.login_name"; +$columns{'reporter'} = "map_reporter.login_name"; +$columns{'qa_contact'} = "map_qa_contact.login_name"; +$columns{'bug_status'} = "bugs.bug_status"; +$columns{'resolution'} = "bugs.resolution"; +$columns{'component'} = "map_components.name"; +$columns{'product'} = "map_products.name"; +$columns{'version'} = "bugs.version"; +$columns{'op_sys'} = "bugs.op_sys"; +$columns{'votes'} = "bugs.votes"; +$columns{'keywords'} = "bugs.keywords"; +$columns{'target_milestone'} = "bugs.target_milestone"; + +my @axis_fields = ($row_field); +# The X axis (horizontal) is optional +push(@axis_fields, $col_field) if $col_field; + +my @selectnames = map($columns{$_}, @axis_fields); + +my $search = new Bugzilla::Search('fields' => \@selectnames, + 'url' => $::buffer); +my $query = $search->getSQL(); + +$query =~ s/DISTINCT//; +SendSQL($query, $::userid); + +# We have a hash for each direction for the totals, and a hash of hashes for +# the data itself. +my %data; +my %row_totals; +my %col_totals; +my $grand_total; + +# Read the bug data and increment the counts. +while (MoreSQLData()) { + my ($row, $col) = FetchSQLData(); + $row = "" if !defined($row); + $col = "" if !defined($col); + + $data{$row}{$col}++; + $row_totals{$row}++; + $col_totals{$col}++; + $grand_total++; +} + +$vars->{'data'} = \%data; +$vars->{'row_totals'} = \%row_totals; +$vars->{'col_totals'} = \%col_totals; +$vars->{'grand_total'} = $grand_total; + +# Determine the labels for the rows and columns +my @row_names = sort(keys(%row_totals)); +my @col_names = sort(keys(%col_totals)); + +$vars->{'row_names'} = \@row_names; +$vars->{'col_names'} = \@col_names; + +$vars->{'row_field'} = $row_field; +$vars->{'col_field'} = $col_field; + +$::buffer =~ s/format=[^&]*&?//g; + +# Calculate the base query URL for the hyperlinked numbers +my $buglistbase = $::buffer; +$buglistbase =~ s/$row_field=[^&]*&?//g; +$buglistbase =~ s/$col_field=[^&]*&?//g; + +$vars->{'buglistbase'} = $buglistbase; +$vars->{'buffer'} = $::buffer; + +$::FORM{'type'} =~ s/[^a-zA-Z\-]//g; + +# Generate and return the result from the appropriate template. +my $format = GetFormat("reports/$::FORM{'type'}", + $::FORM{'format'}, + $::FORM{'ctype'}); +print "Content-Type: $format->{'contenttype'}\n\n"; +$template->process("$format->{'template'}", $vars) + || ThrowTemplateError($template->error()); diff --git a/mozilla/webtools/bugzilla/t/004template.t b/mozilla/webtools/bugzilla/t/004template.t index 41d515435a7..02541d351a4 100644 --- a/mozilla/webtools/bugzilla/t/004template.t +++ b/mozilla/webtools/bugzilla/t/004template.t @@ -81,6 +81,7 @@ my $template = Template->new( js => sub { return $_ } , strike => sub { return $_ } , url_quote => sub { return $_ } , + csv => sub { return $_ } , }, } ); diff --git a/mozilla/webtools/bugzilla/template/en/default/global/code-error.html.tmpl b/mozilla/webtools/bugzilla/template/en/default/global/code-error.html.tmpl index 55fac2acaf4..bf93977adc4 100644 --- a/mozilla/webtools/bugzilla/template/en/default/global/code-error.html.tmpl +++ b/mozilla/webtools/bugzilla/template/en/default/global/code-error.html.tmpl @@ -78,6 +78,10 @@ [% ELSIF error == "no_bug_data" %] No data when fetching bug [% bug_id %]. + [% ELSIF error == "no_y_axis_defined" %] + No Y axis was defined when creating report. The X axis is optional, + but the Y axis is compulsory. + [% ELSIF error == "template_error" %] [% template_error_msg %] diff --git a/mozilla/webtools/bugzilla/template/en/default/reports/table.csv.tmpl b/mozilla/webtools/bugzilla/template/en/default/reports/table.csv.tmpl new file mode 100644 index 00000000000..96b0c3971cf --- /dev/null +++ b/mozilla/webtools/bugzilla/template/en/default/reports/table.csv.tmpl @@ -0,0 +1,41 @@ +[%# 1.0@bugzilla.org %] +[%# 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 + #%] +[%# INTERFACE: + # See report.html.tmpl. + #%] +[% row_field FILTER csv -%] + +[% IF col_field -%] + \ [% col_field FILTER csv -%], +[% FOREACH col = col_names -%] +[% col FILTER csv -%], +[% END -%] +[% ELSE -%] +,Number of bugs, +[% END %] + +[% FOREACH row = row_names %] +[% row FILTER csv -%], +[% FOREACH col = col_names %] +[% IF data.$row AND data.$row.$col %][% data.$row.$col -%],[% ELSE %]0,[% END %] +[% END %] + +[% END %] diff --git a/mozilla/webtools/bugzilla/template/en/default/reports/table.html.tmpl b/mozilla/webtools/bugzilla/template/en/default/reports/table.html.tmpl new file mode 100644 index 00000000000..d9a04d5fd4a --- /dev/null +++ b/mozilla/webtools/bugzilla/template/en/default/reports/table.html.tmpl @@ -0,0 +1,143 @@ + +[%# 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 + # + #%] + +[%# INTERFACE: + # basequery: The base query for this table, in URL form + # row_field: string. The field name for the data in table rows + # col_field: string. The field name for the data in table columns + # col_names: array of strings. Values for the columns + # row_names: array of strings. Values for the rows + # col_totals: hash of integers. Totals for the columns, indexed by col_names. + # row_totals: hash of integers. Totals for the rows, indexed by row_names. + # data: hash of hash of numbers. Bug counts indexed by col_names and + # row_names values. + #%] + +[% PROCESS global/header.html.tmpl + title = "Report" + onload = "selectProduct(document.forms['queryform']);" + style = " + .t1 { background-color: #ffffff } + .t2 { background-color: #dfefff } + .t3 { background-color: #dddddd } + .t4 { background-color: #c3d3ed } + .ttotal { background-color: #cfffdf } + " +%] + +
+ + + + + + + + + + +
+ + [% col_field FILTER html %] +
+ [% row_field FILTER html %] + + + +[% classes = [ [ "t1", "t2" ] , [ "t3", "t4" ] ] %] +[% col_idx = 0 %] +[% row_idx = 0 %] + + + [% IF col_names %] + + + [% FOREACH col = col_names %] + [%# If no col header, skip the col. This makes display look right if + there's no defined X axis. Not doing this gives us two cols. %] + [% NEXT IF col == "" %] + [% col_idx = 1 - col_idx %] + + [% END %] + + + [% END %] + + [% FOREACH row = row_names %] + [% row_idx = 1 - row_idx %] + + + [% FOREACH col = col_names %] + [% NEXT IF col == "" %] + [% col_idx = 1 - col_idx %] + + [% END %] + + + [% END %] + + + [% row_idx = 1 - row_idx %] + + [% FOREACH col = col_names %] + [% NEXT IF col == "" %] + + [% END %] + + +
+ + [% col FILTER html %] + + Total +
+ [% row FILTER html %] + + [% IF data.$row.$col AND data.$row.$col > 0 %] + + [% data.$row.$col %] + [% ELSE %] + . + [% END %] + + [% row_totals.$row %] +
+ Total + + [% col_totals.$col %] + + + [% grand_total %] + +
+ + +
+ + Edit this report +
+ +
+ +[% PROCESS global/footer.html.tmpl %] diff --git a/mozilla/webtools/bugzilla/template/en/default/search/search-report-table.html.tmpl b/mozilla/webtools/bugzilla/template/en/default/search/search-report-table.html.tmpl new file mode 100644 index 00000000000..32f8161354a --- /dev/null +++ b/mozilla/webtools/bugzilla/template/en/default/search/search-report-table.html.tmpl @@ -0,0 +1,125 @@ + +[%# 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 + #%] + +[%# INTERFACE: + # This template has no interface. However, to use it, you need to fulfill + # the interfaces of the templates it contains. + #%] + +[% PROCESS global/header.html.tmpl + title = "Generate Report" + onload = "selectProduct(document.forms['reportform']);" +%] + +

+ Produce a table of bug counts by choosing two fields to plot against each + other, and then refining your set of bugs using the rest of the form. +

+ +[% button_name = "Generate Report" %] + +
+ + + + + + + + + + + + + + + +
+ Vertical Axis + + Horizontal Axis + +    + + Format +
+ [% PROCESS select sel = { name => 'y_axis_field' } %] + + [% PROCESS select sel = { name => 'x_axis_field', noop = 1 } %] + +    + + HTML + CSV +
+ +
+ +[% PROCESS search/form.html.tmpl %] + +
+ + +
+ +[% PROCESS "search/boolean-charts.html.tmpl" %] + +
+ +[% PROCESS global/footer.html.tmpl %] + +[%############################################################################%] +[%# Block for SELECT fields #%] +[%############################################################################%] + +[% BLOCK select %] + [% fields = [ + { name => "", description => "---" }, + { name => "product", description => "Product" }, + { name => "component", description => "Component" }, + { name => "version", description => "Version" }, + { name => "rep_platform", description => "Platform" }, + { name => "op_sys", description => "OS" }, + { name => "bug_status", description => "Status" }, + { name => "resolution", description => "Resolution" }, + { name => "bug_severity", description => "Severity" }, + { name => "priority", description => "Priority" }, + { name => "target_milestone", description => "Target Milestone" }, + { name => "keywords", description => "Keywords" }, + { name => "assigned_to", description => "Assignee" }, + { name => "reporter", description => "Reporter" }, + { name => "qa_contact", description => "QA Contact" }, + { name => "votes", description => "Votes" } ] %] + + +[% END %]