# 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 # Lance Larsh 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. # 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. package Bugzilla::Series; use Bugzilla::Error; use Bugzilla::Util; use Bugzilla::User; sub new { my $invocant = shift; my $class = ref($invocant) || $invocant; my ($param) = @_; my $self = bless {}, $class; if ($param =~ /^(\d+)$/so) { # We've been given a series_id, which should represent an existing Series. $self = $self->initFromDatabase($1); } else { $self->set_all($param); } return $self; } sub initFromDatabase { my ($self, $series_id) = @_; my $dbh = Bugzilla->dbh; my $user = Bugzilla->user; detaint_natural($series_id) || ThrowCodeError("invalid_series_id", { series_id => $series_id }); my $grouplist = $user->groups_as_string; my $rows = $dbh->selectall_arrayref( "SELECT s.series_id, cc1.name category, cc2.name subcategory, s.name," . " s.creator creator_id, s.frequency, s.query, s.is_public public," . " s.category category_id, s.subcategory subcategory_id" . " FROM series s" . " INNER JOIN series_categories cc1 ON s.category = cc1.id" . " INNER JOIN series_categories cc2 ON s.subcategory = cc2.id" . " LEFT JOIN category_group_map cgm ON s.category = cgm.category_id" . " AND cgm.group_id NOT IN ($grouplist) " . " WHERE s.series_id = ? AND (s.creator = ? OR (s.is_public = 1 AND cgm.category_id IS NULL))", {Slice=>{}}, $series_id, $user->id ); if (@$rows) { %$self = %{$rows->[0]}; return $self; } return undef; } sub set_all { my $self = shift; my ($params) = @_; $self->{category} = $params->{category} || ThrowUserError("missing_category"); $self->{subcategory} = $params->{subcategory} || ThrowUserError("missing_subcategory"); $self->{name} = $params->{name} || ThrowUserError("missing_name"); $self->{creator_id} = Bugzilla->user->id || undef; $self->{frequency} = $params->{frequency}; detaint_natural($self->{frequency}) || ThrowUserError("missing_frequency"); if (exists $params->{query}) { $self->{query} = $params->{query}; trick_taint($self->{query}); } $self->{public} = 1 && $params->{public}; $self->{series_id} ||= $self->existsInDatabase(); } 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 $exists; 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) { # 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} ); } 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} ); # Retrieve series_id $self->{series_id} = $dbh->bz_last_key('series', 'series_id'); $self->{series_id} || ThrowCodeError("missing_series_id", { 'series' => $self }); } $dbh->bz_commit_transaction(); } # Check whether a series with this name, category and subcategory exists in # the DB and, if so, returns its series_id. sub existsInDatabase { my $self = shift; my $dbh = Bugzilla->dbh; 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=? AND subcategory=? AND name=?", undef, $category_id, $subcategory_id, $self->{name} ); return $series_id; } # 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 id FROM series_categories WHERE name=?", undef, $category ); last if defined $category_id; $dbh->do("INSERT INTO series_categories (name) VALUES (?)", undef, $category); } return $category_id; } ########### # Methods # ########### sub id { $_[0]->{series_id} } sub name { $_[0]->{name} } sub creator { my $self = shift; if (!$self->{creator} && $self->{creator_id}) { $self->{creator} = new Bugzilla::User($self->{creator_id}); } return $self->{creator}; } sub remove_from_db { my $self = shift; my $dbh = Bugzilla->dbh; $dbh->do('DELETE FROM series WHERE series_id = ?', undef, $self->id); } 1;