Bugzilla::Chart, Bugzilla::Series, chart.cgi: code style
parent
59bc1dc86a
commit
75c8392c67
|
@ -1,5 +1,3 @@
|
|||
# -*- 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
|
||||
|
@ -39,7 +37,8 @@ use Date::Format;
|
|||
use Date::Parse;
|
||||
use List::Util qw(max);
|
||||
|
||||
sub new {
|
||||
sub new
|
||||
{
|
||||
my $invocant = shift;
|
||||
my $class = ref($invocant) || $invocant;
|
||||
my ($params) = @_;
|
||||
|
@ -56,157 +55,170 @@ sub new {
|
|||
return $self;
|
||||
}
|
||||
|
||||
sub init {
|
||||
sub init
|
||||
{
|
||||
my $self = shift;
|
||||
my ($params) = @_;
|
||||
|
||||
# The data structure is a list of lists (lines) of Series objects.
|
||||
# The data structure is a list of lists (lines) of Series objects.
|
||||
# There is a separate list for the labels.
|
||||
#
|
||||
# The URL encoding is:
|
||||
# line0=67&line0=73&line1=81&line2=67...
|
||||
# &label0=B+/+R+/+NEW&label1=...
|
||||
# &select0=1&select3=1...
|
||||
# &select0=1&select3=1...
|
||||
# &cumulate=1&datefrom=2002-02-03&dateto=2002-04-04&ctype=html...
|
||||
# >=1&labelgt=Grand+Total
|
||||
foreach my $param (keys %$params) {
|
||||
# >=1&labelgt=Grand+Total
|
||||
foreach my $param (keys %$params)
|
||||
{
|
||||
# Store all the lines
|
||||
if ($param =~ /^line(\d+)$/) {
|
||||
foreach my $series_id (list $params->{$param}) {
|
||||
detaint_natural($series_id)
|
||||
|| ThrowCodeError("invalid_series_id");
|
||||
if ($param =~ /^line(\d+)$/)
|
||||
{
|
||||
foreach my $series_id (list $params->{$param})
|
||||
{
|
||||
detaint_natural($series_id) || ThrowCodeError("invalid_series_id");
|
||||
my $series = new Bugzilla::Series($series_id);
|
||||
push(@{$self->{'lines'}[$1]}, $series) if $series;
|
||||
push @{$self->{lines}[$1]}, $series if $series;
|
||||
}
|
||||
}
|
||||
|
||||
# Store all the labels
|
||||
if ($param =~ /^label(\d+)$/) {
|
||||
$self->{'labels'}[$1] = $params->{$param};
|
||||
}
|
||||
if ($param =~ /^label(\d+)$/)
|
||||
{
|
||||
$self->{labels}[$1] = $params->{$param};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Store the miscellaneous metadata
|
||||
$self->{'cumulate'} = $params->{cumulate} ? 1 : 0;
|
||||
$self->{'gt'} = $params->{gt} ? 1 : 0;
|
||||
$self->{'labelgt'} = $params->{labelgt};
|
||||
$self->{'datefrom'} = $params->{datefrom};
|
||||
$self->{'dateto'} = $params->{dateto};
|
||||
|
||||
$self->{cumulate} = $params->{cumulate} ? 1 : 0;
|
||||
$self->{gt} = $params->{gt} ? 1 : 0;
|
||||
$self->{labelgt} = $params->{labelgt};
|
||||
$self->{datefrom} = $params->{datefrom};
|
||||
$self->{dateto} = $params->{dateto};
|
||||
|
||||
# If we are cumulating, a grand total makes no sense
|
||||
$self->{'gt'} = 0 if $self->{'cumulate'};
|
||||
|
||||
$self->{gt} = 0 if $self->{cumulate};
|
||||
|
||||
# Make sure the dates are ones we are able to interpret
|
||||
foreach my $date ('datefrom', 'dateto') {
|
||||
if ($self->{$date}) {
|
||||
$self->{$date} = str2time($self->{$date})
|
||||
|| ThrowUserError("illegal_date", { date => $self->{$date}});
|
||||
foreach my $date ('datefrom', 'dateto')
|
||||
{
|
||||
if ($self->{$date})
|
||||
{
|
||||
$self->{$date} = str2time($self->{$date})
|
||||
|| ThrowUserError("illegal_date", { date => $self->{$date}});
|
||||
}
|
||||
}
|
||||
|
||||
# datefrom can't be after dateto
|
||||
if ($self->{'datefrom'} && $self->{'dateto'} &&
|
||||
$self->{'datefrom'} > $self->{'dateto'})
|
||||
if ($self->{datefrom} && $self->{dateto} && $self->{datefrom} > $self->{dateto})
|
||||
{
|
||||
ThrowUserError("misarranged_dates",
|
||||
{'datefrom' => $params->{datefrom},
|
||||
'dateto' => $params->{dateto}});
|
||||
}
|
||||
ThrowUserError("misarranged_dates", {
|
||||
datefrom => $params->{datefrom},
|
||||
dateto => $params->{dateto},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
# Alter Chart so that the selected series are added to it.
|
||||
sub add {
|
||||
sub add
|
||||
{
|
||||
my $self = shift;
|
||||
my @series_ids = @_;
|
||||
|
||||
# Get the current size of the series; required for adding Grand Total later
|
||||
my $current_size = scalar($self->getSeriesIDs());
|
||||
|
||||
|
||||
# Count the number of added series
|
||||
my $added = 0;
|
||||
# Create new Series and push them on to the list of lines.
|
||||
# Note that new lines have no label; the display template is responsible
|
||||
# for inventing something sensible.
|
||||
foreach my $series_id (@series_ids) {
|
||||
foreach my $series_id (@series_ids)
|
||||
{
|
||||
my $series = new Bugzilla::Series($series_id);
|
||||
if ($series) {
|
||||
push(@{$self->{'lines'}}, [$series]);
|
||||
push(@{$self->{'labels'}}, "");
|
||||
if ($series)
|
||||
{
|
||||
push @{$self->{lines}}, [ $series ];
|
||||
push @{$self->{labels}}, "";
|
||||
$added++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# If we are going from < 2 to >= 2 series, add the Grand Total line.
|
||||
if (!$self->{'gt'}) {
|
||||
if ($current_size < 2 &&
|
||||
$current_size + $added >= 2)
|
||||
{
|
||||
$self->{'gt'} = 1;
|
||||
}
|
||||
if (!$self->{gt} && $current_size < 2 && $current_size+$added >= 2)
|
||||
{
|
||||
$self->{gt} = 1;
|
||||
}
|
||||
}
|
||||
|
||||
# Alter Chart so that the selections are removed from it.
|
||||
sub remove {
|
||||
sub remove
|
||||
{
|
||||
my $self = shift;
|
||||
my @line_ids = @_;
|
||||
|
||||
foreach my $line_id (@line_ids) {
|
||||
if ($line_id == 65536) {
|
||||
foreach my $line_id (@line_ids)
|
||||
{
|
||||
if ($line_id == 65536)
|
||||
{
|
||||
# Magic value - delete Grand Total.
|
||||
$self->{'gt'} = 0;
|
||||
}
|
||||
else {
|
||||
delete($self->{'lines'}->[$line_id]);
|
||||
delete($self->{'labels'}->[$line_id]);
|
||||
$self->{gt} = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
delete($self->{lines}->[$line_id]);
|
||||
delete($self->{labels}->[$line_id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Alter Chart so that the selections are summed.
|
||||
sub sum {
|
||||
sub sum
|
||||
{
|
||||
my $self = shift;
|
||||
my @line_ids = @_;
|
||||
|
||||
|
||||
# We can't add the Grand Total to things.
|
||||
@line_ids = grep(!/^65536$/, @line_ids);
|
||||
|
||||
@line_ids = grep !/^65536$/, @line_ids;
|
||||
|
||||
# We can't add less than two things.
|
||||
return if scalar(@line_ids) < 2;
|
||||
|
||||
|
||||
my @series;
|
||||
my $label = "";
|
||||
my $biggestlength = 0;
|
||||
|
||||
|
||||
# We rescue the Series objects of all the series involved in the sum.
|
||||
foreach my $line_id (@line_ids) {
|
||||
my @line = @{$self->{'lines'}->[$line_id]};
|
||||
|
||||
foreach my $series (@line) {
|
||||
foreach my $line_id (@line_ids)
|
||||
{
|
||||
my @line = @{$self->{lines}->[$line_id]};
|
||||
foreach my $series (@line)
|
||||
{
|
||||
push(@series, $series);
|
||||
}
|
||||
|
||||
# We keep the label that labels the line with the most series.
|
||||
if (scalar(@line) > $biggestlength) {
|
||||
if (scalar(@line) > $biggestlength)
|
||||
{
|
||||
$biggestlength = scalar(@line);
|
||||
$label = $self->{'labels'}->[$line_id];
|
||||
$label = $self->{labels}->[$line_id];
|
||||
}
|
||||
}
|
||||
|
||||
$self->remove(@line_ids);
|
||||
|
||||
push(@{$self->{'lines'}}, \@series);
|
||||
push(@{$self->{'labels'}}, $label);
|
||||
push(@{$self->{lines}}, \@series);
|
||||
push(@{$self->{labels}}, $label);
|
||||
}
|
||||
|
||||
sub data {
|
||||
sub data
|
||||
{
|
||||
my $self = shift;
|
||||
$self->{'_data'} ||= $self->readData();
|
||||
return $self->{'_data'};
|
||||
$self->{_data} ||= $self->readData();
|
||||
return $self->{_data};
|
||||
}
|
||||
|
||||
# Convert the Chart's data into a plottable form in $self->{'_data'}.
|
||||
sub readData {
|
||||
# Convert the Chart's data into a plottable form in $self->{_data}.
|
||||
sub readData
|
||||
{
|
||||
my $self = shift;
|
||||
my @data;
|
||||
my @maxvals;
|
||||
|
@ -219,25 +231,26 @@ sub readData {
|
|||
|
||||
# Work out the date boundaries for our data.
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
|
||||
# The date used is the one given if it's in a sensible range; otherwise,
|
||||
# it's the earliest or latest date in the database as appropriate.
|
||||
my $datefrom = $dbh->selectrow_array("SELECT MIN(series_date) " .
|
||||
"FROM series_data " .
|
||||
"WHERE series_id IN ($series_ids)");
|
||||
my $datefrom = $dbh->selectrow_array(
|
||||
"SELECT MIN(series_date) FROM series_data WHERE series_id IN ($series_ids)"
|
||||
);
|
||||
$datefrom = str2time($datefrom);
|
||||
|
||||
if ($self->{'datefrom'} && $self->{'datefrom'} > $datefrom) {
|
||||
$datefrom = $self->{'datefrom'};
|
||||
if ($self->{datefrom} && $self->{datefrom} > $datefrom)
|
||||
{
|
||||
$datefrom = $self->{datefrom};
|
||||
}
|
||||
|
||||
my $dateto = $dbh->selectrow_array("SELECT MAX(series_date) " .
|
||||
"FROM series_data " .
|
||||
"WHERE series_id IN ($series_ids)");
|
||||
$dateto = str2time($dateto);
|
||||
my $dateto = $dbh->selectrow_array(
|
||||
"SELECT MAX(series_date) FROM series_data WHERE series_id IN ($series_ids)"
|
||||
);
|
||||
$dateto = str2time($dateto);
|
||||
|
||||
if ($self->{'dateto'} && $self->{'dateto'} < $dateto) {
|
||||
$dateto = $self->{'dateto'};
|
||||
if ($self->{dateto} && $self->{dateto} < $dateto) {
|
||||
$dateto = $self->{dateto};
|
||||
}
|
||||
|
||||
# Convert UNIX times back to a date format usable for SQL queries.
|
||||
|
@ -246,55 +259,61 @@ sub readData {
|
|||
|
||||
# Prepare the query which retrieves the data for each series
|
||||
my $query = "SELECT " . $dbh->sql_to_days('series_date') . " - " .
|
||||
$dbh->sql_to_days('?') . ", series_value " .
|
||||
"FROM series_data " .
|
||||
"WHERE series_id = ? " .
|
||||
"AND series_date >= ?";
|
||||
if ($dateto) {
|
||||
$dbh->sql_to_days('?') . ", series_value" .
|
||||
" FROM series_data WHERE series_id = ? AND series_date >= ?";
|
||||
if ($dateto)
|
||||
{
|
||||
$query .= " AND series_date <= ?";
|
||||
}
|
||||
|
||||
|
||||
my $sth = $dbh->prepare($query);
|
||||
|
||||
my $gt_index = $self->{'gt'} ? scalar(@{$self->{'lines'}}) : undef;
|
||||
my $gt_index = $self->{gt} ? scalar @{$self->{lines}} : undef;
|
||||
my $line_index = 0;
|
||||
|
||||
$maxvals[$gt_index] = 0 if $gt_index;
|
||||
|
||||
my @datediff_total;
|
||||
|
||||
foreach my $line (@{$self->{'lines'}}) {
|
||||
foreach my $line (@{$self->{lines}})
|
||||
{
|
||||
# Even if we end up with no data, we need an empty arrayref to prevent
|
||||
# errors in the PNG-generating code
|
||||
$data[$line_index] = [];
|
||||
$maxvals[$line_index] = 0;
|
||||
|
||||
foreach my $series (@$line) {
|
||||
|
||||
foreach my $series (@$line)
|
||||
{
|
||||
# Get the data for this series and add it on
|
||||
if ($dateto) {
|
||||
$sth->execute($sql_from, $series->{'series_id'}, $sql_from, $sql_to);
|
||||
if ($dateto)
|
||||
{
|
||||
$sth->execute($sql_from, $series->{series_id}, $sql_from, $sql_to);
|
||||
}
|
||||
else {
|
||||
$sth->execute($sql_from, $series->{'series_id'}, $sql_from);
|
||||
else
|
||||
{
|
||||
$sth->execute($sql_from, $series->{series_id}, $sql_from);
|
||||
}
|
||||
my $points = $sth->fetchall_arrayref();
|
||||
|
||||
foreach my $point (@$points) {
|
||||
foreach my $point (@$points)
|
||||
{
|
||||
my ($datediff, $value) = @$point;
|
||||
$data[$line_index][$datediff] ||= 0;
|
||||
$data[$line_index][$datediff] += $value;
|
||||
if ($data[$line_index][$datediff] > $maxvals[$line_index]) {
|
||||
if ($data[$line_index][$datediff] > $maxvals[$line_index])
|
||||
{
|
||||
$maxvals[$line_index] = $data[$line_index][$datediff];
|
||||
}
|
||||
|
||||
$datediff_total[$datediff] += $value;
|
||||
|
||||
# Add to the grand total, if we are doing that
|
||||
if ($gt_index) {
|
||||
if ($gt_index)
|
||||
{
|
||||
$data[$gt_index][$datediff] ||= 0;
|
||||
$data[$gt_index][$datediff] += $value;
|
||||
if ($data[$gt_index][$datediff] > $maxvals[$gt_index]) {
|
||||
if ($data[$gt_index][$datediff] > $maxvals[$gt_index])
|
||||
{
|
||||
$maxvals[$gt_index] = $data[$gt_index][$datediff];
|
||||
}
|
||||
}
|
||||
|
@ -306,54 +325,62 @@ sub readData {
|
|||
}
|
||||
|
||||
# calculate maximum y value
|
||||
if ($self->{'cumulate'}) {
|
||||
if ($self->{cumulate})
|
||||
{
|
||||
# Make sure we do not try to take the max of an array with undef values
|
||||
my @processed_datediff;
|
||||
while (@datediff_total) {
|
||||
while (@datediff_total)
|
||||
{
|
||||
my $datediff = shift @datediff_total;
|
||||
push @processed_datediff, $datediff if defined($datediff);
|
||||
}
|
||||
$self->{'y_max_value'} = max(@processed_datediff);
|
||||
$self->{y_max_value} = max(@processed_datediff);
|
||||
}
|
||||
else {
|
||||
$self->{'y_max_value'} = max(@maxvals);
|
||||
else
|
||||
{
|
||||
$self->{y_max_value} = max(@maxvals);
|
||||
}
|
||||
$self->{'y_max_value'} |= 1; # For log()
|
||||
$self->{y_max_value} |= 1; # For log()
|
||||
|
||||
# Align the max y value:
|
||||
# For one- or two-digit numbers, increase y_max_value until divisible by 8
|
||||
# For larger numbers, see the comments below to figure out what's going on
|
||||
if ($self->{'y_max_value'} < 100) {
|
||||
do {
|
||||
++$self->{'y_max_value'};
|
||||
} while ($self->{'y_max_value'} % 8 != 0);
|
||||
if ($self->{y_max_value} < 100)
|
||||
{
|
||||
do
|
||||
{
|
||||
++$self->{y_max_value};
|
||||
} while ($self->{y_max_value} % 8 != 0);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
# First, get the # of digits in the y_max_value
|
||||
my $num_digits = 1+int(log($self->{'y_max_value'})/log(10));
|
||||
my $num_digits = 1+int(log($self->{y_max_value})/log(10));
|
||||
|
||||
# We want to zero out all but the top 2 digits
|
||||
my $mask_length = $num_digits - 2;
|
||||
$self->{'y_max_value'} /= 10**$mask_length;
|
||||
$self->{'y_max_value'} = int($self->{'y_max_value'});
|
||||
$self->{'y_max_value'} *= 10**$mask_length;
|
||||
$self->{y_max_value} /= 10**$mask_length;
|
||||
$self->{y_max_value} = int($self->{y_max_value});
|
||||
$self->{y_max_value} *= 10**$mask_length;
|
||||
|
||||
# Add 10^$mask_length to the max value
|
||||
# Continue to increase until it's divisible by 8 * 10^($mask_length-1)
|
||||
# (Throwing in the -1 keeps at least the smallest digit at zero)
|
||||
do {
|
||||
$self->{'y_max_value'} += 10**$mask_length;
|
||||
} while ($self->{'y_max_value'} % (8*(10**($mask_length-1))) != 0);
|
||||
do
|
||||
{
|
||||
$self->{y_max_value} += 10**$mask_length;
|
||||
} while ($self->{y_max_value} % (8*(10**($mask_length-1))) != 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
# Add the x-axis labels into the data structure
|
||||
my $date_progression = generateDateProgression($datefrom, $dateto);
|
||||
unshift(@data, $date_progression);
|
||||
unshift @data, $date_progression;
|
||||
|
||||
if ($self->{'gt'}) {
|
||||
if ($self->{gt})
|
||||
{
|
||||
# Add Grand Total to label list
|
||||
push(@{$self->{'labels'}}, $self->{'labelgt'});
|
||||
push @{$self->{labels}}, $self->{labelgt};
|
||||
|
||||
$data[$gt_index] ||= [];
|
||||
}
|
||||
|
@ -362,43 +389,42 @@ sub readData {
|
|||
}
|
||||
|
||||
# Flatten the data structure into a list of series_ids
|
||||
sub getSeriesIDs {
|
||||
sub getSeriesIDs
|
||||
{
|
||||
my $self = shift;
|
||||
my @series_ids;
|
||||
|
||||
foreach my $line (@{$self->{'lines'}}) {
|
||||
foreach my $series (@$line) {
|
||||
push(@series_ids, $series->{'series_id'});
|
||||
foreach my $line (@{$self->{lines}})
|
||||
{
|
||||
foreach my $series (@$line)
|
||||
{
|
||||
push @series_ids, $series->{series_id};
|
||||
}
|
||||
}
|
||||
|
||||
return @series_ids;
|
||||
}
|
||||
|
||||
# Class method to get the data necessary to populate the "select series"
|
||||
# widgets on various pages.
|
||||
sub getVisibleSeries {
|
||||
sub getVisibleSeries
|
||||
{
|
||||
my %cats;
|
||||
|
||||
my $grouplist = Bugzilla->user->groups_as_string;
|
||||
|
||||
|
||||
# Get all visible series
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $serieses = $dbh->selectall_arrayref("SELECT cc1.name, cc2.name, " .
|
||||
"series.name, series.series_id " .
|
||||
"FROM series " .
|
||||
"INNER JOIN series_categories AS cc1 " .
|
||||
" ON series.category = cc1.id " .
|
||||
"INNER JOIN series_categories AS cc2 " .
|
||||
" ON series.subcategory = cc2.id " .
|
||||
"LEFT JOIN category_group_map AS cgm " .
|
||||
" ON series.category = cgm.category_id " .
|
||||
" AND cgm.group_id NOT IN($grouplist) " .
|
||||
"WHERE creator = ? OR (is_public = 1 AND cgm.category_id IS NULL) " .
|
||||
$dbh->sql_group_by('series.series_id', 'cc1.name, cc2.name, ' .
|
||||
'series.name'),
|
||||
undef, Bugzilla->user->id);
|
||||
foreach my $series (@$serieses) {
|
||||
my $serieses = $dbh->selectall_arrayref(
|
||||
"SELECT cc1.name, cc2.name, series.name, series.series_id FROM series".
|
||||
" INNER JOIN series_categories AS cc1 ON series.category = cc1.id" .
|
||||
" INNER JOIN series_categories AS cc2 ON series.subcategory = cc2.id" .
|
||||
" LEFT JOIN category_group_map AS cgm ON series.category = cgm.category_id" .
|
||||
" AND cgm.group_id NOT IN ($grouplist)" .
|
||||
" WHERE creator = ? OR (is_public = 1 AND cgm.category_id IS NULL)" .
|
||||
" GROUP BY series.series_id, cc1.name, cc2.name, series.name",
|
||||
undef, Bugzilla->user->id
|
||||
);
|
||||
foreach my $series (@$serieses)
|
||||
{
|
||||
my ($cat, $subcat, $name, $series_id) = @$series;
|
||||
$cats{$cat}{$subcat}{$name} = $series_id;
|
||||
}
|
||||
|
@ -406,7 +432,8 @@ sub getVisibleSeries {
|
|||
return \%cats;
|
||||
}
|
||||
|
||||
sub generateDateProgression {
|
||||
sub generateDateProgression
|
||||
{
|
||||
my ($datefrom, $dateto) = @_;
|
||||
my @progression;
|
||||
|
||||
|
@ -420,15 +447,17 @@ sub generateDateProgression {
|
|||
$datefrom += $oneday / 3;
|
||||
$dateto += (2 * $oneday) / 3;
|
||||
|
||||
while ($datefrom < $dateto) {
|
||||
push (@progression, time2str("%Y-%m-%d", $datefrom));
|
||||
while ($datefrom < $dateto)
|
||||
{
|
||||
push @progression, time2str("%Y-%m-%d", $datefrom);
|
||||
$datefrom += $oneday;
|
||||
}
|
||||
|
||||
return \@progression;
|
||||
}
|
||||
|
||||
sub dump {
|
||||
sub dump
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
# Make sure we've read in our data
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
# -*- 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
|
||||
|
@ -24,7 +22,7 @@ use strict;
|
|||
|
||||
# This module implements a series - a set of data to be plotted on a chart.
|
||||
#
|
||||
# This Series is in the database if and only if self->{'series_id'} is defined.
|
||||
# This Series is in the database if and only if self->{series_id} is defined.
|
||||
# Note that the series being in the database does not mean that the fields of
|
||||
# this object are the same as the DB entries, as the object may have been
|
||||
# altered.
|
||||
|
@ -33,6 +31,7 @@ package Bugzilla::Series;
|
|||
|
||||
use Bugzilla::Error;
|
||||
use Bugzilla::Util;
|
||||
use Bugzilla::User;
|
||||
|
||||
sub new
|
||||
{
|
||||
|
@ -108,47 +107,51 @@ sub set_all
|
|||
$self->{series_id} ||= $self->existsInDatabase();
|
||||
}
|
||||
|
||||
sub writeToDatabase {
|
||||
sub writeToDatabase
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
my $dbh = Bugzilla->dbh;
|
||||
$dbh->bz_start_transaction();
|
||||
|
||||
my $category_id = getCategoryID($self->{'category'});
|
||||
my $subcategory_id = getCategoryID($self->{'subcategory'});
|
||||
my $category_id = getCategoryID($self->{category});
|
||||
my $subcategory_id = getCategoryID($self->{subcategory});
|
||||
|
||||
my $exists;
|
||||
if ($self->{'series_id'}) {
|
||||
$exists =
|
||||
$dbh->selectrow_array("SELECT series_id FROM series
|
||||
WHERE series_id = $self->{'series_id'}");
|
||||
if ($self->{series_id})
|
||||
{
|
||||
$exists = $dbh->selectrow_array(
|
||||
"SELECT series_id FROM series WHERE series_id = ?",
|
||||
undef, $self->{series_id}
|
||||
);
|
||||
}
|
||||
|
||||
# Is this already in the database?
|
||||
if ($exists) {
|
||||
if ($exists)
|
||||
{
|
||||
# Update existing series
|
||||
my $dbh = Bugzilla->dbh;
|
||||
$dbh->do("UPDATE series SET " .
|
||||
"category = ?, subcategory = ?," .
|
||||
"name = ?, frequency = ?, is_public = ? " .
|
||||
"WHERE series_id = ?", undef,
|
||||
$category_id, $subcategory_id, $self->{'name'},
|
||||
$self->{'frequency'}, $self->{'public'},
|
||||
$self->{'series_id'});
|
||||
$dbh->do(
|
||||
"UPDATE series SET category = ?, subcategory = ?,".
|
||||
" name = ?, frequency = ?, is_public = ? WHERE series_id = ?", undef,
|
||||
$category_id, $subcategory_id, $self->{name},
|
||||
$self->{frequency}, $self->{public}, $self->{series_id}
|
||||
);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
# Insert the new series into the series table
|
||||
$dbh->do("INSERT INTO series (creator, category, subcategory, " .
|
||||
"name, frequency, query, is_public) VALUES " .
|
||||
"(?, ?, ?, ?, ?, ?, ?)", undef,
|
||||
$self->{'creator_id'}, $category_id, $subcategory_id, $self->{'name'},
|
||||
$self->{'frequency'}, $self->{'query'}, $self->{'public'});
|
||||
$dbh->do(
|
||||
"INSERT INTO series (creator, category, subcategory, " .
|
||||
"name, frequency, query, is_public) VALUES " .
|
||||
"(?, ?, ?, ?, ?, ?, ?)", undef,
|
||||
$self->{creator_id}, $category_id, $subcategory_id, $self->{name},
|
||||
$self->{frequency}, $self->{query}, $self->{public}
|
||||
);
|
||||
|
||||
# Retrieve series_id
|
||||
$self->{'series_id'} = $dbh->selectrow_array("SELECT MAX(series_id) " .
|
||||
"FROM series");
|
||||
$self->{'series_id'}
|
||||
|| ThrowCodeError("missing_series_id", { 'series' => $self });
|
||||
$self->{series_id} = $dbh->bz_last_key('series', 'series_id');
|
||||
$self->{series_id} || ThrowCodeError("missing_series_id", { 'series' => $self });
|
||||
}
|
||||
|
||||
$dbh->bz_commit_transaction();
|
||||
|
@ -156,63 +159,66 @@ sub writeToDatabase {
|
|||
|
||||
# Check whether a series with this name, category and subcategory exists in
|
||||
# the DB and, if so, returns its series_id.
|
||||
sub existsInDatabase {
|
||||
sub existsInDatabase
|
||||
{
|
||||
my $self = shift;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
my $category_id = getCategoryID($self->{'category'});
|
||||
my $subcategory_id = getCategoryID($self->{'subcategory'});
|
||||
my $category_id = getCategoryID($self->{category});
|
||||
my $subcategory_id = getCategoryID($self->{subcategory});
|
||||
|
||||
trick_taint($self->{'name'});
|
||||
my $series_id = $dbh->selectrow_array("SELECT series_id " .
|
||||
"FROM series WHERE category = $category_id " .
|
||||
"AND subcategory = $subcategory_id AND name = " .
|
||||
$dbh->quote($self->{'name'}));
|
||||
trick_taint($self->{name});
|
||||
my ($series_id) = $dbh->selectrow_array(
|
||||
"SELECT series_id FROM series WHERE category=? AND subcategory=? AND name=?",
|
||||
undef, $category_id, $subcategory_id, $self->{name}
|
||||
);
|
||||
|
||||
return($series_id);
|
||||
return $series_id;
|
||||
}
|
||||
|
||||
# Get a category or subcategory IDs, creating the category if it doesn't exist.
|
||||
sub getCategoryID {
|
||||
sub getCategoryID
|
||||
{
|
||||
my ($category) = @_;
|
||||
my $category_id;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
# This seems for the best idiom for "Do A. Then maybe do B and A again."
|
||||
while (1) {
|
||||
while (1)
|
||||
{
|
||||
# We are quoting this to put it in the DB, so we can remove taint
|
||||
trick_taint($category);
|
||||
|
||||
$category_id = $dbh->selectrow_array("SELECT id " .
|
||||
"from series_categories " .
|
||||
"WHERE name =" . $dbh->quote($category));
|
||||
$category_id = $dbh->selectrow_array(
|
||||
"SELECT id FROM series_categories WHERE name=?", undef, $category
|
||||
);
|
||||
last if defined $category_id;
|
||||
|
||||
last if defined($category_id);
|
||||
|
||||
$dbh->do("INSERT INTO series_categories (name) " .
|
||||
"VALUES (" . $dbh->quote($category) . ")");
|
||||
$dbh->do("INSERT INTO series_categories (name) VALUES (?)", undef, $category);
|
||||
}
|
||||
|
||||
return $category_id;
|
||||
}
|
||||
|
||||
##########
|
||||
# Methods
|
||||
##########
|
||||
sub id { return $_[0]->{'series_id'}; }
|
||||
sub name { return $_[0]->{'name'}; }
|
||||
###########
|
||||
# Methods #
|
||||
###########
|
||||
|
||||
sub creator {
|
||||
sub id { $_[0]->{series_id} }
|
||||
sub name { $_[0]->{name} }
|
||||
|
||||
sub creator
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
if (!$self->{creator} && $self->{creator_id}) {
|
||||
require Bugzilla::User;
|
||||
if (!$self->{creator} && $self->{creator_id})
|
||||
{
|
||||
$self->{creator} = new Bugzilla::User($self->{creator_id});
|
||||
}
|
||||
return $self->{creator};
|
||||
}
|
||||
|
||||
sub remove_from_db {
|
||||
sub remove_from_db
|
||||
{
|
||||
my $self = shift;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
|
|
215
chart.cgi
215
chart.cgi
|
@ -1,6 +1,4 @@
|
|||
#!/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
|
||||
|
@ -62,7 +60,8 @@ my $dbh = Bugzilla->dbh;
|
|||
|
||||
my $user = Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
if (!Bugzilla->feature('new_charts')) {
|
||||
if (!Bugzilla->feature('new_charts'))
|
||||
{
|
||||
ThrowCodeError('feature_disabled', { feature => 'new_charts' });
|
||||
}
|
||||
|
||||
|
@ -77,14 +76,15 @@ if (grep /^cmd-/, keys %$ARGS)
|
|||
|
||||
my $action = $ARGS->{action};
|
||||
my $series_id = $ARGS->{series_id};
|
||||
$vars->{'doc_section'} = 'reporting.html#charts';
|
||||
$vars->{doc_section} = 'reporting.html#charts';
|
||||
|
||||
# Because some actions are chosen by buttons, we can't encode them as the value
|
||||
# of the action param, because that value is localization-dependent. So, we
|
||||
# encode it in the name, as "action-<action>". Some params even contain the
|
||||
# series_id they apply to (e.g. subscribe, unsubscribe).
|
||||
my @actions = grep /^action-/, keys %$ARGS;
|
||||
if ($actions[0] && $actions[0] =~ /^action-([^\d]+)(\d*)$/) {
|
||||
if ($actions[0] && $actions[0] =~ /^action-([^\d]+)(\d*)$/)
|
||||
{
|
||||
$action = $1;
|
||||
$series_id = $2 if $2;
|
||||
}
|
||||
|
@ -92,26 +92,30 @@ if ($actions[0] && $actions[0] =~ /^action-([^\d]+)(\d*)$/) {
|
|||
$action ||= "assemble";
|
||||
|
||||
# Go to buglist.cgi if we are doing a search.
|
||||
if ($action eq "search") {
|
||||
if ($action eq "search")
|
||||
{
|
||||
delete $ARGS->{$_} for qw(format ctype action);
|
||||
my $params = http_build_query($ARGS);
|
||||
print Bugzilla->cgi->redirect("buglist.cgi" . ($params ? "?$params" : ""));
|
||||
exit;
|
||||
}
|
||||
|
||||
$user->in_group(Bugzilla->params->{"chartgroup"})
|
||||
|| ThrowUserError("auth_failure", {group => Bugzilla->params->{"chartgroup"},
|
||||
action => "use",
|
||||
object => "charts"});
|
||||
$user->in_group(Bugzilla->params->{chartgroup}) || ThrowUserError("auth_failure", {
|
||||
group => Bugzilla->params->{chartgroup},
|
||||
action => "use",
|
||||
object => "charts",
|
||||
});
|
||||
|
||||
# Only admins may create public queries
|
||||
$user->in_group('admin') || delete $ARGS->{public};
|
||||
|
||||
# All these actions relate to chart construction.
|
||||
if ($action =~ /^(assemble|add|remove|sum|subscribe|unsubscribe)$/) {
|
||||
if ($action =~ /^(assemble|add|remove|sum|subscribe|unsubscribe)$/)
|
||||
{
|
||||
# These two need to be done before the creation of the Chart object, so
|
||||
# that the changes they make will be reflected in it.
|
||||
if ($action =~ /^subscribe|unsubscribe$/) {
|
||||
if ($action =~ /^subscribe|unsubscribe$/)
|
||||
{
|
||||
detaint_natural($series_id) || ThrowCodeError("invalid_series_id");
|
||||
my $series = new Bugzilla::Series($series_id);
|
||||
$series->$action($user->id);
|
||||
|
@ -119,31 +123,38 @@ if ($action =~ /^(assemble|add|remove|sum|subscribe|unsubscribe)$/) {
|
|||
|
||||
my $chart = new Bugzilla::Chart($ARGS);
|
||||
|
||||
if ($action =~ /^remove|sum$/) {
|
||||
if ($action eq 'remove' || $action eq 'sum')
|
||||
{
|
||||
$chart->$action(getSelectedLines());
|
||||
}
|
||||
elsif ($action eq "add") {
|
||||
elsif ($action eq "add")
|
||||
{
|
||||
my @series_ids = getAndValidateSeriesIDs();
|
||||
$chart->add(@series_ids);
|
||||
}
|
||||
|
||||
view($chart);
|
||||
}
|
||||
elsif ($action eq "plot") {
|
||||
elsif ($action eq "plot")
|
||||
{
|
||||
plot();
|
||||
}
|
||||
elsif ($action eq "wrap") {
|
||||
elsif ($action eq "wrap")
|
||||
{
|
||||
# For CSV "wrap", we go straight to "plot".
|
||||
if ($ARGS->{ctype} && $ARGS->{ctype} eq "csv") {
|
||||
if ($ARGS->{ctype} && $ARGS->{ctype} eq "csv")
|
||||
{
|
||||
plot();
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
wrap();
|
||||
}
|
||||
}
|
||||
elsif ($action eq "create") {
|
||||
elsif ($action eq "create")
|
||||
{
|
||||
assertCanCreate();
|
||||
check_hash_token($ARGS->{token}, ['create-series']);
|
||||
check_hash_token($ARGS->{token}, [ 'create-series' ]);
|
||||
|
||||
my $q = { %$ARGS };
|
||||
delete $q->{$_} for qw(series_id category newcategory subcategory newsubcategory name frequency public);
|
||||
|
@ -156,21 +167,25 @@ elsif ($action eq "create") {
|
|||
query => http_build_query($q),
|
||||
});
|
||||
|
||||
ThrowUserError("series_already_exists", {'series' => $series})
|
||||
# Check if another series with the same name exists
|
||||
# FIXME: Should be done by validator
|
||||
ThrowUserError("series_already_exists", { series => $series })
|
||||
if $series->existsInDatabase;
|
||||
|
||||
$series->writeToDatabase();
|
||||
$vars->{'message'} = "series_created";
|
||||
$vars->{'series'} = $series;
|
||||
$vars->{message} = "series_created";
|
||||
$vars->{series} = $series;
|
||||
|
||||
my $chart = new Bugzilla::Chart($ARGS);
|
||||
view($chart);
|
||||
}
|
||||
elsif ($action eq "edit") {
|
||||
elsif ($action eq "edit")
|
||||
{
|
||||
my $series = assertCanEdit($series_id);
|
||||
edit($series);
|
||||
}
|
||||
elsif ($action eq "alter") {
|
||||
elsif ($action eq "alter")
|
||||
{
|
||||
my $series = assertCanEdit($series_id);
|
||||
check_hash_token($ARGS->{token}, [ $series->id, $series->name ]);
|
||||
|
||||
|
@ -188,22 +203,24 @@ elsif ($action eq "alter") {
|
|||
# the return value is us or some other series we need to avoid stomping
|
||||
# on.
|
||||
my $id_of_series_in_db = $series->existsInDatabase();
|
||||
if (defined($id_of_series_in_db) && $id_of_series_in_db != $series->{'series_id'})
|
||||
if (defined($id_of_series_in_db) && $id_of_series_in_db != $series->{series_id})
|
||||
{
|
||||
ThrowUserError("series_already_exists", {'series' => $series});
|
||||
}
|
||||
|
||||
$series->writeToDatabase();
|
||||
$vars->{'changes_saved'} = 1;
|
||||
$vars->{changes_saved} = 1;
|
||||
|
||||
edit($series);
|
||||
}
|
||||
elsif ($action eq "confirm-delete") {
|
||||
$vars->{'series'} = assertCanEdit($series_id);
|
||||
elsif ($action eq "confirm-delete")
|
||||
{
|
||||
$vars->{series} = assertCanEdit($series_id);
|
||||
$template->process("reports/delete-series.html.tmpl", $vars)
|
||||
|| ThrowTemplateError($template->error());
|
||||
|| ThrowTemplateError($template->error());
|
||||
}
|
||||
elsif ($action eq "delete") {
|
||||
elsif ($action eq "delete")
|
||||
{
|
||||
my $series = assertCanEdit($series_id);
|
||||
check_hash_token($ARGS->{token}, [$series->id, $series->name]);
|
||||
|
||||
|
@ -211,25 +228,30 @@ elsif ($action eq "delete") {
|
|||
|
||||
$series->remove_from_db();
|
||||
# Remove (sub)categories which no longer have any series.
|
||||
foreach my $cat (qw(category subcategory)) {
|
||||
my $is_used = $dbh->selectrow_array("SELECT COUNT(*) FROM series WHERE $cat = ?",
|
||||
undef, $series->{"${cat}_id"});
|
||||
if (!$is_used) {
|
||||
$dbh->do('DELETE FROM series_categories WHERE id = ?',
|
||||
undef, $series->{"${cat}_id"});
|
||||
foreach my $cat (qw(category subcategory))
|
||||
{
|
||||
my $is_used = $dbh->selectrow_array(
|
||||
"SELECT COUNT(*) FROM series WHERE $cat = ?",
|
||||
undef, $series->{$cat.'_id'}
|
||||
);
|
||||
if (!$is_used)
|
||||
{
|
||||
$dbh->do('DELETE FROM series_categories WHERE id = ?', undef, $series->{$cat.'_id'});
|
||||
}
|
||||
}
|
||||
$dbh->bz_commit_transaction();
|
||||
|
||||
$vars->{'message'} = "series_deleted";
|
||||
$vars->{'series'} = $series;
|
||||
$vars->{message} = "series_deleted";
|
||||
$vars->{series} = $series;
|
||||
view();
|
||||
}
|
||||
elsif ($action eq "convert_search") {
|
||||
elsif ($action eq "convert_search")
|
||||
{
|
||||
my $saved_search = $ARGS->{series_from_search} || '';
|
||||
my ($query) = grep { $_->name eq $saved_search } @{ $user->queries };
|
||||
my $url = '';
|
||||
if ($query) {
|
||||
if ($query)
|
||||
{
|
||||
my $params = http_decode_query($query->query);
|
||||
# These two parameters conflict with the one below.
|
||||
delete $params->{$_} for ('format', 'query_format');
|
||||
|
@ -237,35 +259,37 @@ elsif ($action eq "convert_search") {
|
|||
}
|
||||
print Bugzilla->cgi->redirect(-location => correct_urlbase() . "query.cgi?format=create-series$url");
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
ThrowCodeError("unknown_action");
|
||||
}
|
||||
|
||||
exit;
|
||||
|
||||
# Find any selected series and return either the first or all of them.
|
||||
sub getAndValidateSeriesIDs {
|
||||
my @series_ids = grep(/^\d+$/, list Bugzilla->input_params->{name});
|
||||
|
||||
sub getAndValidateSeriesIDs
|
||||
{
|
||||
my @series_ids = grep /^\d+$/, list Bugzilla->input_params->{name};
|
||||
return wantarray ? @series_ids : $series_ids[0];
|
||||
}
|
||||
|
||||
# Return a list of IDs of all the lines selected in the UI.
|
||||
sub getSelectedLines {
|
||||
my @ids = map { /^select(\d+)$/ ? $1 : () } keys %{ Bugzilla->input_params };
|
||||
|
||||
return @ids;
|
||||
sub getSelectedLines
|
||||
{
|
||||
return map { /^select(\d+)$/ ? $1 : () } keys %{ Bugzilla->input_params };
|
||||
}
|
||||
|
||||
# Check if the user is the owner of series_id or is an admin.
|
||||
sub assertCanEdit {
|
||||
# Check if the user is the owner of series_id or is an admin.
|
||||
sub assertCanEdit
|
||||
{
|
||||
my $series_id = shift;
|
||||
my $user = Bugzilla->user;
|
||||
|
||||
my $series = new Bugzilla::Series($series_id)
|
||||
|| ThrowCodeError('invalid_series_id');
|
||||
|| ThrowCodeError('invalid_series_id');
|
||||
|
||||
if (!$user->in_group('admin') && $series->{creator_id} != $user->id) {
|
||||
if (!$user->in_group('admin') && $series->{creator_id} != $user->id)
|
||||
{
|
||||
ThrowUserError('illegal_series_edit');
|
||||
}
|
||||
|
||||
|
@ -273,104 +297,115 @@ sub assertCanEdit {
|
|||
}
|
||||
|
||||
# Check if the user is permitted to create this series with these parameters.
|
||||
sub assertCanCreate {
|
||||
sub assertCanCreate
|
||||
{
|
||||
my $user = Bugzilla->user;
|
||||
|
||||
$user->in_group("editbugs") || ThrowUserError("illegal_series_creation");
|
||||
|
||||
# Check permission for frequency
|
||||
my $min_freq = 7;
|
||||
if (Bugzilla->input_params->{frequency} < $min_freq && !$user->in_group("admin")) {
|
||||
if (Bugzilla->input_params->{frequency} < $min_freq && !$user->in_group("admin"))
|
||||
{
|
||||
ThrowUserError("illegal_frequency", { 'minimum' => $min_freq });
|
||||
}
|
||||
}
|
||||
|
||||
sub validateWidthAndHeight {
|
||||
$vars->{'width'} = Bugzilla->input_params->{width};
|
||||
$vars->{'height'} = Bugzilla->input_params->{height};
|
||||
sub validateWidthAndHeight
|
||||
{
|
||||
$vars->{width} = Bugzilla->input_params->{width};
|
||||
$vars->{height} = Bugzilla->input_params->{height};
|
||||
|
||||
if (defined($vars->{'width'})) {
|
||||
(detaint_natural($vars->{'width'}) && $vars->{'width'} > 0)
|
||||
|| ThrowCodeError("invalid_dimensions");
|
||||
if (defined($vars->{width}))
|
||||
{
|
||||
(detaint_natural($vars->{width}) && $vars->{width} > 0)
|
||||
|| ThrowCodeError("invalid_dimensions");
|
||||
}
|
||||
|
||||
if (defined($vars->{'height'})) {
|
||||
(detaint_natural($vars->{'height'}) && $vars->{'height'} > 0)
|
||||
|| ThrowCodeError("invalid_dimensions");
|
||||
if (defined($vars->{height}))
|
||||
{
|
||||
(detaint_natural($vars->{height}) && $vars->{height} > 0)
|
||||
|| ThrowCodeError("invalid_dimensions");
|
||||
}
|
||||
|
||||
# The equivalent of 2000 square seems like a very reasonable maximum size.
|
||||
# This is merely meant to prevent accidental or deliberate DOS, and should
|
||||
# have no effect in practice.
|
||||
if ($vars->{'width'} && $vars->{'height'}) {
|
||||
(($vars->{'width'} * $vars->{'height'}) <= 4000000)
|
||||
|| ThrowUserError("chart_too_large");
|
||||
if ($vars->{width} && $vars->{height} && $vars->{width} * $vars->{height} > 4000000)
|
||||
{
|
||||
ThrowUserError("chart_too_large");
|
||||
}
|
||||
}
|
||||
|
||||
sub edit {
|
||||
sub edit
|
||||
{
|
||||
my $series = shift;
|
||||
|
||||
$vars->{'category'} = Bugzilla::Chart::getVisibleSeries();
|
||||
$vars->{'default'} = $series;
|
||||
$vars->{category} = Bugzilla::Chart::getVisibleSeries();
|
||||
$vars->{default} = $series;
|
||||
|
||||
$template->process("reports/edit-series.html.tmpl", $vars)
|
||||
|| ThrowTemplateError($template->error());
|
||||
|| ThrowTemplateError($template->error());
|
||||
}
|
||||
|
||||
sub plot {
|
||||
sub plot
|
||||
{
|
||||
validateWidthAndHeight();
|
||||
|
||||
my $ARGS = Bugzilla->input_params;
|
||||
$vars->{'chart'} = new Bugzilla::Chart($ARGS);
|
||||
$vars->{chart} = new Bugzilla::Chart($ARGS);
|
||||
|
||||
my $format = $template->get_format("reports/chart", "", $ARGS->{ctype});
|
||||
|
||||
# Debugging PNGs is a pain; we need to be able to see the error messages
|
||||
if ($ARGS->{debug}) {
|
||||
if ($ARGS->{debug})
|
||||
{
|
||||
Bugzilla->cgi->send_header();
|
||||
$vars->{chart}->dump();
|
||||
}
|
||||
|
||||
Bugzilla->cgi->send_header($format->{'ctype'});
|
||||
disable_utf8() if ($format->{'ctype'} =~ /^image\//);
|
||||
Bugzilla->cgi->send_header($format->{ctype});
|
||||
disable_utf8() if ($format->{ctype} =~ /^image\//);
|
||||
|
||||
$template->process($format->{'template'}, $vars)
|
||||
|| ThrowTemplateError($template->error());
|
||||
$template->process($format->{template}, $vars)
|
||||
|| ThrowTemplateError($template->error());
|
||||
}
|
||||
|
||||
sub wrap {
|
||||
sub wrap
|
||||
{
|
||||
validateWidthAndHeight();
|
||||
|
||||
|
||||
my $chart = new Bugzilla::Chart(Bugzilla->input_params);
|
||||
|
||||
$vars->{'time'} = localtime(time());
|
||||
|
||||
$vars->{time} = localtime(time());
|
||||
|
||||
my $q = { %{ Bugzilla->input_params } };
|
||||
delete $q->{$_} for qw(action action-wrap ctype format width height);
|
||||
$vars->{'imagebase'} = http_build_query($q);
|
||||
$vars->{imagebase} = http_build_query($q);
|
||||
|
||||
$template->process("reports/chart.html.tmpl", $vars)
|
||||
|| ThrowTemplateError($template->error());
|
||||
|| ThrowTemplateError($template->error());
|
||||
}
|
||||
|
||||
sub view {
|
||||
sub view
|
||||
{
|
||||
my $chart = shift;
|
||||
|
||||
my $ARGS = Bugzilla->input_params;
|
||||
# Set defaults
|
||||
foreach my $field ('category', 'subcategory', 'name', 'ctype') {
|
||||
$vars->{'default'}{$field} = $ARGS->{$field} || 0;
|
||||
foreach my $field ('category', 'subcategory', 'name', 'ctype')
|
||||
{
|
||||
$vars->{default}->{$field} = $ARGS->{$field} || 0;
|
||||
}
|
||||
|
||||
# Pass the state object to the display UI.
|
||||
$vars->{'chart'} = $chart;
|
||||
$vars->{'category'} = Bugzilla::Chart::getVisibleSeries();
|
||||
$vars->{chart} = $chart;
|
||||
$vars->{category} = Bugzilla::Chart::getVisibleSeries();
|
||||
|
||||
# If we have having problems with bad data, we can set debug=1 to dump
|
||||
# the data structure.
|
||||
$chart->dump() if $ARGS->{debug};
|
||||
|
||||
$template->process("reports/create-chart.html.tmpl", $vars)
|
||||
|| ThrowTemplateError($template->error());
|
||||
|| ThrowTemplateError($template->error());
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue