diff options
-rwxr-xr-x | checksetup.pl | 36 | ||||
-rwxr-xr-x | query.cgi | 3 | ||||
-rwxr-xr-x | report.cgi | 162 | ||||
-rw-r--r-- | template/en/default/global/code-error.html.tmpl | 4 | ||||
-rw-r--r-- | template/en/default/global/field-descs.html.tmpl | 2 | ||||
-rw-r--r-- | template/en/default/global/user-error.html.tmpl | 13 | ||||
-rw-r--r-- | template/en/default/index.html.tmpl | 2 | ||||
-rw-r--r-- | template/en/default/reports/menu.html.tmpl | 8 | ||||
-rw-r--r-- | template/en/default/reports/report-bar.png.tmpl | 54 | ||||
-rw-r--r-- | template/en/default/reports/report-line.png.tmpl | 53 | ||||
-rw-r--r-- | template/en/default/reports/report-pie.png.tmpl | 35 | ||||
-rw-r--r-- | template/en/default/reports/report-table.csv.tmpl | 54 | ||||
-rw-r--r-- | template/en/default/reports/report-table.html.tmpl | 123 | ||||
-rw-r--r-- | template/en/default/reports/report.csv.tmpl | 26 | ||||
-rw-r--r-- | template/en/default/reports/report.html.tmpl | 156 | ||||
-rw-r--r-- | template/en/default/search/search-report-graph.html.tmpl | 112 | ||||
-rw-r--r-- | template/en/default/search/search-report-select.html.tmpl | 47 | ||||
-rw-r--r-- | template/en/default/search/search-report-table.html.tmpl | 43 |
18 files changed, 738 insertions, 195 deletions
diff --git a/checksetup.pl b/checksetup.pl index 1acec457c..58c2c130f 100755 --- a/checksetup.pl +++ b/checksetup.pl @@ -258,25 +258,38 @@ foreach my $module (@{$modules}) { } print "\nThe following Perl modules are optional:\n" unless $silent; -my $charts = 0; -$charts++ if have_vers("GD","1.19"); -$charts++ if have_vers("Chart::Base","0.99"); -my $xmlparser = have_vers("XML::Parser",0); +my $gd = have_vers("GD","1.20"); +my $chartbase = have_vers("Chart::Base","0.99"); +my $xmlparser = have_vers("XML::Parser",0); +my $gdgraph = have_vers("GD::Graph",0); +my $gdtextalign = have_vers("GD::Text::Align",0); print "\n" unless $silent; -if (($charts != 2) && !$silent) { - print "If you you want to see graphical bug dependency charts, you may install\n", - "the optional libgd and the Perl modules GD-1.19 and Chart::Base-0.99b, e.g. by\n", - "running (as root)\n\n", - " perl -MCPAN -e'install \"LDS/GD-1.19.tar.gz\"'\n", - " perl -MCPAN -e'install \"N/NI/NINJAZ/Chart-0.99b.tar.gz\"'\n\n"; +if ((!$gd || !$chartbase) && !$silent) { + print "If you you want to see graphical bug charts (plotting historical "; + print "data over \ntime), you should install libgd and the following Perl "; print "modules:\n\n"; + print "GD: perl -MCPAN -e'install \"GD\"'\n" if !$gd; + print "Chart 0.99b: perl -MCPAN " . + "-e'install \"N/NI/NINJAZ/Chart-0.99b.tar.gz\"'\n" if !$chartbase; + print "\n"; } if (!$xmlparser && !$silent) { print "If you want to use the bug import/export feature to move bugs to or from\n", "other bugzilla installations, you will need to install the XML::Parser module by\n", - "running (as root)\n\n", + "running (as root):\n\n", " perl -MCPAN -e'install \"XML::Parser\"'\n\n"; } +if ((!$gd || !$gdgraph || !$gdtextalign) && !$silent) { + print "If you you want to see graphical bug reports (bar, pie and line "; + print "charts of \ncurrent data), you should install libgd and the "; + print "following Perl modules:\n\n"; + print "GD: perl -MCPAN -e'install \"GD\"'\n" if !$gd; + print "GD::Graph: perl -MCPAN " . + "-e'install \"GD::Graph\"'\n" if !$gdgraph; + print "GD::Text::Align: perl -MCPAN " . + "-e'install \"GD::Text::Align\"'\n" if !$gdtextalign; + print "\n"; +} if (%missing) { print "\n\n"; print "Bugzilla requires some Perl modules which are either missing from your\n", @@ -580,6 +593,7 @@ $contenttypes = { "xml" => "text/xml" , "js" => "application/x-javascript" , "csv" => "text/plain" , + "png" => "image/png" , }; '); @@ -131,7 +131,8 @@ sub PrefillForm { "bug_file_loc_type", "status_whiteboard", "status_whiteboard_type", "bug_id", "bugidtype", "keywords", "keywords_type", - "x_axis_field", "y_axis_field", "z_axis_field") + "x_axis_field", "y_axis_field", "z_axis_field", + "chart_format", "cumulate") { # This is a bit of a hack. The default, empty list has # three entries to accommodate the needs of the email fields - diff --git a/report.cgi b/report.cgi index f4cb74dad..9543bc5b7 100755 --- a/report.cgi +++ b/report.cgi @@ -34,24 +34,58 @@ ConnectToDatabase(); GetVersionTable(); -quietly_check_login(); +confirm_login(); -if ($::FORM{'action'} ne "plot") { +my $action = $cgi->param('action') || 'menu'; + +if ($action eq "menu") { + # No need to do any searching in this case, so bail out early. print "Content-Type: text/html\n\n"; $template->process("reports/menu.html.tmpl", $vars) || ThrowTemplateError($template->error()); exit; } -$::FORM{'y_axis_field'} || ThrowCodeError("no_y_axis_defined"); +my $col_field = $cgi->param('x_axis_field') || ''; +my $row_field = $cgi->param('y_axis_field') || ''; +my $tbl_field = $cgi->param('z_axis_field') || ''; + +if (!($col_field || $row_field || $tbl_field)) { + ThrowUserError("no_axes_defined"); +} + +my $width = $cgi->param('width'); +my $height = $cgi->param('height'); -if ($::FORM{'z_axis_field'} && !$::FORM{'x_axis_field'}) { - ThrowUserError("z_axis_defined_with_no_x_axis"); +if (defined($width)) { + (detaint_natural($width) && $width > 0) + || ThrowCodeError("invalid_dimensions"); + $width <= 2000 || ThrowUserError("chart_too_large"); } -my $col_field = $::FORM{'x_axis_field'}; -my $row_field = $::FORM{'y_axis_field'}; -my $tbl_field = $::FORM{'z_axis_field'}; +if (defined($height)) { + (detaint_natural($height) && $height > 0) + || ThrowCodeError("invalid_dimensions"); + $height <= 2000 || ThrowUserError("chart_too_large"); +} + +# These shenanigans are necessary to make sure that both vertical and +# horizontal 1D tables convert to the correct dimension when you ask to +# display them as some sort of chart. +if ($::FORM{'format'} && $::FORM{'format'} eq "table") { + if ($col_field && !$row_field) { + # 1D *tables* should be displayed vertically (with a row_field only) + $row_field = $col_field; + $col_field = ''; + } +} +else { + if ($row_field && !$col_field) { + # 1D *charts* should be displayed horizontally (with an col_field only) + $col_field = $row_field; + $row_field = ''; + } +} my %columns; $columns{'bug_severity'} = "bugs.bug_severity"; @@ -83,6 +117,9 @@ my $search = new Bugzilla::Search('fields' => \@selectnames, 'params' => $params); my $query = $search->getSQL(); +$::SIG{TERM} = 'DEFAULT'; +$::SIG{PIPE} = 'DEFAULT'; + SendSQL($query); # We have a hash of hashes for the data itself, and a hash to hold the @@ -90,9 +127,11 @@ SendSQL($query); my %data; my %names; -# Read the bug data and increment the counts. +# Read the bug data and count the bugs for each possible value of row, column +# and table. while (MoreSQLData()) { my ($row, $col, $tbl) = FetchSQLData(); + $row = "" if ($row eq $columns{''}); $col = "" if ($col eq $columns{''}); $tbl = "" if ($tbl eq $columns{''}); @@ -102,25 +141,106 @@ while (MoreSQLData()) { $names{"tbl"}{$tbl}++; } -# Determine the labels for the rows and columns +my @col_names = sort(keys(%{$names{"col"}})); +my @row_names = sort(keys(%{$names{"row"}})); +my @tbl_names = sort(keys(%{$names{"tbl"}})); + +# The GD::Graph package requires a particular format of data, so once we've +# gathered everything into the hashes and made sure we know the size of the +# data, we reformat it into an array of arrays of arrays of data. +push(@tbl_names, "-total-") if (scalar(@tbl_names) > 1); + +my @image_data; +foreach my $tbl (@tbl_names) { + my @tbl_data; + push(@tbl_data, \@col_names); + foreach my $row (@row_names) { + my @col_data; + foreach my $col (@col_names) { + $data{$tbl}{$col}{$row} = $data{$tbl}{$col}{$row} || 0; + push(@col_data, $data{$tbl}{$col}{$row}); + if ($tbl ne "-total-") { + # This is a bit sneaky. We spend every loop except the last + # building up the -total- data, and then last time round, + # we process it as another tbl, and push() the total values + # into the image_data array. + $data{"-total-"}{$col}{$row} += $data{$tbl}{$col}{$row}; + } + } + + push(@tbl_data, \@col_data); + } + + push(@image_data, \@tbl_data); +} + $vars->{'col_field'} = $col_field; $vars->{'row_field'} = $row_field; $vars->{'tbl_field'} = $tbl_field; -$vars->{'names'} = \%names; -$vars->{'data'} = \%data; $vars->{'time'} = time(); -$cgi->delete('format'); +$vars->{'col_names'} = \@col_names; +$vars->{'row_names'} = \@row_names; +$vars->{'tbl_names'} = \@tbl_names; + +$vars->{'width'} = $width if $width; +$vars->{'height'} = $height if $height; + +$vars->{'query'} = $query; +$vars->{'debug'} = $::FORM{'debug'}; + +my $formatparam = $cgi->param('format'); + +if ($action eq "wrap") { + # So which template are we using? If action is "wrap", we will be using + # no format (it gets passed through to be the format of the actual data), + # and either report.csv.tmpl (CSV), or report.html.tmpl (everything else). + # report.html.tmpl produces an HTML framework for either tables of HTML + # data, or images generated by calling report.cgi again with action as + # "plot". + $formatparam =~ s/[^a-zA-Z\-]//g; + trick_taint($formatparam); + $vars->{'format'} = $formatparam; + $formatparam = ''; + + # We need a number of different variants of the base URL for different + # URLs in the HTML. + $vars->{'buglistbase'} = $cgi->canonicalise_query( + "x_axis_field", "y_axis_field", "z_axis_field", "format", @axis_fields); + $vars->{'imagebase'} = $cgi->canonicalise_query( + $tbl_field, "action", "ctype", "format", "width", "height"); + $vars->{'switchbase'} = $cgi->canonicalise_query( + "action", "ctype", "format", "width", "height"); + $vars->{'data'} = \%data; +} +elsif ($action eq "plot") { + # If action is "plot", we will be using a format as normal (pie, bar etc.) + # and a ctype as normal (currently only png.) + $vars->{'cumulate'} = $cgi->param('cumulate') ? 1 : 0; + $vars->{'data'} = \@image_data; +} +else { + ThrowUserError("unknown_action", {action => $cgi->param('action')}); +} + +my $format = GetFormat("reports/report", $formatparam, $cgi->param('ctype')); -# Calculate the base query URL for the hyperlinked numbers -$vars->{'querybase'} = $cgi->canonicalise_query("x_axis_field", - "y_axis_field", - "z_axis_field", - @axis_fields); -$vars->{'query'} = $cgi->query_string(); +# If we get a template or CGI error, it comes out as HTML, which isn't valid +# PNG data, and the browser just displays a "corrupt PNG" message. So, you can +# set debug=1 to always get an HTML content-type, and view the error. +$format->{'ctype'} = "text/html" if $::FORM{'debug'}; -# Generate and return the result from the appropriate template. -my $format = GetFormat("reports/report", $::FORM{'format'}, $::FORM{'ctype'}); print "Content-Type: $format->{'ctype'}\n\n"; + +# Problems with this CGI are often due to malformed data. Setting debug=1 +# prints out both data structures. +if ($::FORM{'debug'}) { + use Data::Dumper; + print "<pre>data hash:\n"; + print Dumper(%data) . "\n\n"; + print "data array:\n"; + print Dumper(@image_data) . "\n\n</pre>"; +} + $template->process("$format->{'template'}", $vars) || ThrowTemplateError($template->error()); diff --git a/template/en/default/global/code-error.html.tmpl b/template/en/default/global/code-error.html.tmpl index baad2f5f0..42c07ab2d 100644 --- a/template/en/default/global/code-error.html.tmpl +++ b/template/en/default/global/code-error.html.tmpl @@ -119,6 +119,10 @@ The [% component FILTER html %] component doesn't exist in the [% product FILTER html %] product. + [% ELSIF error == "invalid_dimensions" %] + [% title = "Invalid Dimensions" %] + The width or height specified is not a positive integer. + [% ELSIF error == "mismatched_bug_ids_on_obsolete" %] Attachment [% attach_id FILTER html %] ([% description FILTER html %]) is attached to bug [% attach_bug_id FILTER html %], but you tried to diff --git a/template/en/default/global/field-descs.html.tmpl b/template/en/default/global/field-descs.html.tmpl index 6349b6b1b..3e83da7c0 100644 --- a/template/en/default/global/field-descs.html.tmpl +++ b/template/en/default/global/field-descs.html.tmpl @@ -1,4 +1,4 @@ -<!-- 1.0@bugzilla.org --> +[%# 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 diff --git a/template/en/default/global/user-error.html.tmpl b/template/en/default/global/user-error.html.tmpl index ddc9ce11c..d08c96c4b 100644 --- a/template/en/default/global/user-error.html.tmpl +++ b/template/en/default/global/user-error.html.tmpl @@ -114,6 +114,10 @@ [% title = "Bugs Not Changed" %] Um, you apparently did not change anything on the selected bugs. + [% ELSIF error == "chart_too_large" %] + [% title = "Chart Too Large" %] + Sorry, but 2000 x 2000 is the maximum size for a chart. + [% ELSIF error == "comment_required" %] [% title = "Comment Required" %] You have to specify a <b>comment</b> on this change. @@ -361,6 +365,10 @@ [% title = "New Password Missing" %] You must enter a new password. + [% ELSIF error == "no_axes_defined" %] + [% title = "No Axes Defined" %] + You didn't define any axes to plot. + [% ELSIF error == "no_bugs_chosen" %] [% title = "No Bugs Chosen" %] You apparently didn't choose any bugs to modify. @@ -547,11 +555,6 @@ [% title = "Value Out Of Range" %] Value is out of range for field <em>[% field_descs.$field %]</em>. - [% ELSIF error == "z_axis_defined_with_no_x_axis" %] - [% title = "Nonsensical Options" %] - You've defined a field for multiple tables without having defined - a horizontal axis for those tables. - [% ELSIF error == "zero_length_file" %] [% title = "File Is Empty" %] The file you are trying to attach is empty! diff --git a/template/en/default/index.html.tmpl b/template/en/default/index.html.tmpl index d13ccbc43..9b34d15ee 100644 --- a/template/en/default/index.html.tmpl +++ b/template/en/default/index.html.tmpl @@ -55,7 +55,7 @@ function addSidebar() { <p> <a href="query.cgi">Query existing bug reports</a><br> <a href="enter_bug.cgi">Enter a new bug report</a><br> - <a href="reports.cgi">Get summary reports</a><br> + <a href="report.cgi">Summary reports and charts</a><br> </p><p> [% IF username %] <a href="userprefs.cgi">Change password or user preferences</a><br> diff --git a/template/en/default/reports/menu.html.tmpl b/template/en/default/reports/menu.html.tmpl index d93717532..4e21bf4d6 100644 --- a/template/en/default/reports/menu.html.tmpl +++ b/template/en/default/reports/menu.html.tmpl @@ -29,7 +29,7 @@ %] <p> - Bugzilla allows you to view and track the state of your bug database in + Bugzilla allows you to view and track the state of the bug database in all manner of exciting ways. </p> @@ -46,6 +46,12 @@ </strong> - tables of bug counts in 1, 2 or 3 dimensions, as HTML or CSV. </li> + <li> + <strong> + <a href="query.cgi?format=report-graph">Graphical reports</a> + </strong> - + line graphs, bar and pie charts. + </li> </ul> <h2>Change Over Time</h2> diff --git a/template/en/default/reports/report-bar.png.tmpl b/template/en/default/reports/report-bar.png.tmpl new file mode 100644 index 000000000..a3d73d45c --- /dev/null +++ b/template/en/default/reports/report-bar.png.tmpl @@ -0,0 +1,54 @@ +[%# 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 <gerv@gerv.net> + #%] + +[% y_label = "Bugs" %] + +[% PROCESS "global/field-descs.html.tmpl" %] + +[% col_field_disp = field_descs.$col_field || col_field %] + +[% FILTER null; + USE graph = GD.Graph.bars(width, height); + + graph.set(x_label => col_field_disp, + y_label => y_label, + y_tick_number => 8, + y_number_format => "%d", + x_label_position => 0.5, + bar_spacing => 8, + shadow_depth => 4, + shadowclr => 'dred', + show_values => 1, + legend_placement => "RT"); + + graph.set(cumulate => "true", + show_values => 0) IF cumulate; + + # Workaround for the fact that set_legend won't take row_names directly, + # because row_names is an array reference rather than an array. + graph.set_legend(row_names.0, row_names.1, row_names.2, row_names.3, + row_names.4, row_names.5, row_names.6, row_names.7, + row_names.8, row_names.9, row_names.10, row_names.11, + row_names.12, row_names.13, row_names.14, row_names.15); + + graph.plot(data.0).png | stdout(1); + END; +-%] diff --git a/template/en/default/reports/report-line.png.tmpl b/template/en/default/reports/report-line.png.tmpl new file mode 100644 index 000000000..d0c7b2541 --- /dev/null +++ b/template/en/default/reports/report-line.png.tmpl @@ -0,0 +1,53 @@ +[%# 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 <gerv@gerv.net> + #%] + +[% y_label = "Bugs" %] + +[% PROCESS "global/field-descs.html.tmpl" %] + +[% col_field_disp = field_descs.$col_field || col_field %] + +[% IF cumulate %] + [% USE graph = GD.Graph.area(width, height) %] + [% graph.set(cumulate => "true") %] +[% ELSE %] + [% USE graph = GD.Graph.lines(width, height) %] +[% END %] + +[% FILTER null; + graph.set(x_label => col_field_disp, + y_label => y_label, + y_tick_number => 8, + x_label_position => 0.5, + legend_placement => "RT", + line_width => 2); + + # Workaround for the fact that set_legend won't take row_names directly, + # because row_names is an array reference rather than an array. + graph.set_legend(row_names.0, row_names.1, row_names.2, row_names.3, + row_names.4, row_names.5, row_names.6, row_names.7, + row_names.8, row_names.9, row_names.10, row_names.11, + row_names.12, row_names.13, row_names.14, row_names.15); + + graph.plot(data.0).png | stdout(1); + END; +-%] + diff --git a/template/en/default/reports/report-pie.png.tmpl b/template/en/default/reports/report-pie.png.tmpl new file mode 100644 index 000000000..f34397e12 --- /dev/null +++ b/template/en/default/reports/report-pie.png.tmpl @@ -0,0 +1,35 @@ +[%# 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 <gerv@gerv.net> + #%] + +[% PROCESS "global/field-descs.html.tmpl" %] + +[% col_field_disp = field_descs.$col_field || col_field %] + +[% FILTER null; + USE graph = GD.Graph.pie(width, height); + + graph.set(title => col_field_disp, + pie_height => 20, + start_angle => 180); + + graph.plot(data.0).png | stdout(1); + END; +-%] diff --git a/template/en/default/reports/report-table.csv.tmpl b/template/en/default/reports/report-table.csv.tmpl index a80a618c8..60f3a4eef 100644 --- a/template/en/default/reports/report-table.csv.tmpl +++ b/template/en/default/reports/report-table.csv.tmpl @@ -19,39 +19,33 @@ # Contributor(s): Gervase Markham <gerv@gerv.net> #%] [%# INTERFACE: - # See report.html.tmpl. + # See report-table.html.tmpl. #%] - -[% tbl_names = names.tbl.keys.sort %] -[% col_names = names.col.keys.sort %] -[% row_names = names.row.keys.sort %] +[% num_bugs = "Number of bugs" %] +[% tbl_field_disp = field_descs.$tbl_field || tbl_field %] +[% col_field_disp = field_descs.$col_field || col_field %] +[% row_field_disp = field_descs.$row_field || row_field %] -[% FOREACH tbl = tbl_names %] - [% IF tbl_field -%] - [% tbl FILTER html %] - [% END %] - - [% row_field FILTER csv -%] - - [% IF col_field -%] - \ [% col_field FILTER csv -%], - [% FOREACH col = col_names -%] - [% col FILTER csv -%], - [% END -%] - [% ELSE -%] - [% -%],Number of bugs - [% END %] +[% "$tbl_field_disp: $tbl\n" FILTER csv IF tbl_field %] +[% row_field_disp FILTER csv IF row_field %] +[% " / " IF col_field AND row_field %] +[% col_field_disp FILTER csv %], +[% IF col_field -%] +[% FOREACH col = col_names -%] + [% col FILTER csv -%], +[% END -%] +[% ELSE -%] + [% num_bugs %], +[% END %] - [% FOREACH row = row_names %] - [% row FILTER csv -%], - [% FOREACH col = col_names %] - [% IF data.$tbl AND data.$tbl.$col AND data.$tbl.$col.$row %] - [% data.$tbl.$col.$row -%], - [% ELSE %] - [% -%]0, - [% END %] +[% FOREACH row = row_names %] + [% row FILTER csv -%], + [% FOREACH col = col_names %] + [% IF data.$tbl AND data.$tbl.$col AND data.$tbl.$col.$row %] + [% data.$tbl.$col.$row -%], + [% ELSE %] + [% -%]0, [% END %] - [% END %] - + [% END %] diff --git a/template/en/default/reports/report-table.html.tmpl b/template/en/default/reports/report-table.html.tmpl index 9767f5030..5074484c3 100644 --- a/template/en/default/reports/report-table.html.tmpl +++ b/template/en/default/reports/report-table.html.tmpl @@ -21,92 +21,51 @@ #%] [%# INTERFACE: - # querybase: The base query for this table, in URL form - # query: The query for this table, in URL form - # data: hash of hash of hash of numbers. Bug counts. - # names: hash of hash of strings. Names of tables, rows and columns. + # buglistbase: The base query for this table, in URL form # col_field: string. Name of the field being plotted as columns. # row_field: string. Name of the field being plotted as rows. # tbl_field: string. Name of the field being plotted as tables. + # col_names: array. List of values for the field being plotted as columns. + # row_names: array. List of values for the field being plotted as rows. + # data: <depends on format>. Data to plot. Only data.$tbl is accessed. + # tbl: Name of a hash in data which is the table to be plotted. #%] [% PROCESS "global/field-descs.html.tmpl" %] -[% tbl_field_disp = field_descs.$tbl_field || tbl_field %] [% col_field_disp = field_descs.$col_field || col_field %] [% row_field_disp = field_descs.$row_field || row_field %] - -[% title = BLOCK %] - Report: - [% "$tbl_field_disp / " IF tbl_field %] - [% "$col_field_disp / " IF col_field %] - [% row_field_disp %] -[% END %] - -[% PROCESS global/header.html.tmpl - style = " - .t1 { background-color: #ffffff } /* white */ - .t2 { background-color: #dfefff } /* light blue */ - .t3 { background-color: #dddddd } /* grey */ - .t4 { background-color: #c3d3ed } /* darker blue */ - .ttotal { background-color: #cfffdf } /* light green */ - " -%] - -<div align="right"> - [% time2str("%Y-%m-%d %H:%M:%S", time) %] -</div> - -[% tbl_names = names.tbl.keys.sort %] -[% col_names = names.col.keys.sort %] -[% row_names = names.row.keys.sort %] - -[% total_name = "Total" %] - -[% FOREACH tbl = tbl_names %] - [% FOREACH row = row_names %] - [% FOREACH col = col_names %] - [% data.$tbl.$col.$row = (data.$tbl.$col.$row || 0) %] - - [% IF tbl_field %] - [%# Calculate values for the Total table %] - [% data.$total_name.$col.$row = - (data.$total_name.$col.$row || 0) + data.$tbl.$col.$row %] - [% END %] - [% END %] - [% END %] -[% END %] -[% IF tbl_field %] - [% tbl_names.push(total_name) %] +[% IF tbl == "-total-" %] + [% urlbase = BLOCK %]buglist.cgi?[% buglistbase %][% END %] +[% ELSE %] + [% urlbase = BLOCK %]buglist.cgi?[% buglistbase %]& + [% tbl_field FILTER url_quote %]=[% tbl FILTER url_quote %][% END %] [% END %] -<div align="center"> - -[% FOREACH tbl = tbl_names %] - <table> - [% IF tbl_field %] - <tr> - <td> - </td> - <td align="center"> - <h2>[% tbl FILTER html %]</h2> - </td> - </tr> - [% END %] +<table> + [% IF tbl_field %] <tr> <td> </td> - <td align="center"> - <strong>[% col_field_disp FILTER html %]</strong> + <td align="center"> + <h2>[% tbl_disp %]</h2> </td> </tr> + [% END %] + <tr> + <td> + </td> + <td align="center"> + <strong>[% col_field_disp FILTER html %]</strong> + </td> + </tr> - <tr> - <td valign="middle"> - <strong>[% row_field_disp FILTER html %]</strong> - </td> - <td> + <tr> + <td valign="middle"> + <strong>[% row_field_disp FILTER html %]</strong> + </td> + <td> [% classes = [ [ "t1", "t2" ] , [ "t3", "t4" ] ] %] @@ -150,8 +109,7 @@ [% col_idx = 1 - col_idx %] <td class="[% classes.$row_idx.$col_idx %]" align="center"> [% IF data.$tbl.$col.$row AND data.$tbl.$col.$row > 0 %] - <a href="buglist.cgi?[% querybase FILTER html %]& - [% tbl_field FILTER url_quote %]=[% tbl FILTER url_quote %]& + <a href="[% urlbase %]& [% row_field FILTER url_quote %]=[% row FILTER url_quote %]& [% col_field FILTER url_quote %]=[% col FILTER url_quote %]"> [% data.$tbl.$col.$row %]</a> @@ -161,8 +119,7 @@ </td> [% END %] <td class="ttotal" align="right"> - <a href="buglist.cgi?[% querybase FILTER html %]& - [% tbl_field FILTER url_quote %]=[% tbl FILTER url_quote %]& + <a href="[% urlbase %]& [% row_field FILTER url_quote %]=[% row FILTER url_quote %]"> [% row_total %]</a> [% grand_total = grand_total + row_total %] @@ -179,8 +136,7 @@ [% NEXT IF col == "" %] <td class="ttotal" align="center"> - <a href="buglist.cgi?[% querybase FILTER html %]& - [% tbl_field FILTER url_quote %]=[% tbl FILTER url_quote %]& + <a href="[% urlbase %]& [% col_field FILTER url_quote %]=[% col FILTER url_quote %]"> [% col_totals.$col %]</a> <strong> @@ -188,24 +144,13 @@ [% END %] <td class="ttotal" align="right"> <strong> - <a href="buglist.cgi?[% querybase FILTER html %]">[% grand_total %]</a> + <a href="buglist.cgi?[% urlbase %]">[% grand_total %]</a> </strong> </td> </tr> </table> - </td> - </tr> - </table> - - <br> - -[% END %] - - <a href="query.cgi?[% query FILTER html %]&format=report-table">Edit this report</a> -</div> - -<br> - -[% PROCESS global/footer.html.tmpl %] + </td> + </tr> +</table> diff --git a/template/en/default/reports/report.csv.tmpl b/template/en/default/reports/report.csv.tmpl new file mode 100644 index 000000000..727acb311 --- /dev/null +++ b/template/en/default/reports/report.csv.tmpl @@ -0,0 +1,26 @@ +[%# 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 <gerv@gerv.net> + #%] +[% PROCESS "global/field-descs.html.tmpl" %] +[% FOREACH tbl = tbl_names %] + [% PROCESS "reports/report-table.csv.tmpl" %] + + +[% END %] diff --git a/template/en/default/reports/report.html.tmpl b/template/en/default/reports/report.html.tmpl new file mode 100644 index 000000000..c4a3edd56 --- /dev/null +++ b/template/en/default/reports/report.html.tmpl @@ -0,0 +1,156 @@ + <!-- 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 <gerv@gerv.net> + #%] + +[%# INTERFACE: + # col_field: string. Name of the field being plotted as columns. + # row_field: string. Name of the field being plotted as rows. + # tbl_field: string. Name of the field being plotted as tables. + # tbl_names: array. List of values for the field being plotted as tables. + # time: integer. Seconds since the epoch. + # data: <depends on format>. Data to plot. + # format: string. Format of the individual reports. + # width: integer. For image charts, height of the image. + # height: integer. For image charts, width of the image. + # switchbase: string. Base URL for format switching. + # cumulate: boolean. For bar/line charts, whether to cumulate data sets. + #%] + +[% DEFAULT width = 600 + height = 350 +%] + +[%# We ignore row_field for pie charts %] +[% IF format == "pie" %] + [% row_field = "" %] +[% END %] + +[% PROCESS "global/field-descs.html.tmpl" %] + +[% tbl_field_disp = field_descs.$tbl_field || tbl_field %] +[% col_field_disp = field_descs.$col_field || col_field %] +[% row_field_disp = field_descs.$row_field || row_field %] + +[% title = BLOCK %] + Report: + [% tbl_field_disp IF tbl_field %] + [% " / " IF tbl_field AND (col_field OR row_field) %] + [% row_field_disp IF row_field %] + [% " / " IF col_field AND row_field %] + [% col_field_disp %] +[% END %] + +[% PROCESS global/header.html.tmpl + style = " + .t1 { background-color: #ffffff } /* white */ + .t2 { background-color: #dfefff } /* light blue */ + .t3 { background-color: #dddddd } /* grey */ + .t4 { background-color: #c3d3ed } /* darker blue */ + .ttotal { background-color: #cfffdf } /* light green */ + " + h3 = time2str("%Y-%m-%d %H:%M:%S", time) +%] + +[% IF debug %] + <p>[% query FILTER html %]</p> +[% END %] + +<div align="center"> + + [% FOREACH tbl = tbl_names %] + [% IF tbl == "-total-" %] + [% tbl_disp = "Total" %] + [% ELSE %] + [% tbl_disp = tbl %] + [% END %] + + [% IF format == "table" %] + [% PROCESS "reports/report-table.html.tmpl" %] + [% ELSE %] + [% IF tbl %] + <h2>[% tbl_disp FILTER html %]</h2> + [% END %] + + [% imageurl = BLOCK %]report.cgi?[% imagebase %]&format= + [% format FILTER url_quote %]&ctype=png&action=plot& + [% IF tbl_field AND tbl != "-total-" %] + [% tbl_field FILTER url_quote %]=[% tbl FILTER url_quote %]& + [% END %]width=[% width %]&height=[% height %] + [% END %] + + <img src="[% imageurl %]" width="[% width %]" height="[% height %]"> + [% END %] + <br> + [% END %] + + <table> + <tr> + <td> + [% formats = [ { name => "pie", description => "Pie" }, + { name => "bar", description => "Bar" }, + { name => "line", description => "Line" }, + { name => "table", description => "Table" } ] %] + + [% formaturl = "report.cgi?$switchbase&width=$width&height=$height" _ + "&action=wrap" %] + [% FOREACH other_format = formats %] + [% NEXT IF other_format.name == "pie" AND row_field %] + [% UNLESS other_format.name == format %] + <a href="[% formaturl %]&format=[% other_format.name %]"> + [% END %] + [% other_format.description %] + [% "</a>" UNLESS other_format.name == format %] | + [% END %] + <a href="[% formaturl %]&ctype=csv">CSV</a> + </td> + + [% IF format != "table" %] + <td> + + </td> + + [% sizeurl = "report.cgi?$switchbase&action=wrap&format=$format" %] + <td align="center"> + <a href="[% sizeurl %]&width=[% width %]&height= + [% height + 100 %]">Taller</a><br> + <a href="[% sizeurl %]&width=[% width - 100 %]&height= + [% height %]">Thinner</a> * + <a href="[% sizeurl %]&width=[% width + 100 %]&height= + [% height %]">Fatter</a> <br> + <a href="[% sizeurl %]&width=[% width %]&height= + [% height - 100 %]">Shorter</a><br> + </td> + [% END %] + <tr> + </table> + + <p> + [% IF format == "table" %] + <a href="query.cgi?[% switchbase %]&format=report-table">Edit + this report</a> + [% ELSE %] + <a href="query.cgi?[% switchbase %]&format=report-graph&chart_format= + [% format %]&cumulate=[% cumulate %]">Edit this report</a> + [% END %] + </p> + +</div> + +[% PROCESS global/footer.html.tmpl %] diff --git a/template/en/default/search/search-report-graph.html.tmpl b/template/en/default/search/search-report-graph.html.tmpl new file mode 100644 index 000000000..8280fb0bb --- /dev/null +++ b/template/en/default/search/search-report-graph.html.tmpl @@ -0,0 +1,112 @@ +<!-- 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 <gerv@gerv.net> + #%] + +[%# 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']);" +%] + +[% PROCESS "search/search-report-select.html.tmpl" %] + +<p> + Produce a pictorial graph of bug counts by choosing one or more fields as + your axes, and then refining your set of bugs using the rest of the form. + If you choose a third axis, it will be represented by multiple tables of data. + Note: vertical axis settings will be ignored for pie charts. +</p> + +[% button_name = "Generate Report" %] + +<form method="get" action="report.cgi" name="reportform"> + +<table align="center"> + <tr> + <td valign="middle"> + <b>Vertical Axis:</b><br> + [% PROCESS select name = 'y_axis_field' %]<br> + <br> + <b>Plot Data Sets:</b><br> + <input type="radio" name="cumulate" value="0" + [% " checked" IF default.cumulate.0 != "1" %]> + Individually<br> + <input type="radio" name="cumulate" value="1" + [% " checked" IF default.cumulate.0 == "1" %]> + Added + + </td> + <td width="150px" height="150px"> + <table border="1" width="100%" height="100%"> + <tr> + <td align="center" valign="middle"> + <b>Multiple Images:</b><br> + [% PROCESS select name = 'z_axis_field' %] + </td> + </tr> + </table> + </td> + <td rowspan="2"> + <b>Format:</b><br> + [% chart_formats = [ + { name => "line", description => "Line Graph" }, + { name => "bar", description => "Bar Chart" }, + { name => "pie", description => "Pie Chart" } ] %] + [% default.chart_format.0 = default.chart_format.0 || "bar" %] + + [% FOREACH chart_format = chart_formats %] + <input type="radio" name="format" + value="[% chart_format.name FILTER html %]" + [% " checked" IF default.chart_format.0 == chart_format.name %]> + [% chart_format.description FILTER html %]<br> + [% END %] + </td> + </tr> + + <tr> + <td> + </td> + <td align="center"> + <b>Horizontal Axis:</b> + [% PROCESS select name = 'x_axis_field' %] + </td> + <td> + </td> + </tr> +</table> + +<hr> + +[% PROCESS search/form.html.tmpl %] + +<br> +<input type="submit" value="[% button_name %]"> +<input type="hidden" name="action" value="wrap"> +<hr> + +[% PROCESS "search/boolean-charts.html.tmpl" %] + +</form> + +[% PROCESS global/footer.html.tmpl %] diff --git a/template/en/default/search/search-report-select.html.tmpl b/template/en/default/search/search-report-select.html.tmpl new file mode 100644 index 000000000..75716eac7 --- /dev/null +++ b/template/en/default/search/search-report-select.html.tmpl @@ -0,0 +1,47 @@ +<!-- 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 <gerv@gerv.net> + #%] + +[%# INTERFACE: + # name: string. The name of the select block to output. + # default.$name.0: string. The default value for the block, if any. + #%] + +[% PROCESS "global/field-descs.html.tmpl" %] + +[% BLOCK select %] + [% fields = ["product", "component", "version", "rep_platform", + "op_sys", "bug_status", "resolution", "bug_severity", + "priority", "target_milestone", "keywords", "assigned_to", + "reporter", "qa_contact", "votes" ] %] + + <select name="[% name FILTER html %]"> + <option value=""><none></option> + + [% FOREACH field = fields %] + [% NEXT IF field == "target_milestone" AND !Param('usetargetmilestone') %] + [% NEXT IF field == "qa_contact" AND !Param('useqacontact') %] + [% NEXT IF field == "votes" AND !Param('usevotes') %] + <option value="[% field FILTER html %]" + [% " selected" IF default.$name.0 == field %]> + [% field_descs.$field || field FILTER html %]</option> + [% END %] + </select> +[% END %] diff --git a/template/en/default/search/search-report-table.html.tmpl b/template/en/default/search/search-report-table.html.tmpl index 73d542124..a26553602 100644 --- a/template/en/default/search/search-report-table.html.tmpl +++ b/template/en/default/search/search-report-table.html.tmpl @@ -29,12 +29,12 @@ onload = "selectProduct(document.forms['reportform']);" %] -[% PROCESS "global/field-descs.html.tmpl" %] +[% PROCESS "search/search-report-select.html.tmpl" %] <p> - Produce a table of bug counts by choosing one or more fields to plot against - each other, and then refining your set of bugs using the rest of the form. If - you choose a third axis, it will be represented by multiple tables of data. + Produce a table of bug counts by choosing one or more fields as your axes, + and then refining your set of bugs using the rest of the form. + If you choose a third axis, it will be represented by multiple tables of data. </p> [% button_name = "Generate Report" %] @@ -47,7 +47,7 @@ </td> <td align="center"> <b>Horizontal Axis:</b> - [% PROCESS select sel = { name => 'x_axis_field', noop = 1 } %] + [% PROCESS select name = 'x_axis_field' %] </td> <td> </td> <td rowspan="2"> @@ -60,14 +60,14 @@ <tr> <td valign="middle" align="center"> <b>Vertical Axis:</b><br> - [% PROCESS select sel = { name => 'y_axis_field' } %] + [% PROCESS select name = 'y_axis_field' %] </td> <td width="150px" height="150px"> <table border="1" width="100%" height="100%"> <tr> <td align="center" valign="middle"> <b>Multiple Tables:</b><br> - [% PROCESS select sel = { name => 'z_axis_field', noop = 1 } %] + [% PROCESS select name = 'z_axis_field' %] </td> </tr> </table> @@ -82,7 +82,7 @@ <br> <input type="submit" value="[% button_name %]"> <input type="hidden" name="format" value="table"> -<input type="hidden" name="action" value="plot"> +<input type="hidden" name="action" value="wrap"> <hr> [% PROCESS "search/boolean-charts.html.tmpl" %] @@ -90,30 +90,3 @@ </form> [% PROCESS global/footer.html.tmpl %] - -[%############################################################################%] -[%# Block for SELECT fields #%] -[%############################################################################%] - -[% BLOCK select %] - [% fields = ["product", "component", "version", "rep_platform", - "op_sys", "bug_status", "resolution", "bug_severity", - "priority", "target_milestone", "keywords", "assigned_to", - "reporter", "qa_contact", "votes" ] %] - - <select name="[% sel.name %]"> - [% IF sel.noop %] - <option value=""><none></option> - [% END %] - - [% FOREACH field = fields %] - [% NEXT IF field == "target_milestone" AND !Param('usetargetmilestone') %] - [% NEXT IF field == "qa_contact" AND !Param('useqacontact') %] - [% NEXT IF field == "votes" AND !Param('usevotes') %] - - <option value="[% field FILTER html %]" - [% " selected" IF default.${sel.name}.0 == field %]> - [% field_descs.$field || field FILTER html %]</option> - [% END %] - </select> -[% END %] |