From 4f6b75a65628b0d86c760309dd81dd03f5c6d308 Mon Sep 17 00:00:00 2001 From: "gerv%gerv.net" <> Date: Thu, 26 Jun 2003 06:22:50 +0000 Subject: Bug 16009 - generic charting. Patch by gerv; r,a=justdave. --- Bugzilla/Series.pm | 262 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 262 insertions(+) create mode 100644 Bugzilla/Series.pm (limited to 'Bugzilla/Series.pm') 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 + +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; -- cgit v1.2.3-24-g4f1b