summaryrefslogtreecommitdiffstats
path: root/Bugzilla/Series.pm
diff options
context:
space:
mode:
authorgerv%gerv.net <>2003-06-26 08:22:50 +0200
committergerv%gerv.net <>2003-06-26 08:22:50 +0200
commit4f6b75a65628b0d86c760309dd81dd03f5c6d308 (patch)
tree5363459b06e75bc620ceab9dfd045b0f66b6c27c /Bugzilla/Series.pm
parentda6143f4aae6af35f60b8230b82f649b3b0cbd05 (diff)
downloadbugzilla-4f6b75a65628b0d86c760309dd81dd03f5c6d308.tar.gz
bugzilla-4f6b75a65628b0d86c760309dd81dd03f5c6d308.tar.xz
Bug 16009 - generic charting. Patch by gerv; r,a=justdave.
Diffstat (limited to 'Bugzilla/Series.pm')
-rw-r--r--Bugzilla/Series.pm262
1 files changed, 262 insertions, 0 deletions
diff --git a/Bugzilla/Series.pm b/Bugzilla/Series.pm
new file mode 100644
index 000000000..bc11389c9
--- /dev/null
+++ b/Bugzilla/Series.pm
@@ -0,0 +1,262 @@
+# -*- Mode: perl; indent-tabs-mode: nil -*-
+#
+# The contents of this file are subject to the Mozilla Public
+# License Version 1.1 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS
+# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+# implied. See the License for the specific language governing
+# rights and limitations under the License.
+#
+# The Original Code is the Bugzilla Bug Tracking System.
+#
+# The Initial Developer of the Original Code is Netscape Communications
+# Corporation. Portions created by Netscape are
+# Copyright (C) 1998 Netscape Communications Corporation. All
+# Rights Reserved.
+#
+# Contributor(s): Gervase Markham <gerv@gerv.net>
+
+use strict;
+use lib ".";
+
+# This module implements a series - a set of data to be plotted on a chart.
+package Bugzilla::Series;
+
+use Bugzilla;
+use Bugzilla::Util;
+use Bugzilla::User;
+
+sub new {
+ my $invocant = shift;
+ my $class = ref($invocant) || $invocant;
+
+ # Create a ref to an empty hash and bless it
+ my $self = {};
+ bless($self, $class);
+
+ if ($#_ == 0) {
+ if (ref($_[0])) {
+ # We've been given a CGI object
+ $self->readParametersFromCGI($_[0]);
+ $self->createInDatabase();
+ }
+ else {
+ # We've been given a series_id.
+ $self->initFromDatabase($_[0]);
+ }
+ }
+ elsif ($#_ >= 3) {
+ $self->initFromParameters(@_);
+ }
+ else {
+ die("Bad parameters passed in - invalid number of args \($#_\)($_)");
+ }
+
+ return $self->{'already_exists'} ? $self->{'series_id'} : $self;
+}
+
+sub initFromDatabase {
+ my $self = shift;
+ my $series_id = shift;
+
+ &::detaint_natural($series_id)
+ || &::ThrowCodeError("invalid_series_id", { 'series_id' => $series_id });
+
+ my $dbh = Bugzilla->dbh;
+ my @series = $dbh->selectrow_array("SELECT series.series_id, cc1.name, " .
+ "cc2.name, series.name, series.creator, series.frequency, " .
+ "series.query " .
+ "FROM series " .
+ "LEFT JOIN series_categories AS cc1 " .
+ " ON series.category = cc1.category_id " .
+ "LEFT JOIN series_categories AS cc2 " .
+ " ON series.subcategory = cc2.category_id " .
+ "WHERE series.series_id = $series_id");
+
+ if (@series) {
+ $self->initFromParameters(@series);
+ }
+ else {
+ &::ThrowCodeError("invalid_series_id", { 'series_id' => $series_id });
+ }
+}
+
+sub initFromParameters {
+ my $self = shift;
+
+ # The first four parameters are compulsory, unless you immediately call
+ # createInDatabase(), in which case series_id can be left off.
+ ($self->{'series_id'}, $self->{'category'}, $self->{'subcategory'},
+ $self->{'name'}, $self->{'creator'}, $self->{'frequency'},
+ $self->{'query'}) = @_;
+
+ $self->{'public'} = $self->isSubscribed(0);
+ $self->{'subscribed'} = $self->isSubscribed($::userid);
+}
+
+sub createInDatabase {
+ my $self = shift;
+
+ # Lock some tables
+ my $dbh = Bugzilla->dbh;
+ $dbh->do("LOCK TABLES series_categories WRITE, series WRITE, " .
+ "user_series_map WRITE");
+
+ my $category_id = getCategoryID($self->{'category'});
+ my $subcategory_id = getCategoryID($self->{'subcategory'});
+
+ $self->{'creator'} = $::userid;
+
+ # Check for the series currently existing
+ trick_taint($self->{'name'});
+ $self->{'series_id'} = $dbh->selectrow_array("SELECT series_id " .
+ "FROM series WHERE category = $category_id " .
+ "AND subcategory = $subcategory_id AND name = " .
+ $dbh->quote($self->{'name'}));
+
+ if ($self->{'series_id'}) {
+ $self->{'already_exists'} = 1;
+ }
+ else {
+ trick_taint($self->{'query'});
+
+ # Insert the new series into the series table
+ $dbh->do("INSERT INTO series (creator, category, subcategory, " .
+ "name, frequency, query) VALUES ($self->{'creator'}, " .
+ "$category_id, $subcategory_id, " .
+ $dbh->quote($self->{'name'}) . ", $self->{'frequency'}," .
+ $dbh->quote($self->{'query'}) . ")");
+
+ # Retrieve series_id
+ $self->{'series_id'} = $dbh->selectrow_array("SELECT MAX(series_id) " .
+ "FROM series");
+ $self->{'series_id'}
+ || &::ThrowCodeError("missing_series_id", { 'series' => $self });
+
+ # Subscribe user to the newly-created series.
+ $self->subscribe($::userid);
+ # Public series are subscribed to by userid 0.
+ $self->subscribe(0) if ($self->{'public'} && $::userid != 0);
+ }
+
+ $dbh->do("UNLOCK TABLES");
+}
+
+# Get a category or subcategory IDs, creating the category if it doesn't exist.
+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) {
+ # We are quoting this to put it in the DB, so we can remove taint
+ trick_taint($category);
+
+ $category_id = $dbh->selectrow_array("SELECT category_id " .
+ "from series_categories " .
+ "WHERE name =" . $dbh->quote($category));
+ last if $category_id;
+
+ $dbh->do("INSERT INTO series_categories (name) " .
+ "VALUES (" . $dbh->quote($category) . ")");
+ }
+
+ return $category_id;
+}
+
+sub readParametersFromCGI {
+ my $self = shift;
+ my $cgi = shift;
+
+ $self->{'category'} = $cgi->param('category')
+ || $cgi->param('newcategory')
+ || &::ThrowUserError("missing_category");
+
+ $self->{'subcategory'} = $cgi->param('subcategory')
+ || $cgi->param('newsubcategory')
+ || &::ThrowUserError("missing_subcategory");
+
+ $self->{'name'} = $cgi->param('name')
+ || &::ThrowUserError("missing_name");
+
+ $self->{'frequency'} = $cgi->param('frequency');
+ detaint_natural($self->{'frequency'})
+ || &::ThrowUserError("missing_frequency");
+
+ $self->{'public'} = $cgi->param('public') ? 1 : 0;
+
+ $self->{'query'} = $cgi->canonicalise_query("format", "ctype", "action",
+ "category", "subcategory", "name",
+ "frequency", "public", "query_format");
+}
+
+sub alter {
+ my $self = shift;
+ my $cgi = shift;
+
+ my $old_public = $self->{'public'};
+
+ # Note: $self->{'query'} will be meaningless after this call
+ $self->readParametersFromCGI($cgi);
+
+ my $category_id = getCategoryID($self->{'category'});
+ my $subcategory_id = getCategoryID($self->{'subcategory'});
+
+ # Update the entry
+ trick_taint($self->{'name'});
+ my $dbh = Bugzilla->dbh;
+ $dbh->do("UPDATE series SET " .
+ "category = $category_id, subcategory = $subcategory_id " .
+ ", name = " . $dbh->quote($self->{'name'}) .
+ ", frequency = $self->{'frequency'} " .
+ "WHERE series_id = $self->{'series_id'}");
+
+ # Update the publicness of this query.
+ if ($old_public && !$self->{'public'}) {
+ $self->unsubscribe(0);
+ }
+ elsif (!$old_public && $self->{'public'}) {
+ $self->subscribe(0);
+ }
+}
+
+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;