############################################################################### # Codestriker: Copyright (c) 2001, 2002 David Sitsky. All rights reserved. # sits@users.sourceforge.net # # This program is free software; you can redistribute it and modify it under # the terms of the GPL. # Model object for handling project data. package Codestriker::Model::Project; use strict; use Carp; use Encode qw(decode_utf8); use Codestriker::DB::DBI; # Simple private method which returns the mapping of project state id to # its textual representation. sub _state_id_to_string { my ($id) = @_; return 'Open' if $id == 0; return 'Closed' if $id == 1; return 'Deleted' if $id == 2; return "State $id"; } # Simple private method which returns the mapping of project state to # its id. sub _state_string_to_id { my ($state) = @_; return 0 if $state eq 'Open'; return 1 if $state eq 'Closed'; return 2 if $state eq 'Deleted'; return 3; } # Create a new project with all of the specified properties. sub create($$$$) { my ($type, $name, $description) = @_; my $rc = $Codestriker::OK; # Obtain a database connection. my $dbh = Codestriker::DB::DBI->get_connection(); # Check that a project with this name doesn't already exist. my $select = $dbh->prepare_cached('SELECT COUNT(*) FROM project ' . 'WHERE name = ?'); my $success = defined $select; $success &&= $select->execute($name); if ($success) { my ($count) = $select->fetchrow_array(); $select->finish(); if ($count != 0) { $success = 0; $rc = $Codestriker::DUPLICATE_PROJECT_NAME; } } # Create the project entry. my $timestamp = Codestriker->get_timestamp(time); my $create = $dbh->prepare_cached('INSERT INTO project ' . '(name, description, creation_ts, ' . 'modified_ts, version, state ) ' . 'VALUES (?, ?, ?, ?, ?, ?) '); $success &&= $create->execute($name, $description, $timestamp, $timestamp, 0, 0); $success &&= $create->finish(); Codestriker::DB::DBI->release_connection($dbh, $success); return $rc; } # Return all projects in the system. sub list($$) { my ($type, $state) = @_; # Obtain a database connection. my $dbh = Codestriker::DB::DBI->get_connection(); # Store the results into an array of objects. my @results = (); # Retrieve all of the comment information for the specified topicid. my $select = $dbh->prepare_cached('SELECT id, name, description, version, state ' . 'FROM project ORDER BY state, name, creation_ts'); my $success = defined $select; $success &&= $select->execute(); # Store the results in the array. if ($success) { my @data; while (@data = $select->fetchrow_array()) { my $project = {}; $project->{id} = $data[0]; $project->{name} = decode_utf8($data[1]); $project->{description} = decode_utf8($data[2]); $project->{version} = $data[3]; $project->{state} = _state_id_to_string($data[4]); if (!defined $state || $project->{state} eq $state) { push @results, $project; } } $select->finish(); } Codestriker::DB::DBI->release_connection($dbh, $success); die $dbh->errstr unless $success; return @results; } # Read the details of a specific project from the database. sub read($$) { my ($type, $id) = @_; # Obtain a database connection. my $dbh = Codestriker::DB::DBI->get_connection(); my $select = $dbh->prepare_cached('SELECT name, description, version, state ' . 'FROM project WHERE id = ?'); my $success = defined $select; $success &&= $select->execute($id); my ($name, $description, $version, $state); if ($success && ! (($name, $description, $version, $state) = $select->fetchrow_array())) { $success = 0; } $success &&= $select->finish(); my $project = {}; if ($success) { # Populate return object. $project->{id} = $id; $project->{name} = decode_utf8($name); $project->{description} = decode_utf8($description); $project->{version} = $version; $project->{state} = _state_id_to_string($state); } Codestriker::DB::DBI->release_connection($dbh, $success); return $project; } # Update the project details. sub update($$$$$$) { my ($type, $id, $name, $description, $version, $project_state) = @_; # Map the new state to its number. my $new_stateid = _state_string_to_id($project_state); # Obtain a database connection. my $dbh = Codestriker::DB::DBI->get_connection(); # Check that the version reflects the current version in the DB. my $select = $dbh->prepare_cached('SELECT version FROM project ' . 'WHERE id = ?'); my $update = $dbh->prepare_cached('UPDATE project SET version = ?, ' . 'name = ?, description = ?, modified_ts = ?, ' . 'state = ? WHERE id = ?'); my $success = defined $select && defined $update; my $rc = $Codestriker::OK; # Retrieve the current comment data. $success &&= $select->execute($id); # Make sure that the project still exists, and is therefore valid. my $current_version; if ($success && ! (($current_version) = $select->fetchrow_array())) { # Invalid project id. $success = 0; $rc = $Codestriker::INVALID_PROJECT; } $success &&= $select->finish(); # Check the version number. if ($success && $version != $current_version) { $success = 0; $rc = $Codestriker::STALE_VERSION; } # Update the project details. my $timestamp = Codestriker->get_timestamp(time); $success &&= $update->execute($version+1, $name, $description, $timestamp, $new_stateid, $id); Codestriker::DB::DBI->release_connection($dbh, $success); return $rc; } # Update the project details. sub delete($$) { my ($type, $id, $version) = @_; # Obtain a database connection. my $dbh = Codestriker::DB::DBI->get_connection(); # Check that the version reflects the current version in the DB. my $select = $dbh->prepare_cached('SELECT version FROM project ' . 'WHERE id = ?'); # Create the prepared statements. my $delete = $dbh->prepare_cached('DELETE FROM project WHERE id = ?'); my $success = defined $select && defined $delete; my $rc = $Codestriker::OK; # Retrieve the current comment data. $success &&= $select->execute($id); # Make sure that the project still exists, and is therefore valid. my $current_version; if ($success && ! (($current_version) = $select->fetchrow_array())) { # Invalid project id. $success = 0; $rc = $Codestriker::INVALID_PROJECT; } $success &&= $select->finish(); # Check the version number. if ($success && $version != $current_version) { $success = 0; $rc = $Codestriker::STALE_VERSION; } if ($success == 0) { Codestriker::DB::DBI->release_connection($dbh, $success); return $rc; } else { # Delete the topics in this project. my @sort_order; my @topic_query_results; Codestriker::Model::Topic->query("", "", "", "", "", $id, "", "", "", "", "", "", \@sort_order, \@topic_query_results); # Delete each of the topics for this project for (my $index = 0; $index <= $#topic_query_results; $index++) { my $topic_row = $topic_query_results[$index]; my $topicid = $topic_row->{id}; my $topic_delete = Codestriker::Model::Topic->new($topicid); $topic_delete->delete(); } # Now delete the project. $delete->execute($id); Codestriker::DB::DBI->release_connection($dbh, $success); return $rc; } } # Determine the number of open topics in the specified project. sub num_open_topics { my ($type, $id) = @_; my $dbh = Codestriker::DB::DBI->get_connection(); my $count; eval { $count = $dbh->selectrow_array('SELECT COUNT(topic.id) ' . 'FROM topic ' . 'WHERE topic.projectid = ? ' . 'AND topic.state = 0', {}, $id); }; Codestriker::DB::DBI->release_connection($dbh, 1); if ($@) { carp "Problem retrieving count of open topics in project: $@"; } return $count; } # Determine the number of topics in the specified project. sub num_topics { my ($type, $id) = @_; my $dbh = Codestriker::DB::DBI->get_connection(); my $count; eval { $count = $dbh->selectrow_array('SELECT COUNT(topic.id) ' . 'FROM topic ' . 'WHERE topic.projectid = ? ', {}, $id); }; Codestriker::DB::DBI->release_connection($dbh, 1); if ($@) { carp "Problem retrieving count of topics in project: $@"; } return $count; } 1;