Commit 52abf10f authored by lpsolit%gmail.com's avatar lpsolit%gmail.com

Bug 313123: Implement $component->create and $component->update based on…

Bug 313123: Implement $component->create and $component->update based on Object.pm - Patch by Fré©ric Buclin <LpSolit@gmail.com> r/a=mkanat
parent 52fecc3e
...@@ -930,7 +930,7 @@ sub _check_component { ...@@ -930,7 +930,7 @@ sub _check_component {
$name = trim($name); $name = trim($name);
$name || ThrowUserError("require_component"); $name || ThrowUserError("require_component");
($product = $invocant->product_obj) if ref $invocant; ($product = $invocant->product_obj) if ref $invocant;
my $obj = Bugzilla::Component::check_component($product, $name); my $obj = Bugzilla::Component->check({ product => $product, name => $name });
return $obj; return $obj;
} }
......
...@@ -23,10 +23,12 @@ package Bugzilla::Component; ...@@ -23,10 +23,12 @@ package Bugzilla::Component;
use base qw(Bugzilla::Object); use base qw(Bugzilla::Object);
use Bugzilla::Constants;
use Bugzilla::Util; use Bugzilla::Util;
use Bugzilla::Error; use Bugzilla::Error;
use Bugzilla::User; use Bugzilla::User;
use Bugzilla::FlagType; use Bugzilla::FlagType;
use Bugzilla::Series;
############################### ###############################
#### Initialization #### #### Initialization ####
...@@ -43,8 +45,32 @@ use constant DB_COLUMNS => qw( ...@@ -43,8 +45,32 @@ use constant DB_COLUMNS => qw(
description description
); );
############################### use constant REQUIRED_CREATE_FIELDS => qw(
#### Methods #### name
product
initialowner
description
);
use constant UPDATE_COLUMNS => qw(
name
initialowner
initialqacontact
description
);
use constant VALIDATORS => {
product => \&_check_product,
initialowner => \&_check_initialowner,
initialqacontact => \&_check_initialqacontact,
description => \&_check_description,
initial_cc => \&_check_cc_list,
};
use constant UPDATE_VALIDATORS => {
name => \&_check_name,
};
############################### ###############################
sub new { sub new {
...@@ -79,6 +105,225 @@ sub new { ...@@ -79,6 +105,225 @@ sub new {
return $component; return $component;
} }
sub create {
my $class = shift;
my $dbh = Bugzilla->dbh;
$dbh->bz_start_transaction();
$class->check_required_create_fields(@_);
my $params = $class->run_create_validators(@_);
my $cc_list = delete $params->{initial_cc};
my $component = $class->insert_create_data($params);
# We still have to fill the component_cc table.
$component->_update_cc_list($cc_list);
# Create series for the new component.
$component->_create_series();
$dbh->bz_commit_transaction();
return $component;
}
sub run_create_validators {
my $class = shift;
my $params = $class->SUPER::run_create_validators(@_);
my $product = delete $params->{product};
$params->{product_id} = $product->id;
$params->{name} = $class->_check_name($params->{name}, $product);
return $params;
}
sub update {
my $self = shift;
my $changes = $self->SUPER::update(@_);
# Update the component_cc table if necessary.
if (defined $self->{cc_ids}) {
my $diff = $self->_update_cc_list($self->{cc_ids});
$changes->{cc_list} = $diff if defined $diff;
}
return $changes;
}
sub remove_from_db {
my $self = shift;
my $dbh = Bugzilla->dbh;
$dbh->bz_start_transaction();
if ($self->bug_count) {
if (Bugzilla->params->{'allowbugdeletion'}) {
require Bugzilla::Bug;
foreach my $bug_id (@{$self->bug_ids}) {
# Note: We allow admins to delete bugs even if they can't
# see them, as long as they can see the product.
my $bug = new Bugzilla::Bug($bug_id);
$bug->remove_from_db();
}
} else {
ThrowUserError('component_has_bugs', {nb => $self->bug_count});
}
}
$dbh->do('DELETE FROM flaginclusions WHERE component_id = ?',
undef, $self->id);
$dbh->do('DELETE FROM flagexclusions WHERE component_id = ?',
undef, $self->id);
$dbh->do('DELETE FROM component_cc WHERE component_id = ?',
undef, $self->id);
$dbh->do('DELETE FROM components WHERE id = ?', undef, $self->id);
$dbh->bz_commit_transaction();
}
################################
# Validators
################################
sub _check_name {
my ($invocant, $name, $product) = @_;
$name = trim($name);
$name || ThrowUserError('component_blank_name');
if (length($name) > MAX_COMPONENT_SIZE) {
ThrowUserError('component_name_too_long', {'name' => $name});
}
$product = $invocant->product if (ref $invocant);
my $component = new Bugzilla::Component({product => $product, name => $name});
if ($component && (!ref $invocant || $component->id != $invocant->id)) {
ThrowUserError('component_already_exists', { name => $component->name,
product => $product });
}
return $name;
}
sub _check_description {
my ($invocant, $description) = @_;
$description = trim($description);
$description || ThrowUserError('component_blank_description');
return $description;
}
sub _check_initialowner {
my ($invocant, $owner) = @_;
$owner || ThrowUserError('component_need_initialowner');
my $owner_id = Bugzilla::User->check($owner)->id;
return $owner_id;
}
sub _check_initialqacontact {
my ($invocant, $qa_contact) = @_;
my $qa_contact_id;
if (Bugzilla->params->{'useqacontact'}) {
$qa_contact_id = Bugzilla::User->check($qa_contact)->id if $qa_contact;
}
elsif (ref $invocant) {
$qa_contact_id = $invocant->{initialqacontact};
}
return $qa_contact_id;
}
sub _check_product {
my ($invocant, $product) = @_;
return Bugzilla->user->check_can_admin_product($product->name);
}
sub _check_cc_list {
my ($invocant, $cc_list) = @_;
my %cc_ids;
foreach my $cc (@$cc_list) {
my $id = login_to_id($cc, THROW_ERROR);
$cc_ids{$id} = 1;
}
return [keys %cc_ids];
}
###############################
#### Methods ####
###############################
sub _update_cc_list {
my ($self, $cc_list) = @_;
my $dbh = Bugzilla->dbh;
my $old_cc_list =
$dbh->selectcol_arrayref('SELECT user_id FROM component_cc
WHERE component_id = ?', undef, $self->id);
my ($removed, $added) = diff_arrays($old_cc_list, $cc_list);
my $diff;
if (scalar @$removed || scalar @$added) {
$diff = [join(', ', @$removed), join(', ', @$added)];
}
$dbh->do('DELETE FROM component_cc WHERE component_id = ?', undef, $self->id);
my $sth = $dbh->prepare('INSERT INTO component_cc
(user_id, component_id) VALUES (?, ?)');
$sth->execute($_, $self->id) foreach (@$cc_list);
return $diff;
}
sub _create_series {
my $self = shift;
# Insert default charting queries for this product.
# If they aren't using charting, this won't do any harm.
my $prodcomp = "&product=" . url_quote($self->product->name) .
"&component=" . url_quote($self->name);
my $open_query = 'field0-0-0=resolution&type0-0-0=notregexp&value0-0-0=.' .
$prodcomp;
my $nonopen_query = 'field0-0-0=resolution&type0-0-0=regexp&value0-0-0=.' .
$prodcomp;
my @series = ([get_text('series_all_open'), $open_query],
[get_text('series_all_closed'), $nonopen_query]);
foreach my $sdata (@series) {
my $series = new Bugzilla::Series(undef, $self->product->name,
$self->name, $sdata->[0],
Bugzilla->user->id, 1, $sdata->[1], 1);
$series->writeToDatabase();
}
}
sub set_name { $_[0]->set('name', $_[1]); }
sub set_description { $_[0]->set('description', $_[1]); }
sub set_default_assignee {
my ($self, $owner) = @_;
$self->set('initialowner', $owner);
# Reset the default owner object.
delete $self->{default_assignee};
}
sub set_default_qa_contact {
my ($self, $qa_contact) = @_;
$self->set('initialqacontact', $qa_contact);
# Reset the default QA contact object.
delete $self->{default_qa_contact};
}
sub set_cc_list {
my ($self, $cc_list) = @_;
$self->{cc_ids} = $self->_check_cc_list($cc_list);
# Reset the list of CC user objects.
delete $self->{initial_cc};
}
sub bug_count { sub bug_count {
my $self = shift; my $self = shift;
my $dbh = Bugzilla->dbh; my $dbh = Bugzilla->dbh;
...@@ -143,15 +388,17 @@ sub flag_types { ...@@ -143,15 +388,17 @@ sub flag_types {
sub initial_cc { sub initial_cc {
my $self = shift; my $self = shift;
my $dbh = Bugzilla->dbh; my $dbh = Bugzilla->dbh;
if (!defined $self->{'initial_cc'}) { if (!defined $self->{'initial_cc'}) {
my $cc_ids = $dbh->selectcol_arrayref( # If set_cc_list() has been called but data are not yet written
"SELECT user_id FROM component_cc WHERE component_id = ?", # into the DB, we want the new values defined by it.
undef, $self->id); my $cc_ids = $self->{cc_ids}
my $initial_cc = Bugzilla::User->new_from_list($cc_ids); || $dbh->selectcol_arrayref('SELECT user_id FROM component_cc
$self->{'initial_cc'} = $initial_cc; WHERE component_id = ?',
undef, $self->id);
$self->{'initial_cc'} = Bugzilla::User->new_from_list($cc_ids);
} }
return $self->{'initial_cc'}; return $self->{'initial_cc'};
} }
...@@ -178,27 +425,6 @@ sub product_id { return $_[0]->{'product_id'}; } ...@@ -178,27 +425,6 @@ sub product_id { return $_[0]->{'product_id'}; }
#### Subroutines #### #### Subroutines ####
############################### ###############################
sub check_component {
my ($product, $comp_name) = @_;
$comp_name || ThrowUserError('component_blank_name');
if (length($comp_name) > 64) {
ThrowUserError('component_name_too_long',
{'name' => $comp_name});
}
my $component =
new Bugzilla::Component({product => $product,
name => $comp_name});
unless ($component) {
ThrowUserError('component_not_valid',
{'product' => $product->name,
'name' => $comp_name});
}
return $component;
}
1; 1;
__END__ __END__
...@@ -211,9 +437,8 @@ Bugzilla::Component - Bugzilla product component class. ...@@ -211,9 +437,8 @@ Bugzilla::Component - Bugzilla product component class.
use Bugzilla::Component; use Bugzilla::Component;
my $component = new Bugzilla::Component(1); my $component = new Bugzilla::Component($comp_id);
my $component = new Bugzilla::Component({product => $product, my $component = new Bugzilla::Component({ product => $product, name => $name });
name => 'AcmeComp'});
my $bug_count = $component->bug_count(); my $bug_count = $component->bug_count();
my $bug_ids = $component->bug_ids(); my $bug_ids = $component->bug_ids();
...@@ -228,7 +453,23 @@ Bugzilla::Component - Bugzilla product component class. ...@@ -228,7 +453,23 @@ Bugzilla::Component - Bugzilla product component class.
my $bug_flag_types = $component->flag_types->{'bug'}; my $bug_flag_types = $component->flag_types->{'bug'};
my $attach_flag_types = $component->flag_types->{'attachment'}; my $attach_flag_types = $component->flag_types->{'attachment'};
my $component = Bugzilla::Component::check_component($product, 'AcmeComp'); my $component = Bugzilla::Component->check({ product => $product, name => $name });
my $component =
Bugzilla::Component->create({ name => $name,
product => $product,
initialowner => $user_login1,
initialqacontact => $user_login2,
description => $description});
$component->set_name($new_name);
$component->set_description($new_description);
$component->set_default_assignee($new_login_name);
$component->set_default_qa_contact($new_login_name);
$component->set_cc_list(\@new_login_names);
$component->update();
$component->remove_from_db;
=head1 DESCRIPTION =head1 DESCRIPTION
...@@ -241,14 +482,15 @@ Component.pm represents a Product Component object. ...@@ -241,14 +482,15 @@ Component.pm represents a Product Component object.
=item C<new($param)> =item C<new($param)>
Description: The constructor is used to load an existing component Description: The constructor is used to load an existing component
by passing a component id or a hash with the product by passing a component ID or a hash with the product
id and the component name. object the component belongs to and the component name.
Params: $param - If you pass an integer, the integer is the Params: $param - If you pass an integer, the integer is the
component id from the database that we want to component ID from the database that we want to
read in. If you pass in a hash with 'name' key, read in. If you pass in a hash with the 'name'
then the value of the name key is the name of a and 'product' keys, then the value of the name
component from the DB. key is the name of a component being in the given
product.
Returns: A Bugzilla::Component object. Returns: A Bugzilla::Component object.
...@@ -288,41 +530,117 @@ Component.pm represents a Product Component object. ...@@ -288,41 +530,117 @@ Component.pm represents a Product Component object.
=item C<initial_cc> =item C<initial_cc>
Returns an arrayref of L<Bugzilla::User> objects representing the Description: Returns a list of user objects representing users being
Initial CC List. in the initial CC list.
Params: none.
Returns: An arrayref of L<Bugzilla::User> objects.
=item C<flag_types()> =item C<flag_types()>
Description: Returns all bug and attachment flagtypes available for Description: Returns all bug and attachment flagtypes available for
the component. the component.
Params: none. Params: none.
Returns: Two references to an array of flagtype objects. Returns: Two references to an array of flagtype objects.
=item C<product()> =item C<product()>
Description: Returns the product the component belongs to. Description: Returns the product the component belongs to.
Params: none.
Returns: A Bugzilla::Product object.
=item C<set_name($new_name)>
Description: Changes the name of the component.
Params: $new_name - new name of the component (string). This name
must be unique within the product.
Returns: Nothing.
=item C<set_description($new_desc)>
Description: Changes the description of the component.
Params: $new_desc - new description of the component (string).
Returns: Nothing.
=item C<set_default_assignee($new_assignee)>
Description: Changes the default assignee of the component.
Params: $new_owner - login name of the new default assignee of
the component (string). This user account
must already exist.
Params: none. Returns: Nothing.
Returns: A Bugzilla::Product object. =item C<set_default_qa_contact($new_qa_contact)>
Description: Changes the default QA contact of the component.
Params: $new_qa_contact - login name of the new QA contact of
the component (string). This user
account must already exist.
Returns: Nothing.
=item C<set_cc_list(\@cc_list)>
Description: Changes the list of users being in the CC list by default.
Params: \@cc_list - list of login names (string). All the user
accounts must already exist.
Returns: Nothing.
=item C<update()>
Description: Write changes made to the component into the DB.
Params: none.
Returns: A hashref with changes made to the component object.
=item C<remove_from_db()>
Description: Deletes the current component from the DB. The object itself
is not destroyed.
Params: none.
Returns: Nothing.
=back =back
=head1 SUBROUTINES =head1 CLASS METHODS
=over =over
=item C<check_component($product, $comp_name)> =item C<create(\%params)>
Description: Checks if the component name was passed in and if it is a valid Description: Create a new component for the given product.
component.
Params: $product - A Bugzilla::Product object. Params: The hashref must have the following keys:
$comp_name - String with a component name. name - name of the new component (string). This name
must be unique within the product.
product - a Bugzilla::Product object to which
the Component is being added.
description - description of the new component (string).
initialowner - login name of the default assignee (string).
The following keys are optional:
initiaqacontact - login name of the default QA contact (string),
or an empty string to clear it.
initial_cc - an arrayref of login names to add to the
CC list by default.
Returns: Bugzilla::Component object. Returns: A Bugzilla::Component object.
=back =back
......
...@@ -147,6 +147,7 @@ use File::Basename; ...@@ -147,6 +147,7 @@ use File::Basename;
MAX_LEN_QUERY_NAME MAX_LEN_QUERY_NAME
MAX_MILESTONE_SIZE MAX_MILESTONE_SIZE
MAX_COMPONENT_SIZE
); );
@Bugzilla::Constants::EXPORT_OK = qw(contenttypes); @Bugzilla::Constants::EXPORT_OK = qw(contenttypes);
...@@ -410,6 +411,9 @@ use constant MAX_LEN_QUERY_NAME => 64; ...@@ -410,6 +411,9 @@ use constant MAX_LEN_QUERY_NAME => 64;
# The longest milestone name allowed. # The longest milestone name allowed.
use constant MAX_MILESTONE_SIZE => 20; use constant MAX_MILESTONE_SIZE => 20;
# The longest component name allowed.
use constant MAX_COMPONENT_SIZE => 64;
sub bz_locations { sub bz_locations {
# We know that Bugzilla/Constants.pm must be in %INC at this point. # We know that Bugzilla/Constants.pm must be in %INC at this point.
# So the only question is, what's the name of the directory # So the only question is, what's the name of the directory
......
...@@ -431,6 +431,10 @@ A hash with one element, C<id>. This is the id of the newly-filed bug. ...@@ -431,6 +431,10 @@ A hash with one element, C<id>. This is the id of the newly-filed bug.
=over =over
=item 51 (Invalid Object)
The component you specified is not valid for this Product.
=item 103 (Invalid Alias) =item 103 (Invalid Alias)
The alias you specified is invalid for some reason. See the error message The alias you specified is invalid for some reason. See the error message
...@@ -443,8 +447,7 @@ have more detail. ...@@ -443,8 +447,7 @@ have more detail.
=item 105 (Invalid Component) =item 105 (Invalid Component)
Either you didn't specify a component, or the component you specified was You didn't specify a component.
invalid.
=item 106 (Invalid Product) =item 106 (Invalid Product)
......
...@@ -50,6 +50,9 @@ use base qw(Exporter); ...@@ -50,6 +50,9 @@ use base qw(Exporter);
# comment that it was retired. Also, if an error changes its name, you'll # comment that it was retired. Also, if an error changes its name, you'll
# have to fix it here. # have to fix it here.
use constant WS_ERROR_CODE => { use constant WS_ERROR_CODE => {
# Generic Bugzilla::Object errors are 50-99.
object_name_not_specified => 50,
object_does_not_exist => 51,
# Bug errors usually occupy the 100-200 range. # Bug errors usually occupy the 100-200 range.
improper_bug_id_field_value => 100, improper_bug_id_field_value => 100,
bug_id_does_not_exist => 101, bug_id_does_not_exist => 101,
...@@ -65,7 +68,6 @@ use constant WS_ERROR_CODE => { ...@@ -65,7 +68,6 @@ use constant WS_ERROR_CODE => {
# Component errors # Component errors
require_component => 105, require_component => 105,
component_name_too_long => 105, component_name_too_long => 105,
component_not_valid => 105,
# Invalid Product # Invalid Product
no_products => 106, no_products => 106,
entry_access_denied => 106, entry_access_denied => 106,
......
...@@ -22,46 +22,19 @@ ...@@ -22,46 +22,19 @@
# Terry Weissman <terry@mozilla.org> # Terry Weissman <terry@mozilla.org>
# Frédéric Buclin <LpSolit@gmail.com> # Frédéric Buclin <LpSolit@gmail.com>
# Akamai Technologies <bugzilla-dev@akamai.com> # Akamai Technologies <bugzilla-dev@akamai.com>
#
# Direct any questions on this source code to
#
# Holger Schurig <holgerschurig@nikocity.de>
use strict; use strict;
use lib "."; use lib ".";
use Bugzilla; use Bugzilla;
use Bugzilla::Constants; use Bugzilla::Constants;
use Bugzilla::Series;
use Bugzilla::Util; use Bugzilla::Util;
use Bugzilla::Error; use Bugzilla::Error;
use Bugzilla::User; use Bugzilla::User;
use Bugzilla::Component; use Bugzilla::Component;
use Bugzilla::Bug;
use Bugzilla::Token; use Bugzilla::Token;
###############
# Subroutines #
###############
# Takes an arrayref of login names and returns an arrayref of user ids.
sub check_initial_cc {
my ($user_names) = @_;
my %cc_ids;
foreach my $cc (@$user_names) {
my $id = login_to_id($cc, THROW_ERROR);
$cc_ids{$id} = 1;
}
return [keys %cc_ids];
}
###############
# Main Script #
###############
my $cgi = Bugzilla->cgi; my $cgi = Bugzilla->cgi;
my $dbh = Bugzilla->dbh;
my $template = Bugzilla->template; my $template = Bugzilla->template;
my $vars = {}; my $vars = {};
...@@ -70,7 +43,6 @@ my $vars = {}; ...@@ -70,7 +43,6 @@ my $vars = {};
# #
my $user = Bugzilla->login(LOGIN_REQUIRED); my $user = Bugzilla->login(LOGIN_REQUIRED);
my $whoid = $user->id;
print $cgi->header(); print $cgi->header();
...@@ -115,16 +87,13 @@ my $product = $user->check_can_admin_product($product_name); ...@@ -115,16 +87,13 @@ my $product = $user->check_can_admin_product($product_name);
# #
unless ($action) { unless ($action) {
$vars->{'showbugcounts'} = $showbugcounts; $vars->{'showbugcounts'} = $showbugcounts;
$vars->{'product'} = $product; $vars->{'product'} = $product;
$template->process("admin/components/list.html.tmpl", $vars) $template->process("admin/components/list.html.tmpl", $vars)
|| ThrowTemplateError($template->error()); || ThrowTemplateError($template->error());
exit; exit;
} }
# #
# action='add' -> present form for parameters for new component # action='add' -> present form for parameters for new component
# #
...@@ -136,12 +105,9 @@ if ($action eq 'add') { ...@@ -136,12 +105,9 @@ if ($action eq 'add') {
$vars->{'product'} = $product; $vars->{'product'} = $product;
$template->process("admin/components/create.html.tmpl", $vars) $template->process("admin/components/create.html.tmpl", $vars)
|| ThrowTemplateError($template->error()); || ThrowTemplateError($template->error());
exit; exit;
} }
# #
# action='new' -> add component entered in the 'action=add' screen # action='new' -> add component entered in the 'action=add' screen
# #
...@@ -160,103 +126,23 @@ if ($action eq 'new') { ...@@ -160,103 +126,23 @@ if ($action eq 'new') {
my $description = trim($cgi->param('description') || ''); my $description = trim($cgi->param('description') || '');
my @initial_cc = $cgi->param('initialcc'); my @initial_cc = $cgi->param('initialcc');
$comp_name || ThrowUserError('component_blank_name');
if (length($comp_name) > 64) {
ThrowUserError('component_name_too_long',
{'name' => $comp_name});
}
my $component = my $component =
new Bugzilla::Component({product => $product, Bugzilla::Component->create({ name => $comp_name,
name => $comp_name}); product => $product,
description => $description,
if ($component) { initialowner => $default_assignee,
ThrowUserError('component_already_exists', initialqacontact => $default_qa_contact,
{'name' => $component->name}); initial_cc => \@initial_cc });
}
$description || ThrowUserError('component_blank_description',
{name => $comp_name});
$default_assignee || ThrowUserError('component_need_initialowner',
{name => $comp_name});
my $default_assignee_id = login_to_id($default_assignee);
my $default_qa_contact_id = Bugzilla->params->{'useqacontact'} ?
(login_to_id($default_qa_contact) || undef) : undef;
my $initial_cc_ids = check_initial_cc(\@initial_cc);
trick_taint($comp_name);
trick_taint($description);
$dbh->bz_start_transaction();
$dbh->do("INSERT INTO components
(product_id, name, description, initialowner,
initialqacontact)
VALUES (?, ?, ?, ?, ?)", undef,
($product->id, $comp_name, $description,
$default_assignee_id, $default_qa_contact_id));
$component = new Bugzilla::Component({ product => $product,
name => $comp_name });
my $sth = $dbh->prepare("INSERT INTO component_cc
(user_id, component_id) VALUES (?, ?)");
foreach my $user_id (@$initial_cc_ids) {
$sth->execute($user_id, $component->id);
}
$dbh->bz_commit_transaction();
# Insert default charting queries for this product.
# If they aren't using charting, this won't do any harm.
my @series;
my $prodcomp = "&product=" . url_quote($product->name) .
"&component=" . url_quote($comp_name);
# For localization reasons, we get the title of the queries from the
# submitted form.
my $open_name = $cgi->param('open_name');
my $nonopen_name = $cgi->param('nonopen_name');
my $open_query = "field0-0-0=resolution&type0-0-0=notregexp&value0-0-0=." .
$prodcomp;
my $nonopen_query = "field0-0-0=resolution&type0-0-0=regexp&value0-0-0=." .
$prodcomp;
# trick_taint is ok here, as these variables aren't used as a command
# or in SQL unquoted
trick_taint($open_name);
trick_taint($nonopen_name);
trick_taint($open_query);
trick_taint($nonopen_query);
push(@series, [$open_name, $open_query]);
push(@series, [$nonopen_name, $nonopen_query]);
foreach my $sdata (@series) {
my $series = new Bugzilla::Series(undef, $product->name,
$comp_name, $sdata->[0],
$whoid, 1, $sdata->[1], 1);
$series->writeToDatabase();
}
$vars->{'comp'} = $component; $vars->{'comp'} = $component;
$vars->{'product'} = $product; $vars->{'product'} = $product;
delete_token($token); delete_token($token);
$template->process("admin/components/created.html.tmpl", $template->process("admin/components/created.html.tmpl", $vars)
$vars)
|| ThrowTemplateError($template->error()); || ThrowTemplateError($template->error());
exit; exit;
} }
# #
# action='del' -> ask if user really wants to delete # action='del' -> ask if user really wants to delete
# #
...@@ -266,18 +152,14 @@ if ($action eq 'new') { ...@@ -266,18 +152,14 @@ if ($action eq 'new') {
if ($action eq 'del') { if ($action eq 'del') {
$vars->{'token'} = issue_session_token('delete_component'); $vars->{'token'} = issue_session_token('delete_component');
$vars->{'comp'} = $vars->{'comp'} =
Bugzilla::Component::check_component($product, $comp_name); Bugzilla::Component->check({ product => $product, name => $comp_name });
$vars->{'product'} = $product; $vars->{'product'} = $product;
$template->process("admin/components/confirm-delete.html.tmpl", $vars) $template->process("admin/components/confirm-delete.html.tmpl", $vars)
|| ThrowTemplateError($template->error()); || ThrowTemplateError($template->error());
exit; exit;
} }
# #
# action='delete' -> really delete the component # action='delete' -> really delete the component
# #
...@@ -285,34 +167,9 @@ if ($action eq 'del') { ...@@ -285,34 +167,9 @@ if ($action eq 'del') {
if ($action eq 'delete') { if ($action eq 'delete') {
check_token_data($token, 'delete_component'); check_token_data($token, 'delete_component');
my $component = my $component =
Bugzilla::Component::check_component($product, $comp_name); Bugzilla::Component->check({ product => $product, name => $comp_name });
if ($component->bug_count) {
if (Bugzilla->params->{"allowbugdeletion"}) {
foreach my $bug_id (@{$component->bug_ids}) {
# Note: We allow admins to delete bugs even if they can't
# see them, as long as they can see the product.
my $bug = new Bugzilla::Bug($bug_id);
$bug->remove_from_db();
}
} else {
ThrowUserError("component_has_bugs",
{nb => $component->bug_count });
}
}
$dbh->bz_start_transaction();
$dbh->do("DELETE FROM flaginclusions WHERE component_id = ?",
undef, $component->id);
$dbh->do("DELETE FROM flagexclusions WHERE component_id = ?",
undef, $component->id);
$dbh->do("DELETE FROM component_cc WHERE component_id = ?",
undef, $component->id);
$dbh->do("DELETE FROM components WHERE id = ?",
undef, $component->id);
$dbh->bz_commit_transaction(); $component->remove_from_db;
$vars->{'comp'} = $component; $vars->{'comp'} = $component;
$vars->{'product'} = $product; $vars->{'product'} = $product;
...@@ -323,8 +180,6 @@ if ($action eq 'delete') { ...@@ -323,8 +180,6 @@ if ($action eq 'delete') {
exit; exit;
} }
# #
# action='edit' -> present the edit component form # action='edit' -> present the edit component form
# #
...@@ -334,7 +189,7 @@ if ($action eq 'delete') { ...@@ -334,7 +189,7 @@ if ($action eq 'delete') {
if ($action eq 'edit') { if ($action eq 'edit') {
$vars->{'token'} = issue_session_token('edit_component'); $vars->{'token'} = issue_session_token('edit_component');
my $component = my $component =
Bugzilla::Component::check_component($product, $comp_name); Bugzilla::Component->check({ product => $product, name => $comp_name });
$vars->{'comp'} = $component; $vars->{'comp'} = $component;
$vars->{'initial_cc_names'} = $vars->{'initial_cc_names'} =
...@@ -342,15 +197,11 @@ if ($action eq 'edit') { ...@@ -342,15 +197,11 @@ if ($action eq 'edit') {
$vars->{'product'} = $product; $vars->{'product'} = $product;
$template->process("admin/components/edit.html.tmpl", $template->process("admin/components/edit.html.tmpl", $vars)
$vars)
|| ThrowTemplateError($template->error()); || ThrowTemplateError($template->error());
exit; exit;
} }
# #
# action='update' -> update the component # action='update' -> update the component
# #
...@@ -370,105 +221,23 @@ if ($action eq 'update') { ...@@ -370,105 +221,23 @@ if ($action eq 'update') {
my $description = trim($cgi->param('description') || ''); my $description = trim($cgi->param('description') || '');
my @initial_cc = $cgi->param('initialcc'); my @initial_cc = $cgi->param('initialcc');
my $component_old = my $component =
Bugzilla::Component::check_component($product, $comp_old_name); Bugzilla::Component->check({ product => $product, name => $comp_old_name });
$comp_name || ThrowUserError('component_blank_name');
if (length($comp_name) > 64) {
ThrowUserError('component_name_too_long',
{'name' => $comp_name});
}
if ($comp_name ne $component_old->name) {
my $component =
new Bugzilla::Component({product => $product,
name => $comp_name});
if ($component) {
ThrowUserError('component_already_exists',
{'name' => $component->name});
}
}
$description || ThrowUserError('component_blank_description',
{'name' => $component_old->name});
$default_assignee || ThrowUserError('component_need_initialowner',
{name => $comp_name});
my $default_assignee_id = login_to_id($default_assignee);
my $default_qa_contact_id = login_to_id($default_qa_contact) || undef;
my $initial_cc_ids = check_initial_cc(\@initial_cc);
$dbh->bz_start_transaction();
if ($comp_name ne $component_old->name) {
trick_taint($comp_name);
$dbh->do("UPDATE components SET name = ? WHERE id = ?",
undef, ($comp_name, $component_old->id));
$vars->{'updated_name'} = 1;
}
if ($description ne $component_old->description) {
trick_taint($description);
$dbh->do("UPDATE components SET description = ? WHERE id = ?",
undef, ($description, $component_old->id));
$vars->{'updated_description'} = 1;
}
if ($default_assignee ne $component_old->default_assignee->login) {
$dbh->do("UPDATE components SET initialowner = ? WHERE id = ?",
undef, ($default_assignee_id, $component_old->id));
$vars->{'updated_initialowner'} = 1;
}
if (Bugzilla->params->{'useqacontact'}
&& $default_qa_contact ne $component_old->default_qa_contact->login) {
$dbh->do("UPDATE components SET initialqacontact = ?
WHERE id = ?", undef,
($default_qa_contact_id, $component_old->id));
$vars->{'updated_initialqacontact'} = 1;
}
my @initial_cc_old = map($_->id, @{$component_old->initial_cc});
my ($removed, $added) = diff_arrays(\@initial_cc_old, $initial_cc_ids);
foreach my $user_id (@$removed) {
$dbh->do('DELETE FROM component_cc
WHERE component_id = ? AND user_id = ?', undef,
$component_old->id, $user_id);
$vars->{'updated_initialcc'} = 1;
}
foreach my $user_id (@$added) {
$dbh->do("INSERT INTO component_cc (user_id, component_id)
VALUES (?, ?)", undef, $user_id, $component_old->id);
$vars->{'updated_initialcc'} = 1;
}
$dbh->bz_commit_transaction(); $component->set_name($comp_name);
$component->set_description($description);
$component->set_default_assignee($default_assignee);
$component->set_default_qa_contact($default_qa_contact);
$component->set_cc_list(\@initial_cc);
my $changes = $component->update();
my $component = new Bugzilla::Component($component_old->id);
$vars->{'comp'} = $component; $vars->{'comp'} = $component;
$vars->{'initial_cc_names'} =
join(', ', map($_->login, @{$component->initial_cc}));
$vars->{'product'} = $product; $vars->{'product'} = $product;
$vars->{'changes'} = $changes;
delete_token($token); delete_token($token);
$template->process("admin/components/updated.html.tmpl", $template->process("admin/components/updated.html.tmpl", $vars)
$vars)
|| ThrowTemplateError($template->error()); || ThrowTemplateError($template->error());
exit; exit;
} }
......
...@@ -600,7 +600,8 @@ sub validateComponent { ...@@ -600,7 +600,8 @@ sub validateComponent {
($product && $product->id) ($product && $product->id)
|| ThrowUserError("flag_type_component_without_product"); || ThrowUserError("flag_type_component_without_product");
my $component = Bugzilla::Component::check_component($product, $component_name); my $component = Bugzilla::Component->check({ product => $product,
name => $component_name });
return $component; return $component;
} }
......
...@@ -196,8 +196,8 @@ sub queue { ...@@ -196,8 +196,8 @@ sub queue {
push(@criteria, "bugs.product_id = " . $product->id); push(@criteria, "bugs.product_id = " . $product->id);
push(@excluded_columns, 'product') unless $cgi->param('do_union'); push(@excluded_columns, 'product') unless $cgi->param('do_union');
if (defined $cgi->param('component') && $cgi->param('component') ne "") { if (defined $cgi->param('component') && $cgi->param('component') ne "") {
my $component = my $component = Bugzilla::Component->check({ product => $product,
Bugzilla::Component::check_component($product, scalar $cgi->param('component')); name => scalar $cgi->param('component') });
push(@criteria, "bugs.component_id = " . $component->id); push(@criteria, "bugs.component_id = " . $component->id);
push(@excluded_columns, 'component') unless $cgi->param('do_union'); push(@excluded_columns, 'component') unless $cgi->param('do_union');
} }
......
...@@ -95,8 +95,6 @@ ...@@ -95,8 +95,6 @@
<hr> <hr>
<input type="submit" id="create" value="Add"> <input type="submit" id="create" value="Add">
<input type="hidden" name="action" value="new"> <input type="hidden" name="action" value="new">
<input type="hidden" name='open_name' value='All Open'>
<input type="hidden" name='nonopen_name' value='All Closed'>
<input type="hidden" name='product' value="[% product.name FILTER html %]"> <input type="hidden" name='product' value="[% product.name FILTER html %]">
<input type="hidden" name="token" value="[% token FILTER html %]"> <input type="hidden" name="token" value="[% token FILTER html %]">
</form> </form>
......
...@@ -21,27 +21,12 @@ ...@@ -21,27 +21,12 @@
#%] #%]
[%# INTERFACE: [%# INTERFACE:
# # changes: hashref; contains changes made to the component.
# 'updated_XXX' variables are booleans, and are defined if the
# 'XXX' field was updated during the edit just being handled.
#
# updated_name: the name of the component updated
#
# updated_description: the component description updated
#
# updated_initialowner: the default assignee updated
#
# updated_initialqacontact: the default qa contact updated
#
# updated_initialcc: the default initial cc list
# #
# comp: object; Bugzilla::Component object representing the component # comp: object; Bugzilla::Component object representing the component
# user updated. # user updated.
# product: object; Bugzilla::Product object representing the product to # product: object; Bugzilla::Product object representing the product to
# which the component belongs. # which the component belongs.
#
# initial_cc_names: a comma-separated list of the login names of
# the Initial CC, if it was updated.
#%] #%]
[% title = BLOCK %]Updating Component '[% comp.name FILTER html %]' of Product [% title = BLOCK %]Updating Component '[% comp.name FILTER html %]' of Product
...@@ -50,7 +35,11 @@ ...@@ -50,7 +35,11 @@
title = title title = title
%] %]
[% IF updated_description %] [% IF changes.name.defined %]
<p>Updated Component name to: '[% comp.name FILTER html %]'.</p>
[% END %]
[% IF changes.description.defined %]
<table> <table>
<tr> <tr>
<td>Updated description to:</td> <td>Updated description to:</td>
...@@ -59,11 +48,11 @@ ...@@ -59,11 +48,11 @@
</table> </table>
[% END %] [% END %]
[% IF updated_initialowner %] [% IF changes.initialowner.defined %]
<p>Updated Default Assignee to: '[% comp.default_assignee.login FILTER html %]'.</p> <p>Updated Default Assignee to: '[% comp.default_assignee.login FILTER html %]'.</p>
[% END %] [% END %]
[% IF updated_initialqacontact %] [% IF changes.initialqacontact.defined %]
<p> <p>
[% IF comp.default_qa_contact.id %] [% IF comp.default_qa_contact.id %]
Updated Default QA Contact to '[% comp.default_qa_contact.login FILTER html %]'. Updated Default QA Contact to '[% comp.default_qa_contact.login FILTER html %]'.
...@@ -73,22 +62,19 @@ ...@@ -73,22 +62,19 @@
</p> </p>
[% END %] [% END %]
[% IF updated_name %] [% IF changes.cc_list.defined %]
<p>Updated Component name to: '[% comp.name FILTER html %]'.</p> [% IF comp.initial_cc.size %]
[% END %] [% cc_list = [] %]
[% FOREACH cc_user = comp.initial_cc %]
[% IF updated_initialcc %] [% cc_list.push(cc_user.login) %]
[% IF initial_cc_names %] [% END %]
<p>Updated Default CC list to: <p>Updated Default CC list to: [% cc_list.join(", ") FILTER html %].</p>
'[% initial_cc_names FILTER html %]'.</p>
[% ELSE %] [% ELSE %]
<p>Removed the Default CC list.</p> <p>Removed the Default CC list.</p>
[% END %] [% END %]
[% END %] [% END %]
[% UNLESS updated_description || updated_initialowner || [% UNLESS changes.keys.size %]
updated_initialqacontact || updated_name ||
updated_initialcc %]
<p>Nothing changed for component '[% comp.name FILTER html %]'.</p> <p>Nothing changed for component '[% comp.name FILTER html %]'.</p>
[% END %] [% END %]
......
...@@ -451,6 +451,12 @@ ...@@ -451,6 +451,12 @@
# we can still use get_text(). %] # we can still use get_text(). %]
[% PROCESS "admin/sanitycheck/messages.html.tmpl" %] [% PROCESS "admin/sanitycheck/messages.html.tmpl" %]
[% ELSIF message_tag == "series_all_open" %]
All Open
[% ELSIF message_tag == "series_all_closed" %]
All Closed
[% ELSIF message_tag == "sudo_started" %] [% ELSIF message_tag == "sudo_started" %]
[% title = "Sudo session started" %] [% title = "Sudo session started" %]
The sudo session has been started. For the next 6 hours, or until you The sudo session has been started. For the next 6 hours, or until you
......
...@@ -298,12 +298,13 @@ ...@@ -298,12 +298,13 @@
[% ELSIF error == "component_already_exists" %] [% ELSIF error == "component_already_exists" %]
[% title = "Component Already Exists" %] [% title = "Component Already Exists" %]
A component with the name '[% name FILTER html %]' already exists. The <em>[% product.name FILTER html %]</em> product already has
a component named <em>[% name FILTER html %]</em>.
[% ELSIF error == "component_blank_description" %] [% ELSIF error == "component_blank_description" %]
[% title = "Blank Component Description Not Allowed" %] [% title = "Blank Component Description Not Allowed" %]
You must enter a non-blank description for component '[% name FILTER html %]'. You must enter a non-blank description for this component.
[% ELSIF error == "component_blank_name" %] [% ELSIF error == "component_blank_name" %]
[% title = "Blank Component Name Not Allowed" %] [% title = "Blank Component Name Not Allowed" %]
You must enter a name for this new component. You must enter a name for this new component.
...@@ -317,16 +318,11 @@ ...@@ -317,16 +318,11 @@
[% ELSIF error == "component_name_too_long" %] [% ELSIF error == "component_name_too_long" %]
[% title = "Component Name Is Too Long" %] [% title = "Component Name Is Too Long" %]
The name of a component is limited to 64 characters. The name of a component is limited to 64 characters.
'[% name FILTER html %]' is too long ([% name.size %] characters). '[% name FILTER html %]' is too long ([% name.length %] characters).
[% ELSIF error == "component_need_initialowner" %] [% ELSIF error == "component_need_initialowner" %]
[% title = "Component Requires Default Assignee" %] [% title = "Component Requires Default Assignee" %]
You must enter a default assignee for component '[% name FILTER html %]'. A default assignee is required for this component.
[% ELSIF error == "component_not_valid" %]
[% title = "Specified Component Does Not Exist" %]
Product [% product FILTER html %] does not have a component
named [% name FILTER html %].
[% ELSIF error == "customfield_nonexistent" %] [% ELSIF error == "customfield_nonexistent" %]
[% title = "Unknown Custom Field" %] [% title = "Unknown Custom Field" %]
...@@ -1620,6 +1616,8 @@ ...@@ -1620,6 +1616,8 @@
[% BLOCK object_name %] [% BLOCK object_name %]
[% IF class == "Bugzilla::User" %] [% IF class == "Bugzilla::User" %]
user user
[% ELSIF class == "Bugzilla::Component" %]
component
[% ELSIF class == "Bugzilla::Version" %] [% ELSIF class == "Bugzilla::Version" %]
version version
[% ELSIF class == "Bugzilla::Milestone" %] [% ELSIF class == "Bugzilla::Milestone" %]
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment