Commit f9214d52 authored by gerv%gerv.net's avatar gerv%gerv.net

Bug 225687 - add group controls to charts, along with various other cleanups.…

Bug 225687 - add group controls to charts, along with various other cleanups. Patch by gerv; r=joel, a=justdave.
parent e78dc329
...@@ -71,8 +71,8 @@ sub init { ...@@ -71,8 +71,8 @@ sub init {
foreach my $series_id ($cgi->param($param)) { foreach my $series_id ($cgi->param($param)) {
detaint_natural($series_id) detaint_natural($series_id)
|| &::ThrowCodeError("invalid_series_id"); || &::ThrowCodeError("invalid_series_id");
push(@{$self->{'lines'}[$1]}, my $series = new Bugzilla::Series($series_id);
new Bugzilla::Series($series_id)); push(@{$self->{'lines'}[$1]}, $series) if $series;
} }
} }
...@@ -130,9 +130,11 @@ sub add { ...@@ -130,9 +130,11 @@ sub add {
# for inventing something sensible. # for inventing something sensible.
foreach my $series_id (@series_ids) { foreach my $series_id (@series_ids) {
my $series = new Bugzilla::Series($series_id); my $series = new Bugzilla::Series($series_id);
if ($series) {
push(@{$self->{'lines'}}, [$series]); push(@{$self->{'lines'}}, [$series]);
push(@{$self->{'labels'}}, ""); push(@{$self->{'labels'}}, "");
} }
}
} }
# Alter Chart so that the selections are removed from it. # Alter Chart so that the selections are removed from it.
...@@ -199,6 +201,8 @@ sub readData { ...@@ -199,6 +201,8 @@ sub readData {
my $self = shift; my $self = shift;
my @data; my @data;
# Note: you get a bad image if getSeriesIDs returns nothing
# We need to handle errors better.
my $series_ids = join(",", $self->getSeriesIDs()); my $series_ids = join(",", $self->getSeriesIDs());
# Work out the date boundaries for our data. # Work out the date boundaries for our data.
...@@ -206,7 +210,8 @@ sub readData { ...@@ -206,7 +210,8 @@ sub readData {
# The date used is the one given if it's in a sensible range; otherwise, # 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. # it's the earliest or latest date in the database as appropriate.
my $datefrom = $dbh->selectrow_array("SELECT MIN(date) FROM series_data " . my $datefrom = $dbh->selectrow_array("SELECT MIN(series_date) " .
"FROM series_data " .
"WHERE series_id IN ($series_ids)"); "WHERE series_id IN ($series_ids)");
$datefrom = &::str2time($datefrom); $datefrom = &::str2time($datefrom);
...@@ -214,7 +219,8 @@ sub readData { ...@@ -214,7 +219,8 @@ sub readData {
$datefrom = $self->{'datefrom'}; $datefrom = $self->{'datefrom'};
} }
my $dateto = $dbh->selectrow_array("SELECT MAX(date) FROM series_data " . my $dateto = $dbh->selectrow_array("SELECT MAX(series_date) " .
"FROM series_data " .
"WHERE series_id IN ($series_ids)"); "WHERE series_id IN ($series_ids)");
$dateto = &::str2time($dateto); $dateto = &::str2time($dateto);
...@@ -223,12 +229,13 @@ sub readData { ...@@ -223,12 +229,13 @@ sub readData {
} }
# Prepare the query which retrieves the data for each series # Prepare the query which retrieves the data for each series
my $query = "SELECT TO_DAYS(date) - TO_DAYS(FROM_UNIXTIME($datefrom)), " . my $query = "SELECT TO_DAYS(series_date) - " .
"value FROM series_data " . " TO_DAYS(FROM_UNIXTIME($datefrom)), " .
"series_value FROM series_data " .
"WHERE series_id = ? " . "WHERE series_id = ? " .
"AND date >= FROM_UNIXTIME($datefrom)"; "AND series_date >= FROM_UNIXTIME($datefrom)";
if ($dateto) { if ($dateto) {
$query .= " AND date <= FROM_UNIXTIME($dateto)"; $query .= " AND series_date <= FROM_UNIXTIME($dateto)";
} }
my $sth = $dbh->prepare($query); my $sth = $dbh->prepare($query);
...@@ -296,19 +303,24 @@ sub getSeriesIDs { ...@@ -296,19 +303,24 @@ sub getSeriesIDs {
sub getVisibleSeries { sub getVisibleSeries {
my %cats; my %cats;
# List of groups the user is in; use -1 to make sure it's not empty.
my $grouplist = join(", ", (-1, values(%{Bugzilla->user->groups})));
# Get all visible series # Get all visible series
my $dbh = Bugzilla->dbh; my $dbh = Bugzilla->dbh;
my $serieses = $dbh->selectall_arrayref("SELECT cc1.name, cc2.name, " . my $serieses = $dbh->selectall_arrayref("SELECT cc1.name, cc2.name, " .
"series.name, series.series_id " . "series.name, series.series_id " .
"FROM series " . "FROM series " .
"LEFT JOIN series_categories AS cc1 " . "INNER JOIN series_categories AS cc1 " .
" ON series.category = cc1.category_id " . " ON series.category = cc1.id " .
"LEFT JOIN series_categories AS cc2 " . "INNER JOIN series_categories AS cc2 " .
" ON series.subcategory = cc2.category_id " . " ON series.subcategory = cc2.id " .
"LEFT JOIN user_series_map AS ucm " . "LEFT JOIN category_group_map AS cgm " .
" ON series.series_id = ucm.series_id " . " ON series.category = cgm.category_id " .
"WHERE ucm.user_id = 0 OR ucm.user_id = $::userid"); " AND cgm.group_id NOT IN($grouplist) " .
"WHERE creator = " . Bugzilla->user->id . " OR " .
" cgm.category_id IS NULL " .
"GROUP BY series_id");
foreach my $series (@$serieses) { foreach my $series (@$serieses) {
my ($cat, $subcat, $name, $series_id) = @$series; my ($cat, $subcat, $name, $series_id) = @$series;
$cats{$cat}{$subcat}{$name} = $series_id; $cats{$cat}{$subcat}{$name} = $series_id;
......
...@@ -47,6 +47,11 @@ sub new { ...@@ -47,6 +47,11 @@ sub new {
my $arg_count = scalar(@_); my $arg_count = scalar(@_);
# new() can return undef if you pass in a series_id and the user doesn't
# have sufficient permissions. If you create a new series in this way,
# you need to check for an undef return, and act appropriately.
my $retval = $self;
# There are three ways of creating Series objects. Two (CGI and Parameters) # There are three ways of creating Series objects. Two (CGI and Parameters)
# are for use when creating a new series. One (Database) is for retrieving # are for use when creating a new series. One (Database) is for retrieving
# information on existing series. # information on existing series.
...@@ -60,7 +65,7 @@ sub new { ...@@ -60,7 +65,7 @@ sub new {
else { else {
# We've been given a series_id, which should represent an existing # We've been given a series_id, which should represent an existing
# Series. # Series.
$self->initFromDatabase($_[0]); $retval = $self->initFromDatabase($_[0]);
} }
} }
elsif ($arg_count >= 6 && $arg_count <= 8) { elsif ($arg_count >= 6 && $arg_count <= 8) {
...@@ -73,7 +78,7 @@ sub new { ...@@ -73,7 +78,7 @@ sub new {
die("Bad parameters passed in - invalid number of args: $arg_count"); die("Bad parameters passed in - invalid number of args: $arg_count");
} }
return $self; return $retval;
} }
sub initFromDatabase { sub initFromDatabase {
...@@ -86,23 +91,29 @@ sub initFromDatabase { ...@@ -86,23 +91,29 @@ sub initFromDatabase {
my $dbh = Bugzilla->dbh; my $dbh = Bugzilla->dbh;
my @series = $dbh->selectrow_array("SELECT series.series_id, cc1.name, " . my @series = $dbh->selectrow_array("SELECT series.series_id, cc1.name, " .
"cc2.name, series.name, series.creator, series.frequency, " . "cc2.name, series.name, series.creator, series.frequency, " .
"series.query " . "series.query, series.public " .
"FROM series " . "FROM series " .
"LEFT JOIN series_categories AS cc1 " . "LEFT JOIN series_categories AS cc1 " .
" ON series.category = cc1.category_id " . " ON series.category = cc1.id " .
"LEFT JOIN series_categories AS cc2 " . "LEFT JOIN series_categories AS cc2 " .
" ON series.subcategory = cc2.category_id " . " ON series.subcategory = cc2.id " .
"WHERE series.series_id = $series_id"); "LEFT JOIN category_group_map AS cgm " .
" ON series.category = cgm.category_id " .
"LEFT JOIN user_group_map AS ugm " .
" ON cgm.group_id = ugm.group_id " .
" AND ugm.user_id = " . Bugzilla->user->id .
" AND isbless = 0 " .
"WHERE series.series_id = $series_id AND " .
"(public = 1 OR creator = " . Bugzilla->user->id . " OR " .
"(ugm.group_id IS NOT NULL)) " .
"GROUP BY series_id");
if (@series) { if (@series) {
# Note that we calculate $self->{'public'} ourselves instead of passing
# it as the last parameter in @series; this is because isSubscribed()
# requires the rest of the object to be set up correctly.
$self->initFromParameters(@series); $self->initFromParameters(@series);
$self->{'public'} = $self->isSubscribed(PUBLIC_USER_ID); return $self;
} }
else { else {
&::ThrowCodeError("invalid_series_id", { 'series_id' => $series_id }); return undef;
} }
} }
...@@ -146,16 +157,20 @@ sub initFromCGI { ...@@ -146,16 +157,20 @@ sub initFromCGI {
$self->{'query'} = $cgi->canonicalise_query("format", "ctype", "action", $self->{'query'} = $cgi->canonicalise_query("format", "ctype", "action",
"category", "subcategory", "name", "category", "subcategory", "name",
"frequency", "public", "query_format"); "frequency", "public", "query_format");
trick_taint($self->{'query'});
$self->{'public'} = $cgi->param('public') ? 1 : 0; $self->{'public'} = $cgi->param('public') ? 1 : 0;
# Change 'admin' here and in series.html.tmpl, or remove the check
# completely, if you want to change who can make series public.
$self->{'public'} = 0 unless &::UserInGroup('admin');
} }
sub writeToDatabase { sub writeToDatabase {
my $self = shift; my $self = shift;
my $dbh = Bugzilla->dbh; my $dbh = Bugzilla->dbh;
$dbh->do("LOCK TABLES series_categories WRITE, series WRITE, " . $dbh->do("LOCK TABLES series_categories WRITE, series WRITE");
"user_series_map WRITE");
my $category_id = getCategoryID($self->{'category'}); my $category_id = getCategoryID($self->{'category'});
my $subcategory_id = getCategoryID($self->{'subcategory'}); my $subcategory_id = getCategoryID($self->{'subcategory'});
...@@ -173,35 +188,26 @@ sub writeToDatabase { ...@@ -173,35 +188,26 @@ sub writeToDatabase {
my $dbh = Bugzilla->dbh; my $dbh = Bugzilla->dbh;
$dbh->do("UPDATE series SET " . $dbh->do("UPDATE series SET " .
"category = ?, subcategory = ?," . "category = ?, subcategory = ?," .
"name = ?, frequency = ? " . "name = ?, frequency = ?, public = ? " .
"WHERE series_id = ?", undef, "WHERE series_id = ?", undef,
$category_id, $subcategory_id, $self->{'name'}, $category_id, $subcategory_id, $self->{'name'},
$self->{'frequency'}, $self->{'series_id'}); $self->{'frequency'}, $self->{'public'},
$self->{'series_id'});
} }
else { else {
# Insert the new series into the series table # Insert the new series into the series table
$dbh->do("INSERT INTO series (creator, category, subcategory, " . $dbh->do("INSERT INTO series (creator, category, subcategory, " .
"name, frequency, query) VALUES ($self->{'creator'}, " . "name, frequency, query, public) VALUES " .
"($self->{'creator'}, " .
"$category_id, $subcategory_id, " . "$category_id, $subcategory_id, " .
$dbh->quote($self->{'name'}) . ", $self->{'frequency'}," . $dbh->quote($self->{'name'}) . ", $self->{'frequency'}," .
$dbh->quote($self->{'query'}) . ")"); $dbh->quote($self->{'query'}) . ", $self->{'public'})");
# Retrieve series_id # Retrieve series_id
$self->{'series_id'} = $dbh->selectrow_array("SELECT MAX(series_id) " . $self->{'series_id'} = $dbh->selectrow_array("SELECT MAX(series_id) " .
"FROM series"); "FROM series");
$self->{'series_id'} $self->{'series_id'}
|| &::ThrowCodeError("missing_series_id", { 'series' => $self }); || &::ThrowCodeError("missing_series_id", { 'series' => $self });
# Subscribe creator to the newly-created series.
$self->subscribe($self->{'creator'});
}
# Update publicness by changing subscription
if ($self->{'public'}) {
$self->subscribe(PUBLIC_USER_ID);
}
else {
$self->unsubscribe(PUBLIC_USER_ID);
} }
$dbh->do("UNLOCK TABLES"); $dbh->do("UNLOCK TABLES");
...@@ -236,10 +242,11 @@ sub getCategoryID { ...@@ -236,10 +242,11 @@ sub getCategoryID {
# We are quoting this to put it in the DB, so we can remove taint # We are quoting this to put it in the DB, so we can remove taint
trick_taint($category); trick_taint($category);
$category_id = $dbh->selectrow_array("SELECT category_id " . $category_id = $dbh->selectrow_array("SELECT id " .
"from series_categories " . "from series_categories " .
"WHERE name =" . $dbh->quote($category)); "WHERE name =" . $dbh->quote($category));
last if $category_id;
last if defined($category_id);
$dbh->do("INSERT INTO series_categories (name) " . $dbh->do("INSERT INTO series_categories (name) " .
"VALUES (" . $dbh->quote($category) . ")"); "VALUES (" . $dbh->quote($category) . ")");
...@@ -248,39 +255,4 @@ sub getCategoryID { ...@@ -248,39 +255,4 @@ sub getCategoryID {
return $category_id; return $category_id;
} }
sub subscribe {
my $self = shift;
my $userid = shift;
if (!$self->isSubscribed($userid)) {
# Subscribe current user to series_id
my $dbh = Bugzilla->dbh;
$dbh->do("INSERT INTO user_series_map " .
"VALUES($userid, $self->{'series_id'})");
}
}
sub unsubscribe {
my $self = shift;
my $userid = shift;
if ($self->isSubscribed($userid)) {
# Remove current user's subscription to series_id
my $dbh = Bugzilla->dbh;
$dbh->do("DELETE FROM user_series_map " .
"WHERE user_id = $userid AND series_id = $self->{'series_id'}");
}
}
sub isSubscribed {
my $self = shift;
my $userid = shift;
my $dbh = Bugzilla->dbh;
my $issubscribed = $dbh->selectrow_array("SELECT 1 FROM user_series_map " .
"WHERE user_id = $userid " .
"AND series_id = $self->{'series_id'}");
return $issubscribed;
}
1; 1;
...@@ -1974,26 +1974,26 @@ $table{series} = ...@@ -1974,26 +1974,26 @@ $table{series} =
frequency smallint not null, frequency smallint not null,
last_viewed datetime default null, last_viewed datetime default null,
query mediumtext not null, query mediumtext not null,
public tinyint(1) not null default 0,
index(creator), index(creator),
unique(creator, category, subcategory, name)'; unique(creator, category, subcategory, name)';
$table{series_data} = $table{series_data} =
'series_id mediumint not null, 'series_id mediumint not null,
date datetime not null, series_date datetime not null,
value mediumint not null, series_value mediumint not null,
unique(series_id, date)'; unique(series_id, series_date)';
$table{user_series_map} = $table{category_group_map} =
'user_id mediumint not null, 'category_id smallint not null,
series_id mediumint not null, group_id mediumint not null,
index(series_id), unique(category_id, group_id)';
unique(user_id, series_id)';
$table{series_categories} = $table{series_categories} =
'category_id smallint auto_increment primary key, 'id smallint auto_increment primary key,
name varchar(64) not null, name varchar(64) not null,
unique(name)'; unique(name)';
...@@ -2403,6 +2403,7 @@ sub RenameField ($$$) ...@@ -2403,6 +2403,7 @@ sub RenameField ($$$)
print "Updating field $field in table $table ...\n"; print "Updating field $field in table $table ...\n";
my $type = $$ref[1]; my $type = $$ref[1];
$type .= " NOT NULL" if !$$ref[2]; $type .= " NOT NULL" if !$$ref[2];
$type .= " auto_increment" if $$ref[5] =~ /auto_increment/;
$dbh->do("ALTER TABLE $table $dbh->do("ALTER TABLE $table
CHANGE $field CHANGE $field
$newname $type"); $newname $type");
...@@ -3835,6 +3836,35 @@ if ($mapcnt == 0) { ...@@ -3835,6 +3836,35 @@ if ($mapcnt == 0) {
} }
} }
# 2004-07-17 GRM - Remove "subscriptions" concept from charting, and add
# group-based security instead.
if (TableExists("user_series_map")) {
# Oracle doesn't like "date" as a column name, and apparently some DBs
# don't like 'value' either. We use the changes to subscriptions as
# something to hang these renamings off.
RenameField('series_data', 'date', 'series_date');
RenameField('series_data', 'value', 'series_value');
# series_categories.category_id produces a too-long column name for the
# auto-incrementing sequence (Oracle again).
RenameField('series_categories', 'category_id', 'id');
# We nuke all the chart data and re-import it, partly because there were
# several data corruption bugs in the initial cut of the code, and partly
# because otherwise migration is too complex.
print "Deleting possibly-corrupt new-chart data " .
"(it will be re-migrated) ...\n" unless $silent;
$dbh->do("DELETE FROM series");
$dbh->do("DELETE FROM series_data");
$dbh->do("DELETE FROM series_categories");
# No need to migrate the "publicness" from user_series_map, as we've just
# deleted all the series!
AddField("series", "public", "tinyint(1) not null default 0");
$dbh->do("DROP TABLE user_series_map");
}
# 2003-06-26 Copy the old charting data into the database, and create the # 2003-06-26 Copy the old charting data into the database, and create the
# queries that will keep it all running. When the old charting system goes # queries that will keep it all running. When the old charting system goes
# away, if this code ever runs, it'll just find no files and do nothing. # away, if this code ever runs, it'll just find no files and do nothing.
...@@ -3847,11 +3877,15 @@ if (!$series_exists) { ...@@ -3847,11 +3877,15 @@ if (!$series_exists) {
# We prepare the handle to insert the series data # We prepare the handle to insert the series data
my$seriesdatasth = $dbh->prepare("INSERT INTO series_data " . my$seriesdatasth = $dbh->prepare("INSERT INTO series_data " .
"(series_id, date, value) " . "(series_id, series_date, series_value) " .
"VALUES (?, ?, ?)"); "VALUES (?, ?, ?)");
my $deletesth = $dbh->prepare("DELETE FROM series_data my $deletesth = $dbh->prepare("DELETE FROM series_data
WHERE series_id = ? AND date = ?"); WHERE series_id = ? AND series_date = ?");
my $groupmapsth = $dbh->prepare("INSERT INTO category_group_map " .
"(category_id, group_id) " .
"VALUES (?, ?)");
# Fields in the data file (matches the current collectstats.pl) # Fields in the data file (matches the current collectstats.pl)
my @statuses = my @statuses =
...@@ -3957,6 +3991,30 @@ if (!$series_exists) { ...@@ -3957,6 +3991,30 @@ if (!$series_exists) {
$fielddata{$date} || 0); $fielddata{$date} || 0);
} }
} }
# Create the groupsets for the category
my $category_id =
$dbh->selectrow_array("SELECT id " .
"FROM series_categories " .
"WHERE name = " . $dbh->quote($product));
my $product_id =
$dbh->selectrow_array("SELECT id FROM products " .
"WHERE name = " . $dbh->quote($product));
if (defined($category_id) && defined($product_id)) {
# Get all the mandatory groups for this product
my $group_ids =
$dbh->selectcol_arrayref("SELECT group_id " .
"FROM group_control_map " .
"WHERE product_id = $product_id " .
"AND (membercontrol = " . CONTROLMAPMANDATORY .
" OR othercontrol = " . CONTROLMAPMANDATORY . ")");
foreach my $group_id (@$group_ids) {
$groupmapsth->execute($category_id, $group_id);
}
}
} }
} }
......
...@@ -25,7 +25,9 @@ ...@@ -25,7 +25,9 @@
# Jean-Sebastien Guay <jean_seb@hybride.com> # Jean-Sebastien Guay <jean_seb@hybride.com>
# Run me out of cron at midnight to collect Bugzilla statistics. # Run me out of cron at midnight to collect Bugzilla statistics.
#
# To run new charts for a specific date, pass it in on the command line in
# ISO (2004-08-14) format.
use AnyDBM_File; use AnyDBM_File;
use strict; use strict;
...@@ -58,6 +60,7 @@ Bugzilla->switch_to_shadow_db(); ...@@ -58,6 +60,7 @@ Bugzilla->switch_to_shadow_db();
# To recreate the daily statistics, run "collectstats.pl --regenerate" . # To recreate the daily statistics, run "collectstats.pl --regenerate" .
my $regenerate = 0; my $regenerate = 0;
if ($#ARGV >= 0 && $ARGV[0] eq "--regenerate") { if ($#ARGV >= 0 && $ARGV[0] eq "--regenerate") {
shift(@ARGV);
$regenerate = 1; $regenerate = 1;
} }
...@@ -446,9 +449,7 @@ sub CollectSeriesData { ...@@ -446,9 +449,7 @@ sub CollectSeriesData {
# (days_since_epoch + series_id) % frequency = 0. So they'll run every # (days_since_epoch + series_id) % frequency = 0. So they'll run every
# <frequency> days, but the start date depends on the series_id. # <frequency> days, but the start date depends on the series_id.
my $days_since_epoch = int(time() / (60 * 60 * 24)); my $days_since_epoch = int(time() / (60 * 60 * 24));
my $today = today_dash(); my $today = $ARGV[0] || today_dash();
CleanupChartTables() if ($days_since_epoch % 7 == 0);
# We save a copy of the main $dbh and then switch to the shadow and get # We save a copy of the main $dbh and then switch to the shadow and get
# that one too. Remember, these may be the same. # that one too. Remember, these may be the same.
...@@ -465,13 +466,13 @@ sub CollectSeriesData { ...@@ -465,13 +466,13 @@ sub CollectSeriesData {
# We prepare the insertion into the data table, for efficiency. # We prepare the insertion into the data table, for efficiency.
my $sth = $dbh->prepare("INSERT INTO series_data " . my $sth = $dbh->prepare("INSERT INTO series_data " .
"(series_id, date, value) " . "(series_id, series_date, series_value) " .
"VALUES (?, " . $dbh->quote($today) . ", ?)"); "VALUES (?, " . $dbh->quote($today) . ", ?)");
# We delete from the table beforehand, to avoid SQL errors if people run # We delete from the table beforehand, to avoid SQL errors if people run
# collectstats.pl twice on the same day. # collectstats.pl twice on the same day.
my $deletesth = $dbh->prepare("DELETE FROM series_data my $deletesth = $dbh->prepare("DELETE FROM series_data
WHERE series_id = ? AND date = " . WHERE series_id = ? AND series_date = " .
$dbh->quote($today)); $dbh->quote($today));
foreach my $series_id (keys %$serieses) { foreach my $series_id (keys %$serieses) {
...@@ -485,37 +486,23 @@ sub CollectSeriesData { ...@@ -485,37 +486,23 @@ sub CollectSeriesData {
'user' => $user); 'user' => $user);
my $sql = $search->getSQL(); my $sql = $search->getSQL();
my $data;
# We can't die if we get dodgy SQL back for whatever reason, so we
# eval() this and, if it fails, just ignore it and carry on.
# One day we might even log an error.
eval {
$data = $shadow_dbh->selectall_arrayref($sql);
};
if (!$@) {
# We need to count the returned rows. Without subselects, we can't # We need to count the returned rows. Without subselects, we can't
# do this directly in the SQL for all queries. So we do it by hand. # do this directly in the SQL for all queries. So we do it by hand.
my $data = $shadow_dbh->selectall_arrayref($sql);
my $count = scalar(@$data) || 0; my $count = scalar(@$data) || 0;
$deletesth->execute($series_id); $deletesth->execute($series_id);
$sth->execute($series_id, $count); $sth->execute($series_id, $count);
} }
}
sub CleanupChartTables {
Bugzilla->switch_to_main_db();
my $dbh = Bugzilla->dbh;
$dbh->do("LOCK TABLES series WRITE, user_series_map AS usm READ");
# Find all those that no-one subscribes to
my $series_data = $dbh->selectall_arrayref("SELECT series.series_id " .
"FROM series LEFT JOIN user_series_map AS usm " .
"ON series.series_id = usm.series_id " .
"WHERE usm.series_id IS NULL");
my $series_ids = join(",", map({ $_->[0] } @$series_data));
# Stop collecting data on all series which no-one is subscribed to.
if ($series_ids) {
$dbh->do("UPDATE series SET frequency = 0 " .
"WHERE series_id IN($series_ids)");
} }
$dbh->do("UNLOCK TABLES");
Bugzilla->switch_to_shadow_db();
} }
...@@ -1168,11 +1168,11 @@ Reason: %reason% ...@@ -1168,11 +1168,11 @@ Reason: %reason%
name => 'chartgroup', name => 'chartgroup',
desc => 'The name of the group of users who can use the "New Charts" ' . desc => 'The name of the group of users who can use the "New Charts" ' .
'feature. Administrators should ensure that the public categories ' . 'feature. Administrators should ensure that the public categories ' .
'and series definitions do not divulge unwanted information ' . 'and series definitions do not divulge confidential information ' .
'before enabling this for an untrusted population. If left blank, ' . 'before enabling this for an untrusted population. If left blank, ' .
'no users will be able to use New Charts.', 'no users will be able to use New Charts.',
type => 't', type => 't',
default => '' default => 'editbugs'
}, },
{ {
......
...@@ -741,10 +741,8 @@ ...@@ -741,10 +741,8 @@
<para> <para>
Data sets may be public or private. Everyone sees public data sets in Data sets may be public or private. Everyone sees public data sets in
the list, plus any private data sets they are subscribed to. You are the list, but only their creator sees private data sets. Only
automatically subscribed to any data sets you create, but others may administrators can make data sets public.
subscribe to them too if they know about them. Only administrators can
make data sets public.
No two data sets, even two private ones, can have the same set of No two data sets, even two private ones, can have the same set of
category, subcategory and name. So if you are creating private data category, subcategory and name. So if you are creating private data
sets, one idea is to have the Category be your username. sets, one idea is to have the Category be your username.
...@@ -780,11 +778,7 @@ ...@@ -780,11 +778,7 @@
<para> <para>
Once a data set is in the list, one can also perform certain Once a data set is in the list, one can also perform certain
actions on it. actions on it. For example, one can edit the
For example, one can Subscribe to or Unsubscribe from a private
data set. This is useful if someone else has shown you a chart,
and you want to make some of their data sets appear in your list,
so you can use them in your own charts. One can also edit the
data set's parameters (name, frequency etc.) if it's one you data set's parameters (name, frequency etc.) if it's one you
created or if you are an administrator. created or if you are an administrator.
</para> </para>
......
...@@ -34,6 +34,7 @@ require "globals.pl"; ...@@ -34,6 +34,7 @@ require "globals.pl";
use Bugzilla::Constants; use Bugzilla::Constants;
use Bugzilla::Config qw(:DEFAULT $datadir); use Bugzilla::Config qw(:DEFAULT $datadir);
use Bugzilla::Series; use Bugzilla::Series;
use Bugzilla::Util;
use vars qw($template $vars); use vars qw($template $vars);
...@@ -328,15 +329,19 @@ if ($action eq 'new') { ...@@ -328,15 +329,19 @@ if ($action eq 'new') {
my @series; my @series;
my $prodcomp = "&product=$product&component=$component"; my $prodcomp = "&product=" . url_quote($product) .
"&component=" . url_quote($component);
# For localisation reasons, we get the title of the queries from the # For localisation reasons, we get the title of the queries from the
# submitted form. # submitted form.
my $open_name = $cgi->param('open_name'); my $open_name = $cgi->param('open_name');
my $closed_name = $cgi->param('closed_name'); my $closed_name = $cgi->param('closed_name');
my @openedstatuses = OpenStates(); my @openedstatuses = OpenStates();
my $statuses = join("&", map { "bug_status=$_" } @openedstatuses) . $prodcomp; my $statuses =
my $resolved = "field0-0-0=resolution&type0-0-0=notequals&value0-0-0=---" . $prodcomp; join("&", map { "bug_status=" . url_quote($_) } @openedstatuses) .
$prodcomp;
my $resolved = "field0-0-0=resolution&type0-0-0=notequals&value0-0-0=---" .
$prodcomp;
# trick_taint is ok here, as these variables aren't used as a command # trick_taint is ok here, as these variables aren't used as a command
# or in SQL unquoted # or in SQL unquoted
......
...@@ -557,25 +557,27 @@ if ($action eq 'new') { ...@@ -557,25 +557,27 @@ if ($action eq 'new') {
# We do every status, every resolution, and an "opened" one as well. # We do every status, every resolution, and an "opened" one as well.
foreach my $bug_status (@::legal_bug_status) { foreach my $bug_status (@::legal_bug_status) {
push(@series, [$bug_status, "bug_status=$bug_status"]); push(@series, [$bug_status,
"bug_status=" . url_quote($bug_status)]);
} }
foreach my $resolution (@::legal_resolution) { foreach my $resolution (@::legal_resolution) {
next if !$resolution; next if !$resolution;
push(@series, [$resolution, "resolution=$resolution"]); push(@series, [$resolution, "resolution=" .url_quote($resolution)]);
} }
# For localisation reasons, we get the name of the "global" subcategory # For localisation reasons, we get the name of the "global" subcategory
# and the title of the "open" query from the submitted form. # and the title of the "open" query from the submitted form.
my @openedstatuses = ("UNCONFIRMED", "NEW", "ASSIGNED", "REOPENED"); my @openedstatuses = OpenStates();
my $query = join("&", map { "bug_status=$_" } @openedstatuses); my $query =
join("&", map { "bug_status=" . url_quote($_) } @openedstatuses);
push(@series, [$::FORM{'open_name'}, $query]); push(@series, [$::FORM{'open_name'}, $query]);
foreach my $sdata (@series) { foreach my $sdata (@series) {
my $series = new Bugzilla::Series(undef, $product, my $series = new Bugzilla::Series(undef, $product,
$::FORM{'subcategory'}, $::FORM{'subcategory'},
$sdata->[0], $::userid, 1, $sdata->[0], $::userid, 1,
$sdata->[1] . "&product=$product", 1); $sdata->[1] . "&product=" . url_quote($product), 1);
$series->writeToDatabase(); $series->writeToDatabase();
} }
} }
......
...@@ -60,14 +60,6 @@ function subcatSelected() { ...@@ -60,14 +60,6 @@ function subcatSelected() {
[% gttext = "Grand Total" %] [% gttext = "Grand Total" %]
<form method="get" action="chart.cgi" name="chartform"> <form method="get" action="chart.cgi" name="chartform">
<p>
<span style="color: red">
Note: this new charting system is in beta. This means that retention of
data or defined data sets is on a best-efforts basis only, and cannot be
guaranteed. Please file any [% terms.bugs %] you find or enhancement
ideas you think of.
</span>
</p>
<table cellpadding="2" cellspacing="2" border="0"> <table cellpadding="2" cellspacing="2" border="0">
[% IF NOT category OR category.size == 0 %] [% IF NOT category OR category.size == 0 %]
...@@ -144,7 +136,6 @@ function subcatSelected() { ...@@ -144,7 +136,6 @@ function subcatSelected() {
<th></th> <th></th>
<th>Data Set</th> <th>Data Set</th>
<th></th> <th></th>
<th></th>
</tr> </tr>
[%# The external loop has two counters; one which keeps track of where we [%# The external loop has two counters; one which keeps track of where we
...@@ -190,18 +181,6 @@ function subcatSelected() { ...@@ -190,18 +181,6 @@ function subcatSelected() {
</td> </td>
<td align="center"> <td align="center">
[% IF NOT series.public %]
[% IF series.isSubscribed(user.id) %]
<input type="submit" value="Unsubscribe" style="width: 12ex;"
name="action-unsubscribe[% series.series_id %]">
[% ELSE %]
<input type="submit" value="Subscribe" style="width: 12ex;"
name="action-subscribe[% series.series_id %]">
[% END %]
[% END %]
</td>
<td align="center">
[% IF user.id == series.creator OR UserInGroup("admin") %] [% IF user.id == series.creator OR UserInGroup("admin") %]
<a href="chart.cgi?action=edit&series_id= <a href="chart.cgi?action=edit&series_id=
[% series.series_id %]">Edit</a> | [% series.series_id %]">Edit</a> |
...@@ -233,7 +212,6 @@ function subcatSelected() { ...@@ -233,7 +212,6 @@ function subcatSelected() {
<i>[% gttext FILTER html %]</i> <i>[% gttext FILTER html %]</i>
</td> </td>
<td></td> <td></td>
<td></td>
</tr> </tr>
[% END %] [% END %]
<tr> <tr>
...@@ -255,7 +233,7 @@ function subcatSelected() { ...@@ -255,7 +233,7 @@ function subcatSelected() {
</td> </td>
<td></td> <td></td>
<td valign="bottom" colspan="2"> <td valign="bottom">
<b>Date Range:</b> <b>Date Range:</b>
<input type="text" size="12" name="datefrom" <input type="text" size="12" name="datefrom"
value="[% time2str("%Y-%m-%d", chart.datefrom) IF chart.datefrom%]"> value="[% time2str("%Y-%m-%d", chart.datefrom) IF chart.datefrom%]">
...@@ -274,23 +252,9 @@ function subcatSelected() { ...@@ -274,23 +252,9 @@ function subcatSelected() {
[% END %] [% END %]
</form> </form>
<h4>How Subscriptions Work</h4>
<p>
Administrators may mark data sets as public, which then show up in everyone's
list. All others are not public, and you must explicitly subscribe to them in
order for them to appear in your list.
</p>
<p>
When you
[% IF UserInGroup('editbugs') %] [% IF UserInGroup('editbugs') %]
<a href="query.cgi?format=create-series">create a new data set</a>, <h3><a href="query.cgi?format=create-series">Create New Data Set</a></h3>
[% ELSE %]
create a new data set,
[% END %] [% END %]
you are automatically subscribed to it. When the last person unsubscribes
from a data set, data stops being collected.
</p>
[% PROCESS global/footer.html.tmpl %] [% PROCESS global/footer.html.tmpl %]
...@@ -65,10 +65,13 @@ ...@@ -65,10 +65,13 @@
<input type="text" size="2" name="frequency" <input type="text" size="2" name="frequency"
value="[% (default.frequency.0 OR 7) FILTER html %]"> value="[% (default.frequency.0 OR 7) FILTER html %]">
<span style="font-weight: bold;">&nbsp;day(s)</span><br> <span style="font-weight: bold;">&nbsp;day(s)</span><br>
[%# Change 'admin' here and in Series.pm, or remove the check
completely, if you want to change who can make series public. %]
[% IF UserInGroup('admin') %] [% IF UserInGroup('admin') %]
<input type="checkbox" name="public" <input type="checkbox" name="public"
[% "checked='checked'" IF default.public.0 %]> [% "checked='checked'" IF default.public.0 %]>
<span style="font-weight: bold;">Visible to all</span> <span style="font-weight: bold;">Visible to all<br>
(within group restrictions)</span>
[% END %] [% END %]
</td> </td>
</tr> </tr>
......
...@@ -32,11 +32,6 @@ ...@@ -32,11 +32,6 @@
onload = "selectProduct(document.forms['chartform']);" onload = "selectProduct(document.forms['chartform']);"
%] %]
<p style="color: red">
Note: there is currently a restriction that data sets will only count public
[%+ terms.bugs %] (those not in any group).
</p>
<form method="get" action="chart.cgi" name="chartform"> <form method="get" action="chart.cgi" name="chartform">
[% PROCESS search/form.html.tmpl %] [% PROCESS search/form.html.tmpl %]
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment