summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeff Fearn <jfearn@redhat.com>2020-05-07 11:41:39 +1000
committerJeff Fearn <jfearn@redhat.com>2020-05-07 11:41:39 +1000
commit353af77714fd29aeab330255808636a9088eecc2 (patch)
treeecaadcb99519d2b53a96e7dfe9661b498156827a /Bugzilla/Product.pm
parentBumped version to 5.0.4 (diff)
downloadbugzilla-353af77714fd29aeab330255808636a9088eecc2.tar.gz
bugzilla-353af77714fd29aeab330255808636a9088eecc2.tar.bz2
bugzilla-353af77714fd29aeab330255808636a9088eecc2.zip
Bug 478886 - Open Source Red Hat BugzillaRelease-5.0.4-rh44
Import Red Hat Bugzilla changes.
Diffstat (limited to 'Bugzilla/Product.pm')
-rw-r--r--Bugzilla/Product.pm1496
1 files changed, 913 insertions, 583 deletions
diff --git a/Bugzilla/Product.pm b/Bugzilla/Product.pm
index 0c0cb458d..682d3fd50 100644
--- a/Bugzilla/Product.pm
+++ b/Bugzilla/Product.pm
@@ -19,6 +19,7 @@ use Bugzilla::Error;
use Bugzilla::Group;
use Bugzilla::Version;
use Bugzilla::Milestone;
+use Bugzilla::Release;
use Bugzilla::Field;
use Bugzilla::Status;
use Bugzilla::Install::Requirements;
@@ -38,292 +39,388 @@ use constant IS_CONFIG => 1;
use constant DB_TABLE => 'products';
+## REDHAT EXTENSION BEGIN 706784 739424 829154
+# Extension is the last six values only
use constant DB_COLUMNS => qw(
- id
- name
- classification_id
- description
- isactive
- defaultmilestone
- allows_unconfirmed
+ id
+ name
+ classification_id
+ description
+ isactive
+ defaultmilestone
+ allows_unconfirmed
+ defaultrelease
+ multiple_components
+ multiple_target_releases
+ multiple_versions
+ always_private
+ allows_on_dev
+ allows_no_clear_acks
+ rule_group
+ report_group
);
+## REDHAT EXTENSION END 706784 739424 829154
+## REDHAT EXTENSION BEGIN 706784 739424 829154
+# Extension is the last six values only
use constant UPDATE_COLUMNS => qw(
- name
- description
- defaultmilestone
- isactive
- allows_unconfirmed
+ name
+ description
+ defaultmilestone
+ isactive
+ allows_unconfirmed
+ defaultrelease
+ multiple_components
+ multiple_target_releases
+ multiple_versions
+ always_private
+ allows_on_dev
+ allows_no_clear_acks
+ rule_group
+ report_group
);
+## REDHAT EXTENSION END 706784 739424 829154
use constant VALIDATORS => {
- allows_unconfirmed => \&Bugzilla::Object::check_boolean,
- classification => \&_check_classification,
- name => \&_check_name,
- description => \&_check_description,
- version => \&_check_version,
- defaultmilestone => \&_check_default_milestone,
- isactive => \&Bugzilla::Object::check_boolean,
- create_series => \&Bugzilla::Object::check_boolean
+ allows_unconfirmed => \&Bugzilla::Object::check_boolean,
+ classification => \&_check_classification,
+ name => \&_check_name,
+ description => \&_check_description,
+ version => \&_check_version,
+ defaultmilestone => \&_check_default_milestone,
+ defaultrelease => \&_check_default_release,
+ isactive => \&Bugzilla::Object::check_boolean,
+ create_series => \&Bugzilla::Object::check_boolean
};
+## REDHAT EXTENSION BEGIN 706784
+VALIDATORS->{multiple_components} = \&Bugzilla::Object::check_boolean;
+VALIDATORS->{multiple_target_releases} = \&Bugzilla::Object::check_boolean;
+VALIDATORS->{multiple_versions} = \&Bugzilla::Object::check_boolean;
+## REDHAT EXTENSION END 706784
+## REDHAT EXTENSION BEGIN 829154
+VALIDATORS->{always_private} = \&Bugzilla::Object::check_boolean;
+## REDHAT EXTENSION END 829154
+
+VALIDATORS->{allows_on_dev} = \&Bugzilla::Object::check_boolean;
+VALIDATORS->{allows_no_clear_acks} = \&Bugzilla::Object::check_boolean;
+
+VALIDATORS->{rule_group} = \&Bugzilla::_check_rule_group;
+VALIDATORS->{report_group} = \&Bugzilla::_check_group;
+
###############################
#### Constructors #####
###############################
sub create {
- my $class = shift;
- my $dbh = Bugzilla->dbh;
+ my $class = shift;
+ my $dbh = Bugzilla->dbh;
- $dbh->bz_start_transaction();
+ $dbh->bz_start_transaction();
- $class->check_required_create_fields(@_);
+ $class->check_required_create_fields(@_);
- my $params = $class->run_create_validators(@_);
- # Some fields do not exist in the DB as is.
- if (defined $params->{classification}) {
- $params->{classification_id} = delete $params->{classification};
- }
- my $version = delete $params->{version};
- my $create_series = delete $params->{create_series};
+ my $params = $class->run_create_validators(@_);
- my $product = $class->insert_create_data($params);
- Bugzilla->user->clear_product_cache();
+ # Some fields do not exist in the DB as is.
+ if (defined $params->{classification}) {
+ $params->{classification_id} = delete $params->{classification};
+ }
+ my $version = delete $params->{version};
+ my $create_series = delete $params->{create_series};
- # Add the new version and milestone into the DB as valid values.
- Bugzilla::Version->create({ value => $version, product => $product });
- Bugzilla::Milestone->create({ value => $product->default_milestone,
- product => $product });
+ my $product = $class->insert_create_data($params);
+ Bugzilla->user->clear_product_cache();
- # Create groups and series for the new product, if requested.
- $product->_create_bug_group() if Bugzilla->params->{'makeproductgroups'};
- $product->_create_series() if $create_series;
+ # Add the new version and milestone into the DB as valid values.
+ Bugzilla::Version->create({value => $version, product => $product});
+ Bugzilla::Milestone->create(
+ {value => $product->default_milestone, product => $product});
+ Bugzilla::Release->create(
+ {value => $product->default_release, product => $product});
- Bugzilla::Hook::process('product_end_of_create', { product => $product });
+ # Create groups and series for the new product, if requested.
+ $product->_create_bug_group() if Bugzilla->params->{'makeproductgroups'};
+ $product->_create_series() if $create_series;
- $dbh->bz_commit_transaction();
- Bugzilla->memcached->clear_config();
- return $product;
+ Bugzilla::Hook::process('product_end_of_create', {product => $product});
+
+ $dbh->bz_commit_transaction();
+ Bugzilla->memcached->clear_config();
+ return $product;
}
# This is considerably faster than calling new_from_list three times
# for each product in the list, particularly with hundreds or thousands
# of products.
sub preload {
- my ($products, $preload_flagtypes) = @_;
- my %prods = map { $_->id => $_ } @$products;
- my @prod_ids = keys %prods;
- return unless @prod_ids;
-
- # We cannot |use| it due to a dependency loop with Bugzilla::User.
- require Bugzilla::Component;
- foreach my $field (qw(component version milestone)) {
- my $classname = "Bugzilla::" . ucfirst($field);
- my $objects = $classname->match({ product_id => \@prod_ids });
-
- # Now populate the products with this set of objects.
- foreach my $obj (@$objects) {
- my $product_id = $obj->product_id;
- $prods{$product_id}->{"${field}s"} ||= [];
- push(@{$prods{$product_id}->{"${field}s"}}, $obj);
- }
- }
- if ($preload_flagtypes) {
- $_->flag_types foreach @$products;
+ my ($products, $preload_flagtypes) = @_;
+ my %prods = map { $_->id => $_ } @$products;
+ my @prod_ids = keys %prods;
+ return unless @prod_ids;
+
+ # We cannot |use| it due to a dependency loop with Bugzilla::User.
+ require Bugzilla::Component;
+ foreach my $field (qw(component version milestone release)) {
+ my $classname = "Bugzilla::" . ucfirst($field);
+ my $objects = $classname->match({product_id => \@prod_ids});
+
+ # Now populate the products with this set of objects.
+ foreach my $obj (@$objects) {
+ my $product_id = $obj->product_id;
+ $prods{$product_id}->{"${field}s"} ||= [];
+ push(@{$prods{$product_id}->{"${field}s"}}, $obj);
}
+ }
+ if ($preload_flagtypes) {
+ $_->flag_types foreach @$products;
+ }
}
sub update {
- my $self = shift;
- my $dbh = Bugzilla->dbh;
-
- # Don't update the DB if something goes wrong below -> transaction.
- $dbh->bz_start_transaction();
- my ($changes, $old_self) = $self->SUPER::update(@_);
-
- # Also update group settings.
- if ($self->{check_group_controls}) {
- require Bugzilla::Bug;
- import Bugzilla::Bug qw(LogActivityEntry);
-
- my $old_settings = $old_self->group_controls;
- my $new_settings = $self->group_controls;
- my $timestamp = $dbh->selectrow_array('SELECT NOW()');
-
- foreach my $gid (keys %$new_settings) {
- my $old_setting = $old_settings->{$gid} || {};
- my $new_setting = $new_settings->{$gid};
- # If all new settings are 0 for a given group, we delete the entry
- # from group_control_map, so we have to track it here.
- my $all_zero = 1;
- my @fields;
- my @values;
-
- foreach my $field ('entry', 'membercontrol', 'othercontrol', 'canedit',
- 'editcomponents', 'editbugs', 'canconfirm')
- {
- my $old_value = $old_setting->{$field};
- my $new_value = $new_setting->{$field};
- $all_zero = 0 if $new_value;
- next if (defined $old_value && $old_value == $new_value);
- push(@fields, $field);
- # The value has already been validated.
- detaint_natural($new_value);
- push(@values, $new_value);
- }
- # Is there anything to update?
- next unless scalar @fields;
-
- if ($all_zero) {
- $dbh->do('DELETE FROM group_control_map
- WHERE product_id = ? AND group_id = ?',
- undef, $self->id, $gid);
- }
- else {
- if (exists $old_setting->{group}) {
- # There is already an entry in the DB.
- my $set_fields = join(', ', map {"$_ = ?"} @fields);
- $dbh->do("UPDATE group_control_map SET $set_fields
- WHERE product_id = ? AND group_id = ?",
- undef, (@values, $self->id, $gid));
- }
- else {
- # No entry yet.
- my $fields = join(', ', @fields);
- # +2 because of the product and group IDs.
- my $qmarks = join(',', ('?') x (scalar @fields + 2));
- $dbh->do("INSERT INTO group_control_map (product_id, group_id, $fields)
- VALUES ($qmarks)", undef, ($self->id, $gid, @values));
- }
- }
-
- # If the group is mandatory, restrict all bugs to it.
- if ($new_setting->{membercontrol} == CONTROLMAPMANDATORY) {
- my $bug_ids =
- $dbh->selectcol_arrayref('SELECT bugs.bug_id
+ my $self = shift;
+ my $dbh = Bugzilla->dbh;
+
+ my @colnames = qw(NA Shown Default Mandatory); ## REDHAT EXTENSION 1214523 - Record ACL changes
+
+ ## REDHAT EXTENSION START 829154
+ # Setting a bug as always private with no group control means you cannot edit it
+ my $group_controls = $self->group_controls;
+ my @member_control
+ = grep { $group_controls->{$_}{membercontrol} } keys %$group_controls;
+ if ($self->always_private and scalar(@member_control) == 0) {
+ ThrowUserError('product_cannot_be_private');
+ }
+ ## REDHAT EXTENSION END 829154
+
+ # Don't update the DB if something goes wrong below -> transaction.
+ $dbh->bz_start_transaction();
+ my ($changes, $old_self) = $self->SUPER::update(@_);
+
+ # Also update group settings.
+ if ($self->{check_group_controls}) {
+ require Bugzilla::Bug;
+ import Bugzilla::Bug qw(LogActivityEntry);
+
+ my $old_settings = $old_self->group_controls;
+ my $new_settings = $self->group_controls;
+ my $timestamp = $dbh->selectrow_array('SELECT NOW()');
+
+ foreach my $gid (keys %$new_settings) {
+ my $old_setting = $old_settings->{$gid} || {};
+ my $new_setting = $new_settings->{$gid};
+
+ # If all new settings are 0 for a given group, we delete the entry
+ # from group_control_map, so we have to track it here.
+ my $all_zero = 1;
+ my @fields;
+ my @values;
+
+ my %mapchanges; ## REDHAT EXTENSION 1214523 - Record ACL changes
+
+ foreach my $field (
+ 'entry', 'membercontrol', 'othercontrol', 'canedit',
+ 'editcomponents', 'editbugs', 'canconfirm'
+ )
+ {
+ my $old_value = $old_setting->{$field} // 0;
+ my $new_value = $new_setting->{$field} // 0;
+ $all_zero = 0 if $new_value;
+ next
+ if ((defined $old_value && $old_value == $new_value)
+ || (!$old_value && !$new_value));
+ push(@fields, $field);
+
+ # The value has already been validated.
+ detaint_natural($new_value);
+ push(@values, $new_value);
+ ## REDHAT EXTENSION 1214523 START - Record ACL changes
+ if ($field eq 'membercontrol' || $field eq 'othercontrol') {
+ $changes->{'group_controls'}->{$new_setting->{group}->name}->{$field}
+ = [$colnames[$old_value], $colnames[$new_value]];
+ }
+ else {
+ $old_value //= '';
+ $changes->{'group_controls'}->{$new_setting->{group}->name}->{$field}
+ = [$old_value, $new_value];
+ }
+ ## REDHAT EXTENSION 1214523 END
+ }
+
+ # Is there anything to update?
+ next unless scalar @fields;
+
+ $self->audit_log(\%mapchanges); ## REDHAT EXTENSION 1214523
+
+ if ($all_zero) {
+ $dbh->do(
+ 'DELETE FROM group_control_map
+ WHERE product_id = ? AND group_id = ?', undef, $self->id, $gid
+ );
+ }
+ else {
+ if (exists $old_setting->{group}) {
+
+ # There is already an entry in the DB.
+ my $set_fields = join(', ', map {"$_ = ?"} @fields);
+ $dbh->do(
+ "UPDATE group_control_map SET $set_fields
+ WHERE product_id = ? AND group_id = ?", undef,
+ (@values, $self->id, $gid)
+ );
+ }
+ else {
+ # No entry yet.
+ my $fields = join(', ', @fields);
+
+ # +2 because of the product and group IDs.
+ my $qmarks = join(',', ('?') x (scalar @fields + 2));
+ $dbh->do(
+ "INSERT INTO group_control_map (product_id, group_id, $fields)
+ VALUES ($qmarks)", undef, ($self->id, $gid, @values)
+ );
+ }
+ }
+
+ # If the group is mandatory, restrict all bugs to it.
+ if ($new_setting->{membercontrol} == CONTROLMAPMANDATORY) {
+ my $bug_ids = $dbh->selectcol_arrayref(
+ 'SELECT bugs.bug_id
FROM bugs
LEFT JOIN bug_group_map
ON bug_group_map.bug_id = bugs.bug_id
AND group_id = ?
WHERE product_id = ?
AND bug_group_map.bug_id IS NULL',
- undef, $gid, $self->id);
-
- if (scalar @$bug_ids) {
- my $sth = $dbh->prepare('INSERT INTO bug_group_map (bug_id, group_id)
- VALUES (?, ?)');
-
- foreach my $bug_id (@$bug_ids) {
- $sth->execute($bug_id, $gid);
- # Add this change to the bug history.
- LogActivityEntry($bug_id, 'bug_group', '',
- $new_setting->{group}->name,
- Bugzilla->user->id, $timestamp);
- }
- push(@{$changes->{'_group_controls'}->{'now_mandatory'}},
- {name => $new_setting->{group}->name,
- bug_count => scalar @$bug_ids});
- }
- }
- # If the group can no longer be used to restrict bugs, remove them.
- elsif ($new_setting->{membercontrol} == CONTROLMAPNA) {
- my $bug_ids =
- $dbh->selectcol_arrayref('SELECT bugs.bug_id
+ undef, $gid, $self->id
+ );
+
+ if (scalar @$bug_ids) {
+ my $sth = $dbh->prepare(
+ 'INSERT INTO bug_group_map (bug_id, group_id)
+ VALUES (?, ?)'
+ );
+
+ foreach my $bug_id (@$bug_ids) {
+ $sth->execute($bug_id, $gid);
+
+ # Add this change to the bug history.
+ LogActivityEntry($bug_id, 'bug_group', '', $new_setting->{group}->name,
+ Bugzilla->user->id, $timestamp);
+ }
+ push(
+ @{$changes->{'_group_controls'}->{'now_mandatory'}},
+ {name => $new_setting->{group}->name, bug_count => scalar @$bug_ids}
+ );
+ }
+ }
+
+ # If the group can no longer be used to restrict bugs, remove them.
+ elsif ($new_setting->{membercontrol} == CONTROLMAPNA) {
+ my $bug_ids = $dbh->selectcol_arrayref(
+ 'SELECT bugs.bug_id
FROM bugs
INNER JOIN bug_group_map
ON bug_group_map.bug_id = bugs.bug_id
WHERE product_id = ? AND group_id = ?',
- undef, $self->id, $gid);
-
- if (scalar @$bug_ids) {
- $dbh->do('DELETE FROM bug_group_map WHERE group_id = ? AND ' .
- $dbh->sql_in('bug_id', $bug_ids), undef, $gid);
-
- # Add this change to the bug history.
- foreach my $bug_id (@$bug_ids) {
- LogActivityEntry($bug_id, 'bug_group',
- $old_setting->{group}->name, '',
- Bugzilla->user->id, $timestamp);
- }
- push(@{$changes->{'_group_controls'}->{'now_na'}},
- {name => $old_setting->{group}->name,
- bug_count => scalar @$bug_ids});
- }
- }
+ undef, $self->id, $gid
+ );
+
+ if (scalar @$bug_ids) {
+ $dbh->do(
+ 'DELETE FROM bug_group_map WHERE group_id = ? AND '
+ . $dbh->sql_in('bug_id', $bug_ids),
+ undef, $gid
+ );
+
+ # Add this change to the bug history.
+ foreach my $bug_id (@$bug_ids) {
+ LogActivityEntry($bug_id, 'bug_group', $old_setting->{group}->name,
+ '', Bugzilla->user->id, $timestamp);
+ }
+ push(
+ @{$changes->{'_group_controls'}->{'now_na'}},
+ {name => $old_setting->{group}->name, bug_count => scalar @$bug_ids}
+ );
}
-
- delete $self->{groups_available};
- delete $self->{groups_mandatory};
+ }
}
- $dbh->bz_commit_transaction();
- # Changes have been committed.
- delete $self->{check_group_controls};
- Bugzilla->user->clear_product_cache();
- Bugzilla->memcached->clear_config();
- return $changes;
+ delete $self->{groups_available};
+ delete $self->{groups_mandatory};
+ }
+ $dbh->bz_commit_transaction();
+
+ # Changes have been committed.
+ delete $self->{check_group_controls};
+ Bugzilla->user->clear_product_cache();
+ Bugzilla->memcached->clear_config();
+
+ return $changes;
}
sub remove_from_db {
- my ($self, $params) = @_;
- my $user = Bugzilla->user;
- my $dbh = Bugzilla->dbh;
-
- $dbh->bz_start_transaction();
-
- $self->_check_if_controller();
-
- if ($self->bug_count) {
- if (Bugzilla->params->{'allowbugdeletion'}) {
- require Bugzilla::Bug;
- foreach my $bug_id (@{$self->bug_ids}) {
- # Note that we allow the user to delete bugs they can't see,
- # which is okay, because they're deleting the whole Product.
- my $bug = new Bugzilla::Bug($bug_id);
- $bug->remove_from_db();
- }
- }
- else {
- ThrowUserError('product_has_bugs', { nb => $self->bug_count });
- }
+ my ($self, $params) = @_;
+ my $user = Bugzilla->user;
+ my $dbh = Bugzilla->dbh;
+
+ $dbh->bz_start_transaction();
+
+ $self->_check_if_controller();
+
+ if ($self->bug_count) {
+ if (Bugzilla->params->{'allowbugdeletion'}) {
+ require Bugzilla::Bug;
+ foreach my $bug_id (@{$self->bug_ids}) {
+
+ # Note that we allow the user to delete bugs they can't see,
+ # which is okay, because they're deleting the whole Product.
+ my $bug = new Bugzilla::Bug($bug_id);
+ $bug->remove_from_db();
+ }
}
+ else {
+ ThrowUserError('product_has_bugs', {nb => $self->bug_count});
+ }
+ }
- if ($params->{delete_series}) {
- my $series_ids =
- $dbh->selectcol_arrayref('SELECT series_id
+ if ($params->{delete_series}) {
+ my $series_ids = $dbh->selectcol_arrayref(
+ 'SELECT series_id
FROM series
INNER JOIN series_categories
ON series_categories.id = series.category
- WHERE series_categories.name = ?',
- undef, $self->name);
+ WHERE series_categories.name = ?', undef,
+ $self->name
+ );
- if (scalar @$series_ids) {
- $dbh->do('DELETE FROM series WHERE ' . $dbh->sql_in('series_id', $series_ids));
- }
+ if (scalar @$series_ids) {
+ $dbh->do('DELETE FROM series WHERE ' . $dbh->sql_in('series_id', $series_ids));
+ }
- # If no subcategory uses this product name, completely purge it.
- my $in_use =
- $dbh->selectrow_array('SELECT 1
+ # If no subcategory uses this product name, completely purge it.
+ my $in_use = $dbh->selectrow_array(
+ 'SELECT 1
FROM series
INNER JOIN series_categories
ON series_categories.id = series.subcategory
- WHERE series_categories.name = ? ' .
- $dbh->sql_limit(1),
- undef, $self->name);
- if (!$in_use) {
- $dbh->do('DELETE FROM series_categories WHERE name = ?', undef, $self->name);
- }
+ WHERE series_categories.name = ? '
+ . $dbh->sql_limit(1), undef, $self->name
+ );
+ if (!$in_use) {
+ $dbh->do('DELETE FROM series_categories WHERE name = ?', undef, $self->name);
}
+ }
- $self->SUPER::remove_from_db();
+ $self->SUPER::remove_from_db();
- $dbh->bz_commit_transaction();
- Bugzilla->memcached->clear_config();
+ $dbh->bz_commit_transaction();
+ Bugzilla->memcached->clear_config();
- # We have to delete these internal variables, else we get
- # the old lists of products and classifications again.
- delete $user->{selectable_products};
- delete $user->{selectable_classifications};
+ # We have to delete these internal variables, else we get
+ # the old lists of products and classifications again.
+ delete $user->{selectable_products};
+ delete $user->{selectable_classifications};
}
@@ -332,91 +429,135 @@ sub remove_from_db {
###############################
sub _check_classification {
- my ($invocant, $classification_name) = @_;
-
- my $classification_id = 1;
- if (Bugzilla->params->{'useclassification'}) {
- my $classification = Bugzilla::Classification->check($classification_name);
- $classification_id = $classification->id;
- }
- return $classification_id;
+ my ($invocant, $classification_name) = @_;
+
+ my $classification_id = 1;
+ if (Bugzilla->params->{'useclassification'}) {
+ my $classification = Bugzilla::Classification->check($classification_name);
+ $classification_id = $classification->id;
+ }
+ return $classification_id;
}
sub _check_name {
- my ($invocant, $name) = @_;
+ my ($invocant, $name) = @_;
- $name = trim($name);
- $name || ThrowUserError('product_blank_name');
+ $name = trim($name);
+ $name || ThrowUserError('product_blank_name');
- if (length($name) > MAX_PRODUCT_SIZE) {
- ThrowUserError('product_name_too_long', {'name' => $name});
- }
+ if (length($name) > MAX_PRODUCT_SIZE) {
+ ThrowUserError('product_name_too_long', {'name' => $name});
+ }
- my $product = new Bugzilla::Product({name => $name});
- if ($product && (!ref $invocant || $product->id != $invocant->id)) {
- # Check for exact case sensitive match:
- if ($product->name eq $name) {
- ThrowUserError('product_name_already_in_use', {'product' => $product->name});
- }
- else {
- ThrowUserError('product_name_diff_in_case', {'product' => $name,
- 'existing_product' => $product->name});
- }
+ my $product = new Bugzilla::Product({name => $name});
+ if ($product && (!ref $invocant || $product->id != $invocant->id)) {
+
+ # Check for exact case sensitive match:
+ if ($product->name eq $name) {
+ ThrowUserError('product_name_already_in_use', {'product' => $product->name});
}
- return $name;
+ else {
+ ThrowUserError('product_name_diff_in_case',
+ {'product' => $name, 'existing_product' => $product->name});
+ }
+ }
+ return $name;
}
sub _check_description {
- my ($invocant, $description) = @_;
+ my ($invocant, $description) = @_;
- $description = trim($description);
- $description || ThrowUserError('product_must_have_description');
- return $description;
+ $description = trim($description);
+ $description || ThrowUserError('product_must_have_description');
+ return $description;
}
sub _check_version {
- my ($invocant, $version) = @_;
+ my ($invocant, $version) = @_;
- $version = trim($version);
- $version || ThrowUserError('product_must_have_version');
- # We will check the version length when Bugzilla::Version->create will do it.
- return $version;
+ $version = trim($version);
+ $version || ThrowUserError('product_must_have_version');
+
+ # We will check the version length when Bugzilla::Version->create will do it.
+ return $version;
}
sub _check_default_milestone {
- my ($invocant, $milestone) = @_;
+ my ($invocant, $milestone) = @_;
- # Do nothing if target milestones are not in use.
- unless (Bugzilla->params->{'usetargetmilestone'}) {
- return (ref $invocant) ? $invocant->default_milestone : '---';
- }
+ # Do nothing if target milestones are not in use.
+ unless (Bugzilla->params->{'usetargetmilestone'}) {
+ return (ref $invocant) ? $invocant->default_milestone : '---';
+ }
- $milestone = trim($milestone);
+ $milestone = trim($milestone);
- if (ref $invocant) {
- # The default milestone must be one of the existing milestones.
- my $mil_obj = new Bugzilla::Milestone({name => $milestone, product => $invocant});
+ if (ref $invocant) {
- $mil_obj || ThrowUserError('product_must_define_defaultmilestone',
- {product => $invocant->name,
- milestone => $milestone});
- }
- else {
- $milestone ||= '---';
- }
- return $milestone;
+ # The default milestone must be one of the existing milestones.
+ my $mil_obj
+ = new Bugzilla::Milestone({name => $milestone, product => $invocant});
+
+ $mil_obj || ThrowUserError('product_must_define_defaultmilestone',
+ {product => $invocant->name, milestone => $milestone});
+ }
+ else {
+ $milestone ||= '---';
+ }
+ return $milestone;
}
sub _check_milestone_url {
- my ($invocant, $url) = @_;
+ my ($invocant, $url) = @_;
- # Do nothing if target milestones are not in use.
- unless (Bugzilla->params->{'usetargetmilestone'}) {
- return (ref $invocant) ? $invocant->milestone_url : '';
- }
+ # Do nothing if target milestones are not in use.
+ unless (Bugzilla->params->{'usetargetmilestone'}) {
+ return (ref $invocant) ? $invocant->milestone_url : '';
+ }
+
+ $url = trim($url || '');
+ return $url;
+}
+
+sub _check_default_release {
+ my ($invocant, $release) = @_;
+
+ # Do nothing if target releases are not in use.
+ unless (Bugzilla->params->{'usetargetrelease'}) {
+ return (ref $invocant) ? $invocant->default_release : '---';
+ }
+
+ $release = trim($release);
+
+ if (ref $invocant) {
+
+ # The default release must be one of the existing active releases.
+ my $rel_obj = new Bugzilla::Release({name => $release, product => $invocant});
- $url = trim($url || '');
- return $url;
+ $rel_obj || ThrowUserError('product_must_define_defaultrelease',
+ {product => $invocant->name, release => $release});
+
+ $rel_obj->is_active || ThrowUserError(
+ 'product_must_define_active_defaultrelease',
+ {product => $invocant->name, release => $release}
+ );
+ }
+ else {
+ $release ||= '---';
+ }
+ return $release;
+}
+
+sub _check_release_url {
+ my ($invocant, $url) = @_;
+
+ # Do nothing if target releases are not in use.
+ unless (Bugzilla->params->{'usetargetrelease'}) {
+ return (ref $invocant) ? $invocant->release_url : '';
+ }
+
+ $url = trim($url || '');
+ return $url;
}
#####################################
@@ -431,393 +572,466 @@ use constant is_default => 0;
###############################
sub _create_bug_group {
- my $self = shift;
- my $dbh = Bugzilla->dbh;
-
- my $group_name = $self->name;
- while (new Bugzilla::Group({name => $group_name})) {
- $group_name .= '_';
- }
- my $group_description = get_text('bug_group_description', {product => $self});
-
- my $group = Bugzilla::Group->create({name => $group_name,
- description => $group_description,
- isbuggroup => 1});
-
- # Associate the new group and new product.
- $dbh->do('INSERT INTO group_control_map
+ my $self = shift;
+ my $dbh = Bugzilla->dbh;
+
+ my $group_name = $self->name;
+ while (new Bugzilla::Group({name => $group_name})) {
+ $group_name .= '_';
+ }
+ my $group_description = get_text('bug_group_description', {product => $self});
+
+ my $group
+ = Bugzilla::Group->create({
+ name => $group_name, description => $group_description, isbuggroup => 1
+ });
+
+ # Associate the new group and new product.
+ $dbh->do(
+ 'INSERT INTO group_control_map
(group_id, product_id, membercontrol, othercontrol)
- VALUES (?, ?, ?, ?)',
- undef, ($group->id, $self->id, CONTROLMAPDEFAULT, CONTROLMAPNA));
+ VALUES (?, ?, ?, ?)', undef,
+ ($group->id, $self->id, CONTROLMAPDEFAULT, CONTROLMAPNA)
+ );
}
sub _create_series {
- my $self = shift;
-
- my @series;
- # We do every status, every resolution, and an "opened" one as well.
- foreach my $bug_status (@{get_legal_field_values('bug_status')}) {
- push(@series, [$bug_status, "bug_status=" . url_quote($bug_status)]);
- }
-
- foreach my $resolution (@{get_legal_field_values('resolution')}) {
- next if !$resolution;
- push(@series, [$resolution, "resolution=" . url_quote($resolution)]);
- }
-
- my @openedstatuses = BUG_STATE_OPEN;
- my $query = join("&", map { "bug_status=" . url_quote($_) } @openedstatuses);
- push(@series, [get_text('series_all_open'), $query]);
-
- foreach my $sdata (@series) {
- my $series = new Bugzilla::Series(undef, $self->name,
- get_text('series_subcategory'),
- $sdata->[0], Bugzilla->user->id, 1,
- $sdata->[1] . "&product=" . url_quote($self->name), 1);
- $series->writeToDatabase();
- }
+ my $self = shift;
+
+ my @series;
+
+ # We do every status, every resolution, and an "opened" one as well.
+ foreach my $bug_status (@{get_legal_field_values('bug_status')}) {
+ push(@series, [$bug_status, "bug_status=" . url_quote($bug_status)]);
+ }
+
+ foreach my $resolution (@{get_legal_field_values('resolution')}) {
+ next if !$resolution;
+ push(@series, [$resolution, "resolution=" . url_quote($resolution)]);
+ }
+
+ my @openedstatuses = BUG_STATE_OPEN;
+ my $query = join("&", map { "bug_status=" . url_quote($_) } @openedstatuses);
+ push(@series, [get_text('series_all_open'), $query]);
+
+ foreach my $sdata (@series) {
+ my $series
+ = new Bugzilla::Series(undef, $self->name, get_text('series_subcategory'),
+ $sdata->[0], Bugzilla->user->id, 1,
+ $sdata->[1] . "&product=" . url_quote($self->name), 1);
+ $series->writeToDatabase();
+ }
}
-sub set_name { $_[0]->set('name', $_[1]); }
-sub set_description { $_[0]->set('description', $_[1]); }
-sub set_default_milestone { $_[0]->set('defaultmilestone', $_[1]); }
-sub set_is_active { $_[0]->set('isactive', $_[1]); }
+sub set_name { $_[0]->set('name', $_[1]); }
+sub set_description { $_[0]->set('description', $_[1]); }
+sub set_default_milestone { $_[0]->set('defaultmilestone', $_[1]); }
+sub set_is_active { $_[0]->set('isactive', $_[1]); }
sub set_allows_unconfirmed { $_[0]->set('allows_unconfirmed', $_[1]); }
+sub set_default_release { $_[0]->set('defaultrelease', $_[1]); }
+## REDHAT EXTENSION BEGIN 706784, 1380351 Disable MultipleValues extension
+sub set_multiple_components { return; }
+sub set_multiple_target_releases { return; }
+sub set_multiple_versions { return; }
+## REDHAT EXTENSION END 706784
+## REDHAT EXTENSION BEGIN 829154
+sub set_always_private { $_[0]->set('always_private', $_[1]); }
+## REDHAT EXTENSION END 829154
sub set_group_controls {
- my ($self, $group, $settings) = @_;
-
- $group->is_active_bug_group
- || ThrowUserError('product_illegal_group', {group => $group});
-
- scalar(keys %$settings)
- || ThrowCodeError('product_empty_group_controls', {group => $group});
-
- # We store current settings for this group.
- my $gs = $self->group_controls->{$group->id};
- # If there is no entry for this group yet, create a default hash.
- unless (defined $gs) {
- $gs = { entry => 0,
- membercontrol => CONTROLMAPNA,
- othercontrol => CONTROLMAPNA,
- canedit => 0,
- editcomponents => 0,
- editbugs => 0,
- canconfirm => 0,
- group => $group };
+ my ($self, $group, $settings) = @_;
+
+ $group->is_active_bug_group
+ || ThrowUserError('product_illegal_group', {group => $group});
+
+ scalar(keys %$settings)
+ || ThrowCodeError('product_empty_group_controls', {group => $group});
+
+ # We store current settings for this group.
+ my $gs = $self->group_controls->{$group->id};
+
+ # If there is no entry for this group yet, create a default hash.
+ unless (defined $gs) {
+ $gs = {
+ entry => 0,
+ membercontrol => CONTROLMAPNA,
+ othercontrol => CONTROLMAPNA,
+ canedit => 0,
+ editcomponents => 0,
+ editbugs => 0,
+ canconfirm => 0,
+ group => $group
+ };
+ }
+
+ # Both settings must be defined, or none of them can be updated.
+ if (defined $settings->{membercontrol} && defined $settings->{othercontrol}) {
+
+ # Legality of control combination is a function of
+ # membercontrol\othercontrol
+ # NA SH DE MA
+ # NA + - - -
+ # SH + + + +
+ # DE + - + +
+ # MA - - - +
+ foreach my $field ('membercontrol', 'othercontrol') {
+ my ($is_legal)
+ = grep { $settings->{$field} == $_ }
+ (CONTROLMAPNA, CONTROLMAPSHOWN, CONTROLMAPDEFAULT, CONTROLMAPMANDATORY);
+ defined $is_legal || ThrowCodeError('product_illegal_group_control',
+ {field => $field, value => $settings->{$field}});
}
-
- # Both settings must be defined, or none of them can be updated.
- if (defined $settings->{membercontrol} && defined $settings->{othercontrol}) {
- # Legality of control combination is a function of
- # membercontrol\othercontrol
- # NA SH DE MA
- # NA + - - -
- # SH + + + +
- # DE + - + +
- # MA - - - +
- foreach my $field ('membercontrol', 'othercontrol') {
- my ($is_legal) = grep { $settings->{$field} == $_ }
- (CONTROLMAPNA, CONTROLMAPSHOWN, CONTROLMAPDEFAULT, CONTROLMAPMANDATORY);
- defined $is_legal || ThrowCodeError('product_illegal_group_control',
- { field => $field, value => $settings->{$field} });
- }
- unless ($settings->{membercontrol} == $settings->{othercontrol}
- || $settings->{membercontrol} == CONTROLMAPSHOWN
- || ($settings->{membercontrol} == CONTROLMAPDEFAULT
- && $settings->{othercontrol} != CONTROLMAPSHOWN))
- {
- ThrowUserError('illegal_group_control_combination', {groupname => $group->name});
- }
- $gs->{membercontrol} = $settings->{membercontrol};
- $gs->{othercontrol} = $settings->{othercontrol};
- }
-
- foreach my $field ('entry', 'canedit', 'editcomponents', 'editbugs', 'canconfirm') {
- next unless defined $settings->{$field};
- $gs->{$field} = $settings->{$field} ? 1 : 0;
+ unless (
+ $settings->{membercontrol} == $settings->{othercontrol}
+ || $settings->{membercontrol} == CONTROLMAPSHOWN
+ || ( $settings->{membercontrol} == CONTROLMAPDEFAULT
+ && $settings->{othercontrol} != CONTROLMAPSHOWN)
+ )
+ {
+ ThrowUserError('illegal_group_control_combination',
+ {groupname => $group->name});
}
- $self->{group_controls}->{$group->id} = $gs;
- $self->{check_group_controls} = 1;
+ $gs->{membercontrol} = $settings->{membercontrol};
+ $gs->{othercontrol} = $settings->{othercontrol};
+ }
+
+ foreach
+ my $field ('entry', 'canedit', 'editcomponents', 'editbugs', 'canconfirm')
+ {
+ next unless defined $settings->{$field};
+ $gs->{$field} = $settings->{$field} ? 1 : 0;
+ }
+ $self->{group_controls}->{$group->id} = $gs;
+ $self->{check_group_controls} = 1;
}
sub components {
- my $self = shift;
- my $dbh = Bugzilla->dbh;
+ my $self = shift;
+ my $dbh = Bugzilla->dbh;
- if (!defined $self->{components}) {
- my $ids = $dbh->selectcol_arrayref(q{
+ if (!defined $self->{components}) {
+ my $ids = $dbh->selectcol_arrayref(
+ q{
SELECT id FROM components
WHERE product_id = ?
- ORDER BY name}, undef, $self->id);
+ ORDER BY name}, undef, $self->id
+ );
- require Bugzilla::Component;
- $self->{components} = Bugzilla::Component->new_from_list($ids);
- }
- return $self->{components};
+ require Bugzilla::Component;
+ $self->{components} = Bugzilla::Component->new_from_list($ids);
+ }
+ return $self->{components};
}
-sub group_controls {
- my ($self, $full_data) = @_;
- my $dbh = Bugzilla->dbh;
-
- # By default, we don't return groups which are not listed in
- # group_control_map. If $full_data is true, then we also
- # return groups whose settings could be set for the product.
- my $where_or_and = 'WHERE';
- my $and_or_where = 'AND';
- if ($full_data) {
- $where_or_and = 'AND';
- $and_or_where = 'WHERE';
- }
+sub component_count {
+ my $self = shift;
+ my $dbh = Bugzilla->dbh;
+ if (!defined $self->{component_count}) {
+ $self->{component_count}
+ = $dbh->selectrow_array(
+ "SELECT COUNT(id) FROM components WHERE product_id = ?",
+ undef, $self->id);
+ }
+ return $self->{component_count};
+}
- # If $full_data is true, we collect all the data in all cases,
- # even if the cache is already populated.
- # $full_data is never used except in the very special case where
- # all configurable bug groups are displayed to administrators,
- # so we don't care about collecting all the data again in this case.
- if (!defined $self->{group_controls} || $full_data) {
- # Include name to the list, to allow us sorting data more easily.
- my $query = qq{SELECT id, name, entry, membercontrol, othercontrol,
- canedit, editcomponents, editbugs, canconfirm
+sub group_controls {
+ my ($self, $full_data) = @_;
+ my $dbh = Bugzilla->dbh;
+
+ # By default, we don't return groups which are not listed in
+ # group_control_map. If $full_data is true, then we also
+ # return groups whose settings could be set for the product.
+ my $where_or_and = 'WHERE';
+ my $and_or_where = 'AND';
+ if ($full_data) {
+ $where_or_and = 'AND';
+ $and_or_where = 'WHERE';
+ }
+
+ # If $full_data is true, we collect all the data in all cases,
+ # even if the cache is already populated.
+ # $full_data is never used except in the very special case where
+ # all configurable bug groups are displayed to administrators,
+ # so we don't care about collecting all the data again in this case.
+ if (!defined $self->{group_controls} || $full_data) {
+ ## REDHAT EXTENSION 881990: Add description for sorting
+ # Include name and description to the list, to allow us sorting data more easily.
+ my $query = qq{SELECT id, name, entry, membercontrol, othercontrol,
+ canedit, editcomponents, editbugs, canconfirm, description
FROM groups
LEFT JOIN group_control_map
ON id = group_id
$where_or_and product_id = ?
$and_or_where isbuggroup = 1};
- $self->{group_controls} =
- $dbh->selectall_hashref($query, 'id', undef, $self->id);
-
- # For each group ID listed above, create and store its group object.
- my @gids = keys %{$self->{group_controls}};
- my $groups = Bugzilla::Group->new_from_list(\@gids);
- $self->{group_controls}->{$_->id}->{group} = $_ foreach @$groups;
- }
-
- # We never cache bug counts, for the same reason as above.
- if ($full_data) {
- my $counts =
- $dbh->selectall_arrayref('SELECT group_id, COUNT(bugs.bug_id) AS bug_count
+ $self->{group_controls}
+ = $dbh->selectall_hashref($query, 'id', undef, $self->id);
+
+ # For each group ID listed above, create and store its group object.
+ my @gids = keys %{$self->{group_controls}};
+ my $groups = Bugzilla::Group->new_from_list(\@gids);
+ $self->{group_controls}->{$_->id}->{group} = $_ foreach @$groups;
+ }
+
+ # We never cache bug counts, for the same reason as above.
+ if ($full_data) {
+ my $counts = $dbh->selectall_arrayref(
+ 'SELECT group_id, COUNT(bugs.bug_id) AS bug_count
FROM bug_group_map
INNER JOIN bugs
ON bugs.bug_id = bug_group_map.bug_id
- WHERE bugs.product_id = ? ' .
- $dbh->sql_group_by('group_id'),
- {'Slice' => {}}, $self->id);
- foreach my $data (@$counts) {
- $self->{group_controls}->{$data->{group_id}}->{bug_count} = $data->{bug_count};
- }
+ WHERE bugs.product_id = ? '
+ . $dbh->sql_group_by('group_id'), {'Slice' => {}}, $self->id
+ );
+ foreach my $data (@$counts) {
+ $self->{group_controls}->{$data->{group_id}}->{bug_count} = $data->{bug_count};
}
- return $self->{group_controls};
+ }
+ return $self->{group_controls};
}
sub groups_available {
- my ($self) = @_;
- return $self->{groups_available} if defined $self->{groups_available};
- my $dbh = Bugzilla->dbh;
- my $shown = CONTROLMAPSHOWN;
- my $default = CONTROLMAPDEFAULT;
- my %member_groups = @{ $dbh->selectcol_arrayref(
- "SELECT group_id, membercontrol
+ my ($self) = @_;
+ return $self->{groups_available} if defined $self->{groups_available};
+ my $dbh = Bugzilla->dbh;
+ my $shown = CONTROLMAPSHOWN;
+ my $default = CONTROLMAPDEFAULT;
+ my %member_groups = @{
+ $dbh->selectcol_arrayref(
+ "SELECT group_id, membercontrol
FROM group_control_map
INNER JOIN groups ON group_control_map.group_id = groups.id
WHERE isbuggroup = 1 AND isactive = 1 AND product_id = ?
AND (membercontrol = $shown OR membercontrol = $default)
- AND " . Bugzilla->user->groups_in_sql(),
- {Columns=>[1,2]}, $self->id) };
- # We don't need to check the group membership here, because we only
- # add these groups to the list below if the group isn't already listed
- # for membercontrol.
- my %other_groups = @{ $dbh->selectcol_arrayref(
- "SELECT group_id, othercontrol
+ AND " . Bugzilla->user->groups_in_sql(), {Columns => [1, 2]},
+ $self->id
+ )
+ };
+
+ # We don't need to check the group membership here, because we only
+ # add these groups to the list below if the group isn't already listed
+ # for membercontrol.
+ my %other_groups = @{
+ $dbh->selectcol_arrayref(
+ "SELECT group_id, othercontrol
FROM group_control_map
INNER JOIN groups ON group_control_map.group_id = groups.id
WHERE isbuggroup = 1 AND isactive = 1 AND product_id = ?
- AND (othercontrol = $shown OR othercontrol = $default)",
- {Columns=>[1,2]}, $self->id) };
-
- # If the user is a member, then we use the membercontrol value.
- # Otherwise, we use the othercontrol value.
- my %all_groups = %member_groups;
- foreach my $id (keys %other_groups) {
- if (!defined $all_groups{$id}) {
- $all_groups{$id} = $other_groups{$id};
- }
+ AND (othercontrol = $shown OR othercontrol = $default)",
+ {Columns => [1, 2]}, $self->id
+ )
+ };
+
+ # If the user is a member, then we use the membercontrol value.
+ # Otherwise, we use the othercontrol value.
+ my %all_groups = %member_groups;
+ foreach my $id (keys %other_groups) {
+ if (!defined $all_groups{$id}) {
+ $all_groups{$id} = $other_groups{$id};
}
+ }
- my $available = Bugzilla::Group->new_from_list([keys %all_groups]);
- foreach my $group (@$available) {
- $group->{is_default} = 1 if $all_groups{$group->id} == $default;
- }
+ my $available = Bugzilla::Group->new_from_list([keys %all_groups]);
+ foreach my $group (@$available) {
+ $group->{is_default} = 1 if $all_groups{$group->id} == $default;
+ }
- $self->{groups_available} = $available;
- return $self->{groups_available};
+ $self->{groups_available} = $available;
+ return $self->{groups_available};
}
sub groups_mandatory {
- my ($self) = @_;
- return $self->{groups_mandatory} if $self->{groups_mandatory};
- my $groups = Bugzilla->user->groups_as_string;
- my $mandatory = CONTROLMAPMANDATORY;
- # For membercontrol we don't check group_id IN, because if membercontrol
- # is Mandatory, the group is Mandatory for everybody, regardless of their
- # group membership.
- my $ids = Bugzilla->dbh->selectcol_arrayref(
- "SELECT group_id
+ my ($self) = @_;
+ return $self->{groups_mandatory} if $self->{groups_mandatory};
+ my $groups = Bugzilla->user->groups_as_string;
+ my $mandatory = CONTROLMAPMANDATORY;
+
+ # For membercontrol we don't check group_id IN, because if membercontrol
+ # is Mandatory, the group is Mandatory for everybody, regardless of their
+ # group membership.
+ my $ids = Bugzilla->dbh->selectcol_arrayref(
+ "SELECT group_id
FROM group_control_map
INNER JOIN groups ON group_control_map.group_id = groups.id
WHERE product_id = ? AND isactive = 1
AND (membercontrol = $mandatory
OR (othercontrol = $mandatory
- AND group_id NOT IN ($groups)))",
- undef, $self->id);
- $self->{groups_mandatory} = Bugzilla::Group->new_from_list($ids);
- return $self->{groups_mandatory};
+ AND group_id NOT IN ($groups)))", undef, $self->id
+ );
+ $self->{groups_mandatory} = Bugzilla::Group->new_from_list($ids);
+ return $self->{groups_mandatory};
}
# We don't just check groups_valid, because we want to know specifically
# if this group can be validly set by the currently-logged-in user.
sub group_is_settable {
- my ($self, $group) = @_;
+ my ($self, $group) = @_;
- return 0 unless ($group->is_active && $group->is_bug_group);
+ return 0 unless ($group->is_active && $group->is_bug_group);
- my $is_mandatory = grep { $group->id == $_->id }
- @{ $self->groups_mandatory };
- my $is_available = grep { $group->id == $_->id }
- @{ $self->groups_available };
- return ($is_mandatory or $is_available) ? 1 : 0;
+ my $is_mandatory = grep { $group->id == $_->id } @{$self->groups_mandatory};
+ my $is_available = grep { $group->id == $_->id } @{$self->groups_available};
+ return ($is_mandatory or $is_available) ? 1 : 0;
}
sub group_is_valid {
- my ($self, $group) = @_;
- return grep($_->id == $group->id, @{ $self->groups_valid }) ? 1 : 0;
+ my ($self, $group) = @_;
+ return grep($_->id == $group->id, @{$self->groups_valid}) ? 1 : 0;
}
sub groups_valid {
- my ($self) = @_;
- return $self->{groups_valid} if defined $self->{groups_valid};
-
- # Note that we don't check OtherControl below, because there is no
- # valid NA/* combination.
- my $ids = Bugzilla->dbh->selectcol_arrayref(
- "SELECT DISTINCT group_id
+ my ($self) = @_;
+ return $self->{groups_valid} if defined $self->{groups_valid};
+
+ # Note that we don't check OtherControl below, because there is no
+ # valid NA/* combination.
+ my $ids = Bugzilla->dbh->selectcol_arrayref(
+ "SELECT DISTINCT group_id
FROM group_control_map AS gcm
INNER JOIN groups ON gcm.group_id = groups.id
WHERE product_id = ? AND isbuggroup = 1
- AND membercontrol != " . CONTROLMAPNA, undef, $self->id);
- $self->{groups_valid} = Bugzilla::Group->new_from_list($ids);
- return $self->{groups_valid};
+ AND membercontrol != " . CONTROLMAPNA, undef, $self->id
+ );
+ $self->{groups_valid} = Bugzilla::Group->new_from_list($ids);
+ return $self->{groups_valid};
}
sub versions {
- my $self = shift;
- my $dbh = Bugzilla->dbh;
+ my $self = shift;
+ my $dbh = Bugzilla->dbh;
- if (!defined $self->{versions}) {
- my $ids = $dbh->selectcol_arrayref(q{
+ if (!defined $self->{versions}) {
+ my $ids = $dbh->selectcol_arrayref(
+ q{
SELECT id FROM versions
- WHERE product_id = ?}, undef, $self->id);
+ WHERE product_id = ?}, undef, $self->id
+ );
- $self->{versions} = Bugzilla::Version->new_from_list($ids);
- }
- return $self->{versions};
+ $self->{versions} = Bugzilla::Version->new_from_list($ids);
+ }
+ return $self->{versions};
}
sub milestones {
- my $self = shift;
- my $dbh = Bugzilla->dbh;
+ my $self = shift;
+ my $dbh = Bugzilla->dbh;
- if (!defined $self->{milestones}) {
- my $ids = $dbh->selectcol_arrayref(q{
+ if (!defined $self->{milestones}) {
+ my $ids = $dbh->selectcol_arrayref(
+ q{
SELECT id FROM milestones
- WHERE product_id = ?}, undef, $self->id);
-
- $self->{milestones} = Bugzilla::Milestone->new_from_list($ids);
- }
- return $self->{milestones};
+ WHERE product_id = ?}, undef, $self->id
+ );
+
+ $self->{milestones} = Bugzilla::Milestone->new_from_list($ids);
+ }
+ return $self->{milestones};
+}
+
+sub releases {
+ my $self = shift;
+ my $dbh = Bugzilla->dbh;
+
+ if (!defined $self->{releases}) {
+ my $ids = $dbh->selectcol_arrayref(
+ q{
+ SELECT id FROM releases
+ WHERE product_id = ?}, undef, $self->id
+ );
+
+ $self->{releases} = Bugzilla::Release->new_from_list($ids);
+ }
+ return $self->{releases};
}
sub bug_count {
- my $self = shift;
- my $dbh = Bugzilla->dbh;
+ my $self = shift;
+ my $dbh = Bugzilla->dbh;
- if (!defined $self->{'bug_count'}) {
- $self->{'bug_count'} = $dbh->selectrow_array(qq{
+ if (!defined $self->{'bug_count'}) {
+ $self->{'bug_count'} = $dbh->selectrow_array(
+ qq{
SELECT COUNT(bug_id) FROM bugs
- WHERE product_id = ?}, undef, $self->id);
+ WHERE product_id = ?}, undef, $self->id
+ );
- }
- return $self->{'bug_count'};
+ }
+ return $self->{'bug_count'};
}
sub bug_ids {
- my $self = shift;
- my $dbh = Bugzilla->dbh;
-
- if (!defined $self->{'bug_ids'}) {
- $self->{'bug_ids'} =
- $dbh->selectcol_arrayref(q{SELECT bug_id FROM bugs
- WHERE product_id = ?},
- undef, $self->id);
- }
- return $self->{'bug_ids'};
+ my $self = shift;
+ my $dbh = Bugzilla->dbh;
+
+ if (!defined $self->{'bug_ids'}) {
+ $self->{'bug_ids'} = $dbh->selectcol_arrayref(
+ q{SELECT bug_id FROM bugs
+ WHERE product_id = ?}, undef, $self->id
+ );
+ }
+ return $self->{'bug_ids'};
}
sub user_has_access {
- my ($self, $user) = @_;
+ my ($self, $user) = @_;
- return Bugzilla->dbh->selectrow_array(
- 'SELECT CASE WHEN group_id IS NULL THEN 1 ELSE 0 END
+ return Bugzilla->dbh->selectrow_array(
+ 'SELECT CASE WHEN group_id IS NULL THEN 1 ELSE 0 END
FROM products LEFT JOIN group_control_map
ON group_control_map.product_id = products.id
AND group_control_map.entry != 0
AND group_id NOT IN (' . $user->groups_as_string . ')
- WHERE products.id = ? ' . Bugzilla->dbh->sql_limit(1),
- undef, $self->id);
+ WHERE products.id = ? ' . Bugzilla->dbh->sql_limit(1), undef, $self->id
+ );
}
sub flag_types {
- my $self = shift;
-
- return $self->{'flag_types'} if defined $self->{'flag_types'};
-
- # We cache flag types to avoid useless calls to get_clusions().
- my $cache = Bugzilla->request_cache->{flag_types_per_product} ||= {};
- $self->{flag_types} = {};
- my $prod_id = $self->id;
- my $flagtypes = Bugzilla::FlagType::match({ product_id => $prod_id });
-
- foreach my $type ('bug', 'attachment') {
- my @flags = grep { $_->target_type eq $type } @$flagtypes;
- $self->{flag_types}->{$type} = \@flags;
-
- # Also populate component flag types, while we are here.
- foreach my $comp (@{$self->components}) {
- $comp->{flag_types} ||= {};
- my $comp_id = $comp->id;
-
- foreach my $flag (@flags) {
- my $flag_id = $flag->id;
- $cache->{$flag_id} ||= $flag;
- my $i = $cache->{$flag_id}->inclusions_as_hash;
- my $e = $cache->{$flag_id}->exclusions_as_hash;
- my $included = $i->{0}->{0} || $i->{0}->{$comp_id}
- || $i->{$prod_id}->{0} || $i->{$prod_id}->{$comp_id};
- my $excluded = $e->{0}->{0} || $e->{0}->{$comp_id}
- || $e->{$prod_id}->{0} || $e->{$prod_id}->{$comp_id};
- push(@{$comp->{flag_types}->{$type}}, $flag) if ($included && !$excluded);
- }
- }
+ my $self = shift;
+
+ return $self->{'flag_types'} if defined $self->{'flag_types'};
+
+ # We cache flag types to avoid useless calls to get_clusions().
+ my $cache = Bugzilla->request_cache->{flag_types_per_product} ||= {};
+ $self->{flag_types} = {};
+ my $prod_id = $self->id;
+ my $flagtypes = Bugzilla::FlagType::match({product_id => $prod_id});
+
+ foreach my $type ('bug', 'attachment') {
+ my @flags = grep { $_->target_type eq $type } @$flagtypes;
+ $self->{flag_types}->{$type} = \@flags;
+
+ # Also populate component flag types, while we are here.
+ foreach my $comp (@{$self->components}) {
+ $comp->{flag_types} ||= {};
+ my $comp_id = $comp->id;
+
+ foreach my $flag (@flags) {
+ my $flag_id = $flag->id;
+ $cache->{$flag_id} ||= $flag;
+ my $i = $cache->{$flag_id}->inclusions_as_hash;
+ my $e = $cache->{$flag_id}->exclusions_as_hash;
+ my $included
+ = $i->{0}->{0}
+ || $i->{0}->{$comp_id}
+ || $i->{$prod_id}->{0}
+ || $i->{$prod_id}->{$comp_id};
+ my $excluded
+ = $e->{0}->{0}
+ || $e->{0}->{$comp_id}
+ || $e->{$prod_id}->{0}
+ || $e->{$prod_id}->{$comp_id};
+ push(@{$comp->{flag_types}->{$type}}, $flag) if ($included && !$excluded);
+ }
}
- return $self->{'flag_types'};
+ }
+ return $self->{'flag_types'};
}
sub classification {
- my $self = shift;
- $self->{'classification'} ||=
- new Bugzilla::Classification({ id => $self->classification_id, cache => 1 });
- return $self->{'classification'};
+ my $self = shift;
+ $self->{'classification'} ||= new Bugzilla::Classification(
+ {id => $self->classification_id, cache => 1});
+ return $self->{'classification'};
}
###############################
@@ -825,30 +1039,103 @@ sub classification {
###############################
sub allows_unconfirmed { return $_[0]->{'allows_unconfirmed'}; }
-sub description { return $_[0]->{'description'}; }
-sub is_active { return $_[0]->{'isactive'}; }
-sub default_milestone { return $_[0]->{'defaultmilestone'}; }
-sub classification_id { return $_[0]->{'classification_id'}; }
+sub description { return $_[0]->{'description'}; }
+sub is_active { return $_[0]->{'isactive'}; }
+sub default_milestone { return $_[0]->{'defaultmilestone'}; }
+sub classification_id { return $_[0]->{'classification_id'}; }
+sub default_release { return $_[0]->{'defaultrelease'}; }
+## REDHAT EXTENSION BEGIN 706784
+sub multiple_components { return $_[0]->{'multiple_components'}; }
+sub multiple_target_releases { return $_[0]->{'multiple_target_releases'}; }
+sub multiple_versions { return $_[0]->{'multiple_versions'}; }
+## REDHAT EXTENSION END 706784
+## REDHAT EXTENSION BEGIN 829154
+sub always_private { return $_[0]->{'always_private'}; }
+## REDHAT EXTENSION END 829154
###############################
#### Subroutines ######
###############################
sub check {
- my ($class, $params) = @_;
- $params = { name => $params } if !ref $params;
- if (!$params->{allow_inaccessible}) {
- $params->{_error} = 'product_access_denied';
- }
- my $product = $class->SUPER::check($params);
+ my ($class, $params) = @_;
+ $params = {name => $params} if !ref $params;
+ if (!$params->{allow_inaccessible}) {
+ $params->{_error} = 'product_access_denied';
+ }
+ my $product = $class->SUPER::check($params);
+
+ if ( !$params->{allow_inaccessible}
+ && !Bugzilla->user->can_access_product($product))
+ {
+ ThrowUserError('product_access_denied', $params);
+ }
+ return $product;
+}
- if (!$params->{allow_inaccessible}
- && !Bugzilla->user->can_access_product($product))
- {
- ThrowUserError('product_access_denied', $params);
- }
- return $product;
+## REDHAT EXTENSION START 951975
+sub private_product_check {
+ my $self = shift;
+
+ # Get a list of available groups for the product
+ my $gc = $self->group_controls;
+ my @group_ids
+ = grep { $gc->{$_}{membercontrol} || $gc->{$_}{othercontrol} } keys %$gc;
+
+ ThrowUserError('product_cannot_be_private') if scalar(@group_ids) == 0;
+
+ my $groups_map = join(',', @group_ids);
+
+ # Return the number of bugs that aren't / wouldn't be restricted
+ return Bugzilla->dbh->selectrow_array(
+ qq{
+ SELECT COUNT(*)
+ FROM bugs
+ WHERE product_id = ?
+ AND bug_id NOT IN (
+ SELECT bug_id FROM bug_group_map
+ WHERE group_id IN ($groups_map)
+ )
+ }, undef, $self->id
+ );
+}
+
+
+sub private_product_set {
+ my $self = shift;
+
+ # Get the new group name, and turn it into an object
+ my $new_group_name = shift;
+ my $new_group = Bugzilla::Group->new({name => $new_group_name});
+ ThrowUserError('object_does_not_exist',
+ {class => 'Bugzilla::Group', name => $new_group_name})
+ if not $new_group;
+
+ # Check that this group is available for these bugs
+ my $gc = $self->group_controls;
+ my @group_ids
+ = grep { $gc->{$_}{membercontrol} || $gc->{$_}{othercontrol} } keys %$gc;
+ ThrowUserError('group_restriction_not_allowed',
+ {name => $new_group_name, product => $self->name})
+ unless grep { $_ == $new_group->id } @group_ids;
+
+ my $groups_map = join(',', @group_ids);
+
+ # Add the group to any bugs that don't have a group restriction
+ return Bugzilla->dbh->do(
+ qq{
+ INSERT INTO bug_group_map (bug_id, group_id)
+ SELECT bug_id, ?
+ FROM bugs
+ WHERE product_id = ?
+ AND bug_id NOT IN (
+ SELECT bug_id FROM bug_group_map
+ WHERE group_id IN ($groups_map)
+ )
+ }, undef, $new_group->id, $self->id
+ );
}
+## REDHAT EXTENSION END 951975
1;
@@ -868,6 +1155,7 @@ Bugzilla::Product - Bugzilla product class.
my @components = $product->components();
my $groups_controls = $product->group_controls();
my @milestones = $product->milestones();
+ my @releases = $product->releases();
my @versions = $product->versions();
my $bugcount = $product->bug_count();
my $bug_ids = $product->bug_ids();
@@ -880,6 +1168,7 @@ Bugzilla::Product - Bugzilla product class.
my $description = $product->description;
my isactive = $product->is_active;
my $defaultmilestone = $product->default_milestone;
+ my $defaultrelease = $product->default_release;
my $classificationid = $product->classification_id;
my $allows_unconfirmed = $product->allows_unconfirmed;
@@ -1005,6 +1294,14 @@ a group is valid in a particular product.)
Returns: An array of Bugzilla::Milestone objects.
+=item C<releases>
+
+ Description: Returns all valid releases for that product.
+
+ Params: none.
+
+ Returns: An array of Bugzilla::Release objects.
+
=item C<bug_count()>
Description: Returns the total of bugs that belong to the product.
@@ -1068,6 +1365,17 @@ products and their components are also preloaded.
This function is not exported, so must be called like
C<Bugzilla::Product::preload($products)>.
+=item C<private_product_check>
+
+This is a Red Hat customisation. Returns the number of bugs a product
+has that have no group restrictions. In the case where group restrictions
+are changing, looks at the new list
+
+=item C<private_product_set>
+
+This is a Red Hat customisation. Given a group name will add the group to
+all bugs that match the above criteria.
+
=back
=head1 SEE ALSO
@@ -1108,4 +1416,26 @@ L<Bugzilla::Object>
=item update
+=item always_private
+
+=item set_multiple_components
+
+=item set_always_private
+
+=item default_release
+
+=item multiple_versions
+
+=item component_count
+
+=item set_multiple_target_releases
+
+=item set_default_release
+
+=item multiple_components
+
+=item set_multiple_versions
+
+=item multiple_target_releases
+
=back