]> git.ipfire.org Git - thirdparty/bugzilla.git/commitdiff
Bug 294160: Step 1 (RO): Create libraries for Products, Components, Classifications...
authorlpsolit%gmail.com <>
Wed, 13 Jul 2005 05:02:10 +0000 (05:02 +0000)
committerlpsolit%gmail.com <>
Wed, 13 Jul 2005 05:02:10 +0000 (05:02 +0000)
Bugzilla/Classification.pm [new file with mode: 0644]
Bugzilla/Component.pm [new file with mode: 0644]
Bugzilla/Constants.pm
Bugzilla/Group.pm
Bugzilla/Milestone.pm [new file with mode: 0644]
Bugzilla/Product.pm [new file with mode: 0644]
Bugzilla/Version.pm [new file with mode: 0644]
template/en/default/global/code-error.html.tmpl

diff --git a/Bugzilla/Classification.pm b/Bugzilla/Classification.pm
new file mode 100644 (file)
index 0000000..fd011e6
--- /dev/null
@@ -0,0 +1,193 @@
+# -*- 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.
+#
+# Contributor(s): Tiago R. Mello <timello@async.com.br>
+#
+
+use strict;
+
+package Bugzilla::Classification;
+
+use Bugzilla;
+use Bugzilla::Util;
+
+###############################
+####    Initialization     ####
+###############################
+
+use constant DB_COLUMNS => qw(
+    classifications.id
+    classifications.name
+    classifications.description
+);
+
+our $columns = join(", ", DB_COLUMNS);
+
+###############################
+####       Methods         ####
+###############################
+
+sub new {
+    my $invocant = shift;
+    my $class = ref($invocant) || $invocant;
+    my $self = {};
+    bless($self, $class);
+    return $self->_init(@_);
+}
+
+sub _init {
+    my $self = shift;
+    my ($param) = @_;
+    my $dbh = Bugzilla->dbh;
+
+    my $id = $param unless (ref $param eq 'HASH');
+    my $classification;
+
+    if (defined $id && detaint_natural($id)) {
+
+        $classification = $dbh->selectrow_hashref(qq{
+            SELECT $columns FROM classifications
+            WHERE id = ?}, undef, $id);
+
+    } elsif (defined $param->{'name'}) {
+
+        trick_taint($param->{'name'});
+        $classification = $dbh->selectrow_hashref(qq{
+            SELECT $columns FROM classifications
+            WHERE name = ?}, undef, $param->{'name'});
+    } else {
+        ThrowCodeError('bad_arg',
+            {argument => 'param',
+             function => 'Bugzilla::Classification::_init'});
+    }
+
+    return undef unless (defined $classification);
+
+    foreach my $field (keys %$classification) {
+        $self->{$field} = $classification->{$field};
+    }
+    return $self;
+}
+
+sub product_count {
+    my $self = shift;
+    my $dbh = Bugzilla->dbh;
+
+    if (!defined $self->{'product_count'}) {
+        $self->{'product_count'} = $dbh->selectrow_array(q{
+            SELECT COUNT(*) FROM products
+            WHERE classification_id = ?}, undef, $self->id);
+    }
+    return $self->{'product_count'};
+}
+
+###############################
+####      Accessors        ####
+###############################
+
+sub id          { return $_[0]->{'id'};          }
+sub name        { return $_[0]->{'name'};        }
+sub description { return $_[0]->{'description'}; }
+
+###############################
+####      Subroutines      ####
+###############################
+
+sub get_all_classifications () {
+    my $dbh = Bugzilla->dbh;
+
+    my $ids = $dbh->selectcol_arrayref(q{
+        SELECT id FROM classifications});
+
+    my $classifications;
+    foreach my $id (@$ids) {
+        $classifications->{$id} = new Bugzilla::Classification($id);
+    }
+    return $classifications;
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+Bugzilla::Classification - Bugzilla classification class.
+
+=head1 SYNOPSIS
+
+    use Bugzilla::Classification;
+
+    my $classification = new Bugzilla::Classification(1);
+    my $classification = new Bugzilla::Classification({name => 'Acme'});
+
+    my $id = $classification->id;
+    my $name = $classification->name;
+    my $description = $classification->description;
+    my $product_count = $classification->product_count;
+
+    my $hash_ref = Bugzilla::Classification::get_all_classifications();
+    my $classification = $hash_ref->{1};
+
+=head1 DESCRIPTION
+
+Classification.pm represents a Classification object.
+
+A Classification is a higher-level grouping of Bugzilla Products.
+
+=head1 METHODS
+
+=over
+
+=item C<new($param)>
+
+ Description: The constructor is used to load an existing
+              classification by passing a classification
+              id or classification name using a hash.
+
+ Params:      $param - If you pass an integer, the integer is the
+                      classification_id from the database that we
+                      want to read in. If you pass in a hash with
+                      'name' key, then the value of the name key
+                      is the name of a classification from the DB.
+
+ Returns:     A Bugzilla::Classification object.
+
+=item C<product_count()>
+
+ Description: Returns the total number of products that belong to
+              the classification.
+
+ Params:      none.
+
+ Returns:     Integer - The total of products inside the classification.
+
+=back
+
+=head1 SUBROUTINES
+
+=over
+
+=item C<get_all_classifications()>
+
+ Description: Returns all Bugzilla classifications.
+
+ Params:      none.
+
+ Returns:     A hash with classification id as key and
+              Bugzilla::Classification object as value.
+
+=back
+
+=cut
diff --git a/Bugzilla/Component.pm b/Bugzilla/Component.pm
new file mode 100644 (file)
index 0000000..216616d
--- /dev/null
@@ -0,0 +1,198 @@
+# -*- 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.
+#
+# Contributor(s): Tiago R. Mello <timello@async.com.br>
+#
+
+use strict;
+
+package Bugzilla::Component;
+
+use Bugzilla;
+use Bugzilla::Util;
+use Bugzilla::Error;
+
+###############################
+####    Initialization     ####
+###############################
+
+use constant DB_COLUMNS => qw(
+    components.id
+    components.name
+    components.product_id
+    components.initialowner
+    components.initialqacontact
+    components.description
+);
+
+our $columns = join(", ", DB_COLUMNS);
+
+###############################
+####       Methods         ####
+###############################
+
+sub new {
+    my $invocant = shift;
+    my $class = ref($invocant) || $invocant;
+    my $self = {};
+    bless($self, $class);
+    return $self->_init(@_);
+}
+
+sub _init {
+    my $self = shift;
+    my ($param) = (@_);
+    my $dbh = Bugzilla->dbh;
+
+    my $id = $param unless (ref $param eq 'HASH');
+    my $component;
+
+    if (defined $id && detaint_natural($id)) {
+
+        $component = $dbh->selectrow_hashref(qq{
+            SELECT $columns FROM components
+            WHERE id = ?}, undef, $id);
+
+    } elsif (defined $param->{'product_id'}
+        && detaint_natural($param->{'product_id'})
+        && defined $param->{'name'}) {
+
+        trick_taint($param->{'name'});
+
+        $component = $dbh->selectrow_hashref(qq{
+            SELECT $columns FROM components
+            WHERE name = ? AND product_id = ?}, undef,
+            ($param->{'name'}, $param->{'product_id'}));
+    } else {
+        ThrowCodeError('bad_arg',
+            {argument => 'param',
+             function => 'Bugzilla::Component::_init'});
+    }
+
+    return undef unless (defined $component);
+
+    foreach my $field (keys %$component) {
+        $self->{$field} = $component->{$field};
+    }
+    return $self;
+}
+
+###############################
+####      Accessors        ####
+###############################
+
+sub id                 { return $_[0]->{'id'};               }
+sub name               { return $_[0]->{'name'};             }
+sub description        { return $_[0]->{'description'};      }
+sub product_id         { return $_[0]->{'product_id'};       }
+sub default_assignee   { return $_[0]->{'initialowner'};     }
+sub default_qa_contact { return $_[0]->{'initialqacontact'}; }
+
+###############################
+####      Subroutines      ####
+###############################
+
+sub get_components_by_product ($) {
+    my ($product_id) = @_;
+    my $dbh = Bugzilla->dbh;
+
+    my $stored_product_id = $product_id;
+    unless (detaint_natural($product_id)) {
+        ThrowCodeError(
+            'invalid_numeric_argument',
+            {argument => 'product_id',
+             value    => $stored_product_id,
+             function =>
+                'Bugzilla::Component::get_components_by_product'}
+        );
+    }
+
+    my $ids = $dbh->selectcol_arrayref(q{
+        SELECT id FROM components
+        WHERE product_id = ?}, undef, $product_id);
+
+    my $components;
+    foreach my $id (@$ids) {
+        $components->{$id} = new Bugzilla::Component($id);
+    }
+    return $components;
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+Bugzilla::Component - Bugzilla product component class.
+
+=head1 SYNOPSIS
+
+    use Bugzilla::Component;
+
+    my $component = new Bugzilla::Component(1);
+    my $component = new Bugzilla::Component({product_id => 1,
+                                             name       => 'AcmeComp'});
+
+    my $id                 = $component->id;
+    my $name               = $component->name;
+    my $description        = $component->description;
+    my $product_id         = $component->product_id;
+    my $default_assignee   = $component->default_assignee;
+    my $default_qa_contact = $component->default_qa_contact;
+
+    my $hash_ref = Bugzilla::Component::get_components_by_product(1);
+    my $component = $hash_ref->{1};
+
+=head1 DESCRIPTION
+
+Component.pm represents a Product Component object.
+
+=head1 METHODS
+
+=over
+
+=item C<new($param)>
+
+ Description: The constructor is used to load an existing component
+              by passing a component id or a hash with the product
+              id and the component name.
+
+ Params:      $param - If you pass an integer, the integer is the
+                       component id from the database that we want to
+                       read in. If you pass in a hash with 'name' key,
+                       then the value of the name key is the name of a
+                       component from the DB.
+
+ Returns:     A Bugzilla::Component object.
+
+=back
+
+=head1 SUBROUTINES
+
+=over
+
+=item C<get_components_by_product($product_id)>
+
+ Description: Returns all Bugzilla components that belong to the
+              supplied product.
+
+ Params:      $product_id - Integer with a Bugzilla product id.
+
+ Returns:     A hash with component id as key and Bugzilla::Component
+              object as value.
+
+=back
+
+=cut
index a626294206bdcd98ea82ed5fcdfe8bbd9c3144ff..95b8af98c9025df1dd6fd55bc640991346df8751 100644 (file)
@@ -87,6 +87,8 @@ use base qw(Exporter);
     EVT_FLAG_REQUESTED EVT_REQUESTED_FLAG
 
     FULLTEXT_BUGLIST_LIMIT
+
+    ADMIN_GROUP_NAME
 );
 
 @Bugzilla::Constants::EXPORT_OK = qw(contenttypes);
@@ -233,4 +235,7 @@ use constant GLOBAL_EVENTS => EVT_FLAG_REQUESTED, EVT_REQUESTED_FLAG;
 #  a fulltext search.
 use constant FULLTEXT_BUGLIST_LIMIT => 200;
 
+# Default administration group name.
+use constant ADMIN_GROUP_NAME => 'admin';
+
 1;
index 85e6de5b1fba72efed3ab3fbabe9220763530848..808860274fc22e75e64d7111c4a784ef814ea6ef 100644 (file)
@@ -106,7 +106,7 @@ sub is_active    { return $_[0]->{'isactive'};     }
 #####  Module Subroutines    ###
 ################################
 
-sub ValidateGroupName {
+sub ValidateGroupName ($$) {
     my ($name, @users) = (@_);
     my $dbh = Bugzilla->dbh;
     my $query = "SELECT id FROM groups " .
@@ -125,13 +125,34 @@ sub ValidateGroupName {
     return $ret;
 }
 
+sub get_group_controls_by_product ($) {
+    my ($product_id) = @_;
+    my $dbh = Bugzilla->dbh;
+
+    my $query = qq{SELECT
+                       $columns,
+                       group_control_map.entry,
+                       group_control_map.membercontrol,
+                       group_control_map.othercontrol,
+                       group_control_map.canedit
+                  FROM groups
+                  LEFT JOIN group_control_map
+                        ON groups.id = group_control_map.group_id
+                  WHERE group_control_map.product_id = ?
+                  AND   groups.isbuggroup != 0
+                  ORDER BY groups.name};
+    my $groups = $dbh->selectall_hashref($query, 'id', undef,
+                                         ($product_id));
+    return $groups;
+}
+
 1;
 
 __END__
 
 =head1 NAME
 
-Bugzilla::Group - Object for a Bugzilla group.
+Bugzilla::Group - Bugzilla group class.
 
 =head1 SYNOPSIS
 
@@ -147,6 +168,10 @@ Bugzilla::Group - Object for a Bugzilla group.
     my $user_reg_exp = $group->user_reg_exp;
     my $is_active    = $group->is_active;
 
+    my $group_id = Bugzilla::Group::ValidateGroupName('admin', @users);
+
+    my $grops = Bugzilla::Group::get_group_controls_by_product(1);
+
 =head1 DESCRIPTION
 
 Group.pm represents a Bugzilla Group object.
@@ -157,8 +182,16 @@ Group.pm represents a Bugzilla Group object.
 
 =item C<new($param)>
 
-The constructor is used to load an existing group by passing
-a group id or a hash with the group name.
+ Description: The constructor is used to load an existing group
+              by passing a group id or a hash with the group name.
+
+ Params:      $param - If you pass an integer, the integer is the
+                       group id from the database that we want to
+                       read in. If you pass in a hash with 'name'
+                       key, then the value of the name key is the
+                       name of a product from the DB.
+
+ Returns:     A Bugzilla::Group object.
 
 =back
 
@@ -166,18 +199,29 @@ a group id or a hash with the group name.
 
 =over
 
-=item C<ValidateGroupName($group_name, @users)>
+=item C<ValidateGroupName($name, @users)>
 
-ValidateGroupName checks to see if ANY of the users in the provided list 
-of user objects can see the named group.  It returns the group id if
-successful and undef otherwise.
+ Description: ValidateGroupName checks to see if ANY of the users
+              in the provided list of user objects can see the
+              named group.
 
-=back
+ Params:      $name - String with the group name.
+              @users - An array with Bugzilla::User objects.
+
+ Returns:     It returns the group id if successful
+              and undef otherwise.
+
+=item C<get_group_controls_by_product($product_id)>
+
+ Description: Returns all group controls of a specific product.
+              It is encouraged to use Bugzilla::Product object
+              instead of directly calling this routine.
 
-=head1 AUTHOR
-    
-    Joel Peshkin <bugreport@peshkin.net>
-    Erik Stambaugh <erik@dasbistro.com>
-    Tiago R. Mello <timello@async.com.br>
+ Params:      $product_id - Integer with a Bugzilla product id.
+
+ Returns:     A hash with group id as key and hash containing the
+              group data as value.
+
+=back
 
 =cut
diff --git a/Bugzilla/Milestone.pm b/Bugzilla/Milestone.pm
new file mode 100644 (file)
index 0000000..dad8b6c
--- /dev/null
@@ -0,0 +1,172 @@
+# -*- 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.
+#
+# Contributor(s): Tiago R. Mello <timello@async.com.br>
+
+use strict;
+
+package Bugzilla::Milestone;
+
+use Bugzilla;
+use Bugzilla::Util;
+use Bugzilla::Error;
+
+################################
+#####    Initialization    #####
+################################
+
+use constant DEFAULT_SORTKEY => 0;
+
+use constant DB_COLUMNS => qw(
+    milestones.value
+    milestones.product_id
+    milestones.sortkey
+);
+
+my $columns = join(", ", DB_COLUMNS);
+
+sub new {
+    my $invocant = shift;
+    my $class = ref($invocant) || $invocant;
+    my $self = {};
+    bless($self, $class);
+    return $self->_init(@_);
+}
+
+sub _init {
+    my $self = shift;
+    my ($product_id, $value) = (@_);
+    my $dbh = Bugzilla->dbh;
+
+    my $milestone;
+
+    if (defined $product_id
+        && detaint_natural($product_id)
+        && defined $value) {
+
+        trick_taint($value);
+        $milestone = $dbh->selectrow_hashref(qq{
+            SELECT $columns FROM milestones
+            WHERE value = ?
+            AND product_id = ?}, undef, ($value, $product_id));
+    } else {
+        ThrowCodeError('bad_arg',
+            {argument => 'product_id/value',
+             function => 'Bugzilla::Milestone::_init'});
+    }
+
+    return undef unless (defined $milestone);
+
+    foreach my $field (keys %$milestone) {
+        $self->{$field} = $milestone->{$field};
+    }
+    return $self;
+}
+
+################################
+#####      Accessors      ######
+################################
+
+sub value      { return $_[0]->{'value'};      }
+sub product_id { return $_[0]->{'product_id'}; }
+sub sortkey    { return $_[0]->{'sortkey'};    }
+
+################################
+#####     Subroutines      #####
+################################
+
+sub get_milestones_by_product ($) {
+    my ($product_id) = @_;
+    my $dbh = Bugzilla->dbh;
+
+    my $stored_product_id = $product_id;
+    unless (detaint_natural($product_id)) {
+        ThrowCodeError(
+            'invalid_numeric_argument',
+            {argument => 'product_id',
+             value    => $stored_product_id,
+             function =>
+                'Bugzilla::Milestone::get_milestones_by_product'}
+        );
+    }
+
+    my $values = $dbh->selectcol_arrayref(q{
+        SELECT value FROM milestones
+        WHERE product_id = ?}, undef, $product_id);
+
+    my $milestones;
+    foreach my $value (@$values) {
+        $milestones->{$value} = new Bugzilla::Milestone($product_id,
+                                                        $value);
+    }
+    return $milestones;
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+Bugzilla::Milestone - Bugzilla product milestone class.
+
+=head1 SYNOPSIS
+
+    use Bugzilla::Milestone;
+
+    my $milestone = new Bugzilla::Milestone(1, 'milestone_value');
+
+    my $product_id = $milestone->product_id;
+    my $value = $milestone->value;
+
+    my $hash_ref = Bugzilla::Milestone::get_milestones_by_product(1);
+    my $milestone = $hash_ref->{'milestone_value'};
+
+=head1 DESCRIPTION
+
+Milestone.pm represents a Product Milestone object.
+
+=head1 METHODS
+
+=over
+
+=item C<new($product_id, $value)>
+
+ Description: The constructor is used to load an existing milestone
+              by passing a product id and a milestone value.
+
+ Params:      $product_id - Integer with a Bugzilla product id.
+              $value - String with a milestone value.
+
+ Returns:     A Bugzilla::Milestone object.
+
+=back
+
+=head1 SUBROUTINES
+
+=over
+
+=item C<get_milestones_by_product($product_id)>
+
+ Description: Returns all Bugzilla product milestones that belong
+              to the supplied product.
+
+ Params:      $product_id - Integer with a Bugzilla product id.
+
+ Returns:     A hash with milestone value as key and a
+              Bugzilla::Milestone object as hash value.
+
+=back
+
+=cut
diff --git a/Bugzilla/Product.pm b/Bugzilla/Product.pm
new file mode 100644 (file)
index 0000000..28f1e73
--- /dev/null
@@ -0,0 +1,336 @@
+# -*- 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.
+#
+# Contributor(s): Tiago R. Mello <timello@async.com.br>
+
+use strict;
+
+package Bugzilla::Product;
+
+use Bugzilla;
+use Bugzilla::Component;
+use Bugzilla::Classification;
+use Bugzilla::Version;
+use Bugzilla::Milestone;
+
+use Bugzilla::Util;
+use Bugzilla::Group;
+use Bugzilla::Error;
+
+use constant DEFAULT_CLASSIFICATION_ID => 1;
+
+###############################
+####    Initialization     ####
+###############################
+
+use constant DB_COLUMNS => qw(
+   products.id
+   products.name
+   products.classification_id
+   products.description
+   products.milestoneurl
+   products.disallownew
+   products.votesperuser
+   products.maxvotesperbug
+   products.votestoconfirm
+   products.defaultmilestone
+);
+
+my $columns = join(", ", DB_COLUMNS);
+
+sub new {
+    my $invocant = shift;
+    my $class = ref($invocant) || $invocant;
+    my $self = {};
+    bless($self, $class);
+    return $self->_init(@_);
+}
+
+sub _init {
+    my $self = shift;
+    my ($param) = @_;
+    my $dbh = Bugzilla->dbh;
+
+    my $id = $param unless (ref $param eq 'HASH');
+    my $product;
+
+    if (defined $id && detaint_natural($id)) {
+
+        $product = $dbh->selectrow_hashref(qq{
+            SELECT $columns FROM products
+            WHERE id = ?}, undef, $id);
+
+    } elsif (defined $param->{'name'}) {
+
+        trick_taint($param->{'name'});
+        $product = $dbh->selectrow_hashref(qq{
+            SELECT $columns FROM products
+            WHERE name = ?}, undef, $param->{'name'});
+    } else {
+        ThrowCodeError('bad_arg',
+            {argument => 'param',
+             function => 'Bugzilla::Product::_init'});
+    }
+
+    return undef unless (defined $product);
+
+    foreach my $field (keys %$product) {
+        $self->{$field} = $product->{$field};
+    }
+    return $self;
+}
+
+###############################
+####       Methods         ####
+###############################
+
+sub components {
+    my $self = shift;
+
+    if (!defined $self->{components}) {
+        $self->{components} =
+            Bugzilla::Component::get_components_by_product($self->id);
+    }
+    return $self->{components}
+}
+
+sub classification {
+    my $self = shift;
+
+    if (!defined $self->{'classification'}) {
+        $self->{'classification'} =
+            new Bugzilla::Classification($self->classification_id);
+    }
+    return $self->{'classification'};
+}
+
+sub group_controls {
+    my $self = shift;
+
+    if (!defined $self->{group_controls}) {
+        $self->{group_controls} =
+            Bugzilla::Group::get_group_controls_by_product($self->id);
+    }
+    return $self->{group_controls};
+}
+
+sub versions {
+    my $self = shift;
+
+    if (!defined $self->{versions}) {
+        $self->{versions} =
+            Bugzilla::Version::get_versions_by_product($self->id);
+
+    }
+    return $self->{versions};
+}
+
+sub milestones {
+    my $self = shift;
+
+    if (!defined $self->{milestones}) {
+        $self->{milestones} =
+            Bugzilla::Milestone::get_milestones_by_product($self->id);
+    }
+    return $self->{milestones};
+}
+
+sub bug_count {
+    my $self = shift;
+    my $dbh = Bugzilla->dbh;
+
+    if (!defined $self->{'bug_count'}) {
+        $self->{'bug_count'} = $dbh->selectrow_array(qq{
+            SELECT COUNT(bug_id) FROM bugs
+            WHERE product_id = ?}, undef, $self->id);
+
+    }
+    return $self->{'bug_count'};
+}
+
+###############################
+####      Accessors      ######
+###############################
+
+sub id                { return $_[0]->{'id'};                }
+sub name              { return $_[0]->{'name'};              }
+sub description       { return $_[0]->{'description'};       }
+sub milestone_url     { return $_[0]->{'milestoneurl'};      }
+sub disallow_new      { return $_[0]->{'disallownew'};       }
+sub votes_per_user    { return $_[0]->{'votesperuser'};      }
+sub max_votes_per_bug { return $_[0]->{'maxvotesperbug'};    }
+sub votes_to_confirm  { return $_[0]->{'votestoconfirm'};    }
+sub default_milestone { return $_[0]->{'defaultmilestone'};  }
+sub classification_id { return $_[0]->{'classification_id'}; }
+
+###############################
+####      Subroutines    ######
+###############################
+
+sub get_products_by_classification ($) {
+    my ($class_id) = @_;
+    my $dbh = Bugzilla->dbh;
+    $class_id ||= DEFAULT_CLASSIFICATION_ID;
+
+    my $stored_class_id = $class_id;
+    unless (detaint_natural($class_id)) {
+        ThrowCodeError(
+            'invalid_numeric_argument',
+            {argument => 'product_id',
+             value    => $stored_class_id,
+             function =>
+                'Bugzilla::Product::get_classification_products'}
+        );
+    }
+
+    my $ids = $dbh->selectcol_arrayref(q{
+        SELECT id FROM products
+        WHERE classification_id = ?}, undef, $class_id);
+
+    my $products;
+    foreach my $id (@$ids) {
+        $products->{$id} = new Bugzilla::Product($id);
+    }
+    return $products;
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+Bugzilla::Product - Bugzilla product class.
+
+=head1 SYNOPSIS
+
+    use Bugzilla::Product;
+
+    my $product = new Bugzilla::Product(1);
+    my $product = new Bugzilla::Product('AcmeProduct');
+
+    my $components      = $product->components();
+    my $classification  = $product->classification();
+    my $hash_ref        = $product->group_controls();
+    my $hash_ref        = $product->milestones();
+    my $hash_ref        = $product->versions();
+    my $bugcount        = $product->bug_count();
+
+    my $id               = $product->id;
+    my $name             = $product->name;
+    my $description      = $product->description;
+    my $milestoneurl     = $product->milestone_url;
+    my disallownew       = $product->disallow_new;
+    my votesperuser      = $product->votes_per_user;
+    my maxvotesperbug    = $product->max_votes_per_bug;
+    my votestoconfirm    = $product->votes_to_confirm;
+    my $defaultmilestone = $product->default_milestone;
+    my $classificationid = $product->classification_id;
+
+    my $hash_ref = Bugzilla::Product::get_products_by_classification(1);
+    my $product = $hash_ref->{1};
+
+=head1 DESCRIPTION
+
+Product.pm represents a product object.
+
+=head1 METHODS
+
+=over
+
+=item C<new($param)>
+
+ Description: The constructor is used to load an existing product
+              by passing a product id or a hash.
+
+ Params:      $param - If you pass an integer, the integer is the
+                       product id from the database that we want to
+                       read in. If you pass in a hash with 'name' key,
+                       then the value of the name key is the name of a
+                       product from the DB.
+
+ Returns:     A Bugzilla::Product object.
+
+=item C<components()>
+
+ Description: Returns a hash with all product components.
+
+ Params:      none.
+
+ Returns:     A hash where component id is the hash key and
+              Bugzilla::Component object is the hash value.
+
+=item C<classification()>
+
+ Description: Returns a Bugzilla::Classification object for
+              the product classification.
+
+ Params:      none.
+
+ Returns:     A Bugzilla::Classification object.
+
+=item C<group_controls()>
+
+ Description: Returns a hash (group id as key) with all product
+              group controls.
+
+ Params:      none.
+
+ Returns:     A hash with group id as key and hash containing the
+              group data as value.
+
+=item C<versions()>
+
+ Description: Returns a hash with of all product versions.
+
+ Params:      none.
+
+ Returns:     A hash with version id as key and a Bugzilla::Version
+              as value.
+
+=item C<milestones()>
+
+ Description: Returns a hash with of all product milestones.
+
+ Params:      none.
+
+ Returns:     A hash with milestone id as key and a Bugzilla::Milestone
+              as value.
+
+=item C<bug_count()>
+
+ Description: Returns the total of bugs that belong to the product.
+
+ Params:      none.
+
+ Returns:     Integer with the number of bugs.
+
+=back
+
+=head1 SUBROUTINES
+
+=over
+
+=item C<get_products_by_classification($class_id)>
+
+ Description: Returns all products for a specific classification id.
+
+ Params:      none.
+
+ Returns:     A hash with product id as key and a Bugzilla::Product
+              object as value.
+
+=back
+
+=cut
diff --git a/Bugzilla/Version.pm b/Bugzilla/Version.pm
new file mode 100644 (file)
index 0000000..5cd5b2a
--- /dev/null
@@ -0,0 +1,170 @@
+# -*- 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.
+#
+# Contributor(s): Tiago R. Mello <timello@async.com.br>
+
+use strict;
+
+package Bugzilla::Version;
+
+use Bugzilla;
+use Bugzilla::Util;
+use Bugzilla::Error;
+
+################################
+#####   Initialization     #####
+################################
+
+use constant DEFAULT_VERSION => 'unspecified';
+
+use constant DB_COLUMNS => qw(
+    versions.value
+    versions.product_id
+);
+
+our $columns = join(", ", DB_COLUMNS);
+
+sub new {
+    my $invocant = shift;
+    my $class = ref($invocant) || $invocant;
+    my $self = {};
+    bless($self, $class);
+    return $self->_init(@_);
+}
+
+sub _init {
+    my $self = shift;
+    my ($product_id, $value) = (@_);
+    my $dbh = Bugzilla->dbh;
+
+    my $version;
+
+    if (defined $product_id
+        && detaint_natural($product_id)
+        && defined $value) {
+
+        trick_taint($value);
+        $version = $dbh->selectrow_hashref(qq{
+            SELECT $columns FROM versions
+            WHERE value = ?
+            AND product_id = ?}, undef, ($value, $product_id));
+    } else {
+        ThrowCodeError('bad_arg',
+            {argument => 'product_id/value',
+             function => 'Bugzilla::Version::_init'});
+    }
+
+    return undef unless (defined $version);
+
+    foreach my $field (keys %$version) {
+        $self->{$field} = $version->{$field};
+    }
+    return $self;
+}
+
+###############################
+#####     Accessors        ####
+###############################
+
+sub value      { return $_[0]->{'value'};      }
+sub product_id { return $_[0]->{'product_id'}; }
+
+###############################
+#####     Subroutines       ###
+###############################
+
+sub get_versions_by_product ($) {
+    my ($product_id) = @_;
+    my $dbh = Bugzilla->dbh;
+
+    my $stored_product_id = $product_id;
+    unless (detaint_natural($product_id)) {
+        ThrowCodeError(
+            'invalid_numeric_argument',
+            {argument => 'product_id',
+             value    => $stored_product_id,
+             function =>
+                'Bugzilla::Version::get_versions_by_product'}
+        );
+    }
+
+    my $values = $dbh->selectcol_arrayref(q{
+        SELECT value FROM versions
+        WHERE product_id = ?}, undef, $product_id);
+
+    my $versions;
+    foreach my $value (@$values) {
+        $versions->{$value} = new Bugzilla::Version($product_id,
+                                                    $value);
+    }
+    return $versions;
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+Bugzilla::Version - Bugzilla product version class.
+
+=head1 SYNOPSIS
+
+    use Bugzilla::Version;
+
+    my $version = new Bugzilla::Version(1, 'version_value');
+
+    my $product_id = $version->product_id;
+    my $value = $version->value;
+
+    my $hash_ref = Bugzilla::Version::get_versions_by_product(1);
+    my $version = $hash_ref->{'version_value'};
+
+=head1 DESCRIPTION
+
+Version.pm represents a Product Version object.
+
+=head1 METHODS
+
+=over
+
+=item C<new($product_id, $value)>
+
+ Description: The constructor is used to load an existing version
+              by passing a product id and a version value.
+
+ Params:      $product_id - Integer with a Bugzilla product id.
+              $value - String with a version value.
+
+ Returns:     A Bugzilla::Version object.
+
+=back
+
+=head1 SUBROUTINES
+
+=over
+
+=item C<get_versions_by_product($product_id)>
+
+ Description: Returns all Bugzilla product versions that belong
+              to the supplied product.
+
+ Params:      $product_id - Integer with a Bugzilla product id.
+
+ Returns:     A hash with version value as key and a Bugzilla::Version
+              objects as value.
+
+=back
+
+=cut
index 1b7af3fea2a5f20a7a5170f663faf5a875eb8fc4..5ad05706185bb128a45ab42a95fce646a591a5a3 100644 (file)
     The active flag was improperly set.  There may be
     a problem with [% terms.Bugzilla %] or [% terms.abug %] in your browser.
 
+  [% ELSIF error == "invalid_numeric_argument" %]
+    [% title = "Invalid number argument" %]
+    The argument <code>[% argument FILTER html %] = [% value FILTER html %]</code>
+    of <code>[% function FILTER html %]</code> is not a natural number.
+
   [% ELSIF error == "invalid_series_id" %]
     [% title = "Invalid Series" %]
     The series_id [% series_id FILTER html %] is not valid. It may be that